“리뷰를 열심히 반영했는데 왜 장애 대응 PR이 연달아 생겼을까?”
이 글은 내부 private repo 작업(PR #6817, PR #6937)을 바탕으로 썼다.
외부 접근이 불가한 저장소라 링크 없이, 번호와 익명화 코드 중심으로 정리한다.
이번 회고의 핵심은 AI의 성능 자체가 아니라, AI 제안을 운영하는 인간의 우선순위 체계다.
TL;DR
- 시작은 단순한 장애 우회였다.
- 리뷰를 따라가며 품질 개선이 누적되면서 한 PR에서 공용화/고도화가 과속됐다.
- 그 사이 경계 데이터(null)와 레거시 타이밍 결함이 늦게 드러났다.
- 후속은 나와 AI가 빠르게 패치 루프를 돌렸고, QA 동료들이 재현 시나리오를 촘촘히 잡아줘 복구가 빨랐다.
- 핵심 교훈은 하나다: AI 리뷰는 전부 반영이 아니라 비용 대비 큐잉으로 운영해야 한다.
1) 처음 요구는 작았다
초기 요구:
- warning 코드가 오면 저장 배송지 대신 직접입력 폼 노출
- 기존 폼을 재활용해 빠르게 우회
- 목적은 구조 개선이 아니라 결제 실패 감소
즉, 임시 기능이었다.
그런데 최종 머지(7c5982a)는 다음 규모로 커졌다.
21 files changed+880 / -438feat/refactor/fix/chore/revert다수 누적
이 수치가 말해주는 건 “열심히 했다”보다 “성격이 바뀌었다”다.
2) PR #6817에서 실제로 과속된 구간
squash 메시지 흐름을 보면 아래처럼 움직였다.
초기(요구와 정합성 높음)
- 배송 섹션 display 판별 로직 추가
- 직접입력 컴포넌트 추가
- display별 조건부 렌더
중반(왕복 리팩터 시작)
- display 분기를 전용 컴포넌트로 분리
- 다시 인라인 분기로 회귀
- 일부 분기/컴포넌트 제거 후 복원(revert)
후반(비기능 품질 대량 반영)
- warning 검증 순서·포커스 정리
- 이모지 검증 규칙 확장
- 모바일 고정 결제 바 가림 보정
- 라이브 영역(alert/polite) 조정
- 스타일 이관(emotion -> tailwind)
- hidden form stale error 정리
각 변경은 개별로 타당했다.
하지만 임시 대응 PR 하나에 모두 들어가면서 검증면적과 회귀 위험이 급상승했다.
3) 무엇이 과했는가 (핵심 진단)
이번 회고의 핵심은 “리뷰가 틀렸다”가 아니다.
리뷰 반영 전략이 없었다.
과속 신호 A: 계층 왕복
- 분리 -> 인라인 -> 제거/복원 반복
이 패턴이 보이면 요구 해결보다 구조 실험 비중이 커졌다는 뜻이다.
과속 신호 B: 임시 기능에 장기 개선 동시 탑재
- 스타일 시스템 이관
- 훅 책임 재배치
- 접근성/포커스 디테일 확장
좋은 일이라도 “지금 해야 하는 일”인지 분리하지 않으면 비용은 눈덩이처럼 불어난다.
과속 신호 C: AI 리뷰 추종 모드
AI 리뷰는 개선 후보를 잘 찾는다. 그래서 더 위험하다.
- 고칠 수 있으니 지금 고친다
- 지금 고치다 보니 범위가 커진다
- 범위가 커지니 본질 리스크 확인이 늦어진다
4) 후속 결함 1: null 값 trim 크래시 (PR #6937)
문제는 단순했다.
// 익명화
const payload = {
receiver: values.receiver.trim(),
detailAddress: values.detailAddress.trim(),
memo: values.memo.trim(),
};
null/undefined에서 런타임 에러가 난다.
복구는 “한 줄 가드”가 아니라 “계약 복원”이었다.
const normalizeAddress = (item) => ({
...item,
receiver: item.receiver ?? '',
detailAddress: item.detailAddress ?? '',
memo: item.memo ?? '',
});
const toPayload = (form) => ({
receiver: (form.receiver ?? '').trim(),
detailAddress: (form.detailAddress ?? '').trim(),
memo: (form.memo ?? '').trim(),
});
그리고 PATCH 응답 병합에서 상세주소 보존 규칙까지 고정했다.
핵심: 버그는 trim에서 터졌지만 원인은 데이터 계약 부재였다.
5) 후속 결함 2: 레거시 dialog/overlay 타이밍 충돌
증상:
- 다이얼로그 종료 후 body
overflow: hidden잔존 - 페이지 스크롤 불가
원인 패턴:
await onCancel?.(); // 다음 레이어 open
emitHide(); // 이전 레이어 hide
수정 패턴:
hideDialogSync();
await onCancel?.();
overlay cleanup도 overflow='' 강제 제거가 아니라 이전값 저장/복원으로 변경했다.
6) “휘리릭 장애 대응”의 실체
빠르게 수습한 이유는 감이 아니라 루프였다.
- 재현 고정: QA 시나리오로 실패 경로를 고정
- 최소 안전 패치: null-safe, trim 가드, 병합 규칙, hide/open 순서
- 회귀 방지: 유틸/변환 테스트 추가
즉 “빠르게 많이 고침”이 아니라 위험도 높은 곳부터 닫음이었다.
비용이 늘어난 축 vs 속도가 늘어난 축
- 비용 증가 축: PR 범위 확장, 왕복 리팩터, 검증면적 확대, 회귀 위험 증가
- 속도 증가 축: 재현 고정, 최소 패치 우선, 테스트 보강으로 복구 리드타임 단축
- 핵심 관찰: AI를 쓴다고 자동으로 ROI가 좋아지지 않는다. 비용 축을 통제할 때만 속도 축의 이득이 남는다.
7) AI 리뷰 추종이 엔지니어링 비용을 망가뜨리는 방식
AI 리뷰의 장점:
- 개선 후보를 광범위하게 찾음
- 부채를 빠르게 드러냄
- 일반화된 구조 제안
문제는 반영 필터가 없을 때다.
- 좋은 제안 = 지금 할 일로 오인
- 임시 PR이 구조 PR로 변질
- 운영 리스크 확인이 후순위로 밀림
한 줄 결론:
AI 리뷰 추종은 코드 품질 점수는 올려도, 제품 관점 ROI를 떨어뜨릴 수 있다.
8) 다음부터 적용할 운영 규칙
8-1. Temporary Charter (PR 시작 전 10줄)
- 이번 PR이 막을 실패 시나리오
- 종료 조건
- 비범위
- 후속 분리 항목
8-2. 리뷰 3단 큐
Now: 장애/정합성/보안/접근성 즉시 영향Next: 유지보수 개선Later: 공용화/추상화/아키텍처
8-3. 공용화 게이트
아래 3개 중 2개 이상 충족할 때만 같은 PR에서 공용화:
- 재사용 지점 2곳 이상
- 계약 테스트 존재
- 다음 스프린트 유지 가능성 높음
8-4. 왕복 리팩터 금지
분리 -> 인라인 -> 복원이 보이면 즉시 범위를 재확인한다.
9) QA 동료들에게 감사
이번에 특히 고마웠던 점:
- 모바일 조건부 재현 고정
상세주소 누락 -> 알럿 -> 수정 다이얼로그연쇄 검증- 에러 노출/복구 가능성 확인
- 정상/경계/복귀 시나리오 압박
개발은 문제를 고친다.
QA는 사용자가 실패에서 돌아오게 만든다.
이번 복구 속도는 그 차이 덕분에 가능했다. 진심으로 감사하다.
10) 결론
“AI 리뷰를 전부 반영하는 팀이 아니라, AI 리뷰를 비용 대비로 운영하는 팀이 더 멀리 간다.”
임시 기능일수록 이 질문이 필요하다.
- 지금 이 변경이 사용자 실패를 줄이는가?
- 미래 불편을 미리 고치는가?
- 둘 다라면, 왜 이번 PR에서 같이 해야 하는가?
다음에는 빠른 구현과 적정 비용의 멈춤을 같이 설계하겠다. AI는 빠른 정답을 준다. 팀이 실패하는 지점은 정답의 우선순위를 정하지 못할 때다.