Skip to content

Comments

[Feat] my api#92

Merged
nasohee merged 12 commits intodevelopfrom
feat-my-api
Oct 13, 2025
Merged

[Feat] my api#92
nasohee merged 12 commits intodevelopfrom
feat-my-api

Conversation

@nasohee
Copy link
Collaborator

@nasohee nasohee commented Sep 22, 2025

Related issue 🛠

Work Description 📝

  • 작업 내용
스크린샷 2025-09-22 12 56 34

Screenshot 📸

Uncompleted Tasks 😅

  • 프로필 이미지 수정 api부분의 viewmodel 코드를 작성하지 못했습니다.

To Reviewers 📢

401 실패 뜨면서 토큰 만료됐다고 실행 결과 확인을 하지 못하였습니다.

Summary by CodeRabbit

  • 신기능

    • 프로필 이미지 변경 지원: 갤러리 업로드 및 기본 이미지(PUPPY/CHICK/PANDA/DEFAULT) 선택.
    • 관심 등록/해제 기능을 검색·상세·마이 화면에서 통합 제공.
  • 개선

    • 관심/열람/신고 목록 페이징 파라미터를 lastId로 통일해 로딩 일관성 향상.
    • 카드 식별자를 reportId로 통일하여 상세 이동·북마크 동작 안정화.
    • 마이 화면에서 닉네임과 프로필 이미지를 함께 표시하고 이미지 로딩 방식 개선.
    • 관심 목록에 주소(address) 노출로 정보 가독성 향상.

@coderabbitai
Copy link

coderabbitai bot commented Sep 22, 2025

Walkthrough

마이페이지/관심 기능 전반의 API를 v2로 통합하고 lastId 기반 페이징으로 일원화. 관심 등록/해제 엔드포인트를 단일 동작으로 교체. 프로필(닉네임+이미지) 응답·도메인·프레젠테이션 모델 및 맵퍼 업데이트. 리포지토리/유스케이스/DI/뷰모델/프래그먼트/어댑터 시그니처 및 호출부 일괄 수정.

Changes

