Skip to content

[Feat] #66 미지원 브랜드 QR 스캔 시 대응 기능 구현#67

Merged
ikseong00 merged 9 commits into
developfrom
feat/#66-unsupported-brand-qr
Jan 29, 2026
Merged

[Feat] #66 미지원 브랜드 QR 스캔 시 대응 기능 구현#67
ikseong00 merged 9 commits into
developfrom
feat/#66-unsupported-brand-qr

Conversation

@ikseong00
Copy link
Copy Markdown
Contributor

@ikseong00 ikseong00 commented Jan 28, 2026

🔗 관련 이슈

📙 작업 설명

  • SingleButtonWithTextButtonAlertDialog 컴포넌트 구현
  • QR 스캔 결과 미지원 브랜드 판별 시 대응 다이얼로그 표시
  • 브랜드 지원 여부 확인 로직 뷰모델로 이동
  • "갤러리에서 추가하기" 클릭 시 ResultEventBus를 통해 홈으로 이동 후 갤러리 열기
  • "브랜드 제안하기" 클릭 시 Tally 설문 URL로 이동
  • detekt FunctionOnlyReturningConstant 규칙 비활성화

📷 스크린샷

미지원 브랜드 다이얼로그 브랜드 제안하기
default.mp4
QR.mp4

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

QRScanResult

  • photo-upload:api 에 QRScanResult 를 선언했습니다.
  • 원시타입을 주고받기 보다는 필요에 따라 Result 타입을 선언하는 것이 이후 확장에도 유리할 것 같다고 생각했습니다.
  • 의존성도 결과를 보내주는 쪽에 선언하여 단방향을 유지할 수 있습니다.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 지원되지 않는 브랜드 감지 시 새로운 대화 상자 추가 (갤러리 업로드 및 브랜드 제안 옵션 포함)
    • QR 스캔 플로우에 갤러리 업로드 기능 통합
    • 브랜드 제안 URL 기능 추가
  • 개선

    • 대화 상자 너비 및 해제 동작 최적화

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 28, 2026

개요

QR 코드 스캔 시 지원하지 않는 브랜드 대응 로직을 구현했습니다. 다이얼로그 컴포넌트를 추가하고, 브랜드 지원 여부에 따른 조건부 흐름을 추가했으며, 갤러리 업로드 및 브랜드 제안 기능을 통합했습니다.

변경 사항

코호트 / 파일 변경 요약
디자인 시스템 다이얼로그
core/designsystem/src/main/java/com/neki/android/core/designsystem/dialog/DoubleButtonAlertDialog.kt, SingleButtonAlertDialog.kt
DialogProperties 기본값에 usePlatformDefaultWidth = false 추가하여 플랫폼 기본 너비 비활성화
새로운 다이얼로그 컴포넌트
core/designsystem/src/main/java/com/neki/android/core/designsystem/dialog/SingleButtonWithTextButtonAlertDialog.kt
기본 버튼과 텍스트 버튼을 함께 제공하는 새로운 알림 다이얼로그 추가 (128줄)
QR 스캔 결과 타입 정의
feature/photo-upload/api/src/main/java/com/neki/android/feature/photo_upload/api/QRScanResult.kt
스캔 결과와 갤러리 오픈 액션을 나타내는 sealed interface QRScanResult 추가
QR 스캔 상태 및 의도
feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/QRScanContract.kt
isShowInfoDialogisShowShouldDownloadDialog, isShowUnSupportedBrandDialog로 변경; 새로운 의도 및 사이드 이펙트 추가
QR 스캔 뷰모델
feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/QRScanViewModel.kt
브랜드 지원 여부 판단 로직 추가; 조건부 다이얼로그 표시 및 갤러리/제안 의도 처리 (+45/-6)
QR 스캔 화면 및 라우트
feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/QRScanScreen.kt
navigateToHomesetQRResult 파라미터로 변경; 사이드 이펙트 핸들링 확대 및 다이얼로그 UI 추가 (+48/-3)
QR 이미지 분석기
feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/util/QRImageAnalyzer.kt
URL 필터링 제거; 모든 스캔된 QR 코드를 처리하도록 변경
네비게이션 통합
feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt, feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/di/PhotoUploadEntryProvider.kt
QRScanResult 타입으로 통합; 결과 버스를 통한 새로운 결과 전달 방식
빌드 설정
feature/photo-upload/impl/build.gradle.kts, detekt-config.yml
BRAND_PROPOSAL_URL BuildConfig 필드 추가; Detekt 설정 업데이트

시퀀스 다이어그램

