Conversation
Walkthrough마이페이지/관심 기능 전반의 API를 v2로 통합하고 lastId 기반 페이징으로 일원화. 관심 등록/해제 엔드포인트를 단일 동작으로 교체. 프로필(닉네임+이미지) 응답·도메인·프레젠테이션 모델 및 맵퍼 업데이트. 리포지토리/유스케이스/DI/뷰모델/프래그먼트/어댑터 시그니처 및 호출부 일괄 수정. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as 사용자
participant UI as ViewModel (My / Detail / Search)
participant UC as UseCase (Post/DeleteInterestAnimal)
participant Repo as InterestRepository
participant RDS as InterestRemoteDataSource
participant Svc as InterestService (v2)
U->>UI: 관심 토글(reportId, isInterest)
alt 등록 (isInterest == false)
UI->>UC: postInterestAnimalUseCase(reportId)
UC->>Repo: registerInterestAnimal(reportId)
Repo->>RDS: registerInterestAnimal(reportId)
RDS->>Svc: POST /api/v2/users/me/interest-animals (Body: {reportId})
Svc-->>RDS: 200 NullableBaseResponse<Unit>
else 해제 (isInterest == true)
UI->>UC: deleteInterestAnimalUseCase(reportId)
UC->>Repo: deleteInterestAnimal(reportId)
Repo->>RDS: deleteInterestAnimal(reportId)
RDS->>Svc: DELETE /api/v2/users/me/interest-animals?reportId=
Svc-->>RDS: 200 NullableBaseResponse<Unit>
end
RDS-->>Repo: 응답
Repo-->>UC: Result
UC-->>UI: Result
UI-->>U: UI 상태 갱신
sequenceDiagram
autonumber
actor U as 사용자
participant UI as MyViewModel
participant UC as PatchProfileImageUseCase
participant Repo as MyRepository
participant RDS as MyRemoteDataSource
participant Svc as MyService (v2)
U->>UI: 기본 프로필 이미지 선택(enumName)
UI->>UC: uploadDefault(enumName)
UC->>Repo: patchProfileImageDefault(defaultProfileImageName)
Repo->>RDS: patchProfileImageDefault(defaultProfileImageName)
RDS->>Svc: PATCH /api/v2/users/me/profile-image (multipart)
Svc-->>RDS: 200 NullableBaseResponse<Unit>
UI->>Repo: getNickname()/getMyProfile (재조회)
Repo->>RDS: getNickname()
RDS->>Svc: GET /api/v2/users/me
Svc-->>RDS: 200 BaseResponse<MyNickNameResponseDto>
RDS-->>Repo: DTO
Repo-->>UI: MyProfileData
UI-->>U: 닉네임/이미지 갱신
Estimated code review effort🎯 4 (Complex) | ⏱️ ~70 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (3 warnings, 1 inconclusive)
✅ Passed checks (1 passed)
✨ 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)
app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/DetailSearchViewModel.kt (1)
41-59: 관심 토글 로직 반전 — 조건 뒤집기 및 성공 시 StateFlow 낙관적 갱신 필요File: app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/DetailSearchViewModel.kt (41–59)
- 현재 if 조건이 반대로 되어 있어 interest == true일 때 postInterestAnimalUseCase가 호출됩니다. 조건을 뒤집어 interest true → delete, false → post로 변경하고 onSuccess에서 _detailSearchData.value를 copy(interest = ...)로 갱신하세요.
- 에러 메시지의 "신고 동물" 표현은 맥락과 어긋납니다. "관심 등록/해제" 등 중립 문구로 교체하세요.
- fun setInterestProtectingAnimal(id: Long) { - viewModelScope.launch { - if (_detailSearchData.value?.interest == true) { - postInterestAnimalUseCase(id).fold( - onSuccess = { }, - onFailure = { error -> - _errorMessage.value = error.message ?: "신고 동물 관심 등록 중 오류가 발생했습니다." - } - ) - } else { - deleteInterestAnimalUseCase(id).fold( - onSuccess = { }, - onFailure = { error -> - _errorMessage.value = error.message ?: "신고 동물 관심 해제 중 오류가 발생했습니다." - } - ) - } - } - } + fun setInterestProtectingAnimal(id: Long) { + viewModelScope.launch { + val current = _detailSearchData.value?.interest == true + if (current) { + deleteInterestAnimalUseCase(id).fold( + onSuccess = { + _detailSearchData.value = _detailSearchData.value?.copy(interest = false) + }, + onFailure = { error -> + _errorMessage.value = error.message ?: "관심 해제 중 오류가 발생했습니다." + } + ) + } else { + postInterestAnimalUseCase(id).fold( + onSuccess = { + _detailSearchData.value = _detailSearchData.value?.copy(interest = true) + }, + onFailure = { error -> + _errorMessage.value = error.message ?: "관심 등록 중 오류가 발생했습니다." + } + ) + } + } + }
🧹 Nitpick comments (28)
app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyViewedAnimalsRvAdapter.kt (2)
96-101: DiffUtil 아이덴티티 비교 개선 제안아이템 동일성은 객체 전체 비교보다 고유 키(reportId)로 판단하는 게 안정적입니다.
적용 제안(diff):
-override fun areItemsTheSame( - oldItem: MyViewedAnimalsRv, - newItem: MyViewedAnimalsRv -): Boolean { - return oldItem == newItem -} +override fun areItemsTheSame( + oldItem: MyViewedAnimalsRv, + newItem: MyViewedAnimalsRv +): Boolean = oldItem.reportId == newItem.reportId
37-44: State 매핑 안전화(없는 태그 대비)first { … }는 매칭이 없으면 예외가 납니다. firstOrNull + let으로 안전하게 처리 추천.
적용 제안(diff):
- val stateType = AnimalStateType.entries.first { it.state == item.tag } - chipMyHistoryAnimalState.setTextColor( - binding.root.context.getColor(stateType.textColor) -) - chipMyHistoryAnimalState.chipBackgroundColor = - binding.root.context.getColorStateList(stateType.backgroundChipColor) + AnimalStateType.entries.firstOrNull { it.state == item.tag }?.let { stateType -> + chipMyHistoryAnimalState.setTextColor( + binding.root.context.getColor(stateType.textColor) + ) + chipMyHistoryAnimalState.chipBackgroundColor = + binding.root.context.getColorStateList(stateType.backgroundChipColor) + }app/src/main/java/com/example/findu/domain/model/my/MyReportHistoryData.kt (1)
9-11: address/location 명칭 일관성다른 뷰모델/프리젠테이션은 address를 쓰고, 여기선 location을 사용합니다. 동일 의미면 용어를 통일하면 혼동이 줄어듭니다.
app/src/main/java/com/example/findu/presentation/model/MyRv.kt (1)
19-19: 프리젠테이션 모델 불변성 유지 고려ListAdapter와 궁합을 위해 interest를 val로 두고 토글 시 copy + submitList로 교체하는 패턴을 추천합니다. 현재도 동작은 하지만 상태 싱크 이슈가 생기기 쉬워요.
Also applies to: 30-30
app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyInterestRvAdapter.kt (2)
89-101: DiffUtil 동일성 비교 키로 변경 권장아이템 비교는 전체 동등성보다 reportId 키 비교가 적합합니다.
적용 제안(diff):
-override fun areItemsTheSame( - oldItem: MyInterestRv, - newItem: MyInterestRv -): Boolean { - return oldItem == newItem -} +override fun areItemsTheSame( + oldItem: MyInterestRv, + newItem: MyInterestRv +): Boolean = oldItem.reportId == newItem.reportId
37-44: State 매핑 NPE 방지firstOrNull 사용으로 미정의 태그 대응을 안전하게.
적용 제안(diff):
- val stateType = AnimalStateType.entries.first { it.state == item.tag } - chipMyHistoryAnimalState.setTextColor( - binding.root.context.getColor(stateType.textColor) -) - chipMyHistoryAnimalState.chipBackgroundColor = - binding.root.context.getColorStateList(stateType.backgroundChipColor) + AnimalStateType.entries.firstOrNull { it.state == item.tag }?.let { stateType -> + chipMyHistoryAnimalState.setTextColor( + binding.root.context.getColor(stateType.textColor) + ) + chipMyHistoryAnimalState.chipBackgroundColor = + binding.root.context.getColorStateList(stateType.backgroundChipColor) + }app/src/main/java/com/example/findu/domain/usecase/my/PatchProfileImageUseCase.kt (1)
8-16: 검증 완료 — MyRepository의 patchProfileImage*는 이미 Result 반환
app/src/main/java/com/example/findu/domain/repository/MyRepository.kt의 patchProfileImageFile/patchProfileImageDefault는 suspend ...: Result로 선언되어 있고, 구현체(app/src/main/java/com/example/findu/data/repositoryimpl/MyRepositoryImpl.kt)도 Result를 반환합니다.
- 결론: 시그니처 불일치 없음(타입 변경 불필요).
- 권장(선택): 도메인 경계 명확화를 위해 UseCase에 명시적 반환타입(Result)을 추가하거나, 필요 시 sealed class + operator invoke로 메서드 통합을 적용하세요.
app/src/main/java/com/example/findu/domain/usecase/my/GetInterestUseCase.kt (1)
7-11: 반환 타입 명시 + lastId 기본값 처리
- 현재: app/src/main/java/com/example/findu/domain/repository/MyRepository.kt 에서 suspend fun getMyInterest(lastId: Long): Result 로 정의되어 있음 — 따라서 usecase를 invoke(lastId: Long? = null)로만 바꾸면 시그니처 불일치로 컴파일되지 않습니다.
- 권장(간단): GetInterestUseCase에 반환 타입을 명시하고 파라미터는 유지하세요.
- GetInterestUseCase.kt 예시: suspend operator fun invoke(lastId: Long): Result = myRepository.getMyInterest(lastId)
- 옵션(리팩터): 최초 로드에서 lastId를 생략하려면 MyRepository.kt, MyRepositoryImpl.kt(및 호출부)를 Long? = null로 변경하고 null 처리 로직을 추가해야 합니다.
app/src/main/java/com/example/findu/domain/model/my/MyInterestData.kt (1)
6-6: 페이지네이션 커서 의미 명확화·date 타입 개선 권고
- 확인: app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyInterestResponseDto.kt에서 isLast: Boolean, lastId: Long(비nullable)로 내려오며 app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt에서 그대로 MyInterestData로 매핑됩니다 — 따라서 isLast가 true일 때 lastId의 의미가 모호합니다.
- 권장(선택지):
- (가) 백엔드 변경: 마지막 페이지면 lastId를 null로 내려 nextCursor: Long? 패턴 사용(설계상 명확) — 클라이언트/서버 변동 필요.
- (나) 클라이언트 처리: isLast == true일 때 lastId를 무시하거나 (예: nextCursor = null 또는 -1로 치환) 명시적으로 처리.
- date 필드: DTO·도메인 모두 String으로 되어 있음 — 도메인에서는 Instant/LocalDateTime으로 저장하고 매퍼에서 파싱, UI에서 포맷팅을 권장(정렬/타임존 안정성).
- reportId 네이밍은 DTO·매퍼에서 이미 reportId로 일관 적용되어 있음.
- 참고 파일: MyInterestResponseDto.kt, MyResponseDtoMapper.kt, domain/model/my/MyInterestData.kt
app/src/main/java/com/example/findu/domain/model/my/MyViewedAnimalData.kt (2)
3-4: 불필요한 import 제거 필요
SerialName과Serializable어노테이션이 실제로 사용되지 않고 있습니다.-import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable
11-19: 필드명 일관화 — MyViewedAnimalData.Card.location을 address로 통일 권장MyViewedAnimalData.Card은 location을 사용하는 반면 프로젝트 전반(예: MyInterestData, presentation 모델/Adapter, 일부 DTO 매핑)에서는 address가 더 많이 사용됩니다. 도메인 모델을 address로 변경하거나 전체적으로 location↔address 사용을 통일하세요.
참고 위치(예시):
- app/src/main/java/com/example/findu/domain/model/my/MyViewedAnimalData.kt (Card.location)
- app/src/main/java/com/example/findu/domain/model/my/MyInterestData.kt (address)
- app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt (cards 매핑)
- app/src/main/java/com/example/findu/presentation/model/MyRv.kt 및 관련 Adapter들 (address 사용)
app/src/main/java/com/example/findu/domain/repository/MyRepository.kt (1)
3-3: 불필요한 import 제거
MyNickNameResponseDto는 domain 레이어의 repository 인터페이스에서 직접 사용되지 않아야 합니다. 이는 data 레이어의 구현 세부사항입니다.-import com.example.findu.data.dataremote.model.response.my.MyNickNameResponseDtoapp/src/main/java/com/example/findu/presentation/ui/my/MyFragment.kt (3)
86-93: drawable 리소스를 문자열로 매핑하는 로직drawable 리소스 ID를 문자열 이름으로 변환하는 로직이 하드코딩되어 있습니다. 이를 별도의 매퍼나 enum으로 관리하면 유지보수가 더 쉬울 것 같습니다.
// 별도 파일에 정의 enum class ProfileImage(val resId: Int, val apiName: String) { DEFAULT(R.drawable.img_my_profile_default, "default"), PUPPY(R.drawable.img_my_profile1, "puppy"), CHICK(R.drawable.img_my_profile2, "chick"), PANDA(R.drawable.img_my_profile3, "panda"); companion object { fun fromResId(resId: Int): String = values().find { it.resId == resId }?.apiName ?: "default" } } // 사용 val defaultName = ProfileImage.fromResId(resId)
186-192: 버전 비교 로직 개선 가능버전 문자열 비교 로직이 복잡하고 오류 발생 가능성이 있습니다. 특히 "-" 이후 부분을 제거하는 것은 프리릴리즈 버전 처리를 위한 것으로 보이는데, 더 견고한 버전 비교 로직을 사용하는 것이 좋겠습니다.
private fun compareVersions(current: String, latest: String): Boolean { val currentParts = current.substringBefore("-").split(".").map { it.toIntOrNull() ?: 0 } val latestParts = latest.split(".").map { it.toIntOrNull() ?: 0 } for (i in 0 until maxOf(currentParts.size, latestParts.size)) { val currentPart = currentParts.getOrNull(i) ?: 0 val latestPart = latestParts.getOrNull(i) ?: 0 if (currentPart != latestPart) { return currentPart >= latestPart } } return true }
214-216: Glide를 사용한 프로필 이미지 로딩네트워크 이미지 로딩에 Glide를 사용하는 것은 좋은 선택입니다. 다만 에러 처리나 placeholder 이미지 설정을 추가하면 사용자 경험이 개선될 것 같습니다.
Glide.with(this@MyFragment) .load(it.profileImage) + .placeholder(R.drawable.img_my_profile_default) + .error(R.drawable.img_my_profile_default) .into(binding.ivMyIllust)app/src/main/java/com/example/findu/presentation/mapper/torvmodel/MyInterestDataMapper.kt (1)
10-20: interest 필드 하드코딩 제거 — 원본 값 사용 권장
MyInterestData.InterestAnimal.toRvModel()에서 interest = true로 고정되어 도메인/DTO의 실제 값을 무시합니다. 도메인/DTO(MyInterestData / MyResponseDtoMapper)에 interest 필드가 있으니 interest = this.interest로 변경하거나, 의도라면 의도를 주석으로 명시하세요.
위치: app/src/main/java/com/example/findu/presentation/mapper/torvmodel/MyInterestDataMapper.kt (lines 10-20)app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/SearchViewModel.kt (2)
11-12: 관심 처리 로직 중복 제거 제안 + 파라미터 의미 명확화두 함수(
postProtectInterest,postReportInterest) 구현이 동일합니다. 중복 제거로 유지보수 비용 줄여요. 또한isInterest가 “설정하려는 상태(shouldBeInterested)”인지 “현재 상태”인지 혼동 여지가 있어 명확한 네이밍 권장합니다.- private fun postProtectInterest(id: Long, isInterest: Boolean) { - viewModelScope.launch { - if (isInterest) { - postInterestAnimalUseCase(id).fold( - onSuccess = {}, - onFailure = { - _errorMessage.value = it.message ?: "관심 등록 중 오류가 발생했습니다." - } - ) - } else { - deleteInterestAnimalUseCase(id).fold( - onSuccess = {}, - onFailure = { - _errorMessage.value = it.message ?: "관심 해제 중 오류가 발생했습니다." - } - ) - } - } - } + private fun applyInterest(id: Long, shouldBeInterested: Boolean) { + viewModelScope.launch { + if (shouldBeInterested) { + postInterestAnimalUseCase(id).fold( + onSuccess = {}, + onFailure = { _errorMessage.value = it.message ?: "관심 등록 중 오류가 발생했습니다." } + ) + } else { + deleteInterestAnimalUseCase(id).fold( + onSuccess = {}, + onFailure = { _errorMessage.value = it.message ?: "관심 해제 중 오류가 발생했습니다." } + ) + } + } + } @@ - private fun postProtectInterest(id: Long, isInterest: Boolean) { - viewModelScope.launch { /* ...중복 코드... */ } - } + private fun postProtectInterest(id: Long, isInterest: Boolean) = applyInterest(id, isInterest) @@ - private fun postReportInterest(id: Long, isInterest: Boolean) { - viewModelScope.launch { /* ...중복 코드... */ } - } + private fun postReportInterest(id: Long, isInterest: Boolean) = applyInterest(id, isInterest)추가로,
isInterest가 “현재 상태”라면 위 로직은 반대로 호출됩니다. 이 경우 호출부에서 반전하거나 파라미터명을shouldBeInterested로 바꿔 의미를 고정해 주세요.Also applies to: 26-27, 172-205
105-121: 문구 불일치(사소): 보호 데이터 로딩 에러에서 ‘신고 동물’ 표기보호 탭 로딩 실패 메시지가 “신고 동물”로 표기되어 있습니다. 사용자 혼란 방지를 위해 문구만 교체 권장합니다.
- _errorMessage.value = it.message ?: "신고 동물 데이터를 불러오는 중 오류가 발생했습니다." + _errorMessage.value = it.message ?: "보호 동물 데이터를 불러오는 중 오류가 발생했습니다."app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt (1)
46-61: ViewedAnimals 매핑 정합성 OK + 미세 정리 제안
- cards/Card 구조로의 리네이밍과 reportId 기준 식별 일치합니다. 좋습니다.
- 동일 파일 내 중첩 아이템 매핑이 반복되므로, private 확장 함수로 소폭 중복 제거하면 유지보수에 유리합니다.
예시:
+private fun MyViewedAnimalsResponseDto.Card.toDomain(): MyViewedAnimalData.Card = + MyViewedAnimalData.Card( + reportId = reportId, + date = date, + interest = interest, + location = location, + tag = tag, + thumbnailImageUrl = thumbnailImageUrl, + title = title + ) - cards = cards.map { - MyViewedAnimalData.Card( - reportId = it.reportId, - ... - ) - }, + cards = cards.map { it.toDomain() },또한 여기에도 trailing comma가 있으니 위 코멘트와 동일하게 빌드 설정 확인 부탁드립니다.
app/src/main/java/com/example/findu/data/repositoryimpl/MyRepositoryImpl.kt (2)
66-75: 프로필 이미지 패치: 계약(파트 이름/콘텐츠 타입) 확인Multipart 파트명(file)과 기본 이미지명 RequestBody의 content-type이 서버 스펙과 일치하는지 점검해 주세요. PR 설명에 ViewModel 쪽 미구현 언급이 있어, 연결 시그니처 합의가 중요합니다. 필요하면 뷰모델 바인딩 보일러플레이트 도와드릴게요.
53-64: getNickname: 매핑을 toDomain 확장으로 일원화하세요handleBaseResponse() 구현 확인 결과 BaseResponse.handleBaseResponse()는 성공 시 data가 null이면 Result.failure를 반환하므로 getOrThrow()가 null을 반환하지 않습니다. null 대비(?.)는 불필요합니다.
- 제안: MyNickNameResponseDto.toDomain() 확장 추가 후 사용 — MyRepositoryImpl의 inline let 블록을 toDomain() 호출로 교체.
- 추가 위치: app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt
- 추가 예:
fun MyNickNameResponseDto.toDomain(): MyProfileData =
MyProfileData(nickname = nickname, profileImage = profileImage)- 교체 예 (MyRepositoryImpl):
myRemoteDataSource.getNickname()
.handleBaseResponse()
.getOrThrow()
.toDomain()app/src/main/java/com/example/findu/data/dataremote/datasourceimpl/MyRemoteDataSourceImpl.kt (1)
40-46: 프로필 이미지 패치 위임: OK데이터소스 계층은 얇게 위임만 수행, 좋습니다. 추후 실패 케이스 로깅이 필요하면 이 레이어에서 최소한의 structured log 추가 고려 가능합니다.
app/src/main/java/com/example/findu/data/repositoryimpl/InterestRepositoryImpl.kt (1)
13-37: 중복 매핑 제거하고 공통 mapper 사용 권장여기서 DTO→Domain을 수동으로 구성하고 있습니다. 이미 MyResponseDtoMapper에 동일 목적의 toDomain이 있으므로 재사용하면 중복/불일치 위험을 줄일 수 있습니다. 또한 다른 레포(my 쪽)와 정책을 일치시킵니다.
적용 예:
- override suspend fun getInterestAnimals(lastId: Long): Result<MyInterestData> = - runCatching { - val dto: MyInterestResponseDto = - interestRemoteDataSource - .getInterestAnimals(lastId) - .handleBaseResponse() - .getOrThrow() - ?: error("Empty response body") - - MyInterestData( - interestAnimals = dto.interestAnimals.map { - MyInterestData.InterestAnimal( - reportId = it.reportId, - thumbnailImageUrl = it.thumbnailImageUrl, - title = it.title, - tag = it.tag, - date = it.date, - address = it.address - ) - }, - isLast = dto.isLast, - lastId = dto.lastId - ) - } + override suspend fun getInterestAnimals(lastId: Long): Result<MyInterestData> = + runCatching { + interestRemoteDataSource.getInterestAnimals(lastId) + .handleBaseResponse() + .getOrThrow() + ?.toDomain() ?: error("Empty response body") + }필요 import:
import com.example.findu.data.mapper.toDomain.my.toDomainapp/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt (5)
42-46: 네이밍 미스매치: patchProfileImageFileUseCase 변수명타입은 PatchProfileImageUseCase인데 변수명은 patchProfileImageFileUseCase로 파일 업로드에 한정된 뉘앙스를 줍니다. 혼동 방지를 위해 patchProfileImageUseCase로 변경 권장.
- private val patchProfileImageFileUseCase: PatchProfileImageUseCase, + private val patchProfileImageUseCase: PatchProfileImageUseCase,
152-166: 갤러리 업로드 미구현 — VM에서 Uri 처리 대신 Part를 주입받는 방식으로 단순화 권장ViewModel은 Context가 없어 Uri→File 변환이 번거롭습니다. Fragment에서 MultipartBody.Part 생성 후 VM에 전달하는 형태가 깔끔합니다.
권장 변경안(메서드 시그니처 및 본문):
- fun updateProfileImageFromGallery(uri: Uri) { - _selectedProfileImageUri.value = uri - viewModelScope.launch { - // patchProfileImageFileUseCase.uploadFile(multipartBody) ... - } - } + fun updateProfileImageFromGallery(part: MultipartBody.Part) { + viewModelScope.launch { + patchProfileImageUseCase.uploadFile(part).fold( + onSuccess = { fetchMyProfile() }, + onFailure = { _errorMessage.value = it.message ?: "프로필 이미지 변경 중 오류가 발생했습니다." } + ) + } + }Fragment 예시(참고용):
val bytes = requireContext().contentResolver.openInputStream(uri)!!.readBytes() val body = bytes.toRequestBody("image/*".toMediaType()) val part = MultipartBody.Part.createFormData("profileImageFile", "profile.jpg", body) viewModel.updateProfileImageFromGallery(part)원하시면 PR에 맞춰 구현 코드까지 만들어 드릴게요.
199-216: 불필요한 변수 할당(val result) 제거fold 결과를 변수에 담지 않아도 됩니다. 간단히 정리하세요.
- viewModelScope.launch { - val result = if (isInterest) { + viewModelScope.launch { + if (isInterest) { postInterestAnimalUseCase(id).fold( onSuccess = {}, onFailure = { _errorMessage.value = it.message ?: "관심 등록 중 오류가 발생했습니다." } ) } else { deleteInterestAnimalUseCase(id).fold( onSuccess = {}, onFailure = { _errorMessage.value = it.message ?: "관심 해제 중 오류가 발생했습니다." } ) } }
199-216: UX 제안: 서버 성공 시 로컬 상태 즉시 반영 또는 목록 리프레시관심 토글 성공 후 현재 화면의 리스트/카드의 상태를 즉시 반영하거나, 필요 시 fetchInterestAnimals 호출로 새로고침하는 방식을 고려해 주세요.
186-197: UseCase 네이밍 불일치 — GetNickNameUseCase → GetMyProfileUseCase로 변경 권장fetchMyProfile()에서 getNickNameUseCase()의 성공 결과를 _myProfile에 할당하고 있습니다. UseCase 이름이 반환값/의도를 정확히 반영하도록 이름 변경하거나(예: GetMyProfileUseCase) 반환 데이터와 사용처를 일치시키세요.
파일: app/src/main/java/com/example/findu/domain/usecase/my/GetNickNameUseCase.kt, app/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt (fetchMyProfile, ~186–197행)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (40)
app/src/main/java/com/example/findu/data/dataremote/datasource/InterestRemoteDataSource.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/datasource/MyRemoteDataSource.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/datasourceimpl/InterestRemoteDataSourceImpl.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/datasourceimpl/MyRemoteDataSourceImpl.kt(2 hunks)app/src/main/java/com/example/findu/data/dataremote/model/request/MyInterestRequestDto.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyInterestResponseDto.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyNickNameResponseDto.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyReportHistoryResponseDto.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyViewedAnimalsResponseDto.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/service/InterestService.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt(1 hunks)app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt(2 hunks)app/src/main/java/com/example/findu/data/repositoryimpl/InterestRepositoryImpl.kt(1 hunks)app/src/main/java/com/example/findu/data/repositoryimpl/MyRepositoryImpl.kt(2 hunks)app/src/main/java/com/example/findu/di/UseCaseModule.kt(2 hunks)app/src/main/java/com/example/findu/domain/model/my/MyInterestData.kt(1 hunks)app/src/main/java/com/example/findu/domain/model/my/MyProfileData.kt(1 hunks)app/src/main/java/com/example/findu/domain/model/my/MyReportHistoryData.kt(2 hunks)app/src/main/java/com/example/findu/domain/model/my/MyViewedAnimalData.kt(1 hunks)app/src/main/java/com/example/findu/domain/repository/InterestRepository.kt(1 hunks)app/src/main/java/com/example/findu/domain/repository/MyRepository.kt(1 hunks)app/src/main/java/com/example/findu/domain/usecase/interest/DeleteInterestAnimalUseCase.kt(1 hunks)app/src/main/java/com/example/findu/domain/usecase/interest/PostInterestAnimalUseCase.kt(1 hunks)app/src/main/java/com/example/findu/domain/usecase/interest/PostInterestProtectingAnimalUseCase.kt(0 hunks)app/src/main/java/com/example/findu/domain/usecase/interest/PostInterestReportAnimalUseCase.kt(0 hunks)app/src/main/java/com/example/findu/domain/usecase/my/GetInterestUseCase.kt(1 hunks)app/src/main/java/com/example/findu/domain/usecase/my/GetReportHistoryUseCase.kt(1 hunks)app/src/main/java/com/example/findu/domain/usecase/my/GetViewedAnimalUseCase.kt(1 hunks)app/src/main/java/com/example/findu/domain/usecase/my/PatchProfileImageUseCase.kt(1 hunks)app/src/main/java/com/example/findu/presentation/mapper/torvmodel/MyInterestDataMapper.kt(2 hunks)app/src/main/java/com/example/findu/presentation/model/MyRv.kt(2 hunks)app/src/main/java/com/example/findu/presentation/ui/my/MyFragment.kt(5 hunks)app/src/main/java/com/example/findu/presentation/ui/my/MyInterestFragment.kt(0 hunks)app/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt(8 hunks)app/src/main/java/com/example/findu/presentation/ui/my/MyViewedAnimalFragment.kt(0 hunks)app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyInterestRvAdapter.kt(3 hunks)app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyViewedAnimalsRvAdapter.kt(2 hunks)app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/DetailReportViewModel.kt(3 hunks)app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/DetailSearchViewModel.kt(3 hunks)app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/SearchViewModel.kt(4 hunks)
💤 Files with no reviewable changes (4)
- app/src/main/java/com/example/findu/domain/usecase/interest/PostInterestReportAnimalUseCase.kt
- app/src/main/java/com/example/findu/domain/usecase/interest/PostInterestProtectingAnimalUseCase.kt
- app/src/main/java/com/example/findu/presentation/ui/my/MyInterestFragment.kt
- app/src/main/java/com/example/findu/presentation/ui/my/MyViewedAnimalFragment.kt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-16T15:20:00.175Z
Learnt from: t1nm1ksun
PR: FindYou-Kuit/FindYou-Android#77
File: app/src/main/java/com/example/findu/data/mapper/todomain/LoginResponseDtoMapper.kt:1-1
Timestamp: 2025-08-16T15:20:00.175Z
Learning: LoginResponseDtoMapper.kt와 관련된 패키지/디렉터리 구조는 이미 올바르게 com.example.findu.data.mapper.todomain으로 통일되어 있다. 초기 분석 시 더 신중하게 확인해야 한다.
Applied to files:
app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt
🧬 Code graph analysis (3)
app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyInterestRvAdapter.kt (2)
app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyViewedAnimalsRvAdapter.kt (1)
onKeepClick(14-112)app/src/main/java/com/example/findu/presentation/ui/search/adapter/SearchContentRVAdapter.kt (1)
onItemClick(14-146)
app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyViewedAnimalsRvAdapter.kt (1)
app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyInterestRvAdapter.kt (1)
onKeepClick(14-105)
app/src/main/java/com/example/findu/presentation/ui/my/MyFragment.kt (2)
app/src/main/java/com/example/findu/presentation/ui/my/MyViewedAnimalFragment.kt (1)
observeViewModel(88-101)app/src/main/java/com/example/findu/presentation/ui/my/MyReportHistoryFragment.kt (1)
observeViewModel(83-94)
🔇 Additional comments (41)
app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyNickNameResponseDto.kt (1)
8-13: profileImage 필드 추가 확인됨닉네임과 함께 프로필 이미지를 반환하도록 DTO가 확장되었습니다. 도메인 레이어의
MyProfileData와 일치하는 변경사항으로 보입니다.app/src/main/java/com/example/findu/domain/usecase/interest/DeleteInterestAnimalUseCase.kt (1)
6-12: 관심 동물 삭제 API 통합 완료기존의 분리된 삭제 로직이 단일
deleteInterestAnimal메서드로 통합되었습니다. 리포지토리의 결과를 그대로 반환하는 깔끔한 구현입니다.app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyReportHistoryResponseDto.kt (2)
30-32: interest 필드 추가로 UI 업데이트 가능관심 등록 상태를 나타내는
interest불리언 필드가 추가되었습니다. 이제 리포트 목록에서 하트 아이콘 등으로 관심 상태를 표시할 수 있게 되었네요.
11-12: @SerialName 불일치 — API 필드 확인 필요app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyReportHistoryResponseDto.kt (라인 11–12): @SerialName("lastReportId")와 프로퍼티명 lastId가 불일치합니다. API 응답 필드가 'lastReportId'인지 'lastId'인지 백엔드 스펙(OpenAPI/Swagger) 또는 실제 응답 샘플로 확인해 어노테이션 또는 프로퍼티명을 일치시켜 수정하세요. (참고: 이전 자동 검색에서 rg가 .kt 타입을 인식하지 못해 검증이 실패했습니다.)
app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyInterestResponseDto.kt (2)
8-15: 페이징 필드 통합 완료기존의
lastInterestProtectId와lastInterestReportId두 개의 페이징 필드가lastId하나로 통합되었습니다. API v2로의 마이그레이션에 맞는 적절한 변경입니다.
28-29: 확인 완료 — MyInterestResponseDto의 address 사용은 의도적입니다MyInterestResponseDto의 @SerialName("address")/address는 도메인(MyInterestData.kt: val address), 데이터 매퍼(data/mapper/toDomain/my/MyResponseDtoMapper.kt: address = it.address) 및 UI(MyInterestRvAdapter 등)에서 address로 일관되게 사용되고 있으므로 문제 없음.
app/src/main/java/com/example/findu/data/dataremote/datasource/InterestRemoteDataSource.kt (1)
6-10: 관심 동물 API 인터페이스 간소화4개의 분산된 메서드를 3개의 통합된 메서드로 정리했습니다:
- 조회:
getInterestAnimals(lastId)- 등록:
registerInterestAnimal(reportId)- 삭제:
deleteInterestAnimal(reportId)API v2 마이그레이션에 맞춰 인터페이스가 훨씬 깔끔해졌네요.
app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyViewedAnimalsRvAdapter.kt (2)
61-61: reportId 전환 반영 잘 됨이벤트 파라미터를 reportId로 일원화한 부분 문제 없어 보입니다.
Also applies to: 72-75
59-69: 뷰홀더에서 모델 직접 토글 금지 — 불변 리스트/롤백 확인 필요
ViewHolder에서 item.interest를 직접 토글하면 ListAdapter가 관리하는 불변 리스트와 충돌해 스크롤 등에서 변경이 되돌아갈 수 있습니다. 토글은 onKeepClick → ViewModel/데이터 소스 반영 → adapter.submitList 재바인딩(또는 실패 시 UI 롤백) 으로 처리하세요.
위치: app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyViewedAnimalsRvAdapter.kt (59–69)
검증 결과: repo 검색에서 onKeepClick/submitList/notifyItem/interest 호출을 찾지 못해 구현 흐름(콜백/submitList/롤백) 여부를 수동으로 확인해야 합니다.app/src/main/java/com/example/findu/domain/model/my/MyReportHistoryData.kt (2)
5-5: lastId 리네이밍 반영 LGTM페이징 파라미터 일원화 방향과 일치합니다.
14-16: interest 도입 검증 결과 — 매퍼/소비부 반영 확인, 매퍼 하드코딩·테스트 확인 필요
- DTO→domain 매핑은 반영되어 있음: app/src/main/java/com/example/findu/data/mapper/todomain/SearchResponseDtoMapper.kt, DetailSearchResponseDtoMapper.kt, app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt 등에서 interest 매핑 확인.
- UI 소비부에서 interest를 전달·토글·소비함: app/src/main/java/com/example/findu/presentation/ui/my/* 및 presentation/ui/search/* (예: MyViewedAnimalsRvAdapter.kt, MyInterestRvAdapter.kt, MyViewedAnimalFragment.kt, MyInterestFragment.kt, DetailSearchViewModel/DetailReportViewModel 등).
- 확인 필요: app/src/main/java/com/example/findu/presentation/mapper/torvmodel/MyInterestDataMapper.kt 의 interest = true(라인 ~18) — 의도된 동작인지 검토.
- 테스트 확인 필요: interest 관련 매퍼/UI 소비부 테스트가 검색되지 않음 — 관련 유닛/UI 테스트가 갱신되었는지 확인.
app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyInterestRvAdapter.kt (2)
35-35: reportId/address 전환 반영 OK바인딩과 클릭 콜백 모두 새 스키마와 일치합니다.
Also applies to: 61-61, 70-70
59-69: 토글 실패 원복 / 바인딩 일관성 검증 필요
- 어댑터에서 클릭 시 item.interest를 즉시 반전(optimistic)한 뒤 onKeepClick(→ MyInterestFragment.myViewModel.setInterest)을 호출합니다. setInterest가 API 실패 시 UI/모델을 원복하거나 interestAnimals 플로우로 재동기화하는지 확인하세요.
- 위치: app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyInterestRvAdapter.kt (flMyHistoryKeep 클릭, ~59-69행), app/src/main/java/com/example/findu/presentation/ui/my/MyInterestFragment.kt (adapter 생성·submitList, ~28-31 / ~93-96행), MyViewModel.setInterest 구현 확인 필요.
app/src/main/java/com/example/findu/domain/usecase/my/GetViewedAnimalUseCase.kt (1)
10-13: 단일 lastId 진입점 정리 — 로컬 확인 필요단일 lastId 진입은 적절합니다. 자동 검사(rg)가 저장소에서 검색을 수행하지 않아 호출처/시그니처 일치 여부를 확인하지 못했습니다. 로컬에서 아래 명령을 실행해 출력(또는 관련 파일 경로)을 첨부하세요:
rg -nP '\bgetMyViewedAnimals\s*\(' -g '!**/build/**' -C2 rg -nP '\blastId\s*:' -g '!**/build/**' -C1app/src/main/java/com/example/findu/domain/model/my/MyProfileData.kt (1)
3-6: 도메인 모델 추가 OK — profileImage null 허용 여부 확인 필요리포지토리 검색이 실패해 사용처를 찾을 수 없습니다. profileImage가 서버에서 빈값으로 올 가능성이 있으면 String?로 변경 권장.
-data class MyProfileData( - val nickname: String, - val profileImage: String, -) +data class MyProfileData( + val nickname: String, + val profileImage: String? +)app/src/main/java/com/example/findu/domain/usecase/my/GetReportHistoryUseCase.kt (1)
9-9: lastId 파라미터 리네이밍 LGTM리포지토리 호출 일치, 잔존 호출자/구시그니처 없음 — 확인 파일: app/src/main/java/com/example/findu/domain/usecase/my/GetReportHistoryUseCase.kt, app/src/main/java/com/example/findu/domain/repository/MyRepository.kt, app/src/main/java/com/example/findu/data/repositoryimpl/MyRepositoryImpl.kt
app/src/main/java/com/example/findu/domain/usecase/interest/PostInterestAnimalUseCase.kt (1)
6-11: 표현식 바디로 간결화 — 호출부 .fold 처리 확인invoke를 표현식 바디로 변경해도 됩니다. ViewModel 등 호출부가 모두 Result.fold로 처리되는 것을 스크립트로 확인했습니다.
-class PostInterestAnimalUseCase @Inject constructor( - private val interestRepository: InterestRepository -) { - suspend operator fun invoke(reportId: Long): Result<Unit> { - return interestRepository.registerInterestAnimal(reportId) - } -} +class PostInterestAnimalUseCase @Inject constructor( + private val interestRepository: InterestRepository +) { + suspend operator fun invoke(reportId: Long): Result<Unit> = + interestRepository.registerInterestAnimal(reportId) +}app/src/main/java/com/example/findu/data/dataremote/datasource/MyRemoteDataSource.kt (2)
13-23: 페이징 파라미터 통합이 잘 되었습니다!이전에 분리되어 있던
lastReportId와lastProtectId를 단일lastId로 통합한 것은 API 일관성을 높이는 좋은 변경입니다.
31-33: 프로필 이미지 업데이트 메서드 추가 확인 — ViewModel 호출 존재(파일 업로드 호출은 주석 처리됨)새 메서드들이 구현되어 있고 ViewModel 쪽 호출이 확인됩니다:
- app/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt — UseCase 주입(라인 45), default 업로드 호출(라인 141), file 업로드 호출 주석(라인 157).
- 도메인·데이터 계층 구현 확인: MyRepository.kt, domain/usecase/my/PatchProfileImageUseCase.kt, data/repositoryimpl/MyRepositoryImpl.kt, data/dataremote/datasourceimpl/MyRemoteDataSourceImpl.kt, data/dataremote/service/MyService.kt, data/dataremote/datasource/MyRemoteDataSource.kt
ViewModel이 완전히 완료되었는지와 주석 처리된 파일 업로드 호출을 활성화할지 확인하세요.
app/src/main/java/com/example/findu/domain/repository/MyRepository.kt (1)
28-28: getNickname() 반환 타입 변경 확인
String에서MyProfileData로 반환 타입이 변경되었는데, 이는 프로필 이미지 정보를 함께 제공하기 위한 좋은 개선입니다.app/src/main/java/com/example/findu/presentation/ui/my/MyFragment.kt (2)
66-66: 프로필 데이터 초기 로드 변경 확인
fetchNickName()에서fetchMyProfile()로 변경되어 닉네임과 프로필 이미지를 함께 가져오도록 개선되었습니다.
262-262: 메모리 누수 방지를 위한 null 처리
myProfileImageDialog를 null로 설정하여 메모리 누수를 방지하는 것은 좋은 practice입니다.app/src/main/java/com/example/findu/presentation/mapper/torvmodel/MyInterestDataMapper.kt (1)
17-18: 필드명 변경 확인 — 해결됨MyInterestRv가 이미 address 필드를 사용하도록 정의되어 있어 매퍼의 address 할당과 일치합니다 (app/src/main/java/com/example/findu/presentation/model/MyRv.kt, data class MyInterestRv — 22행).
app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/DetailSearchViewModel.kt (1)
7-9: 관심 UseCase 의존성 교체 LGTMPost/Delete를 Animal 단일 도메인으로 합친 방향 잘 맞습니다. DI 시그니처도 일관적이에요.
Also applies to: 18-19
app/src/main/java/com/example/findu/data/dataremote/model/response/my/MyViewedAnimalsResponseDto.kt (1)
11-14: lastId + cards 구조 전환 LGTM (마이 탭 페이징 정합성 확인 부탁)단일
lastId와Card(reportId, …, interest)로 합친 모델 괜찮습니다. 서버 스키마와 일치만 하면 디코딩 이슈 없을 듯해요. 맵퍼/어댑터가Card로 교체됐는지 최종 확인만 부탁드립니다.Also applies to: 17-31
app/src/main/java/com/example/findu/di/UseCaseModule.kt (1)
22-24: UseCase DI 교체 LGTM — 레거시 제공자 스캔 재실행 필요새 UseCase 바인딩은 정상으로 보입니다. 다만 제공한 rg 스크립트가 "No files were searched"로 실패해 레거시 제공자/주입점 존재 여부를 자동 확인하지 못했습니다.
조치: 레포 루트에서 아래 명령을 실행한 뒤 결과를 올려주기
rg -nP --hidden -S "PostInterest|DeleteInterest|provide(.*Interest"동일 적용: 149-157
app/src/main/java/com/example/findu/domain/repository/InterestRepository.kt (1)
3-8: 단일화된 관심 API 시그니처 승인 — reportId KDoc 추가 권장reportId 통합 방향 OK. 레거시 메서드명으로 전체 검색(rg --no-ignore --hidden -nP)한 결과 일치 항목 없음. reportId가 신고/보호 카드의 공통 키임을 KDoc으로 명시.
app/src/main/java/com/example/findu/data/repositoryimpl/MyRepositoryImpl.kt (3)
19-26: getMyInterest: DTO→Domain 매핑 경로 일관성 좋습니다Remote → handleBaseResponse → toDomain 흐름이 명확합니다.
28-32: getMyReportHistory: OK페이징 인자 및 매핑 경로 일치합니다.
34-41: getMyViewedAnimals: OK단일 lastId 기반으로 정리된 호출부 일관성 확인했습니다.
app/src/main/java/com/example/findu/data/dataremote/datasourceimpl/MyRemoteDataSourceImpl.kt (1)
19-22: 단일 lastId 파라미터로 정리: OKService 위임이 간결하고 변경 의도에 부합합니다.
app/src/main/java/com/example/findu/data/dataremote/datasourceimpl/InterestRemoteDataSourceImpl.kt (1)
13-20: 리모트 데이터소스 위임 OK
- lastId 통합 조회, 등록(POST body DTO), 삭제(경로 파라미터)로 간결합니다.
- 에러 매핑은 상위 레이어(handleBaseResponse)에서 처리되므로 여기서는 이 정도가 적절합니다.
app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt (2)
10-24: MyInterestResponseDto → Domain 매핑 정합성 OK
MyInterestResponseDto의 interestAnimals가 non-nullable List로 선언되어 있고 isLast/lastId도 non-nullable입니다. 현재 매핑은 안전하므로 빈 리스트 대체 처리는 필요 없습니다.
28-44: 확인 필요 — trailing comma 허용 여부 및 null-safety 검토스크립트 실행 결과 빌드 설정에서 관련 항목이 검색되지 않았습니다. 보수적으로 다음을 확인/조치하세요:
- Trailing comma: Kotlin compiler 설정(languageVersion/apiVersion 또는 freeCompilerArgs)에서 trailing comma 허용 여부 확인. 확인 불가 시 trailing comma 제거 제안:
- lastId = lastId, + lastId = lastId
- Null-safety: reports 리스트 및 내부 필드(reportId, tag, thumbnailImageUrl 등)가 nullable인지 확인. nullable이면 map 전에 null 체크하거나 map 내부에서 안전호출(?.) 또는 기본값 사용.
파일: app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt (28–44)
app/src/main/java/com/example/findu/data/repositoryimpl/InterestRepositoryImpl.kt (1)
39-47: handleBaseResponse 결과 처리 누락 가능성현재
.getOrThrow()호출이 없습니다.handleBaseResponse()가Result<Unit>를 반환한다면,runCatching { ... }가Result<Result<Unit>>로 중첩될 수 있고, 에러 전파가 누락됩니다. 다른 레포 구현과 동일하게.getOrThrow()로 정규화하세요.수정 예:
- override suspend fun registerInterestAnimal(reportId: Long): Result<Unit> = - runCatching { - interestRemoteDataSource.registerInterestAnimal(reportId).handleBaseResponse() - } + override suspend fun registerInterestAnimal(reportId: Long): Result<Unit> = + runCatching { + interestRemoteDataSource.registerInterestAnimal(reportId) + .handleBaseResponse() + .getOrThrow() + } - override suspend fun deleteInterestAnimal(reportId: Long): Result<Unit> = - runCatching { - interestRemoteDataSource.deleteInterestAnimal(reportId).handleBaseResponse() - } + override suspend fun deleteInterestAnimal(reportId: Long): Result<Unit> = + runCatching { + interestRemoteDataSource.deleteInterestAnimal(reportId) + .handleBaseResponse() + .getOrThrow() + }확인 스크립트:
app/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt (3)
115-116: cards → RV 모델 매핑 깔끔합니다프레젠테이션 변환이 명확하고 NPE 여지도 없습니다. 굿.
138-150: 기본 프로필 이미지 변경 흐름 OKtext/plain으로 enumName 전송, 성공 시 fetchMyProfile로 데이터 새로고침까지 자연스럽습니다.
서버가 기대하는 파트 키가 "defaultProfileImageName"인지 재확인만 해주세요.
81-83: 초기 호출에 Long.MAX_VALUE 대신 lastId: Long? = null 검토 — 전역 영향 확인 필요검증 결과: MyViewModel에서 초기 호출에 Long.MAX_VALUE를 사용하고 있으나 도메인/레포지토리/유스케이스/원격 계층은 lastId: Long(비-null) 또는 기본값 Long.MAX_VALUE로 정의되어 있어 nullable 전략 적용 시 인터페이스 전면 변경(서버 API 계약·Retrofit 파라미터 처리 포함)이 필요합니다.
확인/수정 포인트(우선 검사 대상)
- app/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt — 라인 80-83, 96-98, 111-113
- app/src/main/java/com/example/findu/domain/repository/MyRepository.kt 및 usecase: domain/usecase/my/GetInterestUseCase.kt, GetReportHistoryUseCase.kt, GetViewedAnimalUseCase.kt
- app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt (Retrofit @query("lastId") lastId: Long), InterestService.kt, MyRemoteDataSource*/impl, InterestRemoteDataSource*/impl
- 검색 페이징 관련: domain/usecase/GetSearchUseCase.kt, data/dataremote/service/SearchService.kt 등(많은 곳에서 Long.MAX_VALUE 기본값 사용)
권장 조치: 먼저 서버가 lastId 미전달을 허용하는지(또는 Retrofit에서 nullable 쿼리를 안전하게 처리하는지) 확인한 뒤, 전체 계층을 한 번에 nullable로 변경하거나 일관성을 위해 sentinel(Long.MAX_VALUE) 사용을 유지하세요.
app/src/main/java/com/example/findu/data/dataremote/service/InterestService.kt (3)
20-23: registerInterestAnimal 바디 스키마 — 백엔드 계약 확인 필요MyInterestRequestDto가 app/src/main/java/com/example/findu/data/dataremote/model/request/MyInterestRequestDto.kt에 data class MyInterestRequestDto(val reportId: Long)로 정의되어 있음. 백엔드가 동일한 JSON 키('reportId' 또는 'report_id')와 숫자 타입을 기대하는지 확인 필요. 불일치 시 400/415 발생 가능 — 불일치하면 DTO 필드명 변경 또는 @SerializedName/@JsonProperty로 매핑 처리.
위치:
- app/src/main/java/com/example/findu/data/dataremote/model/request/MyInterestRequestDto.kt
- app/src/main/java/com/example/findu/data/dataremote/service/InterestService.kt (registerInterestAnimal)
15-18: 중복 엔드포인트 및 응답 래퍼 불일치 — 통일 필요MyService.getInterestAnimals(...): BaseResponse — app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt:20-23
InterestService.getInterestAnimals(...): NullableBaseResponse — app/src/main/java/com/example/findu/data/dataremote/service/InterestService.kt:15-18동일 경로(/api/v2/users/me/interest-animals)가 두 인터페이스에 중복 정의되어 있고 반환 래퍼(BaseResponse vs NullableBaseResponse)가 달라 스펙 드리프트 및 런타임 불일치 위험이 있음.
권장 조치:
- 엔드포인트 정의와 응답 래퍼를 하나로 통일(한 쪽 제거 또는 둘 중 일관된 타입으로 변경).
- 만약 의도적 차이라면 그 근거를 문서화하고 관련 호출부(InterestRemoteDataSource/InterestRemoteDataSourceImpl/InterestRepositoryImpl 및 MyRemoteDataSource/MyRemoteDataSourceImpl/MyRepositoryImpl)를 일괄 정리.
25-28: DELETE에서 @query("reportId") 사용 및 응답 타입(NullableBaseResponse) 유지 권장프로젝트 전반(ReportService.deleteReport, MyService.deleteUser 등)과 InterestRemoteDataSourceImpl/InterestRepository가 NullableBaseResponse로 통일되어 있어 변경 불필요 — 서버가 204 No Content여도 Unit 래핑은 무해하며, 서버가 200 + body면 현 구조 유지.
app/src/main/java/com/example/findu/data/dataremote/model/request/MyInterestRequestDto.kt
Show resolved
Hide resolved
app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt
Show resolved
Hide resolved
app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt
Show resolved
Hide resolved
| @Multipart | ||
| @PATCH("/api/v2/users/me/profile-image") | ||
| suspend fun patchProfileImageFile( | ||
| @Part profileImageFile: MultipartBody.Part | ||
| ): NullableBaseResponse<Unit> | ||
|
|
||
| @Multipart | ||
| @PATCH("/api/v2/users/me/profile-image") | ||
| suspend fun patchProfileImageDefault( | ||
| @Part("defaultProfileImageName") defaultProfileImageName: RequestBody | ||
| ): NullableBaseResponse<Unit> |
There was a problem hiding this comment.
🧩 Analysis chain
멀티파트 파트 이름 확인 필요(profileImageFile)
MultipartBody.Part는 애노테이션의 이름을 쓰지 않고, createFormData의 name이 전송됩니다. 서버가 기대하는 name이 "profileImageFile"이면, 업로드 측에서 반드시 아래처럼 생성되는지 확인해 주세요.
val part = MultipartBody.Part.createFormData(
"profileImageFile", file.name, file.asRequestBody("image/*".toMediaType())
)검증 스크립트(Part 생성부 점검):
🏁 Script executed:
#!/bin/bash
rg -nP 'createFormData\(' --type=kt -C2Length of output: 26
rg가 --type=kt를 인식하지 못해 검색이 실패했습니다. 리포지토리 전체에서 안전하게 재검증할 새 스크립트를 실행하겠습니다 — 결과 받고 최종 리뷰 코멘트로 업데이트할게요.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching for createFormData( in Kotlin files..."
rg -nP 'createFormData\(' -g '**/*.kt' -C2 || true
echo "Searching for MultipartBody.Part.createFormData in Kotlin files..."
rg -nP 'MultipartBody\.Part\.createFormData' -g '**/*.kt' -C2 || true
echo "Searching for literal '\"profileImageFile\"' in Kotlin files..."
rg -nP '"profileImageFile"' -g '**/*.kt' -C2 || true
echo "Searching for usages of patchProfileImageFile / patchProfileImageDefault across repo..."
rg -nP '\bpatchProfileImage(File|Default)\b' -g '**/*' -C2 || true
echo "Done."Length of output: 7735
멀티파트 파트 이름 불일치 확인 및 수정 필요 (profileImageFile vs files)
app/src/main/java/com/example/findu/presentation/util/UriUtil.kt:61에서 MultipartBody.Part.createFormData("files", file.name, image)로 파트를 생성하고 있는데, app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt:48의 @part profileImageFile: MultipartBody.Part와 이름이 다릅니다. createFormData의 첫 인자를 "profileImageFile"로 변경하거나 서버/API 명세에 맞춰 이름을 통일하세요.
🤖 Prompt for AI Agents
In app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt
around lines 46 to 56, the Multipart part name annotated/expected
("profileImageFile") does not match the part name used when building the
MultipartBody.Part ("files") in UriUtil.kt; update either the createFormData
call to use "profileImageFile" or change the @Part parameter name/annotation to
"files" so both client code and service interface use the exact same field name
per the API spec; ensure the chosen name matches the server/API contract and
update tests or callers accordingly.
app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/DetailReportViewModel.kt
Outdated
Show resolved
Hide resolved
ikseong00
left a comment
There was a problem hiding this comment.
고생하셨습니다 ! 코드리뷰 확인 부탁드려요~
app/src/main/java/com/example/findu/data/repositoryimpl/InterestRepositoryImpl.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/example/findu/data/repositoryimpl/MyRepositoryImpl.kt
Outdated
Show resolved
Hide resolved
| suspend fun uploadFile(profileImageFile: MultipartBody.Part) = | ||
| myRepository.patchProfileImageFile(profileImageFile) | ||
|
|
||
| suspend fun uploadDefault(defaultProfileImageName: RequestBody) = | ||
| myRepository.patchProfileImageDefault(defaultProfileImageName) |
There was a problem hiding this comment.
MultipartBody 와 RequestBody 는 Domain 레이어에서 알지 않아도 되는 부분 같습니다!
viewModel 에서 멀티파트, RequestBody 로 매핑하는 것 같은데 해당 로직을 Data 로 옮겨주세요!
https://github.com/FindYou-Kuit/FindYou-Android/pull/88/files 여기 PR에 보시면 좋을 것 같습니다
app/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
app/src/main/java/com/example/findu/data/dataremote/model/request/PatchNicknameRequestDto.kt (1)
1-8: 구조가 깔끔합니다.새로운 DTO가 잘 정의되었습니다.
@Serializable어노테이션도 적절히 적용되어 있고 필드명도 명확합니다.선택 사항: 닉네임 유효성 검증(길이 제한, 특수문자 등)이 필요한 경우 repository나 use case 레이어에서 처리되고 있는지 확인해보시면 좋을 것 같습니다.
app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyProfileMapper.kt (1)
1-1: 패키지 네이밍 컨벤션 확인패키지명이
todomain으로 소문자로 되어 있는데, 일반적으로 카멜케이스(toDomain)를 사용하는 경우가 많습니다. 프로젝트 전체의 패키지 네이밍 컨벤션과 일관성을 유지하는 것이 좋습니다.app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyInterestMapper.kt (1)
1-1: 패키지 네이밍 컨벤션 확인
MyProfileMapper.kt와 동일하게, 패키지명todomain의 컨벤션을 프로젝트 전체와 일관되게 유지해주세요.app/src/main/java/com/example/findu/presentation/ui/my/MyFragment.kt (1)
87-95: 기본 이미지 이름 매핑을 ProfileImageType으로 일원화해주세요.
when으로 리소스 ID와 서버용 문자열을 직접 매핑하면 ProfileImageType enum과 지식이 중복돼서 한쪽만 수정될 위험이 있어요. enum에 drawable→serverName 역매핑 헬퍼를 두고 여기서는 그 값을 사용하도록 정리하면 유지보수가 더 수월할 것 같습니다.app/src/main/java/com/example/findu/data/dataremote/datasourceimpl/MyRemoteDataSourceImpl.kt (1)
21-32: lastId를 nullable로 열어두는 편이 API 계약과 더 잘 맞을 것 같아요.서비스 계층이 이미
@Query("lastId") lastId: Long? = null로 바뀌었는데, 데이터소스에서Long을 강제하면 호출부가 첫 페이지를 위해Long.MAX_VALUE같은 센티넬 값에 여전히 의존할 여지가 있습니다. 시그니처를Long?로 맞추고 초기 페이지에는 null을 넘기도록 하면 API 사용이 더 자연스러울 듯해요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
app/src/main/java/com/example/findu/data/dataremote/datasource/MyRemoteDataSource.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/datasourceimpl/MyRemoteDataSourceImpl.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/model/request/MyInterestRequestDto.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/model/request/PatchNicknameRequestDto.kt(1 hunks)app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt(1 hunks)app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyInterestMapper.kt(1 hunks)app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyProfileMapper.kt(1 hunks)app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt(1 hunks)app/src/main/java/com/example/findu/data/mapper/todomain/SearchResponseDtoMapper.kt(1 hunks)app/src/main/java/com/example/findu/data/repositoryimpl/InterestRepositoryImpl.kt(1 hunks)app/src/main/java/com/example/findu/data/repositoryimpl/MyRepositoryImpl.kt(2 hunks)app/src/main/java/com/example/findu/domain/model/search/SearchData.kt(1 hunks)app/src/main/java/com/example/findu/domain/repository/MyRepository.kt(1 hunks)app/src/main/java/com/example/findu/domain/usecase/my/PatchProfileImageUseCase.kt(1 hunks)app/src/main/java/com/example/findu/presentation/ui/my/MyFragment.kt(6 hunks)app/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt(7 hunks)app/src/main/java/com/example/findu/presentation/ui/my/model/ProfileImageType.kt(1 hunks)app/src/main/java/com/example/findu/presentation/ui/search/adapter/SearchContentRVAdapter.kt(2 hunks)app/src/main/java/com/example/findu/presentation/ui/search/model/SearchRv.kt(1 hunks)app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchAllFragment.kt(2 hunks)app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchReportFragment.kt(2 hunks)app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchRescueFragment.kt(2 hunks)app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/DetailReportViewModel.kt(3 hunks)app/src/main/java/com/example/findu/presentation/ui/search/viewmodel/DetailSearchViewModel.kt(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- app/src/main/java/com/example/findu/data/dataremote/datasource/MyRemoteDataSource.kt
- app/src/main/java/com/example/findu/data/dataremote/model/request/MyInterestRequestDto.kt
🧰 Additional context used
🧬 Code graph analysis (4)
app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchRescueFragment.kt (2)
app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchAllFragment.kt (1)
navigateToDetail(104-133)app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchReportFragment.kt (1)
navigateToDetail(142-169)
app/src/main/java/com/example/findu/presentation/ui/my/MyFragment.kt (2)
app/src/main/java/com/example/findu/presentation/ui/my/MyViewedAnimalFragment.kt (1)
observeViewModel(88-101)app/src/main/java/com/example/findu/presentation/ui/my/MyReportHistoryFragment.kt (1)
observeViewModel(83-94)
app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchAllFragment.kt (2)
app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchReportFragment.kt (1)
navigateToDetail(142-169)app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchRescueFragment.kt (1)
navigateToDetail(102-129)
app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchReportFragment.kt (2)
app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchAllFragment.kt (1)
navigateToDetail(104-133)app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchRescueFragment.kt (1)
navigateToDetail(102-129)
🔇 Additional comments (23)
app/src/main/java/com/example/findu/domain/model/search/SearchData.kt (1)
13-13: LGTM! 식별자 이름 변경이 명확합니다.
cardId에서reportId로의 변경이 도메인 모델 전반에 걸쳐 일관되게 적용되었습니다. 다른 필드들은 그대로 유지되어 변경 범위가 명확합니다.app/src/main/java/com/example/findu/presentation/ui/my/model/ProfileImageType.kt (1)
7-20: 깔끔한 enum 구현입니다.서버 이름과 drawable 리소스를 매핑하는 enum이 잘 구현되었습니다.
fromServerName함수가 null이나 매칭되지 않는 값에 대해 DEFAULT를 반환하여 안전한 폴백을 제공하고 있습니다.app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt (2)
20-26: LGTM! 매핑 업데이트가 일관적입니다.
MyReportHistoryResponseDto의 매핑이 새로운 필드(interest)와 통합된 페이징 식별자(lastId)를 포함하도록 잘 업데이트되었습니다.
29-44: LGTM!MyViewedAnimalsResponseDto매핑도 잘 업데이트되었습니다.필드 이름 변경(
viewedAnimals->cards,cardId->reportId)과 페이징 식별자 통합(lastId)이 명확하고 일관성 있게 적용되었습니다.app/src/main/java/com/example/findu/presentation/ui/search/adapter/SearchContentRVAdapter.kt (2)
25-25: LGTM! DiffUtil 비교가 올바르게 업데이트되었습니다.
areItemsTheSame에서reportId를 사용하도록 변경되어 아이템 식별이 정확하게 이루어집니다.
84-84: LGTM! 북마크 클릭 핸들러도 잘 업데이트되었습니다.
item.reportId를 전달하도록 변경되어 북마크 기능이 올바른 식별자를 사용합니다.app/src/main/java/com/example/findu/presentation/ui/search/model/SearchRv.kt (1)
12-12: LGTM! UI 모델 업데이트가 명확합니다.
cardId에서reportId로의 변경이 프레젠테이션 레이어에 잘 반영되었습니다. 다른 필드들은 그대로 유지되어 변경이 간결합니다.app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchRescueFragment.kt (2)
88-88: LGTM! 도메인에서 UI 모델로의 매핑이 정확합니다.
SearchRv생성 시reportId를 올바르게 매핑하고 있습니다.
260-260: LGTM! 네비게이션 로직도 잘 업데이트되었습니다.상세 화면으로 이동할 때
item.reportId를 사용하도록 변경되어 올바른 식별자로 네비게이션이 이루어집니다.app/src/main/java/com/example/findu/data/mapper/todomain/SearchResponseDtoMapper.kt (1)
21-21: 매핑이 정상입니다
DTO에cardId필드가 존재하므로reportId = this.cardId매핑은 올바릅니다. 수정 불필요합니다.app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyProfileMapper.kt (1)
6-10: 매핑 로직 확인 완료DTO에서 도메인 모델로의 변환이 명확하고 정확합니다.
app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchAllFragment.kt (2)
82-90: 식별자 필드명 변경 확인
cardId에서reportId로의 필드명 변경이 잘 반영되었습니다. 도메인 모델과 일관성을 유지하고 있습니다.
266-269: 네비게이션 코드 업데이트 확인
item.reportId를 사용하도록 올바르게 수정되었습니다.app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyInterestMapper.kt (1)
6-20: 매핑 로직 확인 완료중첩된 리스트 구조를 포함한 DTO → 도메인 변환이 정확하게 구현되어 있습니다.
app/src/main/java/com/example/findu/presentation/ui/search/tablayout/SearchReportFragment.kt (1)
118-140: 식별자 업데이트 확인
SearchAllFragment와 동일한 패턴으로cardId에서reportId로 변경이 일관되게 적용되었습니다.Also applies to: 256-264
app/src/main/java/com/example/findu/domain/usecase/my/PatchProfileImageUseCase.kt (1)
6-14: 도메인 레이어 의존성 개선 확인이전 리뷰 피드백을 잘 반영하여 유스케이스가
String타입만 사용하고,MultipartBody/RequestBody변환은 데이터 레이어(리포지토리)에서 처리하도록 구현되었습니다. 클린 아키텍처 원칙을 잘 지키고 있습니다.app/src/main/java/com/example/findu/domain/repository/MyRepository.kt (1)
13-34: 메서드 시그니처 개선 확인페이징 로직을 단일
lastId로 통합하고, 프로필 이미지 관련 메서드에서String타입만 사용하도록 한 것은 좋은 설계입니다. 도메인 레이어가 프레임워크에 독립적으로 유지됩니다.app/src/main/java/com/example/findu/data/repositoryimpl/MyRepositoryImpl.kt (3)
64-70: 매핑 함수 사용 확인이전 리뷰 피드백대로
toDomain()매핑 함수를 사용하여 DTO를 도메인 모델로 변환하고 있습니다.
73-79: 멀티파트 처리 구현 확인
imagePath에서File및MultipartBody.Part생성을 데이터 레이어에서 처리하는 것이 적절합니다. 도메인 레이어는String경로만 알면 되므로 레이어 분리가 잘 되어 있습니다.
81-85: RequestBody 처리 구현 확인기본 프로필 이미지 설정을 위한
RequestBody생성 로직이 올바르게 구현되어 있습니다.app/src/main/java/com/example/findu/data/repositoryimpl/InterestRepositoryImpl.kt (2)
14-23: 매핑 함수 사용 확인이전 리뷰 피드백을 반영하여
toDomain()함수를 사용한 구조로 잘 개선되었습니다. DTO에서 도메인으로의 변환이 명확하고 일관성 있게 처리됩니다.
25-33: 관심 등록/삭제 메서드 확인API 통합으로 단순화된 메서드 구현이 깔끔합니다.
app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt (1)
47-57: Multipart 파트 이름 일치 확인
MyRepositoryImpl.kt에서 createFormData("profileImageFile", …)가 서비스 시그니처와 일치합니다.
| import com.example.findu.data.mapper.todomain.my.toDomain | ||
| import com.example.findu.data.dataremote.util.handleBaseResponse |
There was a problem hiding this comment.
중복 임포트 제거
handleBaseResponse가 중복으로 임포트되어 있습니다.
다음과 같이 수정해주세요:
import com.example.findu.data.dataremote.datasource.InterestRemoteDataSource
import com.example.findu.data.dataremote.util.handleBaseResponse
import com.example.findu.data.mapper.todomain.my.toDomain
-import com.example.findu.data.dataremote.util.handleBaseResponse
import com.example.findu.domain.model.my.MyInterestData📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import com.example.findu.data.mapper.todomain.my.toDomain | |
| import com.example.findu.data.dataremote.util.handleBaseResponse | |
| import com.example.findu.data.dataremote.datasource.InterestRemoteDataSource | |
| import com.example.findu.data.dataremote.util.handleBaseResponse | |
| import com.example.findu.data.mapper.todomain.my.toDomain | |
| import com.example.findu.domain.model.my.MyInterestData |
🤖 Prompt for AI Agents
In
app/src/main/java/com/example/findu/data/repositoryimpl/InterestRepositoryImpl.kt
around lines 5-6, remove the duplicate import of handleBaseResponse so it is
imported only once; clean up imports (organize or run the IDE's optimize
imports) to ensure no duplicate lines remain and build compiles cleanly.
| import android.net.Uri | ||
| import com.example.findu.data.dataremote.model.response.my.MyNickNameResponseDto | ||
| import com.example.findu.domain.model.my.MyInterestData | ||
| import com.example.findu.domain.model.my.MyProfileData | ||
| import com.example.findu.domain.model.my.MyReportHistoryData | ||
| import com.example.findu.domain.model.my.MyViewedAnimalData | ||
| import okhttp3.MultipartBody | ||
| import okhttp3.RequestBody |
There was a problem hiding this comment.
도메인 인터페이스의 불필요한 의존성 제거 필요
MyRepository 인터페이스에 데이터 레이어의 DTO(MyNickNameResponseDto)와 프레임워크 타입(MultipartBody, RequestBody, Uri)이 임포트되어 있는데, 실제 메서드 시그니처에서는 사용되지 않습니다. 이는 클린 아키텍처 원칙을 위반하며 불필요한 의존성을 만듭니다.
다음 임포트를 제거해주세요:
-import android.net.Uri
-import com.example.findu.data.dataremote.model.response.my.MyNickNameResponseDto
import com.example.findu.domain.model.my.MyInterestData
import com.example.findu.domain.model.my.MyProfileData
import com.example.findu.domain.model.my.MyReportHistoryData
import com.example.findu.domain.model.my.MyViewedAnimalData
-import okhttp3.MultipartBody
-import okhttp3.RequestBody🤖 Prompt for AI Agents
In app/src/main/java/com/example/findu/domain/repository/MyRepository.kt around
lines 3 to 10, the interface file currently imports data-layer DTO and framework
types that are not used in the domain interface; remove the unused imports:
com.example.findu.data.dataremote.model.response.my.MyNickNameResponseDto,
okhttp3.MultipartBody, okhttp3.RequestBody, and android.net.Uri so the domain
interface only depends on domain model types (MyInterestData, MyProfileData,
MyReportHistoryData, MyViewedAnimalData); after removing imports, run a quick
build/IDE sync to ensure no compile errors remain.
| fun updateProfileImageFromGallery(uri: Uri) { | ||
| _selectedProfileImageUri.value = uri | ||
| viewModelScope.launch { | ||
| patchProfileImageFileUseCase.uploadFile(uri.path!!).fold( | ||
| onSuccess = { | ||
| fetchMyProfile() }, | ||
| onFailure = { _errorMessage.value = it.message ?: "프로필 이미지 변경 중 오류 발생" } | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
갤러리 URI에 대한 path!! 강제 언래핑은 즉시 수정이 필요합니다.
Uri가 content:// 스킴으로 넘어오면 uri.path가 null인 경우가 많아서 !!에서 바로 NPE가 발생합니다. 또한 content:// 경로 문자열을 그대로 서버 업로드에 쓰면 안드로이드 10+의 스코프드 스토리지 정책상 실파일 경로를 얻지 못해 업로드가 실패합니다. ContentResolver로 스트림을 열거나 DocumentFile 등 안정적인 변환을 거친 뒤 RequestBody를 생성하도록 바꿔 주세요.
🤖 Prompt for AI Agents
In app/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt around
lines 139 to 147, remove the unsafe uri.path!! usage and change the upload flow
to read the gallery Uri via a ContentResolver or DocumentFile instead of relying
on a filesystem path; obtain an InputStream from
context.contentResolver.openInputStream(uri) (or convert the DocumentFile to a
temporary file), build a RequestBody/MultipartBody from that stream (or temp
file), and pass that to patchProfileImageFileUseCase.uploadFile; ensure the
ViewModel gets a ContentResolver (or Context) injected/available, handle null
InputStream and IO errors gracefully, and avoid any forced unwraps so no NPE
occurs with content:// URIs.
Related issue 🛠
Work Description 📝
Screenshot 📸
Uncompleted Tasks 😅
To Reviewers 📢
401 실패 뜨면서 토큰 만료됐다고 실행 결과 확인을 하지 못하였습니다.
Summary by CodeRabbit
신기능
개선