Skip to content

[feat] #178 select-album 모듈 구현 및 upload-album 전환#185

Merged
ikseong00 merged 14 commits intodevelopfrom
feat/#178-select-album
Apr 13, 2026
Merged

[feat] #178 select-album 모듈 구현 및 upload-album 전환#185
ikseong00 merged 14 commits intodevelopfrom
feat/#178-select-album

Conversation

@ikseong00
Copy link
Copy Markdown
Contributor

@ikseong00 ikseong00 commented Apr 9, 2026

🔗 관련 이슈

📙 작업 설명

  • feature/select-album 모듈(api/impl) 생성 및 의존성 설정
  • SelectAlbumScreen 구현: 앨범 목록 조회, 앨범 생성, 앨범 선택 후 업로드
  • upload-album 제거 후 select-album으로 전환
    • SelectAlbumAction sealed interface 도입: 네비게이션 시 업로드 유형(QR/갤러리) 전달
    • SelectAlbumViewModel에서 직접 업로드 처리 (isUploading 가드 포함)
    • 업로드 성공 시 AlbumDetail로 직접 네비게이션 (ResultBus 경유 제거)

🧪 테스트 내역 (선택)

  • 주요 기능 정상 동작 확인
  • 기기에서 동작 확인
  • 기존 기능 영향 없음

📸 스크린샷 또는 시연 영상 (선택)

기능 미리보기
앨범 선택 후 업로드
Screen_recording_20260410_015713.mp4

💬 추가 설명 or 리뷰 포인트 (선택)

  • SelectAlbumAction을 NavKey에 담아 전달: UploadFromQR(imageUrl) / UploadFromGallery(imageUriStrings)
  • 업로드 로직은 SelectAlbumViewModel에서 처리하며, 성공 시 AlbumDetail로 직접 이동
  • Archive 새로고침: AlbumDetail 닫힐 때 AlbumDetailResult 전송 → Archive 자동 갱신 (별도 ResultBus 이벤트 불필요)
  • isUploading 가드로 더블탭 중복 업로드 방지

Summary by CodeRabbit

릴리스 노트

  • 새 기능
    • 사진 업로드 시 앨범을 선택하는 새로운 선택 화면 추가
    • 앨범 선택 화면에서 새 앨범 생성 기능 추가
    • 갤러리 및 QR 코드를 통한 사진 업로드 전에 대상 앨범 선택 단계 추가

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

이 PR은 기존의 직접 사진 업로드-앨범 플로우를 새로운 통합 SelectAlbum 기능으로 대체합니다. UploadAlbum 기능을 완전히 제거하고 QR 코드 및 갤러리 사진 업로드 모두를 처리하는 새로운 SelectAlbum 기능 모듈을 도입합니다.

Changes

Cohort / File(s) Summary
App Navigation Refactoring
app/build.gradle.kts, app/src/main/java/com/neki/android/app/MainActivity.kt, app/src/main/java/com/neki/android/app/main/MainContract.kt, app/src/main/java/com/neki/android/app/main/MainScreen.kt, app/src/main/java/com/neki/android/app/main/MainViewModel.kt
앱 수준의 내비게이션을 SelectAlbum으로 업데이트. 이전의 두 가지 업로드 경로(navigateToUploadAlbumWithGallery, navigateToUploadAlbumWithQRScan)를 navigateToSelectAlbum으로 통합. MainSideEffect에서 두 가지 업로드 이펙트를 NavigateToSelectAlbum으로 대체. SelectAlbum 기능 모듈 의존성 추가.
UploadAlbum Feature Removal
feature/photo-upload/api/src/main/java/com/neki/android/feature/photo_upload/api/PhotoUploadNavKey.kt, feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/*, feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/di/PhotoUploadEntryProvider.kt
UploadAlbum 기능 관련 모든 컴포넌트 제거: 네비게이션 키, 상태/인텐트/사이드이펙트 계약, UI 화면, ViewModel, TopBar 컴포넌트, DI 엔트리 제공자.
SelectAlbum Feature API
feature/select-album/api/build.gradle.kts, feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/*
새로운 SelectAlbum 기능의 공개 API 정의: SelectAlbumNavKey 네비게이션 키, SelectAlbumAction 직렬화 가능한 액션 타입(QR/갤러리), MainNavigator 확장 함수.
SelectAlbum Feature Implementation
feature/select-album/impl/build.gradle.kts, feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/*
새로운 SelectAlbum 기능의 완전한 구현: 상태/인텐트/사이드이펙트 계약, Compose UI 화면, MVI 기반 ViewModel(앨범 로드 및 업로드 처리), TopBar 컴포넌트, DI 네비게이션 엔트리 제공자.
Build Configuration
settings.gradle.kts
:feature:select-album:api:feature:select-album:impl 새로운 모듈을 빌드 설정에 포함.

Sequence Diagram

sequenceDiagram
    actor User
    participant Main as Main Screen
    participant MainVM as MainViewModel
    participant SelectAlbumNav as SelectAlbum Navigation
    participant SelectAlbumScreen as SelectAlbum Screen
    participant SelectAlbumVM as SelectAlbumViewModel
    participant PhotoRepo as Photo/Folder Repo
    participant UploadUseCase as Upload UseCase

    User->>Main: 갤러리/QR로 업로드 시작
    Main->>MainVM: ClickUploadWithAlbum Intent
    MainVM->>MainVM: SelectAlbumAction 생성
    MainVM->>MainVM: NavigateToSelectAlbum SideEffect 방출
    
    Main->>SelectAlbumNav: navigateToSelectAlbum(action) 호출
    SelectAlbumNav->>SelectAlbumScreen: 라우트 생성
    
    SelectAlbumScreen->>SelectAlbumVM: 초기화
    SelectAlbumVM->>PhotoRepo: 즐겨찾기/앨범 목록 로드
    PhotoRepo-->>SelectAlbumVM: 앨범 데이터 반환
    SelectAlbumVM->>SelectAlbumVM: State 업데이트
    
    SelectAlbumVM-->>SelectAlbumScreen: uiState emit
    SelectAlbumScreen->>User: 앨범 목록 표시
    
    User->>SelectAlbumScreen: 앨범 선택 및 확인
    SelectAlbumScreen->>SelectAlbumVM: ClickConfirmButton Intent
    SelectAlbumVM->>UploadUseCase: uploadSinglePhotoUseCase/uploadMultiplePhotoUseCase 호출
    UploadUseCase-->>SelectAlbumVM: 업로드 성공/실패
    SelectAlbumVM->>SelectAlbumVM: SendUploadResult SideEffect 방출
    
    SelectAlbumVM-->>SelectAlbumScreen: SideEffect 처리
    SelectAlbumScreen->>Main: navigateToAlbumDetail/navigateBack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 선택의 앨범으로 모여드는 사진들

새로운 SelectAlbum의 문이 활짝,
QR도 갤러리도 하나의 길로 통하네,
앨범을 고르고 사진을 담으니,
UploadAlbum의 옛길은 사라지고,
더 나은 여정이 시작되도다! 📸✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 제목은 이 변경사항의 핵심을 명확하게 요약합니다: select-album 모듈 구현 및 upload-album으로의 전환.
Description check ✅ Passed PR 설명은 관련 이슈, 작업 내용, 테스트 결과, 스크린샷, 주요 설계 포인트를 포함하여 템플릿의 필수 섹션을 완벽하게 충족합니다.
Linked Issues check ✅ Passed 코드 변경사항이 #178 이슈의 모든 요구사항을 충족합니다: SelectAlbum 화면 구현, 앨범 목록 조회/생성 기능, 업로드 유형 기반 처리, 올바른 네비게이션 흐름.
Out of Scope Changes check ✅ Passed 모든 변경사항이 #178 범위 내에 있습니다: select-album 모듈 구현, upload-album 제거, 관련 네비게이션/ViewModel 변경은 모두 기능 요구사항과 직접 관련되어 있습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#178-select-album

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.

Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt (1)

125-148: 앨범 생성 중 로딩 표시 부재

handleAddAlbum에서 네트워크 요청 중 로딩 상태를 설정하지 않습니다. 사용자가 "추가하기" 버튼을 여러 번 클릭하면 중복 요청이 발생할 수 있습니다.

🔧 로딩 상태 추가 제안
 private fun handleAddAlbum(
     albumName: String,
     reduce: (SelectAlbumState.() -> SelectAlbumState) -> Unit,
     postSideEffect: (SelectAlbumSideEffect) -> Unit,
 ) {
     viewModelScope.launch {
+        reduce { copy(isLoading = true) }
         folderRepository.createFolder(name = albumName)
             .onSuccess {
                 fetchFolders(reduce)
                 reduce {
                     copy(
+                        isLoading = false,
                         isShowAddAlbumBottomSheet = false,
                         albumNameTextState = TextFieldState(),
                     )
                 }
                 postSideEffect(SelectAlbumSideEffect.ShowToastMessage("새로운 앨범을 추가했어요"))
             }
             .onFailure { e ->
                 Timber.e(e)
-                reduce { copy(isShowAddAlbumBottomSheet = false, albumNameTextState = TextFieldState()) }
+                reduce { copy(isLoading = false, isShowAddAlbumBottomSheet = false, albumNameTextState = TextFieldState()) }
                 postSideEffect(SelectAlbumSideEffect.ShowToastMessage("앨범 추가에 실패했어요"))
             }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`
around lines 125 - 148, The add-album flow in handleAddAlbum does not set a
loading flag so duplicate taps can trigger multiple requests; before launching
folderRepository.createFolder inside viewModelScope.launch, call reduce to set a
new SelectAlbumState flag (e.g., isAddingAlbum or isLoadingAlbum = true) and
disable the add button in the UI based on that flag, then after onSuccess and
onFailure ensure you reset that flag to false (alongside the existing
isShowAddAlbumBottomSheet and albumNameTextState updates); optionally, guard at
the top of handleAddAlbum by checking the state flag and returning early if
already loading to fully prevent concurrent requests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumNavKey.kt`:
- Line 7: Remove the unnecessary consecutive blank line in SelectAlbumNavKey.kt
that triggers Detekt's NoConsecutiveBlankLines rule (the extra empty line around
line 7); open the file containing the SelectAlbumNavKey declaration and delete
the extra blank line so there is only a single empty line between
sections/statements, then run Detekt/CI to confirm the warning is resolved.

In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 69-73: SelectAlbumViewModel currently passes the raw album name
from the intent handler to handleAddAlbum without defensive
trimming/empty-checking; update the ClickAddAlbumConfirmButton handling in
SelectAlbumViewModel so that before calling handleAddAlbum you call .trim() on
state.albumNameTextState.text and skip/return early (or do not call
handleAddAlbum) if the trimmed string is empty, matching the pattern used in
ArchiveMainViewModel.handleAddAlbum; ensure reduce and postSideEffect are
preserved when calling handleAddAlbum.

---

Nitpick comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 125-148: The add-album flow in handleAddAlbum does not set a
loading flag so duplicate taps can trigger multiple requests; before launching
folderRepository.createFolder inside viewModelScope.launch, call reduce to set a
new SelectAlbumState flag (e.g., isAddingAlbum or isLoadingAlbum = true) and
disable the add button in the UI based on that flag, then after onSuccess and
onFailure ensure you reset that flag to false (alongside the existing
isShowAddAlbumBottomSheet and albumNameTextState updates); optionally, guard at
the top of handleAddAlbum by checking the state flag and returning early if
already loading to fully prevent concurrent requests.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e6db1ab7-926d-427f-a80a-f2f6d956babe

📥 Commits

Reviewing files that changed from the base of the PR and between 4d23ae7 and 59853f9.

📒 Files selected for processing (21)
  • app/build.gradle.kts
  • app/src/main/java/com/neki/android/app/MainActivity.kt
  • app/src/main/java/com/neki/android/app/main/MainContract.kt
  • app/src/main/java/com/neki/android/app/main/MainScreen.kt
  • app/src/main/java/com/neki/android/app/main/MainViewModel.kt
  • feature/photo-upload/api/src/main/java/com/neki/android/feature/photo_upload/api/PhotoUploadNavKey.kt
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumContract.kt
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumViewModel.kt
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/component/UploadAlbumTopBar.kt
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/di/PhotoUploadEntryProvider.kt
  • feature/select-album/api/build.gradle.kts
  • feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumNavKey.kt
  • feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumResult.kt
  • feature/select-album/impl/build.gradle.kts
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumContract.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumTopBar.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/navigation/SelectAlbumEntryProvider.kt
  • settings.gradle.kts
💤 Files with no reviewable changes (6)
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/di/PhotoUploadEntryProvider.kt
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/component/UploadAlbumTopBar.kt
  • feature/photo-upload/api/src/main/java/com/neki/android/feature/photo_upload/api/PhotoUploadNavKey.kt
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumContract.kt
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
  • feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumViewModel.kt

Copy link
Copy Markdown
Member

@Ojongseok Ojongseok left a comment

Choose a reason for hiding this comment

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

고생하셨습니닷

Comment thread app/src/main/java/com/neki/android/app/main/MainViewModel.kt Outdated
Copy link
Copy Markdown

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

♻️ Duplicate comments (1)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt (1)

65-69: ⚠️ Potential issue | 🟡 Minor

공백 앨범명이 생성 요청으로 전달될 수 있습니다.

Line 65-67에서 입력값을 그대로 넘겨 " " 같은 값이 요청으로 전달될 수 있습니다. ViewModel에서 trim() 후 빈 값이면 조기 반환해 주세요. (이 이슈는 이전 리뷰에서도 이미 지적된 항목입니다.)

제안 수정안
-            SelectAlbumIntent.ClickAddAlbumConfirmButton -> handleAddAlbum(
-                albumName = state.albumNameTextState.text.toString(),
-                reduce = reduce,
-                postSideEffect = postSideEffect,
-            )
+            SelectAlbumIntent.ClickAddAlbumConfirmButton -> {
+                val trimmedAlbumName = state.albumNameTextState.text.toString().trim()
+                if (trimmedAlbumName.isEmpty()) return
+                handleAddAlbum(
+                    albumName = trimmedAlbumName,
+                    reduce = reduce,
+                    postSideEffect = postSideEffect,
+                )
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`
around lines 65 - 69, When handling
SelectAlbumIntent.ClickAddAlbumConfirmButton, sanitize the input before calling
handleAddAlbum: retrieve state.albumNameTextState.text, call trim() and if the
resulting string is empty return early (do not call handleAddAlbum or post side
effects); otherwise pass the trimmed string as albumName into handleAddAlbum so
no whitespace-only names are sent to the request.
🧹 Nitpick comments (1)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt (1)

130-141: 성공/실패 분기의 바텀시트 초기화 로직이 중복됩니다.

Line 130-135와 Line 140에서 동일 상태 초기화가 반복됩니다. 헬퍼로 추출하면 추후 상태 변경 시 누락 위험을 줄일 수 있습니다.

리팩터링 예시
+    private fun resetAddAlbumSheetState(
+        reduce: (SelectAlbumState.() -> SelectAlbumState) -> Unit,
+    ) {
+        reduce {
+            copy(
+                isShowAddAlbumBottomSheet = false,
+                albumNameTextState = TextFieldState(),
+            )
+        }
+    }
+
     private fun handleAddAlbum(
         albumName: String,
         reduce: (SelectAlbumState.() -> SelectAlbumState) -> Unit,
         postSideEffect: (SelectAlbumSideEffect) -> Unit,
     ) {
         viewModelScope.launch {
             folderRepository.createFolder(name = albumName)
                 .onSuccess {
                     fetchFolders(reduce)
-                    reduce {
-                        copy(
-                            isShowAddAlbumBottomSheet = false,
-                            albumNameTextState = TextFieldState(),
-                        )
-                    }
+                    resetAddAlbumSheetState(reduce)
                     postSideEffect(SelectAlbumSideEffect.ShowToastMessage("새로운 앨범을 추가했어요"))
                 }
                 .onFailure { e ->
                     Timber.e(e)
-                    reduce { copy(isShowAddAlbumBottomSheet = false, albumNameTextState = TextFieldState()) }
+                    resetAddAlbumSheetState(reduce)
                     postSideEffect(SelectAlbumSideEffect.ShowToastMessage("앨범 추가에 실패했어요"))
                 }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`
around lines 130 - 141, The reduce logic that resets isShowAddAlbumBottomSheet
and albumNameTextState is duplicated in both the success and failure branches of
the add-album flow; extract a single helper (e.g., a private method
resetAddAlbumUiState or an inline function resetAddAlbumState) and call it from
both the success path (after reduce { ... } currently) and the onFailure path
before posting side effects, updating uses of reduce, SelectAlbumSideEffect,
albumNameTextState, and isShowAddAlbumBottomSheet so the UI-reset behavior is
centralized and only maintained in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 65-69: When handling SelectAlbumIntent.ClickAddAlbumConfirmButton,
sanitize the input before calling handleAddAlbum: retrieve
state.albumNameTextState.text, call trim() and if the resulting string is empty
return early (do not call handleAddAlbum or post side effects); otherwise pass
the trimmed string as albumName into handleAddAlbum so no whitespace-only names
are sent to the request.

---

Nitpick comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 130-141: The reduce logic that resets isShowAddAlbumBottomSheet
and albumNameTextState is duplicated in both the success and failure branches of
the add-album flow; extract a single helper (e.g., a private method
resetAddAlbumUiState or an inline function resetAddAlbumState) and call it from
both the success path (after reduce { ... } currently) and the onFailure path
before posting side effects, updating uses of reduce, SelectAlbumSideEffect,
albumNameTextState, and isShowAddAlbumBottomSheet so the UI-reset behavior is
centralized and only maintained in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 90a557b9-6173-48e5-8338-ed285d72aa0a

📥 Commits

Reviewing files that changed from the base of the PR and between f1d12d8 and 45d5db3.

📒 Files selected for processing (1)
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt (1)

124-131: 앨범명 중복 검사에서 favoriteAlbum 누락 가능성

existingAlbumNamesuiState.albums만 포함하고 uiState.favoriteAlbum.title은 포함하지 않습니다. 즐겨찾기 앨범명과 동일한 이름으로 새 앨범을 생성하려는 경우 중복 검사가 작동하지 않을 수 있습니다. 서버에서 중복 검사를 수행한다면 문제없지만, 클라이언트 UX 측면에서 검토가 필요합니다.

수정 제안
-        val existingAlbumNames = remember(uiState.albums) { uiState.albums.map { it.title } }
+        val existingAlbumNames = remember(uiState.albums, uiState.favoriteAlbum) {
+            uiState.albums.map { it.title } + uiState.favoriteAlbum.title
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt`
around lines 124 - 131, existingAlbumNames is built only from uiState.albums so
the favorite album title (uiState.favoriteAlbum.title) can be omitted and allow
a duplicate name; update the logic in SelectAlbumScreen to include the favorite
title when computing existingAlbumNames (e.g., build the list from
uiState.albums plus uiState.favoriteAlbum?.title if non-null) and adjust the
remember dependencies to include uiState.favoriteAlbum (or its title) so
errorMessage's derivedStateOf correctly re-evaluates; ensure null-safe handling
of uiState.favoriteAlbum before adding it to the list.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt`:
- Around line 101-103: SelectAlbumScreen currently renders
FavoriteAlbumRowComponent without wiring its onClick; update the item block that
uses FavoriteAlbumRowComponent (with uiState.favoriteAlbum) to pass onClick = {
onIntent(AllAlbumIntent.ClickFavoriteAlbum) } so the favorite-album selection
flow matches AllAlbumScreen; ensure you reference the existing onIntent handler
and AllAlbumIntent.ClickFavoriteAlbum when adding the onClick param.

---

Nitpick comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt`:
- Around line 124-131: existingAlbumNames is built only from uiState.albums so
the favorite album title (uiState.favoriteAlbum.title) can be omitted and allow
a duplicate name; update the logic in SelectAlbumScreen to include the favorite
title when computing existingAlbumNames (e.g., build the list from
uiState.albums plus uiState.favoriteAlbum?.title if non-null) and adjust the
remember dependencies to include uiState.favoriteAlbum (or its title) so
errorMessage's derivedStateOf correctly re-evaluates; ensure null-safe handling
of uiState.favoriteAlbum before adding it to the list.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f34af0bb-1f34-4318-a2f4-dbc95a31106f

📥 Commits

Reviewing files that changed from the base of the PR and between 45d5db3 and 4fe7a67.

📒 Files selected for processing (11)
  • app/src/main/java/com/neki/android/app/MainActivity.kt
  • app/src/main/java/com/neki/android/app/main/MainContract.kt
  • app/src/main/java/com/neki/android/app/main/MainScreen.kt
  • app/src/main/java/com/neki/android/app/main/MainViewModel.kt
  • feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumAction.kt
  • feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumNavKey.kt
  • feature/select-album/impl/build.gradle.kts
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumContract.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/navigation/SelectAlbumEntryProvider.kt
✅ Files skipped from review due to trivial changes (1)
  • feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumAction.kt
🚧 Files skipped from review as they are similar to previous changes (5)
  • app/src/main/java/com/neki/android/app/MainActivity.kt
  • feature/select-album/impl/build.gradle.kts
  • app/src/main/java/com/neki/android/app/main/MainContract.kt
  • app/src/main/java/com/neki/android/app/main/MainScreen.kt
  • app/src/main/java/com/neki/android/app/main/MainViewModel.kt

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

해당 컴포넌트는 component 디렉토리 만들어서 하위에 위치시키죠 !

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

f2df48a 반영했습니다!

Comment on lines +11 to +12
val title: String = "모든 앨범",
val multiSelect: Boolean = true,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

navigateToSelectAlbum()에서 titlemultiSelect의 초기값이 동일하게 지정되어 넘어오는데 SelectAlbum 내부에서는 초기값 정의하지 않아도 되지 않을까요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

기존 코드에 영향이 있을까 해서 했는데 중첩이었군요, 087e979 반영했습니다!

Comment on lines +77 to +79
uploadSingleImage(state.scannedImageUrl, reduce = reduce, postSideEffect = postSideEffect)
} else {
postSideEffect(MainSideEffect.NavigateToUploadAlbumWithGallery(state.selectedUris.map { it.toString() }))
uploadMultipleImages(state.selectedUris, reduce = reduce, postSideEffect = postSideEffect)
Copy link
Copy Markdown
Member

@Ojongseok Ojongseok Apr 12, 2026

Choose a reason for hiding this comment

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

일관되게 첫번째 파라미터명 명시해주시면 좋을 것 같습니다

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

e920a62 확인했습니다!

SelectAlbumIntent.EnterSelectAlbumScreen -> fetchInitialData(reduce)
SelectAlbumIntent.ClickBackIcon -> postSideEffect(SelectAlbumSideEffect.NavigateBack)
SelectAlbumIntent.ClickConfirmButton -> {
if (state.isUploading) return
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

방어적으로 작성하기 위함인건 이해했으나 (isLoading || isUploading) 일 때 LoadingDialog()가 노출되어 사실 n장 업로드 버튼을 선택할 수 없는데 해당 부분에서만 유독 방어적으로 작성할 필요가 있을까요.?.?

UIState에 isConfirmEnabled 플래그를 통해 한 번 방어하고 있기도 하구요!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

987ffed 과한 방어로직이네요, 반영했습니다!

Copy link
Copy Markdown

@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

♻️ Duplicate comments (2)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt (1)

77-81: ⚠️ Potential issue | 🟡 Minor

앨범명 입력은 ViewModel에서 trim/빈값 방어가 필요합니다

Line 77-81에서 공백만 있는 입력이 그대로 전달됩니다. UI 검증과 별개로 ViewModel 방어 로직을 두는 편이 안전합니다.

수정 제안
-            SelectAlbumIntent.ClickAddAlbumConfirmButton -> handleAddAlbum(
-                albumName = state.albumNameTextState.text.toString(),
-                reduce = reduce,
-                postSideEffect = postSideEffect,
-            )
+            SelectAlbumIntent.ClickAddAlbumConfirmButton -> {
+                val albumName = state.albumNameTextState.text.toString().trim()
+                if (albumName.isBlank()) return
+                handleAddAlbum(
+                    albumName = albumName,
+                    reduce = reduce,
+                    postSideEffect = postSideEffect,
+                )
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`
around lines 77 - 81, The ClickAddAlbumConfirmButton branch passes raw input to
handleAddAlbum allowing whitespace-only names; update the
SelectAlbumIntent.ClickAddAlbumConfirmButton handling to trim the input (use
state.albumNameTextState.text.toString().trim()) and guard against empty strings
before calling handleAddAlbum — if the trimmed name is empty, short-circuit
(e.g., postSideEffect or reduce an error/validation state) instead of invoking
handleAddAlbum; this ensures handleAddAlbum receives a non-empty, trimmed
albumName.
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt (1)

103-105: ⚠️ Potential issue | 🟡 Minor

즐겨찾기 앨범 행에 선택 액션 연결이 빠져 있습니다

Line 104는 렌더링만 하고 있어 즐겨찾기 앨범을 선택 대상으로 사용할 수 없습니다.

수정 제안
             item {
-                FavoriteAlbumRowComponent(album = uiState.favoriteAlbum)
+                FavoriteAlbumRowComponent(
+                    album = uiState.favoriteAlbum,
+                    onClick = { onIntent(SelectAlbumIntent.ClickAlbumItem(uiState.favoriteAlbum)) },
+                )
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt`
around lines 103 - 105, The Favorite album row is rendered but has no selection
handler, so make FavoriteAlbumRowComponent in SelectAlbumScreen accept and
receive a click/select callback and forward the selection: add an
onClick/onSelect lambda parameter to FavoriteAlbumRowComponent and, where it is
called in SelectAlbumScreen (passing uiState.favoriteAlbum), pass a lambda that
invokes the existing selection handler (e.g., call
viewModel.selectAlbum(uiState.favoriteAlbum) or the screen's onAlbumSelected
callback) so tapping the row selects the favorite album.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 68-71: The ClickConfirmButton handler and performUpload path can
re-enter and may leave isUploading stuck if a UseCase throws; add a guard at the
start of the ClickConfirmButton branch to return early when state.isUploading is
true, then ensure performUpload (and any other upload paths around the 90-116
block) sets isUploading = true before performing work and always resets it to
false in a finally block (or use runCatching/onFailure) so exceptions don't
leave isUploading set; update references: SelectAlbumIntent.ClickConfirmButton,
state.selectedAlbums, performUpload, reduce, postSideEffect, and the isUploading
state to implement the guard and the try/finally reset.
- Around line 69-70: The code currently uploads only the first selected album
(val album = state.selectedAlbums.firstOrNull()) while the UI allows multiple
selection; update SelectAlbumViewModel to handle all selected albums
consistently by iterating state.selectedAlbums (or guard to enforce
single-selection at the state/UI level). If choosing iteration, replace the
single-call to performUpload with a loop over state.selectedAlbums calling
performUpload(album, reduce, postSideEffect) for each non-null album; if
choosing single-selection, change the selection model so state.selectedAlbums
contains at most one item and keep the existing performUpload call. Ensure
references to state.selectedAlbums and performUpload remain consistent.

---

Duplicate comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt`:
- Around line 103-105: The Favorite album row is rendered but has no selection
handler, so make FavoriteAlbumRowComponent in SelectAlbumScreen accept and
receive a click/select callback and forward the selection: add an
onClick/onSelect lambda parameter to FavoriteAlbumRowComponent and, where it is
called in SelectAlbumScreen (passing uiState.favoriteAlbum), pass a lambda that
invokes the existing selection handler (e.g., call
viewModel.selectAlbum(uiState.favoriteAlbum) or the screen's onAlbumSelected
callback) so tapping the row selects the favorite album.

In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 77-81: The ClickAddAlbumConfirmButton branch passes raw input to
handleAddAlbum allowing whitespace-only names; update the
SelectAlbumIntent.ClickAddAlbumConfirmButton handling to trim the input (use
state.albumNameTextState.text.toString().trim()) and guard against empty strings
before calling handleAddAlbum — if the trimmed name is empty, short-circuit
(e.g., postSideEffect or reduce an error/validation state) instead of invoking
handleAddAlbum; this ensures handleAddAlbum receives a non-empty, trimmed
albumName.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2d1b060d-56e7-45bd-99f6-3a9573d9a79c

📥 Commits

Reviewing files that changed from the base of the PR and between 4fe7a67 and 987ffed.

📒 Files selected for processing (5)
  • app/src/main/java/com/neki/android/app/main/MainViewModel.kt
  • feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumNavKey.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt
  • feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/component/SelectAlbumTopBar.kt
✅ Files skipped from review due to trivial changes (1)
  • feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumNavKey.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/neki/android/app/main/MainViewModel.kt

@ikseong00 ikseong00 merged commit 067476c into develop Apr 13, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] SelectAlbum 화면 구현

2 participants