Skip to content

[INIT/#1] 프로젝트 기초 세팅#2

Merged
nhyeonii merged 27 commits intodevelopfrom
init/#1-project-setting
Jan 5, 2026
Merged

[INIT/#1] 프로젝트 기초 세팅#2
nhyeonii merged 27 commits intodevelopfrom
init/#1-project-setting

Conversation

@nhyeonii
Copy link
Copy Markdown
Contributor

@nhyeonii nhyeonii commented Jan 2, 2026

Related issue 🛠

Work Description ✏️

  • 필요한 라이브러리들을 추가했어요.
  • 필요한 유틸 / 확장 함수들을 추가했어요.
  • ktLint를 적용했어요.
  • CI 워크 플로우를 설정했어요.
  • 기본 네비게이션을 설정했어요.
  • 구조에 맞춰 패키징을 정리했어요.
  • 더미 파일을 추가했어요.

Screenshot 📸

패키징 구조

Uncompleted Tasks 😅

  • N/A

To Reviewers 📢

프로젝트 초기 세팅을 진행했습니다!
구조나 패키징, 라이브러리 구성 위주로 봐주시면 좋을 것 같아요.
더 좋은 방법이나 개선 아이디어가 떠오르시면 자유롭게 리뷰 남겨주세요!
질문도 환영입니다!

Summary by CodeRabbit

  • 새로운 기능

    • 하단 네비게이션(홈·캘린더·챌린지·마이) 및 각 화면 컴포저블 추가
    • 앱 테마·타이포·컬러 디자인 시스템 적용 및 런처 아이콘 추가
    • 네트워크 응답 모델, 리포지토리/데이터소스, DI와 데이터 저장소 기초 추가
    • 애플리케이션 진입점(앱 클래스·메인 액티비티) 추가
  • Chores

    • 프로젝트 빌드·설정(버전 카탈로그·Gradle 래퍼·properties) 및 CI 워크플로우 추가
    • 코드 스타일/정적검사 설정(kotlin/ktlint), .gitignore 및 코드 소유자 구성
  • Tests

    • 단위 테스트 및 기기(instrumented) 테스트 예제 추가
  • 문서/로컬라이제이션

    • 리뷰 자동화 한국어 로컬라이제이션 설정 추가

✏️ Tip: You can customize this high-level summary in your review settings.

@nhyeonii nhyeonii self-assigned this Jan 2, 2026
@nhyeonii nhyeonii added INIT🛠️ 프로젝트 초기 설정 나현🍒 나현 담당 labels Jan 2, 2026
@nhyeonii nhyeonii requested review from sohee6989 and usuuhyn January 2, 2026 07:34
@nhyeonii nhyeonii requested a review from hyeminililo January 2, 2026 12:28
Copy link
Copy Markdown
Contributor

@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.

수고하셨습니다!!
더미 세팅도 해놔주셔서 나중에 작업 편하게 할 수 있을 것 같네요😄
감사합니다아~~

아 추가로 혹시 코드래빗 넣는 거 어떠세요??ㅎㅎ
가끔 뭐 놓치는거 코드래빗이 잡아줬어가지고 편했어서요!!

import kotlinx.coroutines.flow.stateIn

@Stable
class MainAppState(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MainAppState라는 이름이 조금 애매한 것 같다는 생각도 드는데
이렇게 클래스명을 지으신 이유가 궁금합니다!

Copy link
Copy Markdown
Contributor Author

@nhyeonii nhyeonii Jan 3, 2026

Choose a reason for hiding this comment

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

좋은 질문이네용 !! MainAppState라는 네이밍을 선택한 이유를 설명드리겠습니다.
이 클래스는 단순히 네비게이션만 담당하는 게 아니라 앱의 UI 상태를 종합적으로 관리하기 때문에 MainAppState라는 이름을 사용했습니다. 코드를 보시면 navController 외에도 currentTab, isBottomBarVisible 같은 UI 상태들을 함께 관리하고 있죠. 만약 MainNavigator라고 했다면 네비게이션 기능만 강조되어서 이런 UI 상태 관리 역할이 명확하게 드러나지 않을 것 같았습니다!
MainAppState라는 이름은 이 클래스가 Main 화면의 최상위 레벨에서 앱의 전반적인 상태를 관리한다는 의미를 가장 명확하게 전달한다고 생각합니다. 나중에 snackbarHostState나 isNetworkAvailable 같은 다른 상태들을 추가할 때도 자연스럽게 확장할 수 있구요!

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

인터넷 퍼미션 넣어놓는거 어떠세요??
나중에 필요하면 넣을까요??

Copy link
Copy Markdown
Contributor Author

@nhyeonii nhyeonii Jan 3, 2026

Choose a reason for hiding this comment

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

왁 코드레빗이랑 함께 추가해둘게용 ~~

Copy link
Copy Markdown
Contributor

@usuuhyn usuuhyn left a comment

Choose a reason for hiding this comment

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

초기 세팅 너무 깔끔하네요!
감사합니다 🫶

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 3, 2026

Walkthrough

앱 초기화: Gradle 설정·래퍼, CI·CODEOWNERS, Android 매니페스트, Hilt·Timber 초기화, 네트워크·DataStore·레포지토리/데이터 소스, Compose 디자인 시스템·네비게이션·탭 UI, 공통 확장유틸, 리소스 및 테스트 파일을 추가했습니다.

Changes

Cohort / File(s) 변경 사항 요약
프로젝트 메타·빌드 설정
/.coderabbit.yaml, /.gitignore, /build.gradle.kts, /gradle.properties, /gradle/libs.versions.toml, /settings.gradle.kts
프로젝트 레벨 구성(버전 카탈로그, Gradle 설정, CodeRabbit 자동리뷰 설정, .gitignore) 추가.
CI / IDE / 코드 소유자
/.github/CODEOWNERS, /.github/workflows/pr_checker.yml, /.run/Cherrish [ktLintCheck].run.xml, /.run/Cherrish [ktLintFormat].run.xml
CODEOWNERS 등록, PR 체크 워크플로(ktlint, assembleDebug) 및 IDE용 Gradle 실행 구성 추가.
Gradle 래퍼 및 스크립트
/gradle/wrapper/gradle-wrapper.properties, /gradlew, /gradlew.bat
Gradle 래퍼 구성 및 실행 스크립트 추가.
앱 모듈 빌드 설정
app/build.gradle.kts, app/.gitignore
앱 모듈 빌드스크립트(플러그인, 의존성, ktlint 설정) 및 앱용 .gitignore 추가.
앱 매니페스트·진입점
app/src/main/AndroidManifest.xml, app/src/main/java/com/cherrish/android/CherrishApplication.kt, app/src/main/java/com/cherrish/android/presentation/main/MainActivity.kt
INTERNET 권한, Application(Hilt, Timber 초기화, 낮모드 강제) 및 MainActivity(Compose 진입) 추가.
공통 확장·유틸
app/src/main/java/.../FlowExt.kt, ModifierExt.kt, RunCatchingExt.kt, StateFlowExt.kt, .../SuspendRunCatching.kt
Compose용 Flow/Modifier 확장, Result 실패 로깅 확장, MutableStateFlow 업데이트 헬퍼, suspendRunCatching 유틸 추가.
UI 상태·네비게이션 타입
app/src/main/java/.../state/UiState.kt, .../navigation/Route.kt, .../navigation/MainTabRoute.kt
UiState sealed 타입과 Route/ MainTabRoute 인터페이스 추가.
디자인 시스템
app/src/main/java/.../designsystem/theme/Color.kt, Theme.kt, Type.kt
색상 상수, Material3 기반 테마(동적 색 포함), Typography 추가.
로컬 DataStore DI
app/src/main/java/.../local/datastore/PreferencesDataStore.kt, .../local/di/DataStoreModule.kt
Preferences DataStore 래퍼 및 Hilt 모듈 제공자 추가.
네트워크 구성
app/src/main/java/.../network/BaseResponse.kt, JsonStringExt.kt, NetworkModule.kt
BaseResponse 직렬화 DTO, JSON 검사 확장, Retrofit/OkHttp/Json 제공 모듈(로깅 인터셉터 포함) 추가.
데이터 모델·DTO
app/src/main/java/.../data/model/DummyModel.kt, .../remote/dto/response/DummyResponseDto.kt
도메인 모델과 응답 DTO, DTO→모델 매퍼 추가.
서비스·데이터소스·구현
app/src/main/java/.../remote/service/DummyService.kt, .../datasource/DummyDataSource.kt, .../datasourceimpl/DummyDataSourceImpl.kt
Retrofit 서비스 인터페이스, 데이터 소스 계약 및 구현 추가.
레포지토리·DI 바인딩
app/src/main/java/.../repository/DummyRepository.kt, .../repositoryimpl/DummyRepositoryImpl.kt, app/src/main/java/.../data/di/{DataSourceModule,RepositoryModule,ServiceModule}.kt
Repository 인터페이스/구현 및 Hilt 바인딩 모듈 추가.
프레젠테이션: 메인 네비/상태/컴포넌트
app/src/main/java/.../presentation/main/{MainAppState.kt,MainScreen.kt,MainTab.kt}, .../component/MainBottomBar.kt
MainAppState(탭 상태·네비 관리), MainScreen(Scaffold+NavHost), MainTab enum, 하단 바 컴포넌트 추가.
프레젠테이션: 탭 화면·네비게이터
app/src/main/java/.../presentation/{home,calendar,challenge,mypage}/*
Home/Calendar/Challenge/MyPage 간단한 Compose 라우트와 각 탭용 NavGraph/네비게이터 추가.
리소스: 런처 아이콘·값·백업 규칙
app/src/main/res/drawable/*, mipmap-anydpi/*, values/colors.xml, strings.xml, themes.xml, res/xml/{backup_rules,data_extraction_rules}.xml
어댑티브 런처 아이콘, 색상/문자열/테마, 백업·데이터 추출 규칙 리소스 추가.
테스트 샘플
app/src/test/.../ExampleUnitTest.kt, app/src/androidTest/.../ExampleInstrumentedTest.kt
로컬 단위 테스트 및 기기용 인스트루먼트 테스트 샘플 추가.
앱용 ProGuard 템플릿
app/proguard-rules.pro
ProGuard 주석/템플릿 파일 추가.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User as 사용자
  participant MainActivity as MainActivity
  participant CherrishTheme as CherrishTheme
  participant MainAppState as MainAppState
  participant NavController as NavController
  participant NavHost as NavHost

  Note over MainActivity,MainAppState: 앱 시작 — Compose 설정 및 상태 초기화
  User->>MainActivity: 앱 실행 / 탭 선택
  MainActivity->>CherrishTheme: setContent { CherrishTheme { ... } }
  MainActivity->>MainAppState: onTabSelected(tab)
  MainAppState->>NavController: navigateTo{TargetTab}()
  NavController->>NavHost: 변경 요청 (popUpTo/restoreState/launchSingleTop)
  NavHost-->>MainActivity: 해당 Route의 Composable 렌더링
  Note right of MainAppState: currentTab / isBottomBarVisible는 StateFlow로 관찰되어 UI에 반영
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 프로젝트 기초 세팅이라는 주요 변경사항을 명확히 반영하고 있으며, 모든 파일 요약에서 확인되는 초기 설정 작업 내용과 잘 부합합니다.
Description check ✅ Passed PR 설명이 제공된 템플릿의 대부분 섹션을 포함하고 있으며, 관련 이슈, 작업 내용, 스크린샷, 미완료 항목, 리뷰어 요청사항이 모두 작성되어 있습니다.
Linked Issues check ✅ Passed PR의 모든 변경사항(라이브러리 추가, 유틸/확장 함수, ktLint 설정, CI 워크플로우, 네비게이션, 패키징 구조, 더미 파일)이 linked issue #1의 프로젝트 기초 세팅 목표를 완전히 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 프로젝트 초기 설정과 직접 관련되어 있으며, 범위를 벗어난 불필요한 수정은 발견되지 않습니다.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@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: 13

🧹 Nitpick comments (29)
app/src/main/java/com/cherrish/android/core/common/state/UiState.kt (1)

3-13: Failure 상태에 에러 정보 추가를 고려하세요.

현재 구현은 일반적인 UI 상태 패턏을 잘 따르고 있습니다. sealed interface와 공변성 사용이 적절합니다.

다만, Failure 상태가 에러 정보(에러 메시지, 예외 객체, 에러 코드 등)를 포함하지 않아 실제 사용 시 에러 처리와 사용자 피드백 제공에 제한이 있을 수 있습니다.

🔎 에러 정보를 포함하는 개선안
 sealed interface UiState<out T> {
     data object Empty : UiState<Nothing>
 
     data object Loading : UiState<Nothing>
 
     data class Success<T>(
         val data: T
     ) : UiState<T>
 
-    data object Failure : UiState<Nothing>
+    data class Failure(
+        val error: Throwable? = null,
+        val message: String? = null
+    ) : UiState<Nothing>
 }
app/src/main/java/com/cherrish/android/core/local/datastore/PreferencesDataStore.kt (1)

7-14: 빈 래퍼 클래스의 필요성을 검토해주세요.

현재 PreferencesDataStore 클래스는 주석으로 예시만 포함하고 있으며 실제 구현된 메서드가 없습니다. 초기 세팅 단계에서 구조만 잡아둔 것으로 보이는데, 두 가지 옵션을 고려해볼 수 있습니다:

  1. 당장 사용 예정이라면 주석의 예시 메서드를 실제로 구현
  2. 나중에 구현 예정이라면 클래스 상단에 해당 의도를 명시하는 KDoc 추가

현재 상태로는 DataStore<Preferences>를 직접 주입받아 사용하는 것과 차이가 없어 보입니다.

💡 KDoc 추가 예시
+/**
+ * DataStore Preferences를 관리하는 래퍼 클래스입니다.
+ * 향후 토큰 저장/조회 등의 기능이 추가될 예정입니다.
+ */
 class PreferencesDataStore @Inject constructor(
     private val dataStore: DataStore<Preferences>
 ) {
app/src/main/java/com/cherrish/android/core/local/di/DataStoreModule.kt (1)

29-33: 불필요한 provide 메서드를 제거하는 것을 권장합니다.

PreferencesDataStore는 이미 @Inject 생성자를 가지고 있어 Hilt가 자동으로 의존성을 제공할 수 있습니다. 따라서 이 @Provides 메서드는 중복됩니다.

Hilt는 다음 조건을 만족하는 클래스를 자동으로 제공합니다:

  • @Inject 생성자 보유
  • 생성자의 모든 파라미터가 의존성 그래프에 존재

현재 PreferencesDataStore는 이 조건을 만족하므로 별도의 provide 메서드 없이도 주입 가능합니다.

🔎 제안하는 리팩토링
     @Provides
     @Singleton
     fun provideDataStore(
         @ApplicationContext context: Context
     ): DataStore<Preferences> = context.dataStore
-
-    @Provides
-    @Singleton
-    fun providePreferencesDataStore(
-        dataStore: DataStore<Preferences>
-    ): PreferencesDataStore = PreferencesDataStore(dataStore)
 }

이렇게 수정해도 PreferencesDataStore는 여전히 다른 클래스에서 @Inject로 주입받을 수 있습니다.

app/src/main/java/com/cherrish/android/core/common/extension/FlowExt.kt (1)

11-33: KDoc 문서화를 추가하는 것을 권장합니다.

재사용 가능한 유틸리티 함수에 대한 문서화가 없어 다른 개발자가 함수의 목적과 사용법을 이해하기 어렵습니다. 특히 key 파라미터의 용도와 사용 시기에 대한 설명이 필요합니다.

🔎 제안하는 문서화 예시
+/**
+ * Lifecycle-aware하게 Flow를 collect하는 Composable 확장 함수입니다.
+ * Flow는 lifecycle이 STARTED 상태일 때만 수집되며, 자동으로 취소됩니다.
+ *
+ * @param key LaunchedEffect를 재시작할 추가 키. Flow나 lifecycle 외에
+ *            추가로 effect를 재시작하고 싶을 때 사용합니다.
+ * @param collector Flow에서 방출된 값을 처리하는 suspend 함수
+ */
 @Composable
 fun <T> Flow<T>.collectSideEffect(
     key: Any? = Unit,
     collector: suspend (T) -> Unit
 ) {

+/**
+ * Lifecycle-aware하게 Flow를 collectLatest로 수집하는 Composable 확장 함수입니다.
+ * 새로운 값이 방출되면 이전 collector 실행을 취소하고 최신 값만 처리합니다.
+ * Flow는 lifecycle이 STARTED 상태일 때만 수집됩니다.
+ *
+ * @param key LaunchedEffect를 재시작할 추가 키. Flow나 lifecycle 외에
+ *            추가로 effect를 재시작하고 싶을 때 사용합니다.
+ * @param collector Flow에서 방출된 최신 값을 처리하는 suspend 함수
+ */
 @Composable
 fun <T> Flow<T>.collectLatestSideEffect(
     key: Any? = Unit,
     collector: suspend (T) -> Unit
 ) {
app/src/main/java/com/cherrish/android/core/network/JsonStringExt.kt (1)

3-5: JSON 타입 확인 로직 개선 권장

현재 구현은 JSON 유효성을 완전히 검증하지 않습니다. isJsonObject()isJsonArray()는 시작/끝 문자만 확인하므로 "{invalid}", "[incomplete" 같은 잘못된 문자열도 true를 반환합니다. 이들 함수는 NetworkModule.kt의 HttpLoggingInterceptor에서 JSONObject()/JSONArray() 생성자 호출 전 가벼운 타입 체크로 사용 중이므로, 생성자가 실제 검증을 수행하지만, 다음과 같은 개선이 가능합니다:

  1. 가독성 개선: this?.startsWith() == true && this.endsWith() 패턴은 short-circuit으로 안전하지만, 명시적 null 체크가 더 명확합니다.
  2. 공백 처리: " { } " 같은 앞뒤 공백이 있는 경우 정상적으로 처리하도록 trim() 추가 검토
  3. KDoc 추가: 이것이 경량 체크(완전한 JSON 검증 아님)임을 문서화
가독성 개선 제안
-fun String?.isJsonObject(): Boolean = this?.startsWith("{") == true && this.endsWith("}")
+fun String?.isJsonObject(): Boolean = 
+    this != null && this.startsWith("{") && this.endsWith("}")

-fun String?.isJsonArray(): Boolean = this?.startsWith("[") == true && this.endsWith("]")
+fun String?.isJsonArray(): Boolean = 
+    this != null && this.startsWith("[") && this.endsWith("]")

또는 공백 처리 포함:

-fun String?.isJsonObject(): Boolean = this?.startsWith("{") == true && this.endsWith("}")
+fun String?.isJsonObject(): Boolean {
+    val trimmed = this?.trim() ?: return false
+    return trimmed.startsWith("{") && trimmed.endsWith("}")
+}

-fun String?.isJsonArray(): Boolean = this?.startsWith("[") == true && this.endsWith("]")
+fun String?.isJsonArray(): Boolean {
+    val trimmed = this?.trim() ?: return false
+    return trimmed.startsWith("[") && trimmed.endsWith("]")
+}
app/src/main/res/values/strings.xml (1)

1-3: 파일 끝 줄바꿈(newline) 추가를 고려하세요.

현재 파일이 마지막 닫는 태그 뒤에 줄바꿈 없이 끝나고 있습니다. 일반적인 개발 관행 및 POSIX 표준에 따라 모든 텍스트 파일은 최종 줄바꿈으로 끝나는 것이 권장됩니다.

🔎 제안된 수정
 <resources>
     <string name="app_name">Cherrish</string>
-</resources>
\ No newline at end of file
+</resources>
app/src/test/java/com/cherrish/android/ExampleUnitTest.kt (2)

5-5: 와일드카드 임포트를 명시적 임포트로 변경하는 것을 고려하세요.

와일드카드 임포트(import org.junit.Assert.*)는 코드 가독성을 저하시키고 네임스페이스 충돌 가능성을 높입니다. 명시적으로 필요한 함수만 임포트하는 것을 권장합니다.

🔎 명시적 임포트로 변경
-import org.junit.Assert.*
+import org.junit.Assert.assertEquals

12-17: 실제 비즈니스 로직 테스트로 교체하는 것을 고려하세요.

이 예제 테스트는 초기 세팅 시 기본으로 생성된 보일러플레이트입니다. 프로젝트가 진행됨에 따라 실제 기능을 테스트하는 의미 있는 테스트 케이스로 교체하거나 제거하는 것을 권장합니다.

app/src/main/java/com/cherrish/android/data/remote/dto/response/DummyResponseDto.kt (2)

6-14: @SerialName 어노테이션의 필요성을 검토하세요.

필드명과 JSON 키가 동일한 경우(id, name, age) @SerialName 어노테이션은 불필요합니다. kotlinx.serialization은 기본적으로 필드명을 JSON 키로 사용합니다. 단, 향후 JSON API 스펙이 변경될 가능성이 있거나 명시적 매핑을 선호하는 팀 컨벤션이 있다면 유지할 수 있습니다.

🔎 중복 어노테이션 제거 예시
 @Serializable
 data class DummyResponseDto(
-    @SerialName("id")
     val id: Long,
-    @SerialName("name")
     val name: String,
-    @SerialName("age")
     val age: Int
 )

7-14: 더미 코드 제거 계획을 확인하세요.

DummyResponseDto는 초기 세팅을 위한 예제 코드입니다. 실제 API 응답 DTO가 구현되면 이 더미 클래스와 관련된 코드(DummyDataSource, DummyService, DummyRepository 등)를 제거하거나 실제 구현으로 교체해야 합니다.

다음 스크립트로 더미 관련 코드의 범위를 확인할 수 있습니다:

#!/bin/bash
# Description: Find all dummy-related code files

echo "=== Dummy related files ==="
fd -e kt -x grep -l "Dummy" {}

echo -e "\n=== Dummy class/interface declarations ==="
ast-grep --pattern 'class Dummy$_'
ast-grep --pattern 'interface Dummy$_'
.gitignore (1)

24-24: .idea/ 패턴 중복 고려사항

Line 24에서 .idea/ 전체를 제외하고 있어, Lines 178-202의 개별 .idea/ 파일 패턴들이 중복됩니다. 현재 상태로도 문제없이 동작하지만, 코드 정리 차원에서 개별 패턴들을 제거할 수 있습니다.

Also applies to: 178-202

.github/workflows/pr_checker.yml (2)

8-56: lint와 build 작업 간 중복 설정을 고려해 보세요.

두 작업에서 checkout, cache, JDK 설정, Android SDK 설정, local.properties 생성, gradlew 권한 부여 단계가 동일하게 반복됩니다. 향후 유지보수성을 위해 composite action이나 reusable workflow로 공통 단계를 추출하는 것을 고려해 보세요.

또한 현재 CI에서 단위 테스트(./gradlew test)가 실행되지 않습니다. 테스트 커버리지 확보를 위해 추가를 권장합니다.


50-55: 멀티 모듈 확장 시 ktlint 리포트 경로 확인 필요.

현재 app/build/reports/ktlint/ 경로만 업로드됩니다. 향후 멀티 모듈 구조로 확장할 경우, 와일드카드 패턴(**/build/reports/ktlint/)을 사용하여 모든 모듈의 리포트를 수집하는 것을 고려해 보세요.

app/src/main/java/com/cherrish/android/presentation/home/HomeScreen.kt (1)

9-25: LGTM! Route/Screen 분리 패턴이 잘 적용되어 있습니다.

공개 HomeRoute와 비공개 HomeScreen의 분리가 적절합니다. 향후 상태 관리나 ViewModel 연동 시 이 구조가 확장에 유리합니다.

선택적 개선 사항: HomeRoute에도 modifier 파라미터를 노출하면 호출부에서 추가적인 modifier 적용이 가능해집니다.

🔎 Modifier 노출 예시
 @Composable
 fun HomeRoute(
-    paddingValues: PaddingValues
+    paddingValues: PaddingValues,
+    modifier: Modifier = Modifier
 ) {
-    HomeScreen(paddingValues = paddingValues)
+    HomeScreen(paddingValues = paddingValues, modifier = modifier)
 }
app/src/main/java/com/cherrish/android/core/designsystem/theme/Color.kt (1)

5-11: 템플릿 색상을 브랜드 컬러로 교체하는 것을 권장합니다.

현재 정의된 색상들은 Android Studio의 기본 템플릿 색상입니다. 초기 세팅 단계에서는 적절하지만, 향후 Cherrish 앱의 브랜드 아이덴티티를 반영한 커스텀 색상으로 교체하는 것이 좋습니다. 또한 Purple80, Pink40 같은 일반적인 이름 대신 Primary, Secondary, OnSurface 등 의미론적 네이밍을 고려해보세요.

디자인 시스템이 확정되면 색상 팔레트 구조 제안을 도와드릴 수 있습니다.

app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt (2)

9-14: Preview 어노테이션 추가를 고려해보세요.

개발 편의성을 위해 CalendarRoute@Preview 어노테이션을 추가하면 Android Studio의 Compose Preview에서 UI를 빠르게 확인할 수 있습니다.

🔎 Preview 추가 예시
+@Preview(showBackground = true)
+@Composable
+private fun CalendarRoutePreview() {
+    CalendarRoute(paddingValues = PaddingValues())
+}
+
 @Composable
 fun CalendarRoute(
     paddingValues: PaddingValues

16-25: 향후 상태 관리 아키텍처 구현을 권장합니다.

현재는 플레이스홀더 구현으로 적절하지만, 실제 기능 구현 시 ViewModel, UiState, 단방향 데이터 플로우 패턴을 적용하는 것이 좋습니다.

코딩 가이드라인에 따라 Jetpack Compose 구조, 상태 관리, recomposition 최적화에 집중해주세요.

ViewModel과 UiState 구조 예시가 필요하시면 도움 드릴 수 있습니다.

app/src/main/java/com/cherrish/android/presentation/main/MainTab.kt (2)

17-17: 플레이스홀더 아이콘을 실제 아이콘으로 교체해주세요.

모든 탭이 ic_launcher_background를 아이콘으로 사용하고 있습니다. 초기 세팅 단계에서는 적절하지만, 각 탭의 역할을 나타내는 고유한 아이콘으로 교체가 필요합니다.

Also applies to: 22-22, 27-27, 32-32


37-45: Companion 헬퍼 메서드 필요성을 재검토해보세요.

find()contains() 메서드는 Kotlin의 표준 entries.find(predicate)entries.any(predicate)와 동일한 기능을 제공합니다. 추가적인 로직이 없다면 표준 라이브러리 메서드를 직접 사용하는 것이 더 명확할 수 있습니다.

🔎 사용 예시 비교

현재 방식:

MainTab.find { it.route == someRoute }

표준 라이브러리 직접 사용:

MainTab.entries.find { it.route == someRoute }

추가 로직이 필요하거나 명확한 의도 전달이 중요하다면 현재 방식도 충분히 유효합니다.

app/src/main/java/com/cherrish/android/core/common/navigation/MainTabRoute.kt (1)

1-3: LGTM! 네비게이션 마커 인터페이스가 올바르게 정의되었습니다.

Route를 확장하는 MainTabRoute 인터페이스가 탭 네비게이션의 타입 계층을 명확하게 정의하고 있습니다.

만약 모든 MainTabRoute 구현체가 예측 가능한 data object들이라면, sealed interface로 변경하면 컴파일 타임 타입 안정성을 강화하고 when 표현식에서 exhaustive 체크를 받을 수 있습니다.

🔎 선택적 개선: sealed interface 사용
-interface MainTabRoute : Route
+sealed interface MainTabRoute : Route
app/src/main/java/com/cherrish/android/presentation/mypage/MyPageScreen.kt (1)

9-14: modifier 파라미터가 Route에서 전달되지 않습니다.

MyPageScreenmodifier 파라미터가 정의되어 있지만, MyPageRoute에서 호출할 때 전달되지 않아 기본값만 사용됩니다. 향후 확장성을 위해 Route 레벨에서도 modifier를 받아 전달하는 것을 고려해주세요.

🔎 수정 제안
 @Composable
 fun MyPageRoute(
-    paddingValues: PaddingValues
+    paddingValues: PaddingValues,
+    modifier: Modifier = Modifier
 ) {
-    MyPageScreen(paddingValues = paddingValues)
+    MyPageScreen(paddingValues = paddingValues, modifier = modifier)
 }
app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt (1)

26-31: 불필요한 재구성 시 리스트 재생성을 최적화할 수 있습니다.

Line 28의 MainTab.entries.toPersistentList()가 매 recomposition마다 새 리스트를 생성합니다. MainTab.entries는 불변이므로 remember를 사용하거나 컴포저블 외부에서 정의하여 최적화할 수 있습니다.

🔎 최적화 제안

방법 1: remember 사용

+    val tabs = remember { MainTab.entries.toPersistentList() }
+
     Scaffold(
         bottomBar = {
             MainBottomBar(
                 visible = isBottomBarVisible,
-                tabs = MainTab.entries.toPersistentList(),
+                tabs = tabs,
                 currentTab = currentTab,
                 onTabSelected = appState::navigate
             )

방법 2: 파일 최상위 레벨에 정의

private val mainTabs = MainTab.entries.toPersistentList()

@Composable
fun MainScreen(appState: MainAppState) {
    // ...
    MainBottomBar(
        tabs = mainTabs,
        // ...
    )
}
app/src/main/java/com/cherrish/android/data/repositoryimpl/DummyRepositoryImpl.kt (1)

13-16: data!! 사용 시 null 데이터에 대한 의미 있는 에러 처리 필요

data!!가 null인 경우 NullPointerException이 발생하고, 이는 suspendRunCatching에 의해 Result.failure로 반환됩니다. 하지만 이렇게 하면 실제 문제(서버 응답에 데이터가 없음)가 일반적인 NPE로 가려집니다.

🔎 의미 있는 에러 메시지를 포함한 수정 제안
 override suspend fun getDummy(): Result<DummyModel> =
     suspendRunCatching {
-        dummyDataSource.getDummyData().data!!.toModel()
+        requireNotNull(dummyDataSource.getDummyData().data) {
+            "Dummy data response is null"
+        }.toModel()
     }
app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt (2)

35-41: 동적 색상 기본값 설정 재검토 필요

dynamicColor의 기본값이 true로 설정되어 있어, Android 12+ 기기에서는 항상 시스템 색상을 따릅니다. 이는 앱의 브랜드 아이덴티티를 유지하는 데 방해가 될 수 있습니다. 앱이 고유한 브랜드 색상을 가지고 있다면 false를 기본값으로 고려하세요.

🔎 수정 제안
 @Composable
 fun CherrishTheme(
     darkTheme: Boolean = isSystemInDarkTheme(),
     // Dynamic color is available on Android 12+
-    dynamicColor: Boolean = true,
+    dynamicColor: Boolean = false,
     content: @Composable () -> Unit
 ) {

24-32: 주석처리된 기본 색상 코드 정리 필요

Material3 기본 색상들이 주석으로 남아있습니다. 이후 커스터마이징 시 참고용으로 유용할 수 있지만, 프로덕션 코드에서는 제거하거나 실제로 사용하는 것을 권장합니다.

app/src/main/java/com/cherrish/android/core/network/NetworkModule.kt (1)

62-68: OkHttpClient에 타임아웃 설정 추가 권장

네트워크 요청에 대한 타임아웃이 설정되지 않아 무한 대기 상황이 발생할 수 있습니다. connectTimeout, readTimeout, writeTimeout을 설정하여 네트워크 안정성을 개선하세요.

🔎 타임아웃 설정 추가 제안
+import java.util.concurrent.TimeUnit
+
 @Provides
 @Singleton
 fun provideDefaultOkHttpClient(
     loggingInterceptor: Interceptor
 ): OkHttpClient = OkHttpClient.Builder()
     .addInterceptor(loggingInterceptor)
+    .connectTimeout(30, TimeUnit.SECONDS)
+    .readTimeout(30, TimeUnit.SECONDS)
+    .writeTimeout(30, TimeUnit.SECONDS)
     .build()
app/src/main/java/com/cherrish/android/presentation/main/component/MainBottomBar.kt (2)

84-93: 조건부 색상 선택 최적화 가능

if (selected) 표현식이 Icon과 Text에서 각각 평가되어 불필요한 계산이 반복됩니다. 색상을 미리 계산하여 재사용하면 가독성과 성능을 개선할 수 있습니다.

🔎 색상 미리 계산 제안
 @Composable
 private fun RowScope.MainBottomBarItem(
     tab: MainTab,
     selected: Boolean,
     onClick: () -> Unit
 ) {
+    val tintColor = if (selected) Color.Black else Color.Gray
+    
     Column(
         modifier = Modifier.weight(1f)
             .clickable(onClick = onClick),
         horizontalAlignment = Alignment.CenterHorizontally,
         verticalArrangement = Arrangement.spacedBy(4.dp)
     ) {
         Icon(
             imageVector = ImageVector.vectorResource(id = tab.iconRes),
             modifier = Modifier.size(24.dp),
             contentDescription = null,
-            tint = if (selected) Color.Black else Color.Gray
+            tint = tintColor
         )
         Text(
             text = tab.label,
-            color = if (selected) Color.Black else Color.Gray
+            color = tintColor
         )
     }
 }

79-80: clickable modifier 위치 조정 권장

Modifier.weight(1f).clickable(onClick = onClick) 순서에서 clickable을 먼저 적용하면 리플 효과가 weight 영역 전체에 적용됩니다. 현재 순서도 작동하지만, 일반적으로 clickable을 먼저 적용하는 것이 권장됩니다.

🔎 modifier 순서 수정 제안
     Column(
-        modifier = Modifier.weight(1f)
-            .clickable(onClick = onClick),
+        modifier = Modifier
+            .weight(1f)
+            .clickable(onClick = onClick),
         horizontalAlignment = Alignment.CenterHorizontally,
         verticalArrangement = Arrangement.spacedBy(4.dp)
     ) {
app/src/main/java/com/cherrish/android/presentation/main/MainAppState.kt (1)

22-59: StateFlow 기반 상태 관리 구조가 잘 설계되었습니다.

@Stable 어노테이션 사용과 stateIn을 통한 StateFlow 변환이 적절하며, WhileSubscribed(5_000) 정책으로 메모리 효율성도 고려되었습니다. 반응형 상태 관리 패턴이 잘 적용되었습니다.

다만 선택적으로 개선할 수 있는 부분들이 있습니다:

  1. null 초기값 처리: currentDestinationcurrentTabinitialValuenull입니다. 소비하는 측에서 null 처리가 필요한데, startDestination을 활용하여 초기값을 제공하는 것도 고려해볼 수 있습니다.

  2. 공개 API 문서화: public 프로퍼티들에 대한 KDoc 추가를 권장합니다. 특히 currentTabisBottomBarVisible의 null 가능성과 의미를 명확히 하면 좋습니다.

🔎 선택적 개선사항: null 처리 예시
 val currentTab: StateFlow<MainTab?> = currentDestination
     .map { destination ->
         MainTab.find { tab ->
             destination?.hasRoute(tab.route::class) == true
         }
     }
     .stateIn(
         scope = coroutineScope,
         started = SharingStarted.WhileSubscribed(5_000),
-        initialValue = null
+        initialValue = MainTab.HOME // 또는 MainTab.fromRoute(startDestination)
     )
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b851bd and a3d920b.

⛔ Files ignored due to path filters (11)
  • app/src/main/res/mipmap-hdpi/ic_launcher.webp is excluded by !**/*.webp
  • app/src/main/res/mipmap-hdpi/ic_launcher_round.webp is excluded by !**/*.webp
  • app/src/main/res/mipmap-mdpi/ic_launcher.webp is excluded by !**/*.webp
  • app/src/main/res/mipmap-mdpi/ic_launcher_round.webp is excluded by !**/*.webp
  • app/src/main/res/mipmap-xhdpi/ic_launcher.webp is excluded by !**/*.webp
  • app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp is excluded by !**/*.webp
  • app/src/main/res/mipmap-xxhdpi/ic_launcher.webp is excluded by !**/*.webp
  • app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp is excluded by !**/*.webp
  • app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp is excluded by !**/*.webp
  • app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp is excluded by !**/*.webp
  • gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
📒 Files selected for processing (73)
  • .coderabbit.yaml
  • .github/CODEOWNERS
  • .github/ISSUE_TEMPLATE/issue_template.md
  • .github/workflows/pr_checker.yml
  • .gitignore
  • .run/Cherrish [ktLintCheck].run.xml
  • .run/Cherrish [ktLintFormat].run.xml
  • app/.gitignore
  • app/build.gradle.kts
  • app/proguard-rules.pro
  • app/src/androidTest/java/com/cherrish/android/ExampleInstrumentedTest.kt
  • app/src/main/AndroidManifest.xml
  • app/src/main/java/com/cherrish/android/CherrishApplication.kt
  • app/src/main/java/com/cherrish/android/core/common/extension/FlowExt.kt
  • app/src/main/java/com/cherrish/android/core/common/extension/ModifierExt.kt
  • app/src/main/java/com/cherrish/android/core/common/extension/RunCatchingExt.kt
  • app/src/main/java/com/cherrish/android/core/common/extension/StateFlowExt.kt
  • app/src/main/java/com/cherrish/android/core/common/navigation/MainTabRoute.kt
  • app/src/main/java/com/cherrish/android/core/common/navigation/Route.kt
  • app/src/main/java/com/cherrish/android/core/common/state/UiState.kt
  • app/src/main/java/com/cherrish/android/core/designsystem/component/.gitkeep
  • app/src/main/java/com/cherrish/android/core/designsystem/theme/Color.kt
  • app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt
  • app/src/main/java/com/cherrish/android/core/designsystem/theme/Type.kt
  • app/src/main/java/com/cherrish/android/core/local/datastore/PreferencesDataStore.kt
  • app/src/main/java/com/cherrish/android/core/local/di/DataStoreModule.kt
  • app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt
  • app/src/main/java/com/cherrish/android/core/network/JsonStringExt.kt
  • app/src/main/java/com/cherrish/android/core/network/NetworkModule.kt
  • app/src/main/java/com/cherrish/android/core/util/SuspendRunCatching.kt
  • app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt
  • app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt
  • app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt
  • app/src/main/java/com/cherrish/android/data/local/datasource/.gitkeep
  • app/src/main/java/com/cherrish/android/data/local/datasourceimpl/.gitkeep
  • app/src/main/java/com/cherrish/android/data/model/DummyModel.kt
  • app/src/main/java/com/cherrish/android/data/remote/datasource/DummyDataSource.kt
  • app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/DummyDataSourceImpl.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/request/.gitkeep
  • app/src/main/java/com/cherrish/android/data/remote/dto/response/DummyResponseDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/service/DummyService.kt
  • app/src/main/java/com/cherrish/android/data/repository/DummyRepository.kt
  • app/src/main/java/com/cherrish/android/data/repositoryimpl/DummyRepositoryImpl.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/navigation/CalendarNavigator.kt
  • app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt
  • app/src/main/java/com/cherrish/android/presentation/challenge/navigation/ChallengeNavigator.kt
  • app/src/main/java/com/cherrish/android/presentation/home/HomeScreen.kt
  • app/src/main/java/com/cherrish/android/presentation/home/navigation/HomeNavigator.kt
  • app/src/main/java/com/cherrish/android/presentation/main/MainActivity.kt
  • app/src/main/java/com/cherrish/android/presentation/main/MainAppState.kt
  • app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt
  • app/src/main/java/com/cherrish/android/presentation/main/MainTab.kt
  • app/src/main/java/com/cherrish/android/presentation/main/component/MainBottomBar.kt
  • app/src/main/java/com/cherrish/android/presentation/mypage/MyPageScreen.kt
  • app/src/main/java/com/cherrish/android/presentation/mypage/navigation/MyPageNavigator.kt
  • app/src/main/res/drawable/ic_launcher_background.xml
  • app/src/main/res/drawable/ic_launcher_foreground.xml
  • app/src/main/res/mipmap-anydpi/ic_launcher.xml
  • app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
  • app/src/main/res/values/colors.xml
  • app/src/main/res/values/strings.xml
  • app/src/main/res/values/themes.xml
  • app/src/main/res/xml/backup_rules.xml
  • app/src/main/res/xml/data_extraction_rules.xml
  • app/src/test/java/com/cherrish/android/ExampleUnitTest.kt
  • build.gradle.kts
  • gradle.properties
  • gradle/libs.versions.toml
  • gradle/wrapper/gradle-wrapper.properties
  • gradlew
  • gradlew.bat
  • settings.gradle.kts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: - Jetpack Compose 구조, 상태 관리, recomposition 최적화에 집중

  • ViewModel, UiState, 단방향 데이터 흐름 패턴 검토
  • 불필요한 recomposition 가능성, remember/derivedStateOf 적절한 사용 확인
  • 네이밍 컨벤션, 가독성, Google 권장 Android 아키텍처 준수 여부

Files:

  • app/src/main/java/com/cherrish/android/core/common/navigation/Route.kt
  • app/src/main/java/com/cherrish/android/data/remote/service/DummyService.kt
  • app/src/main/java/com/cherrish/android/core/common/navigation/MainTabRoute.kt
  • app/src/main/java/com/cherrish/android/presentation/challenge/navigation/ChallengeNavigator.kt
  • app/src/main/java/com/cherrish/android/core/designsystem/theme/Type.kt
  • app/src/main/java/com/cherrish/android/core/common/state/UiState.kt
  • app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt
  • app/src/main/java/com/cherrish/android/core/util/SuspendRunCatching.kt
  • app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/navigation/CalendarNavigator.kt
  • app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt
  • app/src/main/java/com/cherrish/android/presentation/main/MainActivity.kt
  • app/src/main/java/com/cherrish/android/presentation/home/HomeScreen.kt
  • app/src/main/java/com/cherrish/android/core/common/extension/RunCatchingExt.kt
  • app/src/main/java/com/cherrish/android/core/designsystem/theme/Color.kt
  • app/src/main/java/com/cherrish/android/presentation/mypage/MyPageScreen.kt
  • app/src/main/java/com/cherrish/android/data/model/DummyModel.kt
  • app/src/main/java/com/cherrish/android/data/repository/DummyRepository.kt
  • app/src/test/java/com/cherrish/android/ExampleUnitTest.kt
  • app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt
  • app/src/main/java/com/cherrish/android/core/local/datastore/PreferencesDataStore.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt
  • app/src/main/java/com/cherrish/android/core/network/JsonStringExt.kt
  • app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt
  • app/src/main/java/com/cherrish/android/presentation/home/navigation/HomeNavigator.kt
  • app/src/main/java/com/cherrish/android/core/common/extension/StateFlowExt.kt
  • app/src/main/java/com/cherrish/android/CherrishApplication.kt
  • app/src/main/java/com/cherrish/android/core/common/extension/FlowExt.kt
  • app/src/main/java/com/cherrish/android/core/network/NetworkModule.kt
  • app/src/main/java/com/cherrish/android/core/common/extension/ModifierExt.kt
  • app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/DummyDataSourceImpl.kt
  • app/src/androidTest/java/com/cherrish/android/ExampleInstrumentedTest.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/response/DummyResponseDto.kt
  • app/src/main/java/com/cherrish/android/presentation/main/MainTab.kt
  • app/src/main/java/com/cherrish/android/presentation/mypage/navigation/MyPageNavigator.kt
  • app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt
  • app/src/main/java/com/cherrish/android/core/local/di/DataStoreModule.kt
  • app/src/main/java/com/cherrish/android/presentation/main/component/MainBottomBar.kt
  • app/src/main/java/com/cherrish/android/data/repositoryimpl/DummyRepositoryImpl.kt
  • app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt
  • app/src/main/java/com/cherrish/android/presentation/main/MainAppState.kt
  • app/src/main/java/com/cherrish/android/data/remote/datasource/DummyDataSource.kt
🧬 Code graph analysis (7)
app/src/main/java/com/cherrish/android/presentation/challenge/navigation/ChallengeNavigator.kt (2)
app/src/main/java/com/cherrish/android/presentation/main/MainAppState.kt (1)
  • navigate (61-79)
app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt (1)
  • ChallengeRoute (9-14)
app/src/main/java/com/cherrish/android/presentation/calendar/navigation/CalendarNavigator.kt (2)
app/src/main/java/com/cherrish/android/presentation/main/MainAppState.kt (1)
  • navigate (61-79)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt (1)
  • CalendarRoute (9-14)
app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt (5)
app/src/main/java/com/cherrish/android/presentation/main/component/MainBottomBar.kt (1)
  • MainBottomBar (35-70)
app/src/main/java/com/cherrish/android/presentation/home/navigation/HomeNavigator.kt (1)
  • homeNavGraph (24-32)
app/src/main/java/com/cherrish/android/presentation/calendar/navigation/CalendarNavigator.kt (1)
  • calendarNavGraph (24-32)
app/src/main/java/com/cherrish/android/presentation/challenge/navigation/ChallengeNavigator.kt (1)
  • challengeNavGraph (24-32)
app/src/main/java/com/cherrish/android/presentation/mypage/navigation/MyPageNavigator.kt (1)
  • myPageNavGraph (24-32)
app/src/main/java/com/cherrish/android/presentation/home/navigation/HomeNavigator.kt (2)
app/src/main/java/com/cherrish/android/presentation/main/MainAppState.kt (1)
  • navigate (61-79)
app/src/main/java/com/cherrish/android/presentation/home/HomeScreen.kt (1)
  • HomeRoute (9-14)
app/src/main/java/com/cherrish/android/presentation/mypage/navigation/MyPageNavigator.kt (2)
app/src/main/java/com/cherrish/android/presentation/main/MainAppState.kt (1)
  • navigate (61-79)
app/src/main/java/com/cherrish/android/presentation/mypage/MyPageScreen.kt (1)
  • MyPageRoute (9-14)
app/src/main/java/com/cherrish/android/presentation/main/component/MainBottomBar.kt (1)
app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt (1)
  • CherrishTheme (35-56)
app/src/main/java/com/cherrish/android/data/repositoryimpl/DummyRepositoryImpl.kt (1)
app/src/main/java/com/cherrish/android/core/util/SuspendRunCatching.kt (1)
  • suspendRunCatching (8-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build Check
🔇 Additional comments (58)
app/proguard-rules.pro (1)

1-21: 표준 ProGuard 템플릿 파일로 적절합니다.

초기 프로젝트 세팅 단계에서 기본 템플릿을 사용하는 것은 적절합니다. 현재 모든 규칙이 주석 처리되어 있으며, 향후 릴리즈 빌드 및 코드 난독화가 필요할 때 실제 ProGuard/R8 규칙을 추가하면 됩니다.

app/src/main/java/com/cherrish/android/core/common/extension/StateFlowExt.kt (1)

7-17: LGTM! 상태 업데이트 패턴이 올바르게 구현되었습니다.

이 확장 함수는 StateFlow 기반 상태 관리의 best practice를 잘 따르고 있습니다:

  • inlinecrossinline 키워드의 적절한 사용으로 성능 최적화
  • MutableStateFlow.update를 통한 원자적(atomic) 업데이트 보장
  • Success 상태일 때만 변환을 적용하고 다른 상태는 보존하는 안전한 로직
  • 불변성을 유지하는 copy() 사용

단방향 데이터 흐름 패턴과 Compose의 상태 관리 원칙에 부합합니다.

app/src/main/java/com/cherrish/android/core/local/di/DataStoreModule.kt (2)

15-17: DataStore 설정이 적절합니다.

private 확장 속성을 사용하여 DataStore를 생성하는 방식이 올바르며, 파일명 "cherrish_preferences"도 명확합니다.


23-27: DataStore 제공 메서드가 올바르게 구현되었습니다.

@Singleton@ApplicationContext를 적절히 사용하여 DataStore 인스턴스를 제공하고 있습니다.

app/.gitignore (1)

1-1: LGTM!

표준적인 안드로이드 프로젝트 .gitignore 설정입니다. Gradle 빌드 산출물이 버전 관리에서 적절히 제외됩니다.

.github/CODEOWNERS (1)

1-1: LGTM!

초기 설정 단계에서 모든 경로를 팀에 할당하는 것은 적절합니다. 프로젝트가 성장함에 따라 나중에 모듈별(예: feature, data, ui) 또는 담당자별로 규칙을 세분화할 수 있습니다.

.run/Cherrish [ktLintFormat].run.xml (1)

1-24: LGTM!

ktLintFormat 작업을 위한 IDE 실행 구성이 올바르게 설정되었습니다. 이는 개발자가 IDE에서 직접 코드 포맷팅을 실행할 수 있게 하여 개발 경험을 향상시킵니다.

app/src/main/res/xml/backup_rules.xml (1)

8-13: 초기 세팅으로 적절합니다.

백업 규칙이 주석 처리된 예제로만 구성되어 있습니다. 프로젝트 초기 세팅 단계에서는 적절하지만, 향후 실제 앱 데이터 백업 정책을 정의할 때 이 파일을 업데이트해야 합니다. SharedPreferences나 민감한 데이터를 포함/제외할 규칙을 명시적으로 정의하는 것을 고려하세요.

gradle/wrapper/gradle-wrapper.properties (1)

3-3: Gradle 9.0.0 호환성 확인 완료 - 문제 없음

프로젝트의 플러그인 버전을 확인했습니다:

  • Android Gradle Plugin (AGP): 8.13.2
  • Kotlin: 2.1.20
  • Hilt: 2.56.2

Gradle 9.0.0은 AGP 8.4 이상을 요구하며, 현재 프로젝트의 AGP 8.13.2는 이 요구사항을 충족합니다. 따라서 Gradle 9.0.0과의 호환성 문제는 없습니다.

.run/Cherrish [ktLintCheck].run.xml (1)

1-24: ktLintCheck 실행 구성이 올바르게 설정되었습니다.

IDE에서 ktLintCheck를 실행하기 위한 표준 Gradle 실행 구성이 적절하게 구성되어 있습니다. 디버그 옵션도 활성화되어 있어 필요시 디버깅이 가능합니다.

gradle.properties (1)

1-23: Gradle 프로젝트 설정이 적절합니다.

프로젝트 전역 Gradle 설정이 Android 프로젝트에 적합하게 구성되어 있습니다:

  • JVM 메모리 할당 (2GB)이 적절함
  • AndroidX 사용 활성화
  • Kotlin 공식 코드 스타일 적용
  • nonTransitiveRClass로 R 클래스 크기 최적화
.gitignore (1)

238-238: Gradle wrapper jar 명시적 포함은 모범 사례입니다.

!/gradle/wrapper/gradle-wrapper.jar 패턴으로 Gradle wrapper를 버전 관리에 포함시키는 것은 프로젝트의 재현 가능한 빌드를 보장하는 모범 사례입니다.

app/src/main/java/com/cherrish/android/core/common/extension/RunCatchingExt.kt (1)

5-10: Result 확장 함수가 올바르게 구현되었습니다.

이 확장 함수는 실패한 Result를 로깅하면서도 원본 Result를 보존합니다:

  • suspend inline + crossinline의 올바른 사용
  • onFailure를 통한 체이닝으로 원본 Result 반환
  • Timber를 활용한 중앙화된 에러 로깅
  • 명확한 네이밍으로 함수 의도 표현
.coderabbit.yaml (1)

1-32: CodeRabbit 설정이 프로젝트에 적합하게 구성되었습니다.

설정이 Android Kotlin 프로젝트에 맞게 적절히 구성되었습니다:

  • 한국어 리뷰 활성화 (line 1)
  • 자동 리뷰 및 고수준 요약 포함 (lines 5-9)
  • Kotlin 파일에 대한 명확한 리뷰 가이드라인 제공 (lines 18-23): Jetpack Compose, ViewModel, 상태 관리 패턴 중심
  • 빌드 산출물 및 이미지 파일 제외 (lines 26-32)

모든 설정이 스키마에 부합하며 팀의 리뷰 프로세스를 효과적으로 지원합니다.

gradlew.bat (1)

1-94: LGTM!

Gradle에서 자동 생성된 표준 Windows 배치 스크립트입니다. Java 감지, 에러 처리, 실행 로직이 올바르게 구성되어 있습니다.

app/src/main/res/drawable/ic_launcher_foreground.xml (1)

1-30: LGTM!

표준 Android 적응형 아이콘 foreground 벡터 드로어블입니다. 108dp 크기와 그라데이션 그림자가 올바르게 설정되어 있습니다.

app/src/androidTest/java/com/cherrish/android/ExampleInstrumentedTest.kt (1)

1-24: LGTM!

표준 Android Instrumented 테스트 템플릿입니다. 패키지명 검증이 올바르게 설정되어 있습니다.

gradlew (1)

1-251: LGTM!

Gradle에서 자동 생성된 표준 POSIX 셸 스크립트입니다. 다양한 운영체제(Cygwin, MSYS, Darwin 등)에 대한 처리가 올바르게 구성되어 있습니다.

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

3-9: 미사용 템플릿 색상 제거 또는 브랜드 컬러로 교체가 필요합니다.

이 색상들은 Android Studio 기본 템플릿이며 프로젝트 전체에서 참조되지 않습니다.

  • 프로젝트는 Compose 기반이므로 XML 레이아웃이 없고, 이 XML 색상들이 사용되는 곳이 없습니다.
  • 프로젝트 색상은 Color.kt에서 관리되므로 (Purple80, PurpleGrey80, Pink80 등), 이 미사용 템플릿 색상들은 제거하거나 Cherrish 브랜드 컬러로 교체하는 것을 권장합니다.

Likely an incorrect or invalid review comment.

app/src/main/res/mipmap-anydpi/ic_launcher.xml (1)

1-6: LGTM! Adaptive icon 구조가 올바릅니다.

Android 13+의 themed icons를 지원하는 monochrome 레이어가 포함되어 있습니다. 표준 adaptive icon 패턴을 따르고 있습니다.

app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt (2)

11-13: LGTM! Hilt 모듈 구조가 올바릅니다.

SingletonComponent에 설치된 표준 Hilt 모듈 패턴을 따르고 있습니다.


14-18: LGTM! Retrofit 서비스 제공 패턴이 올바릅니다.

DummyService 제공 로직이 표준 Retrofit + Hilt 패턴을 따르고 있습니다. 초기 설정 단계에서 Dummy 서비스는 적절하며, 실제 서비스 구현 시 유사한 패턴을 따르면 됩니다.

app/src/main/java/com/cherrish/android/core/common/navigation/Route.kt (1)

1-3: LGTM! 네비게이션 시스템의 기본 계약이 적절합니다.

Route 인터페이스가 네비게이션 계층의 루트 타입으로 명확하게 정의되었습니다. 마커 인터페이스 패턴을 사용하여 타입 안정성을 제공하면서도 확장성을 유지하고 있습니다.

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

1-4: LGTM! Compose 앱을 위한 기본 테마 설정이 적절합니다.

NoActionBar를 사용하여 Compose 기반 UI와 호환되도록 설정했습니다. 실제 앱 테마링은 CherrishTheme Compose 함수에서 처리될 것으로 보이며, 이 XML 테마는 시스템 UI를 위한 기본 설정으로 적절합니다.

app/src/main/res/drawable/ic_launcher_background.xml (1)

1-170: LGTM!

적응형 아이콘 배경을 위한 표준 Android 벡터 드로어블입니다. 108dp 크기와 뷰포트가 적응형 아이콘 가이드라인에 맞게 올바르게 설정되어 있습니다.

app/src/main/res/xml/data_extraction_rules.xml (1)

1-19: LGTM!

Android 12+ 백업/복원을 위한 표준 데이터 추출 규칙 템플릿입니다. 프로젝트 초기 설정 단계에서는 적절하며, 추후 민감한 데이터가 추가되면 백업 규칙을 구체화하시면 됩니다.

app/src/main/res/mipmap-anydpi/ic_launcher_round.xml (1)

1-6: LGTM!

적응형 아이콘 구성이 올바릅니다. 모노크롬 레이어가 포그라운드를 사용하여 Android 13+ 테마 아이콘을 지원합니다.

app/src/main/java/com/cherrish/android/data/repository/DummyRepository.kt (1)

1-7: LGTM!

Repository 인터페이스가 Google 권장 Android 아키텍처 패턴을 잘 따르고 있습니다. Result<T> 타입을 사용한 에러 핸들링과 suspend 함수 사용이 적절합니다.

app/src/main/java/com/cherrish/android/presentation/mypage/MyPageScreen.kt (1)

16-25: LGTM!

플레이스홀더 스크린으로서 적절한 구현입니다. Route-to-Screen 패턴이 프로젝트 내 다른 스크린들과 일관되게 적용되어 있습니다.

app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt (1)

1-19: LGTM!

Hilt DI 모듈이 올바르게 구성되어 있습니다. @Binds를 사용한 인터페이스-구현체 바인딩과 @Singleton 스코프 적용이 적절합니다.

app/src/main/java/com/cherrish/android/core/util/SuspendRunCatching.kt (1)

8-19: 코루틴 예외 처리가 올바르게 구현되었습니다.

suspend 함수의 예외 처리 패턴이 정확합니다:

  • CancellationException 재발생으로 코루틴 취소를 보존
  • TimeoutCancellationException을 실패로 처리하여 타임아웃을 명시적으로 핸들링
  • 다른 예외 발생 시 ensureActive() 호출로 코루틴 상태 검증
build.gradle.kts (1)

1-8: 멀티모듈 프로젝트 설정이 적절합니다.

루트 build.gradle.kts의 플러그인 선언이 올바릅니다:

  • apply false로 하위 모듈에서 선택적 적용 가능
  • Version catalog 활용으로 의존성 버전 중앙 관리
  • 필요한 핵심 플러그인(Android, Kotlin, Compose, Hilt, KSP) 포함
app/src/main/java/com/cherrish/android/CherrishApplication.kt (1)

8-25: Application 초기화가 올바르게 구성되었습니다.

구현이 안드로이드 모범 사례를 따릅니다:

  • @HiltAndroidApp으로 Hilt DI 설정 완료
  • Timber 로깅은 디버그 빌드에만 활성화
  • Day mode 강제 설정으로 일관된 UI 제공 (의도적인 설계로 보임)
app/src/main/java/com/cherrish/android/data/remote/datasource/DummyDataSource.kt (1)

6-8: 데이터 소스 인터페이스가 적절합니다.

초기 프로젝트 세팅을 위한 더미 데이터 소스 계약이 명확하게 정의되어 있습니다.

app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt (1)

17-51: Compose 아키텍처와 상태 관리가 잘 구현되었습니다.

MainScreen의 구조가 권장 패턴을 따릅니다:

  • collectAsStateWithLifecycle로 생명주기 안전한 상태 수집
  • MainAppState를 통한 단방향 데이터 흐름
  • Scaffold와 NavHost를 활용한 명확한 네비게이션 구조
  • 탭 네비게이션에 적합하게 전환 애니메이션 비활성화
app/src/main/java/com/cherrish/android/presentation/main/MainActivity.kt (1)

10-23: 깔끔한 Activity 구조입니다!

@AndroidEntryPoint를 통한 Hilt 통합, enableEdgeToEdge()를 통한 엣지-투-엣지 UI 설정, 그리고 Compose 기반 UI 구성이 모두 적절하게 구현되어 있습니다. rememberMainAppState()를 통해 상태가 올바르게 remember되어 불필요한 recomposition을 방지하고 있습니다.

app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt (1)

11-19: 표준적인 Hilt DI 모듈 구성입니다.

@Binds를 사용한 인터페이스 바인딩과 @Singleton 스코프 적용이 올바르게 구현되어 있습니다. SingletonComponent에 설치되어 스코프 일관성도 유지되고 있습니다.

app/src/main/java/com/cherrish/android/data/model/DummyModel.kt (1)

5-15: 명확한 데이터 모델 구조입니다.

DummyModel 데이터 클래스와 toModel() 확장 함수를 통한 DTO-to-Model 매핑이 간결하게 구현되어 있습니다. 초기 더미 구현으로 적절합니다.

app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt (1)

9-25: Route/Screen 분리 패턴이 잘 적용되었습니다.

Public ChallengeRoute와 private ChallengeScreen으로 분리하여 테스트 가능성과 캡슐화를 확보했습니다. modifierpaddingValues가 적절히 적용되어 있으며, 초기 플레이스홀더 구현으로 적합합니다.

app/src/main/java/com/cherrish/android/presentation/challenge/navigation/ChallengeNavigator.kt (1)

12-32: 타입 안전 네비게이션 패턴이 일관되게 적용되었습니다.

@Serializable data object를 활용한 타입 안전 네비게이션 구현이 우수합니다. navigateToChallenge 확장 함수와 challengeNavGraph 빌더가 다른 탭 네비게이터들(Home, Calendar, MyPage)과 일관된 패턴을 따르고 있어 유지보수성이 높습니다.

gradle/libs.versions.toml (2)

108-167: LGTM!

번들 구성이 잘 정리되어 있습니다. 관련 라이브러리들이 논리적으로 그룹화되어 있어 의존성 관리가 용이합니다.


3-5: 모든 버전이 유효합니다

각 버전이 실제로 존재합니다:

  • compileSdk = "36": Android 16으로 2025년 3월에 platform stable 상태로 출시됨
  • agp = "8.13.2": Android Gradle Plugin 8.13 라인의 정식 릴리스
  • kotlin = "2.1.20": 2025년 3월 20일 릴리스
  • retrofit = "3.0.0": 2025년 5월 15일 릴리스

다만 Retrofit 3.0.0은 OkHttp 4.12로 테스트되었으나, 프로젝트에서는 OkHttp 5.3.2를 사용하고 있습니다. 호환성 확인이 필요할 수 있습니다.

Likely an incorrect or invalid review comment.

app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/DummyDataSourceImpl.kt (1)

9-14: LGTM!

DataSource 구현이 깔끔하고 표준 패턴을 잘 따르고 있습니다. Hilt DI를 위한 @Inject constructor 사용과 인터페이스 위임이 적절합니다.

app/src/main/java/com/cherrish/android/presentation/home/navigation/HomeNavigator.kt (1)

12-32: LGTM!

Type-safe navigation 패턴을 잘 따르고 있습니다. @Serializable data object와 NavController/NavGraphBuilder 확장 함수 구성이 다른 탭 네비게이터들과 일관성 있게 구현되어 있습니다.

app/src/main/java/com/cherrish/android/presentation/calendar/navigation/CalendarNavigator.kt (1)

12-32: LGTM!

HomeNavigator와 동일한 패턴으로 일관성 있게 구현되어 있습니다. 네비게이션 구조가 유지보수하기 좋게 설계되었습니다.

app/src/main/java/com/cherrish/android/core/designsystem/theme/Type.kt (1)

10-34: LGTM!

Material 3 Typography의 기본 설정이 적절합니다. 주석 처리된 템플릿은 추후 커스터마이징 시 유용한 가이드가 됩니다.

app/src/main/AndroidManifest.xml (1)

1-30: LGTM!

Manifest 설정이 완벽합니다. 인터넷 권한, 백업 규칙, 런처 액티비티 등 초기 설정에 필요한 모든 요소가 적절하게 구성되어 있습니다.

app/build.gradle.kts (2)

40-53: Release 빌드에서 코드 난독화 비활성화 확인

Release 빌드 타입에서 isMinifyEnabled = false로 설정되어 있습니다. 초기 설정 단계에서는 문제없지만, 프로덕션 배포 전에 true로 변경하여 코드 난독화 및 최적화를 활성화해야 합니다.


103-114: ktlint 설정이 적절합니다

ktlint 구성이 잘 되어 있으며, 테스트 디렉토리를 제외하는 것도 합리적입니다. Android 모드, 디버그, 컬러 출력 등의 옵션이 개발 환경에 적합합니다.

app/src/main/java/com/cherrish/android/presentation/mypage/navigation/MyPageNavigator.kt (2)

12-13: Serializable 타입 안전 네비게이션 적용이 우수합니다

@Serializable data object를 사용한 타입 안전 네비게이션 패턴이 잘 적용되어 있습니다. Kotlin Serialization 기반의 최신 Compose Navigation API를 올바르게 활용하고 있습니다.


15-32: 네비게이션 구조가 일관성 있게 구현되었습니다

navigateToMyPage 확장 함수와 myPageNavGraph 빌더 함수가 다른 탭 네비게이터들과 동일한 패턴을 따르고 있어 코드베이스의 일관성이 유지됩니다. PaddingValues를 전달하여 시스템 UI와의 충돌을 방지하는 것도 적절합니다.

app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt (1)

42-49: 색상 스키마 선택 로직이 올바르게 구현되었습니다

동적 색상 지원과 다크/라이트 모드 전환 로직이 Material3 가이드라인에 맞게 잘 구현되어 있습니다. Build.VERSION 체크도 적절합니다.

app/src/main/java/com/cherrish/android/core/network/NetworkModule.kt (2)

29-34: JSON 설정이 적절합니다

ignoreUnknownKeys = true 설정으로 API 변경에 유연하게 대응할 수 있고, prettyPrint = true는 디버깅에 유용합니다. Kotlinx Serialization 활용이 잘 되어 있습니다.


43-60: 확장 함수가 이미 정의되어 있습니다. isJsonObject()isJsonArray() 확장 함수는 app/src/main/java/com/cherrish/android/core/network/JsonStringExt.kt에서 정의되어 있으며, NetworkModule.kt과 동일한 패키지(com.cherrish.android.core.network)에 있으므로 명시적 import 없이 자동으로 접근 가능합니다. 현재 코드는 정상적으로 작동합니다.

Likely an incorrect or invalid review comment.

app/src/main/java/com/cherrish/android/presentation/main/component/MainBottomBar.kt (3)

36-41: ImmutableList 사용으로 불필요한 리컴포지션 방지

ImmutableList<MainTab> 타입을 사용하여 Compose가 리스트 변경을 효율적으로 추적할 수 있도록 했습니다. 이는 리컴포지션 최적화를 위한 모범 사례입니다.


58-66: key()를 사용한 안정적인 컴포지션 보장

key(tab.route)를 사용하여 각 탭 아이템에 안정적인 식별자를 부여했습니다. 이는 리스트 재정렬 시 불필요한 리컴포지션을 방지하고 애니메이션을 안정적으로 유지하는 올바른 패턴입니다.


42-46: AnimatedVisibility 전환 효과가 적절합니다

fadeIn/fadeOut과 slideIn/slideOut을 조합하여 부드러운 바텀바 표시/숨김 애니메이션을 구현했습니다. UX 측면에서 자연스러운 전환 효과입니다.

app/src/main/java/com/cherrish/android/presentation/main/MainAppState.kt (1)

82-91: rememberMainAppState 구현이 우수합니다.

Composable에서 상태를 안전하게 기억하는 표준 패턴을 정확히 따르고 있습니다. remember의 키로 navControllercoroutineScope를 지정하여 불필요한 재생성을 방지하고 있으며, 기본값 제공으로 사용성도 좋습니다.

Comment thread app/build.gradle.kts
Comment thread app/build.gradle.kts
Comment thread app/build.gradle.kts
Comment thread app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt
Comment thread app/src/main/java/com/cherrish/android/presentation/main/MainTab.kt
Comment thread settings.gradle.kts
Copy link
Copy Markdown
Contributor

@hyeminililo hyeminililo left a comment

Choose a reason for hiding this comment

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

기초 세팅 정말 ,,, 엄청나게 잘하셨네요 ,, 세팅하신 코드를 보면서 열심히 공부를 더욱 해보겠습니다 !! 너무 수고하셨습니다 😭🥹

import androidx.compose.ui.unit.dp

@Composable
inline fun Modifier.noRippleClickable(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Modifier를 이렇게 따로 선언해서 사용하는 이유가 있을까요 ? 궁금합니다 ! 그리고 inline으로 선언한 이유도 궁금합니다 :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

먼저 Modifier를 확장 함수로 따로 선언하는 이유는 재사용성과 가독성 때문입니다! ripple 효과 없는 클릭 기능은 앱 전체에서 자주 사용되는 패턴인데, 매번 clickable에 indication nullMutableInteractionSource를 기억해서 작성하는 건 번거롭고 실수하기 쉽죠. 이렇게 확장 함수로 만들어두면 필요한 곳에서 간단히 Modifier.noRippleClickable { }로 호출할 수 있어서 코드가 훨씬 깔끔해지고 의도도 명확해집니다. 또한 나중에 로직을 수정해야 할 때도 한 곳만 고치면 되니까 유지보수 측면에서도 유리해요.
그리고 inline으로 선언한 이유는 성능 최적화 때문입니다. inline을 사용하면 함수 호출 시 람다가 객체로 생성되는 대신, 호출 지점에 코드가 직접 삽입돼요. UI 관련 코드는 리컴포지션 때마다 자주 실행되는데 특히 LazyColumn같은 리스트에서 각 아이템마다 이 Modifier를 사용한다고 생각해보면 람다 객체 생성 오버헤드를 줄이는 게 꽤 중요하답니다 ㅎㅎ

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

항상 Modifier가 중요하다고 들어서 공부를 해봤는데 아예 이렇게 따로 관리를 하는게 오버헤드가 훨씬 줄어드는 군요 ,,!! 감사합니다 😊

suspend inline fun <T> Result<T>.onLogFailure(
crossinline action: suspend (exception: Throwable) -> Unit
): Result<T> = onFailure { e ->
Timber.e(e)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

저번 합세에서도 log를 활용하는것보다 Timber를 사용하는 것을 추천했는데 아직 이유를 모르겠습니다.,. 궁금합니다 😭

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

안드로이드 기본 Log 클래스는 몇 가지 불편한 점들이 있어요. 가장 큰 문제는 TAG를 매번 수동으로 관리해야 한다는 거예요. Log.d(TAG, "message") 이런 식으로 쓰려면 클래스마다 private const val TAG = "MyActivity" 같은 걸 선언해야 하는데 이게 귀찮을 뿐만 아니라 클래스 이름이 바뀌면 TAG도 같이 수정해야 해서 실수하기 쉽죠. 리팩토링할 때도 놓치기 쉽구용.
Timber는 이런 문제를 자동으로 해결해줘요! Timber.d("message")만 쓰면 Timber가 알아서 호출한 클래스 이름을 TAG로 사용해줍니다. 코드도 더 간결해지고 실수할 여지도 없어지죠. 그리고 더 중요한 건 릴리즈 빌드 관리예요. 일반 Log는 릴리즈 빌드에서도 그대로 출력되기 때문에 ProGuard 설정으로 제거하거나 직접 BuildConfig.DEBUG로 감싸야 하는데 이것도 매번 하기 번거롭고 빼먹기 쉬워요. Timber는 Application 클래스에서 디버그 모드일 때만 Timber.plant(Timber.DebugTree())로 초기화하면 릴리즈 빌드에서는 자동으로 모든 로그가 무시돼요. 별도 설정 없이 안전하게 로그를 관리할 수 있는 거죠 ㅎㅎ

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

릴리즈 되었을 때는 그럼 로그가 안 보이군요 !! 너무 신기해요 감사합니다 !!

@@ -0,0 +1,13 @@
package com.cherrish.android.core.common.state

sealed interface UiState<out T> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

out은 찾아보니까 이 제네릭 타입을 결과로만 사용하려고 한다고 나오는데 맞을까요 ?? 정확히 조금 더 궁금합니다 !!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

네 맞아용! out 키워드는 공변성을 나타내는데, 쉽게 말하면 이 제네릭 타입 T를 생산만 하고 소비하지 않는다는 의미예요. 코드에서 보면 Success 클래스가 val data: T처럼 T를 결과로만 내보내고 있죠. 만약 fun setData(data: T) 같은 함수를 만들려고 하면 컴파일 에러가 나요. T를 파라미터로 받아서 소비하는 건 허용되지 않거든요.
그럼 왜 이렇게 할까요? 바로 타입 안정성을 유지하면서 유연한 타입 변환을 가능하게 하기 위해서예요. out 덕분에 UiState<String>UiState<Any>로 취급할 수 있어요. String은 Any의 하위 타입이니까 이런 변환이 안전하게 가능한 거죠. 이걸 공변성이라고 해요. 만약 out이 없으면 제네릭 타입이 정확히 일치해야만 하고 이런 유연한 변환이 불가능해요. 예를 들어 fun handleState(state: UiState<Any>) 같은 함수가 있을 때 User 타입의 UiState<User>를 넘길 수 있어요. out 덕분에 User가 Any의 하위 타입이면 자동으로 변환되는 거죠. 이게 없으면 타입별로 함수를 따로 만들어야 하는데 out 덕분에 하나의 함수로 모든 타입을 처리할 수 있답니다 ㅎㅎㅎ 결국 out은 이 타입은 안전하게 읽기만 할 거니까 더 넓은 타입으로 변환해도 괜찮다고 컴파일러에게 보장해주는 키워드예요. 그래서 UiState처럼 데이터를 담아서 전달만 하는 용도로 사용되는 타입에 딱 맞는 거죠!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

아 객체 타입을 한 번에 읽어줄 수 있는 역할을 하는 것이군요 ,,!! 감사합니다 !! ☺️

import kotlinx.serialization.Serializable

@Serializable
data class DummyResponseDto(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

이렇게 더미로 넣어두는 방법이 있군요 ,, 이러면 확실히 세미나에서 배운대로 구조화를 하는게 더욱 효과적일 것 같네요 ,,! 덕분에 공부한번 더 하게 됩니당 ,,! !

Copy link
Copy Markdown

@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 (2)
.github/workflows/pr_checker.yml (2)

1-6: 동시성 제어 추가를 고려해보세요.

워크플로우 기본 설정은 잘 구성되어 있습니다. 다만 동일한 PR에 대해 여러 워크플로우가 동시에 실행될 수 있어 CI 리소스가 낭비될 수 있습니다.

🔎 동시성 제어 추가 제안
 name: Cherrish CI
 
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
 on:
   pull_request:
     branches: [ develop ]

이렇게 하면 동일한 PR에 새로운 커밋이 푸시될 때 이전 실행 중인 워크플로우가 취소됩니다.


50-83: 중복된 설정 단계를 리팩토링하는 것을 권장합니다.

빌드 작업이 정상적으로 구성되어 있으나, lint 작업과 설정 단계가 거의 동일하게 중복되어 있습니다 (lines 55-80과 lines 13-38).

🔎 중복 제거 방법 제안

방법 1: Composite Action 생성 (권장)

.github/actions/setup-android-env/action.yml 파일을 생성하여 공통 설정을 재사용:

name: 'Setup Android Environment'
description: 'Setup Gradle, JDK, Android SDK and local.properties'
runs:
  using: "composite"
  steps:
    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@v4
      
    - name: Set up JDK 17
      uses: actions/setup-java@v5
      with:
        java-version: 17
        distribution: 'temurin'
        
    - name: Set up Android SDK
      uses: android-actions/setup-android@v3
      
    - name: Create local.properties
      shell: bash
      env:
        DEBUG_BASE_URL: ${{ env.DEBUG_BASE_URL }}
        RELEASE_BASE_URL: ${{ env.RELEASE_BASE_URL }}
      run: |
        echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties
        echo "debug.base.url=\"${DEBUG_BASE_URL}\"" >> local.properties
        echo "release.base.url=\"${RELEASE_BASE_URL}\"" >> local.properties
        
    - name: Grant permission to gradlew
      shell: bash
      run: chmod +x ./gradlew

그 다음 워크플로우에서 사용:

jobs:
  lint:
    name: PR Lint Check
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        
      - name: Setup Android Environment
        uses: ./.github/actions/setup-android-env
        env:
          DEBUG_BASE_URL: ${{ secrets.DEBUG_BASE_URL }}
          RELEASE_BASE_URL: ${{ secrets.RELEASE_BASE_URL }}
          
      - name: Run ktlint
        run: ./gradlew ktlintCheck
      # ... 나머지 단계

방법 2: build 작업이 lint 작업에 의존하도록 설정

   build:
     name: PR Build Check
     runs-on: ubuntu-latest
+    needs: lint

lint가 실패하면 build가 실행되지 않아 리소스를 절약할 수 있습니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d5404f and d10bf2f.

📒 Files selected for processing (2)
  • .github/workflows/pr_checker.yml
  • gradle.properties
🚧 Files skipped from review as they are similar to previous changes (1)
  • gradle.properties
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: PR Lint Check
  • GitHub Check: PR Build Check
🔇 Additional comments (1)
.github/workflows/pr_checker.yml (1)

8-49: Lint 작업이 잘 구성되어 있습니다.

ktlint 검사 및 리포트 업로드 설정이 적절합니다. 최신 액션 버전을 사용하고 있으며, setup-gradle@v4가 자동으로 캐싱을 처리합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

INIT🛠️ 프로젝트 초기 설정 나현🍒 나현 담당

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[INIT] 프로젝트 기초 세팅

4 participants