Skip to content

[Feature] 마이페이지 api 연결#211

Open
ckals413 wants to merge 15 commits into
developfrom
FLT-17-마이페이지-api-연결

Hidden character warning

The head ref may contain hidden characters: "FLT-17-\ub9c8\uc774\ud398\uc774\uc9c0-api-\uc5f0\uacb0"
Open

[Feature] 마이페이지 api 연결#211
ckals413 wants to merge 15 commits into
developfrom
FLT-17-마이페이지-api-연결

Conversation

@ckals413

@ckals413 ckals413 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

📮 관련 이슈

  • closed #이슈번호

📌 작업 내용

작업 요약

  • 마이페이지 API를 최신 스펙에 맞게 연동했습니다.

    • 내 프로필 조회 API(/users/me) 적용
    • 타 유저 프로필 및 저장 콘텐츠 조회 경로 수정
    • 프로필의 작품 수, 북마크 수, 키워드 재계산 가능 여부 데이터 반영
  • 저장한 작품 목록 기능을 개선했습니다.

    • 내 북마크 콘텐츠 조회를 커서 페이지네이션 방식으로 변경
    • 저장 작품 더보기 화면 및 뒤로가기 흐름 추가
    • 북마크 해제 시 목록에서 즉시 제거되도록 반영
    • 타 유저 저장 목록에서는 북마크 여부만 토글되도록 분기 처리
  • 북마크 상태 동기화를 추가했습니다.

    • 콘텐츠/컬렉션 북마크 변경 이벤트를 공유 Flow로 관리
    • 프로필 화면의 저장 콘텐츠/컬렉션 목록에 변경 상태 실시간 반영
  • 취향 키워드 재계산 기능을 구현했습니다.

    • 재계산 API 연동
    • 재계산 가능 여부에 따른 버튼 활성화 제어
    • 재계산 중 로딩 애니메이션 추가
    • 재계산 성공 후 키워드 재조회
  • 북마크 최소 개수 제한 처리를 서버 에러 응답 기반으로 변경했습니다.

    • BOOKMARK.CONTENT_MIN_LIMIT 에러 수신 시 제한 안내 모달 노출
    • 비즈니스 에러는 글로벌 네트워크 에러 emit 대상에서 제외

😅 미구현

  • 컬랙션 생성해보고 조회되는지 확인해보기

🫛 To. 리뷰어

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 다른 사용자의 저장한 콘텐츠 목록 조회 기능 추가
    • 프로필 키워드 재계산 기능 추가
    • 저장한 콘텐츠 목록 페이지네이션 개선
  • 버그 수정

    • 네트워크 오류 응답 처리 개선
    • 북마크 상태 관리 최적화

@ckals413 ckals413 self-assigned this Jun 11, 2026
@ckals413 ckals413 added the Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경 label Jun 11, 2026
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ckals413, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 46 minutes and 48 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d84b2cf6-a963-4e81-a387-633a47bb1e74

📥 Commits

Reviewing files that changed from the base of the PR and between 6d0d6e1 and 2cef44e.

📒 Files selected for processing (7)
  • app/src/main/java/com/flint/core/navigation/Route.kt
  • app/src/main/java/com/flint/data/api/UserApi.kt
  • app/src/main/java/com/flint/domain/repository/UserRepository.kt
  • app/src/main/java/com/flint/presentation/main/MainNavHost.kt
  • app/src/main/java/com/flint/presentation/main/MainNavigator.kt
  • app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt
  • app/src/main/java/com/flint/presentation/profile/navigation/ProfileNavigation.kt
📝 Walkthrough

워크스루

PR은 북마크 콘텐츠 목록 조회에 커서 기반 페이지네이션을 도입하고, Route.SavedContentList를 사용자별 매개변수를 받는 클래스로 변경합니다. BookmarkRepository는 변경 이벤트를 스트림으로 브로드캐스트하며, 사용자 프로필에서 키워드 재계산 기능을 추가합니다. 네비게이션 체인 전체에서 userId를 전달하여 사용자별 저장 콘텐츠 조회를 지원합니다.

변경 사항

북마크 목록 및 키워드 재계산 기능

