Repository navigation
[NM-449] 알약 식별 흐름의 spec 불일치 3건 (분석 실패 버튼 · 한도 팝업 · 탭 진입 게이트) - #166
Merged
Merged
Conversation
spec 의 분기는 이렇다 (feature/pill-recognition/README.md §식별 → 인식 결과). E -->|재시도| B 재시도는 인식으로 되돌아간다 — 같은 사진으로 다시 분석 E -->|뒤로| X 뒤로는 촬영/미리보기로 복귀 둘 다 어긋나 있었다. 재시도가 pop 이라 사실상 spec 의 '뒤로' 였고(분석을 다시 하지 않는다), 뒤로는 홈으로 나가 버려 사진을 다시 고를 기회 없이 흐름이 끊겼다. "이 화면엔 돌아갈 이전 단계가 없다"고 적혀 있었지만 showPreview 가 push 한 PhotoPreviewVC 가 스택에 그대로 남아 있다 — 실제로 재시도가 그리로 가고 있었다. - startIdentification 을 makeLoadingVC(image:) 로 쪼갠다. 재시도는 실패 화면을 새 로딩 VC 로 replace 해 온디바이스 탐지부터 다시 돌린다 — VM 은 이미 종료 상태를 방출한 뒤라 재사용할 수 없다. - 뒤로(네비바·푸터 모두)는 pop 으로 미리보기에 되돌린다. 곁가지 — 오프라인 문구가 원문 그대로 나갔다. URLSessionTask failed with error: The Internet connection appears to be offline. BaseService.interpret 은 응답 본문이 있어야 APIError.network 를 만드는데, 서버에 닿지 못한 요청은 응답 자체가 없어 그 분기를 못 타고 원문을 돌려줬다. APIError.offline 을 추가하고 Moya → AFError → URLError 체인을 풀어 연결 계열 코드를 여기로 모은다. 타임아웃은 제외한다 — 연결은 됐는데 느린 것이라 "인터넷 연결을 확인하라"가 맞는 안내가 아니다(pill-attributes 는 서버 Gemini 처리로 수십 초 걸려 60초로 잡혀 있다). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ch9vPGGKfeTUWMKEsZ3NWM
spec 은 "확인 시 **현재 화면에 머무른다**" 다 (feature/pill-recognition/README.md §식별 횟수 제한 §한도 안내 팝업). 지금은 popToRootViewController + 홈 탭 전환이라 찍은 사진이 통째로 날아간다. "미리보기에 남겨두면 재촬영·이 사진 사용 둘 다 다시 막혀 막다른 길" 이라는 게 옛 근거였다. 그런데 미리보기는 남은 횟수를 0회·경고색으로 표시하는 화면이라 막다른 길이 아니라 **왜 막혔는지 보이는 자리**다. 사진을 버리는 대가로 얻는 게 없다 — 다시 하려면 촬영부터 다시 해야 한다. 호출부가 둘이고 서 있는 자리가 달라 각각 처리한다. - **요청 전 게이트**(이 사진 사용 시 remaining 0): 이미 미리보기라 화면을 건드리지 않고 팝업만 띄운다. - **서버 429**: 로딩 화면은 머무를 수 있는 자리가 아니다(PillLoadingVC 가 나갈 버튼을 숨긴다). 미리보기로 pop 해 게이트와 같은 자리에서 같은 팝업을 띄운다 — spec 이 "요청이 429 로 거부된 경우도 같은 팝업으로 처리한다" 고 한 그대로다. DSAlertCardView.presentOverWindow 는 루트(네비게이션 컨트롤러) 뷰에 붙으므로 pop 과 같은 런루프에 띄워도 알럿이 사라지지 않는다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ch9vPGGKfeTUWMKEsZ3NWM
spec 은 게이트 위치를 "식별 진입 시점(홈 '알약 식별' 카드/**탭**)" 으로 못박는다 (feature/pill-recognition/README.md §식별 횟수 제한 §흐름 규칙). 홈 카드는 HomeViewModel 이 막고 있는데 탭 경로엔 게이트가 없어서, 탭으로 들어오면 0회여도 촬영까지 그대로 진행됐다. 두 경로는 결국 같은 자리로 모인다 — 홈 카드도 .selectPillTab → 탭 전환 → .pillTabSelected 로 이어져 presentCameraIfAppropriate() 를 탄다. 그래서 게이트를 거기 한 곳에 둔다. - 진입 시 fetchPillUsage() — 홈을 거치지 않고 탭으로 바로 들어온 경우에도 값을 최신으로 만든다. 조회 전용이라 카운트는 늘지 않는다(NM-331). - 잔여 0 이면 카메라를 띄우지 않고 한도 팝업. 값을 모르면 통과한다 — 최종 판정은 서버 429 다(spec: "잔여 미확인 시 통과"). 막을 때는 홈으로 되돌린다. 알약 탭 루트는 카메라가 덮는 걸 전제로 한 빈 화면이라 그대로 두면 흰 화면만 남는다. spec 흐름도도 팝업 확인 뒤를 홈으로 둔다(P -->|확인| H). 앞 커밋에서 미리보기는 반대로 화면을 유지하게 했는데, 거긴 찍은 사진이 있고 잔여 0회를 경고색으로 보여 주는 자리라 남길 이유가 있다 — 여기는 없다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ch9vPGGKfeTUWMKEsZ3NWM
CoreML 세그멘테이션이 실패하면 error.localizedDescription 을 그대로 띄웠다. CoreML 에러는 영문 시스템 메시지라 한국어 앱에 이런 문장이 나갔다. Failed to build the model execution plan using a model architecture file .../rfdetr_seg_small_fp32.mlmodelc/model.mil with error code: -5 가상의 예가 아니라 배포 빌드에서 실제로 났던 문구다 — CI 체크아웃에 lfs: true 가 빠져 모델 가중치가 포인터 파일로 번들된 사고. 로컬 빌드는 멀쩡해서 네트워크 문제로 오인하기 쉬운 부류인데, 사용자는 읽고 할 수 있는 게 없고 원인은 원인대로 화면에서 휘발됐다. 화면과 로그를 가른다. - 화면: "사진을 분석하지 못했어요. 잠시 후 다시 시도해 주세요." - 로그: FirebaseService.recordError(원문) + 단계(load/inference) 로그. Crashlytics 는 내부 빌드에서도 유지되고 dev 프로젝트로 분리돼 있어 운영 지표와 섞이지 않는다(AppEnvironment 주석 참고). 모델 로드와 추론 두 자리가 같은 처리를 하므로 reportSegmentationFailure(_:stage:) 한 곳으로 모았다 — stage 로 어느 쪽인지 남는다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ch9vPGGKfeTUWMKEsZ3NWM
7 of 8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
관련 이슈
변경 요약
알약 식별 흐름에서 spec과 어긋난 화면 동작 3건을 맞춘다. 커밋 3개로 나눠 두었고 각각 독립적으로 빌드된다.
변경 내용
1.
[NM-449] fix: 분석 실패 화면의 두 버튼을 spec 대로 되돌리고 오프라인 문구를 다듬는다spec 의 분기 (
feature/pill-recognition/README.md§식별 → 인식 결과):둘 다 어긋나 있었다. 재시도가 pop 이라 사실상 spec 의 '뒤로' 였고(분석을 다시 하지 않는다), 뒤로는 홈으로 나가 사진을 다시 고를 기회 없이 흐름이 끊겼다. "이 화면엔 돌아갈 이전 단계가 없다"고 적혀 있었지만
showPreview가 push 한PhotoPreviewVC가 스택에 그대로 남아 있다 — 실제로 재시도가 그리로 가고 있었다.startIdentification→makeLoadingVC(image:)로 분리. 재시도는 실패 화면을 새 로딩 VC 로 replace 해 온디바이스 탐지부터 다시 돌린다(VM 은 종료 상태를 방출한 뒤라 재사용 불가).곁가지 — 오프라인 문구가 원문 그대로 나갔다.
BaseService.interpret은 응답 본문이 있어야APIError.network를 만드는데, 서버에 닿지 못한 요청은 응답 자체가 없어 그 분기를 못 타고 원문을 돌려줬다.APIError.offline을 추가하고 Moya → AFError → URLError 체인을 풀어 연결 계열 코드를 여기로 모은다. 타임아웃은 제외 — 연결은 됐는데 느린 것이라 "인터넷 연결을 확인하라"가 맞는 안내가 아니다(pill-attributes는 서버 Gemini 처리로 60초 타임아웃).2.
[NM-449] fix: 한도 팝업을 확인해도 찍은 사진을 잃지 않는다spec 은 "확인 시 현재 화면에 머무른다"(§식별 횟수 제한 §한도 안내 팝업). 지금은
popToRootViewController+ 홈 탭 전환이라 찍은 사진이 통째로 날아간다.옛 근거는 "미리보기에 남겨두면 재촬영·이 사진 사용 둘 다 막혀 막다른 길" 이었다. 그런데 미리보기는 남은 횟수를 0회·경고색으로 표시하는 화면이라(§남은 횟수 표시) 막다른 길이 아니라 왜 막혔는지 보이는 자리다. 사진을 버려서 얻는 게 없다.
호출부가 둘이고 서 있는 자리가 달라 각각 처리했다.
이 사진 사용시remaining0)PillLoadingVC가 나갈 버튼을 숨긴다) → 미리보기로 pop 후 같은 팝업spec 이 "요청이 429 로 거부된 경우도 같은 팝업으로 처리한다" 고 한 그대로, 두 경로가 같은 자리에서 끝난다.
3.
[NM-449] fix: 탭으로 알약에 들어와도 남은 횟수를 확인한다spec 은 게이트 위치를 "식별 진입 시점(홈 '알약 식별' 카드/탭)" 으로 못박는다. 홈 카드는
HomeViewModel이 막는데 탭 경로엔 게이트가 없어 0회여도 촬영까지 그대로 진행됐다.두 경로는 결국 같은 자리로 모인다 — 홈 카드도
.selectPillTab→ 탭 전환 →.pillTabSelected로 이어져presentCameraIfAppropriate()를 탄다. 게이트를 그 합류 지점 한 곳에 두어 두 경로가 자동으로 같은 규칙을 타게 했다.fetchPillUsage()— 홈을 거치지 않고 탭으로 바로 들어온 경우에도 값을 최신으로(조회 전용이라 카운트 미증가, NM-331)리뷰에서 봐줬으면 하는 것 — 2번과 3번이 반대로 보인다
2번은 "홈으로 내보내지 마라", 3번은 "홈으로 보낸다". 의도된 차이다.
테스트
tuist build App성공NetworksTests6개 통과(오프라인 해석 신규 3개)남은 확인 (한도 관련은 계정 상태가 필요)
이 사진 사용→ 팝업 → 확인 → 미리보기에 사진 유지체크리스트
[NM-XXX] 요약형식<type>/NM-XXX-요약형식[NM-XXX] type: 요약형식4.
[NM-449] fix: 온디바이스 분석 실패의 원문을 화면 대신 Crashlytics 로 보낸다CoreML 세그멘테이션 실패도
error.localizedDescription을 그대로 띄웠다. 영문 시스템 메시지라 한국어 앱에 이런 문장이 나갔다.가상의 예가 아니라 배포 빌드에서 실제로 났던 문구다 — CI 체크아웃에
lfs: true가 빠져 모델 가중치가 포인터 파일로 번들된 사고. 로컬 빌드는 멀쩡해서 네트워크 문제로 오인하기 쉬운 부류인데, 사용자는 읽고 할 수 있는 게 없고 원인은 원인대로 화면에서 휘발됐다.화면과 로그를 가른다 — 화면은
사진을 분석하지 못했어요. 잠시 후 다시 시도해 주세요., 원문은FirebaseService.recordError로 Crashlytics 비치명적 이슈에 단계(load/inference)와 함께 남긴다.