Skip to content

NR-142 구독 상태때 등록한 이미지가 남아있는 경우 테마 수정 시도시 토스트를 띄운다.#143

Merged
juhwankim-dev merged 1 commit into
developfrom
bugfix/NR-142
May 30, 2026
Merged

NR-142 구독 상태때 등록한 이미지가 남아있는 경우 테마 수정 시도시 토스트를 띄운다.#143
juhwankim-dev merged 1 commit into
developfrom
bugfix/NR-142

Conversation

@juhwankim-dev

Copy link
Copy Markdown
Member

왜?
구독 상태때 등록한 이미지가 남아있는경우
테마 정보를 수정하려 시도할때 이미지 정보를 포함하여 서버에 post 요청을 보내기 때문에 구독 상태가 아니여서 api 요청을 실패한다.

이미지를 삭제하고 수정을 해야함을 사용자에게 안내하는 방향으로 수정한다.

왜?
구독 상태때 등록한 이미지가 남아있는경우
테마 정보를 수정하려 시도할때 이미지 정보를 포함하여 서버에 post 요청을 보내기 때문에
구독 상태가 아니여서 api 요청을 실패한다.

이미지를 삭제하고 수정을 해야함을 사용자에게 안내하는 방향으로 수정한다.
@juhwankim-dev juhwankim-dev self-assigned this May 30, 2026
@github-actions

Copy link
Copy Markdown

🤖 AI Code Review (Gemini)

terminal-notifier -title "Claude Code" -message "코드 리뷰를 시작합니다." -sound default

안녕하세요! NR-142 PR에 대한 코드 리뷰를 진행하겠습니다.

제출하신 변경 사항은 구독 상태가 아닌 사용자가 이미지가 포함된 힌트를 수정하려 할 때 발생하는 API 요청 실패 문제를 잘 해결하고 있습니다. API 요청을 보내기 전에 유효성 검사를 추가하여 사용자에게 적절한 피드백을 제공하는 방식이 좋습니다.


🟢 전반적인 평가

제안된 변경 사항은 문제 설명을 정확히 반영하고 있으며, Clean Architecture 및 Orbit MVI 패턴을 준수하여 구현되었습니다. 특정 조건에서 불필요한 API 호출을 방지하고 사용자 경험을 개선하는 데 기여합니다.


📝 코드 리뷰

File: presentation/src/main/java/com/nextroom/nextroom/presentation/ui/manage/hint/HintManageFragment.kt