레이어 / 파일(들) 요약
API 계약 & 응답 DTO
app/src/main/java/com/flint/data/api/ContentApi.kt, app/src/main/java/com/flint/data/api/UserApi.kt, app/src/main/java/com/flint/data/dto/content/response/BookmarkedContentListResponseDto.kt, app/src/main/java/com/flint/data/dto/user/response/UserProfileResponseDto.kt
ContentApi getBookmarkedContentList은 커서/사이즈 쿼리 파라미터를 받고 MyBookmarkedContentListResponseDto를 반환하도록 변경됩니다. UserApi는 PATCH /users/me/keywords/recalculate 엔드포인트와 사용자별 북마크 조회 경로 수정을 추가합니다. 응답 DTO에 totalCount, BookmarkCursorMetaDto, bookmarkCount, isBookmarked 필드가 추가되며, 프로필 DTO에 keywordRecalculatable 플래그가 포함됩니다.
도메인 모델 & 매퍼
app/src/main/java/com/flint/domain/model/bookmark/BookmarkChange.kt, app/src/main/java/com/flint/domain/model/content/BookmarkedContentListModel.kt, app/src/main/java/com/flint/domain/model/user/UserProfileResponseModel.kt, app/src/main/java/com/flint/domain/mapper/content/ContentMapper.kt, app/src/main/java/com/flint/domain/mapper/user/ProfileMapper.kt
BookmarkChange sealed 클래스가 북마크 상태 변경을 나타냅니다. BookmarkedContentListModelBookmarkedContentItemModeltotalCount, bookmarkCount, isBookmarked 필드가 추가되고, UserProfileResponseModelprofileImageUrlkeywordRecalculatable이 추가됩니다. 매퍼에서 새 필드를 API 응답에서 도메인 모델로 변환합니다.
리포지토리 패턴 & 스트리밍
app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt, app/src/main/java/com/flint/domain/repository/UserRepository.kt, app/src/main/java/com/flint/data/di/interceptor/NetworkErrorInterceptor.kt
BookmarkRepository는 북마크 변경을 SharedFlow<BookmarkChange>로 브로드캐스트합니다. UserRepositoryuserId == null일 때 커서 페이지네이션으로 내 북마크를, userId != null일 때 사용자별 북마크를 조회하고, recalculateKeywords() 메서드를 추가합니다. NetworkErrorInterceptor는 응답 바디의 errorCode 존재 여부를 확인하여 앱 레이어 비즈니스 에러를 전역 에러 발행 대상에서 제외합니다.
네비게이션 & 라우팅
app/src/main/java/com/flint/core/navigation/Route.kt, app/src/main/java/com/flint/presentation/main/MainNavigator.kt, app/src/main/java/com/flint/presentation/main/MainNavHost.kt, app/src/main/java/com/flint/presentation/savedcontent/navigation/SavedContentNavigation.kt
Route.SavedContentList가 싱글톤 객체에서 userId: String? = null를 받는 데이터 클래스로 변환됩니다. MainNavigator.navigateToSavedContent와 네비게이션 유틸리티에서 userId를 받아 라우팅 대상에 전달하고, savedContentListNavGraphnavigateUp 콜백이 추가됩니다.
프로필 키워드 재계산
app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt, app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt, app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordSection.kt, app/src/main/java/com/flint/presentation/profile/uistate/ProfileUiState.kt
ProfileViewModel에서 북마크 변경 스트림을 관찰하여 저장 목록을 갱신하고, recalculateKeywords() 메서드로 키워드 재계산을 트리거합니다. ProfileKeywordSectionisRecalculatableisRecalculating 파라미터가 추가되고, ProfileRefreshButton에 무한 회전 애니메이션이 적용됩니다. ProfileUiStateisRecalculating 플래그가 추가되고, ProfileScreen에서 onRefreshClickrecalculateKeywords에 연결합니다.
저장 콘텐츠 화면 리팩터링 & 북마크 관리
app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt, app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt, app/src/main/java/com/flint/presentation/savedcontent/SavedContentListScreen.kt, app/src/main/java/com/flint/presentation/profile/navigation/ProfileNavigation.kt, app/src/main/java/com/flint/presentation/profile/uistate/SavedContentUiState.kt
SavedContentViewModeluserId 파라미터로 사용자별 북마크를 조회하고, 북마크 토글 시 내 프로필(제거)과 타 사용자 프로필(상태만 토글)을 구분하여 처리합니다. SavedContentScreen은 각 항목에 애니메이션을 적용하고 per-item 북마크 상태를 표시합니다. SavedContentListRoutenavigateUp 콜백을 받으며, SavedContentUiStatetotalCount가 응답 모델에서 읽힙니다.

예상 코드 리뷰 난이도

🎯 4 (Complex) | ⏱️ ~60분

관련 PR들

  • imflint/Flint-Android#204: 이번 PR의 SavedContentViewModelSavedContentScreen 수정이 PR #204에서 도입된 저장 콘텐츠 목록 화면 위에 구축됩니다.
  • imflint/Flint-Android#203: 양쪽 PR이 ProfileScreen.ktProfileKeywordSection.kt의 프로필 키워드 UI 흐름을 수정합니다(이번 PR은 재계산 상태/새로고침 연결 추가, PR #203은 정보/설정 모달 파라미터 추가).
  • imflint/Flint-Android#153: 양쪽 PR이 BookmarkRepository.toggleContentBookmark(...) 주변 북마크 토글 흐름을 수정합니다(이번 PR은 BookmarkChange.Content SharedFlow 발행 확장, PR #153은 컬렉션 상세에서 콘텐츠 토글 메서드 추가/사용).

제안 라벨

🔖 API

제안 리뷰어

  • chanmi1125
  • kimjw2003

시 🐰

북마크의 흐름이 춤을 추고 🎵
커서 따라 목록이 흘러간다
프로필 키워드 회전하며 새로워지니
사용자별 저장품, 이제는 함께
공유되는 변화로 가득한 여름 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 PR의 주요 변경사항인 마이페이지 API 연동을 명확하게 설명하고 있습니다.
Description check ✅ Passed 설명이 대부분 완성되었으나 관련 이슈 번호가 미기재되어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch FLT-17-마이페이지-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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

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/flint/data/dto/user/response/UserProfileResponseDto.kt (1)

1-18: ⚠️ Potential issue | 🟡 Minor

