Skip to content

Flt 15 컬렉션 생성 UI 개편#209

Merged
chanmi1125 merged 14 commits into
developfrom
FLT-15-컬렉션-생성-UI-개편
Jun 11, 2026

Hidden character warning

The head ref may contain hidden characters: "FLT-15-\uceec\ub809\uc158-\uc0dd\uc131-UI-\uac1c\ud3b8"
Merged

Flt 15 컬렉션 생성 UI 개편#209
chanmi1125 merged 14 commits into
developfrom
FLT-15-컬렉션-생성-UI-개편

Conversation

@chanmi1125

@chanmi1125 chanmi1125 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

📮 관련 이슈

  • closed #이슈번호

📌 작업 내용

📸 스크린샷

스크린샷

😅 미구현

  • [ ]

🫛 To. 리뷰어

  • 급하게 알바가야돼서 이미지는 다녀와서 첨부하겟슨.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 컬렉션 생성 시 썸네일 이미지를 선택하고 업로드할 수 있습니다.
    • 컬렉션의 각 콘텐츠별로 여러 개의 이미지를 추가할 수 있습니다(최대 5개).
    • 선택된 이미지는 자동으로 클라우드에 업로드됩니다.
    • 썸네일 및 콘텐츠 이미지를 삭제할 수 있습니다.
  • UI 개선

    • 컬렉션 생성 화면 레이아웃을 개선하여 더 직관적인 구성으로 변경되었습니다.

chanmi1125 added 10 commits May 7, 2026 20:55
  - CollectionCreateContentItemList 제거
  - CollectionCreateContentImage: HorizontalPager 기반 이미지 슬라이더 (최대 5개, 빈 리스트 시 미표시, 1개 시 인디케이터 숨김)
  - CollectionCreateContentReason: 선택 이유 텍스트필드 + 스포일러 토글 분리
  - AddContentSelectItem으로 컴포넌트 네이밍 개선
  - CollectionCreateScreen에서 세 컴포넌트 조합으로 교체
 - AddContentSelectItem: 콘텐츠 선택 항목 (체크 아이콘 포함)
  - CollectionCreateContentImage: 이미지 페이저 (삭제 버튼, 페이지 인디케이터 포함)
  - CollectionCreateContentReason: 선택 이유 입력 (이미지 첨부, 스포일러 토글 포함)
  - CollectionCreateContentItemList 제거
  - CollectionCreateContentImage: HorizontalPager 기반 이미지 슬라이더 (최대 5개, 빈 리스트 시 미표시, 1개 시 인디케이터 숨김)
  - CollectionCreateContentReason: 선택 이유 텍스트필드 + 스포일러 토글 분리
  - AddContentSelectItem으로 컴포넌트 네이밍 개선
  - CollectionCreateScreen에서 세 컴포넌트 조합으로 교체
 - AddContentSelectItem: 콘텐츠 선택 항목 (체크 아이콘 포함)
  - CollectionCreateContentImage: 이미지 페이저 (삭제 버튼, 페이지 인디케이터 포함)
  - CollectionCreateContentReason: 선택 이유 입력 (이미지 첨부, 스포일러 토글 포함)
  - ContentDetail에 imageUrls 필드 추가
  - CollectionCreateContentImage HorizontalPager 무한 스크롤 적용
  - 인디케이터 도트 currentIndex 기준으로 수정
  - CollectionCreateScreen 프리뷰로 교체 (더미 데이터 포함)
  - 저작권 문구 수정
…-컬렉션-생성-UI-개편

# Conflicts:
#	app/src/main/java/com/flint/presentation/collectioncreate/CollectionCreateScreen.kt
#	app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentImage.kt
  - 커버 사진 삭제 버튼 동작 연결
  - 콘텐츠 이미지 Uri 로컬 저장 및 S3 업로드 플로우 구현
  - 콘텐츠 이미지 1장씩 추가, 최대 5장 제한
  - 이미지 표시 방식 Crop → Fit(4:3)으로 변경
  - S3 업로드 로직 공통 함수로 리팩터링
  - 이미지 추가 시 새 사진으로 자동 스크롤
  - 이미지 삭제 시 이전/다음 사진으로 자연스럽게 이동
  - 사진 1장일 때 페이저 스크롤 비활성화
  - 이미지 스케일 FillBounds로 변경 (전체 영역 채우기)
