
'AI 자동 PR'이라는 말, 사실 절반은 거짓말입니다. AQI Gemini는 IDE 안에서 diff만 제안하고, PR 생성은 결국 제 손으로 해야 했거든요.
지난 주말에 사이드 프로젝트 prod 크래시 3건을 골라서, 안드로이드 스튜디오(Android Studio)의 App Quality Insights(AQI) 툴 윈도우에서 "Suggest a fix" 버튼을 차례로 눌러봤어요. 친구들 단톡방에 결과 스샷 뿌렸더니 후배 한 명이 "PR까지 자동으로 만들어주는 거 맞냐"고 물어보길래, 이참에 한 번 정리해봅니다.
AQI Gemini "Suggest a fix" 동작 조건과 PR 생성 경로
이름이 "Suggest a fix"라서 PR이 알아서 올라온다고 오해하기 쉬운데, 실제로는 IDE 안에서 diff 한 덩어리를 제안하는 데서 끝납니다. 브랜치 생성·커밋 메시지·PR 본문 작성은 그 뒤로 별도 작업이에요.
제가 굴려본 절차는 이렇게 나뉘었습니다.
- AQI 툴 윈도우에서 크래시 한 건 선택 후 "Suggest a fix" 클릭, 제미나이(Gemini)가 스택 트레이스와 로컬 소스를 묶어 diff 제안.
- Accept 누르면 working tree에 변경 반영. 여기서 끝.
- 브랜치 따고 PR 올리는 건 Agent Mode에 자연어로 시키거나, 그냥 gh pr create로 직접 처리.
CLI 쪽으로 완전 자동화를 보고 싶으시면 Crashlytics MCP + gh CLI 조합이 더 깔끔하긴 한데, 그러면 IDE가 가지고 있는 코드 인덱싱·심볼 추적 능력은 잃게 됩니다. 제 경험으로는 PR 생성 자동화 1~2분 아끼려고 IDE 컨텍스트를 버리는 건 손해라고 봅니다.

그리고 전제 조건이 두 가지 있는데요, 크래시가 터진 그 빌드의 소스가 로컬에 체크아웃되어 있어야 하고, Proguard/R8 mappings.txt가 Crashlytics에 업로드돼 있어야 합니다. 매핑 파일 없으면 제미나이가 난독화된 라인을 엉뚱한 코드로 분석해버리더라고요.
NullPointerException은 1차 제안 그대로 머지한 케이스
가장 깔끔하게 끝난 건 단순 NPE였습니다. 외부 API가 빈 응답을 내려서 userResponse.profile.name에서 터지는 흔한 패턴이었어요.
제미나이가 제안한 diff가 거의 정답이었습니다. userResponse?.profile?.let { ... } 안전 호출로 감싸고, 빈 데이터일 때 보여줄 fallback UI까지 같이 깔아줬어요. 스택 트레이스가 5단계 이내로 짧고, 해당 함수가 6개월 안에 제가 직접 작성한 코드라서 그런지 1차 제안 그대로 머지했습니다.

다만 한 가지, AI가 해결한 건 '크래시'이지 '원인'이 아니라는 점은 분명히 해두고 싶어요. 왜 서버가 빈 응답을 내렸는지는 백엔드 로그를 따로 까봐야 했고, 그건 제미나이가 해줄 수 있는 영역이 아니었네요.
IllegalStateException은 diff만 받고 PR 본문은 직접 쓴 경우
두 번째는 Fragment 생명주기 관련 크래시였습니다. onDestroyView 이후에 코루틴이 뒤늦게 끝나면서 이미 분리된 View를 건드린 race condition이었어요.
제미나이는 binding 참조에 ?.let을 둘러 증상은 멈췄는데, 근본 원인을 잡지는 못했습니다. 더 곤란했던 건 PR 본문 초안에 "Fragment가 백그라운드 스레드에서 View를 그리려다 발생"이라고 그럴듯하게 틀린 설명을 적어둔 부분이었어요. 그대로 머지했으면 다음에 비슷한 버그 만났을 때 잘못된 단서만 남는 셈입니다.

