Skip to content

fix: 시스템 권한 팝업에서 허용 시 카메라 프리뷰 안보이는 문제 수정#131

Merged
seoyoon513 merged 1 commit intodevelopfrom
BOOK-245-fix/#124
Aug 9, 2025
Merged

fix: 시스템 권한 팝업에서 허용 시 카메라 프리뷰 안보이는 문제 수정#131
seoyoon513 merged 1 commit intodevelopfrom
BOOK-245-fix/#124

Conversation

@seoyoon513
Copy link
Copy Markdown
Contributor

@seoyoon513 seoyoon513 commented Aug 9, 2025

🔗 관련 이슈

📙 작업 설명

  • 시스템 권한 팝업에서 허용 시 카메라 프리뷰 안보이는 문제 수정

🧪 테스트 내역

  • 주요 기능 정상 동작 확인
  • 브라우저/기기에서 동작 확인
  • 엣지 케이스 테스트 완료
  • 기존 기능 영향 없음

📸 스크린샷 또는 시연 영상

Reed_OCR_._.mp4

💬 추가 설명 or 리뷰 포인트

  • 카메라 권한 허용 시 프리뷰 즉시 표시되도록 isGranted를 상태로 변경했습니다
  • onResume에서 해당 상태를 동기화하는 방식으로 수정했어요!

Summary by CodeRabbit

  • 개선 사항
    • 카메라 권한 상태가 실시간으로 반영되어, 권한 부여 시 카메라 미리보기가 자동으로 활성화됩니다.
    • 앱이 다시 활성화될 때 카메라 권한 상태가 UI에 즉시 반영됩니다.
    • 권한이 없을 경우 카메라가 자동으로 해제되어 불필요한 리소스 사용이 줄어듭니다.
    • 상태바 색상 등 시스템 UI 설정이 더욱 안정적으로 적용됩니다.

@github-actions github-actions Bot added the 🐞 fix Something isn't working label Aug 9, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 9, 2025

Walkthrough

카메라 권한 관리 및 라이프사이클 처리가 리액티브 상태 기반으로 리팩토링되었습니다. 권한 상태를 추적하는 방식이 변경되고, 권한이 허용된 경우에만 카메라 바인딩 및 이미지 분석기가 동작하도록 DisposableEffect로 분리되었습니다. 시스템 UI 컨트롤러 설정도 별도 블록으로 이동되었습니다.

Changes

Cohort / File(s) Change Summary
카메라 권한 및 라이프사이클 리팩토링
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt
카메라 권한 상태를 mutableState로 관리, 권한 요청 런처 콜백 구조 개선, 권한 허용 시에만 카메라 바인딩 및 이미지 분석기 설정, DisposableEffect로 라이프사이클 및 UI 컨트롤러 관리 분리, 내부 상태 관리 및 코드 구조 개선

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CameraPreview(Composable)
    participant PermissionLauncher
    participant CameraController

    User->>CameraPreview: 진입
    CameraPreview->>PermissionLauncher: 권한 요청
    PermissionLauncher-->>CameraPreview: 권한 허용/거부 결과
    alt 권한 허용됨
        CameraPreview->>CameraController: 카메라 바인딩 및 이미지 분석기 설정
    else 권한 거부됨
        CameraPreview-->>User: 권한 필요 안내
    end
    User->>CameraPreview: 앱 재진입/Resume
    CameraPreview->>PermissionLauncher: 권한 상태 동기화
    CameraPreview->>CameraController: (필요시) 바인딩/해제
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Assessment against linked issues

Objective Addressed Explanation
시스템 권한 팝업에서 허용 시 카메라 프리뷰 안뜨는 문제 수정 (#124)
카메라 프리뷰 영역을 벗어난 부분까지 텍스트 인식되는 문제 수정 (#124) 해당 영역 제한 관련 코드 변경 없음
인식률 높이기 위한 발악 해보기 (#124) 인식률 개선 관련 코드 변경 없음

Assessment against linked issues: Out-of-scope changes

(해당 사항 없음)

Poem

토끼가 깡충, 코드를 뒤적
권한 허용, 카메라가 번쩍!
상태를 기억해, 프리뷰가 반짝
라이프사이클 따라, 렌즈가 찰칵
버그는 안녕, OCR은 활짝!
🐇📸✨

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BOOK-245-fix/#124

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🔭 Outside diff range comments (1)
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt (1)

124-138: ON_RESUME 동기화는 좋고, 키와 주석 개선 제안

DisposableEffect 키를 Unit 대신 lifecycleOwner로 두면 NavBackStack 변경 등 라이프사이클 소유자 변경에도 안전합니다. 또한 주석 오타(‘북귀’ → ‘복귀’) 수정 권장.

적용 diff:

-    // 앱이 포그라운드로 북귀할 때 OS 권한 동기화
-    DisposableEffect(Unit) {
+    // 앱이 포그라운드로 복귀할 때 OS 권한 동기화
+    DisposableEffect(lifecycleOwner) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99b14d8 and 5684370.

📒 Files selected for processing (1)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#46
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/component/InfiniteLazyColumn.kt:83-95
Timestamp: 2025-07-14T00:46:03.843Z
Learning: seoyoon513과 팀은 한국어 주석을 선호하며, 한국어 주석을 영어로 번역하라는 제안을 하지 않아야 함
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#75
File: feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt:0-0
Timestamp: 2025-07-29T06:07:11.727Z
Learning: seoyoon513 팀에서는 UI 구현 단계에서 더미 데이터를 하드코딩하여 화면을 먼저 구현하고, 이후 서버 연동 시점에 실제 데이터로 교체하는 개발 방식을 사용합니다.
📚 Learning: 2025-07-28T18:08:47.298Z
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#72
File: feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/component/CameraFrame.kt:21-82
Timestamp: 2025-07-28T18:08:47.298Z
Learning: Jetpack Compose에서 scale() 변환은 시각적 변환만 적용하며 레이아웃 좌표계는 변경하지 않는다. 따라서 scale(scaleX = -1f, scaleY = -1f)로 반전된 아이콘에서 padding()은 원래 레이아웃 기준으로 동작하므로, 시각적으로 올바른 위치를 위해서는 변환 전 좌표계 기준으로 padding을 설정해야 한다.

Applied to files:

  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ci-build
🔇 Additional comments (5)
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt (5)

37-41: Compose 상태 import 추가: OK

반응형 권한 상태 관리를 위한 필요한 import 추가가 적절합니다.


95-103: 권한 상태를 mutableState로 관리 — PR 목적과 정확히 부합

OS 권한을 초기값으로 한 상태 관리로 권한 허용 즉시 UI 반영 가능합니다. 설계 방향 맞습니다.


106-111: 권한 승인 콜백에서 상태 갱신 처리: OK

isGranted 갱신 → DisposableEffect 재실행 → 카메라 즉시 바인딩 흐름이 잘 연결됩니다.


116-123: 최초 진입 시 권한 요청 흐름 적절

권한 미허용 시 런처 호출 및 다이얼로그 숨김 처리로 UX 플로우가 자연스럽습니다.


165-185: System UI 컨트롤러 처리: OK

진입 시 투명/라이트, 종료 시 투명/다크로 복원하는 DisposableEffect 구성 적절합니다.

Comment on lines +144 to +148
val imageAnalyzer = remember {
ImageAnalysis.Analyzer { imageProxy ->
state.eventSink(OcrUiEvent.OnFrameReceived(imageProxy))
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Analyzer에서 imageProxy.close() 보장 필요 + eventSink 참조 최신화

  • close 누락 시 프레임 정체/메모리 누수 위험이 큽니다. 소비 측(이벤트 핸들러)에서 반드시 close를 보장하는지 확인해 주세요.
  • 또한 Analyzer를 remember(state.eventSink)로 생성하면 sink 변경 시 최신 참조를 사용합니다.

권장 diff(참고: 비동기 처리라면 finally에서 close하지 말고, 소비 측에서 완료 시 close):

-    val imageAnalyzer = remember {
-        ImageAnalysis.Analyzer { imageProxy ->
-            state.eventSink(OcrUiEvent.OnFrameReceived(imageProxy))
-        }
-    }
+    val imageAnalyzer = remember(state.eventSink) {
+        ImageAnalysis.Analyzer { imageProxy ->
+            try {
+                state.eventSink(OcrUiEvent.OnFrameReceived(imageProxy))
+            } finally {
+                // 동기 처리일 때만 사용. 비동기 처리라면 소비 측에서 close 책임.
+                // imageProxy.close()
+            }
+        }
+    }

검증 스크립트(소비 측에서 close 호출 여부 확인):


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "OnFrameReceived 처리부 검색"
rg -n "OnFrameReceived\\(" -A 50

echo
echo "imageProxy.close() 호출 위치 검색"
rg -n "imageProxy\\.close\\(\\)" -A 2

Length of output: 9435


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1. OcrPresenter.kt에서 OnFrameReceived 처리부 검색"
rg -n "OnFrameReceived" -A 20 feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrPresenter.kt

echo
echo "2. Presenter에서 imageProxy.close() 호출 여부 확인"
rg -n "close(" -A 2 feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrPresenter.kt

echo
echo "3. LiveTextAnalyzer, StillTextAnalyzer 사용 위치 검색"
rg -n "LiveTextAnalyzer" -A 10 feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrPresenter.kt
rg -n "StillTextAnalyzer" -A 10 feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrPresenter.kt

echo
echo "4. Analyzer.invoke/analyze 호출 검색"
rg -n "analyz" -A 5 feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrPresenter.kt

Length of output: 1372


Analyzer에서 close 처리 확인 및 remember(key) 반영 요청

  • core/ocr 모듈의 StillTextAnalyzer/LiveTextAnalyzer에서 imageProxy.close()를 호출해 리소스를 해제하고 있으므로, UI 레이어의 Analyzer에서 별도 close 호출은 불필요합니다.
  • 다만 remember 블록에 state.eventSink를 키로 포함하지 않으면, eventSink가 변경되어도 람다에 최신 참조가 반영되지 않습니다. 아래 예시처럼 수정해 주세요.
-    val imageAnalyzer = remember {
+    val imageAnalyzer = remember(state.eventSink) {
         ImageAnalysis.Analyzer { imageProxy ->
             state.eventSink(OcrUiEvent.OnFrameReceived(imageProxy))
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val imageAnalyzer = remember {
ImageAnalysis.Analyzer { imageProxy ->
state.eventSink(OcrUiEvent.OnFrameReceived(imageProxy))
}
}
val imageAnalyzer = remember(state.eventSink) {
ImageAnalysis.Analyzer { imageProxy ->
state.eventSink(OcrUiEvent.OnFrameReceived(imageProxy))
}
}
🤖 Prompt for AI Agents
In
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt
around lines 144 to 148, the ImageAnalysis.Analyzer does not need to call
imageProxy.close() because resource management is handled in the core/ocr
module. However, the remember block should include state.eventSink as a key to
ensure the lambda captures the latest reference when eventSink changes. Update
the remember call to remember(state.eventSink) and remove any explicit
imageProxy.close() calls in this analyzer.

Comment on lines +150 to +157
DisposableEffect(isGranted, lifecycleOwner, cameraController) {
if (isGranted) {
cameraController.bindToLifecycle(lifecycleOwner)
cameraController.setImageAnalysisAnalyzer(
ContextCompat.getMainExecutor(context),
imageAnalyzer,
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

이미지 분석을 메인 스레드가 아닌 전용 Executor에서 실행하세요

현재 MainExecutor로 분석하면 OCR 로직이 UI 스레드를 점유해 프레임 드랍 가능성이 큽니다. 전용 싱글 스레드 Executor 사용을 권장합니다. 이 변경으로 권한 허용 직후에도 안정적으로 프리뷰+분석이 동작합니다.

적용 diff(실행 스레드 전환):

@@
-    DisposableEffect(isGranted, lifecycleOwner, cameraController) {
+    DisposableEffect(isGranted, lifecycleOwner, cameraController) {
         if (isGranted) {
-            cameraController.bindToLifecycle(lifecycleOwner)
-            cameraController.setImageAnalysisAnalyzer(
-                ContextCompat.getMainExecutor(context),
-                imageAnalyzer,
-            )
+            cameraController.bindToLifecycle(lifecycleOwner)
+            // 전용 Executor 사용 권장 (아래 cameraExecutor 참고)
+            cameraController.setImageAnalysisAnalyzer(
+                cameraExecutor,
+                imageAnalyzer,
+            )
         }

추가 코드(해당 블록 위/아래에 배치):

// 상단 import 추가
import java.util.concurrent.Executors

// controller 바로 아래에 배치
val cameraExecutor = remember { Executors.newSingleThreadExecutor() }

// onDispose 블록 내 자원 정리
onDispose {
    cameraController.unbind()
    cameraController.clearImageAnalysisAnalyzer()
    cameraExecutor.shutdown()
}

원하시면 위 변경을 반영한 전체 패치를 준비해드리겠습니다.

🤖 Prompt for AI Agents
In
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt
around lines 150 to 157, the image analysis is currently executed on the main
thread using ContextCompat.getMainExecutor, which can cause UI frame drops. To
fix this, create a dedicated single-threaded executor using
Executors.newSingleThreadExecutor() and use it instead of the main executor for
image analysis. Also, add proper resource cleanup by shutting down this executor
in the onDispose block along with unbinding the cameraController and clearing
the image analysis analyzer.

@easyhooon
Copy link
Copy Markdown
Contributor

Good!

@seoyoon513 seoyoon513 merged commit 91b2d74 into develop Aug 9, 2025
5 checks passed
@seoyoon513 seoyoon513 deleted the BOOK-245-fix/#124 branch August 9, 2025 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 fix Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOOK-245/fix] OCR 기능 버그 수정

2 participants