Skip to content

Conversation

@fredleeJH
Copy link
Collaborator

@fredleeJH fredleeJH commented Jun 20, 2025

Related issue 🛠

Work Description 📝

  • 더미 추가
  • hilt 세팅
  • 폴더링
  • 기초 네비게이션 세팅..

Screenshot 📸

PR Point 📌

컴포즈 세팅은 처음 해봐서 빠진 부분도 많을테고, 코드적으로도 수정할 부분도 많아보입니다..
네비게이션은 정말 기본적인 세팅만 했습니다!
제발 저에게 많은 피드백을 부탁드립니다.

다른 SOPT 기초세팅 많이 살펴봤습니다,,

트러블 슈팅 💥

린트 문제 해결, 코드래빗 오류 해결

Summary by CodeRabbit

새로운 기능

  • 메인, 홈, 퀘스트, 그래프, 마이페이지 등 주요 화면 및 탭 네비게이션 UI와 구조가 추가되었습니다.
  • 하단 바, 테마, 컬러, 타이포그래피 등 디자인 시스템이 적용되었습니다.
  • 더미 데이터 조회, 상태 관리, 네트워크 통신, 데이터 매핑 등 예시 비즈니스 로직이 구현되었습니다.
  • 의존성 주입, 네트워크, 로컬 데이터 저장소, 레포지토리 등 앱 아키텍처 전반이 구축되었습니다.
  • 앱 테마에 맞춘 다크 컬러 스킴과 UI 모드 기본 설정이 적용되었습니다.
  • 클릭 시 물결 효과 없이 동작하는 커스텀 클릭 모디파이어가 추가되었습니다.

버그 수정

  • 테스트 코드 및 주석이 정리되었습니다.

문서화

  • 접근성 및 하단 바 아이콘 설명 문자열이 추가되었습니다.

환경/설정

  • 빌드 스크립트 및 버전 관리가 현대화되고, 다양한 플러그인과 라이브러리, 번들, 리소스가 추가되었습니다.
  • 카카오 등 외부 저장소가 연결되었습니다.
  • AndroidManifest에 인터넷 권한과 메인 액티비티 선언이 추가되었습니다.

@fredleeJH fredleeJH requested review from sohee6989 and znayeonzn June 20, 2025 07:58
@fredleeJH fredleeJH self-assigned this Jun 20, 2025
@fredleeJH fredleeJH linked an issue Jun 20, 2025 that may be closed by this pull request
@cherry-p0p
Copy link

image

LGTM !!

@znayeonzn znayeonzn added ⚙️ [SETTING] 프로젝트 기초 세팅 🦊 종훈 종훈 labels Jun 23, 2025
Copy link
Collaborator

@znayeonzn znayeonzn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다~~!

Comment on lines 20 to 28
compileSdk = 35