결국 제가 repeatOnLifecycle(Lifecycle.State.STARTED) 블록으로 코루틴 수집부를 감싸고, PR 요약은 처음부터 다시 작성했어요. 라이프사이클·코루틴이 얽힌 케이스는 제미나이 제안 채택률이 체감상 60~70% 수준이었습니다. 코드는 참고하되 본문은 사람이 다시 써야 한다는 게 솔직한 결론이라고 봅니다.
OutOfMemoryError는 Coil 라이브러리 때문에 반려한 케이스
세 번째 OOM은 반려했습니다. 고해상도 이미지 로딩 중에 터진 크래시였는데, 제미나이가 제안한 fix가 Bitmap.Config.RGB_565로 다운그레이드하는 코드였어요.
문제는 제 프로젝트가 Coil로 이미지 로딩을 통일해놨다는 점입니다. AI는 그걸 모르고 일반적인 BitmapFactory.Options 답안지를 내놓은 셈이지요. 정답은 Coil의 ImageRequest 빌더에서 size()나 bitmapConfig()를 조정하는 쪽이었습니다.

프로젝트 고유 아키텍처를 모르면 제미나이 제안 채택률은 급격히 떨어집니다. 제가 보기엔 이게 IDE 안 AI의 가장 큰 한계예요. 스택 트레이스만 보고 답하지, 우리 모듈이 어떤 라이브러리로 같은 문제를 이미 풀고 있는지는 알지 못합니다.
안드로이드 크래시 PR 자동화 결정 트리
3건 굴려보고 나니까 어떤 크래시에 "Suggest a fix"를 누르고 어떤 건 손으로 짚을지 기준이 잡히더라고요. 제 작업 패턴에 맞춰 세 단계로 정리해봤습니다.
1. 그대로 머지 후보
스택이 5단계 이내로 짧고, 6개월 안에 본인이 직접 작성한 코드에서 발생한 단일 스레드 NPE나 IndexOutOfBoundsException 정도예요. 이 범위는 1차 제안 채택률이 충분히 높았습니다.
2. diff는 받되 직접 보강 후보
Fragment·코루틴 race condition, ANR 직전의 IllegalStateException 같은 케이스입니다. 코드는 출발점으로 쓸 만한데 PR 요약은 무조건 직접 다시 쓰셔야 해요. 잘못된 원인 설명이 다음 디버깅 단서를 오염시킵니다.
3. Agent 호출 보류 후보
난독화가 심한 라이브러리 내부 크래시, Native .so 라이브러리에서 터지는 크래시, A/B 테스트 분기에서만 재현되는 크래시는 컨텍스트 부족으로 정확도가 낮습니다. 이 영역은 사람이 매핑 파일과 빌드 분기부터 따라가는 게 빠릅니다.
개인적으로는 1번에 해당하는 단순 NPE 한 무더기를 한꺼번에 정리할 때 AQI Gemini가 진짜 시간을 아껴주는 것 같습니다. 초기 디버깅 5~10분이 사실상 0이 되는 느낌이라, 그 시간을 2번·3번 케이스의 근본 원인 추적에 돌리는 식으로 쓰고 있어요.
3건 굴려본 결과, 채택률은 결국 30%
1차 제안 채택 1건, 보강 후 채택 1건, 반려 1건이었어요.
앞서 케이스 유형별로 체감했던 30~70% 채택률 구간, 그 한가운데에 이번 3건 전체 수치가 정확히 들어맞은 셈이었습니다.
처음 "Suggest a fix" 버튼을 누를 때만 해도 PR이 알아서 올라올 거라 기대했는데, 막상 받아보니 제미나이가 잘하는 건 스택 트레이스 읽고 의심 코드 짚어주는 초기 디버깅까지였어요. 그 뒤의 "왜 null이 흘러왔는가", "우리 프로젝트는 이 문제를 어떤 라이브러리로 풀고 있는가" 같은 질문은 여전히 사람 몫이지요.

저는 다음 스프린트에 1번 범주 NPE만 한 묶음으로 모아서 일괄 처리해볼 생각입니다. 결국 3건 중 1건. 그 한 건이 어디에 이 도구를 써야 하는지를 보여준 숫자였습니다.
'개발 도구 > AI 코딩 도구' 카테고리의 다른 글
| 안드 팀이 6월 첫 주에 챙길 깃허브 코파일럿 AI Credits 전환과 MS Build 2026 (0) | 2026.06.19 |
|---|---|
| 클로드 코드·커서·코덱스가 노션에 들어온 자리, 안드 팀 한 주 실측기 (0) | 2026.06.17 |
| 미스트랄 스몰 4 reasoning_effort 다이얼, 직장인 일감 5개로 굴려본 후기 (0) | 2026.06.14 |
| Quail 2·Cursor·Claude Code 셋을 한 주 같이 굴려본 맥북 워크플로 (0) | 2026.06.11 |
| Cursor Composer 2.5 vs Claude Code, 안드 프로젝트 닷새 같이 굴린 1차 후기 (1) | 2026.06.09 |