@@ -72,6 +72,7 @@ class HintManageFragment : ComposeBaseViewModelFragment<HintManageViewModel>() {
                             is HintManageEvent.RequestDeleteHint -> showDeleteConfirmDialog(event.hint)
                             is HintManageEvent.HintSaved -> snackbar(R.string.hint_manage_save_success)
                             is HintManageEvent.HintDeleted -> snackbar(R.string.hint_manage_delete_success)
+                            is HintManageEvent.SaveBlockedDueToImages -> toast(R.string.hint_save_blocked_due_to_images)
                             is HintManageEvent.ImageUploadFailed -> {
                                 when (event.reason) {
                                     HintManageEvent.ImageUploadFailed.Reason.INVALID_FORMAT -> R.string.image_invalid_format
  • 💡 Suggestion: ComposeBaseViewModelFragment를 사용하고 있으므로, snackbartoast 대신 Compose의 SnackbarHost 또는 Toast 컴포저블을 활용하는 것이 더 일관성 있는 UI 경험을 제공할 수 있습니다. 현재는 기존 toast 확장 함수를 사용하고 있으므로 기능상 문제는 없습니다.

File: presentation/src/main/java/com/nextroom/nextroom/presentation/ui/manage/hint/HintManageState.kt

@@ -57,6 +57,7 @@ sealed interface HintManageEvent {
     data class RequestDeleteHint(val hint: Hint) : HintManageEvent
     data object HintSaved : HintManageEvent
     data object HintDeleted : HintManageEvent
+    data object SaveBlockedDueToImages : HintManageEvent
     data class ImageUploadFailed(val reason: Reason) : HintManageEvent {
         enum class Reason {
             INVALID_FORMAT,
  • ✅ Good: data object를 사용하여 payload가 없는 단일 이벤트를 명확하게 정의한 것은 Kotlin의 좋은 관용구입니다. Orbit MVI의 SIDE_EFFECT로 적절하게 사용되었습니다.

File: presentation/src/main/java/com/nextroom/nextroom/presentation/ui/manage/hint/HintManageViewModel.kt

@@ -196,6 +196,15 @@ class HintManageViewModel @Inject constructor(
 
     fun saveHint() {
         val editData = _editData.value
+        if (editData.hintId != null &&
+            adminRepository.cachedSubscribeStatus != SubscribeStatus.Subscribed &&
+            (editData.hintImageUrls.isNotEmpty() || editData.answerImageUrls.isNotEmpty())
+        ) {
+            baseViewModelScope.launch {
+                _uiEvent.emit(HintManageEvent.SaveBlockedDueToImages)
+            }
+            return
+        }
         _sheetType.value = HintSheetType.None
         baseViewModelScope.launch {
             try {
  • ✅ Good:
    • editData.hintId != null 조건을 통해 기존 힌트 수정 시에만 이 로직이 적용되도록 한 점이 적절합니다. 새로운 힌트 생성 시에는 이미지가 없으므로 이 검사가 필요 없습니다.
    • adminRepository.cachedSubscribeStatus != SubscribeStatus.Subscribed와 이미지 존재 여부(isNotEmpty())를 함께 검사하여 정확한 시나리오를 포착합니다.
    • _uiEvent.emit(HintManageEvent.SaveBlockedDueToImages)를 통해 UI에 단발성 이벤트를 전달하는 Orbit MVI 패턴을 올바르게 사용했습니다.
    • 검사 후 return을 통해 불필요한 API 호출을 방지하는 로직 흐름이 명확합니다.
  • 💡 Suggestion: adminRepository.cachedSubscribeStatusdata 레이어의 AdminRepository 인터페이스를 통해 접근하는 domain 레이어의 추상화된 캐시된 상태일 것으로 예상됩니다. 만약 adminRepositorydata 레이어의 구체적인 구현체라면, ViewModel에서 직접 data 레이어 구현체를 참조하는 것은 Clean Architecture의 원칙에 약간 벗어날 수 있습니다. 하지만 domain 레이어에 AdminRepository 인터페이스가 정의되어 있고, ViewModel이 이 인터페이스를 주입받는다면 문제는 없습니다. 현재 프로젝트 구조상 Repository 인터페이스는 domain에 정의되어 있으므로 큰 문제는 없을 것으로 보입니다.
  • 🟡 Warning: adminRepository.cachedSubscribeStatus의 캐시 갱신 주기에 대한 고려가 필요합니다. 만약 사용자가 앱을 사용 중 구독 상태가 변경되었을 때, 이 캐시 값이 즉시 갱신되지 않는다면 일시적으로 잘못된 상태로 동작할 수 있습니다. 이는 이 PR의 범위 밖의 문제이지만, 장기적인 관점에서 cachedSubscribeStatus의 신뢰성을 높이는 방안(예: Flow를 통한 실시간 업데이트 또는 주기적인 갱신)을 고려해볼 수 있습니다.

File: presentation/src/main/res/values/strings.xml

@@ -296,4 +296,5 @@
     <string name="image_invalid_size">이미지 크기는 10MB 이하여야 합니다</string>
     <string name="image_invalid_format">지원하지 않는 이미지 형식입니다</string>
     <string name="feature_for_subscriber">구독이 필요한 기능입니다.</string>
+    <string name="hint_save_blocked_due_to_images">구독이 만료되었습니다. 이미지를 삭제한 후 저장해 주세요.</string>
 </resources>
\ No newline at end of file
  • ✅ Good: 사용자에게 명확한 안내 메시지를 제공하는 스트링 리소스가 추가되었습니다.
  • 💡 Suggestion: strings.xml 파일의 마지막 라인에 개행 문자를 추가하는 것이 좋습니다. (현재 \ No newline at end of file로 표시됨)

🚀 결론

제출하신 코드는 문제 해결에 효과적이며, 프로젝트의 아키텍처 및 코딩 가이드라인을 잘 따르고 있습니다. adminRepository.cachedSubscribeStatus의 갱신 주기에 대한 고려는 장기적인 개선 사항으로 남겨두고, 이 PR 자체는 승인해도 무방하다고 판단됩니다.


terminal-notifier -title "Claude Code" -message "코드 리뷰를 완료했습니다." -sound default


This review was automatically generated using Gemini AI. Please use your judgment when addressing the feedback.

@juhwankim-dev

Copy link
Copy Markdown
Member Author

구독 캐싱 정책에 대한것은 의도된 것이므로 수정하지 않고 머지합니다.

@juhwankim-dev juhwankim-dev merged commit b468ef8 into develop May 30, 2026
1 check passed
@juhwankim-dev juhwankim-dev deleted the bugfix/NR-142 branch May 30, 2026 06:55
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.

1 participant