[Refactor/#72] 회원연결 API Response Type을 변경합니다.#73
Conversation
Walkthrough회원연결 API의 응답 타입을 Changes
Sequence Diagram(s)sequenceDiagram
participant UI as UI/Screen
participant VM as ViewModel
participant Repo as Repository
participant DS as DataSource
participant Service as UserService
participant Net as NetworkAdapter
UI->>VM: connectToSenior(userId)
VM->>VM: isLoading = true
VM->>Repo: connectToSenior(userId)
Repo->>DS: connectToSenior(userId)
DS->>Service: connectToSenior(userId)
Service->>Net: execute request
Net-->>Service: NetworkResult<Unit>
Service-->>DS: NetworkResult<Unit>
DS-->>Repo: Result<Unit>
Repo-->>VM: Result<Unit>
alt Success
VM->>VM: isLoading = false
VM->>UI: navigate to guardian home
else Failure
VM->>VM: isLoading = false
VM->>UI: show error
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 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
📜 Recent review detailsConfiguration used: Repository UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
⏰ 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)
🔇 Additional comments (3)
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: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/network/src/main/kotlin/com/moa/app/network/adapter/NetworkResultCallAdapter.kt (1)
15-24:ResponseHandler()타입 추론이 명시적으로 지정되지 않아서 타입 안전성이 불명확합니다.현재 코드에서
if분기의 한쪽은UnitResponseHandler() as ResponseHandler<Any>로 명시적으로 캐스팅하고, 다른 쪽은ResponseHandler()로 두어서 타입 추론에 의존하고 있습니다.ResponseHandler<T>는 invariant 제네릭 클래스이므로, 두 분기의 타입이 명확하게 일치해야 합니다.
handler변수에 명시적 타입을 지정하지 않으면 컴파일러가 두 분기의 공통 상위 타입으로 추론하게 되어, 타입 체킹이 느슨해질 수 있습니다. 안전하고 명확하게 하려면val handler: ResponseHandler<Any>형태로 명시적 타입을 선언하고,ResponseHandler<Any>()로 제네릭 파라미터를 지정하는 것이 좋습니다.제안 diff
@Suppress("UNCHECKED_CAST") override fun adapt(call: Call<BaseResponse<*>>): Call<NetworkResult<*>> { - val handler = if (successType == Unit::class.java) { - UnitResponseHandler() as ResponseHandler<Any> - } else { - ResponseHandler() - } + val handler: ResponseHandler<Any> = if (successType == Unit::class.java) { + UnitResponseHandler() as ResponseHandler<Any> + } else { + ResponseHandler<Any>() + } return NetworkResultCall(call as Call<BaseResponse<Any>>, handler) as Call<NetworkResult<*>> }
🧹 Nitpick comments (3)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/connection/model/ConnectionCheckUiState.kt (1)
8-23:isLoading기본값을 주면 초기화/복사 실수 여지가 줄어듭니다.
ConnectionCheckUiState.INIT로 통일하는 전략이면 현재도 OK인데,isLoading이 non-default라 생성/테스트 코드에서 누락되기 쉬워요. 아래처럼 기본값을 주면 안전합니다.제안 diff
data class ConnectionCheckUiState( - val isLoading: Boolean, + val isLoading: Boolean = false, val seniorProfile: SeniorProfile?, ) { @@ companion object Companion { val INIT = ConnectionCheckUiState( - isLoading = false, seniorProfile = null, ) } }feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/connection/ConnectionCheckViewModel.kt (2)
39-52:isLoading해제는finally로 보장하는 편이 더 안전합니다.현재
fold의 onSuccess/onFailure에서만isLoading=false로 복구해서, 예외가 발생하면 로딩이 영구 고착될 수 있어요.제안 diff
private fun loadSeniorProfile(userId: Long) { viewModelScope.launch { - _uiState.update { it.copy(isLoading = true) } - fetchSeniorProfilesUseCase(userId).fold( - onSuccess = { seniorProfile -> - _uiState.update { it.copy(isLoading = false, seniorProfile = seniorProfile) } - }, - onFailure = { t -> - _uiState.update { it.copy(isLoading = false) } - Timber.e("loadSeniorProfile: $t") - }, - ) + _uiState.update { it.copy(isLoading = true) } + try { + fetchSeniorProfilesUseCase(userId).fold( + onSuccess = { seniorProfile -> + _uiState.update { it.copy(seniorProfile = seniorProfile) } + }, + onFailure = { t -> + Timber.e(t, "loadSeniorProfile failed") + }, + ) + } finally { + _uiState.update { it.copy(isLoading = false) } + } } }
54-69: 연결 API는 중복 호출 가드(또는 버튼 disable)가 있으면 더 견고합니다.빠른 연타로
connectToSenior()가 여러 번 실행될 수 있어요(서버 side-effect가 있으면 특히). UI에서 버튼 disable을 이미 한다면 OK이고, 아니면 VM에서도 가드가 있으면 안전합니다. 또한 로그는Timber.e(t, ...)형태가 좋아요.제안 diff(가드 + finally + Timber)
fun connectToSenior() { viewModelScope.launch { + if (_uiState.value.isLoading) return@launch _uiState.update { it.copy(isLoading = true) } - connectToSeniorUseCase(userId).fold( - onSuccess = { - _uiState.update { it.copy(isLoading = false) } - navigateToGuardianHome() - }, - onFailure = { t -> - _uiState.update { it.copy(isLoading = false) } - Timber.e("connectToSenior: $t") - } - ) + try { + connectToSeniorUseCase(userId).fold( + onSuccess = { + navigateToGuardianHome() + }, + onFailure = { t -> + Timber.e(t, "connectToSenior failed") + } + ) + } finally { + _uiState.update { it.copy(isLoading = false) } + } } }
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
core/network/src/main/kotlin/com/moa/app/network/adapter/NetworkResultCallAdapter.ktcore/network/src/main/kotlin/com/moa/app/network/adapter/ResponseHandler.ktcore/network/src/main/kotlin/com/moa/app/network/adapter/UnitResponseHandler.ktdata/src/main/kotlin/com/moa/app/data/user/datasource/UserDataSource.ktdata/src/main/kotlin/com/moa/app/data/user/datasourceImpl/UserDataSourceImpl.ktdata/src/main/kotlin/com/moa/app/data/user/model/response/ProfileResponse.ktdata/src/main/kotlin/com/moa/app/data/user/repositoryImpl/UserRepositoryImpl.ktdata/src/main/kotlin/com/moa/app/data/user/service/UserService.ktfeature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/connection/ConnectionCheckViewModel.ktfeature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/connection/model/ConnectionCheckUiState.kt
💤 Files with no reviewable changes (1)
- data/src/main/kotlin/com/moa/app/data/user/model/response/ProfileResponse.kt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-21T09:07:31.486Z
Learnt from: wjdrjs00
Repo: team-memory-care/MoA-Android PR: 31
File: core/network/src/main/kotlin/com/moa/app/network/adapter/NetworkResultCall.kt:36-37
Timestamp: 2025-11-21T09:07:31.486Z
Learning: The MoA-Android project exclusively uses Retrofit suspend functions for network calls in the core/network module. The NetworkResultCall.execute() method intentionally throws UnsupportedOperationException because synchronous execution is not needed.
Applied to files:
core/network/src/main/kotlin/com/moa/app/network/adapter/NetworkResultCallAdapter.kt
⏰ 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: Run Unit Tests
🔇 Additional comments (6)
data/src/main/kotlin/com/moa/app/data/user/datasource/UserDataSource.kt (1)
6-14:connectToSenior를Result<Unit>로 단순화한 건 변경 의도에 잘 맞습니다.(호출 측에서 더 이상 응답 바디를 기대하지 않으니) 데이터 레이어 의존성도 같이 줄어드는 방향이라 좋습니다.
data/src/main/kotlin/com/moa/app/data/user/repositoryImpl/UserRepositoryImpl.kt (1)
39-41: 불필요한Result래핑 제거로 더 깔끔해졌습니다.core/network/src/main/kotlin/com/moa/app/network/adapter/UnitResponseHandler.kt (1)
8-19: Unit 응답 처리 로직이 올바르게 개선되었습니다.HTTP 응답이 성공(2xx)일 때 body가 null이거나 body.success가 true인 경우를 성공으로 처리하는 로직이 Unit 반환 타입에 적합합니다. 의미있는 데이터 페이로드가 없는 API에 대해 올바른 접근입니다.
data/src/main/kotlin/com/moa/app/data/user/datasourceImpl/UserDataSourceImpl.kt (1)
26-28: 데이터 소스 구현이 올바르게 업데이트되었습니다.
Result<ProfileResponse>에서Result<Unit>으로의 반환 타입 변경과 함께toResult { it }에서toResult()로 변경한 것이 적절합니다. Unit 응답에는 매핑할 데이터가 없으므로 변환 함수가 불필요합니다.core/network/src/main/kotlin/com/moa/app/network/adapter/ResponseHandler.kt (1)
8-21: 응답 처리 로직이 안전하게 개선되었습니다.변경된 로직의 장점:
- HTTP 상태를 먼저 확인하여 더 논리적인 흐름 제공
- 데이터 응답(T != Unit)에 대해 non-null body를 요구하는 것이 적절함
body?.message ?: "Unknown Error"로 null 안전성 향상- Line 20의 최종 에러 반환으로 명확한 에러 처리 경로 제공
Line 14의
UNCHECKED_CAST는BaseResponse<T>계약이 준수된다면 안전합니다.UnitResponseHandler가 null body를 허용하는 것과 달리, 이 핸들러는 데이터 응답을 위해 body를 요구하는 것이 올바른 설계입니다.data/src/main/kotlin/com/moa/app/data/user/service/UserService.kt (1)
24-24: API 반환 타입 변경이 올바르게 적용되었습니다.
connectToSenior의 반환 타입이NetworkResult<Unit>로 변경되었으며, 서비스 계층부터 뷰모델까지 모든 레이어에서 일관되게 적용되었습니다. 데이터소스, 레포지토리, 유스케이스를 거쳐 뷰모델에서.fold()로 올바르게 처리되고 있습니다.
Related issue 🛠
Work Description ✏️
Screenshot 📸
Uncompleted Tasks 😅
Summary by CodeRabbit
버그 수정
개선 사항
테스트
✏️ Tip: You can customize this high-level summary in your review settings.