UserProfileResponseDto 직렬화 애노테이션 불일치 수정 필요

  • NetworkModule에서 Retrofit addConverterFactorykotlinx.serialization(asConverterFactory)만 사용 중입니다.
  • 그런데 UserProfileResponseDto@Serializable(kotlinx)와 @SerializedName(Gson)을 함께 쓰고 있어, Gson @SerializedName은 kotlinx 직렬화에서는 적용되지 않습니다.
  • BookmarkedContentListResponseDto를 포함한 대부분의 DTO가 kotlinx.serialization.SerialName을 쓰므로, UserProfileResponseDto@SerializedName@SerialName으로 바꾸거나 Gson 애노테이션/임포트를 제거해 일관성을 맞추세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/flint/data/dto/user/response/UserProfileResponseDto.kt`
around lines 1 - 18, UserProfileResponseDto mixes Gson (`@SerializedName`) with
kotlinx.serialization (`@Serializable`) while NetworkModule uses Retrofit with
kotlinx.asConverterFactory; replace all `@SerializedName` usages in
UserProfileResponseDto with kotlinx.serialization.SerialName (or remove Gson
imports) so field names are honored by kotlinx serialization, e.g., update the
annotations on id, profileImageUrl, isFliner, nickname, keywordRecalculatable to
`@SerialName` and remove com.google.gson imports to match the rest of DTOs and
NetworkModule’s asConverterFactory usage.
🧹 Nitpick comments (3)
app/src/main/java/com/flint/presentation/savedcontent/SavedContentListScreen.kt (1)

5-16: 💤 Low value

savedcontent 패키지에서 profile 패키지로의 크로스 패키지 의존성

SavedContentListRouteprofile.SavedContentRoute로 단순 위임하면서 savedcontent → profile 방향의 프레젠테이션 레이어 간 의존성이 생성되었습니다. 이는 SavedContentRoute가 profile 패키지로 통합된 의도적인 리팩토링으로 보이지만, 패키지 간 결합도가 증가하는 점을 유의하시기 바랍니다.

만약 향후 savedcontent와 profile 기능을 독립적으로 유지하려는 경우, SavedContentRoute의 위치를 재고하거나 공통 모듈로 추출하는 것을 검토해보세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/com/flint/presentation/savedcontent/SavedContentListScreen.kt`
around lines 5 - 16, SavedContentListRoute currently delegates to
profile.SavedContentRoute, introducing a cross-package dependency from
savedcontent to profile; either move SavedContentRoute into the savedcontent
package, extract it into a shared/common module, or create a local
SavedContentRoute implementation/adapter in savedcontent that wraps the profile
implementation to avoid direct package coupling—update references to use the
chosen location and ensure the function signature (SavedContentListRoute,
SavedContentRoute, paddingValues, navigateUp) remains consistent.
app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt (1)

85-141: ⚖️ Poor tradeoff

수동 JSON 파싱의 취약성

북마크 토글 실패 시 에러 응답을 수동으로 JSON 파싱하여 BOOKMARK.CONTENT_MIN_LIMIT 에러 코드를 확인하고 있습니다(124-132줄). 이 접근 방식은 다음과 같은 문제가 있습니다:

  1. 에러 응답 구조 변경 시 파싱 실패 가능
  2. JSON 파싱 예외 처리가 runCatching으로만 되어 있어 실패 원인 추적 어려움
  3. 타입 안정성 부재

다만 PR 목표에 따르면 해당 비즈니스 에러를 글로벌 네트워크 에러 처리에서 제외하기 위한 의도적인 설계로 보입니다.

장기적으로는 에러 응답을 별도의 DTO로 정의하고 Converter를 통해 파싱하는 것을 권장합니다:

data class ErrorResponse(
    val errorCode: String,
    val message: String?
)

// Retrofit Converter 또는 별도 파싱 유틸리티 활용

현재는 PR 목표에 부합하므로 승인하되, 향후 리팩토링 시 고려해주세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt`
around lines 85 - 141, The toggleBookmark failure handling in
SavedContentViewModel currently does fragile manual JSON parsing to detect
BOOKMARK.CONTENT_MIN_LIMIT; replace this with a typed ErrorResponse DTO and use
Retrofit's converter (or a shared parsing utility) to convert
throwable.response().errorBody() into ErrorResponse, then check
errorResponse.errorCode == "BOOKMARK.CONTENT_MIN_LIMIT"; update the onFailure
block (where isMinLimitError is computed) to use the converter and proper
try/catch that logs parsing errors, and then call
_uiState.update/showBookmarkRestrictionModal or Timber.e accordingly.
app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt (1)

209-273: ⚡ Quick win

프리뷰 데이터에 북마크 상태 추가를 권장합니다.

SavedContentPreviewData.FakeListBookmarkedContentItemModel 인스턴스들이 isBookmarkedbookmarkCount 속성을 설정하지 않고 있습니다. 이제 실제 화면에서 이 속성들을 사용하므로(lines 196-197), 프리뷰에서도 현실적인 북마크 상태를 보여주도록 데이터를 업데이트하는 것이 좋습니다.

♻️ 프리뷰 데이터 개선 제안
 BookmarkedContentItemModel(
     id = "0",
     title = "은하수를 여행하는 히치하이커를 위한 안내서",
     year = 2005,
     imageUrl = "",
     getOttSimpleList = listOf(
         OttType.Netflix,
         OttType.Disney,
         OttType.Tving,
     ),
+    isBookmarked = true,
+    bookmarkCount = 42,
 ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt`
