Conversation
WalkthroughOCRScannerViewController에서 오버레이/마스크 기반 스캔 UI를 상/하 dim 뷰와 errorBackView로 재구성하고 캡처 버튼 아이콘을 템플릿 크기로 변경했으며 DataScanner 설정에 핀치 줌 활성화 및 가이던스 비활성화를 적용. OCRScannerViewModel은 캡처 액션에서 스캔 영역 의존성을 제거하고 텍스트를 문장 단위로 분리하는 로직을 추가함. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant VC as OCRScannerViewController
participant VM as OCRScannerViewModel
participant DS as DataScanner
User->>VC: 캡처 버튼 탭
VC->>VM: Action.captureButtonTapped
VM->>DS: 인식 요청 (DataScanner)
DS-->>VM: RecognizedItems(텍스트)
VM->>VM: 줄 단위 분리 -> NLTokenizer(.sentence)로 문장 분할
VM-->>VC: Effect.showRecognizedSentences(문장들)
VC->>User: 문장 결과 표시 / UI 업데이트
note right of VC#lightblue: UI는 top/bottom dim 및 errorBackView로 구성
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (10)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (5)
97-112: 동기 로딩 토글로 인한 로딩 인디케이터 ‘깜빡임’ 가능성현재
.captureButtonTapped처리에서 동기적으로isLoading = true→ 즉시 false 로 전환됩니다. 실제로 비동기 작업이 없다면 사용자 입장에선 로딩 인디케이터가 순간 깜빡이는 느낌을 줄 수 있습니다.권장:
- 로딩 표시를 생략하거나
- 150~200ms 지연 후 아직 로딩 중일 때만 표시(스피너 깜빡임 방지)
- 또는 실제 비동기 처리(예: 후속 정제/필터/정렬)로 옮겨 로딩 표시의 필요성을 명확히
162-166: 사용되지 않는 파라미터 제거로 API 명확성 개선
extractTextsInScanArea(items:scanAreaFrame:)의scanAreaFrame은 더 이상 사용되지 않습니다(주석 처리된 코드만 참조). 전체 영역 스캔이 목표라면 파라미터를 제거해 API를 단순화하는 것이 좋습니다.아래처럼 시그니처만 정리하면, 현재 호출부(라인 101)는 그대로 동작합니다.
- private func extractTextsInScanArea( - items: [RecognizedItem], - scanAreaFrame: CGRect? = nil - ) -> [String] { + private func extractTextsInScanArea( + items: [RecognizedItem] + ) -> [String] {추가로, 함수명이 현재 역할(문장 추출)에 맞도록
extractSentences(from:)등으로 변경하는 것도 가독성에 도움이 됩니다.
171-188: 주석 처리된 이전 로직 정리 권장스캔 영역 중첩 계산 관련 코드(좌표 변환/오버랩 퍼센트)가 대량 주석으로 남아 있습니다. 유지보수성과 가독성을 위해 과감히 삭제하거나 Git 히스토리에만 남기는 것을 권장합니다.
불필요한 주석 블록을 제거하는 최소 예:
-// let textFrame = convertToViewCoordinates(textItem.bounds) -// let overlapPercentage = calculateOverlapPercentage(textFrame: textFrame, scanAreaFrame: scanAreaFrame) ... -// if overlapPercentage >= 0.5 { -// debugPulse("\(overlapPercentage)") -// -// -// }
196-222: 미사용 헬퍼(좌표 변환/오버랩 계산) 전체 삭제 제안해당 블록은 호출되지 않으며, 현재 설계와도 맞지 않습니다. 혼란을 줄이기 위해 삭제를 권장합니다.
-// private func convertToViewCoordinates(_ bounds: RecognizedItem.Bounds) -> CGRect { ... } -// private func calculateOverlapPercentage(textFrame: CGRect, scanAreaFrame: CGRect) -> Double { ... }
228-228: SwiftLint 경고: 불필요한 옵셔널 초기화 제거
quoteChar의= nil초기화는 중복입니다. 유지 시 아래처럼 정리하세요.- var quoteChar: Character? = nil + var quoteChar: Character?src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (5)
21-23: 디자인 상수의 기기별 대응 검토
topDimHeight(176),bottomDimHeight(138)가 고정값입니다. 작은/큰 화면, 세로/가로 전환에서 가이드/딤 영역 비율이 과도해질 수 있습니다. 안전영역·화면 높이 비율 기반 산정(예: 상/하 각각 화면의 x%)을 검토해 주세요.
109-111: 템플릿 이미지 리사이즈 실패 시 대비
resizedAsTemplate가 nil을 반환할 수 있어 버튼 아이콘이 사라질 수 있습니다. 폴백을 추가해 안전하게 설정하는 편이 좋습니다.- let resizedImage = BKImage.Icon.maximize.resizedAsTemplate(to: CGSize(width: 32, height: 32)) - captureButton.setImage(resizedImage, for: .normal) + let size = CGSize(width: 32, height: 32) + if let resizedImage = BKImage.Icon.maximize.resizedAsTemplate(to: size) { + captureButton.setImage(resizedImage, for: .normal) + } else { + // 폴백: 원본 이미지를 템플릿으로 사용 + captureButton.setImage(BKImage.Icon.maximize.withRenderingMode(.alwaysTemplate), for: .normal) + }
118-124: 색상 하드코딩 최소화 권장
UIColor(hex: "0A0A0A")기반 반투명 색상은 디자인 토큰 체계와 분리됩니다. BKDesign에 유사 토큰이 있다면 그쪽을 사용하거나, 공용 색상 유틸로 추출해 재사용성을 높이는 것을 추천합니다.
233-238: 에러 토스트 자동 숨김 처리의 중복 타이머 경합 방지연속 실패 시 여러 타이머가 경쟁할 수 있습니다. 기존 작업을 취소할 수 있도록
DispatchWorkItem을 보관해두고 재사용하는 패턴을 권장합니다.예시:
private var errorHideWorkItem: DispatchWorkItem? // 표시 시 errorHideWorkItem?.cancel() errorBackView.isHidden = false viewModel.send(.alertDismissed) let workItem = DispatchWorkItem { [weak self] in self?.errorBackView.isHidden = true } errorHideWorkItem = workItem DispatchQueue.main.asyncAfter(deadline: .now() + 1.5, execute: workItem)
319-329: 이미지 리사이즈 유틸: Renderer 사용 및 위치 이동 제안
- 구현 자체는 문제없으나,
UIGraphicsImageRenderer사용이 더 현대적이며 스케일 처리도 안전합니다.- 공용 확장은 View 파일보다 디자인/유틸 모듈(BKDesign Extensions 등)로 이동하면 재사용성/발견성이 높습니다.
Renderer 기반으로 변경 예:
- func resizedAsTemplate(to size: CGSize) -> UIImage? { - UIGraphicsBeginImageContextWithOptions(size, false, 0.0) - defer { UIGraphicsEndImageContext() } - draw(in: CGRect(origin: .zero, size: size)) - - guard let resizedImage = UIGraphicsGetImageFromCurrentImageContext() else { return nil } - - return resizedImage.withRenderingMode(.alwaysTemplate) - } + func resizedAsTemplate(to size: CGSize) -> UIImage? { + let format = UIGraphicsImageRendererFormat.default() + format.scale = UIScreen.main.scale + let renderer = UIGraphicsImageRenderer(size: size, format: format) + let img = renderer.image { _ in + draw(in: CGRect(origin: .zero, size: size)) + } + return img.withRenderingMode(.alwaysTemplate) + }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift(10 hunks)src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift(4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#125
File: src/Projects/BKPresentation/Sources/MainFlow/Note/View/SentenceRegistrationView.swift:12-14
Timestamp: 2025-07-31T12:41:20.058Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 OCR 기능 구현 시 SentenceRegistrationEvent.ocrScanTapped enum을 미리 선언해두고, 나중에 SentenceRegistrationView에서 버튼으로 진입하는 플로우를 구현할 때 연결할 예정이라고 명시했다. UI와 기능 구현을 먼저 완성하고 화면 간 연결 로직은 후속 작업으로 계획하는 개발 방식을 선호한다.
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/AuthFlow/View/LoginView.swift:43-45
Timestamp: 2025-08-08T01:39:15.620Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성 개선 작업을 별도의 전용 PR이나 이슈에서 일괄 처리하는 것을 선호한다. 개별 기능 구현 PR에서는 접근성 관련 제안을 하지 않고, 접근성 전담 작업에서 한번에 처리하는 방식을 원한다.
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/MainFlow/Home/View/HomeViewController.swift:19-24
Timestamp: 2025-08-08T01:38:59.656Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성(accessibility) 관련 개선사항은 현재 작업 중인 PR에서 즉시 처리하지 않고, 접근성 전용 PR이나 이슈를 별도로 만들어 한번에 처리하는 것을 선호한다.
🧬 Code Graph Analysis (2)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (4)
src/Projects/BKDesign/Sources/Extensions/UIView+.swift (1)
addSubviews(7-9)src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (1)
send(64-68)src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/NoteViewModel.swift (1)
send(49-53)src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/RecognizedTextViewModel.swift (1)
send(62-66)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (1)
captureButtonTapped(264-267)
🪛 SwiftLint (0.57.0)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift
[Warning] 227-227: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
🔇 Additional comments (4)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (1)
32-32: 액션 시그니처 단순화 일치 확인 (VC와 정합성 OK)
.captureButtonTapped의 연관값 제거가 컨트롤러의viewModel.send(.captureButtonTapped)와 정확히 일치합니다. 스캔 영역 의존성 제거라는 PR 목표에도 부합합니다.src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (3)
161-170: 에러 백뷰 레이아웃 제약조건 재확인
errorBackView는centerX+bottom만 지정되어 있고, 폭은 내부 라벨의 intrinsicSize와 인셋으로 유도됩니다. 보통은 문제 없지만 다국어/긴 문자열에서 예상보다 넓어질 수 있으며, 넓이 상한이 없습니다. 레이아웃 경고가 없는지 확인을 부탁드립니다.권장(예시):
- 최대 폭 제한 또는 안전영역 인셋 고정
errorBackView.snp.makeConstraints { $0.centerX.equalToSuperview() $0.bottom.equalTo(bottomDimView.snp.top).offset(-16) + $0.leading.greaterThanOrEqualTo(view.safeAreaLayoutGuide).offset(20) + $0.trailing.lessThanOrEqualTo(view.safeAreaLayoutGuide).inset(20) + $0.width.lessThanOrEqualTo(320) // 필요 시 최대폭 제한 }
189-191: 핀치줌 활성화, 가이던스 비활성화 설정 적합전체 화면 인식으로 바뀐 흐름에서 핀치줌 활성화, 가이던스 비활성화 설정은 사용성에 맞습니다.
266-267: 캡처 이벤트 시그니처 변경 반영 OK컨트롤러에서
.captureButtonTapped를 인자 없이 전달하도록 업데이트되어, ViewModel 변경과 일관됩니다.
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift
Outdated
Show resolved
Hide resolved
b65fbb2 to
b4f2888
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (1)
244-251: startScanning은 메인 스레드에서 호출되어야 합니다 (현재 Task 내부 호출로 메인 스레드 위반 가능성).VisionKit의 DataScanner는 UI 컴포넌트이며 startScanning은 메인 스레드에서 호출되어야 합니다. 현재 Task { } 내부에서 호출되어 실행 스레드가 보장되지 않아 Main Thread Checker 경고/크래시가 날 수 있습니다. 불필요한 Task를 제거하고 동기 호출로 바꾸는 편이 안전합니다. 대안으로 VC 전체를 @mainactor로 선언하는 것도 좋습니다.
적용 예:
- private func startScanning() { - Task { - do { - try scannerViewController?.startScanning() - } catch { - debugPulse(error) - } - } - } + private func startScanning() { + do { + try scannerViewController?.startScanning() + } catch { + debugPulse(error) + } + }추가로, 보다 강하게 보장하려면 클래스 선언에 @mainactor를 부여하는 것도 고려해 주세요:
@MainActor final class OCRScannerViewController: UIViewController { ... }
🧹 Nitpick comments (6)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (6)
228-239: 에러 배너 표시/숨김 타이밍 경쟁 조건 가능성 (연속 트리거 시 마지막 토스트가 조기 숨김될 수 있음).여러 번 빠르게
shouldShowAlert가 true가 되면, 이전에 예약된 hide가 새 표시를 덮어 조기 숨김될 수 있습니다. 이전 작업을 취소 가능한 DispatchWorkItem으로 관리하세요.아래처럼 변경을 제안합니다:
} else if state.shouldShowAlert { - errorBackView.isHidden = false + errorBackView.isHidden = false viewModel.send(.alertDismissed) - DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in - self?.errorBackView.isHidden = true - } + errorHideWorkItem?.cancel() + let work = DispatchWorkItem { [weak self] in + self?.errorBackView.isHidden = true + } + errorHideWorkItem = work + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5, execute: work) }추가: 속성 선언(파일 상단 프로퍼티 섹션)에 다음을 한 줄 추가해 주세요.
private var errorHideWorkItem: DispatchWorkItem?
19-23: 미사용 상수 정리 제안.
LayoutGuide.errorLabelBottomOffset가 더 이상 사용되지 않습니다. 제거하여 노이즈를 줄이는 것을 권장합니다.- static let errorLabelBottomOffset: CGFloat = -16 static let topDimHeight: CGFloat = 176 static let bottomDimHeight: CGFloat = 138
109-111: 캡처 아이콘 리사이즈 실패 시 대체 아이콘을 설정하세요.
resizedAsTemplate가 nil을 반환하면 버튼 아이콘이 사라집니다. 안전하게 폴백을 두는 편이 좋습니다.- let resizedImage = BKImage.Icon.maximize.resizedAsTemplate(to: CGSize(width: 32, height: 32)) - captureButton.setImage(resizedImage, for: .normal) + if let resizedImage = BKImage.Icon.maximize.resizedAsTemplate(to: CGSize(width: 32, height: 32)) { + captureButton.setImage(resizedImage, for: .normal) + } else { + captureButton.setImage(BKImage.Icon.maximize.withRenderingMode(.alwaysTemplate), for: .normal) + }참고: 아래 확장 함수 자체를 non-optional 반환으로 개선하거나 UIGraphicsImageRenderer로 교체하는 것도 가능합니다(아래 다른 코멘트 참고).
166-169: errorBackView 최대 너비를 화면에 맞게 제한하는 약식 제약 추가 권장.현재는 레이블 intrinsic size를 기반으로 컨테이너 크기가 정해지지만, 안전하게 화면 좌우를 넘지 않도록 불등식 제약을 추가하는 편이 좋습니다.
errorBackView.snp.makeConstraints { $0.centerX.equalToSuperview() $0.bottom.equalTo(bottomDimView.snp.top).offset(-16) + $0.leading.greaterThanOrEqualToSuperview().inset(16) + $0.trailing.lessThanOrEqualToSuperview().inset(16) }추가로 레이블의 가로 CR/Hugging 우선순위를 높이면 안정성이 올라갑니다(선택):
errorLabel.setContentCompressionResistancePriority(.required, for: .horizontal) errorLabel.setContentHuggingPriority(.required, for: .horizontal)
118-124: 하드코딩된 Hex 색상 대신 디자인 토큰 사용을 고려하세요.
UIColor(hex: "0A0A0A").withAlphaComponent(...)사용 대신 BKDesign의 스크림/딤 용도에 맞는 시맨틱 컬러가 있다면 그 토큰을 사용하는 것이 테마 일관성과 유지보수에 유리합니다.
320-328: 이미지 리사이즈 구현을 UIGraphicsImageRenderer로 교체하면 더 안전하고 명확합니다.현재 컨텍스트 API도 동작은 문제 없으나, Renderer는 스케일/메모리 관리 측면에서 더 현대적입니다. 시그니처는 그대로 두고 내부 구현만 교체해도 됩니다.
- func resizedAsTemplate(to size: CGSize) -> UIImage? { - UIGraphicsBeginImageContextWithOptions(size, false, 0.0) - defer { UIGraphicsEndImageContext() } - draw(in: CGRect(origin: .zero, size: size)) - - guard let resizedImage = UIGraphicsGetImageFromCurrentImageContext() else { return nil } - - return resizedImage.withRenderingMode(.alwaysTemplate) - } + func resizedAsTemplate(to size: CGSize) -> UIImage? { + let format = UIGraphicsImageRendererFormat() + format.scale = UIScreen.main.scale + let renderer = UIGraphicsImageRenderer(size: size, format: format) + let image = renderer.image { _ in + self.draw(in: CGRect(origin: .zero, size: size)) + } + return image.withRenderingMode(.alwaysTemplate) + }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift(11 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#125
File: src/Projects/BKPresentation/Sources/MainFlow/Note/View/SentenceRegistrationView.swift:12-14
Timestamp: 2025-07-31T12:41:20.058Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 OCR 기능 구현 시 SentenceRegistrationEvent.ocrScanTapped enum을 미리 선언해두고, 나중에 SentenceRegistrationView에서 버튼으로 진입하는 플로우를 구현할 때 연결할 예정이라고 명시했다. UI와 기능 구현을 먼저 완성하고 화면 간 연결 로직은 후속 작업으로 계획하는 개발 방식을 선호한다.
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/AuthFlow/View/LoginView.swift:43-45
Timestamp: 2025-08-08T01:39:15.620Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성 개선 작업을 별도의 전용 PR이나 이슈에서 일괄 처리하는 것을 선호한다. 개별 기능 구현 PR에서는 접근성 관련 제안을 하지 않고, 접근성 전담 작업에서 한번에 처리하는 방식을 원한다.
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/MainFlow/Home/View/HomeViewController.swift:19-24
Timestamp: 2025-08-08T01:38:59.656Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성(accessibility) 관련 개선사항은 현재 작업 중인 PR에서 즉시 처리하지 않고, 접근성 전용 PR이나 이슈를 별도로 만들어 한번에 처리하는 것을 선호한다.
🧬 Code Graph Analysis (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (5)
src/Projects/BKDesign/Sources/Extensions/UIColor+.swift (2)
bkContentColor(52-63)bkBaseColor(91-102)src/Projects/BKDesign/Sources/Extensions/UIView+.swift (1)
addSubviews(7-9)src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (1)
send(64-68)src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/NoteViewModel.swift (1)
send(49-53)src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/RecognizedTextViewModel.swift (1)
send(62-66)
⏰ 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: build
🔇 Additional comments (2)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (2)
189-191: 핀치 줌 활성화 및 가이던스 비활성화 설정 적절합니다.UX 목적(전체 영역 스캔 + UI 단순화)에 잘 부합합니다.
174-181: 지원 여부 외에 사용 가능 여부(isAvailable)도 점검하는 방어 로직 검토 요청.일부 기기/권한 상태에서
isSupported == true라도 카메라 권한 미부여 등으로 사용 불가일 수 있습니다.isAvailable(정확한 API 명은 사용 중인 iOS SDK 버전에 따라 다를 수 있음) 체크 및 권한 안내/폴백 처리를 고려해 주세요.가능하다면 아래와 같은 가드 추가를 검토해 주세요(실제 API 명은 프로젝트 SDK 기준 확인 필요):
guard DataScannerViewController.isSupported else { return } // 권한/가용성 체크 (API 명 확인 필요) guard DataScannerViewController.isAvailable else { // 권한 안내 UI 또는 설정 이동 처리 return }
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (1)
7-7: NLTokenizer 도입 선택 적절NaturalLanguage 임포트로 문장 단위 토크나이징을 사용한 점 좋습니다. 수동 구두점 분리의 엣지 케이스(…/약어/소수점 등) 문제를 해소합니다.
🧹 Nitpick comments (5)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (5)
98-103: scanAreaFrame 미사용 — 시그니처/네이밍 정리 제안이제 전체 영역 스캔이므로 scanAreaFrame 파라미터는 의미가 없어졌습니다. 함수명을 역할에 맞게 바꾸고 파라미터를 제거하면 가독성과 유지보수성이 좋아집니다.
아래처럼 변경을 제안합니다:
- let capturedTexts = extractTextsInScanArea(items: currentRecognizedItems) + let capturedTexts = extractSentences(from: currentRecognizedItems)- private func extractTextsInScanArea( - items: [RecognizedItem], - scanAreaFrame: CGRect? = nil - ) -> [String] { + private func extractSentences(from items: [RecognizedItem]) -> [String] {Also applies to: 162-166
172-178: 문장 토크나이즈는 줄 단위가 아닌 transcript 전체로 처리 권장현재는 개행으로 라인을 쪼갠 뒤 라인별로 토크나이즈합니다. NLTokenizer가 문장 경계를 스스로 처리하므로 transcript 전체를 한 번에 토크나이즈하면:
- 정확도: 라인 경계에 걸친 문장/인용부호 케이스를 더 자연스럽게 처리
- 성능: 토크나이저 생성/호출 횟수 감소
아래처럼 단순화할 수 있습니다.
- let lines = textItem.transcript.components(separatedBy: .newlines) - for line in lines { - let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines) - if !trimmedLine.isEmpty { - let sentences = splitByToken(trimmedLine) - capturedTexts.append(contentsOf: sentences) - } - } + let sentences = splitByToken(textItem.transcript) + capturedTexts.append(contentsOf: sentences)추가로, 동일 문장이 중복 인식되는 경우가 잦다면 반환 전 중복 제거를 고려해 주세요:
// return 직전에 예시: var seen = Set<String>() capturedTexts = capturedTexts.filter { seen.insert($0).inserted }
188-200: 네이밍/접근 제어 정리: splitByToken → splitIntoSentences, private 지정메서드 역할을 드러내는 이름이 더 읽기 쉽습니다. 또한 ViewModel 내부 유틸이므로 private으로 한정하는 게 좋습니다.
- func splitByToken(_ text: String) -> [String] { + private func splitIntoSentences(_ text: String) -> [String] { let tokenizer = NLTokenizer(unit: .sentence) tokenizer.string = text var results: [String] = [] tokenizer.enumerateTokens(in: text.startIndex..<text.endIndex) { range, _ in let sentence = String(text[range]).trimmingCharacters(in: .whitespacesAndNewlines) if !sentence.isEmpty { results.append(sentence) } return true } return results }호출부도 함께 변경:
- let sentences = splitByToken(trimmedLine) + let sentences = splitIntoSentences(trimmedLine)Also applies to: 176-177
98-123: 성공/실패 플로우는 명확합니다만, UX 튜닝에 대한 제안 (선택)
- 연속 실패 카운트 기준(3회)과 알럿/다이얼로그 전환은 명확합니다. 다만 캡처 실패 원인(예: items 비어 있음) 로그에 원인 힌트를 함께 남기면 추적이 용이합니다.
- isLoading 토글은 적절합니다. 로딩 상태가 너무 짧게 깜빡이는 UX라면 버튼 디스에이블/스로틀링(예: 500ms)도 고려해볼 수 있습니다.
필수는 아니고 현 PR 범위 밖이면 무시하셔도 됩니다.
41-44: SideEffect는 단순하지만 테스트 포인트 노출 가능.showRecognizedSentences([String])로 View 단 검증이 용이합니다. 이 부분은 현 변경과 잘 맞습니다. 단위 테스트에서 split 결과의 안정성을 검증하는 포인트로 활용 추천드립니다. 필요 시 테스트 템플릿 제공 가능합니다.
원하시면 "3.14", "U.S.A.", "Hello… world!", 따옴표 포함 문장 등 케이스로 ViewModel 단위 테스트 예시를 작성해 드릴게요. 원하시나요?
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift(5 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#125
File: src/Projects/BKPresentation/Sources/MainFlow/Note/View/SentenceRegistrationView.swift:12-14
Timestamp: 2025-07-31T12:41:20.058Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 OCR 기능 구현 시 SentenceRegistrationEvent.ocrScanTapped enum을 미리 선언해두고, 나중에 SentenceRegistrationView에서 버튼으로 진입하는 플로우를 구현할 때 연결할 예정이라고 명시했다. UI와 기능 구현을 먼저 완성하고 화면 간 연결 로직은 후속 작업으로 계획하는 개발 방식을 선호한다.
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/MainFlow/Home/View/HomeViewController.swift:19-24
Timestamp: 2025-08-08T01:38:59.656Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성(accessibility) 관련 개선사항은 현재 작업 중인 PR에서 즉시 처리하지 않고, 접근성 전용 PR이나 이슈를 별도로 만들어 한번에 처리하는 것을 선호한다.
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/AuthFlow/View/LoginView.swift:43-45
Timestamp: 2025-08-08T01:39:15.620Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성 개선 작업을 별도의 전용 PR이나 이슈에서 일괄 처리하는 것을 선호한다. 개별 기능 구현 PR에서는 접근성 관련 제안을 하지 않고, 접근성 전담 작업에서 한번에 처리하는 방식을 원한다.
🧬 Code Graph Analysis (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (2)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (1)
captureButtonTapped(264-267)src/Projects/BKCore/Sources/Logger/DebugLogger.swift (1)
debugPulse(10-27)
⏰ 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: build
🔇 Additional comments (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (1)
33-33: .captureButtonTapped 호출부 및 scanAreaFrame 잔여 참조 없음 확인
.captureButtonTapped(…)호출부 검색 결과: 전혀 없음scanAreaFrame참조 검색 결과: 오직extractTextsInScanArea(scanAreaFrame: CGRect? = nil)함수 파라미터로만 사용 중이상으로 이전 시그니처의 잔여 호출부 및 연관값 흔적이 모두 제거되었음을 확인했습니다.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (1)
7-7: NLTokenizer 사용을 위한 NaturalLanguage 임포트, 적절합니다문장 단위 분리를 위해 NaturalLanguage를 도입한 방향이 맞습니다. 이전 제안사항을 깔끔하게 반영하셨어요.
🧹 Nitpick comments (4)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (4)
98-113: 변수 이름 의미 정교화: capturedTexts → capturedSentences이 블록에서 실제로는 “문장” 배열을 다루므로, 가독성을 위해 변수명을 ‘capturedSentences’로 맞추는 편이 명확합니다. 기능 변화 없음.
- let capturedTexts = extractTextsInScanArea(items: currentRecognizedItems) + let capturedSentences = extractTextsInScanArea(items: currentRecognizedItems) - if !capturedTexts.isEmpty { + if !capturedSentences.isEmpty { newState.failureCount = 0 - newState.capturedSentences = capturedTexts - let combinedText = capturedTexts.joined(separator: "\n") + newState.capturedSentences = capturedSentences + let combinedText = capturedSentences.joined(separator: "\n") newState.capturedText = combinedText newState.isLoading = false - effects.append(.showRecognizedSentences(capturedTexts)) + effects.append(.showRecognizedSentences(capturedSentences))
163-166: 사용되지 않는 파라미터 제거 제안(scanAreaFrame)전체 영역 스캔으로 전환되어 현재 파라미터는 미사용입니다. 사인이 private이므로 과감히 제거하면 인터페이스가 더 깔끔해집니다.
- private func extractTextsInScanArea( - items: [RecognizedItem], - scanAreaFrame: CGRect? = nil - ) -> [String] { + private func extractTextsInScanArea( + items: [RecognizedItem] + ) -> [String] {
172-179: 줄 단위 분리 대신 전체 transcript에 토크나이저 1회 적용 권장OCR는 줄바꿈/하이픈으로 문장 경계가 끊기는 일이 잦습니다. NLTokenizer를 전체 transcript에 1회 적용하면 정확도와 성능 모두 개선됩니다.
- let lines = textItem.transcript.components(separatedBy: .newlines) - for line in lines { - let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines) - if !trimmedLine.isEmpty { - let sentences = splitByToken(trimmedLine) - capturedTexts.append(contentsOf: sentences) - } - } + let sentences = splitByToken(textItem.transcript) + capturedTexts.append(contentsOf: sentences)
188-200: splitByToken의 접근 수준을 private으로 제한클래스 내부 전용 유틸로 보입니다. 접근 제어자를 명시하면 의도를 더 분명히 할 수 있습니다.
- func splitByToken(_ text: String) -> [String] { + private func splitByToken(_ text: String) -> [String] {
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift(11 hunks)src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#125
File: src/Projects/BKPresentation/Sources/MainFlow/Note/View/SentenceRegistrationView.swift:12-14
Timestamp: 2025-07-31T12:41:20.058Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 OCR 기능 구현 시 SentenceRegistrationEvent.ocrScanTapped enum을 미리 선언해두고, 나중에 SentenceRegistrationView에서 버튼으로 진입하는 플로우를 구현할 때 연결할 예정이라고 명시했다. UI와 기능 구현을 먼저 완성하고 화면 간 연결 로직은 후속 작업으로 계획하는 개발 방식을 선호한다.
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/MainFlow/Home/View/HomeViewController.swift:19-24
Timestamp: 2025-08-08T01:38:59.656Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성(accessibility) 관련 개선사항은 현재 작업 중인 PR에서 즉시 처리하지 않고, 접근성 전용 PR이나 이슈를 별도로 만들어 한번에 처리하는 것을 선호한다.
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/AuthFlow/View/LoginView.swift:43-45
Timestamp: 2025-08-08T01:39:15.620Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성 개선 작업을 별도의 전용 PR이나 이슈에서 일괄 처리하는 것을 선호한다. 개별 기능 구현 PR에서는 접근성 관련 제안을 하지 않고, 접근성 전담 작업에서 한번에 처리하는 방식을 원한다.
📚 Learning: 2025-08-08T01:38:59.656Z
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/MainFlow/Home/View/HomeViewController.swift:19-24
Timestamp: 2025-08-08T01:38:59.656Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성(accessibility) 관련 개선사항은 현재 작업 중인 PR에서 즉시 처리하지 않고, 접근성 전용 PR이나 이슈를 별도로 만들어 한번에 처리하는 것을 선호한다.
Applied to files:
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift
📚 Learning: 2025-08-08T01:39:15.620Z
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#157
File: src/Projects/BKPresentation/Sources/AuthFlow/View/LoginView.swift:43-45
Timestamp: 2025-08-08T01:39:15.620Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 접근성 개선 작업을 별도의 전용 PR이나 이슈에서 일괄 처리하는 것을 선호한다. 개별 기능 구현 PR에서는 접근성 관련 제안을 하지 않고, 접근성 전담 작업에서 한번에 처리하는 방식을 원한다.
Applied to files:
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift
📚 Learning: 2025-07-31T12:41:20.058Z
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#125
File: src/Projects/BKPresentation/Sources/MainFlow/Note/View/SentenceRegistrationView.swift:12-14
Timestamp: 2025-07-31T12:41:20.058Z
Learning: doyeonk429는 Reed-iOS 프로젝트에서 OCR 기능 구현 시 SentenceRegistrationEvent.ocrScanTapped enum을 미리 선언해두고, 나중에 SentenceRegistrationView에서 버튼으로 진입하는 플로우를 구현할 때 연결할 예정이라고 명시했다. UI와 기능 구현을 먼저 완성하고 화면 간 연결 로직은 후속 작업으로 계획하는 개발 방식을 선호한다.
Applied to files:
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift
📚 Learning: 2025-08-06T08:53:16.909Z
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#147
File: src/Projects/BKData/Sources/Service/AppleLoginDelegateProxy.swift:65-78
Timestamp: 2025-08-06T08:53:16.909Z
Learning: clxxrlove는 Reed-iOS 프로젝트의 Apple 로그인 구현에서 authorizationCode 강제 언래핑 이슈에 대해 현재 구현으로도 문제없다고 판단했다. 초기 구현 단계에서는 기본 기능 동작을 우선시하고 추후 개선할 예정이라는 기존 패턴과 일치한다.
Applied to files:
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift
🧬 Code Graph Analysis (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/View/OCRScannerViewController.swift (1)
captureButtonTapped(264-267)
🔇 Additional comments (1)
src/Projects/BKPresentation/Sources/MainFlow/Note/ViewModel/OCRScannerViewModel.swift (1)
33-33: 연관값 호출 및 선언 흔적 없음 확인
rg검색 결과.captureButtonTapped(…)호출과case captureButtonTapped(…)선언이 모두 검출되지 않아, 레거시 파라미터 사용 흔적이 없음을 확인했습니다. 변경된 Action API 적용이 안전합니다.
🔗 관련 이슈
📘 작업 유형
📙 작업 내역
🧪 테스트 내역
🎨 스크린샷 또는 시연 영상 (선택)
✅ PR 체크리스트
💬 추가 설명 or 리뷰 포인트 (선택)
Summary by CodeRabbit
신기능
스타일