defaultConfig {
applicationId = "com.android.heartz"
applicationId = "com.heartz.app"
minSdk = 28
targetSdk = 35
versionCode = 1
versionName = "1.0"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p1
버전 관리 하드코딩 말고 버전 카탈로그 방식으로 libs.version.toml에서 관리하는 거 어떨까요?!

compileSdk = libs.versions.compileSdk.get().toInt()
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = libs.versions.versionCode.get().toInt()
versionName = libs.versions.versionName.get()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

버전 카탈로그 좋은 것 같아요! 반영하겠습니다!!

Comment on lines 32 to 37
// TODO: kakao social login
buildConfigField(
"String",
"KAKAO_NATIVE_APP_KEY",
properties["kakao.native.app.key"].toString()
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3
단순 궁금.. 우리 이거 나중에 스프린트 위해서 안 지우는 거 맞죠?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 앱잼 내에 진행할 줄 알고 만든 필드인데 굳이 앱잼 후에 스프린트한다면 남겨 둘 필요는 없어보이네요!

import androidx.compose.ui.Modifier
import androidx.compose.ui.composed

inline fun Modifier.noRippleClickable(crossinline onClick: () -> Unit = {}): Modifier =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3
crossinline을 처음 봐서 찾아봤는데 람다에서 non-local return을 막아주는 역할을 하는 거구뇽 배우 ㅓ 가요 ! 🙇‍♀️

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline함수를 쓰면서 정말 많이 나오는 부분 같아요! 앱잼하면서 런캐칭으로 오류처리 할 때 Timber 찍는 거 같이 내부함수로 만들면서 고민해봐요!
민재형 블로그 참고 https://angrypodo.tistory.com/7

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 이거 저도 공부해보겠습니다~~신기하네요

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2
파일명 ServiceModule은 어때요?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@znayeonzn 👍️👍️

Comment on lines 18 to 21
val response =
dummyService.getDummies(
request = request.toData()
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2
한 줄로 가도 되지 않을까요??

Suggested change
val response =
dummyService.getDummies(
request = request.toData()
)
val response = dummyService.getDummies(request = request.toData())

visible = navigator.showBottomBar(),
tabs = MainNavTab.entries.toImmutableList(),
currentTab = navigator.currentTab,
onTabSelected = navigator::navigate
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3
👍👍👍👍

}

fun navigateUp() {
navController.navigateUp()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p1
성능 최적화를 위해서 이렇게 해줘야 한다고 알고 있습니다!

Suggested change
navController.navigateUp()
navController::navigateUp

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@znayeonzn 이 부분은 함수 참조(::navigateUp)와 함수 호출(navigateUp())의 차이에 대해 한 번 공부해보면 좋을 것 같아요! 😊

예를 들어 ButtononClick처럼 버튼이 눌렸을 때 동작해야 하는 상황에서는 다음처럼 함수 참조를 사용해요

fun navigateUp(): Boolean {
    return navController.navigateUp()
}

Button(onClick = ::navigateUp) // 버튼을 누를 때 실행 됨.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하~ 종리드 천재. 떠먹어주셔서 감사합니다 🤩

Comment on lines +94 to +97
Icon(
imageVector = ImageVector.vectorResource(tab.icon),
contentDescription = stringResource(tab.contentDescription),
tint = bottomItemColor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2
icon 사이즈는 설정 안 해도 되나요?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@znayeonzn 이 부분은 이미지 벡터에서 따로 사이즈를 설정하고 있습니다! 근데 아직 확정 아이콘이 아니라 변동사항이 있을 것 같아요~~

contentDescription = ic_mypage_desc,
route = Mypage
)
;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p1
위로 붙여주자!!


@Composable
fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) {
val uiState by viewModel.uiState.collectAsState()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2
collectAsStateWithLifecycle()??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 부분까지 알고 계시는 군요! 많이 공부해보겠습니다. 항상 생각없이 상태 수집을 collectAsState()로만 했었는데 감사합니다 공부해볼게요!

Copy link
Collaborator

@sohee6989 sohee6989 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다~~

) {
QUEST(
icon = ic_folder_open,
contentDescription = ic_quest_desc,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: tab 바에서만 string 추출해서 쓰는걸까요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sohee6989 탭 바에 보통 텍스트가 같이 포함된 UI가 많은데 초기 세팅할 때는 텍스트가 없길래 추출만 해두고, 추후에 생긴다면 반영하려고 뒀습니다! 탭 바 전용 string리소스가 맞는거 같아요!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 제가 말을 애매하게 썼네요..!ㅜㅜ
저희 string 추출 안 하기로 했었는데 이 부분 string 추출 해놓았길래 질문드린거였습니다!ㅎㅎ

*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: 얘도 테스트코드인건가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분은 안드로이드 Instrumented 테스트 코드입니다! 저도 unit test만 해보고 요런 테스트에 대해서는 처음 공부해봤는데 이 차이점은 나중에 같이 공부해보면 좋을 것 같아요! 여기서 수정이 일어난 이유는 ktlint에서 import * 이런식으로 와일드카드 쓰는 것을 지양하기 때문에 수정했습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오오 Instrumented 테스트 코드 이 부분 저도 한 번 미리 공부해보겠습니다~~

super.onCreate()

initTimber()
setDayMode()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: 다크 모드 확장하는 거 물어봐주는거 까먹지 말기~~

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sohee6989 앱잼 기간 동안에는 일단 안하는걸루!

@coderabbitai
Copy link

coderabbitai bot commented Jun 26, 2025

"""

Walkthrough

이 변경 사항은 안드로이드 프로젝트의 초기 세팅을 광범위하게 수행합니다. Gradle 설정, 버전 관리, 의존성 관리, 코드 스타일 도구, 네트워크 및 DI 모듈, 기본 도메인/데이터/프레젠테이션 구조, 네비게이션, 테마, UI 컴포넌트, 리소스 파일 등이 대규모로 추가 및 구성되었습니다.

Changes

파일/경로 그룹 변경 요약
app/build.gradle.kts, build.gradle.kts, gradle/libs.versions.toml, settings.gradle.kts Gradle 빌드 스크립트, 버전 카탈로그, 플러그인, 저장소, 의존성 및 번들 대규모 추가 및 현대화
app/src/main/AndroidManifest.xml 앱 네임스페이스, Application 클래스, 메인 액티비티, 권한, 구조 수정
app/src/main/java/com/heartz/app/Heartz.kt Hilt DI 및 Timber, DayMode 적용 Application 클래스 추가
app/src/main/java/com/heartz/app/core/designsystem/ui/theme/* 테마, 컬러, 타이포그래피 등 Compose UI 테마 시스템 구축
app/src/main/java/com/heartz/app/core/navigation/* Route, MainTabRoute 등 네비게이션 인터페이스 정의
app/src/main/java/com/heartz/app/core/state/UiState.kt UI 상태 표현을 위한 sealed interface 및 상태 클래스 추가
app/src/main/java/com/heartz/app/core/util/ModifierExt.kt Compose Modifier 확장 함수(noRippleClickable) 추가
app/src/main/java/com/heartz/app/data/**/* 데이터 소스, DI 모듈, DTO, 매퍼, 레포지토리, 서비스 등 데이터 레이어 전반 구현
app/src/main/java/com/heartz/app/domain/**/* 도메인 모델, 레포지토리, 유스케이스 인터페이스 등 도메인 레이어 구축
app/src/main/java/com/heartz/app/presentation/**/navigation/* 각 탭별 네비게이션 그래프 및 네비게이션 함수 추가
app/src/main/java/com/heartz/app/presentation/main/* MainActivity, MainScreen, MainNavTab, MainNavigator, MainNavHost, MainBottomBar 등 메인 UI 및 네비게이션 구조 구현
app/src/main/java/com/heartz/app/presentation/home/* HomeScreen, HomeViewModel, HomeState 등 홈 화면 및 상태관리 구현
app/src/main/java/com/heartz/app/presentation/quest/, .../graph/, .../mypage/* Quest, Figure, Mypage 화면 및 각 네비게이션 구성 추가
app/src/main/res/drawable/* 하단바 아이콘 등 벡터 드로어블 리소스 4종 추가
app/src/main/res/values/strings.xml 하단바 아이콘 설명 문자열(한글) 4종 추가
app/src/androidTest/java/com/android/heartz/ExampleInstrumentedTest.kt, app/src/test/java/com/android/heartz/ExampleUnitTest.kt 테스트 파일 import 정리 및 주석 정리(로직 변화 없음)

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MainActivity
    participant MainScreen
    participant MainNavigator
    participant NavHost
    participant HomeViewModel
    participant DummyRepository
    participant DummyService

    User->>MainActivity: 앱 실행
    MainActivity->>MainScreen: setContent(HeartzTheme)
    MainScreen->>MainNavigator: rememberMainNavigator()
    MainScreen->>NavHost: MainNavHost(navigator)
    NavHost->>HomeViewModel: (Home 탭 진입 시) getDummies()
    HomeViewModel->>DummyRepository: getDummies(Dummy)
    DummyRepository->>DummyService: getDummies(RequestDummyDto)
    DummyService-->>DummyRepository: DummyBaseResponse
    DummyRepository-->>HomeViewModel: Result<DummyResultModel>
    HomeViewModel-->>MainScreen: uiState 업데이트
    MainScreen-->>User: 결과 UI 표시
Loading

Assessment against linked issues

Objective Addressed Explanation
프로젝트 기초 세팅 ( #1 )

Assessment against linked issues: Out-of-scope changes

(해당 변경 사항 내에서는 명확히 범위를 벗어난 기능적 코드 변경이 발견되지 않았습니다.)

Poem

🐇
프로젝트의 뿌리를 단단히,
코드밭에 씨를 뿌렸지!
네비게이션, 테마, 데이터까지
토끼도 놀랄 기초 세팅의 바람!
하단바 아이콘 반짝이며
이제 모험의 출발선에 섰네—
깡총깡총, 앞으로 Go!

"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 008177f and 4994c0c.

📒 Files selected for processing (2)
  • app/src/main/java/com/heartz/app/data/datasource/local/DummyLocalDataSource.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/service/DummyService.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/com/heartz/app/data/datasource/local/DummyLocalDataSource.kt
  • app/src/main/java/com/heartz/app/data/service/DummyService.kt
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

♻️ Duplicate comments (4)
app/src/main/java/com/heartz/app/data/repositoryimpl/DummyRepositoryImpl.kt (1)

18-19: 코드를 더 간결하게 만들 수 있습니다.

이전 리뷰 코멘트와 동일하게, 한 줄로 작성하는 것이 더 깔끔할 것 같습니다:

-            val response = dummyService.getDummies(request = request.toData())
-            response.data.toDomain()
+            dummyService.getDummies(request = request.toData()).data.toDomain()
app/src/main/java/com/heartz/app/presentation/main/component/MainBottomBar.kt (1)

94-98: 아이콘 크기를 명시적으로 설정하는 것을 고려하세요.

이미 이전 리뷰에서 논의되었지만, 일관성 있는 UI를 위해 아이콘 크기를 명시적으로 설정하는 것을 고려해볼 수 있습니다.

app/build.gradle.kts (1)

20-27: 버전 카탈로그 사용이 잘 적용되었습니다.

이전 리뷰에서 제안했던 버전 카탈로그 방식이 잘 적용되었습니다.

app/src/main/java/com/heartz/app/presentation/main/MainNavigator.kt (1)

59-61: navigateUp 함수 구현이 적절합니다.

이전 리뷰에서 논의된 함수 참조 vs 함수 호출에 대한 내용으로, 현재 구현이 적절합니다.

🧹 Nitpick comments (20)
app/src/main/java/com/heartz/app/core/navigation/Route.kt (1)

1-3: Route 인터페이스를 sealed interface로 변경 고려

sealed interface Route 로 선언하면 when 식 사용 시 컴파일러가 분기 누락 여부를 체크해줄 수 있어 안전성이 높아집니다.
추후 하위 Route 가 객체·data class 등으로 다양해질 경우 유용합니다.

settings.gradle.kts (1)

19-20: 외부 Maven 저장소 범위 제한 권장

카카오 사설 저장소를 전체 Maven 저장소로 추가하면 불필요한 아티팩트 해상도 시간이 증가하고, 잠재적으로 의도치 않은 그룹이 내려올 수 있습니다.

maven {
    url = uri("https://devrepo.kakao.com/nexus/content/groups/public")
    content { includeGroup("com.kakao") } // 예시: 사용 그룹만 한정
}

필요 그룹만 필터링하도록 content 블록을 설정해 보시는 것을 권장드립니다.

app/src/main/res/values/strings.xml (1)

4-8: 다국어 리소스 분리 및 키 네이밍 통일 제안

  1. values/strings.xml 은 기본(영어) 리소스이므로, 한글 문구는 values-ko/strings.xml 로 분리하면 다국어 준비가 수월합니다.
  2. 키 접두사를 bottom_bar_*_desc 등으로 통일하면 검색성이 향상됩니다.

예시:

<!-- values/strings.xml -->
<string name="bottom_bar_home_desc">Home</string>

<!-- values-ko/strings.xml -->
<string name="bottom_bar_home_desc">홈</string>
app/src/main/res/drawable/ic_folder_open.xml (1)

1-10: 하드코딩 색상 → 테마 속성 적용 권장

Vector Drawable 내부에 고정 HEX 색상 #FAAEAE 가 박혀있어 다크모드·테마 변경 시 일관성이 깨질 수 있습니다.

-android:fillColor="#FAAEAE"
+android:fillColor="?attr/colorPrimary"

색상을 테마 속성으로 치환해 UI 일관성과 유지보수성을 높여주세요.

app/src/main/java/com/heartz/app/core/state/UiState.kt (1)

12-14: 에러 처리를 위한 추가 정보 제공을 고려해보세요.

현재 Failure 상태는 문자열 메시지만 포함하고 있습니다. 더 구체적인 에러 처리를 위해 에러 코드나 예외 타입을 추가하는 것을 고려해보세요.

 data class Failure(
-    val msg: String
+    val msg: String,
+    val errorCode: String? = null,
+    val throwable: Throwable? = null
 ) : UiState<Nothing>
gradle/libs.versions.toml (1)

55-61: 공백 라인 정리가 필요합니다.

버전 정의 섹션에 불필요한 공백 라인이 있어 가독성이 떨어집니다.

 #Third Party
 ksp = "2.0.21-1.0.25"
-
-
-
 ktlint = "11.6.1"
-
-
app/src/main/java/com/heartz/app/core/navigation/MainTabRoute.kt (1)

1-3: 인터페이스 목적에 대한 문서화를 추가해보세요.

마커 인터페이스의 역할과 사용법을 명확히 하기 위해 KDoc 주석을 추가하는 것을 권장합니다.

 package com.heartz.app.core.navigation

+/**
+ * 메인 탭 네비게이션을 위한 마커 인터페이스
+ * 이 인터페이스를 구현하는 객체들은 메인 탭의 네비게이션 대상이 됩니다.
+ */
 interface MainTabRoute : Route
app/src/main/java/com/heartz/app/domain/model/Dummy.kt (1)

3-6: 실제 도메인 모델로의 점진적 교체를 고려해보세요.

현재는 더미 데이터 모델로 적절하지만, 향후 실제 비즈니스 요구사항에 맞는 도메인 모델로 점진적으로 교체하는 것을 권장합니다. 이때 데이터 검증 로직이나 비즈니스 룰도 함께 추가하는 것을 고려해보세요.

app/src/main/java/com/heartz/app/domain/model/DummyResultModel.kt (1)

3-5: 도메인 모델에 대한 최소한의 문서화 필요

DummyResultModel이 어떤 목적과 제약(예: info 리스트가 비어있을 수 있는지)에 대한 설명이 전혀 없습니다. 간단한 KDoc을 추가해두면 이후 모델 변경 / 확장 시 혼란을 줄일 수 있습니다.

/**
 * 서버에서 내려오는 더미 데이터의 결과 모델.
 * @param info 표시용 문자열 리스트
 */
data class DummyResultModel(
    val info: List<String>
)
app/src/main/java/com/heartz/app/data/mapper/todata/DummyMapper.kt (1)

6-10: 매핑 누락/변경 감지용 테스트 추가 권장

확장 함수 구현은 간단하지만, 필드가 추가·변경될 때 컴파일이 통과되면서 런타임에 누락이 발생할 위험이 있습니다.
Mapper 단위 테스트를 작성해 Dummy(id, email)RequestDummyDto 변환이 정확한지 검증해 두면 리팩터링 시 안전망이 됩니다.

app/src/main/java/com/heartz/app/presentation/mypage/MypageScreen.kt (1)

3-12: Modifier 체이닝 컨벤션

Modifier 체이닝을 매 줄마다 .로 시작하도록 정렬하면 가독성이 높습니다.

modifier = Modifier
    .fillMaxSize()
    .background(MaterialTheme.colorScheme.background)
app/src/main/java/com/heartz/app/core/designsystem/ui/theme/TypoGraphy.kt (1)

9-18: 변수명 충돌 방지: TypographyHeartzTypography 등으로 변경 제안

동일한 이름의 타입(androidx.compose.material3.Typography)과 프로퍼티가 공존하면 IDE 자동완성에서 혼란을 야기할 수 있습니다. 프로젝트 전역에서 사용할 커스텀 타이포그래피는 HeartzTypography 같이 고유한 이름으로 선언하는 편이 명확합니다.

-val Typography =
+val HeartzTypography =
     Typography(
         bodyLarge = TextStyle(

변경 후 HeartzTheme 등에서 해당 프로퍼티를 참조하도록 수정해 주세요.

app/src/main/java/com/heartz/app/presentation/graph/FigureScreen.kt (1)

15-27: 테마 시스템을 사용하여 일관성을 개선하세요.

하드코딩된 색상 대신 테마 시스템을 사용하면 일관성과 유지보수성이 향상됩니다.

다음과 같이 테마 색상을 사용하도록 수정하는 것을 권장합니다:

-import androidx.compose.ui.graphics.Color
+import androidx.compose.material3.MaterialTheme

@Composable
fun FigureScreen() {
    Column(
        modifier = Modifier
            .fillMaxSize()
-            .background(Color.White),
+            .background(MaterialTheme.colorScheme.background),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "figure Screen",
-            color = Color.Black
+            color = MaterialTheme.colorScheme.onBackground,
+            style = MaterialTheme.typography.bodyLarge
        )
    }
}
app/src/main/java/com/heartz/app/presentation/home/HomeState.kt (1)

6-8: 상태 불변성을 위해 val 사용을 고려해보세요.

현재 user 프로퍼티가 var로 선언되어 있는데, 상태 관리에서는 불변성을 유지하는 것이 좋습니다. val로 변경하고 상태 업데이트 시 copy() 메서드를 사용하는 것을 권장합니다.

data class HomeState(
-    var user: UiState<DummyResultModel> = UiState.Loading
+    val user: UiState<DummyResultModel> = UiState.Loading
)
app/src/main/java/com/heartz/app/data/datasource/local/DummyLocalDataSource.kt (1)

5-12: 인터페이스 설계가 적절합니다.

Flow를 사용한 반응형 로그인 상태 관리와 suspend 함수를 통한 비동기 처리가 잘 설계되었습니다. TODO 주석에 따라 임시 코드로 보이니, 추후 리팩토링 시점을 고려해주세요.

app/src/main/java/com/heartz/app/presentation/main/MainNavHost.kt (1)

18-30: 기본 네비게이션 구조가 잘 설계되었습니다

모듈러 방식으로 각 기능별 네비게이션 그래프가 분리되어 있어 유지보수성이 좋습니다.

향후 사용자 경험 개선을 위해 적절한 전환 애니메이션 추가를 고려해보시면 좋을 것 같습니다.

app/src/main/java/com/heartz/app/data/di/NetworkModule.kt (1)

21-26: 로깅 인터셉터 레벨을 빌드 타입에 따라 조정하는 것을 고려해보세요.

현재 HttpLoggingInterceptor.Level.BODY로 설정되어 있어 디버깅에는 유용하지만, 프로덕션 환경에서는 성능과 보안상 적절하지 않을 수 있습니다.

다음과 같이 빌드 타입에 따라 로깅 레벨을 조정하는 것을 권장합니다:

 @Provides
 @Singleton
 fun providesLoggingInterceptor() =
     HttpLoggingInterceptor().apply {
-        level = HttpLoggingInterceptor.Level.BODY
+        level = if (BuildConfig.DEBUG) {
+            HttpLoggingInterceptor.Level.BODY
+        } else {
+            HttpLoggingInterceptor.Level.NONE
+        }
     }
app/src/main/java/com/heartz/app/presentation/main/component/MainBottomBar.kt (2)

44-48: 애니메이션 전환 효과를 개선하세요.

현재 EnterTransition.NoneExitTransition.None을 사용하여 애니메이션이 전혀 없어 사용자 경험이 좋지 않을 수 있습니다.

 AnimatedVisibility(
     visible = visible,
-    enter = EnterTransition.None,
-    exit = ExitTransition.None
+    enter = slideInVertically(
+        initialOffsetY = { it },
+        animationSpec = tween(300)
+    ),
+    exit = slideOutVertically(
+        targetOffsetY = { it },
+        animationSpec = tween(300)
+    )
 )

59-59: 하드코딩된 패딩 값을 테마나 상수로 관리하세요.

21.dp와 같은 매직 넘버가 하드코딩되어 있어 일관성 있는 디자인 시스템 구축에 문제가 될 수 있습니다.

앱의 디자인 시스템에 spacing 상수들을 정의하고 사용하는 것을 권장합니다.

// DesignSystem.kt
object Spacing {
    val bottomBarVertical = 21.dp
    val bottomBarItemSpacing = 1.dp
}
app/src/main/java/com/heartz/app/presentation/main/MainNavigator.kt (1)

27-31: currentTab 계산 최적화를 고려하세요.

매번 MainNavTab.find를 호출하는 것보다 더 효율적인 방법을 고려해볼 수 있습니다.

 val currentTab: MainNavTab?
     @Composable get() =
-        MainNavTab.find { tab ->
-            currentDestination?.hasRoute(tab::class) == true
-        }
+        currentDestination?.route?.let { route ->
+            MainNavTab.entries.firstOrNull { tab ->
+                currentDestination?.hasRoute(tab.route::class) == true
+            }
+        }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6eb76d and 367370b.

📒 Files selected for processing (56)
  • app/build.gradle.kts (2 hunks)
  • app/src/androidTest/java/com/android/heartz/ExampleInstrumentedTest.kt (1 hunks)
  • app/src/main/AndroidManifest.xml (2 hunks)
  • app/src/main/java/com/heartz/app/Heartz.kt (1 hunks)
  • app/src/main/java/com/heartz/app/core/designsystem/ui/theme/Color.kt (1 hunks)
  • app/src/main/java/com/heartz/app/core/designsystem/ui/theme/Theme.kt (1 hunks)
  • app/src/main/java/com/heartz/app/core/designsystem/ui/theme/TypoGraphy.kt (1 hunks)
  • app/src/main/java/com/heartz/app/core/navigation/MainTabRoute.kt (1 hunks)
  • app/src/main/java/com/heartz/app/core/navigation/Route.kt (1 hunks)
  • app/src/main/java/com/heartz/app/core/state/UiState.kt (1 hunks)
  • app/src/main/java/com/heartz/app/core/util/ModifierExt.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/datasource/local/DummyLocalDataSource.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/datasource/remote/DummyRemoteDataSource.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/datasourceimpl/local/DummyLocalDataSourceImpl.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/datasourceimpl/remote/DummyRemoteDataSourceImpl.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/di/DataSourceModule.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/di/NetworkModule.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/di/RepositoryModule.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/di/ServiceModule.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/dto/base/DummyBaseResponse.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/dto/base/DummyNullableBaseResponse.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/dto/request/RequestDummyDto.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/dto/response/ResponseDummyDto.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/mapper/todata/DummyMapper.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/mapper/todomain/ResponseDummyDtoMapper.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/repositoryimpl/DummyRepositoryImpl.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/service/DummyService.kt (1 hunks)
  • app/src/main/java/com/heartz/app/domain/model/Dummy.kt (1 hunks)
  • app/src/main/java/com/heartz/app/domain/model/DummyResultModel.kt (1 hunks)
  • app/src/main/java/com/heartz/app/domain/repository/DummyRepository.kt (1 hunks)
  • app/src/main/java/com/heartz/app/domain/usecase/DummyUseCase.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/graph/FigureScreen.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/graph/navigation/FigureNavigation.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/home/HomeScreen.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/home/HomeState.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/home/HomeViewModel.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/home/navigation/HomeNavigation.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/main/MainActivity.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/main/MainNavHost.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/main/MainNavTab.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/main/MainNavigator.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/main/MainScreen.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/main/component/MainBottomBar.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/mypage/MypageScreen.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/mypage/navigation/MypageNavigation.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/quest/QuestScreen.kt (1 hunks)
  • app/src/main/java/com/heartz/app/presentation/quest/navigation/QuestNavigation.kt (1 hunks)
  • app/src/main/res/drawable/ic_folder_open.xml (1 hunks)
  • app/src/main/res/drawable/ic_graph.xml (1 hunks)
  • app/src/main/res/drawable/ic_home.xml (1 hunks)
  • app/src/main/res/drawable/ic_user.xml (1 hunks)
  • app/src/main/res/values/strings.xml (1 hunks)
  • app/src/test/java/com/android/heartz/ExampleUnitTest.kt (1 hunks)
  • build.gradle.kts (1 hunks)
  • gradle/libs.versions.toml (1 hunks)
  • settings.gradle.kts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
app/src/main/java/com/heartz/app/presentation/main/MainActivity.kt (2)
app/src/main/java/com/heartz/app/core/designsystem/ui/theme/Theme.kt (1)
  • HeartzTheme (6-13)
app/src/main/java/com/heartz/app/presentation/main/MainScreen.kt (1)
  • MainScreen (14-36)
app/src/main/java/com/heartz/app/presentation/quest/navigation/QuestNavigation.kt (2)
app/src/main/java/com/heartz/app/presentation/main/MainNavigator.kt (1)
  • navigate (33-51)
app/src/main/java/com/heartz/app/presentation/quest/QuestScreen.kt (1)
  • QuestScreen (13-27)
app/src/main/java/com/heartz/app/presentation/main/MainNavHost.kt (4)
app/src/main/java/com/heartz/app/presentation/home/navigation/HomeNavigation.kt (1)
  • homeGraph (15-19)
app/src/main/java/com/heartz/app/presentation/quest/navigation/QuestNavigation.kt (1)
  • questGraph (15-19)
app/src/main/java/com/heartz/app/presentation/graph/navigation/FigureNavigation.kt (1)
  • figureGraph (15-19)
app/src/main/java/com/heartz/app/presentation/mypage/navigation/MypageNavigation.kt (1)
  • mypageGraph (15-19)
app/src/main/java/com/heartz/app/presentation/mypage/navigation/MypageNavigation.kt (2)
app/src/main/java/com/heartz/app/presentation/main/MainNavigator.kt (1)
  • navigate (33-51)
app/src/main/java/com/heartz/app/presentation/mypage/MypageScreen.kt (1)
  • MypageScreen (13-27)
app/src/main/java/com/heartz/app/presentation/main/MainScreen.kt (3)
app/src/main/java/com/heartz/app/presentation/main/MainNavigator.kt (1)
  • rememberMainNavigator (64-69)
app/src/main/java/com/heartz/app/presentation/main/component/MainBottomBar.kt (1)
  • MainBottomBar (37-76)
app/src/main/java/com/heartz/app/presentation/main/MainNavHost.kt (1)
  • MainNavHost (13-31)
app/src/main/java/com/heartz/app/presentation/graph/navigation/FigureNavigation.kt (2)
app/src/main/java/com/heartz/app/presentation/main/MainNavigator.kt (1)
  • navigate (33-51)
app/src/main/java/com/heartz/app/presentation/graph/FigureScreen.kt (1)
  • FigureScreen (13-27)
app/src/main/java/com/heartz/app/presentation/main/component/MainBottomBar.kt (1)
app/src/main/java/com/heartz/app/core/designsystem/ui/theme/Theme.kt (1)
  • HeartzTheme (6-13)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (33)
build.gradle.kts (1)

6-11: 플러그인 설정이 적절합니다

최신 Android 개발에 필요한 플러그인들을 올바르게 추가했습니다:

  • Compose 지원을 위한 kotlin.compose
  • JSON 직렬화를 위한 kotlin.serialization
  • 의존성 주입을 위한 hilt
  • 코드 생성을 위한 ksp
  • 데이터 클래스 최적화를 위한 kotlin.parcelize
  • 코드 스타일 검사를 위한 ktlint

apply false를 사용하여 하위 모듈에서 선택적으로 적용할 수 있도록 한 것도 좋은 접근입니다.

app/src/main/java/com/heartz/app/core/state/UiState.kt (1)

3-15: UI 상태 관리를 위한 잘 설계된 sealed interface입니다.

타입 안전성을 보장하면서 다양한 UI 상태를 명확히 표현할 수 있는 구조입니다. 코드가 간결하고 이해하기 쉽습니다.

gradle/libs.versions.toml (2)

123-164: 번들 구성이 잘 되어 있습니다.

관련된 라이브러리들을 논리적으로 그룹화하여 의존성 관리를 효율적으로 할 수 있도록 구성했습니다. 이는 모듈화된 빌드 설정에 도움이 됩니다.


11-11: ```shell
#!/bin/bash

AGP 8.9.2 버전 존재 여부 확인 (Google Maven)

curl -s "https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/maven-metadata.xml"
| grep -o '8.9.2' || echo "버전 없음"


</details>
<details>
<summary>app/src/main/java/com/heartz/app/core/navigation/MainTabRoute.kt (1)</summary>

`3-3`: **네비게이션 구조를 위한 적절한 마커 인터페이스입니다.**

메인 탭 네비게이션을 위한 타입 안전성을 제공하는 깔끔한 설계입니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/domain/model/Dummy.kt (1)</summary>

`3-6`: **테스트용 도메인 모델이 적절하게 구성되었습니다.**

간단하고 명확한 구조로 초기 개발 단계에서 사용하기에 적합합니다.

</details>
<details>
<summary>app/src/androidTest/java/com/android/heartz/ExampleInstrumentedTest.kt (1)</summary>

`13-15`: **`applicationId` 확인 필요**

`assertEquals("com.android.heartz", appContext.packageName)`가 실제 `applicationId`와 일치하지 않으면 테스트가 바로 실패합니다.  
`build.gradle`의 `namespace` / `applicationId`가 `com.heartz.app`와 같이 달라졌다면 값도 함께 수정해 주세요.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/core/designsystem/ui/theme/Theme.kt (1)</summary>

`6-13`: **잘 구현된 테마 설정입니다.**

MaterialTheme을 올바르게 래핑하여 커스텀 컬러 스킴과 타이포그래피를 적용하는 깔끔한 구현입니다. Compose 테마 패턴을 잘 따르고 있습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/domain/repository/DummyRepository.kt (1)</summary>

`6-8`: **깔끔한 리포지토리 인터페이스 설계입니다.**

도메인 레이어의 리포지토리 패턴을 올바르게 구현했습니다. suspend 함수와 Result<T> 타입을 사용한 에러 핸들링이 적절합니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/mapper/todomain/ResponseDummyDtoMapper.kt (1)</summary>

`6-9`: **간단하고 명확한 매퍼 구현입니다.**

DTO에서 도메인 모델로의 변환이 깔끔하게 구현되어 있습니다. 현재 구조에서는 적절한 직접 매핑입니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/dto/response/ResponseDummyDto.kt (1)</summary>

`6-10`: **적절한 DTO 클래스 구현입니다.**

Kotlin Serialization을 올바르게 사용하여 API 응답 구조를 정의했습니다. @SerialName 어노테이션과 @Serializable 사용이 적절합니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/dto/request/RequestDummyDto.kt (1)</summary>

`6-12`: **요청 DTO 구현이 적절합니다.**

kotlinx serialization을 올바르게 사용하고 있으며, @SerialName을 통한 JSON 매핑도 명확합니다. 데이터 클래스 패턴을 잘 따르고 있네요.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/datasource/remote/DummyRemoteDataSource.kt (1)</summary>

`7-9`: **원격 데이터 소스 인터페이스 구현이 깔끔합니다.**

suspend 함수를 사용한 비동기 네트워크 처리와 제네릭 베이스 응답 래퍼를 활용한 설계가 좋습니다. Repository 패턴을 잘 따르고 있네요.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/core/util/ModifierExt.kt (1)</summary>

`9-17`: **리플 효과 없는 클릭 가능한 Modifier 확장 함수가 잘 구현되었습니다.**

`composed`와 `remember`를 적절히 사용하여 재구성 시에도 효율적이고, `indication = null`로 리플 효과를 제거하는 로직이 정확합니다. UI/UX 요구사항에 따라 유용하게 활용될 것 같네요.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/presentation/main/MainActivity.kt (1)</summary>

`10-21`: **깔끔한 MainActivity 구현입니다.**

Hilt를 사용한 의존성 주입, Compose 설정, 그리고 edge-to-edge 디스플레이 활성화가 올바르게 구현되어 있습니다. 초기 프로젝트 설정으로 적절한 구조를 갖추고 있습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/di/ServiceModule.kt (1)</summary>

`11-19`: **Hilt 의존성 주입 모듈이 올바르게 구현되었습니다.**

SingletonComponent 스코프와 @Singleton 어노테이션을 사용하여 DummyService를 제공하는 방식이 적절합니다. Retrofit 인스턴스를 통해 서비스를 생성하는 패턴도 표준적입니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/dto/base/DummyBaseResponse.kt (1)</summary>

`6-16`: **잘 구조화된 API 응답 래퍼 클래스입니다.**

제네릭 타입을 사용하여 재사용 가능하도록 설계되었고, @SerialName 어노테이션을 통한 JSON 매핑도 올바르게 구현되어 있습니다. 표준적인 API 응답 구조(success, code, message, data)를 따르고 있어 좋습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/di/RepositoryModule.kt (1)</summary>

`11-17`: **Repository 바인딩이 올바르게 구현되었습니다.**

@Binds 어노테이션을 사용하여 구현체를 인터페이스에 바인딩하는 방식이 효율적이고 적절합니다. Singleton 스코프로 Repository 패턴을 올바르게 적용했습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/dto/base/DummyNullableBaseResponse.kt (1)</summary>

`6-16`: **API 응답 기본 구조가 잘 설계되었습니다**

nullable 데이터를 처리하기 위한 기본 응답 클래스가 깔끔하게 구현되었습니다. Serializable 어노테이션과 SerialName을 적절히 사용하여 JSON 직렬화가 잘 설정되어 있습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/Heartz.kt (1)</summary>

`8-24`: **Application 클래스 초기화가 적절하게 구현되었습니다**

Hilt DI 설정과 기본 초기화 로직이 깔끔하게 분리되어 있습니다. DEBUG 빌드에서만 Timber를 활성화하는 것도 좋은 패턴입니다.

향후 다크모드 지원을 고려할 때는 사용자 설정이나 시스템 설정을 따르도록 개선할 수 있을 것 같습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/presentation/home/navigation/HomeNavigation.kt (1)</summary>

`11-22`: **type-safe 네비게이션이 잘 구현되었습니다**

Kotlin Serialization을 활용한 type-safe 네비게이션과 확장 함수를 통한 모듈화가 적절하게 구현되었습니다. 다른 기능 모듈들과 일관된 패턴을 유지하고 있어 좋습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/datasourceimpl/remote/DummyRemoteDataSourceImpl.kt (1)</summary>

`10-19`: **Clean Architecture 패턴이 적절히 구현되었습니다**

데이터소스 구현체가 단일 책임 원칙을 잘 따르고 있으며, Hilt를 통한 의존성 주입과 코루틴을 활용한 비동기 처리가 적절하게 설정되어 있습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/presentation/main/MainScreen.kt (1)</summary>

`14-36`: **잘 구현된 메인 스크린 구조입니다!**

Scaffold를 사용한 구조가 깔끔하고, MainNavigator를 통한 네비게이션 상태 관리도 적절합니다. 하단 네비게이션의 가시성 제어와 ImmutableList 사용도 좋은 패턴입니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/presentation/quest/navigation/QuestNavigation.kt (1)</summary>

`11-22`: **타입 안전 네비게이션이 잘 구현되었습니다!**

Kotlin Serialization을 사용한 타입 안전 네비게이션과 확장 함수 패턴이 일관성 있게 적용되어 있습니다. MainTabRoute 인터페이스 구현도 적절합니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/presentation/mypage/navigation/MypageNavigation.kt (1)</summary>

`11-22`: **Quest 네비게이션과 일관된 패턴으로 잘 구현되었습니다!**

다른 탭 네비게이션 모듈들과 동일한 패턴을 사용하여 일관성을 유지하고 있으며, 타입 안전 네비게이션이 적절히 적용되어 있습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/presentation/graph/navigation/FigureNavigation.kt (1)</summary>

`1-22`: **네비게이션 구현이 올바르게 작성되었습니다.**

다른 탭 네비게이션들과 일관된 패턴을 따르고 있으며, 타입 안전한 네비게이션을 위한 Kotlinx Serialization을 적절히 활용하고 있습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/presentation/home/HomeScreen.kt (2)</summary>

`14-19`: **`collectAsStateWithLifecycle()` 사용이 올바르게 적용되었습니다.**

이전 리뷰에서 언급된 라이프사이클 인식 상태 수집 권장사항이 잘 반영되었습니다. 이를 통해 컴포저블이 비활성 상태일 때 불필요한 리컴포지션을 방지할 수 있습니다.

---

`28-46`: **UI 상태 처리가 포괄적으로 구현되었습니다.**

Loading, Success, Failure, Empty 상태를 모두 처리하여 사용자에게 명확한 피드백을 제공하고 있습니다.

</details>
<details>
<summary>app/src/main/AndroidManifest.xml (3)</summary>

`5-5`: **네트워크 권한이 적절히 추가되었습니다.**

애플리케이션에서 네트워크 통신을 위한 INTERNET 권한이 올바르게 선언되었습니다.

---

`8-8`: **애플리케이션 클래스 설정이 올바릅니다.**

Hilt를 사용하기 위한 애플리케이션 클래스가 적절히 설정되었습니다.

---

`18-28`: **메인 액티비티가 올바르게 선언되었습니다.**

런처 액티비티로서 필요한 모든 속성과 인텐트 필터가 적절히 구성되어 있습니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/datasourceimpl/local/DummyLocalDataSourceImpl.kt (1)</summary>

`16-37`: **DataStore 구현이 올바르게 작성되었습니다.**

DataStore Preferences를 사용한 로컬 데이터 저장소 구현이 베스트 프랙티스를 따르고 있습니다. 의존성 주입과 인터페이스 구현도 적절합니다.

</details>
<details>
<summary>app/src/main/java/com/heartz/app/data/di/NetworkModule.kt (1)</summary>

`28-52`: **네트워크 모듈 구성이 올바르게 작성되었습니다.**

OkHttpClient, Retrofit, 그리고 Kotlinx Serialization 컨버터가 적절히 구성되어 있으며, 의존성 주입 설정도 올바릅니다.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +3 to +5
// / TODO: usecase는 생각이 많은데 고민을 조금 해봐야 겠습니다!

interface DummyUseCase
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

구현 없이 비어있는 UseCase 인터페이스는 혼란을 유발합니다
현재 DummyUseCase는 메서드가 전혀 없어서 실제 도메인 레이어의 계약을 정의하지 못하고 있습니다. 추후 구현 의도가 명확해질 때까지는 파일을 아예 제거하거나, 최소한 invoke() 시그니처만이라도 정의해 두는 편이 좋습니다.

아래는 일반적인 Clean Architecture 패턴에 맞춰 fun interface 형태로 간략히 제안한 예시입니다.

-interface DummyUseCase
+/**
+ * 더미 데이터를 가져오는 도메인 계층 UseCase.
+ */
+fun interface DummyUseCase {
+    /**
+     * 도메인 계층에서 더미 데이터를 조회합니다.
+     */
+    suspend operator fun invoke(): DummyResultModel
+}
  • 불필요한 TODO 주석 대신 KDoc을 이용해 의도를 명시했습니다.
  • fun interface + operator fun invoke() 형태로 사용 시 DI 주입 후 간결하게 dummyUseCase() 로 호출할 수 있습니다.
  • 반환형 DummyResultModel 은 예시이므로 실제 모델 타입에 맞게 수정해 주세요.
🤖 Prompt for AI Agents
In app/src/main/java/com/heartz/app/domain/usecase/DummyUseCase.kt around lines
3 to 5, the DummyUseCase interface is empty, which can cause confusion as it
does not define any contract. To fix this, either remove the file until the
implementation is clear or define at least a minimal function signature, such as
an operator fun invoke() method, to clarify its intended use. Additionally,
replace the TODO comment with a KDoc explaining the interface's purpose, and
consider using a fun interface with a proper return type matching your domain
model.

Comment on lines 0 to 11
package com.android.heartz

import org.junit.Assert.assertEquals
import org.junit.Test

import org.junit.Assert.*

/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

패키지 네이밍 일관성 확보 필요

테스트 코드가 com.android.heartz 패키지에 위치해 있으나, 실제 소스는 com.heartz.app 네임스페이스를 사용합니다.
패키지가 불일치하면 IDE-기반 코드 탐색, 커버리지 집계 등이 꼬일 수 있으니 동일한 루트 패키지로 정렬해 주세요.

🤖 Prompt for AI Agents
In app/src/test/java/com/android/heartz/ExampleUnitTest.kt lines 1 to 11, the
package declaration is com.android.heartz, but the main source code uses the
com.heartz.app namespace. To fix this, change the package declaration at the top
of the test file to match the main source root package, specifically to
com.heartz.app, ensuring consistency for IDE navigation and coverage tools.

Comment on lines +1 to +10
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="19dp"
android:viewportWidth="18"
android:viewportHeight="19">
<path
android:pathData="M0,10.262C0,8.314 -0,7.339 0.334,6.478C0.502,6.044 0.73,5.635 1.013,5.264C1.573,4.528 2.406,4.014 4.072,2.987L4.252,2.875C6.299,1.613 7.322,0.981 8.445,0.857C8.814,0.816 9.186,0.816 9.554,0.857C10.677,0.981 11.701,1.613 13.748,2.875L13.928,2.987C15.594,4.014 16.427,4.528 16.987,5.264C17.27,5.635 17.498,6.044 17.667,6.478C18,7.339 18,8.314 18,10.262C18,13.627 18,15.309 17.214,16.532C16.823,17.14 16.303,17.656 15.691,18.045C14.46,18.826 12.767,18.826 9.382,18.826H8.618C5.232,18.826 3.54,18.826 2.309,18.045C1.697,17.656 1.177,17.14 0.786,16.532C-0,15.309 0,13.627 0,10.262ZM10.999,17.331C10.518,17.335 9.983,17.336 9.382,17.336H8.618C8.017,17.336 7.482,17.335 7.001,17.331V13.699C7.001,12.595 7.896,11.7 9,11.7C10.104,11.7 10.999,12.595 10.999,13.699V17.331ZM12.499,17.295V13.699C12.499,11.767 10.932,10.2 9,10.2C7.067,10.2 5.501,11.767 5.501,13.699V17.295C5.25,17.282 5.018,17.266 4.802,17.245C3.916,17.159 3.45,17.001 3.116,16.789C2.688,16.517 2.324,16.155 2.05,15.73C1.837,15.398 1.678,14.935 1.591,14.054C1.502,13.15 1.5,11.974 1.5,10.262C1.5,8.196 1.524,7.555 1.733,7.013C1.851,6.709 2.011,6.423 2.209,6.164C2.562,5.701 3.097,5.343 4.863,4.253L5.044,4.142C6.088,3.497 6.794,3.063 7.378,2.766C7.939,2.481 8.295,2.373 8.612,2.338C8.87,2.309 9.13,2.309 9.388,2.338C9.705,2.373 10.061,2.481 10.622,2.766C11.205,3.063 11.912,3.497 12.956,4.142L13.137,4.253C14.903,5.343 15.438,5.701 15.791,6.164C15.989,6.423 16.149,6.709 16.267,7.013C16.476,7.555 16.5,8.196 16.5,10.262C16.5,11.974 16.498,13.15 16.409,14.054C16.322,14.935 16.163,15.398 15.95,15.73C15.676,16.155 15.312,16.517 14.884,16.789C14.55,17.001 14.084,17.159 13.198,17.245C12.982,17.266 12.75,17.282 12.499,17.295Z"
android:fillColor="#FFB0B0"
android:fillType="evenOdd"/>
</vector>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

벡터 드로어블 구조와 일관성 검토 필요

아이콘의 기본 구조는 올바르지만 몇 가지 개선사항이 있습니다:

  1. 크기가 18x19dp로 비대칭적입니다 - 일반적으로 아이콘은 정사각형(예: 24x24dp)을 사용합니다
  2. 색상이 하드코딩되어 있어 테마 변경에 대응하기 어렵습니다

다음과 같이 개선을 권장합니다:

-    android:width="18dp"
-    android:height="19dp"
-    android:viewportWidth="18"
-    android:viewportHeight="19">
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
  <path
      android:pathData="M0,10.262C0,8.314 -0,7.339 0.334,6.478C..."
-      android:fillColor="#FFB0B0"
+      android:fillColor="?attr/colorOnPrimary"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="19dp"
android:viewportWidth="18"
android:viewportHeight="19">
<path
android:pathData="M0,10.262C0,8.314 -0,7.339 0.334,6.478C0.502,6.044 0.73,5.635 1.013,5.264C1.573,4.528 2.406,4.014 4.072,2.987L4.252,2.875C6.299,1.613 7.322,0.981 8.445,0.857C8.814,0.816 9.186,0.816 9.554,0.857C10.677,0.981 11.701,1.613 13.748,2.875L13.928,2.987C15.594,4.014 16.427,4.528 16.987,5.264C17.27,5.635 17.498,6.044 17.667,6.478C18,7.339 18,8.314 18,10.262C18,13.627 18,15.309 17.214,16.532C16.823,17.14 16.303,17.656 15.691,18.045C14.46,18.826 12.767,18.826 9.382,18.826H8.618C5.232,18.826 3.54,18.826 2.309,18.045C1.697,17.656 1.177,17.14 0.786,16.532C-0,15.309 0,13.627 0,10.262ZM10.999,17.331C10.518,17.335 9.983,17.336 9.382,17.336H8.618C8.017,17.336 7.482,17.335 7.001,17.331V13.699C7.001,12.595 7.896,11.7 9,11.7C10.104,11.7 10.999,12.595 10.999,13.699V17.331ZM12.499,17.295V13.699C12.499,11.767 10.932,10.2 9,10.2C7.067,10.2 5.501,11.767 5.501,13.699V17.295C5.25,17.282 5.018,17.266 4.802,17.245C3.916,17.159 3.45,17.001 3.116,16.789C2.688,16.517 2.324,16.155 2.05,15.73C1.837,15.398 1.678,14.935 1.591,14.054C1.502,13.15 1.5,11.974 1.5,10.262C1.5,8.196 1.524,7.555 1.733,7.013C1.851,6.709 2.011,6.423 2.209,6.164C2.562,5.701 3.097,5.343 4.863,4.253L5.044,4.142C6.088,3.497 6.794,3.063 7.378,2.766C7.939,2.481 8.295,2.373 8.612,2.338C8.87,2.309 9.13,2.309 9.388,2.338C9.705,2.373 10.061,2.481 10.622,2.766C11.205,3.063 11.912,3.497 12.956,4.142L13.137,4.253C14.903,5.343 15.438,5.701 15.791,6.164C15.989,6.423 16.149,6.709 16.267,7.013C16.476,7.555 16.5,8.196 16.5,10.262C16.5,11.974 16.498,13.15 16.409,14.054C16.322,14.935 16.163,15.398 15.95,15.73C15.676,16.155 15.312,16.517 14.884,16.789C14.55,17.001 14.084,17.159 13.198,17.245C12.982,17.266 12.75,17.282 12.499,17.295Z"
android:fillColor="#FFB0B0"
android:fillType="evenOdd"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M0,10.262C0,8.314 -0,7.339 0.334,6.478C0.502,6.044 0.73,5.635 1.013,5.264C1.573,4.528 2.406,4.014 4.072,2.987L4.252,2.875C6.299,1.613 7.322,0.981 8.445,0.857C8.814,0.816 9.186,0.816 9.554,0.857C10.677,0.981 11.701,1.613 13.748,2.875L13.928,2.987C15.594,4.014 16.427,4.528 16.987,5.264C17.27,5.635 17.498,6.044 17.667,6.478C18,7.339 18,8.314 18,10.262C18,13.627 18,15.309 17.214,16.532C16.823,17.14 16.303,17.656 15.691,18.045C14.46,18.826 12.767,18.826 9.382,18.826H8.618C5.232,18.826 3.54,18.826 2.309,18.045C1.697,17.656 1.177,17.14 0.786,16.532C-0,15.309 0,13.627 0,10.262ZM10.999,17.331C10.518,17.335 9.983,17.336 9.382,17.336H8.618C8.017,17.336 7.482,17.335 7.001,17.331V13.699C7.001,12.595 7.896,11.7 9,11.7C10.104,11.7 10.999,12.595 10.999,13.699V17.331ZM12.499,17.295V13.699C12.499,11.767 10.932,10.2 9,10.2C7.067,10.2 5.501,11.767 5.501,13.699V17.295C5.25,17.282 5.018,17.266 4.802,17.245C3.916,17.159 3.45,17.001 3.116,16.789C2.688,16.517 2.324,16.155 2.05,15.73C1.837,15.398 1.678,14.935 1.591,14.054C1.502,13.15 1.5,11.974 1.5,10.262C1.5,8.196 1.524,7.555 1.733,7.013C1.851,6.709 2.011,6.423 2.209,6.164C2.562,5.701 3.097,5.343 4.863,4.253L5.044,4.142C6.088,3.497 6.794,3.063 7.378,2.766C7.939,2.481 8.295,2.373 8.612,2.338C8.87,2.309 9.13,2.309 9.388,2.338C9.705,2.373 10.061,2.481 10.622,2.766C11.205,3.063 11.912,3.497 12.956,4.142L13.137,4.253C14.903,5.343 15.438,5.701 15.791,6.164C15.989,6.423 16.149,6.709 16.267,7.013C16.476,7.555 16.5,8.196 16.5,10.262C16.5,11.974 16.498,13.15 16.409,14.054C16.322,14.935 16.163,15.398 15.95,15.73C15.676,16.155 15.312,16.517 14.884,16.789C14.55,17.001 14.084,17.159 13.198,17.245C12.982,17.266 12.75,17.282 12.499,17.295Z"
android:fillColor="?attr/colorOnPrimary"
android:fillType="evenOdd"/>
</vector>
🤖 Prompt for AI Agents
In app/src/main/res/drawable/ic_home.xml lines 1 to 10, the vector drawable uses
a non-square size of 18x19dp and hardcoded fill color #FFB0B0, which reduces
consistency and theming flexibility. Update the android:width and android:height
attributes to a standard square size like 24x24dp, and replace the hardcoded
android:fillColor with a theme attribute reference (e.g.,
?attr/colorControlNormal) to support dynamic theming.

Comment on lines +1 to +16
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M5.291,4.304C5.291,1.835 7.496,0 10,0C12.504,0 14.709,1.835 14.709,4.304C14.709,6.773 12.504,8.608 10,8.608C7.496,8.608 5.291,6.773 5.291,4.304ZM10,1.519C8.124,1.519 6.778,2.858 6.778,4.304C6.778,5.75 8.124,7.089 10,7.089C11.876,7.089 13.222,5.75 13.222,4.304C13.222,2.858 11.876,1.519 10,1.519Z"
android:fillColor="#FE9191"
android:fillAlpha="0.7"
android:fillType="evenOdd"/>
<path
android:pathData="M4.933,11.208C8.043,9.242 11.957,9.242 15.067,11.208C16.889,12.361 18,14.41 18,16.618V17.132C18,17.334 18,17.495 17.985,17.64C17.854,18.873 16.913,19.849 15.718,19.984C15.577,20 15.422,20 15.226,20H4.774C4.579,20 4.423,20 4.282,19.984C3.087,19.849 2.146,18.873 2.015,17.64C2,17.495 2,17.334 2,17.132V16.618C2,14.41 3.111,12.361 4.933,11.208ZM14.285,12.527C11.654,10.864 8.346,10.864 5.715,12.527C4.339,13.398 3.498,14.947 3.498,16.618V17.09C3.498,17.353 3.499,17.422 3.505,17.472C3.559,17.986 3.952,18.391 4.446,18.447C4.494,18.452 4.56,18.453 4.814,18.453H15.186C15.44,18.453 15.507,18.452 15.554,18.447C16.048,18.391 16.441,17.986 16.495,17.472C16.5,17.422 16.502,17.353 16.502,17.09V16.618C16.502,14.947 15.661,13.398 14.285,12.527Z"
android:fillColor="#FE9191"
android:fillAlpha="0.7"
android:fillType="evenOdd"/>
</vector>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

아이콘 일관성과 접근성 개선 필요

사용자 아이콘의 구현에서 몇 가지 개선점이 있습니다:

  1. 색상이 하드코딩되어 테마 대응이 어려움
  2. fillAlpha="0.7"로 투명도를 적용했는데, 이는 접근성에 영향을 줄 수 있음
  3. 다른 아이콘들과 크기 일관성 부족
  <path
      android:pathData="M5.291,4.304C..."
-      android:fillColor="#FE9191"
-      android:fillAlpha="0.7"
+      android:fillColor="?attr/colorOnSurface"
      android:fillType="evenOdd"/>
  <path
      android:pathData="M4.933,11.208C..."
-      android:fillColor="#FE9191"
-      android:fillAlpha="0.7"
+      android:fillColor="?attr/colorOnSurface"
      android:fillType="evenOdd"/>

투명도가 필요하다면 테마에서 알파값이 포함된 색상을 정의하는 것이 좋습니다.

🤖 Prompt for AI Agents
In app/src/main/res/drawable/ic_user.xml lines 1 to 16, the user icon has
hardcoded fill colors and uses fillAlpha for transparency, which hinders theme
adaptability and accessibility. Replace the hardcoded color values with theme
attribute references to support dynamic theming, and remove the fillAlpha
attribute by defining colors with the desired alpha directly in the theme. Also,
verify and adjust the icon size to maintain consistency with other icons in the
app.

Comment on lines +13 to +27
@Composable
fun QuestScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "quest Screen",
color = Color.Black
)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Compose 테마 사용 및 일관성 개선 필요

현재 구현은 기본적으로 작동하지만 몇 가지 개선이 필요합니다:

  1. 하드코딩된 색상 사용으로 테마 적용이 어려움
  2. 텍스트 대소문자 일관성 부족 ("quest Screen" → "Quest Screen")
  3. Material Design 테마 활용 부족
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface

 @Composable
 fun QuestScreen() {
-    Column(
-        modifier = Modifier
-            .fillMaxSize()
-            .background(Color.White),
-        horizontalAlignment = Alignment.CenterHorizontally,
-        verticalArrangement = Arrangement.Center
-    ) {
-        Text(
-            text = "quest Screen",
-            color = Color.Black
-        )
-    }
+    Surface(
+        modifier = Modifier.fillMaxSize(),
+        color = MaterialTheme.colorScheme.background
+    ) {
+        Column(
+            modifier = Modifier.fillMaxSize(),
+            horizontalAlignment = Alignment.CenterHorizontally,
+            verticalArrangement = Arrangement.Center
+        ) {
+            Text(
+                text = "Quest Screen",
+                style = MaterialTheme.typography.headlineMedium,
+                color = MaterialTheme.colorScheme.onBackground
+            )
+        }
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Composable
fun QuestScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "quest Screen",
color = Color.Black
)
}
}
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
@Composable
fun QuestScreen() {
- Column(
- modifier = Modifier
- .fillMaxSize()
- .background(Color.White),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center
- ) {
- Text(
- text = "quest Screen",
- color = Color.Black
- )
- }
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text = "Quest Screen",
+ style = MaterialTheme.typography.headlineMedium,
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ }
+ }
}
🤖 Prompt for AI Agents
In app/src/main/java/com/heartz/app/presentation/quest/QuestScreen.kt around
lines 13 to 27, the code uses hardcoded colors and inconsistent text casing
which reduces theme consistency and Material Design compliance. Replace
hardcoded Color.White and Color.Black with colors from the MaterialTheme color
palette, and update the text to "Quest Screen" with proper capitalization. Also,
ensure the composable uses MaterialTheme styling conventions for background and
text colors to improve theme integration and visual consistency.

Column(
modifier =
Modifier
.background(Color.White)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

하드코딩된 색상을 테마 색상으로 변경하세요.

Color.White가 하드코딩되어 있어 다크 테마 지원이나 디자인 시스템과의 일관성에 문제가 있을 수 있습니다.

 modifier =
 Modifier
-    .background(Color.White)
+    .background(MaterialTheme.colorScheme.surface)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.background(Color.White)
modifier =
Modifier
.background(MaterialTheme.colorScheme.surface)
🤖 Prompt for AI Agents
In app/src/main/java/com/heartz/app/presentation/main/component/MainBottomBar.kt
at line 52, replace the hardcoded Color.White with a color from the app's theme
to support dark mode and maintain design consistency. Use the appropriate theme
color reference instead of the fixed white color.

Comment on lines +47 to +57
companion object {
@Composable
fun find(predicate: @Composable (MainTabRoute) -> Boolean): MainNavTab? {
return entries.find { predicate(it.route) }
}

@Composable
fun contains(predicate: @Composable (Route) -> Boolean): Boolean {
return entries.map { it.route }.any { predicate(it) }
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

불필요한 @composable 어노테이션을 제거하세요.

Companion object의 findcontains 함수들이 @composable로 표시되어 있지만, 실제로는 Composition에 의존하지 않는 순수 함수들입니다. 이는 불필요한 recomposition을 유발할 수 있습니다.

 companion object {
-    @Composable
     fun find(predicate: (MainTabRoute) -> Boolean): MainNavTab? {
         return entries.find { predicate(it.route) }
     }

-    @Composable
     fun contains(predicate: (Route) -> Boolean): Boolean {
         return entries.map { it.route }.any { predicate(it) }
     }
 }

또한, 호출하는 곳에서도 @composable 어노테이션을 제거해야 합니다:

MainNavigator.kt의 사용 부분:

 val currentTab: MainNavTab?
     @Composable get() =
         MainNavTab.find { tab ->
             currentDestination?.hasRoute(tab::class) == true
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
companion object {
@Composable
fun find(predicate: @Composable (MainTabRoute) -> Boolean): MainNavTab? {
return entries.find { predicate(it.route) }
}
@Composable
fun contains(predicate: @Composable (Route) -> Boolean): Boolean {
return entries.map { it.route }.any { predicate(it) }
}
}
companion object {
fun find(predicate: (MainTabRoute) -> Boolean): MainNavTab? {
return entries.find { predicate(it.route) }
}
fun contains(predicate: (Route) -> Boolean): Boolean {
return entries.map { it.route }.any { predicate(it) }
}
}
🤖 Prompt for AI Agents
In app/src/main/java/com/heartz/app/presentation/main/MainNavTab.kt between
lines 47 and 57, remove the unnecessary @Composable annotations from the
companion object's find and contains functions because they are pure functions
that do not depend on Composition. Also, update all call sites, such as in
MainNavigator.kt, to remove the @Composable annotation when invoking these
functions to prevent unnecessary recompositions.

Comment on lines +13 to +16
val properties =
Properties().apply {
load(project.rootProject.file("local.properties").inputStream())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

local.properties 파일 누락 시 에러 처리를 추가하세요.

현재 local.properties 파일이 존재하지 않을 경우 빌드가 실패할 수 있습니다.

 val properties =
     Properties().apply {
-        load(project.rootProject.file("local.properties").inputStream())
+        val localPropertiesFile = project.rootProject.file("local.properties")
+        if (localPropertiesFile.exists()) {
+            load(localPropertiesFile.inputStream())
+        }
     }
🤖 Prompt for AI Agents
In app/build.gradle.kts around lines 13 to 16, the code loads local.properties
without checking if the file exists, which can cause a build failure if the file
is missing. Add a check to verify the existence of local.properties before
loading it, and handle the case where the file is absent by either skipping the
load or providing a clear error message to prevent the build from failing
unexpectedly.

Comment on lines +31 to +36
buildConfigField("String", "BASE_URL", properties["base.url"].toString())
buildConfigField(
"String",
"KAKAO_NATIVE_APP_KEY",
properties["kakao.native.app.key"].toString()
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

BuildConfig에서 민감한 정보 노출을 방지하세요.

buildConfigField를 통해 API 키와 URL이 APK에 평문으로 노출될 수 있습니다. 이는 보안상 위험할 수 있습니다.

더 안전한 방법들을 고려해보세요:

  1. Gradle 속성 사용: 런타임에 리소스에서 읽기
  2. 암호화: 키를 암호화하여 저장
  3. 서버 프록시: 민감한 API 호출을 서버를 통해 프록시
-        buildConfigField("String", "BASE_URL", properties["base.url"].toString())
-        buildConfigField(
-            "String",
-            "KAKAO_NATIVE_APP_KEY",
-            properties["kakao.native.app.key"].toString()
-        )
+        // 리소스 파일을 통한 방법 고려
+        resValue("string", "base_url", properties["base.url"]?.toString() ?: "\"\"")
+        manifestPlaceholders["kakaoAppKey"] = properties["kakao.native.app.key"]?.toString() ?: ""
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
buildConfigField("String", "BASE_URL", properties["base.url"].toString())
buildConfigField(
"String",
"KAKAO_NATIVE_APP_KEY",
properties["kakao.native.app.key"].toString()
)
// 리소스 파일을 통한 방법 고려
resValue("string", "base_url", properties["base.url"]?.toString() ?: "\"\"")
manifestPlaceholders["kakaoAppKey"] = properties["kakao.native.app.key"]?.toString() ?: ""
🤖 Prompt for AI Agents
In app/build.gradle.kts around lines 31 to 36, sensitive information like API
keys and URLs are exposed in plain text via buildConfigField, which poses a
security risk. To fix this, remove these sensitive values from buildConfigField
and instead load them securely at runtime from encrypted resources or
environment variables. Alternatively, consider using a server proxy for API
calls to avoid embedding keys in the APK altogether.

Comment on lines +33 to +51
fun navigate(tab: MainNavTab) {
val navOptions =
navOptions {
navController.currentDestination?.route?.let {
popUpTo(it) {
inclusive = true
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
when (tab) {
MainNavTab.QUEST -> navController.navigateToQuest(navOptions)
MainNavTab.HOME -> navController.navigateToHome(navOptions)
MainNavTab.FIGURE -> navController.navigateToFigure(navOptions)
MainNavTab.MYPAGE -> navController.navigateToMypage(navOptions)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

네비게이션 로직의 복잡성을 개선하세요.

현재 navigate 함수의 popUpTo 로직이 복잡하고 예상치 못한 동작을 일으킬 수 있습니다. 특히 inclusive = truesaveState = true를 함께 사용하는 것은 상태 관리에 문제를 일으킬 수 있습니다.

 fun navigate(tab: MainNavTab) {
     val navOptions =
         navOptions {
-            navController.currentDestination?.route?.let {
-                popUpTo(it) {
-                    inclusive = true
-                    saveState = true
-                }
-            }
+            popUpTo(startDestination) {
+                saveState = true
+            }
             launchSingleTop = true
             restoreState = true
         }
     when (tab) {
         MainNavTab.QUEST -> navController.navigateToQuest(navOptions)
         MainNavTab.HOME -> navController.navigateToHome(navOptions)
         MainNavTab.FIGURE -> navController.navigateToFigure(navOptions)
         MainNavTab.MYPAGE -> navController.navigateToMypage(navOptions)
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/src/main/java/com/heartz/app/presentation/main/MainNavigator.kt around
lines 33 to 51, the navigate function's popUpTo logic is overly complex and may
cause unexpected behavior due to using inclusive = true and saveState = true
together. Simplify the popUpTo block by removing inclusive = true or adjusting
the logic to avoid conflicting state management, ensuring consistent navigation
state restoration without unintended side effects.

@36-APPJAM-HEARTZ 36-APPJAM-HEARTZ deleted a comment from coderabbitai bot Jun 26, 2025
@36-APPJAM-HEARTZ 36-APPJAM-HEARTZ deleted a comment from coderabbitai bot Jun 26, 2025
@36-APPJAM-HEARTZ 36-APPJAM-HEARTZ deleted a comment from coderabbitai bot Jun 26, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/src/main/java/com/heartz/app/data/datasource/local/DummyLocalDataSource.kt (1)

5-5: TODO 주석을 추후 제거해주세요.

임시 구현임을 나타내는 TODO 주석이 있습니다. 실제 구현이 완료되면 이 주석을 제거하고 적절한 문서화 주석으로 대체하는 것을 권장합니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 367370b and da22a30.

📒 Files selected for processing (4)
  • app/src/main/java/com/heartz/app/data/datasource/local/DummyLocalDataSource.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/datasourceimpl/local/DummyLocalDataSourceImpl.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/di/DataSourceModule.kt (1 hunks)
  • app/src/main/java/com/heartz/app/data/service/DummyService.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/main/java/com/heartz/app/data/service/DummyService.kt
  • app/src/main/java/com/heartz/app/data/di/DataSourceModule.kt
  • app/src/main/java/com/heartz/app/data/datasourceimpl/local/DummyLocalDataSourceImpl.kt
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (1)
app/src/main/java/com/heartz/app/data/datasource/local/DummyLocalDataSource.kt (1)

6-12: 인터페이스 구조가 잘 설계되었습니다.

로컬 데이터 소스의 계약을 명확히 정의하고 있습니다:

  • Flow<Boolean> 타입의 isLogin 속성으로 반응형 상태 관찰
  • suspend fun setIsLogin()으로 비동기 상태 설정
  • suspend fun clear()로 데이터 정리

초기 프로젝트 세팅 단계에서 "Dummy" 명명은 적절하며, 추후 실제 구현 시 적절한 이름으로 변경하면 됩니다.

@fredleeJH fredleeJH merged commit 37120ef into develop Jun 26, 2025
2 checks passed
@fredleeJH fredleeJH deleted the init/#1-project-setting branch August 1, 2025 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚙️ [SETTING] 프로젝트 기초 세팅 🦊 종훈 종훈

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[INIT] 프로젝트 기초 세팅

5 participants