around lines 209 - 273, SavedContentPreviewData.FakeList contains
BookmarkedContentItemModel entries missing isBookmarked and bookmarkCount;
update each BookmarkedContentItemModel in SavedContentScreen.kt (FakeList) to
set realistic isBookmarked (true/false) and bookmarkCount integers so previews
reflect actual UI usage (see usages at lines referencing isBookmarked and
bookmarkCount). Locate the FakeList declaration and add the isBookmarked and
bookmarkCount properties to each BookmarkedContentItemModel constructor (vary
values across items to show different states: bookmarked vs not and different
counts).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/flint/data/di/interceptor/NetworkErrorInterceptor.kt`:
- Around line 29-31: The current detection uses
response.peekBody(Long.MAX_VALUE).string().contains("\"errorCode\"") which risks
OOM and false positives; change NetworkErrorInterceptor to call
response.peekBody with a bounded buffer (e.g., 8KB–64KB) instead of
Long.MAX_VALUE and replace the naive contains check with a proper JSON field
existence test (use your project's JSON parser or JSONObject to parse the peeked
body and check for the "errorCode" key) when computing isBusinessError so large
bodies are not fully buffered and only a parsed JSON check determines presence
of the errorCode field.

In `@app/src/main/java/com/flint/domain/mapper/content/ContentMapper.kt`:
- Around line 23-28: The current MyBookmarkedContentListResponseDto.toModel()
sets totalCount = data.size which only reflects items in the current cursor
page; change it to use the DTO's meta.returned value when available (e.g.,
totalCount = meta?.returned ?: data.size) so the model's totalCount reflects the
DTO's intended count metric, and leave UserRepository's aggregation logic (which
builds BookmarkedContentListModel(totalCount = allContents.size)) to override as
needed; update the MyBookmarkedContentListResponseDto.toModel() implementation
accordingly.

In `@app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt`:
- Around line 26-40: The current use of Result.onSuccess in
toggleCollectionBookmark and toggleContentBookmark attempts to call the suspend
function _bookmarkChanges.emit(...) from a non-suspending lambda
(Result.onSuccess), causing a compile error; fix by replacing the onSuccess
usage with a suspend-friendly check such as val isBookmarked =
result.getOrNull(); if (isBookmarked != null) {
_bookmarkChanges.emit(BookmarkChange.Collection(collectionId, isBookmarked)) }
(and similarly for BookmarkChange.Content in toggleContentBookmark) so the emit
call runs directly in the surrounding suspend function.

In `@app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt`:
- Around line 64-70: 현재 코드에서 savedContents.contents를 필터한 뒤 totalCount를
filtered.size로 덮어써 전체 개수 계약이 깨집니다; 대신 북마크 해제 로직(예: where change.id is
removed)에서는 contents를 filtered로 갱신하되 savedContents.totalCount는 기존 totalCount에서
1만 감소시키는 방식으로 유지하세요 (즉, data.savedContents.copy(contents = filtered, totalCount
= maxOf(data.savedContents.totalCount - 1, 0))처럼 기존 totalCount를 직접 조작),
savedContents와 화면 리스트 길이를 분리해서 다루도록 ProfileViewModel의 해당 분기(데이터 업데이트:
data.savedContents.contents, totalCount)를 수정하십시오.

---

Outside diff comments:
In
`@app/src/main/java/com/flint/data/dto/user/response/UserProfileResponseDto.kt`:
- Around line 1-18: UserProfileResponseDto mixes Gson (`@SerializedName`) with
kotlinx.serialization (`@Serializable`) while NetworkModule uses Retrofit with
kotlinx.asConverterFactory; replace all `@SerializedName` usages in
UserProfileResponseDto with kotlinx.serialization.SerialName (or remove Gson
imports) so field names are honored by kotlinx serialization, e.g., update the
annotations on id, profileImageUrl, isFliner, nickname, keywordRecalculatable to
`@SerialName` and remove com.google.gson imports to match the rest of DTOs and
NetworkModule’s asConverterFactory usage.

---

Nitpick comments:
In `@app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt`:
- Around line 209-273: SavedContentPreviewData.FakeList contains
BookmarkedContentItemModel entries missing isBookmarked and bookmarkCount;
update each BookmarkedContentItemModel in SavedContentScreen.kt (FakeList) to
set realistic isBookmarked (true/false) and bookmarkCount integers so previews
reflect actual UI usage (see usages at lines referencing isBookmarked and
bookmarkCount). Locate the FakeList declaration and add the isBookmarked and
bookmarkCount properties to each BookmarkedContentItemModel constructor (vary
values across items to show different states: bookmarked vs not and different
counts).

In `@app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt`:
- Around line 85-141: The toggleBookmark failure handling in
SavedContentViewModel currently does fragile manual JSON parsing to detect
BOOKMARK.CONTENT_MIN_LIMIT; replace this with a typed ErrorResponse DTO and use
Retrofit's converter (or a shared parsing utility) to convert
throwable.response().errorBody() into ErrorResponse, then check
errorResponse.errorCode == "BOOKMARK.CONTENT_MIN_LIMIT"; update the onFailure
block (where isMinLimitError is computed) to use the converter and proper
try/catch that logs parsing errors, and then call
_uiState.update/showBookmarkRestrictionModal or Timber.e accordingly.

In
`@app/src/main/java/com/flint/presentation/savedcontent/SavedContentListScreen.kt`:
- Around line 5-16: SavedContentListRoute currently delegates to
profile.SavedContentRoute, introducing a cross-package dependency from
savedcontent to profile; either move SavedContentRoute into the savedcontent
package, extract it into a shared/common module, or create a local
SavedContentRoute implementation/adapter in savedcontent that wraps the profile
implementation to avoid direct package coupling—update references to use the
chosen location and ensure the function signature (SavedContentListRoute,
SavedContentRoute, paddingValues, navigateUp) remains consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9c09fff1-2fe7-4eed-b808-61be63be29f2

📥 Commits

Reviewing files that changed from the base of the PR and between 8d221be and 6d0d6e1.

📒 Files selected for processing (25)
  • app/src/main/java/com/flint/core/navigation/Route.kt
  • app/src/main/java/com/flint/data/api/ContentApi.kt
  • app/src/main/java/com/flint/data/api/UserApi.kt
  • app/src/main/java/com/flint/data/di/interceptor/NetworkErrorInterceptor.kt
  • app/src/main/java/com/flint/data/dto/content/response/BookmarkedContentListResponseDto.kt
  • app/src/main/java/com/flint/data/dto/user/response/UserProfileResponseDto.kt
  • app/src/main/java/com/flint/domain/mapper/content/ContentMapper.kt
  • app/src/main/java/com/flint/domain/mapper/user/ProfileMapper.kt
  • app/src/main/java/com/flint/domain/model/bookmark/BookmarkChange.kt
  • app/src/main/java/com/flint/domain/model/content/BookmarkedContentListModel.kt
  • app/src/main/java/com/flint/domain/model/user/UserProfileResponseModel.kt
  • app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt
  • app/src/main/java/com/flint/domain/repository/UserRepository.kt
  • app/src/main/java/com/flint/presentation/main/MainNavHost.kt
  • app/src/main/java/com/flint/presentation/main/MainNavigator.kt
  • app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt
  • app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt
  • app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt
  • app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt
  • app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordSection.kt
  • app/src/main/java/com/flint/presentation/profile/navigation/ProfileNavigation.kt
  • app/src/main/java/com/flint/presentation/profile/uistate/ProfileUiState.kt
  • app/src/main/java/com/flint/presentation/profile/uistate/SavedContentUiState.kt
  • app/src/main/java/com/flint/presentation/savedcontent/SavedContentListScreen.kt
  • app/src/main/java/com/flint/presentation/savedcontent/navigation/SavedContentNavigation.kt

Comment on lines +29 to +31
val isBusinessError = response.peekBody(Long.MAX_VALUE)
.string()
.contains("\"errorCode\"")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

peekBody 버퍼 크기를 제한하세요.

Long.MAX_VALUE를 버퍼 크기로 사용하면 대용량 응답 시 메모리 부족(OOM) 위험이 있습니다. 실무에서는 에러 응답 바디가 보통 작으므로 8KB~64KB 정도로 제한하는 것이 안전합니다.

또한, contains("\"errorCode\"")로 문자열 기반 감지는 취약합니다. errorCode가 메시지나 다른 필드 값에 포함될 경우 오탐이 발생할 수 있습니다(예: {"message": "Missing errorCode field"}). JSON 파싱이나 정규식을 사용해 errorCode 필드의 실제 존재 여부를 확인하는 것이 더 정확합니다.

🛡️ 개선 제안
-                        val isBusinessError = response.peekBody(Long.MAX_VALUE)
+                        val bufferSize = 8192L // 8KB
+                        val isBusinessError = response.peekBody(bufferSize)
                             .string()
-                            .contains("\"errorCode\"")
+                            .let { body ->
+                                // JSON 필드로 정확히 감지
+                                Regex(""""errorCode"\s*:""").containsMatchIn(body)
+                            }

또는 프로젝트에서 JSON 파서(Gson, Moshi 등)를 사용한다면 파싱 후 필드 존재 여부를 확인하는 것이 가장 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/flint/data/di/interceptor/NetworkErrorInterceptor.kt`
around lines 29 - 31, The current detection uses
response.peekBody(Long.MAX_VALUE).string().contains("\"errorCode\"") which risks
OOM and false positives; change NetworkErrorInterceptor to call
response.peekBody with a bounded buffer (e.g., 8KB–64KB) instead of
Long.MAX_VALUE and replace the naive contains check with a proper JSON field
existence test (use your project's JSON parser or JSONObject to parse the peeked
body and check for the "errorCode" key) when computing isBusinessError so large
bodies are not fully buffered and only a parsed JSON check determines presence
of the errorCode field.

Comment on lines +23 to +28
fun MyBookmarkedContentListResponseDto.toModel() : BookmarkedContentListModel {
return BookmarkedContentListModel(
totalCount = data.size,
contents = data.map { it.toModel() }.toImmutableList()
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: MyBookmarkedContentListResponseDto의 totalCount 필드 존재 여부 확인

rg -A 10 'data class MyBookmarkedContentListResponseDto' --type=kt

Repository: imflint/Flint-Android

Length of output: 92


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) DTO 선언(정확한 타입/패키지 포함) 찾기
echo "== DTO 선언 위치 =="
rg -n "data class MyBookmarkedContentListResponseDto" -S .

# 2) DTO 내부 필드 중 totalCount 관련 존재 여부 확인
echo
echo "== DTO 내부 totalCount 관련 라인 =="
rg -n "MyBookmarkedContentListResponseDto[^{]*\{|totalCount|meta\.totalCount" -S app/src/main/java || true

# 3) ContentMapper 해당 메서드 확인
echo
echo "== ContentMapper.kt: toModel() 주변 =="
rg -n "fun MyBookmarkedContentListResponseDto\.toModel\(\)" -S app/src/main/java/com/flint/domain/mapper/content/ContentMapper.kt
sed -n '1,120p' app/src/main/java/com/flint/domain/mapper/content/ContentMapper.kt | nl -ba | sed -n '1,120p' >/tmp/ContentMapper_head.txt
# 출력 제한 위해 필요한 구간만 재출력
sed -n '15,60p' app/src/main/java/com/flint/domain/mapper/content/ContentMapper.kt | nl -ba

# 4) UserRepository에서 allContents.size를 totalCount로 쓰는지 확인
echo
echo "== UserRepository.kt: allContents.size 사용 여부 =="
rg -n "allContents\.size|totalCount" -S app/src/main/java/com/flint || true

Repository: imflint/Flint-Android

Length of output: 2068


🏁 Script executed:

#!/bin/bash
set -euo pipefail

DTO="app/src/main/java/com/flint/data/dto/content/response/BookmarkedContentListResponseDto.kt"
MAPPER="app/src/main/java/com/flint/domain/mapper/content/ContentMapper.kt"
MODEL="app/src/main/java/com/flint/domain/model/content/BookmarkedContentListModel.kt"
REPO="app/src/main/java/com/flint/domain/repository/UserRepository.kt"

echo "== DTO: $(basename "$DTO") (head 60)=="
cat -n "$DTO" | sed -n '1,80p'

echo
echo "== DTO: tail 80 (필요 시)=="
wc -l "$DTO"
cat -n "$DTO" | tail -n 80

echo
echo "== MAPPER: $(basename "$MAPPER") (lines 1-80)=="
cat -n "$MAPPER" | sed -n '1,120p'

echo
echo "== MODEL: $(basename "$MODEL") (lines 1-120)=="
cat -n "$MODEL" | sed -n '1,120p'

echo
echo "== REPO: $(basename "$REPO") (around totalCount mapping)=="
# show only the relevant block around line ~82
sed -n '50,120p' "$REPO" | cat -n

Repository: imflint/Flint-Android

Length of output: 11121


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) MyBookmarkedContentListResponseDto.toModel() 사용처 찾기
rg -n "MyBookmarkedContentListResponseDto\.toModel\(\)" -S app/src/main/java || true
rg -n "MyBookmarkedContentListResponseDto" -S app/src/main/java || true

