Repository navigation
feat(mindrecord): 작성 실패를 텔레메트리에 남긴다 — 쓰기 승격 3종 (#964) - #1278
Conversation
이 모듈은 Log.e 도 ErrorReporter 도 0건이라, 사용자가 방금 쓴 글이 서버에 닿지 못해도 릴리즈에서 알 방법이 없었다. #1109 가 읽기 3종(임시저장 목록·삭제·수신자 열람)을 세웠고, #964 체크리스트의 «쓰기 실패 — 승격 1순위» 가 남아 있었다. 승격한 자리 셋: - DIARY_SUBMIT — 일기 저장(정식·임시저장 공통). 방금 쓴 글이 사라지는 자리다 - DAILY_QUESTION_SUBMIT — 데일리질문 답변 저장. upsert 라 실패가 기존 임시저장과 얽힌다 - MEDIA_UPLOAD — 작성 화면 미디어 업로드. 실패하면 첨부만 빠진 채 저장이 이어져 «올린 줄 알았는데 없는» 기록이 남는다 일괄 치환하지 않았다. Crashlytics 가 non-fatal 을 최근 8건만 보관하므로, 흔한 읽기 실패까지 넣으면 잡음이 한도를 채워 실제 장애를 민다 — 사용자가 쓴 것이 날아가고 재현이 어려운 경로만 골랐다. 가드 4건: 세 stage 가 각각 그 문자열로 기록되는지와, **성공은 기록하지 않는지**. 뒤가 없으면 «항상 기록» 으로 만들어도 앞이 통과한다. 계측을 빼면 실패하는 것을 확인했다. stage 문자열까지 고정한 이유는 콘솔 필터가 그 값이라 바뀌면 추이가 끊기기 때문이다. ktlintCheck · testDebugUnitTest · :app:assembleDebug green, MindRecordFlowAndroidTest 는 CI 와 같은 pixel2Api30 에서 3/3 통과. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🚧 리뷰 적체 가드 — 이 PR 을 닫습니다리뷰 응답을 기다리는 PR 이 1건 남아 있습니다.
이 저장소는 리뷰 응답을 기다리는 PR 이 1건이라도 남아 있으면 새 PR 을 열 수 없습니다 (#1136). 각 PR 에 필요한 리뷰는 한 건입니다 — 누구든 먼저 판정을 내면 그 PR 은 모두의 목록에서 빠집니다. 승인 뒤 변경을 다시 볼 의무는 없고, 변경요청도 작성자가 같은 리뷰어에게 재요청한 뒤 반영 커밋을 올린 경우에만 다시 리뷰어의 몫이 됩니다. 되살리는 법위 리뷰를 끝낸 뒤 이 PR 을 다시 여세요. 브랜치는 지워지지 않았고 커밋도 그대로입니다. gh pr reopen 1278리뷰보다 이 PR 이 급하면 |
Repository Quality 가 계측 테스트 필요 여부의 명시적 결정을 요구한다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1hyok
left a comment
There was a problem hiding this comment.
직접 계측 경계와 한 호출부의 회귀 테스트가 빠져 있어 변경을 요청합니다. debug에서 Crashlytics 업로드가 꺼져 있어도 주입한 ErrorReporter fake로 실제 화면 행동이 리포팅 경계를 정확히 한 번 통과하는지는 관찰할 수 있습니다. 인라인 두 건을 반영한 뒤 QA Metadata를 androidTest.required: true와 정확한 testRef·동일 ref evidence로 갱신해 주세요.
리뷰 지적대로 앞선 가드 셋이 아무것도 지키지 못하고 있었다. - androidTest 가 FakeErrorReporter() 를 만들자마자 버려, 새 계측 호출을 지워도 통과했다. reporter 를 밖에서 보존해 diary_submit 이 **정확히 1건**이고 성공한 재시도가 더 남기지 않는 것까지 단언한다 - 데일리질문 계측 테스트가 빈 테마만 compose 하고 submit() 을 직접 불러, 화면-ViewModel 결선을 지나지 않았다. 실제 DailyQuestionWriteScreen 을 띄우고 저장 버튼 클릭으로 실패를 만든다 — 화면을 태우니 에디터가 되돌려 주는 HTML(`<p>…</p>`)이 실제 값이라 기대값도 프로덕션 형태로 바뀌었다 - media_upload 가드가 일기 쪽만 봐서, 데일리질문 호출부를 지워도 통과했다. 같은 stage 의 두 호출부를 각각 가드한다 역검증 3종 모두 확인했다 — 각 계측 호출을 지우면 그 테스트가 실패한다(실기는 CI 와 같은 pixel2Api30 에서). PR 본문의 QA Metadata 는 develop 이 CI Test Plan 으로 교체해 그 형식으로 옮겼다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
세 지적 모두 맞습니다. 가드 셋이 실제로는 아무것도 지키지 못하고 있었습니다. P1 — 실제 화면 실패 경계에서 단언
assertEquals(listOf("diary_submit"), reporter.mindRecordStages)실패 1회분만 기록되고 성공한 재시도가 더 남기지 않는 것까지 봅니다. 이 테스트를 P1 — 데일리질문도 실제 화면으로빈 테마만 compose 하고 화면을 태우니 기대값이 하나 달라졌습니다 — 리치 에디터가 되돌려 주는 HTML 이 실제 값입니다. - assertEquals("오늘은 용기 냈다", viewModel.uiState.value.answer)
+ assertEquals("<p>오늘은 용기 냈다</p>", viewModel.uiState.value.answer)종전 테스트가 화면을 건너뛰어 원문 문자열이 남았던 것이고, 지금이 프로덕션 값입니다. P1 —
|
| 지운 계측 | 결과 |
|---|---|
DIARY_SUBMIT |
diaryFailureThenRetry_... FAILED (pixel2Api30) |
DAILY_QUESTION_SUBMIT |
dailyQuestion_failureThenRetry_... FAILED (pixel2Api30) |
데일리질문 MEDIA_UPLOAD |
데일리질문 미디어 업로드 실패도 media_upload 으로 기록된다 FAILED |
본문 형식
QA Metadata 가 develop 에서 CI Test Plan 으로 교체돼 그 스키마로 옮겼습니다 — mode: "selected" 로 위 두 테스트를 지정했습니다.
ktlintCheck · testDebugUnitTest(5건) · :app:assembleDebug green, MindRecordFlowAndroidTest 는 pixel2Api30 에서 3/3 통과입니다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1hyok
left a comment
There was a problem hiding this comment.
최신 HEAD 2b86faf 재리뷰입니다.
5be7cb9에서 기존 지적 3건이 모두 닫혔습니다. Diary 실제 화면의 실패→성공 재시도는 diary_submit을 정확히 1회 단언하고, DailyQuestion은 실제 저장 버튼을 거쳐 daily_question_submit 1회와 성공 재시도 시 추가 기록 없음까지 검증합니다. 데일리질문 미디어 업로드 호출부도 media_upload과 기존 UI 오류 상태를 함께 고정했습니다.
exact-head Ktlint, Android Lint, Unit Test, Screenshot, CodeQL, API 30·34 GMD가 모두 성공했습니다. 보안·성능 새 문제는 없고 정확성·유지보수성 blocker도 찾지 못했습니다. 제 변경 요청 기준으로는 해소됐으며, 최종 APPROVE는 현재 요청된 @koongmai의 승인 판단에 남깁니다.
1hyok
left a comment
There was a problem hiding this comment.
최신 HEAD 2b86faf를 다시 확인하고 승인합니다.
앞선 재리뷰에서 확인한 실제 Diary·DailyQuestion 화면 텔레메트리와 두 미디어 업로드 호출부 회귀 가드가 그대로 유지됐습니다. 승인 직전 14개 체크에 pending·failure가 없고 새 커밋도 없습니다.
|
❌ Screenshot test fail — baseline 과 CI rendered PNG 차이. 📦 Artifact: https://github.com/Afternote/Afternote-FE/actions/runs/33172209346 (페이지 하단 Artifacts → 🔍 확인 위치 (artifact zip 내부):
📝 조치 가이드:
CI ↔ 로컬 환경 차이는 본 워크플로의 container 단계가 root fix 한다 (README 의 "Compose Preview Screenshot Testing (docker baseline)" 섹션 참조). |
test: mindrecord androidTest를 JVM으로 이동 (#1090)
#1278(작성 실패 텔레메트리)이 develop 에 들어오며 `DailyQuestionWriteViewModel` 의 `errorReporter` 가 필수 인자가 됐다. develop 병합 뒤 이 계측이 컴파일되지 않았다. 앞 커밋 검증에서 샜다 — `:feature:mindrecord:presentation:testDebugUnitTest` 만 돌리고 `:app:compileDebugAndroidTestKotlin` 을 안 돌렸다. 계측 파일은 그 태스크에만 걸린다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Refs #964
무엇을 승격했나
diary_submitdaily_question_submitmedia_upload일괄 치환하지 않았습니다
Crashlytics 는 non-fatal 을 최근 8건만 보관합니다. 흔한 읽기 실패까지 넣으면 네트워크가 한 번 끊길 때 잡음이 한도를 채워 실제 장애를 밀어냅니다.
그래서 이슈의 판단 기준대로 «사용자가 쓴 것이 날아가고 재현이 어려운» 경로만 골랐습니다. 목록 로드 실패 같은 읽기 쪽은 이번에 넣지 않았습니다.
가드 — 양방향
WriteFailureReportingTest4건입니다.diary_submit으로 기록된다daily_question_submit으로 기록된다media_upload으로 기록된다계측 호출을 지우면 해당 테스트가
FAILED인 것을 확인했습니다.stage 문자열까지 단언합니다. 콘솔 필터가 그 값이라, 바뀌면 그 시점 이후 기록이 기존 이슈 그룹과 끊겨 추이를 잃습니다(기존
MindRecordFailureStageKDoc 의 규약).딸려 나온 것
두 ViewModel 생성자에
ErrorReporter가 늘어 호출부 20곳(단위 12 · 계측 4 · 나머지)을 함께 고쳤습니다. 계측 테스트 쪽은 이미 있던FakeErrorReporter를, 단위 테스트 쪽은RecordingErrorReporter를 씁니다. Firebase 의존은 추가하지 않습니다 — 주입 타입은ErrorReporter추상이고 실제 결선은app의 모듈에만 있습니다.검증
ktlintCheck:feature:mindrecord:presentation:testDebugUnitTest:app:compileDebugAndroidTestKotlin·:app:assembleDebugMindRecordFlowAndroidTest(CI 와 같은pixel2Api30)시안 대조 — 해당 없음. 화면·리소스 변경이 0건이고 텔레메트리 호출만 추가됩니다.
CI Test Plan
{ "androidTest": { "mode": "full", "reason": "쓰기 계측 3종이 두 작성 ViewModel 의 생성자를 바꿔 app/src/androidTest 의 여러 파일이 함께 수정됐다. 변경 테스트가 20개 상한을 넘어 전체 회귀로 컴파일과 런타임 호환성을 함께 검증한다." } }