Cohort / File(s) Summary
Remote interfaces
app/src/main/java/com/example/findu/data/dataremote/datasource/InterestRemoteDataSource.kt, app/src/main/java/com/example/findu/data/dataremote/datasource/MyRemoteDataSource.kt
관심 조회/등록/삭제 메서드 집합으로 재정렬(단일 lastId), 프로필 이미지 패치 메서드 2종 추가, 패치 닉네임 DTO 적용.
Remote impl
app/src/main/java/com/example/findu/data/dataremote/datasourceimpl/InterestRemoteDataSourceImpl.kt, app/src/main/java/com/example/findu/data/dataremote/datasourceimpl/MyRemoteDataSourceImpl.kt
서비스 호출 위임을 신규 시그니처에 맞게 변경(등록에 MyInterestRequestDto 사용, 삭제/조회 통합), 멀티파트/RequestBody 도입.
Retrofit services
app/src/main/java/com/example/findu/data/dataremote/service/InterestService.kt, app/src/main/java/com/example/findu/data/dataremote/service/MyService.kt
엔드포인트를 /api/v2/users/me로 이동, lastId 쿼리 통일, 관심 등록 POST(Body)/삭제 DELETE(Query) 추가, 프로필 이미지 PATCH 멀티파트 2종 추가.
DTOs / Remote models
app/src/main/java/com/example/findu/data/dataremote/model/request/MyInterestRequestDto.kt, app/src/main/java/com/example/findu/data/dataremote/model/request/PatchNicknameRequestDto.kt, app/src/main/java/com/example/findu/data/dataremote/model/response/my/...
관심/조회/이력 응답 구조를 reportId 중심·lastId 단일화. 프로필 응답에 profileImage 추가. 요청 DTO 추가.
Domain models
app/src/main/java/com/example/findu/domain/model/my/MyInterestData.kt, app/src/main/java/com/example/findu/domain/model/my/MyViewedAnimalData.kt, app/src/main/java/com/example/findu/domain/model/my/MyReportHistoryData.kt, app/src/main/java/com/example/findu/domain/model/my/MyProfileData.kt
lastId 통합, 내부 엔티티를 reportId·address 등으로 재구성, MyProfileData 신규 추가.
Mappers (dto→domain)
app/src/main/java/com/example/findu/data/mapper/todomain/my/MyInterestMapper.kt, app/src/main/java/com/example/findu/data/mapper/todomain/my/MyProfileMapper.kt, app/src/main/java/com/example/findu/data/mapper/toDomain/my/MyResponseDtoMapper.kt, app/src/main/java/com/example/findu/data/mapper/todomain/SearchResponseDtoMapper.kt
MyInterestResponseDto.toDomain 신규 추가, 기존 매퍼들 lastId·cards/reportId 등 새로운 스키마로 갱신, 닉네임→MyProfileData 매핑 추가.
Repositories / impl
app/src/main/java/com/example/findu/data/repositoryimpl/InterestRepositoryImpl.kt, app/src/main/java/com/example/findu/data/repositoryimpl/MyRepositoryImpl.kt, app/src/main/java/com/example/findu/domain/repository/InterestRepository.kt, app/src/main/java/com/example/findu/domain/repository/MyRepository.kt
관심 리포지토리 API 통합(get/register/delete, 반환 타입 변경), My 리포지토리 lastId 통일·프로필 반환 및 프로필 이미지 패치 로직 추가.
UseCases — Interest / My
app/src/main/java/com/example/findu/domain/usecase/interest/*.kt, app/src/main/java/com/example/findu/domain/usecase/my/*.kt
보호/신고 분리 유즈케이스를 단일 PostInterestAnimal/DeleteInterestAnimal로 통합(구 클래스 삭제), My 측에 PatchProfileImageUseCase 추가, lastId 파라미터 통합.
DI
app/src/main/java/com/example/findu/di/UseCaseModule.kt
관심 관련 유즈케이스 바인딩 교체(신규 Post/DeleteInterestAnimal) 및 제거된 제공자 삭제.
Presentation — ViewModels / Fragments
app/src/main/java/com/example/findu/presentation/ui/my/MyViewModel.kt, .../MyFragment.kt, .../MyInterestFragment.kt, .../MyViewedAnimalFragment.kt, .../search/viewmodel/*.kt, .../search/DetailReportViewModel.kt
관심 호출부를 통합 유즈케이스로 교체. MyViewModel에 myProfile 상태·프로필 이미지 업로드 흐름 추가, fetchNickName→fetchMyProfile 변경, setInterest 시 tag 제거 반영. 각 뷰모델 생성자/호출부 시그니처 갱신.
Presentation — Adapters / Rv models / mappers
app/src/main/java/com/example/findu/presentation/ui/my/adapter/MyInterestRvAdapter.kt, .../MyViewedAnimalsRvAdapter.kt, .../presentation/model/MyRv.kt, .../presentation/mapper/torvmodel/MyInterestDataMapper.kt, .../search/*
식별자/필드명 교체(animalId/cardId→reportId, location→address), 매핑 함수 시그니처 및 호출부 업데이트, RecyclerView 모델 필드명 변경.

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 상태 갱신
Loading
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: 닉네임/이미지 갱신
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Possibly related PRs

  • [Feat] search UI #80 — Search/interest flow을 unified usecase/엔드포인트로 전환한 변경과 코드 레벨 연관성 있음 (ViewModel 및 usecase 교체 관련).

Suggested reviewers

  • ikseong00
  • t1nm1ksun

Poem

lastId로 길을 묶고, reportId로 별을 세네 🌟
닉네임과 그림자도 함께 날아와—프로필 갱신 🎨
관심 누르면 POST, 다시 누르면 DELETE, 한길로 가네 🛤️
v2로 옮겨 탄 마이페이지, 조용히 빛나네 ✨

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning 이 PR에는 My Page API 연동 외에도 검색 화면 관련 뷰모델, 어댑터, 매퍼 등 검색 기능 전반에 걸친 변경사항이 포함되어 있어 이슈 #84 범위를 벗어납니다. 이러한 변경은 별도의 PR로 분리하는 것이 좋습니다. 검색 기능 관련 변경을 My Page API 연동 PR과 분리하거나 이슈와 관련된 변경만 남기고 나머지 코드를 분리해 주세요.
Description Check ⚠️ Warning PR 설명은 템플릿의 주요 섹션을 포함하고 있으나 Work Description에 구체적인 작업 내용이 없어 변경점을 파악하기 어렵고 Screenshot 섹션이 빈 상태로 남아 있습니다. 스크린샷이 Work Description 영역에 잘못 배치되어 있어 템플릿 구조를 제대로 따르지 않고 있습니다. Work Description 섹션에 실제 변경 사항을 상세히 기술하고 스크린샷은 Screenshot 섹션에 올바른 이미지로 업데이트해 주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive PR 제목 “[Feat] my api”는 주요 변경 사항을 구체적으로 담고 있지 않아 어떤 API가 추가·수정됐는지 파악하기 어렵습니다. 명확하고 간결한 요약 문장이 아니어서 변경 이력을 빠르게 이해하기 힘듭니다. 제목을 “마이페이지 API 연동 기능 추가”처럼 어떤 기능이 변경·추가됐는지 구체적으로 표현하도록 수정해 주세요.
✅ Passed checks (1 passed)
Check name Status Explanation
Linked Issues Check ✅ Passed 이 PR은 My Page 화면의 데이터 로드를 위한 API 호출, DTO 매핑, 뷰모델 및 프래그먼트 업데이트 등 이슈 #84의 요구사항을 모두 구현하고 있습니다. 따라서 이슈에서 명시한 마이페이지 API 연동 작업을 충족한다고 판단됩니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-my-api

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.

❤️ Share

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

@nasohee nasohee changed the title Feat my api [Feat] my api Sep 22, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 제거 필요

SerialNameSerializable 어노테이션이 실제로 사용되지 않고 있습니다.

-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.MyNickNameResponseDto
app/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.toDomain
app/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

📥 Commits

Reviewing files that changed from the base of the PR and between f6d1f0c and 0ee67c4.

📒 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: 페이징 필드 통합 완료

기존의 lastInterestProtectIdlastInterestReportId 두 개의 페이징 필드가 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/**' -C1
app/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: 페이징 파라미터 통합이 잘 되었습니다!

이전에 분리되어 있던 lastReportIdlastProtectId를 단일 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 의존성 교체 LGTM

Post/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 (마이 탭 페이징 정합성 확인 부탁)

단일 lastIdCard(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 파라미터로 정리: OK

Service 위임이 간결하고 변경 의도에 부합합니다.

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: 기본 프로필 이미지 변경 흐름 OK

text/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면 현 구조 유지.

Comment on lines +46 to +56
@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>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 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 -C2

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

@t1nm1ksun t1nm1ksun assigned t1nm1ksun and nasohee and unassigned t1nm1ksun Sep 22, 2025
Copy link
Collaborator

@ikseong00 ikseong00 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다 ! 코드리뷰 확인 부탁드려요~

Comment on lines 11 to 15
suspend fun uploadFile(profileImageFile: MultipartBody.Part) =
myRepository.patchProfileImageFile(profileImageFile)

suspend fun uploadDefault(defaultProfileImageName: RequestBody) =
myRepository.patchProfileImageDefault(defaultProfileImageName)
Copy link
Collaborator

Choose a reason for hiding this comment

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

MultipartBody 와 RequestBody 는 Domain 레이어에서 알지 않아도 되는 부분 같습니다!
viewModel 에서 멀티파트, RequestBody 로 매핑하는 것 같은데 해당 로직을 Data 로 옮겨주세요!
https://github.com/FindYou-Kuit/FindYou-Android/pull/88/files 여기 PR에 보시면 좋을 것 같습니다

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0ee67c4 and b92542c.

📒 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에서 FileMultipartBody.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", …)가 서비스 시그니처와 일치합니다.

Comment on lines +5 to +6
import com.example.findu.data.mapper.todomain.my.toDomain
import com.example.findu.data.dataremote.util.handleBaseResponse
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

중복 임포트 제거

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.

Suggested change
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.

Comment on lines +3 to +10
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
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

도메인 인터페이스의 불필요한 의존성 제거 필요

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.

Comment on lines 139 to 147
fun updateProfileImageFromGallery(uri: Uri) {
_selectedProfileImageUri.value = uri
viewModelScope.launch {
patchProfileImageFileUseCase.uploadFile(uri.path!!).fold(
onSuccess = {
fetchMyProfile() },
onFailure = { _errorMessage.value = it.message ?: "프로필 이미지 변경 중 오류 발생" }
)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

갤러리 URI에 대한 path!! 강제 언래핑은 즉시 수정이 필요합니다.

Uricontent:// 스킴으로 넘어오면 uri.pathnull인 경우가 많아서 !!에서 바로 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.

@nasohee nasohee merged commit e96b457 into develop Oct 13, 2025
1 check passed
@coderabbitai coderabbitai bot mentioned this pull request Oct 17, 2025
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 마이페이지 api 연동

3 participants