Conversation
- 멀티파트를 json으로 감싸지 않고 각 컨텐츠를 requestpart로 적용되도록 수정 - 이미지 없는 경우 json으로 보내지 않고 동일하게 멀티파트로 적용
📝 WalkthroughWalkthrough이 PR은 신규 평가(evaluate) 기능을 추가합니다. shared:data:evaluate, shared:domain:evaluate, shared:domain:model, shared:feature:evaluate 모듈과 관련된 Gradle 설정·의존성, EvaluateApi 및 리포지토리 구현, 도메인 유스케이스(Get/Post), Koin DI 모듈(evaluateDomainModule/evaluateDataModule/evaluateFeatureModule), 평가 UI(라우트/화면/뷰모델/상태/모델) 및 내비게이션(Detail/Evaluate/Tier) 파라미터 전달 변경을 포함하여 이전 평가 조회와 평가 제출 흐름을 구현합니다. Sequence Diagram(s)sequenceDiagram
participant UI as EvaluateRoute/UI
participant VM as EvaluateViewModel
participant UC_GET as GetEvaluationUseCase
participant UC_POST as PostEvaluationUseCase
participant Repo as EvaluateRepositoryImpl
participant API as EvaluateApi
participant Server
rect rgba(100,200,150,0.5)
Note over UI,Server: 이전 평가 조회
UI->>VM: getPreviousEvaluation(restaurantId)
VM->>UC_GET: invoke(restaurantId)
UC_GET->>Repo: getEvaluation(restaurantId)
Repo->>API: getEvaluation(restaurantId)
API->>Server: GET /api/v2/auth/restaurants/{id}/evaluation
Server-->>API: EvaluationResponse
API-->>Repo: EvaluationResponse
Repo-->>UC_GET: PreviousEvaluation (toDomain)
UC_GET-->>VM: PreviousEvaluation
VM-->>UI: uiState = Success(Evaluation)
end
rect rgba(150,150,200,0.5)
Note over UI,Server: 평가 제출
UI->>VM: submitEvaluation()
VM->>UC_POST: invoke(restaurantId, score, situations, comment, imageBytes)
UC_POST->>Repo: postEvaluation(restaurantId, request, imageBytes)
Repo->>API: postEvaluation(restaurantId, multipart)
API->>Server: POST /api/v2/auth/restaurants/{id}/evaluation (multipart)
Server-->>API: EvaluationResponse
API-->>Repo: success
Repo-->>UC_POST: success
UC_POST-->>VM: success
VM-->>UI: submitState = Success -> 트리거 onBackClick()
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
shared/feature/evaluate/build.gradle.kts (1)
12-12:⚠️ Potential issue | 🟡 Minor오타: "evaluteKit" → "evaluateKit"
iOS 프레임워크 이름에 오타가 있습니다.
evalute가 아닌evaluate가 맞습니다.🐛 수정 제안
- val xcfName = "shared:feature:evaluteKit" + val xcfName = "shared:feature:evaluateKit"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/feature/evaluate/build.gradle.kts` at line 12, The string assigned to val xcfName contains a typo ("shared:feature:evaluteKit"); update the value of xcfName to the correct framework name "shared:feature:evaluateKit" so the iOS framework name is spelled correctly (locate and modify the val xcfName declaration).
🧹 Nitpick comments (7)
shared/domain/evaluate/build.gradle.kts (1)
60-63: 빈 dependency 블록 정리를 고려해 보세요.
androidMain과iosMain소스셋의 dependencies 블록이 비어 있습니다. 현재 플랫폼별 의존성이 없다면 해당 블록을 제거하여 보일러플레이트를 줄일 수 있습니다.♻️ 제안된 정리
- androidMain { - dependencies { - } - }- iosMain { - dependencies { - } - }Also applies to: 73-76
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/domain/evaluate/build.gradle.kts` around lines 60 - 63, The androidMain and iosMain sourceSet dependency blocks are empty; remove these empty dependencies { } blocks from the androidMain and iosMain sourceSets in the build.gradle.kts (or leave a brief comment if you expect platform-specific deps soon) to reduce boilerplate; locate the androidMain and iosMain declarations in the shared/domain/evaluate module and delete the empty dependencies blocks (or replace with a TODO comment) so the script remains clean.composeApp/build.gradle.kts (1)
58-60::shared:core:logging의존성이 중복되어 있습니다.Line 58과 60에 동일한 의존성이 두 번 선언되어 있습니다.
♻️ 중복 제거 제안
implementation(project(":shared:core:designSystem")) implementation(project(":shared:core:logging")) implementation(project(":shared:core:config")) - implementation(project(":shared:core:logging")) implementation(project(":shared:core:serialization"))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@composeApp/build.gradle.kts` around lines 58 - 60, 삭제해야 할 중복 의존성이 있습니다: implementation(project(":shared:core:logging"))가 두 번 선언되어 있으므로 하나만 남기고 중복된 선언을 제거하세요; build script 내에서 동일한 symbol인 implementation(project(":shared:core:logging"))를 찾아 하나로 정리하면 됩니다.settings.gradle.kts (1)
41-49: Core 모듈에 중복 항목이 있습니다.
:shared:core:serialization과:shared:core:presentation이 두 번 포함되어 있습니다. 빌드에는 영향을 주지 않지만 정리하는 것이 좋습니다.♻️ 중복 제거 제안
include( ":shared:core:designSystem", ":shared:core:presentation", ":shared:core:serialization", ":shared:core:logging", ":shared:core:config", - ":shared:core:serialization", - ":shared:core:presentation", )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@settings.gradle.kts` around lines 41 - 49, The include list in settings.gradle.kts contains duplicate module entries (:shared:core:serialization and :shared:core:presentation); remove the repeated occurrences so each core module appears only once in the include(...) call, keeping a single entry for :shared:core:serialization and a single entry for :shared:core:presentation to clean up the module list.shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/model/EvaluateRestaurant.kt (1)
11-11: 공개 모델에서는ArrayList대신List를 노출하는 편이 낫습니다.지금 타입은 호출부가 컬렉션을 제자리에서 바꿀 수 있어서, 상태 객체를 들고 있는 Compose/StateFlow 쪽에서 변경 추적이 꼬이기 쉽습니다. 여기서는 읽기 전용 인터페이스로 낮추고 기본값도
emptyList()로 두는 편이 안전합니다.변경 예시
data class EvaluateRestaurant( val restaurantId: Int, val restaurantName: String, val mainTier: Int, val restaurantCuisine: String, val restaurantCuisineImgUrl: String, val restaurantPosition: String, val restaurantAddress: String, - val situationList: ArrayList<String>, + val situationList: List<String>, val partnershipInfo: String, ) { companion object { fun empty() = EvaluateRestaurant( restaurantId = 0, restaurantName = "", mainTier = 0, restaurantCuisine = "", restaurantCuisineImgUrl = "", restaurantPosition = "", restaurantAddress = "", - situationList = arrayListOf(), + situationList = emptyList(), partnershipInfo = "", ) } }Also applies to: 23-23
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/model/EvaluateRestaurant.kt` at line 11, The public data model EvaluateRestaurant exposes a mutable ArrayList via the property situationList (and similarly any other ArrayList-typed public properties), which allows callers to mutate internal state; change the property types to read-only List<String> and provide a safe default of emptyList() so callers get an immutable view (e.g., update the situationList declaration and any other ArrayList fields in EvaluateRestaurant to List<String> = emptyList()); ensure constructors/serialization still populate lists but surface only List in the public API.shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.kt (1)
38-38: 불필요한ArrayList변환
evaluate.situationList는 이미List<String>이므로ArrayList()로 감싸는 것은 불필요합니다.EvaluateRestaurant.situationList가ArrayList를 명시적으로 요구하지 않는다면 제거해도 됩니다.♻️ 제안된 수정
- situationList = ArrayList(evaluate.situationList), + situationList = evaluate.situationList,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.kt` at line 38, The code wraps evaluate.situationList with ArrayList unnecessarily; update the call site in EvaluateNavGraph (the place building the EvaluateRestaurant/constructor that currently uses situationList = ArrayList(evaluate.situationList)) to pass evaluate.situationList directly instead of ArrayList(...), unless the target type (EvaluateRestaurant.situationList) explicitly requires a mutable ArrayList—in which case convert at the model/constructor level; locate the usage of situationList in EvaluateNavGraph and remove the ArrayList(...) wrapper to use the existing List<String>.shared/data/evaluate/src/commonMain/kotlin/com/kus/kustaurant/evaluate/api/EvaluateApi.kt (1)
41-50: 이미지 업로드 시 Content-Type이 항상image/jpeg로 고정됨사용자가 PNG나 다른 형식의 이미지를 선택할 경우, 실제 이미지 형식과 Content-Type이 일치하지 않을 수 있습니다. 이미지 형식을 동적으로 감지하거나 파라미터로 전달받는 것을 고려해주세요.
♻️ 이미지 타입 파라미터 추가 제안
suspend fun postEvaluation( restaurantId: Long, request: EvaluationRequest, imageBytes: ByteArray?, + imageContentType: String = "image/jpeg", ) { client.post("/api/v2/auth/restaurants/$restaurantId/evaluation") { setBody( MultiPartFormDataContent( formData { // ... other fields ... imageBytes?.let { append( key = "newImage", value = it, headers = Headers.build { - append(HttpHeaders.ContentType, "image/jpeg") + append(HttpHeaders.ContentType, imageContentType) append(HttpHeaders.ContentDisposition, "filename=evaluation.jpg") } ) } } ) ) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/data/evaluate/src/commonMain/kotlin/com/kus/kustaurant/evaluate/api/EvaluateApi.kt` around lines 41 - 50, 현재 EvaluateApi.kt의 multipart 생성에서 imageBytes를 첨부할 때 Content-Type을 고정("image/jpeg")하고 있어 실제 업로드 이미지와 불일치할 수 있습니다; 수정 방법은 imageBytes 처리 위치(이미지 첨부 블록에서 imageBytes?.let { ... }와 Headers.build 사용 부분)를 찾아 업로드 시점에 이미지의 MIME 타입을 동적으로 결정하거나 외부에서 imageMimeType (예: "image/png" 등)을 파라미터로 받아 HttpHeaders.ContentType에 해당 값을 사용하도록 바꾸고, ContentDisposition의 파일명 확장자도 MIME 타입 또는 전달된 파일명에 맞춰 설정하도록 변경하세요.shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateViewModel.kt (1)
45-47: 모든 예외를 네트워크 오류로 처리함
onFailure블록에서 모든 예외를UiError.Network로 처리하면 실제 오류 원인(인증 오류, 서버 오류, 파싱 오류 등)을 파악하기 어렵습니다. 최소한 로깅을 추가하거나, 예외 타입에 따라 다른UiError를 반환하는 것을 고려해주세요.♻️ 오류 분류 예시
}.onFailure { throwable -> val error = when (throwable) { is java.net.UnknownHostException -> UiError.Network is kotlinx.serialization.SerializationException -> UiError.Parse else -> UiError.Unknown } _uiState.update { it.copy(evaluation = UiState.Failure(error)) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateViewModel.kt` around lines 45 - 47, 현재 EvaluateViewModel의 onFailure 블록이 모든 예외를 UiError.Network로만 처리하고 있어 실제 원인을 알기 어렵습니다; _uiState.update { it.copy(evaluation = UiState.Failure(...)) } 호출부(즉 onFailure 핸들러)에서 Throwable을 받아 예외 타입별로 매핑(예: UnknownHostException -> UiError.Network, SerializationException -> UiError.Parse, 인증/HTTP 예외는 UiError.Auth/Server 등)하고 기본 케이스는 UiError.Unknown으로 설정한 뒤 그 값을 사용해 UiState.Failure를 업데이트하도록 변경하고, 동시에 로깅(예: logger.error 또는 적절한 로그 유틸)으로 throwable 메시지/스택트레이스를 남기세요; 대상 식별자는 EvaluateViewModel의 onFailure 블록, _uiState.update 및 UiState.Failure/U iError 열거형입니다.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@shared/data/evaluate/build.gradle.kts`:
- Around line 47-57: The commonMain source set is missing the Koin core
dependency, causing compilation failures where Koin DSL symbols (module,
singleOf, bind from evaluateDataModule.kt and imports from
org.koin.core.module.dsl and org.koin.dsl) are used; add the Koin core
dependency (libs.koin.core) to the commonMain.dependencies block in the same
Gradle Kotlin DSL file so common/shared code (iOS/desktop/common) can compile
(update the commonMain { dependencies { ... } } section to include
libs.koin.core).
In `@shared/feature/evaluate/build.gradle.kts`:
- Line 61: resources.srcDir("src/commonMain/composeResources")가 현재 dependencies
블록 안에 잘못 위치해 있어 적용되지 않습니다; 이 줄을 dependencies 블록 밖의 source set 설정 영역(예:
kotlin.sourceSets 또는 sourceSets["commonMain"]의 resources 설정)으로 옮겨서 commonMain
소스셋의 리소스 디렉토리로 올바르게 등록되도록 수정하세요; 참조할 심볼: resources.srcDir 및 dependencies 블록(현재
위치)과 commonMain source set(이동 대상).
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/model/EvaluateRestaurant.kt`:
- Line 4: The UI model EvaluateRestaurant has restaurantId typed as Int which
mismatches the domain/data layer Long; change the property restaurantId: Int to
restaurantId: Long in the EvaluateRestaurant data class and update all
constructors, factory/mapping functions and serializers/deserializers that
create or consume EvaluateRestaurant so they accept/produce Long (including any
copy/equals/hash usages and JSON/Kotlinx serialization code) to avoid
downcasting issues; also update the other occurrence referenced (same symbol at
the other location) to keep the type consistent across the codebase.
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.kt`:
- Around line 29-31: The code converts evaluate.restaurantId (Long) to Int when
constructing EvaluateRestaurant (and similarly for DetailRestaurant), risking
overflow; update the data models to use Long for restaurantId instead of Int
(change EvaluateRestaurant.restaurantId and DetailRestaurant.restaurantId to
Long) and then remove the .toInt() conversions in callers such as
EvaluateNavGraph where EvaluateRestaurant(...) is constructed using
evaluate.restaurantId; ensure you update all constructors/usages of
EvaluateRestaurant and DetailRestaurant across the codebase to accept Long.
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt`:
- Around line 83-95: EvaluateScreen currently handles UiState.Failure for
`evaluation` but ignores failures in `uiState.submitState`; inside the
EvaluateScreen composable add a LaunchedEffect keyed on `uiState.submitState`
that when `is UiState.Success` calls `onBackClick()` and when `is
UiState.Failure` shows user feedback (e.g., use an existing `snackbarHostState`
or a dialog to display the error message and provide a retry action that calls
the submit retry method on the ViewModel); reference `uiState.submitState`,
`LaunchedEffect`, `onBackClick`, and the ViewModel submit/retry function to
locate where to hook this behavior.
In
`@shared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/ui/TierScreen.kt`:
- Line 61: The Map tab's restaurant selection isn't wired to the new shared
callback: connect the MAP-related click handlers (the empty lambda assigned to
onBottomSheetClick and any map selection handlers in TierScreen) to call the
same onNavigateRestaurantDetail(Long) passed into TierScreen instead of being
left empty; specifically replace the empty onBottomSheetClick = { id -> } with
onBottomSheetClick = { id -> onNavigateRestaurantDetail(id) } (and any other
map/tab callbacks) so MAP and LIST both use
onRestaurantClick/onNavigateRestaurantDetail consistently.
---
Outside diff comments:
In `@shared/feature/evaluate/build.gradle.kts`:
- Line 12: The string assigned to val xcfName contains a typo
("shared:feature:evaluteKit"); update the value of xcfName to the correct
framework name "shared:feature:evaluateKit" so the iOS framework name is spelled
correctly (locate and modify the val xcfName declaration).
---
Nitpick comments:
In `@composeApp/build.gradle.kts`:
- Around line 58-60: 삭제해야 할 중복 의존성이 있습니다:
implementation(project(":shared:core:logging"))가 두 번 선언되어 있으므로 하나만 남기고 중복된 선언을
제거하세요; build script 내에서 동일한 symbol인
implementation(project(":shared:core:logging"))를 찾아 하나로 정리하면 됩니다.
In `@settings.gradle.kts`:
- Around line 41-49: The include list in settings.gradle.kts contains duplicate
module entries (:shared:core:serialization and :shared:core:presentation);
remove the repeated occurrences so each core module appears only once in the
include(...) call, keeping a single entry for :shared:core:serialization and a
single entry for :shared:core:presentation to clean up the module list.
In
`@shared/data/evaluate/src/commonMain/kotlin/com/kus/kustaurant/evaluate/api/EvaluateApi.kt`:
- Around line 41-50: 현재 EvaluateApi.kt의 multipart 생성에서 imageBytes를 첨부할 때
Content-Type을 고정("image/jpeg")하고 있어 실제 업로드 이미지와 불일치할 수 있습니다; 수정 방법은 imageBytes
처리 위치(이미지 첨부 블록에서 imageBytes?.let { ... }와 Headers.build 사용 부분)를 찾아 업로드 시점에 이미지의
MIME 타입을 동적으로 결정하거나 외부에서 imageMimeType (예: "image/png" 등)을 파라미터로 받아
HttpHeaders.ContentType에 해당 값을 사용하도록 바꾸고, ContentDisposition의 파일명 확장자도 MIME 타입
또는 전달된 파일명에 맞춰 설정하도록 변경하세요.
In `@shared/domain/evaluate/build.gradle.kts`:
- Around line 60-63: The androidMain and iosMain sourceSet dependency blocks are
empty; remove these empty dependencies { } blocks from the androidMain and
iosMain sourceSets in the build.gradle.kts (or leave a brief comment if you
expect platform-specific deps soon) to reduce boilerplate; locate the
androidMain and iosMain declarations in the shared/domain/evaluate module and
delete the empty dependencies blocks (or replace with a TODO comment) so the
script remains clean.
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/model/EvaluateRestaurant.kt`:
- Line 11: The public data model EvaluateRestaurant exposes a mutable ArrayList
via the property situationList (and similarly any other ArrayList-typed public
properties), which allows callers to mutate internal state; change the property
types to read-only List<String> and provide a safe default of emptyList() so
callers get an immutable view (e.g., update the situationList declaration and
any other ArrayList fields in EvaluateRestaurant to List<String> = emptyList());
ensure constructors/serialization still populate lists but surface only List in
the public API.
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.kt`:
- Line 38: The code wraps evaluate.situationList with ArrayList unnecessarily;
update the call site in EvaluateNavGraph (the place building the
EvaluateRestaurant/constructor that currently uses situationList =
ArrayList(evaluate.situationList)) to pass evaluate.situationList directly
instead of ArrayList(...), unless the target type
(EvaluateRestaurant.situationList) explicitly requires a mutable ArrayList—in
which case convert at the model/constructor level; locate the usage of
situationList in EvaluateNavGraph and remove the ArrayList(...) wrapper to use
the existing List<String>.
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateViewModel.kt`:
- Around line 45-47: 현재 EvaluateViewModel의 onFailure 블록이 모든 예외를
UiError.Network로만 처리하고 있어 실제 원인을 알기 어렵습니다; _uiState.update { it.copy(evaluation
= UiState.Failure(...)) } 호출부(즉 onFailure 핸들러)에서 Throwable을 받아 예외 타입별로 매핑(예:
UnknownHostException -> UiError.Network, SerializationException ->
UiError.Parse, 인증/HTTP 예외는 UiError.Auth/Server 등)하고 기본 케이스는 UiError.Unknown으로
설정한 뒤 그 값을 사용해 UiState.Failure를 업데이트하도록 변경하고, 동시에 로깅(예: logger.error 또는 적절한 로그
유틸)으로 throwable 메시지/스택트레이스를 남기세요; 대상 식별자는 EvaluateViewModel의 onFailure 블록,
_uiState.update 및 UiState.Failure/U iError 열거형입니다.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: a07a2b12-e294-4357-8668-cf5e49de4950
📒 Files selected for processing (35)
composeApp/build.gradle.ktscomposeApp/src/commonMain/kotlin/com/kus/kustaurant/di/initKoin.ktcomposeApp/src/commonMain/kotlin/com/kus/kustaurant/navigation/KusNavHost.ktsettings.gradle.ktsshared/data/evaluate/build.gradle.ktsshared/data/evaluate/src/androidMain/AndroidManifest.xmlshared/data/evaluate/src/commonMain/kotlin/com/kus/kustaurant/evaluate/api/EvaluateApi.ktshared/data/evaluate/src/commonMain/kotlin/com/kus/kustaurant/evaluate/di/evaluateDataModule.ktshared/data/evaluate/src/commonMain/kotlin/com/kus/kustaurant/evaluate/remote/mapper/EvaluateMapper.ktshared/data/evaluate/src/commonMain/kotlin/com/kus/kustaurant/evaluate/remote/request/EvaluationRequest.ktshared/data/evaluate/src/commonMain/kotlin/com/kus/kustaurant/evaluate/remote/response/EvaluationResponse.ktshared/data/evaluate/src/commonMain/kotlin/com/kus/kustaurant/evaluate/repositoryimpl/EvaluateRepositoryImpl.ktshared/domain/evaluate/build.gradle.ktsshared/domain/evaluate/src/androidMain/AndroidManifest.xmlshared/domain/evaluate/src/commonMain/kotlin/com/kus/shared/domain/evaluate/di/evaluateDomainModule.ktshared/domain/evaluate/src/commonMain/kotlin/com/kus/shared/domain/evaluate/repository/EvaluateRepository.ktshared/domain/evaluate/src/commonMain/kotlin/com/kus/shared/domain/evaluate/usecase/GetEvaluationUseCase.ktshared/domain/evaluate/src/commonMain/kotlin/com/kus/shared/domain/evaluate/usecase/PostEvaluationUseCase.ktshared/domain/model/src/commonMain/kotlin/com/kus/shared/domain/model/evaluate/PreviousEvaluation.ktshared/feature/detail/src/commonMain/kotlin/com/kus/feature/detail/navigation/DetailNavGraph.ktshared/feature/detail/src/commonMain/kotlin/com/kus/feature/detail/ui/DetailScreen.ktshared/feature/evaluate/build.gradle.ktsshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/component/EvaluationRestInfoCard.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/di/evaluateFeatureModule.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/model/EvaluateRestaurant.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/model/Evaluation.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateRoute.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/state/EvaluateUiState.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateContract.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateViewModel.ktshared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/navigation/TierGraph.ktshared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/navigation/TierRoute.ktshared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/ui/TierScreen.kt
💤 Files with no reviewable changes (2)
- shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateRoute.kt
- shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateContract.kt
| implementation(project(":shared:domain:evaluate")) | ||
| implementation(project(":shared:domain:model")) | ||
|
|
||
| resources.srcDir("src/commonMain/composeResources") |
There was a problem hiding this comment.
resources.srcDir가 dependencies 블록 안에 잘못 배치되어 있습니다.
resources.srcDir는 dependencies 블록 바깥의 source set 설정에 위치해야 합니다. 현재 위치에서는 의도한 대로 동작하지 않을 수 있습니다.
♻️ 수정 제안
commonMain {
+ resources.srcDir("src/commonMain/composeResources")
dependencies {
implementation(compose.runtime)
// ... other dependencies ...
implementation(project(":shared:domain:model"))
-
- resources.srcDir("src/commonMain/composeResources")
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@shared/feature/evaluate/build.gradle.kts` at line 61,
resources.srcDir("src/commonMain/composeResources")가 현재 dependencies 블록 안에 잘못
위치해 있어 적용되지 않습니다; 이 줄을 dependencies 블록 밖의 source set 설정 영역(예: kotlin.sourceSets
또는 sourceSets["commonMain"]의 resources 설정)으로 옮겨서 commonMain 소스셋의 리소스 디렉토리로 올바르게
등록되도록 수정하세요; 참조할 심볼: resources.srcDir 및 dependencies 블록(현재 위치)과 commonMain
source set(이동 대상).
.../feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/model/EvaluateRestaurant.kt
Outdated
Show resolved
Hide resolved
...ature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.kt
Outdated
Show resolved
Hide resolved
| is UiState.Failure -> { | ||
| Box( | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| .background(color = KusTheme.colors.c_FFFFFF), | ||
| contentAlignment = Alignment.Center, | ||
| ) { | ||
| Text(text = "서버 연결이 불안정합니다. 다시 시도해주세요.") | ||
| } | ||
| } | ||
|
|
||
| is UiState.Idle -> {} | ||
| } |
There was a problem hiding this comment.
제출 실패 시 사용자 피드백 없음
UiState.Failure 상태가 evaluation에는 처리되지만, submitState의 실패는 UI에서 처리되지 않습니다. 제출 실패 시 사용자에게 에러 메시지를 표시하거나 재시도 옵션을 제공해야 합니다.
💡 제출 실패 처리 추가 제안
LaunchedEffect(uiState.submitState) {
when (val state = uiState.submitState) {
is UiState.Success -> onBackClick()
is UiState.Failure -> {
// TODO: Show error snackbar or dialog
// e.g., snackbarHostState.showSnackbar("제출에 실패했습니다. 다시 시도해주세요.")
}
else -> {}
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt`
around lines 83 - 95, EvaluateScreen currently handles UiState.Failure for
`evaluation` but ignores failures in `uiState.submitState`; inside the
EvaluateScreen composable add a LaunchedEffect keyed on `uiState.submitState`
that when `is UiState.Success` calls `onBackClick()` and when `is
UiState.Failure` shows user feedback (e.g., use an existing `snackbarHostState`
or a dialog to display the error message and provide a retry action that calls
the submit retry method on the ViewModel); reference `uiState.submitState`,
`LaunchedEffect`, `onBackClick`, and the ViewModel submit/retry function to
locate where to hook this behavior.
| onAlarmClick: () -> Unit = {}, | ||
| onFilterClick: () -> Unit = {}, | ||
| onNavigateRestaurantDetail: () -> Unit = {}, | ||
| onNavigateRestaurantDetail: (Long) -> Unit = {}, |
There was a problem hiding this comment.
MAP 탭 상세 이동은 아직 연결되지 않았습니다.
onNavigateRestaurantDetail를 추가했지만 현재 실제 연결은 LIST 탭의 onRestaurantClick뿐입니다. 같은 파일의 Line 127은 onBottomSheetClick = { id -> }로 비워져 있어서 MAP 탭에서 선택한 식당 상세 화면으로는 이동할 수 없습니다. Tier 화면 전체에서 상세 이동을 지원하려는 변경이라면 이 경로도 같은 콜백으로 연결해야 합니다.
Also applies to: 113-127
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@shared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/ui/TierScreen.kt`
at line 61, The Map tab's restaurant selection isn't wired to the new shared
callback: connect the MAP-related click handlers (the empty lambda assigned to
onBottomSheetClick and any map selection handlers in TierScreen) to call the
same onNavigateRestaurantDetail(Long) passed into TierScreen instead of being
left empty; specifically replace the empty onBottomSheetClick = { id -> } with
onBottomSheetClick = { id -> onNavigateRestaurantDetail(id) } (and any other
map/tab callbacks) so MAP and LIST both use
onRestaurantClick/onNavigateRestaurantDetail consistently.
| when (val evaluationState = uiState.evaluation) { | ||
| is UiState.Loading -> { | ||
| Box( | ||
| modifier = Modifier.fillMaxSize(), |
There was a problem hiding this comment.
@imtaejugkim 영상 5초 부분에서 확인했는데, 여기서 background 값을 흰색으로 주면 좋을 것 같아요!
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.kt (1)
38-38:ArrayList대신List사용 권장
EvaluateRestaurant가ArrayList<String>대신List<String>을 받도록 수정하면, 호출부에서 명시적 변환이 필요 없어집니다. 인터페이스 타입 사용이 Kotlin 권장 패턴입니다.♻️ EvaluateRestaurant 모델 수정 제안
// EvaluateRestaurant.kt data class EvaluateRestaurant( ... - val situationList: ArrayList<String>, + val situationList: List<String>, ... )그러면 이 파일에서:
- situationList = ArrayList(evaluate.situationList), + situationList = evaluate.situationList,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.kt` at line 38, 현재 호출부에서 situationList = ArrayList(evaluate.situationList)로 강제 변환을 하고 있는데, EvaluateRestaurant 모델을 ArrayList<String> 대신 List<String>으로 변경하면 이 변환이 불필요해집니다; EvaluateRestaurant의 상황 필드 타입을 List<String>으로 수정하고, 호출부(EvaluateNavGraph.kt)의 situationList 할당에서 ArrayList(...) 생성자 호출을 제거하고 evaluate.situationList를 그대로 전달하도록 변경하세요 (참고 식별자: EvaluateRestaurant, situationList, evaluate.situationList).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt`:
- Around line 59-95: The top app bar (KusTopBar) should be rendered outside the
when (val evaluationState = uiState.evaluation) so it's always visible across
UiState.Loading, UiState.Failure and UiState.Success; move the KusTopBar out of
EvaluateSuccessScreen and pass onBackClick into it so system/back navigation
always works. In the UiState.Failure branch add a visible retry action (e.g., a
Button or action in KusTopBar) that calls the viewModel retry method (e.g.,
viewModel.fetchEvaluation() or an existing retry entry point) and keep the
current failure message; keep EvaluateSuccessScreen usage for UiState.Success
unchanged. Ensure the back handler uses onBackClick and the retry handler calls
the viewModel retry function.
---
Nitpick comments:
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.kt`:
- Line 38: 현재 호출부에서 situationList = ArrayList(evaluate.situationList)로 강제 변환을 하고
있는데, EvaluateRestaurant 모델을 ArrayList<String> 대신 List<String>으로 변경하면 이 변환이
불필요해집니다; EvaluateRestaurant의 상황 필드 타입을 List<String>으로 수정하고,
호출부(EvaluateNavGraph.kt)의 situationList 할당에서 ArrayList(...) 생성자 호출을 제거하고
evaluate.situationList를 그대로 전달하도록 변경하세요 (참고 식별자: EvaluateRestaurant,
situationList, evaluate.situationList).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 779d7cf1-7ba0-4d74-b415-e53494f89031
📒 Files selected for processing (4)
shared/data/evaluate/build.gradle.ktsshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/model/EvaluateRestaurant.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/navigation/EvaluateNavGraph.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt
🚧 Files skipped from review as they are similar to previous changes (2)
- shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/model/EvaluateRestaurant.kt
- shared/data/evaluate/build.gradle.kts
shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt (1)
53-57:⚠️ Potential issue | 🟠 Major제출 실패가 여전히 UI에서 소비되지 않습니다.
submitState에서 성공만 처리하고 실패는 무시해서, 네트워크 오류 시 사용자는 왜 제출이 안 됐는지 알 수 없습니다. 최소한 snackbar/dialog로 실패를 알리고 재시도 액션을 연결하는 처리가 필요합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt` around lines 53 - 57, Currently LaunchedEffect(uiState.submitState) only handles UiState.Success and ignores failures; update the effect to handle all submit states (e.g., when or if/else on uiState.submitState) so UiState.Failure is consumed and surfaces a snackbar/dialog with the error message and a retry action, while only calling onBackClick for UiState.Success; after handling a failure make sure to reset/consume the submit state (e.g., call a viewModel.resetSubmitState() or emit UiState.Idle) so the error doesn't re-fire on recomposition.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@composeApp/src/commonMain/kotlin/com/kus/kustaurant/navigation/KusNavHost.kt`:
- Around line 231-245: navigateToEvaluate currently only navigates to Evaluate
and never wires a result back, so you must send a refresh/result via the
NavController savedStateHandle (or an explicit result callback) when the
evaluation completes; update the navigateToEvaluate lambda to set a known
result-key (e.g., "evaluate_result") on the originating
backStackEntry.savedStateHandle before navigating or attach a callback to the
destination so Evaluate can write to
navController.previousBackStackEntry?.savedStateHandle on success, and update
the Detail/Tier screens to observe that key (in their composables or in
tierNavGraph/communityNavGraph) and refresh when it changes; reference
navigateToEvaluate, Evaluate, onBackClick and the destination
backStackEntry.savedStateHandle so the reviewer can locate the spots to add the
set/get logic.
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt`:
- Around line 48-50: The current LaunchedEffect uses restaurantId as the key but
reads both restaurant and restaurantId, causing uiState.restaurant not to update
when only restaurant metadata changes; split the side-effects into two
LaunchedEffect blocks: one LaunchedEffect(key1 = restaurant) that calls
viewModel.initRestaurant(restaurant) and another LaunchedEffect(key1 =
restaurantId) that calls viewModel.getPreviousEvaluation(restaurantId), so
initRestaurant reacts only to restaurant changes and getPreviousEvaluation runs
only when the ID changes (refer to LaunchedEffect, viewModel.initRestaurant,
viewModel.getPreviousEvaluation, restaurantId, restaurant, uiState.restaurant).
---
Duplicate comments:
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt`:
- Around line 53-57: Currently LaunchedEffect(uiState.submitState) only handles
UiState.Success and ignores failures; update the effect to handle all submit
states (e.g., when or if/else on uiState.submitState) so UiState.Failure is
consumed and surfaces a snackbar/dialog with the error message and a retry
action, while only calling onBackClick for UiState.Success; after handling a
failure make sure to reset/consume the submit state (e.g., call a
viewModel.resetSubmitState() or emit UiState.Idle) so the error doesn't re-fire
on recomposition.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 57c578ac-9ea8-4342-9478-82da8f9e1930
📒 Files selected for processing (10)
composeApp/build.gradle.ktscomposeApp/src/commonMain/kotlin/com/kus/kustaurant/App.ktcomposeApp/src/commonMain/kotlin/com/kus/kustaurant/di/initKoin.ktcomposeApp/src/commonMain/kotlin/com/kus/kustaurant/navigation/KusNavHost.ktsettings.gradle.ktsshared/core/designSystem/src/commonMain/kotlin/com/kus/designsystem/component/KusButton.ktshared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.ktshared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/navigation/TierGraph.ktshared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/navigation/TierRoute.ktshared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/ui/TierScreen.kt
🚧 Files skipped from review as they are similar to previous changes (3)
- composeApp/build.gradle.kts
- shared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/ui/TierScreen.kt
- shared/feature/tier/src/commonMain/kotlin/com/kus/feature/tier/navigation/TierRoute.kt
| navigateToEvaluate = { restaurant -> | ||
| navController.navigate( | ||
| Evaluate( | ||
| restaurantId = restaurant.restaurantId, | ||
| restaurantName = restaurant.restaurantName, | ||
| mainTier = restaurant.mainTier, | ||
| restaurantCuisine = restaurant.restaurantCuisine, | ||
| restaurantCuisineImgUrl = restaurant.restaurantCuisineImgUrl, | ||
| restaurantPosition = restaurant.restaurantPosition, | ||
| restaurantAddress = restaurant.restaurantAddress, | ||
| situationList = restaurant.situationList, | ||
| partnershipInfo = restaurant.partnershipInfo, | ||
| ) | ||
| ) | ||
| }, |
There was a problem hiding this comment.
평가 완료 후 이전 화면을 갱신할 결과 전달 경로가 없습니다.
여기서는 Evaluate로 이동만 하고, 이 파일의 evaluate 흐름은 onBackClick만 연결돼 있어서 제출 성공 여부나 갱신 신호가 Detail/Tier로 돌아오지 않습니다. 같은 파일의 tierNavGraph/communityNavGraph처럼 savedStateHandle이나 명시적 result callback을 붙이지 않으면, 사용자는 방금 평가한 내용이 즉시 반영되지 않은 화면을 보게 됩니다.
원하시면 savedStateHandle 기반 결과 전달 키 설계까지 같이 정리해드릴게요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@composeApp/src/commonMain/kotlin/com/kus/kustaurant/navigation/KusNavHost.kt`
around lines 231 - 245, navigateToEvaluate currently only navigates to Evaluate
and never wires a result back, so you must send a refresh/result via the
NavController savedStateHandle (or an explicit result callback) when the
evaluation completes; update the navigateToEvaluate lambda to set a known
result-key (e.g., "evaluate_result") on the originating
backStackEntry.savedStateHandle before navigating or attach a callback to the
destination so Evaluate can write to
navController.previousBackStackEntry?.savedStateHandle on success, and update
the Detail/Tier screens to observe that key (in their composables or in
tierNavGraph/communityNavGraph) and refresh when it changes; reference
navigateToEvaluate, Evaluate, onBackClick and the destination
backStackEntry.savedStateHandle so the reviewer can locate the spots to add the
set/get logic.
| LaunchedEffect(restaurantId) { | ||
| viewModel.initRestaurant(restaurant) | ||
| viewModel.getPreviousEvaluation(restaurantId) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and read the target file
find . -path "*shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt" 2>/dev/null | head -5Repository: kustaurant/Android-CMP
Length of output: 159
🏁 Script executed:
# List files to find the correct path
fd "EvaluateScreen.kt" 2>/dev/nullRepository: kustaurant/Android-CMP
Length of output: 157
🏁 Script executed:
# Search for EvaluateScreen.kt more broadly
find . -name "EvaluateScreen.kt" -type f 2>/dev/nullRepository: kustaurant/Android-CMP
Length of output: 159
🏁 Script executed:
# Read the file to see the code at lines 48-50 and surrounding context
cat -n ./shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt | head -80Repository: kustaurant/Android-CMP
Length of output: 3642
🏁 Script executed:
# Find the EvaluateViewModel to understand the methods
find . -name "EvaluateViewModel*" -type f 2>/dev/nullRepository: kustaurant/Android-CMP
Length of output: 162
🏁 Script executed:
# Read the EvaluateViewModel to understand the methods
cat -n ./shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateViewModel.ktRepository: kustaurant/Android-CMP
Length of output: 5215
initRestaurant과 조회를 같은 LaunchedEffect에 묶지 않는 편이 안전합니다.
현재 restaurantId만 key로 두고 restaurant도 함께 읽고 있어서, 같은 ID로 식당 메타데이터만 바뀌면 uiState.restaurant가 갱신되지 않습니다. 반대로 key에 restaurant를 추가하면 단순 메타데이터 변경에도 조회가 다시 나갑니다. 두 effect로 분리하는 쪽이 부작용이 적습니다.
♻️ 제안 수정안
- LaunchedEffect(restaurantId) {
- viewModel.initRestaurant(restaurant)
- viewModel.getPreviousEvaluation(restaurantId)
- }
+ LaunchedEffect(restaurant) {
+ viewModel.initRestaurant(restaurant)
+ }
+
+ LaunchedEffect(restaurantId) {
+ viewModel.getPreviousEvaluation(restaurantId)
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@shared/feature/evaluate/src/commonMain/kotlin/com/kus/feature/evaluate/ui/EvaluateScreen.kt`
around lines 48 - 50, The current LaunchedEffect uses restaurantId as the key
but reads both restaurant and restaurantId, causing uiState.restaurant not to
update when only restaurant metadata changes; split the side-effects into two
LaunchedEffect blocks: one LaunchedEffect(key1 = restaurant) that calls
viewModel.initRestaurant(restaurant) and another LaunchedEffect(key1 =
restaurantId) that calls viewModel.getPreviousEvaluation(restaurantId), so
initRestaurant reacts only to restaurant changes and getPreviousEvaluation runs
only when the ID changes (refer to LaunchedEffect, viewModel.initRestaurant,
viewModel.getPreviousEvaluation, restaurantId, restaurant, uiState.restaurant).
개요
PR 유형
해당하는 항목에 체크해주세요.
변경 사항
📸 화면 / 영상 (선택)
Screen_recording_20260308_204716.mp4
Before
After
리뷰어에게 전달할 사항
Summary by CodeRabbit
새로운 기능
개선사항