# 2) contentApi.getBookmarkedContentList 반환 타입 확인(어디서 MyBookmarkedContentListResponseDto를 매핑하는지)
rg -n "getBookmarkedContentList\(cursor" -S app/src/main/java || true
rg -n "interface .*ContentApi|class .*ContentApi|ContentApi" -S app/src/main/java || true

# 3) apiService.getBookmarkedContentListByUserId(userId) 경로 확인
rg -n "getBookmarkedContentListByUserId" -S app/src/main/java || true

Repository: imflint/Flint-Android

Length of output: 2050


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) MyBookmarkedContentListResponseDto.toModel() 사용처 찾기
rg -n "MyBookmarkedContentListResponseDto\.toModel\(\)" -S app/src/main/java || true
rg -n "MyBookmarkedContentListResponseDto" -S app/src/main/java || true

# 2) contentApi.getBookmarkedContentList 반환 타입 확인(어디서 MyBookmarkedContentListResponseDto를 매핑하는지)
rg -n "getBookmarkedContentList\(cursor" -S app/src/main/java || true
rg -n "interface .*ContentApi|class .*ContentApi|ContentApi" -S app/src/main/java || true

# 3) apiService.getBookmarkedContentListByUserId(userId) 경로 확인
rg -n "getBookmarkedContentListByUserId" -S app/src/main/java || true

Repository: imflint/Flint-Android

Length of output: 2050


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# ContentApi: getBookmarkedContentList 응답 구조 확인
echo "== ContentApi.kt =="
sed -n '1,200p' app/src/main/java/com/flint/data/api/ContentApi.kt | cat -n

echo
echo "== MyBookmarkedContentListResponseDto.toModel() 호출처 =="
rg -n "MyBookmarkedContentListResponseDto\.toModel\(" -S app/src/main/java || true

echo
echo "== meta.returned 사용처 =="
rg -n "meta\.returned|returned\s*:" -S app/src/main/java || true

Repository: imflint/Flint-Android

Length of output: 1972


