-
Notifications
You must be signed in to change notification settings - Fork 1
[Refactor] 프로필 수정 뷰모델 #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough
Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant VM as ProfileUpdateViewModel
participant Repo as ProfileRepository
participant DS as AconAppRemoteDataSource
participant NoAuth as AconAppNoAuthApi
participant Up as FileUploadApi
participant ProfAPI as ProfileRemoteDataSource/Api
participant Srv as Server(Object Storage & App)
U->>VM: 저장 클릭
VM->>Repo: updateProfile(newProfile)
alt 이미지가 로컬 content Uri
Repo->>DS: getPresignedUrl(request:imageType, fileName)
DS->>NoAuth: POST /images/presigned-url
NoAuth-->>DS: PresignedUrlResponse(fileUrl, preSignedUrl)
DS-->>Repo: PresignedUrlResponse
Repo->>Up: PUT preSignedUrl with RequestBody(bytes)
Up->>Srv: PUT to Object Storage
Srv-->>Up: 200
Repo->>ProfAPI: updateProfile(profileImage = fileUrl, ...)
else 이미지 변경 없음/원격 URL
Repo->>ProfAPI: updateProfile(existing image, ...)
end
ProfAPI->>Srv: PATCH /profile
Srv-->>ProfAPI: 200/에러
ProfAPI-->>Repo: 결과
Repo-->>VM: 성공/실패
VM-->>U: 네비게이션 뒤로 or 실패 토스트
Estimated code review effort🎯 4 (Complex) | ⏱️ ~70 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
♻️ Duplicate comments (1)
core/data/src/main/kotlin/com/acon/core/data/dto/request/GetPresignedUrlRequest.kt (1)
7-11: ImageType 직렬화 전제 재언급이 DTO가 ImageType을 직렬화하므로, ImageType에 @serializable 및 모듈 플러그인 적용이 필요합니다. 상세는 ImageType.kt 코멘트 참고.
🧹 Nitpick comments (15)
core/data/src/main/kotlin/com/acon/core/data/dto/request/profile/UpdateProfileRequest.kt (1)
11-14: 속성명 일관성(선택) — image → profileImage 리네이밍서버 키가 profileImage라면 DTO 속성도 동일 명칭으로 맞추면 가독성이 올라갑니다. 영향 범위가 넓다면 보류 가능.
제안:
- @SerialName("profileImage") val image: String? + @SerialName("profileImage") val profileImage: String?호출부/매핑도 함께 정리 필요.
core/data/src/main/kotlin/com/acon/core/data/api/remote/ProfileApi.kt (1)
18-18: Retrofit 반환 타입 명시로 의도 드러내기응답 본문을 쓰지 않는다면 Unit 명시 또는 Response로 상태코드 접근을 고려하세요.
- suspend fun updateProfile(@Body updateProfileRequest: UpdateProfileRequest) + suspend fun updateProfile(@Body updateProfileRequest: UpdateProfileRequest): Unit- suspend fun validateNickname(@Query("nickname") nickname: String) + suspend fun validateNickname(@Query("nickname") nickname: String): Unit상태코드나 헤더가 필요하면 Response을 권장합니다.
Also applies to: 21-21
core/data/src/main/kotlin/com/acon/core/data/di/ApiModule.kt (1)
118-124: 대용량 업로드 대비 OkHttp 타임아웃/헤더 관리 확인S3 등 presigned 업로드는 기본 타임아웃(10s~)으로 실패할 수 있고, 불필요한 공통 헤더가 서명과 충돌할 수 있습니다. NoAuth 클라이언트에 한해 업로드용 별도 OkHttp 설정(연장 타임아웃, 헤더 최소화, 바디 로깅 금지)을 권장합니다.
필요 시 업로드 전용 Retrofit/Client 분리(예: @UploadNoAuth)로 운영 리스크를 줄이세요.
domain/src/main/java/com/acon/acon/domain/usecase/ValidateNicknameUseCase.kt (1)
15-21: 검증 로직 소폭 단순화 제안(선택)단일 정규식으로 길이/형식을 함께 검증하면 분기와 에러 매핑이 단순해집니다. 필요 시 유지보수성을 위해 현 구조 유지도 무방합니다.
core/data/src/main/kotlin/com/acon/core/data/dto/request/GetPresignedUrlRequest.kt (1)
9-11: originalFileName의 개인정보/경로 노출 위험단말 파일명(경로/사용자명 포함 가능)을 그대로 전송하면 불필요한 메타데이터가 저장/로그될 수 있습니다. 확장자만 보존하거나 난수/UUID 파일명으로 대체하는 방식을 권장합니다(서버 요구사항 허용 시).
core/data/src/main/kotlin/com/acon/core/data/datasource/remote/AconAppRemoteDataSource.kt (1)
19-25: 메서드 시그니처/책임 분리 제안현재는 성공/실패만 예외로 표출됩니다. 필요 시 업로드 응답 상태(예: ETag, Content-MD5)를 반환하도록 확장해도 좋습니다. 다만 지금 단계에선 현행 유지해도 무방합니다.
core/data/src/main/kotlin/com/acon/core/data/repository/ProfileRepositoryImpl.kt (1)
12-12: 불필요한 import 제거FileUploadApi는 이 파일에서 사용되지 않습니다. 삭제해 주세요.
-import com.acon.core.data.api.remote.noauth.FileUploadApifeature/profile/src/test/kotlin/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModelTest.kt (1)
461-462: 검증 대상 함수가 잘못되었습니다 — 닉네임 선택 변경 시 닉네임 유효성 호출 금지 검증 필요Text Selection으로 onNicknameInputChanged가 호출될 때는 ValidateNicknameUseCase가 호출되지 않아야 합니다. 현재는 ValidateBirthDateUseCase를 검증하고 있습니다.
- coVerify(exactly = 0) { validateBirthDateUseCase(any()) } + coVerify(exactly = 0) { validateNicknameUseCase(any()) }core/data/src/test/java/com/acon/core/data/repository/ProfileRepositoryTest.kt (3)
266-285: 원격 성공 시 캐시 저장 호출까지 검증 추가 권장현재는 결과만 검증합니다. cacheSavedSpots 호출 여부까지 확인하면 회귀 방지에 도움이 됩니다.
val actualResult = profileRepository.getSavedSpots().first() // Then assertEquals(expectedResult, actualResult) + coVerify(exactly = 1) { profileLocalDataSource.cacheSavedSpots(sampleSavedSpots) } + coVerify(exactly = 1) { profileRemoteDataSource.getSavedSpots() }
168-185: 변수 이름 섀도잉(naming shadowing) 정리 제안프로퍼티 getter
sampleNewProfile과 동일한 이름의 지역 변수val sampleNewProfile = sampleNewProfile는 혼동을 줍니다. 지역 변수 제거 또는 프로퍼티명 변경을 권장합니다.- val sampleNewProfile = sampleNewProfile + val profileToUpdate = sampleNewProfile ... - val actualResult = profileRepository.updateProfile(sampleNewProfile) + val actualResult = profileRepository.updateProfile(profileToUpdate)
114-147: 유사 테스트 설명 중복/명확성 개선 제안두 테스트가 모두 "로컬 없음 → 원격 호출" 시나리오를 다룹니다. 설명을 통일/정리하여 목적을 구분하면 가독성이 좋아집니다.
feature/profile/src/main/java/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModel.kt (4)
103-103: 생일 입력 유효성 검사 로직 개선 필요
isDigitsOnly()확장 함수를 사용하면 더 명확하고 간결한 코드가 됩니다.- if (input.text.any { it.isDigit().not() }) return@intent + if (!input.text.isDigitsOnly()) return@intent
234-241: 저장 버튼 활성화 조건 로직 단순화 필요현재
isSaveEnabled로직이 복잡하고 이해하기 어렵습니다. 조건을 더 명확하게 분리하면 유지보수성이 향상됩니다.val isSaveEnabled: Boolean - get() = ( - nicknameValidationStatus is NicknameValidationStatus.Available && - birthDateValidationStatus is BirthDateValidationStatus.Valid) || ( - profileImageInputStatus is ProfileImageInputStatus.Changed && - nicknameValidationStatus is NicknameValidationStatus.Idle && - birthDateValidationStatus is BirthDateValidationStatus.Valid - ) + get() { + val isNicknameValid = nicknameValidationStatus is NicknameValidationStatus.Available || + (nicknameValidationStatus is NicknameValidationStatus.Idle && + profileImageInputStatus is ProfileImageInputStatus.Changed) + + val isBirthDateValid = birthDateValidationStatus is BirthDateValidationStatus.Valid + + return isNicknameValid && isBirthDateValid + }
203-215: 프로필 업데이트 실패 시 상세 에러 처리 필요현재 모든 실패를 동일하게 처리하고 있습니다. 네트워크 오류, 서버 오류 등을 구분하여 사용자에게 더 명확한 피드백을 제공하는 것이 좋습니다.
실패 케이스별 에러 처리를 구현해 드릴까요? 예를 들어:
- 네트워크 오류: "네트워크 연결을 확인해 주세요"
- 서버 오류: "일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요"
- 유효성 검사 실패: 구체적인 실패 이유 표시
119-145: 생일 유효성 검사 로직 중복 제거 필요생일 입력 처리에서 중복된
reduce호출이 많습니다. 하나의reduce로 통합하면 더 효율적입니다.- if (input.text.isEmpty()) { - reduce { - state.copy(birthDateValidationStatus = BirthDateValidationStatus.Valid) - } - } else if (input.text.length in 1 until 8) { - reduce { - state.copy(birthDateValidationStatus = BirthDateValidationStatus.Idle) - } - } else if (input.text.length == 8) { - val localDate = input.text.toLocalDate() - if (localDate == null) - reduce { - state.copy(birthDateValidationStatus = BirthDateValidationStatus.Invalid) - } - else { - validateBirthDateUseCase(localDate).onSuccess { - reduce { - state.copy(birthDateValidationStatus = BirthDateValidationStatus.Valid) - } - }.onFailure { - reduce { - state.copy(birthDateValidationStatus = BirthDateValidationStatus.Invalid) - } - } - } - } + val validationStatus = when { + input.text.isEmpty() -> BirthDateValidationStatus.Valid + input.text.length in 1 until 8 -> BirthDateValidationStatus.Idle + input.text.length == 8 -> { + val localDate = input.text.toLocalDate() + if (localDate == null) { + BirthDateValidationStatus.Invalid + } else { + validateBirthDateUseCase(localDate) + .fold( + onSuccess = { BirthDateValidationStatus.Valid }, + onFailure = { BirthDateValidationStatus.Invalid } + ) + } + } + else -> state.birthDateValidationStatus + } + + reduce { + state.copy(birthDateValidationStatus = validationStatus) + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
core/common/src/main/java/com/acon/acon/core/common/utils/DateExtensions.kt(1 hunks)core/data/src/main/kotlin/com/acon/core/data/api/remote/ProfileApi.kt(1 hunks)core/data/src/main/kotlin/com/acon/core/data/api/remote/noauth/AconAppNoAuthApi.kt(2 hunks)core/data/src/main/kotlin/com/acon/core/data/api/remote/noauth/FileUploadApi.kt(1 hunks)core/data/src/main/kotlin/com/acon/core/data/datasource/remote/AconAppRemoteDataSource.kt(1 hunks)core/data/src/main/kotlin/com/acon/core/data/di/ApiModule.kt(2 hunks)core/data/src/main/kotlin/com/acon/core/data/dto/request/GetPresignedUrlRequest.kt(1 hunks)core/data/src/main/kotlin/com/acon/core/data/dto/request/profile/UpdateProfileRequest.kt(1 hunks)core/data/src/main/kotlin/com/acon/core/data/dto/response/PresignedUrlResponse.kt(1 hunks)core/data/src/main/kotlin/com/acon/core/data/repository/ProfileRepositoryImpl.kt(2 hunks)core/data/src/test/java/com/acon/core/data/repository/ProfileRepositoryTest.kt(10 hunks)core/model/src/main/java/com/acon/acon/core/model/type/ImageType.kt(1 hunks)domain/src/main/java/com/acon/acon/domain/error/profile/UpdateProfileError.kt(1 hunks)domain/src/main/java/com/acon/acon/domain/error/profile/ValidateBirthDateError.kt(1 hunks)domain/src/main/java/com/acon/acon/domain/error/profile/ValidateNicknameError.kt(1 hunks)domain/src/main/java/com/acon/acon/domain/usecase/ValidateBirthDateUseCase.kt(1 hunks)domain/src/main/java/com/acon/acon/domain/usecase/ValidateNicknameUseCase.kt(2 hunks)feature/profile/src/main/java/com/acon/feature/profile/update/status/BirthDateValidationStatus.kt(1 hunks)feature/profile/src/main/java/com/acon/feature/profile/update/status/NicknameValidationStatus.kt(1 hunks)feature/profile/src/main/java/com/acon/feature/profile/update/status/ProfileImageInputStatus.kt(1 hunks)feature/profile/src/main/java/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModel.kt(1 hunks)feature/profile/src/test/kotlin/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModelTest.kt(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-19T04:19:20.229Z
Learnt from: ThirFir
PR: AconInc/ACON-Android#253
File: core/data/src/test/java/com/acon/core/data/repository/ProfileRepositoryTest.kt:273-277
Timestamp: 2025-09-19T04:19:20.229Z
Learning: ProfileRepositoryTest.kt의 updateProfileErrorScenarios()에서 UNSPECIFIED_SERVER_ERROR_CODE를 여러 에러 타입에 사용하는 것은 테스트용으로 의도된 것이며, 실제 서버 에러 코드가 확정되기 전의 임시적인 접근방식입니다.
Applied to files:
domain/src/main/java/com/acon/acon/domain/error/profile/UpdateProfileError.ktdomain/src/main/java/com/acon/acon/domain/error/profile/ValidateNicknameError.ktdomain/src/main/java/com/acon/acon/domain/error/profile/ValidateBirthDateError.ktcore/data/src/test/java/com/acon/core/data/repository/ProfileRepositoryTest.kt
📚 Learning: 2025-09-19T04:52:02.675Z
Learnt from: ThirFir
PR: AconInc/ACON-Android#254
File: app/src/main/java/com/acon/acon/navigation/nested/ProfileNavigationLegacy.kt:38-40
Timestamp: 2025-09-19T04:52:02.675Z
Learning: In ProfileNavigationLegacy.kt, the onNavigateToProfileUpdate callback is intentionally left as a placeholder (just returning navController) and will be implemented in a future PR according to ThirFir.
Applied to files:
feature/profile/src/main/java/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModel.kt
📚 Learning: 2025-09-19T04:19:12.297Z
Learnt from: ThirFir
PR: AconInc/ACON-Android#253
File: domain/src/main/java/com/acon/acon/domain/error/profile/ValidateNicknameError.kt:10-15
Timestamp: 2025-09-19T04:19:12.297Z
Learning: ValidateNicknameError의 InvalidFormat과 AlreadyExist 클래스에서 UNSPECIFIED_SERVER_ERROR_CODE(-1)를 사용하는 것은 테스트용으로 의도된 것입니다. 실제 서버 에러 코드는 나중에 적용될 예정입니다.
Applied to files:
domain/src/main/java/com/acon/acon/domain/error/profile/ValidateNicknameError.kt
🧬 Code graph analysis (3)
feature/profile/src/test/kotlin/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModelTest.kt (1)
domain/src/test/kotlin/com/acon/acon/domain/usecase/ValidateBirthDateUseCaseTest.kt (1)
validateBirthDateUseCase(14-80)
feature/profile/src/main/java/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModel.kt (1)
domain/src/test/kotlin/com/acon/acon/domain/usecase/ValidateBirthDateUseCaseTest.kt (1)
validateBirthDateUseCase(14-80)
core/data/src/test/java/com/acon/core/data/repository/ProfileRepositoryTest.kt (1)
core/data/src/test/java/com/acon/core/data/TestUtils.kt (1)
createErrorStream(12-16)
⏰ 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
🔇 Additional comments (25)
domain/src/main/java/com/acon/acon/domain/error/profile/ValidateBirthDateError.kt (1)
10-10: 특정 에러 코드 값 할당 완료40053 값으로 특정 에러 코드가 정상적으로 할당되었습니다. 이전의 UNSPECIFIED_SERVER_ERROR_CODE 사용에서 벗어나 명확한 에러 식별이 가능하게 되었습니다.
domain/src/main/java/com/acon/acon/domain/usecase/ValidateBirthDateUseCase.kt (2)
5-5: DI 지원을 위한 import 추가의존성 주입(DI) 지원을 위해 필요한 import가 적절히 추가되었습니다.
7-7: DI 지원을 위한 constructor annotation 추가@Inject 어노테이션이 primary constructor에 적절히 추가되어 의존성 주입을 지원합니다. ProfileUpdateViewModel에서 이 UseCase를 주입받아 사용할 수 있게 되었습니다.
core/data/src/main/kotlin/com/acon/core/data/api/remote/noauth/FileUploadApi.kt (1)
8-14: Presigned URL 업로드를 위한 간단하고 효과적인 API 정의Presigned URL 방식의 파일 업로드를 위한 API가 적절하게 정의되었습니다. @put과 @Body RequestBody를 사용하는 방식이 Presigned URL 업로드에 적합합니다.
core/data/src/main/kotlin/com/acon/core/data/dto/response/PresignedUrlResponse.kt (1)
6-10: Presigned URL 응답 DTO 구조가 적절함fileUrl과 presignedUrl 필드를 가진 DTO 구조가 Presigned URL 워크플로우에 적합하게 설계되었습니다. SerialName 매핑도 정확히 설정되었습니다.
core/common/src/main/java/com/acon/acon/core/common/utils/DateExtensions.kt (3)
7-9: 날짜 포맷터의 효율적인 lazy 초기화DateTimeFormatter를 lazy로 초기화하여 메모리 효율성과 성능을 고려한 좋은 구현입니다.
14-16: LocalDate to String 변환 확장 함수LocalDate를 yyyyMMdd 형식으로 변환하는 확장 함수가 간단하고 명확하게 구현되었습니다.
25-31: 안전한 String to LocalDate 변환예외 처리를 통해 파싱 실패 시 null을 반환하는 안전한 구현입니다. DateTimeParseException을 적절히 처리하여 견고성을 확보했습니다.
feature/profile/src/main/java/com/acon/feature/profile/update/status/NicknameValidationStatus.kt (1)
3-10: 닉네임 검증 상태를 위한 적절한 sealed interface 설계프로필 업데이트 기능에 필요한 모든 닉네임 검증 상태가 포함되어 있으며, 다른 status 클래스들(BirthDateValidationStatus, ProfileImageInputStatus)과 일관된 패턴을 따르고 있습니다.
domain/src/main/java/com/acon/acon/domain/error/profile/UpdateProfileError.kt (3)
8-8: 기존 에러 코드의 명확한 숫자 값 할당기존 에러들에 대해 UNSPECIFIED_SERVER_ERROR_CODE에서 명확한 숫자 에러 코드(40901, 40051, 40053)로 업데이트되어 에러 식별과 처리가 개선되었습니다.
Also applies to: 11-11, 14-14
16-24: 이미지 업로드 관련 새로운 에러 타입 추가Presigned URL 기반 이미지 업로드 기능에 필요한 새로운 에러 타입들이 적절한 에러 코드와 함께 추가되었습니다:
- InvalidBucketImagePath (40052): S3 버킷 경로 관련 에러
- InvalidImageType (40045): 이미지 타입 검증 에러
- InternalServerError (50005): 서버 내부 에러
이미지 업로드 워크플로우에서 발생할 수 있는 주요 에러 케이스들을 잘 포괄하고 있습니다.
30-33: createErrorInstances에 새로운 에러 타입 추가새로 추가된 에러 타입들이 createErrorInstances 메서드에 적절히 포함되어 에러 매핑이 완전하게 처리됩니다.
feature/profile/src/main/java/com/acon/feature/profile/update/status/ProfileImageInputStatus.kt (1)
3-6: 프로필 이미지 입력 상태를 위한 간결한 sealed interface프로필 이미지의 변경 여부를 나타내는 간단하고 명확한 상태 타입입니다. 다른 status 파일들과 일관된 패턴을 따르고 있어 코드 일관성이 좋습니다.
core/data/src/main/kotlin/com/acon/core/data/dto/request/profile/UpdateProfileRequest.kt (2)
24-27: Custom 이미지 URL이 로컬 URI일 가능성 — 사전 업로드 보장 필요ProfileImageStatus.Custom.url이 content:// 혹은 file:// 로컬 URI라면 서버로 그대로 전송되니, 사전 업로드 후 presigned 응답의 fileUrl을 세팅했는지 확인이 필요합니다.
레포지토리에서 업로드 후 Profile 모델의 image.url을 fileUrl로 치환하는지 테스트로 보강 부탁드립니다(로컬 URI 입력 → 요청 페이로드에 원격 URL 포함).
9-14: 해결 — Retrofit이 kotlinx.serialization 컨버터로 구성되어 있습니다core/data/src/main/kotlin/com/acon/core/data/di/NetworkModule.kt에서 com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory를 import하고 json.asConverterFactory("application/json".toMediaType())를 addConverterFactory로 등록하고 있습니다 (라인 14, 126, 140, 156, 171). UpdateProfileRequest의 @serializable 사용은 Retrofit 설정과 호환됩니다.
core/data/src/main/kotlin/com/acon/core/data/api/remote/noauth/AconAppNoAuthApi.kt (1)
18-21: 엔드포인트 스펙 정합성 확인POST /api/v1/images/presigned-url의 요청/응답(JSON 키, enum 문자열)과 서버 스펙이 1:1로 일치하는지 점검해 주세요(특히 ImageType 직렬화).
서버가 기대하는 키/값이 다르면 @SerialName으로 고정하거나 커스텀 직렬화를 적용해야 합니다.
domain/src/main/java/com/acon/acon/domain/usecase/ValidateNicknameUseCase.kt (1)
28-30: 상수 도입 좋습니다MAX_NICKNAME_LENGTH 공개 상수화로 UI/테스트 재사용성이 좋아졌습니다.
feature/profile/src/main/java/com/acon/feature/profile/update/status/BirthDateValidationStatus.kt (1)
3-7: data object는 Kotlin 1.9+ 필요 — Kotlin 버전 확인 필요data object는 Kotlin 1.9 이상에서만 지원됩니다; 검증 스크립트 출력이 비어 있어 저장소의 Kotlin 버전을 확인하지 못했습니다. 빌드가 1.9 미만이면 아래처럼 data object를 object로 교체하세요.
-sealed interface BirthDateValidationStatus { - data object Idle: BirthDateValidationStatus - data object Valid: BirthDateValidationStatus - data object Invalid: BirthDateValidationStatus -} +sealed interface BirthDateValidationStatus { + object Idle: BirthDateValidationStatus + object Valid: BirthDateValidationStatus + object Invalid: BirthDateValidationStatus +}검증 요청: 루트/모듈별 Kotlin 버전(gradle.properties 또는 build.gradle(.kts) 내용) 출력 제공.
domain/src/main/java/com/acon/acon/domain/error/profile/ValidateNicknameError.kt (1)
10-14: 명시적 에러 코드 매핑 LGTMInvalidFormat=40051, AlreadyExist=40901로의 전환이 테스트/도메인 전반의 코드 일치에 도움 됩니다. 서버 스펙과 다른 코드가 배포 환경에 없는지만 마지막으로 한 번만 확인 부탁드립니다.
core/data/src/main/kotlin/com/acon/core/data/repository/ProfileRepositoryImpl.kt (1)
85-87: 캐시 업데이트 타이밍 LGTM원격 업데이트 성공 후에만 cacheProfile을 호출하고 있어 일관성이 유지됩니다.
feature/profile/src/test/kotlin/com/acon/feature/profile/update/viewmodel/ProfileUpdateViewModelTest.kt (2)
528-546: 입력 완료(valid) 시 플로우 검증 LGTM유즈케이스 성공 시 Valid로 전이되는 경로가 안정적으로 테스트되고 있습니다.
615-652: 프로필 이미지 입력 핸들링 테스트 커버리지 적절기본/사용자 지정 선택 시 상태 전이와 값 바인딩이 명확히 검증됩니다.
core/data/src/test/java/com/acon/core/data/repository/ProfileRepositoryTest.kt (3)
61-65: Context 주입 방식 합리적 (relaxed mock)Android 의존을 최소화하면서 새 생성자 시그니처를 잘 반영했습니다.
247-264: SavedSpots 캐시 히트 경로 테스트 LGTM로컬 히트 시 원격 미호출을 검증하고 있습니다.
302-316: 에러 코드 매핑 테스트 LGTM도메인 코드(40051/40901/40053/40052)와 일치합니다.
💻 Work Description
Summary by CodeRabbit