Conversation
사진 업로드(갤러리, QR)가 완료된 후, 아카이브 메인 화면이 자동으로 새로고침되도록 기능을 추가했습니다. `MainViewModel`에서 사진 업로드 성공 시 `RefreshArchive` 사이드 이펙트를 발생시키고, 이를 `MainScreen`에서 받아 `ResultBus`를 통해 `PhotoUploaded` 결과를 아카이브 기능으로 전달하여 화면을 갱신합니다.
사진 업로드, 삭제, 즐겨찾기 변경 후 화면 전체를 다시 불러오는 `RefreshArchiveMainScreen` 인텐트를 사진 목록만 새로고치도록 `RefreshArchiveMainPhotos`로 변경하고, 관련 로직을 수정했습니다. 이를 통해 불필요한 전체 데이터 요청을 줄이고 사진 영역만 효율적으로 갱신하도록 개선했습니다.
`ResultEventBus` 클래스에 `@Stable` 어노테이션을 추가하여 Compose 런타임이 해당 클래스를 안정적인 것으로 인식하고 불필요한 리컴포지션을 줄이도록 했습니다. 또한, `channelMap`의 구현을 `mutableMapOf`에서 `mutableStateMapOf`로 변경하여 상태 변경을 Compose가 올바르게 관찰할 수 있도록 수정했습니다.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review infoConfiguration used: Repository UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Walkthrough메인에서 사진 업로드 성공 시 새 사이드이펙트와 결과 타입을 추가해 아카이브에 사진 업로드 이벤트를 전파하고, 아카이브 쪽의 업로드·갤러리·QR 관련 인텐트·사이드이펙트를 정리·단순화했습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant MainScreen as Main 화면
participant MainVM as MainViewModel
participant ResultBus as ResultEventBus
participant ArchiveVM as ArchiveMainViewModel
participant PhotoRepo as PhotoRepository
MainScreen->>MainVM: uploadSingleImage()/uploadMultipleImages()
MainVM->>MainVM: 업로드 성공 처리 (발생: RefreshArchive)
MainVM->>MainScreen: emit MainSideEffect.RefreshArchive
MainScreen->>ResultBus: sendResult(ArchiveResult.PhotoUploaded)
ResultBus->>ArchiveVM: deliver ArchiveResult.PhotoUploaded
ArchiveVM->>ArchiveVM: 처리: ArchiveMainIntent.RefreshArchiveMainPhotos
ArchiveVM->>PhotoRepo: getPhotos(size)
PhotoRepo-->>ArchiveVM: photos 반환
ArchiveVM->>ArchiveVM: 상태 업데이트 (photos)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
아카이빙 메인 화면(ArchiveMain)에서 사진 업로드와 관련된 로직을 제거했습니다. 주요 변경 사항: - `ArchiveMainViewModel`에서 `UploadSinglePhotoUseCase`, `UploadMultiplePhotoUseCase` 및 관련 업로드 메서드를 삭제했습니다. - 사진 선택(갤러리, QR 스캔 결과) 및 앨범 지정 여부를 묻는 다이얼로그 관련 상태와 인텐트 처리를 제거했습니다. - `ArchiveMainScreen`과 `ArchiveEntryProvider`에서 더 이상 사용하지 않는 업로드 관련 탐색 로직과 UI 컴포넌트(`SelectWithAlbumDialog` 등)를 삭제했습니다. - 이 변경으로 인해 아카이빙 화면은 사진 조회 및 앨범 관리에만 집중하게 됩니다.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt (1)
76-87:⚠️ Potential issue | 🟠 MajorPhotoUploaded 이벤트가 현재 화면에서 소모되어 ArchiveMain 갱신을 놓칠 수 있습니다.
Line 76-87, Line 115-126의
else -> {}는ArchiveResult.PhotoUploaded도 no-op 처리합니다.
ResultEffect기본값(withRemove = true)에서는onResult이후 결과가 제거되므로, AllPhoto/AlbumDetail이 활성 상태일 때 업로드 이벤트가 ArchiveMain까지 전달되지 않을 수 있습니다.
PhotoUploaded를 별도 결과 키/타입으로 분리하거나, 해당 이벤트를 이 화면들에서 소비하지 않도록 처리 구조를 분리하는 쪽이 안전합니다.Also applies to: 115-126
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt` around lines 76 - 87, 현재 ResultEffect<ArchiveResult> 블록에서 else -> {}로 ArchiveResult.PhotoUploaded를 no-op 처리해 결과가 제거되어 ArchiveMain이 갱신을 놓칠 수 있습니다; 수정 방법은 해당 화면에서 PhotoUploaded를 소비하지 않도록 명시적으로 처리하는 것입니다 — 즉, ResultEffect 생성자에서 withRemove = false로 설정하거나(결과 제거 방지) 해당 when에 is ArchiveResult.PhotoUploaded 분기를 추가해 이벤트를 무시하되 제거되지 않도록 처리하도록 변경하세요; 관련 식별자: ResultEffect<ArchiveResult>, ArchiveResult.PhotoUploaded, withRemove, AllPhotoIntent(현재 소비되는 블록)을 참고하여 코드를 수정하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt`:
- Around line 76-87: 현재 ResultEffect<ArchiveResult> 블록에서 else -> {}로
ArchiveResult.PhotoUploaded를 no-op 처리해 결과가 제거되어 ArchiveMain이 갱신을 놓칠 수 있습니다; 수정
방법은 해당 화면에서 PhotoUploaded를 소비하지 않도록 명시적으로 처리하는 것입니다 — 즉, ResultEffect 생성자에서
withRemove = false로 설정하거나(결과 제거 방지) 해당 when에 is ArchiveResult.PhotoUploaded 분기를
추가해 이벤트를 무시하되 제거되지 않도록 처리하도록 변경하세요; 관련 식별자: ResultEffect<ArchiveResult>,
ArchiveResult.PhotoUploaded, withRemove, AllPhotoIntent(현재 소비되는 블록)을 참고하여 코드를
수정하세요.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
core/navigation/src/main/java/com/neki/android/core/navigation/result/ResultEventBus.ktfeature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainContract.ktfeature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.ktfeature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainViewModel.ktfeature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt
💤 Files with no reviewable changes (1)
- feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
1 similar comment
✅ Actions performedReview triggered.
|
Ojongseok
left a comment
There was a problem hiding this comment.
고생하셨습니다-!
아카이빙쪽 ViewModel/Contract 에서 업로드 관련 코드가 제거되고, Main쪽으로 이동하면서 아카이빙쪽 코드가 깔끔해졌네요! 오히려 업로드 관련 로직은 Main에서 처리되는게 적절한 것 같기도 하구요.
카톡에서 이야기 나눈 것 처럼 ArchiveMain에 대한 Result 처리는 이후에 진행하시죠!
(아카이빙 외 다른 탭에서도 FAB 통한 이미지 추가 시 정상적으로 추가되는 것 확인했습니다)
core/navigation/src/main/java/com/neki/android/core/navigation/result/ResultEventBus.kt
Outdated
Show resolved
Hide resolved
| * It provides a solution for event based results. | ||
| */ | ||
| // https://github.com/android/nav3-recipes/blob/main/app/src/main/java/com/example/nav3recipes/results/event/README.md | ||
| @Stable |
There was a problem hiding this comment.
현재 상태에서 ResultEventBus.channelMap의 밸류에 직접 접근하는 부분이 없어 문제되지는 않을 것 같지만 Map의 밸류로 Cannel 타입을 갖기 때문에 @Stable 조건을 만족하지 못하는 것 같습니다!?
There was a problem hiding this comment.
지적 감사합니다!
말씀하신 대로 Channel이 @stable 조건을 엄밀히 만족하지 않는 건 맞습니다. 다만 @stable을 제거하면 ResultEventBus 자체가 unstable로 추론되어 불필요한 recomposition이 발생할 수 있습니다.
MainRoute 에서 리컴포지션이 발생하면, ResultEffect 는 unstable 해지기 때문에 반드시 리컴포지션이 발생합니다.

따라서 해당 ResultEventBus 클래스를 stable 하진 않지만 @Stable 하다고 명시하여 불필요한 리컴포지션을 줄일 수 있습니다!
매번 새로운 인스턴스를 반환하지 않고, 매번 같은 인스턴스를 사용하지만 unstable 하기에 @stable 추가.
There was a problem hiding this comment.
말씀하신 부분은 이해가 되어 로그를 통해 실제로 리컴포지션을 줄일 수 있는지 확인을 해 보았는데요. ResultEffect의 경우 @Stable 어노테이션에 관계 없이 리컴포지션이 동일하게 발생하는 것 같습니다.?.?
제가 확인한 바로는 ResultEffect의 경우 inline 함수이고, MainRoute에서 리컴포지션이 발생하면 독립적인 리컴포지션의 대상이 아니기 때문에(inline 함수의 경우 접근할 때 그대로 함수를 가지고 오기 때문에) 항상 다시 작성되는 것으로 이해했습니다.
결국 ResultEffect() 내부의 LaunchedEffect 구문에 설정된 키의 영향을 받는데 키로 설정된 resultKey, resultEventBus.channelMap[resultKey]는 MainRoute가 리컴포지션 되더라도 각각 String과 Channel<Any?> 타입이기 때문에 MainRoute의 리컴포지션과는 무관하게 설정된 키의 변화에 따라 LaunchedEffect 블럭의 코드가 트리거 되는 것 같습니다.
제가 확인하지 못한 MainRoute의 리컴포지션 시에 해당 키의 변화가 있는지, 추가적으로 확인한 내용이 올바르게 이해한 것인지는 모르겠네요...! 리컴포지션 어렵다어려워
There was a problem hiding this comment.
아하, inline 함수가 된다면 실제 코드에서 이렇게 반영이 되는군요,
그래서 말씀하신 것처럼 ResultEffect 가 내부 함수로 펼쳐지기(?) 때문에 리컴포지션 대상 자체가 안되네요!
inline 이 기본 함수에만 작동되는 줄 알았는데 코틀린 기본 개념처럼 진짜 코드 인라인에 복사되는 거기 때문에 실행 환경에선 ResultEffect 가 없어진 거나 마찬가지가 되어 해당 함수의 Stability 를 설정할 필요가 없네요.
(무작정 복붙 가져오기의 폐해...)
LaunchedEffect 의 키에는 ResultEventBus 인스턴스 자체는 상관이 없으니 @Stable 어노테이션이 의미가 없겠네요.
fun MainRoute(...) {
val resultBus = LocalResultEventBus.current
// ...
// ---- ResultEffect가 여기에 펼쳐짐 ----
Log.d("ResultEffect", "Recompose")
LaunchedEffect(resultKey, resultBus.channelMap[resultKey]) {
resultBus.getResultFlow<QRScanResult>(resultKey)?.collect { result ->
// ...
}
}
// ---- 여기까지 ----
// ...
}
08b4282 불필요한 @Stable 어노테이션 제거했습니다!
`ResultEventBus` 클래스에 적용되어 있던 `@Stable` 어노테이션과 관련 임포트를 제거했습니다.
🔗 관련 이슈
📙 작업 설명
Main에서Result를 먼저collect하여 가로채기 때문에ArchiveMain쪽에서의 갱신 로직에 다다를 수 없었습니다.Main에서PhotoUpload Result를 받으면,ArchiveMain쪽으로Result를 보내도록 적용했습니다.ArchiveMain쪽에서Result를 받을 가능성이 있으니when으로 처리하도록 했습니다.dead code가 되어서 제거했습니다.아래 케이스들 모두 체크 완료했습니다. (앨범에 추가까지)
📸 스크린샷 또는 시연 영상 (선택)
default.mp4
QR.mp4
💬 추가 설명 or 리뷰 포인트 (선택)
ArchiveMain이 먼저 생긴 상태에서, 그 시점의channelMap을 캡처해LaunchedEffect에서 체크합니다.channelMap은 상태가 아니기 때문에 해당 Map 의 변화가 생겨도LaunchedEffect에서 자동적으로 체크하지 못합니다.mutableStateMapOf으로channelMap을 수정해주었고,ResultEventBus에@Stable어노테이션을 추가했습니다.Summary by CodeRabbit
새로운 기능
개선 사항
제거