Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Crash Auto Pipelines

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →
Avatar for Veronikapj Veronikapj
November 02, 2026

Crash Auto Pipelines

DroidKnights 2026

Avatar for Veronikapj

Veronikapj

November 02, 2026

More Decks by Veronikapj

Other Decks in Programming

Transcript

  1. ✦ DROIDKNIGHTS 2026 크래시 추적에 뺏긴 멘탈, AI가 올려준 PR로

    되찾기 크래시가 터지고 내 자리로 Draft PR이 배달되기까지의 파이프라인 구축기 배필주 컬리 프로덕트앱개발팀 Staff Engineer 2026.11.02 서울 ST센터
  2. ✦ 01 INTRO 크래시 발생! 😱1 😨1 🫠1 알림 →

    확인 → 대응 시작 N E X T › 크래시 하나에 팀 전체가 멈춤 11:23 😱 😵‍💫 PM 퇴근 후 가장 보고 싶지 않은 슬랙 알림, Velocity Alert 02
  3. ✦ 01 INTRO N E X T › 반복되는 작업은

    시스템에 맡기자 크래시 하나에 팀 전체가 멈춤 🚨 👩‍💻 🧑‍💻 👨‍💻 👩‍🔧 새 크래시 급증 하던 일 01 새 화면 개발 각자 흩어진 정보 수집 발생 규모 크래시 콘솔 02 Compose 전환 버전과 기기 이벤트 상세 03 릴리즈 QA 대응 직전 행동과 로그 Breadcrumb 04 라이브러리 버전 업데이트 조사할 코드 앱 레포 하던 일 멈춤 하던 일 멈춤 → 각자 흩어진 정보 수집 → 무슨 일이 일어났는지 맞추기 무슨 일이 일어났을까? ⏱ 한나절 소요 03
  4. ✦ 01 INTRO N E X T › 시스템과 담당자가

    나눠 맡는 일 GOAL 반복되는 대응은 시스템에 맡기고 팀은 문제 해결에 집중하기 그러려면 파이프라인을 어떻게 만들어야 할까? 감지 → 수집 → 기록 → 분석과 수정 → 검토와 배포 → 재발 확인 PA R T 0 3 PA R T 0 4 PA R T 0 5 어떤 정보를 모아야 할까? AI에게 어떻게 조사시킬까? 나온 결과는 어떻게 검토할까? 04
  5. ✦ 01 INTRO N E X T › 크래시 대응

    시스템 전체 지도 시스템은 수정안까지 담당자는 판단부터 시스템이 맡은 일 담당자가 이어갈 일 파이프라인 파이프라인 AI 정보 모으기 티켓 채우기 처음 분석 AI Draft PR 수정안 추가 확인 보완 리뷰 시스템 반복되던 준비를 대신 흩어진 정보 모으기, 티켓에 근거 채우기, 원인 후보와 검토할 코드 초안 담당자 판단이 필요한 일에 집중 근거 다시 확인, 부족한 수정 보완, 코드 리뷰, 배포 결정 배포 05
  6. ✦ 02 SYSTEM MAP N E X T › PART

    03 어떤 정보를 모아야 할까? 한눈에 보는 크래시 대응 시스템 지도 감지와 수집 Firebase, Google Cloud Crashlytics Firebase Alerts BigQuery Jira 티켓 Crashlytics export GA4 분석과 수정 GitHub 실행 수집 중 GitHub Actions Claude, Codex API 스택, 로그, 환경 조회 Cloud Run 함수 기록 Jira crash-ready 티켓 생성과 채우기 → 알림이 오면 함수가 BigQuery 조회 스택, 로그, 환경, 직전 행동 모으기 분석 코멘트 분석 결과 → 데이터가 늦으면 티켓 먼저 나중에 채움 Draft PR 기록 → 결과는 먼저 티켓에 고칠 수 있을 때만 PR 담당자 리뷰와 배포 배포 후 재발하면 티켓 다시 열림 06
  7. ✦ 0 3 D ATA N E X T ›

    스택만으로 답할 수 있는 질문 PART 03 어떤 정보를 모아야 할까? 감지 → 수집 → 기록 → 분석과 수정 → 검토와 배포 → 재발 확인 PA R T 0 3 PA R T 0 4 PA R T 0 5 어떤 정보를 모아야 할까? AI에게 어떻게 조사시킬까? 나온 결과는 어떻게 검토할까? 07
  8. ✦ 0 3 D ATA NEXT › 스택만으로 답할 수

    없는 질문 Stack Trace 만으로 무엇을 알 수 있을까? Crashlytics Stack trace Fatal Exception: java.lang.IllegalStateException Can not perform this action after onSaveInstanceState at androidx.fragment.app.FragmentManager.checkStateLoss at androidx.fragment.app.FragmentManager.enqueueAction at androidx.fragment.app.BackStackRecord.commit ◀ 어떤 예외가 어디서 발생했는지 예외와 메시지 무슨 예외인지 호출 경로 어디서 터졌는지 그 밖의 질문은? → at com.example.detail.DetailActivity.showOptionSheet at com.example.detail.DetailActivity$onLoaded$1.invoke at kotlinx.coroutines.DispatchedTask.run 08
  9. ✦ 0 3 D ATA NEXT › 콘솔에 있던 정보를

    BigQuery로 받아오기 더 필요한 것은? 조사할 때 떠올리는 질문 필요한 정보 영향 얼마나 발생했고, 몇 명이 겪었을까? → 발생 건수 발생 조건 어떤 환경에서 발생했을까? → 버전 기기 직전 행동 직전에 무엇을 했고, 어떤 작업 중이었을까? → 화면 행동 기록 사용 이력 설치 직후일까, 계속 쓰다가일까? → 설치 시점 스택에는 없음 → 크래시마다 사람이 따로 찾아 모으던 정보 영향 사용자 OS 발생 시각 앱 상태 앱 로그 몇 번째 세션 행동 기록 그럼 이 정보는 어디에? → 09
  10. ✦ 0 3 D ATA N E X T ›

    받아온 정보를 티켓 하나에 채우기 콘솔에서 보던 정보를 BigQuery로 받아오기 Crashlytics 콘솔 콘솔에서 보던 정보 스택, 통계, 로그, 기기 IllegalStateException at DetailActivity 이벤트 영향 사용자 영향 BigQuery export 스택 Breadcrumbs 로그 기기 Keys 실패 경로 직전 행동 직전 행동 발생 조건 - Crashlytics 이벤트 테이블을 SQL로 조회 Jira 티켓에 채우기 → 대부분 콘솔에 있음. 다만 매번 사람이 열어 봐야 했음 10
  11. ✦ 0 3 D ATA NEXT › 여러 번 발생했다면

    어느 1건을 채울까 규모와 환경, 마지막 동작까지 한 티켓에 전에는 콘솔 탭마다 따로 Jira 이슈 요약 JIRA-1001 [자동분석][Android][Fatal] IllegalStateException 통계 통계 발생 건수 예외 정보 blame, 타입, 메시지 스택 트레이스 예외, 환경, 스택 Breadcrumbs 사용자 경로 Logs 앱 로그 발생 환경 기기, OS 영향 사용자 버전 여러 발생 집계 주요 기기 선택한 1건 FOREGROUND PORTRAIT 콘솔 탭을 돌아다니지 않아도 담당자는 티켓 하나만 열면 됨 메모리, 저장 공간 스택 IllegalStateException … 재현 경로 _vs _sc=DetailActivity (최신→과거) 사용 이력 같은 설치의 앞선 이벤트 (뒤에서) 로그 Launch GetProductUseCase … 선택한 1건 선택한 1건 Breadcrumbs Google Analytics Logs 11
  12. ✦ 0 3 D ATA N E X T ›

    Breadcrumb이 뭐지? 단서가 가장 많은 1건으로 티켓 채우기 같은 크래시가 여러 번 났다면 최근 14일 동안의 발생 ↓ 단, 통계는 모든 발생을 합산 발생 건수, 영향 사용자, 버전, 주요 기기 단서가 가장 많은 1건 Breadcrumb이 가장 많이 남은 건 ↓ 환경, 스택, 사용자 경로, 로그 모두 이 1건에서 가져옴 1건만 보는 이유 모든 발생의 Breadcrumb을 읽으면 조회 시간이 초과됨 그래서 단서가 가장 많은 1건만 읽기로 함 12
  13. ✦ CASE JIRA-1005 NullPointerException NEXT › 앱 안에서 일어난 일은

    앱 로그에서 Breadcrumb은 GA4가 남긴 크래시 직전 행동 기록 1 Breadcrumb에 담기는 것 CRASH 이벤트 이름, 파라미터, 시각, 직접 남긴 분석 이벤트 2 GA4 이벤트의 직전 일부 Breadcrumbs 직전 약 2분 겹치는 구간은 같은 이벤트 3 GA4 연동과 이벤트 기록 필요 이벤트로 남긴 것만 보임 4 GA4 연동, 이벤트로 기록한 것만 같은 이벤트 Google Analytics 4 Breadcrumb에 없는 앞선 맥락 앞선 행동은 GA4에서 조회 9분 전 이동, 설치 시점, 세션 회차 −9분 −2분 OOM, 화면 반복 진입처럼 긴 사용 흐름을 봐야 하는 크래시에 유용 JIRA-1005 앞선 맥락도 필요할까 티켓 생성 언제 누가 만들어야 할까 13
  14. ✦ 0 3 D ATA NEXT › 알림이 데이터보다 먼저

    올 때 앱 로그에 담기는 것 개발자가 코드에 직접 남긴 문자열 우리 앱은 UseCase 실행 공통 지점에서 시작, 소요 시간, 분석 이벤트 이름을 남김 Firebase 문서 예시 // Kotlin Firebase.crashlytics.log("message") Android Crashlytics Logs → 시각 예시 메시지 −00:11 Launch GetProductUseCase −00:11 product_selection 보이는 곳 한도 콘솔 Logs 탭, BigQuery logs 같은 크래시 이벤트 안 세션당 64kB 넘으면 오래된 줄부터 삭제 출처: Firebase 문서 Customize crash reports, BigQuery dataset schema −00:11 GetProductUseCase took 231ms to execute −00:08 Launch AddToCartUseCase −00:08 AddToCartUseCase took 126ms to execute 00:00 CRASH 14
  15. ✦ CASE N E X T › 같은 크래시라도 발생

    경로가 여러 개일 수 있음 Jira 티켓을 먼저 만들고 데이터는 도착한 뒤 채우기 알림이 먼저 오면 조회가 비어 있음 → 데이터가 늦으면 티켓부터 만들고 나중에 채움 1 BigQuery 조회 2 짧은 재조회 3 티켓 먼저 생성 4 매시간 재조회 알림 받자마자 바로 도착했는지 두 번 확인 그래도 없는 일부만 같은 티켓에 채우기 Cloud Functions 알림 수신 후 조회 BigQuery 데이터 도착 데이터 도착 ✕ 결과 없음 Jira 티켓 상태 JIRA-1005 앞선 맥락도 필요할까 대부분 여기서 생성 수집 중 티켓 (일부) 5 crash-ready 분석 시작 티켓 생성 언제 누가 만들어야 할까 15
  16. ✦ N E X T › PART 04 AI에게 어떻게

    조사시킬까? 남은 과제 발생 경로가 여러 개인 경우 경로 예시 경로 1 홈 → 상품 상세 지금 파이프라인 단서가 가장 많은 1건으로 분석 Breadcrumb가 가장 많이 남은 건 크래시 하나 경로 2 검색 → 상품 상세 다른 경로 대응 (고민 중) 하위 티켓으로 분리? 사람이 확인? 발생 경로는 여러 개일 수 있음 → 경로를 나눠 보는 것은 아직 파이프라인 밖 16
  17. ✦ 0 4 I N V E ST I G

    AT E N E X T › 모은 정보로 AI에 분석을 맡기는 방법 PART 04 AI에게 어떻게 조사시킬까? 감지 → 수집 → 기록 → 분석과 수정 → 검토와 배포 → 재발 확인 PA R T 0 3 PA R T 0 4 PA R T 0 5 어떤 정보를 모아야 할까? AI에게 어떻게 조사시킬까? 나온 결과는 어떻게 검토할까? 17
  18. ✦ 0 4 I N V E ST I G

    AT E N E X T › 분석 결과를 검증할 수 있게 만드는 방법 분석 방법을 사람이 설계할 필요가 있을까? AI 대화창 결과 A 크래시 분석해 줘 결론: 생명주기 문제 결과에 안 보이는 과정 로그와 코드까지 확인 결과 B 분석 방법 없이 그냥 던지면? ↑ 결과를 받은 담당자 두 결과가 똑같아 보임 무엇을 확인했을까? 어떤 근거로 판단했을까? 결론: 생명주기 문제 결과에 안 보이는 과정 스택만 보고 추정 ✕ 맞는 결론인지 검증할 수 없음 18
  19. ✦ 0 4 I N V E ST I G

    AT E N E X T › 확인 항목은 크래시를 볼 때 떠오르는 질문에서 나옴 무엇을 어떤 순서로 확인할지 사람이 먼저 정하기 사람이 먼저 정한 분석 방법 1 2 3 무엇을 확인할지 확인 항목 어떤 순서로 확인할지 조사 순서 무엇을 근거로 남길지 결과에 함께 기록 AI 분석 결과 결과를 받은 담당자 결론 결과마다 같은 확인 항목과 근거가 남음 결과에 함께 보이는 것 ✓ 확인한 항목 무엇을 보고 그렇게 판단했는지 따라갈 수 있음 ✓ 확인한 순서 ✓ 판단 근거 ✓ 결론을 검증할 수 있음 왜 사람이 AI 결론은 틀릴 수 있고, 사용자 경험을 해치지 않는지는 코드 밖의 맥락이라 사람이 판단 19
  20. ✦ CASE 화면 이동 뒤 BottomSheet 표시 N E X

    T › 세 기록을 시간 순서대로 맞춰 보기 IllegalStateException 이 크래시에서 무엇부터 확인하고 싶을까? 이걸 보고 떠오른 질문이 그대로 확인 항목이 됨 예외 스택 STA C K 1 IllegalStateException: Can not perform this action after onSaveInstanceState 2 at FragmentManager.checkStateLoss 1 상태가 저장된 뒤 띄우려 함 3 at DialogFragment.show 4 at DetailActivity.showOptionBottomSheet 2 조회가 끝나면 BottomSheet를 띄움 5 at DetailActivity.runOnLoadSuccess 6 at SharedFlow … 7 at DetailViewModel.startOptionSelection 화면 기록 BREADCRUMB 19:53:09 19:53:16 _vs _vs _sc=DetailActivity _sc=ListActivity 3 사용자는 목록 화면으로 이동 화면 이동 뒤 BottomSheet 표시 앱 로그 A P P LO G [19:53:09] Launch LoadDetailExtrasUseCase [19:53:17] LoadDetailExtrasUseCase took 8276ms to execute 4 조회는 이동한 뒤에 끝남 20
  21. ✦ CASE 화면 이동 뒤 BottomSheet 표시 N E X

    T › 말풍선 질문을 코드에서 확인한 결과 IllegalStateException 다른 화면으로 이동한 뒤에 끝난 조회 상세 화면 진입 화면 기록 목록 화면 이동 Breadcrumb 3 목록 화면 뒤에도 상세는 결과를 받고 있었을까? 추가 정보 조회 시작 앱 로그 조회 완료 (8276ms) UseCase 실행 1 상세 화면은 왜 추가 정보를 조회했지? 2 조회가 끝나면 어떻게 옵션 선택창이 뜨지? 옵션 선택창(BottomSheet) 표시 → 예외 예외 스택 크래시 4 그때 상세 화면의 옵션 선택창을 띄워도 되는 상태였을까? 19:53:09 화면 이동 뒤 BottomSheet 표시 약 7초 19:53:16 19:53:17 21
  22. ✦ CASE 화면 이동 뒤 BottomSheet 표시 N E X

    T › 사례 이름을 빼고 질문 일반화하기 IllegalStateException 안 보이는 화면에서도 계속된 이벤트 처리 상세 화면 진입 상세 화면 상태 다른 화면으로 이동 조회 완료 RESUMED 보이고 입력 받음 STARTED 화면에 보임 CREATED 화면에 안 보임 onCreate onStart onResume 화면 보임 STARTED 이상 이벤트 처리 repeatOnLifecycle (CREATED) onPause onStop onSaveInstanceState onStart부터 onStop까지 CREATED 이상이면 처리 안 보여도 처리 ✕ 옵션 선택창 show() 예외 상태 저장 뒤라 표시할 수 없음 결론 표시 이벤트를 처리하는 범위가 화면이 보이는 범위보다 넓었음 화면 이동 뒤 BottomSheet 표시 22
  23. ✦ 0 4 I N V E ST I G

    AT E N E X T › 질문과 찾아볼 곳을 스킬에 적기 질문마다 코드에서 찾아볼 곳 떠오른 질문 코드에서 찾아볼 곳 1 어디서 시작했지? 호출 시작점 UseCase 호출부, 작업을 시작한 화면 2 끝나면 어떻게 화면에 표시되지? 결과 전달 경로 조회 성공 콜백, Flow 이벤트 처리 3 화면이 바뀐 뒤에도 결과를 받았을까? 생명주기 조건 작업 scope, 이벤트 수집 범위 4 그때 띄워도 되는 상태였을까? 화면 상태 FragmentManager 상태, 표시 조건 23
  24. ✦ 0 4 I N V E ST I G

    AT E N E X T › 크래시 유형마다 먼저 볼 곳이 다름 질문과 찾아볼 곳을 스킬에 적기 1 사람이 정리한 것 질문 1 질문 2 질문 3 질문 4 → 호출 시작점 → 결과 전달 경로 → 생명주기 조건 → 화면 상태 2 스킬에 옮겨 적기 3 AI가 매번 따라감 질문 + 코드에서 찾아볼 곳 = 확인 항목 한 줄 crash 시점의 생명주기 상태와 작업을 띄운 트리거 확인 크래시마다 같은 순서로 확인 ✓ repeatOnLifecycle (CREATED) 안 보이는 화면에서도 처리 ✓ 확인한 근거를 분석 코멘트에 남김 analyze-crash T2 23장: 질문마다 코드에서 찾아볼 곳 한 번 옮겨 두면 사람이 바뀌어도, 크래시가 달라도 같은 순서로 확인 24
  25. ✦ 0 4 I N V E ST I G

    AT E N E X T › 확인한 결과는 티켓 분석 코멘트에 정리 크래시 유형마다 먼저 볼 곳 analyze-crash 기준 6유형 유형 이런 예외일 때 먼저 볼 곳 NullPointerException null이 어디서 들어왔는지 IllegalStateException 그때 화면 상태와 작업을 띄운 트리거 T3 동시성/스레드 CalledFromWrongThreadException 어느 스레드가 공유 상태를 바꿨는지 T4 ANR Application Not Responding 메인 스레드를 막은 작업 T5 Native/3rd-party JNI, 외부 SDK, androidx 내부 SDK 버전과 알려진 이슈 T6 직렬화/저장 JsonSyntaxException + Gson 저장 위치와 @SerializedName 유무 T1 Null/Type T2 생명주기 화면 이동 뒤 BottomSheet 표시 25
  26. ✦ 0 4 I N V E ST I G

    AT E N E X T › 판정 값에 따라 정해지는 후속 작업 분석 결과를 티켓에 어떻게 정리할까? I S S U E 티켓의 분석 코멘트 1 한 줄 요약 어디서 터지는지와 영향 규모 2 어떤 흐름으로 터졌나 사용자 행동을 단계별로 3 왜 터졌나 코드로 확인한 이유 4 어떻게 막나 해결 방향과 추천 5 참고 담당자 후보, 우선순위, 판정, 미검증 이렇게 맞추는 이유 담당자가 분석을 다시 하지 않고 바로 대응을 이어 가도록 26
  27. ✦ 0 4 I N V E ST I G

    AT E N E X T › PART 05 나온 결과는 어떻게 검토할까? 판정 값에 따라 정해지는 후속 작업 1 한 줄 요약 2 어떤 흐름으로 터졌나 3 왜 터졌나 FIXABLE 판정의 근거 4 어떻게 막나 5 참고 ESCALATE 가 고칠 곳 있음 → 수정안 Draft PR 앱에서 막을 수 있음 → 방어안 판정: FIXABLE ESCALATE 나 손댈 곳 없음 → Known Issue 화면 이동 뒤 BottomSheet 표시 사례는 FIXABLE → 수정안 Draft PR로 이어짐 (PART 05) 27
  28. ✦ 05 REVIEW N E X T › 라벨이 올라가도

    확인된 것은 일부 PART 05 나온 결과는 어떻게 검토할까? 감지 → 수집 → 기록 → 분석과 수정 → 검토와 배포 → 재발 확인 PA R T 0 3 PA R T 0 4 PA R T 0 5 어떤 정보를 모아야 할까? AI에게 어떻게 조사시킬까? 나온 결과는 어떻게 검토할까? 28
  29. ✦ 05 REVIEW N E X T › 수정안 Draft

    PR을 만드는 원칙 라벨이 올라가도 확인된 것은 일부 Jira 라벨 티켓 생성 → 분석 준비 → 분석 완료 → 수정안 준비 → 배포 완료 분석 완료 수정안 준비 배포 완료 원인 후보가 나옴 검토할 코드가 생김 배포 버전에 포함됨 아직 모르는 것 아직 모르는 것 아직 모르는 것 원인이 맞는지 사용자 흐름 전체가 괜찮은지 크래시가 정말 사라졌는지 → 발생 조건 그대로 재현 → 담당자 리뷰 → 재발 확인까지 라벨은 어디까지 왔는지를 보여 줄 뿐, 해결됐다는 확인은 아님 29
  30. ✦ 05 REVIEW NEXT › AI에게 최소한으로 고쳐 달라고 한

    이유 수정안 Draft PR을 만드는 원칙 01 빠르게 02 분석이 끝나면 바로 수정안까지 작게 확신 있는 최소 수정만 03 Draft PR로 사람이 확인하고 보완한 뒤에 나감 30
  31. ✦ 05 REVIEW N E X T › 담당자가 다시

    본 세 사례 AI에게 코드를 맡기면서 왜 최소한으로 고쳐 달라고 했을까? 먼저, 발생 직후 이후, 검토 후 AI가 준비 담당자가 판단 최소 수정 근본 수정 앱 종료부터 막음 문제가 생기는 구조를 고침 › 긴급할 때 검토할 코드를 빨리 받음 이어서 › 엔지니어가 정한 범위 안에서만 수정 try-catch나 guard를 근본 해결책처럼 내지 않음 › 범위와 시점은 분석을 보고 결정 › 같은 Draft PR에서 이어서 보완 예: 화면이 닫힌 뒤 남는 팝업과 동작을 함께 정리 빠르게 준비하는 최소 수정, 시간을 들여 완성하는 근본 수정 31
  32. ✦ 05 REVIEW NEXT › 분석이 남긴 원인과 세 가지

    방향 AI 초안 뒤에 담당자가 다시 본 세 가지 사례 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 IllegalStateException HttpException IllegalStateException 담당자가 본 것 담당자가 본 것 담당자가 본 것 사용자 흐름 원인 설명 수정 방향 크래시를 막은 수정이 사용자 대기까지 줄이는지 분석 코멘트가 말한 원인이 실제 크래시와 맞는지 AI는 구조를 그대로 둔 수정을 고르고, 구조는 담당자가 바꿈 크래시 수정이 UX에도 맞을까 분석이 원인을 맞게 설명할까 구조를 바꾸는 판단은 누가 세 사례 모두 같은 Draft PR 안에서 담당자가 보완 32
  33. ✦ CASE 화면 이동 뒤 BottomSheet 표시 NEXT › Draft

    PR이 고른 수정 IllegalStateException 분석이 남긴 원인과 세 가지 방향 크래시 다시 보기 판정 FIXABLE 화면을 떠났는데도 요청된 정보 조회 이벤트 흐름이 끝나지 않고 선택창을 띄우려다 크래시 분석에서 제시한 세 가지 해결 방향 ① 추천 ② 병행 ③ 검토 화면에 보일 때만 이벤트 수집 화면을 떠났으면 선택창 생략 조회 대기 시간 제한 repeatOnLifecycle(STARTED) isStateSaved 확인 후 return 화면 상태 문제는 그대로 남음 1 분석 결과 화면 이동 뒤 BottomSheet 표시 › 2 Draft PR의 선택 토큰 재발급 401 미처리 › 3 담당자 판단 › 4 담당자 보완 해제된 ActivityResultLauncher 실행 33
  34. ✦ CASE 화면 이동 뒤 BottomSheet 표시 NEXT › 사용자

    흐름으로 다시 본 수정 IllegalStateException Draft PR은 세 방향 중 ①과 ②만 반영 P R - A DetailActivity.kt DRAFT − repeatOnLifecycle(Lifecycle.State.CREATED) { 화면에 보일 때만 이벤트 수집 ① ✓ 반영 + repeatOnLifecycle(Lifecycle.State.STARTED) { + if (supportFragmentManager.isStateSaved) + 화면을 떠났으면 선택창 생략 ② ✓ 반영 return@launch 크래시는 막힘. 조회 대기는 손대지 않음 PR-A 첫 수정의 diff 일부. 클래스명은 일반화 1 분석 결과 화면 이동 뒤 BottomSheet 표시 › 2 Draft PR의 선택 토큰 재발급 401 미처리 › 3 담당자 판단 › 4 담당자 보완 해제된 ActivityResultLauncher 실행 34
  35. ✦ CASE 화면 이동 뒤 BottomSheet 표시 N E X

    T › 기다리던 값의 실제 사용처 IllegalStateException 구매 전에 추가 정보 조회를 꼭 기다려야 했을까? 담당자 리뷰 AI 수정 코드에 더해 사용자 흐름까지 다시 확인 추가 정보 조회 결과를 기다림 구매하기 클릭 옵션 선택창 이번 기록에서 8.3초 “크래시는 막아도, 사용자는 여전히 기다려야 하네” Draft PR에서는 ① ②만 반영, 조회 대기는 그대로 1 분석 결과 화면 이동 뒤 BottomSheet 표시 › 2 Draft PR의 선택 토큰 재발급 401 미처리 › 3 담당자 판단 › 4 담당자 보완 해제된 ActivityResultLauncher 실행 35
  36. ✦ CASE 화면 이동 뒤 BottomSheet 표시 N E X

    T › 담당자가 같은 PR에서 보완한 수정 IllegalStateException 8초를 기다린 값은 장바구니 통계에만 쓰임 await() 옵션 선택 이벤트 장바구니 통계 속성 추가 정보 조회 결과를 기다림 값을 실어 선택창으로 전달 담기, 담기 성공 통계의 부가 속성 C OD E 개념 코드 최종 사용처 val hasExtraSection = extras 선택창을 여는 데 필요한 값은 아님 ?.await() 장바구니 담기 통계에 붙는 추가 정보 섹션 값 ?.getOrNull()?.items has_extra_section: Boolean? ?.isNotEmpty() emit(ShowOptionSheet(hasExtraSection)) 실제 구조를 단순화한 개념 코드, 이름 일반화. 사용자 행동 통계이며 크래시 분석과는 별개 1 분석 결과 화면 이동 뒤 BottomSheet 표시 › 2 Draft PR의 선택 토큰 재발급 401 미처리 › 3 담당자 판단 › 4 담당자 보완 해제된 ActivityResultLauncher 실행 36
  37. ✦ CASE 화면 이동 뒤 BottomSheet 표시 NEXT › 토큰

    재발급 401 미처리 사례의 원인 설명 IllegalStateException 조회 대기 없이 선택창을 바로 표시하도록 수정 P R - A 담당자 보완 개념 코드, 코드 일반화 DRAFT val hasExtraSection = extras + ?.takeIf { + + 조회가 끝난 값만 통과 it.isCompleted && !it.isCancelled 아직이면 null, 기다리지 않음 } ?.await()?.getOrNull() 실패나 빈 값도 null ?.items?.isNotEmpty() 통계 값만 비고 흐름은 계속 emit(ShowOptionSheet(hasExtraSection)) 통계 값이 빌 수 있어 리뷰에서 결정 1 분석 결과 화면 이동 뒤 BottomSheet 표시 이 속성의 원래 의미 › 선택창은 바로 표시 API 지연과 데이터 없음 구분 2 Draft PR의 선택 토큰 재발급 401 미처리 › iOS와 관련자 공유 3 담당자 판단 › 4 담당자 보완 해제된 ActivityResultLauncher 실행 37
  38. ✦ CASE 토큰 재발급 401 미처리 N E X T

    › 발생 버전 재현 테스트 방법 HttpException 분석 코멘트가 크래시 원인을 맞게 설명할까? 1 2 3 4 가정 앱 다시 열기 토큰 재발급 요청 응답 전에 화면 닫힘 늦은 401로 크래시 로그인이 만료된 상태 화면이 응답을 기다림 기다리던 작업도 취소 받을 작업이 이미 취소돼서 원문 analyze-crash 분석 코멘트, 한 줄 요약 세션 만료 후 앱 복귀 시 화면(Activity)의 lifecycleScope 에서 토큰 갱신 API 응답을 기다리던 중, 화면이 먼저 종료되어 취소된 코루틴에 401 예외를 전달하지 못하고 OkHttp 콜백 스레드에서 그대로 크래시 제안된 수정 토큰 재발급을 화면 대신 앱 전체 범위(앱 전역 scope)에서 실행 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 38
  39. ✦ CASE 토큰 재발급 401 미처리 NEXT › 재현 테스트

    A와 B의 결과 HttpException 발생 버전 그대로 조건만 바꿔 401을 재현 1 버전 고정 2 같은 응답 3 조건 두 가지 크래시 발생 버전 체크아웃, 라이브러리 버전 고정 kotlinx.coroutines 1.9.0, Retrofit 2.10.0, OkHttp 4.12.0 Retrofit suspend 호출이 401 응답을 받도록 구성 A 응답 도착 전 coroutine 취소 B coroutine 유지, 호출부에서 HttpException 미처리 예외 전파 여부 4 확인한 것 화면 이동 뒤 BottomSheet 표시 OkHttp 콜백 스레드, UncaughtExceptionHandler, CoroutineExceptionHandler 결과 스택을 티켓 스택과 라인 번호까지 비교 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 39
  40. ✦ CASE 토큰 재발급 401 미처리 NEXT › 수정 PR이

    정리한 범위 HttpException 취소된 coroutine에서는 크래시 재현 안 됨 A B 응답 도착 전 coroutine 취소 coroutine 유지, 호출부에서 HttpException 미처리 ✓ 티켓 스택과 일치 ✕ 예외 전파 없음 늦게 도착한 예외는 무시, 토큰 갱신 결과만 사라짐 라인 번호까지 동일 KotlinExtensions.kt:53, OkHttpCall.java:164 실제 원인 1 인증 재시도에서 서버가 다시 401 반환 2 이 응답을 사전 정의된 앱 오류 타입으로 바꾸지 않고 HttpException 그대로 전달 3 호출부 catch는 사전 정의된 앱 오류 타입만 처리, HttpException은 처리되지 않아 크래시 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 40
  41. ✦ CASE 토큰 재발급 401 미처리 NEXT › 같은 오판을

    막는 스킬 규칙 HttpException 수정 PR은 실제 원인 경로까지만 정리 1 재시도 4xx를 앱 오류 타입으로 변환 P R - B 인증 처리 인터셉터 개념 코드 인증 재시도 응답이 여전히 4xx면 HttpException 대신 사전 정의된 앱 오류 타입으로 전달 재시도 응답이 여전히 4xx면 앱 오류 타입으로 바꿔 throw 2 앱 전역 scope 이동은 유지, 성격만 재정리 크래시 수정이 아니라 화면 종료 시 토큰 갱신 결과가 사라지는 문제의 수정 val retried = retryWithNewToken() + if (retried.code >= 400) { throw retried.toAppError() + + } 3 return retried 회귀 테스트 추가 재시도 401이 호출부에 앱 오류 타입으로 도달하는지 확인 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 41
  42. ✦ CASE 토큰 재발급 401 미처리 HttpException NEXT › Type

    7이면 바뀌는 탐색과 분석 같은 오판을 막도록 크래시 유형 분류 Type 7 추가 S K I L L analyze-crash 크래시 유형 판별 T1~T6 기존 유형: Null, 생명주기, 동시성, ANR, 서드파티, 직렬화 Type 7을 따로 둔 이유 스택 트레이스에 크래시 지점이 없음 1 OkHttp 콜백에서 HttpException 생성 스택 트레이스는 이 시점에 기록 T7 비동기 uncaught 추가 2 coroutine으로 예외 전달 판별 기준 3 호출부가 처리하지 못해 크래시 › 스택에 앱 코드 프레임이 0개 › HttpException + KotlinExtensions$await › onResponse + ThreadPoolExecutor 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 이 지점은 스택 트레이스에 없음 그래서 스택 대신 이 예외를 받은 호출부를 역추적 해제된 ActivityResultLauncher 실행 42
  43. ✦ CASE 토큰 재발급 401 미처리 NEXT › 해제된 ActivityResultLauncher

    실행 사례 HttpException Type 7에 맞춰 탐색과 분석 규칙을 바꿈 PHASE 1 탐색 PHASE 2 분석 스택 위치를 믿지 않고 호출부부터 역추적 발생 버전에서 재현된 경로만 원인으로 채택 이번 사례 재시도 응답만 변환 없이 호출부에 도달 이번 사례 취소 가설은 재현되지 않아 제외 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 43
  44. ✦ CASE 해제된 ActivityResultLauncher 실행 N E X T ›

    AI가 고른 수정 방향 IllegalStateException AI는 이 크래시를 어떻게 고쳤을까? 크래시 런처를 등록한 화면보다 런처를 쓰는 팝업이 더 오래 남아 크래시 ✕ 팝업 런처 등록 런처 해제 1 상품 선택 바텀시트 2 인증 만료 안내 팝업 3 바텀시트가 먼저 닫힘 4 팝업 확인 버튼 바텀시트로 런처 등록 바텀시트와 별도로 표시 런처 등록도 함께 해제 해제된 런처 실행 CRASH IllegalStateException Attempting to launch an unregistered ActivityResultLauncher 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 44
  45. ✦ CASE 해제된 ActivityResultLauncher 실행 N E X T ›

    구조는 담당자가 바꿈 IllegalStateException AI는 지금 구조를 그대로 둔 채 ③을 고름 방향 구조를 바꾸나 AI 판단 ① 런처를 Activity에 등록 구조 변경 보류 지금 위치에선 등록하면 바로 크래시 ② 팝업을 바텀시트에 묶기 구조 변경 보류 팝업 3개 동작 변화를 검증하지 못함 ③ 런처 실행 실패 방어 구조 유지 선택 크래시만 막는 가장 작은 수정 구조를 바꾸는 선택지는 모두 지금 구조에서는 안 됨으로 보류 그래서 남은 건 구조를 그대로 둔 방어 코드 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 45
  46. ✦ CASE 해제된 ActivityResultLauncher 실행 N E X T ›

    AI 수정 PR 리뷰 체크리스트 IllegalStateException 구조는 담당자가 바꿈 AI 초안 (③) 담당자 보완 (같은 PR): 런처와 팝업의 수명을 맞춤 런처 실행을 예외 처리로 감쌈 1 바텀시트가 끝나면 런처와 콜백을 비움 구조 유지 2 남아 있는 만료 팝업도 함께 닫음 수명이 어긋난 채로 실행만 막음 3 ③의 예외 처리 코드는 제거 AI가 보류한 ②와 같은 효과 구조를 다르게 바꿔서 검증 부담 없이 구현 스킬 개선 포인트 보류할 때 원래 불가인지, 지금 구조에서만 불가인지 근거 남기기 AI는 지금 구조 안에서 답을 찾고, 구조를 바꾸는 답은 사람이 찾음 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 46
  47. ✦ CHECKPOINT NEXT › 한 건짜리 크래시도 수정해야 할까? AI

    수정 PR 리뷰 체크리스트 화면 이동 뒤 BottomSheet 표시 토큰 재발급 401 미처리 해제된 ActivityResultLauncher 실행 확인할 것 기억할 것 수정 후 사용자 경험이 나빠지지 않았는지 확인 사용자 경험은 사람이 직접 확인 분석이 말한 원인을 크래시가 난 버전에서 재현 AI가 찾은 원인은 재현해 보기 전까지 추측에 불과함 AI가 미룬 수정 방향이 정말 안 되는지 확인 AI는 지금 코드 구조 안에서만 판단 구조를 바꾸는 방법은 사람이 찾음 47
  48. ✦ 0 6 O P E R AT I O

    N N E X T › 자동화로 줄어든 일과 늘어난 일 한 건짜리 크래시도 수정해야 할까? 01 02 03 → 커지기 전에 수정 가능 → 코드를 다시 파악하는 시간 절약 → 새로 발생하는 크래시 알림에 더 집중할 수 있음 처음에는 규모를 알 수 없음 담당자가 코드를 기억하는 시점 같은 크래시 알림이 사라짐 수정 범위가 작고 명확하면 한 건일 때 바로 수정 모든 크래시를 고치지는 않음. 큰 장애 예방 효과나 비용 감소를 측정한 결과는 아님 48
  49. ✦ 0 6 O P E R AT I O

    N N E X T › 크래시 대응 시간의 비중 변화 자동화로 줄어든 일과 늘어난 일 ↓ 자동화로 줄어든 일 ↑ 대신 늘어난 일 흩어진 정보 모으기 티켓 수 티켓 하나에 모여서 옴 한 건짜리 크래시도 티켓으로 남음 처음 원인 분석과 수정안 상태 관리와 PR 검토 분석 코멘트와 Draft PR로 받음 올라온 PR마다 담당자가 확인 반복되던 준비는 시스템이, 판단과 검토는 담당자가 초반엔 Draft PR이 검토 대기로 쌓이기도 했지만, 쌓인 이슈를 정리한 뒤로는 새 PR을 당일 확인하는 편 49
  50. ✦ 0 6 O P E R AT I O

    N NEXT › 분석한 크래시가 나뉜 흐름 크래시 대응 시간의 비중 변화 이전 원인 분석 여러 명이 한나절 지금 시스템과 담당자 수정 확인 시스템이 준비 사용자 경험 확인 4분 티켓 근본 원인 수정 검토 41분 수정안* 원인 분석에 쓰던 시간을 판단과 검토에 * 시간 값: 2026년 8~9월에 바로 머지 처리된 Android fatal 크래시의 첫 발생 기준 중앙값 지연 재실행 건 제외, 리뷰와 배포 시간 미포함. 막대 길이는 개념도 41분이면 수정안까지, 판단은 팀이 50
  51. ✦ 0 6 O P E R AT I O

    N N E X T › 실제 앱 안정성의 변화 AI가 올린 수정안 PR은 절반이 머지됨 분석을 마친 Android 크래시가 판정과 처리로 나뉜 흐름 (4월~9월, 띠 굵기는 비중) FIXABLE 앱 코드 수정 분석 완료 ESCALATE(가) 앱에서 방어 ESCALATE(나) 앱에서 못 고침 기타 수정안 PR 그중 약 절반 머지 PR 없이 반영 스킵, 중복 종결 조치 대기 Known Issue로 종결 기타 출처: 팀 내부 집계 (Jira 라벨, GitHub PR). 판정은 최신 분석 기준, 수치는 공개하지 않음 51
  52. ✦ 0 6 O P E R AT I O

    N N E X T › 팀이 알림 스레드에서 보는 화면 사용자가 겪는 크래시 영향은 얼마나 줄었을까? Play Store 비정상 종료 추이 Android, 월별 Crash-free rate ▪ 비정상 종료 Android 최근 버전 기준, Firebase Crashlytics • 설치 사용자 분석 자동화 이후 99.99% Play Store 비정상 종료, 1년 전 대비 약 81% 감소 1년 전 최근 기준: Android, Play Console 월별 비정상 종료와 설치 사용자. 수치는 가림 52
  53. ✦ 0 6 O P E R AT I O

    N NEXT › 고친 뒤 재발한 티켓의 처리 같은 알림 스레드에서 맥락, 분석, 수정안까지 확인 크래시 알림 Firebase, 새 fatal 감지 발생 맥락이 정리된 티켓 Jira 티켓 자동 생성 분석 결과 원인 요약, 급증 가능성, 참고 경로 수정안 Draft PR 53
  54. ✦ CASE N E X T › 마지막 질문 RE-OPEN

    배포가 나갔으니 끝난 줄 알았죠? 사람이 할 일 자동으로 되는 일 다시 열린 티켓을 보고 이번에는 무엇이 남았는지 판단 티켓 다시 열림 담당자에게 다시 할당 배포 수정 버전 출시 LOOP 고칠 때까지 반복 재발 감지, 크래시 카운트 집계, 티켓 다시 열기 재발 같은 크래시가 다시 발생 크래시 카운트 수집 파이프라인이 자동 집계 54
  55. N E X T › Q&A 크래시 하나를 고치기 전에

    매번 되풀이하는 준비는 무엇인가요? 감사합니다 55
  56. ✦ APPENDIX CI 설정 N E X T › 모델만

    바꿔 끼면 CI에서 바로 돌아갈지 CI에서 AI 분석을 돌리려면 무엇이 필요할까? CI 레포, 도구, 권한, 격리된 실행 환경 CLI 도구 실행 ↻ 분석 반복 모델 API 판단 GitHub Actions 러너 Claude Code, Codex CLI 무엇을 볼지, 결론이 무엇인지 모델은 판단만 하고, 파일 읽기와 명령 실행은 CLI가 반복하며, CI가 그 환경과 권한을 정함 56
  57. ✦ APPENDIX CI 설정 N E X T › Q&A

    Claude와 Codex는 CI 실행 설정이 따로 필요 🤔 “지침이 같으니 모델만 바꿔 끼면 바로 돌아가겠지?” 같은 것 스킬과 지침 (/analyze-crash) 개발자 Claude 공식 CI 설정 Claude Codex 공식 CI 설정 실행 anthropics/claude-code-action@v1 실행 codex exec 지침 넘기기 Action의 prompt 입력 지침 넘기기 실행할 때 프롬프트 인자 권한 설정 claude_args (--allowedTools) 권한 설정 설정 파일의 권한 프로필, 샌드박스 우리가 막힌 곳과 맞춘 설정 우리가 막힌 곳과 맞춘 설정 ✕ 분석 명령이 기본 권한에 막힘 ✕ 샌드박스가 엄격해 실행 파일 경로도 못 읽음 → claude_args에 허용 도구 추가 → 권한 프로필에 경로 읽기 허용 Codex 설정 이름은 각 공식 문서 기준(2026년 10월). 막힌 곳과 수정은 PR-CI1~3 기준. 출처: anthropics/claude-code-action, openai/codex 57