상황

네이티브 브릿지 레이어를 리팩토링한 PR에 아키텍처 관점의 리뷰 코멘트가 여러 건 달렸다. 답변을 쓰기 위해 서브에이전트에게 변경 범위를 조사시키고 답변 초안을 받았다. 초안은 잘 읽혔다. 논지도 맞았고, 문단 구성도 리뷰어의 질문 순서를 따라가고 있었다.

문제는 잘 읽힌다는 것 자체였다. 초안을 문단 단위로 훑으면 "이 문단은 맞는 말"이라는 판정이 쉽게 나온다. 그런데 리뷰어는 문단을 읽지 않는다. 리뷰어가 읽는 건 문장이고, 그중 자기가 의심하는 한 문장이다.

문제

초안에는 결론을 지지하기 위해 끼워 넣은 보조 주장이 여럿 있었다. 예를 들면 이런 것들이다.

  • "레거시 구현을 1:1로 이관했다"
  • "SDK가 OS 판별까지 수행하므로 통합하면 UA 분기가 사라진다"
  • "전역 유틸 객체의 기존 시그니처를 그대로 유지했다"

세 문장 모두 결론(리팩토링이 동작 회귀 없이 이뤄졌다)을 강화하는 방향으로 쓰여 있고, 셋 다 그 자리에서 반박하기 어렵게 들린다. 하지만 리뷰 답글은 반박 상대가 있는 글이다. 리뷰어가 이 중 하나에 "그거 어디요?"라고 한 줄만 달면, 답변 전체의 신뢰가 무너진다. 논지가 맞아도 근거 하나가 비면 역공 지점이 된다.

그래서 검증 단위를 문단에서 문장으로 내려야 했다. 각 문장에 대해 던지는 질문은 하나다 — 이 문장의 근거를 파일:라인으로 댈 수 있는가.

접근과 판단

문장별로 grep을 돌리면서 곧바로 두 가지 함정을 만났다.

함정 1 — HEAD에서 grep하면 0건이 나온다. "레거시 구현을 1:1 이관했다"를 확인하려고 현재 브랜치에서 원본 심볼을 grep하면 당연히 아무것도 안 나온다. 그 심볼은 이번 PR에서 삭제되거나 이관된 대상이기 때문이다. 0건을 "근거 없음"으로 읽으면 오판이고, "그러니까 이관된 거네"로 읽으면 순환논증이다. 대조 기준을 origin/main 또는 base 브랜치로 잡아야 한다.

base에서 다시 확인하니 실제 결과가 나왔다. 특정 안드로이드 스킴 문자열은 base 전체에서 0건이었고, iOS 판별용 UA 문자열은 babel 설정과 목/테스트 파일에만 있었다. 즉 레거시 프로덕션 코드에 존재하지 않는 분기를 "1:1 이관했다"고 쓴 셈이다. 이 주장은 성립하지 않으므로 삭제했다.

함정 2 — 소스가 레포 안에 없다. 전역 유틸 객체의 실체는 런타임에 외부 컨테이너로 주입되는 CDN 스크립트다. 레포에는 호출부와 타입 선언만 있고 구현이 없다. 이건 "근거 없음"이 아니라 대조 불가다. 둘을 구분하지 않으면, 확인할 수 없는 것을 확인된 것처럼 쓰거나 반대로 멀쩡한 주장을 과잉 삭제하게 된다. 대조 불가로 분류한 뒤, 그 주장에 의존하는 문장을 뺐다.

flowchart TD
    A[초안의 문장 하나] --> B{근거를 파일:라인으로<br/>댈 수 있는가}
    B -->|HEAD에서 0건| C{base/origin-main<br/>에서 재확인}
    C -->|존재| D[유지 — 라인 인용 첨부]
    C -->|여전히 0건| E[삭제 — 주장 미성립]
    B -->|소스가 레포 밖| F[대조 불가로 분류<br/>의존 문장 제거]
    B -->|내부 동작이라 확인 불가| G[강도 하향<br/>단정 → 확인 후 후속]
    B -->|근거 있음| D

구현

세 번째 주장인 "SDK가 OS 판별까지 하니 통합하면 UA 분기가 사라진다"는 앞의 둘과 성격이 달랐다. SDK 내부 동작이라 레포에서 확인할 수 없지만, 근거가 아예 없는 것도 아니다. 공개된 인터페이스만 보면 기능 중복이 있어 보이는 건 사실이다.

여기서 검증 불가를 곧바로 삭제로 연결하면 유효한 관찰까지 버리게 된다. 대신 주장의 강도를 확인 가능한 범위까지 낮췄다.

  • 원래: "SDK가 OS 판별까지 수행하므로 통합하면 UA 분기가 사라집니다"
  • 수정: "기능 중복이 있어 보입니다. 확인 후 별도 티켓으로 분리하겠습니다"

수정본은 단정을 관찰로 바꾸고, 미확인 부분을 후속 작업으로 넘긴다. 리뷰어 입장에서 반박할 지점이 없어지고, 답변자 입장에서는 관찰을 잃지 않는다. 검증 게이트를 통과하지 못한 주장의 기본 처리는 삭제지만, 부분적으로 확인 가능하다면 하향이 더 나은 선택이다.

이 게이트를 모든 초안에 똑같이 적용할 필요는 없다. 적용 조건은 글에 반박 상대가 있는가다.

산출물 게이트 적용
PR 리뷰 답글, ADR, 티켓 코멘트 필수 — 문장마다 근거 파일:라인 확인
외부 공개 문서, 기술 블로그 필수
내부 탐색용 초안, 개인 메모 생략 가능 — 틀려도 비용이 다음 탐색 한 번

결과

초안에 있던 보조 주장 세 개 중 하나는 삭제, 하나는 의존 문장 제거, 하나는 강도 하향으로 처리됐다. 답변의 결론은 초안과 동일하게 유지됐다 — 논지 자체는 처음부터 맞았기 때문이다. 달라진 건 남은 문장이 전부 인용 가능한 근거를 갖게 됐다는 점이다.

부수 효과로, base 대조 과정에서 "레거시에 존재한다고 믿었지만 실제로는 테스트 코드에만 있던 분기"를 발견했다. 검증을 위해 돌린 grep이 코드베이스에 대한 오해 하나를 같이 걷어냈다.

배운 것

  • 검증 단위는 문단이 아니라 문장이다. 문단 단위 리뷰는 "논지가 맞는가"만 통과시키고, 논지를 지지하는 보조 주장의 근거는 통과시키지 않는다. 무너지는 지점은 항상 보조 주장 쪽이다.
  • 삭제·이관을 다루는 PR에서 현재 브랜치 grep의 0건은 정보가 아니다. 대조 기준은 base여야 한다. 이 함정은 리팩토링 PR에서 구조적으로 반복되므로, 0건을 보면 먼저 "내가 지금 어느 시점을 보고 있나"를 물어야 한다.
  • "근거 없음"과 "대조 불가"는 다른 판정이다. 런타임 주입 스크립트, 서드파티 SDK 내부처럼 소스가 레포 밖에 있는 대상은 후자로 분류하고, 그 주장에 기대는 문장만 걷어낸다.
  • 검증 불가 주장의 처리는 삭제 아니면 하향이다. 강도를 낮춰 확인 가능한 범위 안으로 넣을 수 있다면, 관찰을 버리지 않으면서도 반박 지점을 없앨 수 있다.