sequenceDiagram
    actor User
    participant QRImageAnalyzer as QRImageAnalyzer
    participant QRScanViewModel as QRScanViewModel
    participant QRScanScreen as QRScanScreen
    participant Dialog as Dialog UI
    
    User->>QRImageAnalyzer: QR 코드 스캔
    QRImageAnalyzer->>QRScanViewModel: ScanQRCode(scannedUrl)
    
    alt 지원하는 브랜드
        QRScanViewModel->>QRScanViewModel: isSupportedBrand() 확인
        alt 먼저 다운로드 필요
            QRScanViewModel->>QRScanScreen: isShowShouldDownloadDialog = true
            QRScanScreen->>Dialog: 다운로드 다이얼로그 표시
            User->>Dialog: 다운로드 클릭
            QRScanViewModel->>QRScanScreen: WEB_VIEW로 전환
        else 바로 실행 가능
            QRScanViewModel->>QRScanScreen: WEB_VIEW로 전환
        end
    else 지원하지 않는 브랜드
        QRScanViewModel->>QRScanScreen: isShowUnSupportedBrandDialog = true
        QRScanScreen->>Dialog: 지원 안 함 다이얼로그 표시
        alt 갤러리 업로드
            User->>Dialog: 갤러리 오픈 클릭
            QRScanViewModel->>QRScanScreen: SetOpenGalleryResult
        else 브랜드 제안
            User->>Dialog: 제안하기 클릭
            QRScanViewModel->>QRScanScreen: OpenBrandProposalUrl
        end
    end
Loading

예상 코드 리뷰 소요 시간

🎯 4 (Complex) | ⏱️ ~45 minutes

관련 PR

제안 라벨

feat

제안 리뷰어

  • Ojongseok

축하 시

🐰 QR 코드가 살짝 혼란해도 괜찮아,
다이얼로그로 친절히 안내해주지!
브랜드 지원 확인, 갤러리 오픈, 제안까지—
스캔 흐름이 더욱 똑똑해졌네요 ✨
모든 경로를 우아하게 처리하는 로직이라니!

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 미지원 브랜드 QR 스캔 대응 기능 구현이라는 주요 변경사항을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR이 issue #66의 모든 코딩 요구사항을 충족합니다: 미지원 브랜드 팝업 표시, 갤러리 오픈, 브랜드 제안 기능이 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 issue #66 범위 내입니다. 디자인 시스템 다이얼로그 업데이트, QRScanResult 타입 추가, ViewModel 로직 강화는 모두 미지원 브랜드 대응 기능 구현에 필요한 변경입니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

🤖 Fix all issues with AI agents
In `@detekt-config.yml`:
- Around line 57-58: 현재 detekt 설정에서 FunctionOnlyReturningConstant 규칙을 전역
비활성화(active: false)한 대신 해당 규칙을 다시 활성화하고(삭제하거나 true로 변경) 상수만 반환하는 특정 함수들에
`@Suppress`("FunctionOnlyReturningConstant")를 적용하도록 변경하세요; 특히 Composable preview
함수나 예외가 필요한 함수들(예: `@Preview` 또는 미리보기 관련 함수명)에만 `@Suppress를` 추가하고, 이미 사용 중인
ignoreAnnotated 패턴이 있다면 동일한 방식으로 해당 주석을 허용하도록 detekt 설정에 반영하세요.

In
`@feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/component/QRScannerContent.kt`:
- Line 54: QRScannerContent 컴포저블 본문에서 직접 onQRCodeScanned("")를 호출하고 있으니 해당 호출을
제거하고 사이드 이펙트로 옮기세요: Composable 내부에서 onQRCodeScanned("") 라인(테스트/디버그 호출)을 삭제하고, 실제
초기 호출이 필요하면 QRScannerContent(…)의 상태/결과가 변경될 때만 실행되도록 LaunchedEffect 또는
rememberUpdatedState+SideEffect 패턴을 사용해 트리거하세요; 대상 심볼: onQRCodeScanned,
QRScannerContent(컴포저블) — 빈 문자열 직접 전달을 제거하고 실제 스캔 결과가 준비되었을 때만 콜백을 호출하도록 변경하세요.

In
`@feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/QRScanScreen.kt`:
- Around line 43-46: The OpenBrandProposalUrl handling currently calls
context.startActivity(...) directly and can crash if no browser can handle the
intent; in the QRScanSideEffect.OpenBrandProposalUrl branch, build the
Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.BRAND_PROPOSAL_URL)) and call
intent.resolveActivity(context.packageManager) before startActivity; if
resolveActivity returns null, do not start the activity and instead show a safe
failure UI (e.g., Toast) to inform the user that no browser is available or the
link cannot be opened.

In
`@feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/QRScanViewModel.kt`:
- Around line 59-60: The ClickGoDownload intent handler in QRScanViewModel only
sets viewType but doesn't close the dialog, so update the
QRScanIntent.ClickGoDownload branch (where reduce { copy(viewType =
QRScanViewType.WEB_VIEW) } is used) to also set isShowShouldDownloadDialog =
false (e.g., reduce { copy(viewType = QRScanViewType.WEB_VIEW,
isShowShouldDownloadDialog = false) }) so the dialog is dismissed when
navigating to the download view.
🧹 Nitpick comments (4)
feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt (1)

48-57: 타입 안전한 QR 스캔 결과 처리로의 리팩토링이 잘 되었습니다.

String 대신 sealed interface QRScanResult를 사용하여 타입 안전성을 높이고, when 표현식을 통해 컴파일 타임에 모든 케이스를 처리하도록 보장한 점이 좋습니다.

사소한 스타일 제안: Line 53의 빈 줄은 when 블록 내에서 불필요해 보입니다. 제거하면 코드가 더 간결해집니다.

,

🧹 선택적 스타일 개선
         ResultEffect<QRScanResult>(resultBus) { result ->
             when (result) {
                 is QRScanResult.QRCodeScanned -> {
                     viewModel.store.onIntent(ArchiveMainIntent.QRCodeScanned(result.imageUrl))
                 }
-
                 QRScanResult.OpenGallery -> {
                     viewModel.store.onIntent(ArchiveMainIntent.ClickGalleryUploadRow)
                 }
             }
         }
core/designsystem/src/main/java/com/neki/android/core/designsystem/dialog/DoubleButtonAlertDialog.kt (1)

40-42: DialogProperties 기본값이 다른 다이얼로그들과 불일치합니다.

SingleButtonAlertDialogSingleButtonWithTextButtonAlertDialogdismissOnBackPress = false, dismissOnClickOutside = false를 기본값으로 설정하지만, 이 다이얼로그는 usePlatformDefaultWidth = false만 설정합니다.

의도된 차이라면 무시해도 되지만, 일관성을 위해 확인이 필요합니다.

♻️ 일관성을 위한 제안
     properties: DialogProperties = DialogProperties(
         usePlatformDefaultWidth = false,
+        dismissOnBackPress = false,
+        dismissOnClickOutside = false,
     ),
core/designsystem/src/main/java/com/neki/android/core/designsystem/dialog/SingleButtonWithTextButtonAlertDialog.kt (1)

97-105: 접근성 개선을 위해 semantics 추가를 고려해 보세요.

clickableSingle 수정자만으로는 스크린 리더가 이 요소를 버튼으로 인식하지 못할 수 있습니다.

♻️ 접근성 개선 제안
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
...
                 Text(
                     modifier = Modifier
                         .padding(bottom = 8.dp)
-                        .clickableSingle(onClick = onTextButtonClick),
+                        .semantics { role = Role.Button }
+                        .clickableSingle(onClick = onTextButtonClick),
                     text = textButtonText,
                     style = NekiTheme.typography.body14Regular,
                     color = NekiTheme.colorScheme.primary600,
                     textDecoration = TextDecoration.Underline,
                 )
feature/photo-upload/impl/build.gradle.kts (1)

23-23: BRAND_PROPOSAL_URL 누락 여부를 빌드 단계에서 보장해주세요.
local.properties/CI에 값이 없으면 BuildConfig에 "null" 문자열이 들어가 잘못된 URI가 될 수 있습니다. 존재 검증(없으면 빌드 실패)이나 기본값 처리 확인이 필요합니다.

Comment thread detekt-config.yml
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.

고생하셨습니다-!

디자인 시스템 내 다이어로그 컴포넌트에

properties: DialogProperties = DialogProperties(
    usePlatformDefaultWidth = false,
),

해당 속성을 추가하자고 말씀을 드리려 했는데 변경해주셨군요. 제가 사용하고 있는 다이어로그에서 해당 속성 설정하고 있는 부분도 차차 제거하겠습니다.

Comment on lines +72 to +74
private fun isShouldFirstDownloadBrand(url: String): Boolean {
return false
}
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.

다운로드를 해야 앱에 저장되는 브랜드 url을 구분하는 함수로 보이는데 이후 추가될 예정인가요?

함수명의 First라는 워딩이 어떤 의미인지 궁금합니다.

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.

다운로드를 해야 앱에 저장되는 브랜드 url을 구분하는 함수로 보이는데 이후 추가될 예정인가요?

네 맞습니다.
추후 다운로드 되어야하는 브랜드 URL 을 삽입하기만 하면 되도록 구현했습니다.

함수명의 First라는 워딩이 어떤 의미인지 궁금합니다.

먼저 저장되어야한다, 라는 의미로 작성했습니다. First라는 단어를 빼는 게 좋을까요?

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.

다운로드를 해야 하는 브랜드의 경우 '먼저 다운로드'의 개념 보다는 사실 '반드시 다운로드'해야 앱에도 저장되기 때문에 First를 제외한 isShouldDownloadBrand()의 네이밍이 좀 더 적절하지 않나 생각이 드네요!

"다운로드 하러가기" 버튼 클릭 시, `isShowShouldDownloadDialog`를 `false`로 설정하여 QR 코드 스캔 후 나타나는 다운로드 안내 팝업이 정상적으로 닫히도록 수정합니다.
`android.net.Uri`의 `parse()` static 메서드를 사용하는 대신, `androidx.core.net`의 `String.toUri()` 확장 함수를 사용하도록 수정합니다.
@ikseong00 ikseong00 merged commit ee15c53 into develop Jan 29, 2026
6 checks passed
@ikseong00 ikseong00 deleted the feat/#66-unsupported-brand-qr branch February 5, 2026 10:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 지원하지 않는 QR 브랜드 대응 로직 구현

2 participants