@chanmi1125 chanmi1125 self-assigned this Jun 9, 2026
@chanmi1125 chanmi1125 added 🔖 API feat - API 연동 Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경 📱 UI feat - 컴포넌트 조립, 화면 구현 labels Jun 9, 2026
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

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

More reviews will be available in 10 minutes and 56 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: 90b2f70f-68b5-4caa-86f1-edd91189cacd

📥 Commits

Reviewing files that changed from the base of the PR and between be55ed3 and b95923f.

📒 Files selected for processing (4)
  • app/src/main/java/com/flint/presentation/collectioncreate/CollectionCreateScreen.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/CollectionCreateViewModel.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentImage.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt
📝 Walkthrough

Walkthrough

이 PR은 컬렉션 생성 흐름에 썸네일 및 콘텐츠별 이미지 선택·업로드 기능을 추가합니다. 데이터 계약부터 UI 렌더링까지 일관된 이미지 URI 관리를 구현하고, StorageRepository를 통한 S3 업로드 처리를 추가합니다.

Changes

Collection Image Upload Feature

Layer / File(s) Summary
Data Contract & State
app/src/main/java/com/flint/data/dto/collection/request/CollectionCreateRequestDto.kt, app/src/main/java/com/flint/domain/model/collection/CollectionCreateModel.kt, app/src/main/java/com/flint/domain/mapper/collection/CollectionCreateMapper.kt, app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt
DTO의 ContentimageUrls: List<String> 필드를 추가하고, 도메인 모델에도 동일 필드를 포함시킵니다. 매퍼는 이를 요청에 반영하도록 업데이트되며, UI 상태에 thumbnailImageUri: Uri?contentImageUris: List<Uri>를 추가하여 클라이언트 이미지 선택을 추적합니다.
ViewModel Image Upload Implementation
app/src/main/java/com/flint/presentation/collectioncreate/CollectionCreateViewModel.kt
생성자에 StorageRepository@ApplicationContext Context를 추가하여 이미지 업로드 인프라를 확장합니다. updateThumbnailImageUri(), addContentImageUri(), removeContentImageUri() public 메서드로 UI로부터 이미지 URI를 수집하고, uploadImageIfNeeded()uploadContentImagesIfNeeded()는 MIME 타입 추정, presigned URL 발급, S3 업로드를 수행하여 이미지 키를 얻습니다. postCollectionCreate()에서 업로드된 키를 요청 모델에 반영합니다.
UI Components Refactoring
app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentSection.kt, app/src/main/java/com/flint/presentation/collectioncreate/component/AddContentSelectItem.kt, app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentReason.kt, app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentImage.kt, app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateThumbnail.kt
ContentSection의 파라미터명을 imageUrl에서 posterImageUrl로 변경하여 의미를 명확히 합니다. 새 CollectionCreateContentImage 컴포넌트는 HorizontalPager로 여러 콘텐츠 이미지를 페이징하며 삭제 콜백을 제공합니다. CollectionCreateContentItemListCollectionCreateContentReason으로 대체되어 선택 이유와 스포일러 토글을 함께 렌더링합니다. Thumbnail 컴포넌트는 imageUrl 타입을 Any?로 확장하여 Uri 객체도 지원합니다.
CollectionCreateScreen Refactoring
app/src/main/java/com/flint/presentation/collectioncreate/CollectionCreateScreen.kt
화면 서명을 리팩토링하여 명시적 selectedContents / contentDetailsMap 파라미터를 제거하고 onGalleryClick(), onThumbnailDelete(), onSelectContentImage(contentId), onRemoveContentImage(contentId, index) 콜백을 추가합니다. CollectionCreateRoute에는 썸네일 갤러리 선택과 콘텐츠별 이미지 선택용 두 개의 rememberLauncherForActivityResult 런처를 추가하고, 최대 5개 이미지 제한을 적용합니다. UI는 이전의 인라인 레이아웃에서 LazyColumn 기반 섹션 컴포저블(CollectionCreateThumbnail, CollectionTitle, CollectionDescription, CollectionPublicSection, CollectionAddContentSection)로 재구성되며, 썸네일 선택/삭제용 MenuBottomSheet를 도입합니다. 미리보기는 새 서명에 맞춰 업데이트되고 private으로 변경됩니다.
AddContentScreen Integration
app/src/main/java/com/flint/presentation/collectioncreate/AddContentScreen.kt
CollectionCreateContentSelect import을 AddContentSelectItem으로 변경하고, 컴포넌트 호출 시 파라미터명을 imageUrl에서 posterImageUrl로 통일합니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • imflint/Flint-Android#152: 기존 컬렉션 생성 DTO/모델/매퍼 구조에 콘텐츠별 imageUrls 필드를 추가하는 데이터 계약 확장과 직결됩니다.
  • imflint/Flint-Android#102: CollectionCreateScreen 및 라우트 배선을 리팩토링하여 썸네일·갤러리 처리 및 화면 서명 변경을 수행합니다.
  • imflint/Flint-Android#65: 썸네일 선택·삭제용 MenuBottomSheet 및 모달 흐름을 UI 레벨에서 구현하는 데 직접 연결됩니다.

Suggested labels

🧩 Component

Suggested reviewers

  • kimjw2003

Poem

🐰 이미지 가득한 컬렉션이 피어나네,
갤러리에서 고르고 S3 구름 위로 올려,
URI는 춤을 추고 페이저는 넘어가고,
썸네일 아래 콘텐츠들이 반짝이네—
컬렉션의 이야기가 이제 시각으로 말하나요! 🌸

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning PR 설명이 템플릿의 필수 섹션 대부분이 비어있거나 불완전합니다. 작업 내용, 스크린샷, 미구현 사항이 채워지지 않았습니다. 작업 내용을 상세히 기입하고, 스크린샷을 첨부하며, 미구현 사항이 있으면 명시해주세요. 이슈 번호도 확인 후 업데이트 필요합니다.
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 (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항을 명확하게 반영하고 있습니다. 컬렉션 생성 UI 개편이 정확한 요약입니다.
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-15-컬렉션-생성-UI-개편

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

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/collectioncreate/component/AddContentSelectItem.kt (1)

60-67: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

선택 아이콘에 접근성 설명/상태를 추가해 주세요.

Line 62가 contentDescription = null이고 컨트롤이 Line 67에서 클릭 가능해서, 보조기기 사용자는 이 요소의 목적과 선택 상태를 파악하기 어렵습니다. 선택/해제 상태를 설명하는 semantics(또는 동적 contentDescription)를 넣어 주세요.

🤖 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/collectioncreate/component/AddContentSelectItem.kt`
around lines 60 - 67, The Icon currently uses contentDescription = null and only
Modifier.clickable, so screen readers can't tell its purpose or selected state;
update the Icon to provide a dynamic contentDescription and semantics by using
isSelected to produce a descriptive string (e.g., "Select item, selected" vs
"Select item, not selected") and add accessibility semantics on the Modifier
(e.g., set role to Checkbox/toggle and set a stateDescription or proper checked
state) while keeping onClick behavior (refer to Icon, isSelected, onClick and
the Modifier.clickable in AddContentSelectItem.kt).
🤖 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/presentation/collectioncreate/CollectionCreateScreen.kt`:
- Around line 86-90: The gallery launcher handler currently calls
viewModel.updateThumbnailImageUri(uri) even when uri is null (user cancelled),
causing the thumbnail to be cleared; change the callback in the
rememberLauncherForActivityResult registered as galleryLauncher so it ignores
null results (only call viewModel.updateThumbnailImageUri(uri) when uri != null)
to preserve the existing thumbnail on cancel.

In
`@app/src/main/java/com/flint/presentation/collectioncreate/CollectionCreateViewModel.kt`:
- Around line 63-64: The creation flow currently masks upload failures by using
a fallback empty string for thumbnail (uploadImageIfNeeded(... ) ?: "") and by
letting uploadContentImagesIfNeeded() feed into a mapNotNull path (lines around
235-243), which silently drops failed uploads; change the flow so that
uploadImageIfNeeded and uploadContentImagesIfNeeded return explicit failure
results (e.g., nullable/Result/ sealed outcome) and in the collection creation
path (where thumbnailImageUri is processed and where content images are mapped)
explicitly check for failures and abort the create operation, setting _uiState
to an error/failure state and returning early instead of proceeding with missing
image keys; update the callers (the createCollection routine and any mapNotNull
usage) to handle a null/failure by stopping the request and surface a clear
error to the UI.
- Around line 249-251: The contentResolver.getType(uri) call inside
withContext(Dispatchers.IO) (used to set mimeType) can throw
SecurityException/FileNotFoundException/IOException and will cancel the
surrounding postCollectionCreate() coroutine; wrap the contentResolver access in
a try/catch (or runCatching) inside withContext(Dispatchers.IO) to catch those
exceptions, log the error, and return a safe default like "image/jpeg" instead
of rethrowing. Apply the same pattern to the other contentResolver access in
this method (the similar call at the 262-264 area) so both mimeType resolution
and the second resolver call do not propagate exceptions that would cancel
postCollectionCreate().
- Around line 225-230: removeContentImageUri에서
current.contentImageUris.removeAt(index)가 유효 범위 검증 없이 호출되어
IndexOutOfBoundsException을 일으킬 수 있습니다; _uiState.update 내부에서 current을 얻은
직후(contentDetailsMap 조회 후) index가 0 이상이고 index < current.contentImageUris.size인지
검사해 범위 밖이면 단순히 state를 반환(변경 없음)하도록 하고, 유효할 때만 current.copy를 만들어
contentImageUris에서 removeAt(index)를 수행하도록 수정하세요; 관련 심볼: removeContentImageUri,
_uiState.update, contentDetailsMap, current, contentImageUris, removeAt.

In
`@app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentImage.kt`:
- Around line 87-96: The Icon for deletion currently has contentDescription =
null which makes it inaccessible to screen readers; update the Icon (in
CollectionCreateContentImage where deletedIndex and onDeleteClick are used) to
provide a meaningful, localized contentDescription (e.g., via stringResource
like a "delete image" label) so assistive tech can identify the action; ensure
the label is descriptive and localized and consider adding a semantic role
(button) if not already present.
- Around line 73-79: The HorizontalPager code computes val index = page %
imageUris.size which will throw a divide-by-zero when imageUris is empty; update
the CollectionCreateContentImage composable to check imageUris.isEmpty() and
return early (or render a safe placeholder) before constructing HorizontalPager
(refer to pagerState, imageUris and the Box that uses index) so the pager is
never created with an empty list.

In
`@app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentReason.kt`:
- Line 74: The image-selection control uses .size(width = 48.dp, height = 28.dp)
and contentDescription = null which violates accessibility touch-target and
description requirements; update the composable (the Image/Icon or IconButton
used in CollectionCreateContentReason) to ensure a minimum touch target of 48.dp
(e.g., use Modifier.size(48.dp) or add
Modifier.minimumInteractiveComponentSize()) and replace contentDescription =
null with a meaningful description (use stringResource like
stringResource(R.string.select_image) or a descriptive literal) so assistive
technologies can convey the button purpose; apply the same changes to the other
occurrences in the same block (lines referenced 78-85).

---

Outside diff comments:
In
`@app/src/main/java/com/flint/presentation/collectioncreate/component/AddContentSelectItem.kt`:
- Around line 60-67: The Icon currently uses contentDescription = null and only
Modifier.clickable, so screen readers can't tell its purpose or selected state;
update the Icon to provide a dynamic contentDescription and semantics by using
isSelected to produce a descriptive string (e.g., "Select item, selected" vs
"Select item, not selected") and add accessibility semantics on the Modifier
(e.g., set role to Checkbox/toggle and set a stateDescription or proper checked
state) while keeping onClick behavior (refer to Icon, isSelected, onClick and
the Modifier.clickable in AddContentSelectItem.kt).
🪄 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: 3e40fda7-1e4e-4869-b62f-9f72068cf5bb

📥 Commits

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

📒 Files selected for processing (12)
  • app/src/main/java/com/flint/data/dto/collection/request/CollectionCreateRequestDto.kt
  • app/src/main/java/com/flint/domain/mapper/collection/CollectionCreateMapper.kt
  • app/src/main/java/com/flint/domain/model/collection/CollectionCreateModel.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/AddContentScreen.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/CollectionCreateScreen.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/CollectionCreateViewModel.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/component/AddContentSelectItem.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentImage.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentReason.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateContentSection.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/component/CollectionCreateThumbnail.kt
  • app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt

  - 갤러리 선택 취소 시 기존 썸네일이 삭제되던 문제 수정
  - contentResolver 호출 시 발생할 수 있는 예외(SecurityException 등) 처리
  - 이미지 업로드 실패 시 생성 요청을 중단하고 실패 상태를 반환하도록 변경
  - 콘텐츠 이미지가 비어있을 때 발생할 수 있는 0으로 나누기 예외 방지
  - removeContentImageUri에서 유효 범위 밖 인덱스로 인한 IndexOutOfBoundsException 방지
@@ -36,6 +43,8 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel

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.

[버그] 갤러리 복귀 시 pendingContentId 유실

remember는 컴포지션이 살아있는 동안만 유지됨. 저사양 기기에서 갤러리 앱을 여는 동안 시스템이 앱 프로세스를 종료하거나 Activity가 재생성되면 pendingContentId가 null로 리셋됨.

결과: 사용자가 갤러리에서 사진을 선택해도 addContentImageUri()가 호출되지 않고 사진이 무음으로 버려짐.

rememberSaveable로 교체하면 해결됨.

// 변경 전
var pendingContentId by remember { mutableStateOf<String?>(null) }

// 변경 후
var pendingContentId by rememberSaveable { mutableStateOf<String?>(null) }

import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue

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.

[버그] 초기 렌더링 시 pager가 마지막 이미지로 점프

prevSizeimageUris.size로 초기화되므로 첫 컴포지션에서 LaunchedEffect가 실행될 때 isAdded = (N > N) = false → 삭제 경로 진입.

targetIndex = minOf(deletedIndex, imageUris.size - 1) = minOf(-1, N-1) = -1

targetPage = base + (-1) → pager가 이미지[N-1]로 이동해 첫 이미지가 아닌 마지막 이미지가 표시됨. 이미지가 2장 이상일 때 항상 재현.

prevSize 초기값을 0으로 바꾸거나, LaunchedEffect를 초기 실행과 크기 변경을 구분해서 처리해야 함.

// 변경 전
var prevSize by remember { mutableIntStateOf(imageUris.size) }

// 변경 후
var prevSize by remember { mutableIntStateOf(0) }

@@ -53,8 +60,18 @@ class CollectionCreateViewModel @Inject constructor(

private fun postCollectionCreate() {
viewModelScope.launch {

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.

[버그] 완료 버튼 빠르게 두 번 탭 시 컬렉션 2개 생성

onClickFinish()postCollectionCreate() 흐름에서 isLoading 같은 중복 실행 방어가 없음.

업로드 + API 호출이 수 초 걸리는 동안 버튼이 비활성화되지 않으므로, 두 번 빠르게 탭하면 viewModelScope.launch 코루틴 2개가 병렬 실행 → 컬렉션이 2개 생성되고 네비게이션도 2번 발생.

isLoading 플래그로 보호 필요.

fun onClickFinish() {
    if (_uiState.value.isLoading) return  // 추가
    postCollectionCreate()
}

private fun postCollectionCreate() {
    viewModelScope.launch {
        _uiState.update { it.copy(isLoading = true) }  // 추가
        // ... 기존 로직
        _uiState.update { it.copy(isLoading = false) }  // 완료 후
    }
}


val imageBytes = runCatching {
withContext(Dispatchers.IO) {
context.contentResolver.openInputStream(uri)?.use { it.readBytes() }

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.

[정리] mimeTypeToFileExtension 3중 복제

동일한 when 블록이 CollectionCreateViewModel, EditProfileViewModel, OnboardingViewModel 세 곳에 각각 private fun으로 존재.

새 포맷(예: image/avif) 지원 시 세 파일을 모두 수정해야 하고 하나라도 누락되면 포맷별 동작이 달라짐.

FileExtension enum에 companion 함수로 추출하는 것을 권장.

// FileExtension.kt
companion object {
    fun fromMimeType(mimeType: String): FileExtension = when (mimeType) {
        "image/png" -> PNG
        "image/gif" -> GIF
        "image/webp" -> WEBP
        else -> JPEG
    }
}

  - 갤러리 선택 중 프로세스/Activity 재생성 시 pendingContentId 유실 방지 (rememberSaveable 적용)
    - 콘텐츠 이미지 페이저가 초기 진입 시 마지막 이미지로 표시되던 문제 수정
  - isLoading 플래그를 추가해 업로드~생성 API 호출 중 완료 버튼을 비활성화하고 onClickFinish 재호출을 차단

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

잘했어요~

@chanmi1125 chanmi1125 merged commit 45e3f53 into develop Jun 11, 2026
2 checks passed
@chanmi1125 chanmi1125 deleted the FLT-15-컬렉션-생성-UI-개편 branch June 11, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔖 API feat - API 연동 Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경 📱 UI feat - 컴포넌트 조립, 화면 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants