Skip to content

ErrorReporter 계측 신설 — 실패가 Logcat 에도 텔레메트리에도 안 남는다 #964

Closed

Description

@OnlyReadOneBook

#928 에서 분할된 이슈다. 인프라 전제·제약·판단 기준을 아래에 그대로 복제해 두었으니 이 이슈만 보고 진행하면 된다. 다른 모듈은 건드리지 않으므로 나머지 두 이슈와 파일이 겹치지 않는다.

배경

Result.failure 로 흡수되는 실패는 Crashlytics 자동 수집(uncaught 예외)에 잡히지 않는다. ErrorReporter 를 거쳐 명시적으로 기록해야 릴리즈에서 보인다.

feature/mindrecord 는 전 모듈 중에서도 특이하다 — Log.e 도 ErrorReporter 도 0건이다. 실패가 UI 상태로만 흡수되고 어디에도 기록되지 않아, 릴리즈에서 무슨 일이 있었는지 알 방법이 전혀 없다.

app·feature/onboarding·feature/afternote/presentation 은 이미 채택이 끝났다(30곳). 이 모듈은 계측을 처음부터 세우는 작업이다.

인프라 — 새로 만들 것이 없다

  • ErrorReporter 를 생성자에 주입하면 끝이다. 정책(코루틴 취소 제외·예외 문구 redact)은 인터페이스가 이미 지킨다 — 호출부에서 다시 거를 필요 없다
  • 실제 Crashlytics 결선은 app 의 ErrorReportingModule 에만 있다 — 이 모듈에 Firebase 의존을 추가하지 않는다. 주입받는 타입은 항상 ErrorReporter 추상이다
  • stage 어휘는 모듈마다 따로 둔다(도메인 어휘라서 의도된 분산이다). 선례 3개:

제약 — 일괄 치환 금지

Crashlytics 는 non-fatal 을 최근 8건만 보관하고 초과분을 오래된 것부터 버린다 (코드 근거 · Firebase 문서).

전부 recordFailure 로 갈아끼우면 네트워크가 한 번 끊길 때 잡음이 한도를 채워 실제 장애를 밀어낸다. 이 작업은 "어떤 실패가 텔레메트리 가치가 있는가" 를 골라내는 분류이지 치환이 아니다.

판단 기준

  • 승격(ErrorReporter) — 사용자 작업이 실패했고 서버·기기 문제가 의심되며, 재현이 어려워 실기 QA 로 못 잡는 것 (저장 실패, 세션 만료, 업로드 실패)
  • 유지(Logcat) — 개발 중 흐름 추적용이고 정상 경로에서도 발생 가능한 것 (지문 인증 취소 등)
  • 삭제 — 이미 상위에서 처리되어 중복이거나, 디버깅이 끝난 흔적

승격한 지점은 어느 단계에서 깨졌는지 구분할 stage 속성을 함께 넘긴다. 개인정보·자격증명은 넣지 않는다.


작업 — 계측 지점 선정 후 MindRecordFailureStage 신설

데이터 계층(DailyQuestionRepositoryImpl 등)이 runCatching 으로 감싸 Result 를 돌려주므로, 계측 지점은 ViewModel 의 onFailure 다. 아래는 실측한 후보 전량이다.

쓰기 실패 — 승격 1순위

사용자가 작성한 내용이 날아가는 경로라 재현이 어렵고 실기 QA 로 못 잡는다.

읽기 실패 — 선별 승격

목록 로드 실패는 흔해서 전부 넣으면 한도(최근 8건)를 잡음으로 채운다. 화면 단위로 골라낸다.

취소 혼입 주의

DailyQuestionListViewModel.kt#L78 · DiaryListViewModel.kt#L100 · WeeklyReportViewModel.kt#L104 에 "repository 가 runCatching 으로 감싸 CancellationException 까지 실패로 바꿔 돌려준다" 는 주석이 이미 달려 있다.

ErrorReporter.recordFailure 는 이 취소를 창구에서 걸러 주므로 호출부에서 따로 막지 않아도 된다. 다만 화면 이탈이 잦은 목록 화면이라, 승격 대상을 고를 때 이 성질을 감안한다.


완료 조건

  • 승격 지점이 선정되고 MindRecordFailureStage + recordMindRecordFailure 확장이 정의되어 반영
  • 승격 지점에 stage 속성이 붙어 있다
  • feature/mindrecord 에 Firebase 의존이 추가되지 않았다
  • 판정 로직이 생긴 곳은 단위 테스트로 덮는다 (선례 · 이 모듈에는 DailyQuestionWriteViewModelTest 가 이미 있다)

Activity

  1. added 2 commits that reference this issue on Aug 25, 2026
  2. changed the title [-]refactor(mindrecord): ErrorReporter 계측 신설 — 실패가 Logcat 에도 텔레메트리에도 안 남는다[/-] [+]ErrorReporter 계측 신설 — 실패가 Logcat 에도 텔레메트리에도 안 남는다[/+] on Aug 28, 2026
  3. added
    refactor동작 변경 없이 코드 구조·책임·경계를 개선하는 작업
    area:mindrecord마음의 기록 기능 영역
    and removed
    enhancementNew feature or request
    on Aug 28, 2026
  4. added a commit that references this issue on Aug 29, 2026
  5. added
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Sep 1, 2026
  6. removed
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Sep 3, 2026
  7. Sadturtleman commented on Sep 7, 2026

    @Sadturtleman
    Collaborator

    완료 확인 (2026-09-07, develop 기준)

    이슈가 든 완료 조건 넷을 현재 코드로 대조했고 전부 충족돼 있다. 구현이 여러 PR 에 나눠 들어가면서 이슈만 열린 채 남았다.

    1. MindRecordFailureStage + recordMindRecordFailure 신설

    feature/mindrecord/presentation/.../reporting/MindRecordFailureReporting.kt 에 stage 어휘 12종이 서 있고 각각 왜 승격 대상인지 KDoc 이 붙어 있다.

    draft_list_load · draft_delete · diary_submit · daily_question_submit · media_upload
    record_list_load · record_delete · record_detail_load · daily_question_load
    memory_space_load · weekly_report_load · receiver_record_load
    

    2. 승격 지점 — 이슈가 든 후보 전량이 붙어 있다

    이슈 후보 현재 자리 stage
    쓰기 — DiaryWriteViewModel DiaryWriteViewModel.kt:236 diary_submit
    쓰기 — DailyQuestionWriteViewModel DailyQuestionWriteViewModel.kt:345 daily_question_submit
    쓰기 — DraftListViewModel DraftListViewModel.kt:90 · :166 draft_delete · draft_list_load
    읽기 — 질문 로드 DailyQuestionWriteViewModel.kt:90 · :133 daily_question_load
    읽기 — DiaryListViewModel DiaryListViewModel.kt:154 record_list_load
    읽기 — WeeklyReportViewModel WeeklyReportViewModel.kt:204 weekly_report_load
    읽기 — 수신자 열람 ReceiverMindRecordViewModel.kt:105 receiver_record_load

    후보 밖에서 추가로 붙은 곳도 같은 판단 기준을 따른다 — 미디어 업로드(media_upload), 기록 삭제(record_delete, 되돌릴 수 없음), 상세 조회(record_detail_load, 「내가 쓴 기록이 안 열린다」), 추억 공간 전체 실패(memory_space_load).

    「일괄 치환 금지」도 지켜졌다 — 전달 조건 미충족(DeliveryNotReadyException)은 정상 상태라 기록하지 않는다고 ReceiverMindRecordViewModel 에 근거째 적혀 있고, 추억 공간은 합친 결과가 비었고 실패 출처가 있을 때만 올린다.

    3. Firebase 의존 없음

    feature/mindrecord/*/build.gradle.kts 에 firebase·crashlytics 문자열이 0건이다. 주입받는 타입은 core:common 의 ErrorReporter 추상뿐이다.

    4. 판정 로직의 단위 테스트

    • WriteFailureReportingTest — 쓰기 실패 승격
    • ReadFailureReportingTest — 읽기 실패 선별(부분 실패는 안 올리고 전체 실패만 올리는 경계 포함)
    • ReceiverFailureReportingTest — 수신자 경로

    더 할 일이 남아 있지 않아 닫는다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:mindrecord마음의 기록 기능 영역refactor동작 변경 없이 코드 구조·책임·경계를 개선하는 작업

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions