Skip to content

[Feat] CollectionDetailScreen 구현#139

Merged
nahy-512 merged 21 commits into
developfrom
feat/#137-bookmark-collection
Jan 20, 2026
Merged

[Feat] CollectionDetailScreen 구현#139
nahy-512 merged 21 commits into
developfrom
feat/#137-bookmark-collection

Conversation

@giovannijunseokim
Copy link
Copy Markdown
Contributor

@giovannijunseokim giovannijunseokim commented Jan 19, 2026

📮 관련 이슈

📌 작업 내용

  • API 연결을 통해 컬렉션 상세 정보 표시
  • API 연결을 통해 컬렉션 북마크 기능 추가

📸 스크린샷

컬렉션 조회 컬렉션 저장 상단 앱 바의 뒤로가기 버튼 클릭
view_collection.mp4
save_collection.mp4
back_click_collection.mp4

😅 미구현

  • API 연결을 통해 컬렉션 내부 작품 콘텐츠
  • 스포일러 확인하기 기능 구현
  • 저장한 사람들 프로필 클릭 시 해당 사용자 프로필로 이동

🫛 To. 리뷰어

  • 시간 상 궁금한 점을 실시간으로 물어봐주세요. 👍

Summary by CodeRabbit

  • 새로운 기능

    • 컬렉션 상세 화면 및 관련 화면 흐름 추가
    • 컬렉션 북마크 토글 기능 추가
    • 컬렉션을 북마크한 사용자 목록 조회 및 표시 기능 추가
    • 상세 화면 상태/사이드이펙트 관리를 위한 뷰모델 추가
  • 개선사항

    • 사용자 프로필 표시(프로필 이미지, 닉네임, 역할 아이콘) 개선
    • 컬렉션 콘텐츠와 설명 등 상세 데이터 확장
    • 토스트 표시 레이아웃(padding) 개선

✏️ Tip: You can customize this high-level summary in your review settings.

# Conflicts:
#	app/src/main/java/com/flint/core/common/di/interceptor/TokenInterceptor.kt
#	app/src/main/java/com/flint/data/api/CollectionApi.kt
#	app/src/main/java/com/flint/domain/repository/CollectionRepository.kt
#	app/src/main/java/com/flint/presentation/main/MainNavigator.kt
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 19, 2026

Warning

Rate limit exceeded

@giovannijunseokim has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 10 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 29568ba and 14814ed.

📒 Files selected for processing (4)
  • app/src/main/java/com/flint/core/designsystem/component/collection/PeopleBottomSheet.kt
  • app/src/main/java/com/flint/core/designsystem/component/toast/ShowSaveToast.kt
  • app/src/main/java/com/flint/core/designsystem/component/toast/ShowToast.kt
  • app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt
📝 Walkthrough

Walkthrough

컬렉션 북마크 기능을 추가하고 CollectionDetail 화면과 관련 UI/데이터 계층을 CollectionBookmarkUsersModel 및 CollectionDetailModelNew 등의 새 타입으로 마이그레이션했습니다. API, DTO, 매퍼, 리포지토리, ViewModel, 화면, 내비게이션, 토스트/PeopleBottomSheet UI가 변경되었습니다.

Changes

Cohort / File(s) 변경 요약
API
app/src/main/java/com/flint/data/api/BookmarkApi.kt, app/src/main/java/com/flint/data/api/CollectionApi.kt
북마크 관련 엔드포인트 추가: getCollectionBookmarkUsers, toggleCollectionBookmark, 및 getCollectionDetail 메서드 및 관련 Retrofit 어노테이션/임포트 추가
DTOs
app/src/main/java/com/flint/data/dto/bookmark/CollectionBookmarkUsersDto.kt, app/src/main/java/com/flint/data/dto/collection/response/CollectionDetailResponseDto.kt
컬렉션 상세 및 컬렉션 북마크 사용자용 직렬화 DTO 추가(필드명: nickName, profileImageUrl 등)
도메인 모델
app/src/main/java/com/flint/domain/model/bookmark/..., .../AuthorModelNew.kt, .../content/ContentModel.kt, .../collection/CollectionDetailModel.kt
CollectionBookmarkUsersModel, AuthorModelNew, ContentModelNew, CollectionDetailModelNew 등 새 도메인 모델 추가 및 기존 모델 확장/샘플 데이터 업데이트
매퍼
app/src/main/java/com/flint/domain/mapper/bookmark/CollectionBookmarkUsersMapper.kt
DTO → 도메인 모델 변환 로직 추가 (toModel() 확장함수 및 role parsing 안전 처리)
저장소(Repository)
app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt, app/src/main/java/com/flint/domain/repository/CollectionRepository.kt
북마크 사용자 조회/토글 및 컬렉션 상세 조회 메서드 추가; CollectionRepository 생성자에 PreferencesManager 주입 및 userId 조회 사용
프레젠테이션 - ViewModel / State / SideEffect
app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailViewModel.kt, .../uistate/CollectionDetailUiState.kt, .../sideeffect/CollectionDetailSideEffect.kt
CollectionDetailViewModel 추가(초기 로드, 병렬 요청, 토글 처리), UI 상태 및 사이드 이펙트 타입 추가
프레젠테이션 - Screen / Navigation
app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt, .../navigation/CollectionDetailNavigation.kt, .../main/MainNavHost.kt
Route/Screen 서명 변경(collectionId 제거, SavedStateHandle 사용), navigateUp 콜백 추가, contents/people 타입을 새 도메인 타입으로 마이그레이션
UI 컴포넌트 / Previews
app/src/main/java/com/flint/core/designsystem/component/collection/PeopleBottomSheet.kt, app/src/main/java/com/flint/presentation/toast/ShowToast.kt, app/src/main/java/com/flint/presentation/toast/ShowSaveToast.kt
PeopleBottomSheet 및 Author composable 타입을 CollectionBookmarkUsersModel.User로 전환; Toast 컴포넌트에 paddingValues 파라미터 추가 및 레이아웃 수정; 프리뷰 데이터 갱신

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant Screen as CollectionDetailScreen
    participant VM as CollectionDetailViewModel
    participant Repo as Repository
    participant API as RetrofitAPI
    participant Mapper as Mapper
    participant Model as DomainModel

    User->>Screen: 화면 진입
    Screen->>VM: 초기 로드 요청 (collectionId from SavedStateHandle)
    par 병렬
        VM->>Repo: getCollectionDetail(collectionId)
        Repo->>API: GET /api/v1/collections/{collectionId}
        API-->>Repo: BaseResponse<CollectionDetailResponseDto>
        Repo->>Mapper: toModel() -> CollectionDetailModelNew
        Mapper-->>Repo: CollectionDetailModelNew
    and
        VM->>Repo: getCollectionBookmarkUsers(collectionId)
        Repo->>API: GET /api/v1/bookmarks/{collectionId}
        API-->>Repo: BaseResponse<CollectionBookmarkUsersDto>
        Repo->>Mapper: toModel() -> CollectionBookmarkUsersModel
        Mapper-->>Repo: CollectionBookmarkUsersModel
    end
    Repo-->>VM: Results
    VM-->>Screen: UiState.Success(CollectionDetailUiState)
    User->>Screen: 북마크 토글 클릭
    Screen->>VM: toggleCollectionBookmark()
    VM->>Repo: toggleCollectionBookmark(collectionId)
    Repo->>API: POST /api/v1/bookmarks/collections/{collectionId}
    API-->>Repo: BaseResponse<Boolean>
    Repo-->>VM: Result<Boolean>
    VM-->>Screen: SideEffect (ToggleCollectionBookmarkSuccess/Failure)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

📱 UI

Suggested reviewers

  • nahy-512
  • kimjw2003

Poem

🐰 새 북마크가 폴짝폴짝 왔어요,
DTO와 Mapper가 춤추며 다듬고,
Repo와 VM이 힘을 모아 불러오면,
화면엔 유저들이 반짝 반갑게 떠요. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.24% 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 제목은 구현된 주요 변경사항인 CollectionDetailScreen 구현을 명확하게 요약하고 있으며, 간결하고 구체적입니다.
Description check ✅ Passed PR 설명이 필수 템플릿 항목들을 포함하고 있으며, 관련 이슈, 작업 내용, 스크린샷, 미구현 항목을 명시하고 있습니다.
Linked Issues check ✅ Passed PR의 코드 변경사항들은 이슈 #137의 컬렉션 북마크 기능 구현 요구사항을 충족합니다. API 연결, 북마크 토글 기능, 컬렉션 상세정보 표시가 구현되어 있습니다.
Out of Scope Changes check ✅ Passed 변경사항들은 모두 컬렉션 상세 화면 구현과 북마크 기능에 관련되어 있으며, 스코프 외의 변경은 발견되지 않습니다.

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


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.

@giovannijunseokim giovannijunseokim force-pushed the feat/#137-bookmark-collection branch from 36bd2b1 to 8456760 Compare January 19, 2026 20:35
@giovannijunseokim giovannijunseokim marked this pull request as ready for review January 20, 2026 16:13
@giovannijunseokim giovannijunseokim requested a review from a team as a code owner January 20, 2026 16:13
@giovannijunseokim giovannijunseokim changed the title Feat/#137 bookmark collection [Feat] CollectionDetailScreen 구현 Jan 20, 2026
Copy link
Copy Markdown
Contributor

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt (2)

363-451: 컴파일 에러: Preview Provider 타입 불일치

PeoplePreviewProviderImmutableList<AuthorModel>을 반환하지만, Line 456의 PeopleWhoSavedThisCollectionPreviewImmutableList<CollectionBookmarkUsersModel.User>를 기대합니다. 이로 인해 컴파일 에러가 발생합니다.

🐛 제안하는 수정 방안
-private class PeoplePreviewProvider : PreviewParameterProvider<ImmutableList<AuthorModel>> {
-    override val values: Sequence<ImmutableList<AuthorModel>> =
+private class PeoplePreviewProvider : PreviewParameterProvider<ImmutableList<CollectionBookmarkUsersModel.User>> {
+    override val values: Sequence<ImmutableList<CollectionBookmarkUsersModel.User>> =
         sequenceOf(
             persistentListOf(
-                AuthorModel(
+                CollectionBookmarkUsersModel.User(
                     userId = "1",
-                    nickname = "유저1",
-                    profileUrl = "",
+                    nickName = "유저1",
+                    profileImageUrl = "",
                     userRole = UserRoleType.FLING,
                 ),
             ),
             // ... 나머지 항목들도 동일하게 변경
         )
 }

807-850: 컴파일 에러: ContentPreviewProvider 타입 불일치

ContentPreviewProviderContentModel을 반환하지만, Line 850의 ContentPreviewContentModelNew를 기대합니다. 이로 인해 컴파일 에러가 발생합니다.

🐛 제안하는 수정 방안
-private class ContentPreviewProvider : PreviewParameterProvider<ContentModel> {
-    override val values: Sequence<ContentModel> =
+private class ContentPreviewProvider : PreviewParameterProvider<ContentModelNew> {
+    override val values: Sequence<ContentModelNew> =
         sequenceOf(
-            ContentModel(
-                contentId = "0",
+            ContentModelNew(
+                id = "0",
                 title = "드라마 제목",
                 year = 2000,
-                posterImage = "",
-                ottSimpleList = listOf(OttType.Netflix, OttType.Disney),
+                imageUrl = "",
                 director = "가스 제닝스",
-                description = "달라진 온도...",
-                isSpoiler = false,
+                reason = "달라진 온도...",
                 isBookmarked = false,
+                isSpoiler = false,
+                bookmarkCount = 0,
             ),
             // ... 나머지 항목들도 동일하게 변경
         )
 }
🤖 Fix all issues with AI agents
In `@app/src/main/java/com/flint/data/api/BookmarkApi.kt`:
- Around line 11-20: The two Retrofit endpoints use inconsistent path formats:
getCollectionBookmarkUsers uses "/api/v1/..." (leading slash) while
toggleCollectionBookmark uses "api/v1/..." (no leading slash); update the path
in toggleCollectionBookmark to include the leading slash (change
`@POST`("api/v1/bookmarks/collections/{collectionId}") to
`@POST`("/api/v1/bookmarks/collections/{collectionId}")) so both endpoints
(getCollectionBookmarkUsers and toggleCollectionBookmark) use the same
absolute-path style.

In `@app/src/main/java/com/flint/data/dto/bookmark/CollectionBookmarkUsersDto.kt`:
- Around line 13-18: The User data class declares profileImageUrl as a
non-nullable String but other DTOs treat profile image URLs as nullable; update
the User data class (data class User) to make the profileImageUrl property of
type String? (nullable) so it matches RecommendCollectionResponseDto,
RecentCollectionListResponseDto, and CollectionDetailResponseDto and safely
handles API responses that may return null for profile images; ensure any usages
or mappings of User.profileImageUrl handle the nullable type accordingly.

In
`@app/src/main/java/com/flint/data/dto/collection/response/CollectionDetailResponseDto.kt`:
- Around line 6-37: Update the DTO to make fields that can be null according to
the API spec nullable: in CollectionDetailResponseDto change description: String
-> String? and thumbnailUrl: String -> String?; in nested Author keep
profileUrl: String? (already nullable); in nested Content change director:
String -> String?, reason: String -> String?, imageUrl: String -> String?, and
year: Int -> Int? (or keep Int if spec guarantees a value). Verify the API
contract and update the types in CollectionDetailResponseDto,
CollectionDetailResponseDto.Author.profileUrl, and
CollectionDetailResponseDto.Content (director, reason, imageUrl, year) to
String?/Int? as appropriate.

In
`@app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailViewModel.kt`:
- Around line 87-90: The onFailure block in CollectionDetailViewModel (inside
viewModelScope.launch) must not rethrow the exception because that will crash
the app; remove the throw it and instead set an error UiState or emit an error
event so the UI can show a dialog. Update the existing UiState to include an
Error case (or add a one-off event flow) and in the onFailure handler call the
appropriate state setter or emit function (e.g., update UiState to Error or send
via errorEventFlow) and log the exception for diagnostics; ensure the rest of
the coroutine continues to handle failure paths gracefully.
🧹 Nitpick comments (11)
app/src/main/java/com/flint/presentation/toast/ShowToast.kt (1)

27-32: 호출부 업데이트 여부 확인

paddingValues가 새 필수 파라미터라 기존 호출부가 모두 갱신되지 않으면 컴파일 오류가 납니다. 사용처가 전부 패딩 값을 전달하도록 확인해주세요. 필요하다면 기본값을 둬서 호환성을 유지하는 것도 고려해볼 수 있습니다.

♻️ 선택적 개선: 기본값 제공
-fun ShowToast(
-    text: String,
-    imageVector: ImageVector?,
-    paddingValues: PaddingValues,
-    yOffset: Dp,
-    hide: () -> Unit,
-) {
+fun ShowToast(
+    text: String,
+    imageVector: ImageVector?,
+    paddingValues: PaddingValues = PaddingValues.Zero,
+    yOffset: Dp,
+    hide: () -> Unit,
+) {

Also applies to: 60-65, 78-83

app/src/main/java/com/flint/presentation/toast/ShowSaveToast.kt (1)

24-28: 호출부 업데이트 여부 확인

paddingValues가 새 필수 파라미터라 기존 호출부가 모두 갱신되지 않으면 컴파일 오류가 납니다. 사용처가 전부 패딩 값을 전달하도록 확인해주세요. 필요하다면 기본값을 둬서 호환성을 유지하는 것도 고려해볼 수 있습니다.

♻️ 선택적 개선: 기본값 제공
-fun ShowSaveToast(
-    navigateToSavedCollection: () -> Unit,
-    paddingValues: PaddingValues,
-    yOffset: Dp,
-    hide: () -> Unit,
-) {
+fun ShowSaveToast(
+    navigateToSavedCollection: () -> Unit,
+    paddingValues: PaddingValues = PaddingValues.Zero,
+    yOffset: Dp,
+    hide: () -> Unit,
+) {

Also applies to: 55-59

app/src/main/java/com/flint/domain/model/AuthorModelNew.kt (1)

5-10: AuthorModelNew 네이밍에 대한 제안

클래스 이름에 "New" 접미사가 있는 것은 리팩토링 중임을 나타내지만, 장기적으로 코드 가독성을 저해할 수 있습니다. 기존 AuthorModel과의 통합이나 더 명확한 이름(예: CollectionAuthorModel)으로 변경을 고려해 주세요.

app/src/main/java/com/flint/domain/model/content/ContentModel.kt (1)

6-16: ContentModelNew 네이밍 일관성

AuthorModelNew와 동일하게 "New" 접미사가 사용되었습니다. 마이그레이션이 완료되면 기존 ContentModel을 대체하거나 더 구체적인 이름(예: CollectionContentModel)으로 리팩토링을 계획해 주세요.

구조와 필드 정의는 적절합니다.

app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailEvent.kt (1)

3-7: LGTM! 이벤트 구조가 적절합니다.

sealed interface를 사용한 이벤트 정의가 명확합니다. 향후 ToggleCollectionBookmarkSuccessdata class로 변경하면 디버깅 시 toString() 출력이 더 유용해질 수 있습니다.

💡 선택적 개선안
 sealed interface CollectionDetailEvent {
-    class ToggleCollectionBookmarkSuccess(val isBookmarked: Boolean) : CollectionDetailEvent
+    data class ToggleCollectionBookmarkSuccess(val isBookmarked: Boolean) : CollectionDetailEvent

     object ToggleCollectionBookmarkFailure : CollectionDetailEvent
 }
app/src/main/java/com/flint/domain/repository/BookmarkRepository.kt (1)

21-22: 미구현 placeholder 코멘트가 있습니다.

콘텐츠 북마크 토글 기능이 주석으로만 남아있습니다. 향후 구현 예정이라면 TODO 형식으로 변경하거나, 이번 PR 범위가 아니라면 제거를 고려해주세요.

app/src/main/java/com/flint/domain/model/collection/CollectionDetailModel.kt (3)

3-4: 도메인 모델에서 DTO를 직접 참조하고 있습니다.

CollectionDetailResponseDto를 도메인 모델 파일에서 직접 import하고 secondary constructor에서 사용하고 있습니다. 다른 매퍼 파일들(CollectionBookmarkUsersMapper.kt)처럼 별도의 mapper로 분리하면 계층 간 의존성이 명확해지고 일관성이 향상됩니다.


117-124: UserRoleType 파싱 로직이 중복됩니다.

CollectionBookmarkUsersMapper.kt의 Line 20에 동일한 runCatching { UserRoleType.valueOf(...) }.getOrDefault(UserRoleType.NONE) 패턴이 있습니다. 공통 유틸리티 함수로 추출하면 유지보수성이 향상됩니다.

♻️ 공통 유틸리티 예시
// UserRoleType.kt 또는 별도 유틸리티 파일에 추가
fun String.toUserRoleType(): UserRoleType =
    runCatching { UserRoleType.valueOf(this) }.getOrDefault(UserRoleType.NONE)

12-37: CollectionDetailModelNew 네이밍을 확인해주세요.

CollectionDetailModelCollectionDetailModelNew가 공존하는데, 마이그레이션 완료 후에는 New 접미사를 제거하고 기존 모델을 deprecated 처리하거나 정리하는 것이 좋습니다.

app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailViewModel.kt (1)

71-84: API 호출 병렬화를 고려해 주세요.

collectionRepository.getCollectionDetailbookmarkRepository.getCollectionBookmarkUsers가 순차적으로 호출되고 있습니다. 두 호출은 독립적이므로 async를 사용하여 병렬로 실행하면 로딩 시간을 줄일 수 있습니다.

♻️ 병렬 호출 예시
private fun getCollectionDetail(collectionId: String) {
    viewModelScope.launch {
        runCatching {
            coroutineScope {
                val collectionDetailDeferred = async {
                    collectionRepository.getCollectionDetail(collectionId).getOrThrow()
                }
                val bookmarkUsersDeferred = async {
                    bookmarkRepository.getCollectionBookmarkUsers(collectionId).getOrThrow()
                }
                
                UiState.Success(
                    CollectionDetailUiState(
                        collectionDetail = collectionDetailDeferred.await(),
                        collectionBookmarkUsers = bookmarkUsersDeferred.await()
                    )
                )
            }
        }.onSuccess { newUiState ->
            _uiState.update { newUiState }
        }.onFailure {
            // 에러 처리
        }
    }
}
app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt (1)

69-72: 사용되지 않는 import 정리 필요

ContentModelAuthorModel import가 남아있습니다. Preview Provider를 수정한 후 이 import들이 여전히 필요한지 확인해 주세요.

Comment thread app/src/main/java/com/flint/data/api/BookmarkApi.kt Outdated
- 북마크 상태 변경 시 `showSaveToast`와 `showCancelToast`가 동시에 활성화되지 않도록 명시적으로 수정
- `isProgressBarSticky` 변수에 타입 어노테이션 추가
Copy link
Copy Markdown
Contributor

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt (2)

365-466: Preview Provider 타입 불일치로 인한 컴파일 에러

PeoplePreviewProviderImmutableList<AuthorModel>을 반환하지만, PeopleWhoSavedThisCollectionPreview (Line 458)는 ImmutableList<CollectionBookmarkUsersModel.User>를 기대합니다. 이로 인해 컴파일 에러가 발생합니다.

Provider를 CollectionBookmarkUsersModel.User 타입으로 업데이트해야 합니다.

🐛 수정 제안
-private class PeoplePreviewProvider : PreviewParameterProvider<ImmutableList<AuthorModel>> {
-    override val values: Sequence<ImmutableList<AuthorModel>> =
+private class PeoplePreviewProvider : PreviewParameterProvider<ImmutableList<CollectionBookmarkUsersModel.User>> {
+    override val values: Sequence<ImmutableList<CollectionBookmarkUsersModel.User>> =
         sequenceOf(
             persistentListOf(
-                AuthorModel(
+                CollectionBookmarkUsersModel.User(
                     userId = "1",
-                    nickname = "유저1",
-                    profileUrl = "",
+                    nickName = "유저1",
+                    profileImageUrl = "",
                     userRole = UserRoleType.FLING,
                 ),
             ),
             // ... 나머지 항목들도 동일하게 수정
         )
 }

809-860: ContentPreviewProvider 타입 불일치로 인한 컴파일 에러

ContentPreviewProviderContentModel을 반환하지만, ContentPreview (Line 852)는 ContentModelNew를 기대합니다. 필드명도 다릅니다 (contentIdid, posterImageimageUrl, descriptionreason).

🐛 수정 제안
-private class ContentPreviewProvider : PreviewParameterProvider<ContentModel> {
-    override val values: Sequence<ContentModel> =
+private class ContentPreviewProvider : PreviewParameterProvider<ContentModelNew> {
+    override val values: Sequence<ContentModelNew> =
         sequenceOf(
-            ContentModel(
-                contentId = "0",
+            ContentModelNew(
+                id = "0",
                 title = "드라마 제목",
                 year = 2000,
-                posterImage = "",
-                ottSimpleList = listOf(OttType.Netflix, OttType.Disney),
+                imageUrl = "",
                 director = "가스 제닝스",
-                description = "달라진 온도\n-\n같은 구도에 채도를 달리해 변해버린 사랑을 시각적으로 담아낸 장면들",
+                reason = "달라진 온도\n-\n같은 구도에 채도를 달리해 변해버린 사랑을 시각적으로 담아낸 장면들",
                 isSpoiler = false,
                 isBookmarked = false,
+                bookmarkCount = 0,
             ),
             // ... 나머지 항목들도 동일하게 수정
         )
 }
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt`:
- Around line 119-120: Replace the empty else branch with a specific
UiState.Failure case: handle UiState.Failure by rendering an error UI (e.g., a
Text showing failure.message or localized text and a Button labeled “Retry”),
and wire the Button to the retry action (call the existing retry function or
viewModel.fetchCollection()/retryFetch()/onRetry lambda used elsewhere). Remove
the else -> {} and ensure you reference UiState.Failure in the when expression
inside CollectionDetailScreen so API errors display a message and let the user
retry.
- Around line 104-106: In CollectionDetailScreen.kt the Thumbnail prop is
assigning userId = collectionDetail.id (collection ID) instead of the
collection's owner, breaking the authorId != userId check; change the prop to
userId = collectionDetail.userId (use collectionDetail.userId) when calling
Thumbnail (the lines setting authorId, userId, isBookmarked) so the component
receives the actual author/user ID for correct bookmark/author checks.

- CollectionBookmarkUsersDto.User의 profileImageUrl 타입을 String?로 변경
- CollectionBookmarkUsersModel.User의 profileImageUrl 타입을 String?로 변경
- CollectionDetailResponseDto의 profileUrl을 profileImageUrl로 수정
- AuthorModelNew의 profileUrl을 profileImageUrl로 수정
- 관련 매퍼 함수(toDomain) 수정
@giovannijunseokim giovannijunseokim force-pushed the feat/#137-bookmark-collection branch from 452c427 to c454e9b Compare January 20, 2026 16:35
- onFailure 블록에서 throw it 구문을 제거하여 에러 발생 시 앱이 크래시되지 않도록 수정
- `userId`에 전달되는 값을 `collectionDetail.id`에서 `collectionDetail.userId`로 변경
@giovannijunseokim giovannijunseokim added Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경 🔖 API feat - API 연동 labels Jan 20, 2026
# Conflicts:
#	app/src/main/java/com/flint/domain/repository/CollectionRepository.kt
Copy link
Copy Markdown
Contributor

@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: 0

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/presentation/collectiondetail/CollectionDetailScreen.kt (1)

809-852: ContentPreviewProviderContentPreview 간 타입 불일치로 컴파일 에러 발생

ContentPreviewProviderContentModel을 반환하지만, ContentPreview 함수는 ContentModelNew를 기대합니다. 이로 인해 컴파일 에러가 발생합니다.

🐛 제안하는 수정
-private class ContentPreviewProvider : PreviewParameterProvider<ContentModel> {
-    override val values: Sequence<ContentModel> =
+private class ContentPreviewProvider : PreviewParameterProvider<ContentModelNew> {
+    override val values: Sequence<ContentModelNew> =
         sequenceOf(
-            ContentModel(
-                contentId = "0",
+            ContentModelNew(
+                id = "0",
                 title = "드라마 제목",
                 year = 2000,
-                posterImage = "",
-                ottSimpleList = listOf(OttType.Netflix, OttType.Disney),
+                imageUrl = "",
                 director = "가스 제닝스",
-                description = "달라진 온도\n-\n같은 구도에 채도를 달리해 변해버린 사랑을 시각적으로 담아낸 장면들",
+                reason = "달라진 온도\n-\n같은 구도에 채도를 달리해 변해버린 사랑을 시각적으로 담아낸 장면들",
                 isSpoiler = false,
                 isBookmarked = false,
+                bookmarkCount = 0,
             ),
-            ContentModel(
-                contentId = "0",
+            ContentModelNew(
+                id = "0",
                 title = "스포일러 있는 영화",
                 year = 2024,
-                posterImage = "",
-                ottSimpleList = listOf(OttType.Netflix),
+                imageUrl = "",
                 director = "감독 이름",
-                description = "이 내용은 스포일러가 포함되어 있습니다.",
+                reason = "이 내용은 스포일러가 포함되어 있습니다.",
                 isSpoiler = true,
                 isBookmarked = false,
+                bookmarkCount = 0,
             ),
-            ContentModel(
-                contentId = "0",
+            ContentModelNew(
+                id = "0",
                 title = "저장된 영화",
                 year = 2023,
-                posterImage = "",
-                ottSimpleList = listOf(OttType.Watcha, OttType.Wave),
+                imageUrl = "",
                 director = "다른 감독",
-                description = "내가 저장한 영화입니다.",
+                reason = "내가 저장한 영화입니다.",
                 isSpoiler = false,
                 isBookmarked = true,
                 bookmarkCount = 42,
             ),
         )
 }

또한 Line 69의 ContentModel import는 더 이상 사용되지 않으므로 제거할 수 있습니다.

🧹 Nitpick comments (4)
app/src/main/java/com/flint/domain/model/collection/CollectionDetailModel.kt (2)

23-36: DTO에서 도메인 모델로의 매핑을 별도 mapper로 분리하는 것을 고려해주세요.

현재 secondary constructor에서 DTO 매핑 로직을 처리하고 있습니다. 이 프로젝트의 다른 파일들(예: CollectionBookmarkUsersMapper.kt)처럼 별도의 mapper 확장 함수로 분리하면 일관성과 테스트 용이성이 향상됩니다.

♻️ 제안하는 리팩토링

별도 mapper 파일 생성:

// CollectionDetailMapper.kt
fun CollectionDetailResponseDto.toModel(userId: String): CollectionDetailModelNew {
    return CollectionDetailModelNew(
        author = author.toModel(),
        contents = contents.map { it.toModel() }.toImmutableList(),
        createdAt = createdAt,
        description = description,
        id = id,
        thumbnailUrl = thumbnailUrl,
        isBookmarked = isBookmarked,
        title = title,
        userId = userId
    )
}

117-124: UserRoleType 파싱 실패 시 로깅 추가를 권장합니다.

runCatching으로 예외를 조용히 처리하고 있어 디버깅 시 문제 원인 파악이 어려울 수 있습니다.

♻️ 제안하는 개선
 private fun CollectionDetailResponseDto.Author.toModel(): AuthorModelNew {
     return AuthorModelNew(
         id = id,
         nickname = nickname,
         profileImageUrl = profileImageUrl,
-        userRole = runCatching { UserRoleType.valueOf(userRole) }.getOrDefault(UserRoleType.NONE)
+        userRole = runCatching { UserRoleType.valueOf(userRole) }
+            .onFailure { /* Log.w("CollectionDetail", "Unknown userRole: $userRole") */ }
+            .getOrDefault(UserRoleType.NONE)
     )
 }
app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailViewModel.kt (2)

73-77: 두 API 호출을 병렬로 실행하면 성능이 향상됩니다.

현재 getCollectionDetailgetCollectionBookmarkUsers가 순차적으로 실행되고 있습니다. 두 호출이 독립적이므로 async를 사용하여 병렬로 실행할 수 있습니다.

♻️ 제안하는 개선
 private fun getCollectionDetail(collectionId: String) {
     viewModelScope.launch {
         runCatching {
+            val collectionDetailDeferred = async {
+                collectionRepository.getCollectionDetail(collectionId).getOrThrow()
+            }
+            val bookmarkUsersDeferred = async {
+                bookmarkRepository.getCollectionBookmarkUsers(collectionId).getOrThrow()
+            }
+
             val collectionDetail: CollectionDetailModelNew =
-                collectionRepository.getCollectionDetail(collectionId).getOrThrow()
+                collectionDetailDeferred.await()
             val collectionBookmarkUsers: CollectionBookmarkUsersModel =
-                bookmarkRepository.getCollectionBookmarkUsers(collectionId).getOrThrow()
+                bookmarkUsersDeferred.await()

coroutineScope import 추가:

import kotlinx.coroutines.async

87-89: 실패 시 UiStateLoading 상태로 남아있는 문제

onFailure 블록이 비어있어 API 호출 실패 시 UI가 로딩 상태에서 벗어나지 못합니다. 이전 리뷰에서 언급된 대로 전체 화면 통합 에러 처리 예정이시라면, 최소한 UiState.Failure로 상태를 업데이트하는 것이 좋습니다.

♻️ 제안하는 최소한의 처리
             }.onFailure {
                 // TODO: 데이터 불러오지 못한 경우, 다이얼로그 띄우도록 구현
+                _uiState.update { UiState.Failure(it) }
             }

…패키지 이동

- `CollectionDetailEvent` 클래스를 삭제하고 `sideeffect` 패키지에 `CollectionDetailSideEffect` 생성
- `CollectionDetailViewModel`에서 `event` 변수를 `sideEffect`로 변경
- `CollectionDetailScreen`에서 `viewModel.event` 대신 `viewModel.sideEffect`를 관찰하도록 수정
Copy link
Copy Markdown
Contributor

@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: 0

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/presentation/collectiondetail/CollectionDetailScreen.kt (1)

810-853: ContentPreviewProviderContentPreview 간 타입 불일치

ContentPreviewProviderPreviewParameterProvider<ContentModel>을 상속하지만, ContentPreview 함수의 파라미터 타입은 ContentModelNew입니다. 타입 불일치로 인해 컴파일 에러 또는 런타임 오류가 발생할 수 있습니다.

🐛 수정 제안
-private class ContentPreviewProvider : PreviewParameterProvider<ContentModel> {
-    override val values: Sequence<ContentModel> =
+private class ContentPreviewProvider : PreviewParameterProvider<ContentModelNew> {
+    override val values: Sequence<ContentModelNew> =
         sequenceOf(
-            ContentModel(
-                contentId = "0",
+            ContentModelNew(
+                id = "0",
                 title = "드라마 제목",
                 year = 2000,
-                posterImage = "",
-                ottSimpleList = listOf(OttType.Netflix, OttType.Disney),
+                imageUrl = "",
                 director = "가스 제닝스",
-                description = "달라진 온도\n-\n같은 구도에 채도를 달리해 변해버린 사랑을 시각적으로 담아낸 장면들",
+                reason = "달라진 온도\n-\n같은 구도에 채도를 달리해 변해버린 사랑을 시각적으로 담아낸 장면들",
                 isSpoiler = false,
                 isBookmarked = false,
+                bookmarkCount = 0,
             ),
-            ContentModel(
-                contentId = "0",
+            ContentModelNew(
+                id = "0",
                 title = "스포일러 있는 영화",
                 year = 2024,
-                posterImage = "",
-                ottSimpleList = listOf(OttType.Netflix),
+                imageUrl = "",
                 director = "감독 이름",
-                description = "이 내용은 스포일러가 포함되어 있습니다.",
+                reason = "이 내용은 스포일러가 포함되어 있습니다.",
                 isSpoiler = true,
                 isBookmarked = false,
+                bookmarkCount = 0,
             ),
-            ContentModel(
-                contentId = "0",
+            ContentModelNew(
+                id = "0",
                 title = "저장된 영화",
                 year = 2023,
-                posterImage = "",
-                ottSimpleList = listOf(OttType.Watcha, OttType.Wave),
+                imageUrl = "",
                 director = "다른 감독",
-                description = "내가 저장한 영화입니다.",
+                reason = "내가 저장한 영화입니다.",
                 isSpoiler = false,
                 isBookmarked = true,
                 bookmarkCount = 42,
             ),
         )
 }
🧹 Nitpick comments (1)
app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailViewModel.kt (1)

72-92: 독립적인 API 호출의 병렬 처리를 권장합니다.

getCollectionDetailgetCollectionBookmarkUsers는 서로 의존성이 없어 병렬로 실행할 수 있습니다. 순차 실행 시 불필요한 대기 시간이 발생합니다.

♻️ async/await을 사용한 병렬 처리 제안
 private fun getCollectionDetail(collectionId: String) {
     viewModelScope.launch {
         runCatching {
-            val collectionDetail: CollectionDetailModelNew =
-                collectionRepository.getCollectionDetail(collectionId).getOrThrow()
-            val collectionBookmarkUsers: CollectionBookmarkUsersModel =
-                bookmarkRepository.getCollectionBookmarkUsers(collectionId).getOrThrow()
+            val collectionDetailDeferred = async {
+                collectionRepository.getCollectionDetail(collectionId).getOrThrow()
+            }
+            val collectionBookmarkUsersDeferred = async {
+                bookmarkRepository.getCollectionBookmarkUsers(collectionId).getOrThrow()
+            }
+
+            val collectionDetail: CollectionDetailModelNew = collectionDetailDeferred.await()
+            val collectionBookmarkUsers: CollectionBookmarkUsersModel = collectionBookmarkUsersDeferred.await()

             UiState.Success(
                 CollectionDetailUiState(
                     collectionDetail = collectionDetail,
                     collectionBookmarkUsers = collectionBookmarkUsers
                 )
             )
         }.onSuccess { newUiState: UiState.Success<CollectionDetailUiState> ->
             _uiState.update { newUiState }
         }.onFailure {
             // TODO: 데이터 불러오지 못한 경우, 다이얼로그 띄우도록 구현
         }
     }
 }

async 사용을 위해 kotlinx.coroutines.async import가 필요합니다.

- `PreferencesManager`의 패키지 위치를 `com.flint.core.common.datastore`에서 `com.flint.data.local`로 변경
- 사용하지 않는 import 제거 및 코드 포맷팅 수정
Copy link
Copy Markdown
Contributor

@nahy-512 nahy-512 left a comment

Choose a reason for hiding this comment

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

어푸푸🚀 저랑 스타일이 다른 부분이 많아서 이것저것 많이 여쭤봤네요ㅎㅎ 친절히 답변주셔서 너무 감사합니다! 작업 너무 고생하셨습니다

@Composable
fun PeopleBottomSheet(
people: List<AuthorModel>,
people: List<CollectionBookmarkUsersModel.User>,
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.

p2
ImmutableList로 받지 않고 있는 이유가 있나요?

@nahy-512 nahy-512 merged commit 0951250 into develop Jan 20, 2026
2 checks passed
@nahy-512 nahy-512 deleted the feat/#137-bookmark-collection branch January 20, 2026 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 컬렉션 북마크 기능 구현

2 participants