totalCount 산출 근거를 DTO/사용처 기준으로 정리.

  • MyBookmarkedContentListResponseDto(커서 페이지네이션)에는 meta.totalCount 같은 전체 개수 필드가 없고, meta에는 returnednextCursor만 있습니다. 따라서 현재 totalCount = data.size는 “해당 응답에 포함된 아이템 수” 기준입니다.
  • 더 중요한 점: UserRepository에서는 userId == null 경로에서 커서를 반복 누적해 BookmarkedContentListModel(totalCount = allContents.size)로 전체 개수를 계산하므로, 해당 MyBookmarkedContentListResponseDto.toModel()totalCount 값은 현재 코드 경로에서 실제 결과에 반영되지 않는 상태입니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/flint/domain/mapper/content/ContentMapper.kt` around
lines 23 - 28, The current MyBookmarkedContentListResponseDto.toModel() sets
totalCount = data.size which only reflects items in the current cursor page;
change it to use the DTO's meta.returned value when available (e.g., totalCount
= meta?.returned ?: data.size) so the model's totalCount reflects the DTO's
intended count metric, and leave UserRepository's aggregation logic (which
builds BookmarkedContentListModel(totalCount = allContents.size)) to override as
needed; update the MyBookmarkedContentListResponseDto.toModel() implementation
accordingly.

Comment on lines 26 to +40
suspend fun toggleCollectionBookmark(collectionId: String): Result<Boolean> =
suspendRunCatching { api.toggleCollectionBookmark(collectionId).data }
.also { result ->
result.onSuccess { isBookmarked ->
_bookmarkChanges.emit(BookmarkChange.Collection(collectionId, isBookmarked))
}
}

// 콘텐츠 북마크 토글
suspend fun toggleContentBookmark(contentId: String): Result<Boolean> =
suspendRunCatching { api.toggleContentBookmark(contentId).data }
.also { result ->
result.onSuccess { isBookmarked ->
_bookmarkChanges.emit(BookmarkChange.Content(contentId, isBookmarked))
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the relevant repository file
FILE="app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt"
echo "=== $FILE (lines around toggle methods) ==="
if [ -f "$FILE" ]; then
  nl -ba "$FILE" | sed -n '1,220p'
else
  echo "File not found: $FILE"
  exit 1
fi

# Find the extension/helper suspendRunCatching definition
echo
echo "=== Find suspendRunCatching definition ==="
rg -n "suspendRunCatching" -S app/src/main/java || true

# Find definitions/usages of _bookmarkChanges.emit and BookmarkChange
echo
echo "=== Find _bookmarkChanges definition and type ==="
rg -n "_bookmarkChanges" -S app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt || true

echo
echo "=== Find BookmarkChange sealed/interface ==="
rg -n "sealed class BookmarkChange|BookmarkChange\\." -S app/src/main/java || true

# Find any custom Result.onSuccess or imports (rare but check)
echo
echo "=== Check imports in BookmarkRepository.kt ==="
nl -ba "$FILE" | sed -n '1,80p'

Repository: imflint/Flint-Android

Length of output: 232


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt"
echo "=== Check file exists ==="
ls -l "$FILE" || { echo "File not found: $FILE"; exit 1; }

echo
echo "=== $FILE (first 220 lines with line numbers) ==="
awk '{printf "%d\t%s\n", NR, $0}' "$FILE" | sed -n '1,220p'

echo
echo "=== Find suspendRunCatching definition/usages ==="
rg -n "suspendRunCatching" -S app/src/main/java

echo
echo "=== Find _bookmarkChanges definition/usages ==="
rg -n "_bookmarkChanges" -S app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt

echo
echo "=== Find BookmarkChange definitions ==="
rg -n "BookmarkChange" -S app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt

echo
echo "=== Find any onSuccess usage in BookmarkRepository.kt ==="
rg -n "\.onSuccess\s*\{" -S app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt

echo
echo "=== Show imports (top of file) ==="
awk '{printf "%d\t%s\n", NR, $0}' "$FILE" | sed -n '1,80p'

Repository: imflint/Flint-Android

Length of output: 9637


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for any custom Result.onSuccess extension or redefinition
rg -n "fun\s+<.*>\s*Result<.*>\.onSuccess|onSuccess\s*\(" app/src/main/java

# Show stdlib usage candidate: maybe imported extension
rg -n "onSuccess" app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt

# Inspect suspendRunCatching implementation to ensure it returns Kotlin Result and uses kotlin.Result.onSuccess
echo "=== SuspendRunCatching.kt ==="
sed -n '1,120p' app/src/main/java/com/flint/core/common/util/SuspendRunCatching.kt

Repository: imflint/Flint-Android

Length of output: 47


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for custom Result.onSuccess extension definitions ==="
rg -n "fun\s+.*Result<.*>\.onSuccess|extension\s+Result.*onSuccess|Result<.*>\.onSuccess\s*\{" app/src/main/java || true
rg -n "fun\s+.*onSuccess\s*\(" app/src/main/java || true

echo
echo "=== Search for .onSuccess usage (limit to files to reduce output) ==="
rg -n "\.onSuccess\s*\{" app/src/main/java || true

echo
echo "=== Show SuspendRunCatching.kt ==="
FILE="app/src/main/java/com/flint/core/common/util/SuspendRunCatching.kt"
if [ -f "$FILE" ]; then
  awk '{printf "%d\t%s\n", NR, $0}' "$FILE" | sed -n '1,200p'
else
  echo "File not found: $FILE"
fi

Repository: imflint/Flint-Android

Length of output: 5451


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect WithdrawViewModel usage around line 43
FILE="app/src/main/java/com/flint/presentation/setting/withdraw/WithdrawViewModel.kt"
echo "=== $FILE (lines 1-120) ==="
awk '{printf "%d\t%s\n", NR, $0}' "$FILE" | sed -n '1,140p'

echo
echo "=== Definitions related to _navigateToWithdrawComplete ==="
rg -n "_navigateToWithdrawComplete" "$FILE" || true

Repository: imflint/Flint-Android

Length of output: 2237


Result.onSuccess에서 MutableSharedFlow.emit(suspend) 호출 → 컴파일 불가(컬렉션/콘텐츠 토글 모두)

kotlin.Result.onSuccess의 람다는 non-suspend 타입이라 _bookmarkChanges.emit(...)를 호출할 수 없습니다.

🛠 제안 패치
     suspend fun toggleCollectionBookmark(collectionId: String): Result<Boolean> =
-        suspendRunCatching { api.toggleCollectionBookmark(collectionId).data }
-            .also { result ->
-                result.onSuccess { isBookmarked ->
-                    _bookmarkChanges.emit(BookmarkChange.Collection(collectionId, isBookmarked))
-                }
-            }
+        suspendRunCatching {
+            api.toggleCollectionBookmark(collectionId).data.also { isBookmarked ->
+                _bookmarkChanges.emit(BookmarkChange.Collection(collectionId, isBookmarked))
+            }
+        }

     suspend fun toggleContentBookmark(contentId: String): Result<Boolean> =
-        suspendRunCatching { api.toggleContentBookmark(contentId).data }
-            .also { result ->
-                result.onSuccess { isBookmarked ->
-                    _bookmarkChanges.emit(BookmarkChange.Content(contentId, isBookmarked))
-                }
-            }
+        suspendRunCatching {
+            api.toggleContentBookmark(contentId).data.also { isBookmarked ->
+                _bookmarkChanges.emit(BookmarkChange.Content(contentId, isBookmarked))
+            }
+        }
📝 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
suspend fun toggleCollectionBookmark(collectionId: String): Result<Boolean> =
suspendRunCatching { api.toggleCollectionBookmark(collectionId).data }
.also { result ->
result.onSuccess { isBookmarked ->
_bookmarkChanges.emit(BookmarkChange.Collection(collectionId, isBookmarked))
}
}
// 콘텐츠 북마크 토글
suspend fun toggleContentBookmark(contentId: String): Result<Boolean> =
suspendRunCatching { api.toggleContentBookmark(contentId).data }
.also { result ->
result.onSuccess { isBookmarked ->
_bookmarkChanges.emit(BookmarkChange.Content(contentId, isBookmarked))
}
suspend fun toggleCollectionBookmark(collectionId: String): Result<Boolean> =
suspendRunCatching {
api.toggleCollectionBookmark(collectionId).data.also { isBookmarked ->
_bookmarkChanges.emit(BookmarkChange.Collection(collectionId, isBookmarked))
}
}
// 콘텐츠 북마크 토글
suspend fun toggleContentBookmark(contentId: String): Result<Boolean> =
suspendRunCatching {
api.toggleContentBookmark(contentId).data.also { isBookmarked ->
_bookmarkChanges.emit(BookmarkChange.Content(contentId, isBookmarked))
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt` around
lines 26 - 40, The current use of Result.onSuccess in toggleCollectionBookmark
and toggleContentBookmark attempts to call the suspend function
_bookmarkChanges.emit(...) from a non-suspending lambda (Result.onSuccess),
causing a compile error; fix by replacing the onSuccess usage with a
suspend-friendly check such as val isBookmarked = result.getOrNull(); if
(isBookmarked != null) {
_bookmarkChanges.emit(BookmarkChange.Collection(collectionId, isBookmarked)) }
(and similarly for BookmarkChange.Content in toggleContentBookmark) so the emit
call runs directly in the surrounding suspend function.

Comment on lines +60 to +83
is BookmarkChange.Content -> {
val updatedContents = if (change.isBookmarked) {
data.savedContents
} else {
val filtered = data.savedContents.contents
.filter { it.id != change.id }
.toPersistentList()
data.savedContents.copy(
contents = filtered,
totalCount = filtered.size,
)
}
data.copy(savedContents = updatedContents)
}
is BookmarkChange.Collection -> {
val updatedCollections = if (change.isBookmarked) {
data.savedCollections
} else {
val filtered = data.savedCollections.collections
.filter { it.id != change.id }
.toPersistentList()
data.savedCollections.copy(collections = filtered)
}
data.copy(savedCollections = updatedCollections)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

타 유저 프로필의 저장 목록도 함께 지워집니다.

지금 분기는 isBookmarked == false이면 현재 보고 있는 프로필의 주인과 무관하게 항목을 목록에서 제거합니다. 그래서 다른 유저 프로필을 보고 있을 때 내가 북마크를 해제하면, 그 유저가 저장한 항목까지 사라집니다. PR 목표대로라면 내 프로필일 때만 목록에서 제거하고, 타 유저 프로필에서는 항목을 유지한 채 내 북마크 상태만 토글해야 합니다. 같은 이유로 isBookmarked == true 이벤트도 현재는 실질적으로 반영되지 않습니다.

Comment on lines +64 to +70
val filtered = data.savedContents.contents
.filter { it.id != change.id }
.toPersistentList()
data.savedContents.copy(
contents = filtered,
totalCount = filtered.size,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

totalCount를 현재 리스트 길이로 덮어쓰면 전체 개수 계약이 깨집니다.

savedContents.contents는 현재 메모리에 올라온 일부 항목일 수 있는데, 여기서 filtered.size를 넣으면 서버가 준 전체 저장 개수가 현재 화면 개수로 축소됩니다. 북마크 해제 시에는 기존 totalCount에서 1만 감소시키고, 화면 리스트 길이와 전체 개수는 분리해서 유지해야 합니다.

🛠 제안 패치
                                 data.savedContents.copy(
                                     contents = filtered,
-                                    totalCount = filtered.size,
+                                    totalCount = (data.savedContents.totalCount - 1).coerceAtLeast(0),
                                 )
📝 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
val filtered = data.savedContents.contents
.filter { it.id != change.id }
.toPersistentList()
data.savedContents.copy(
contents = filtered,
totalCount = filtered.size,
)
val filtered = data.savedContents.contents
.filter { it.id != change.id }
.toPersistentList()
data.savedContents.copy(
contents = filtered,
totalCount = (data.savedContents.totalCount - 1).coerceAtLeast(0),
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt` around
lines 64 - 70, 현재 코드에서 savedContents.contents를 필터한 뒤 totalCount를 filtered.size로
덮어써 전체 개수 계약이 깨집니다; 대신 북마크 해제 로직(예: where change.id is removed)에서는 contents를
filtered로 갱신하되 savedContents.totalCount는 기존 totalCount에서 1만 감소시키는 방식으로 유지하세요 (즉,
data.savedContents.copy(contents = filtered, totalCount =
maxOf(data.savedContents.totalCount - 1, 0))처럼 기존 totalCount를 직접 조작),
savedContents와 화면 리스트 길이를 분리해서 다루도록 ProfileViewModel의 해당 분기(데이터 업데이트:
data.savedContents.contents, totalCount)를 수정하십시오.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant