AI 코드 작성 리스크 — 에러가 아니라 검수 통제 상실을 말하라

왜 AI가 쓴 코드를 「돌아갔는지」만 봐서는 안 되는가? 문제를 검수 메커니즘 층위로 격상시켜야 한다.

1층: 의미 보존

모델은 중복 코드를 공통 함수로 뽑아내기를 아주 좋아하지만, 어떤 중복은 의도적으로 남겨둔 것이다.

모델이 구조의 유사성만 보면 「두 개의 업무 도메인을 하나의 범용 로직으로 뒤섞어 버린다」.

2층: 경계 조건

  • 널 처리
  • 과거 호환
  • 극단적 수량
  • 동시 쓰기
  • 실패 재시도

3층: 의존 계약

모델은 「에러를 리턴 코드에서 예외 던지기로 바꾸는 것」이 더 우아하다고 여기지만, 호출 측은 줄곧 리턴 코드로 업무 분기를 판단해 왔을 수 있다.

결과: 코드 스타일은 더 우아해지고 규범적이 되었는데 시스템 계약은 파괴되었다.

4층: 테스트 커버리지

  • 테스트는 「이 패치가 무엇을 보호했는가」를 덮어야지 모델이 고친 그 몇 줄만 덮어서는 안 된다
  • 공유 모듈은 특히 회귀 테스트를 보충해 옛 시나리오가 새 작성법의 영향을 받지 않음을 증명해야 한다
  • 한 문장 원칙: 테스트는 업무 결과에 봉사해야지 코드 줄 수에 봉사하면 안 된다.

5층: 무관 변경

AI는 변경 범위를 점점 크게 만든다. 결과적으로 범위가 클수록 의미가 보존되었는지 판단하기 어려워진다.

대책: diff 압축. 매 변경이 하나의 명확한 문제만 해결하게 할 것.

핵심 통찰

  1. AI 프로그래밍의 리스크 중심이 「제대로 못 쓴다」에서 「검수를 못 한다」로 이동했다 — 모델의 문법 정확률은 이미 매우 높고, 남은 리스크는 전부 「표면상 사용 가능하지만 원래 목표를 만족하지 않는」 부류에 집중되어 있으며, 이 부류가 바로 컴파일러와 lint가 전혀 막을 수 없는 것이다.
  2. 위험한 패치는 최적화처럼 생겼다 — 암묵적 오류가 상용에 올라갈 수 있는 이유는 바로 그것이 diff에서 「코드가 깨끗해졌다」로 보이기 때문이다. 리뷰어의 직관이 코드의 미관에 오도된다.
  3. 중복 코드가 반드시 나쁜 냄새인 것은 아니며, 의도적으로 보존된 업무 경계일 수 있다 — 「국내 배송비 / 해외 배송비」 예시가 보여주듯 구조가 비슷함 ≠ 업무가 같음. DRY 원칙은 업무 도메인을 가로지를 때 결합 재난을 곧바로 만들어낸다.
  4. 변경의 판정 기준은 「코드가 바뀌었는가」가 아니라 「업무 결정이 바뀌었는가」여야 한다&&||로 바꾸는 것은 한 글자짜리 diff지만 한 번의 요구사항 변경이다. 리뷰 입도는 반드시 텍스트가 아니라 의미까지 내려가야 한다.
  5. 계약에는 암묵 계약이 포함된다 — 인터페이스 문서는 명시적 부분일 뿐이고, 「오랜 운영으로 형성된 호출 습관」도 마찬가지로 계약이다. 게다가 문자 기록이 없어서 AI는 전혀 볼 수 없고 사람도 잊기 쉽다. 이것이 AI가 기존 시스템을 개조할 때의 최대 사각지대다.
  6. 테스트의 대상은 줄이 아니라 의미다 — 「모델이 고친 그 몇 줄을 덮는 것」은 일종의 가짜 커버리지다. 그 줄들이 실행되었음만 증명할 뿐, 패치가 보호하려던 제약이 여전히 살아 있음을 증명하지 못한다.
  7. Diff 크기 자체가 리스크 지표다 — 범위가 클수록 의미 보존을 판정할 수 없게 된다. 따라서 「diff 압축, 한 번에 하나의 문제만 해결」은 결벽증이 아니라 리뷰가 사람의 인지 대역폭 안에서 완료될 수 있게 하는 전제다.
  8. AI는 악의로 요구사항을 바꾸는 것이 아니라 컨텍스트를 오해하는 것이다 — 이 귀인이 대책의 방향을 결정한다: 「AI를 불신하기」가 아니라 「컨텍스트를 보충하고 검수 메커니즘을 구축하기」다.

엔지니어링 실전 Tips

  • AI에게 개조 요구를 줄 때 「어떤 중복이 의도적으로 보존된 것인지」를 컨텍스트(주석이나 README)에 써넣을 것. 그러지 않으면 모델은 반드시 그것들을 병합하려 든다.
  • diff를 리뷰할 때 논리 연산자와 조건식의 변화를 우선해서 볼 것. 문자당 리스크가 가장 높은 지점이다.
  • 외부에 노출되는 에러 전달 방식(리턴 코드 / 예외 / 에러 객체)은 공개 API의 일부로 간주해, 어떤 변경이든 리팩터링이 아니라 인터페이스 변경 프로세스를 타게 할 것.
  • 암묵 계약은 능동적으로 명시화할 수 있다: 핵심 인터페이스에 계약 테스트(contract test)를 보충해 「호출 측이 실제로 의존하는 행위」를 고정시키면, AI가 고치는 순간 빨간불이 뜬다.
  • 공유 모듈의 변경은 일률적으로 회귀 테스트를 요구하고, 「옛 시나리오가 영향을 받지 않음」을 병합 관문으로 삼을 것.
  • prompt나 도구 설정에서 변경 범위를 명확히 제한할 것(「X 함수만 수정하고 이름 변경이나 포맷 변경은 하지 말 것」). 원천에서 diff를 압축한다.
  • 「버그 수정」과 「리팩터링」을 두 개의 독립된 커밋/PR로 쪼갤 것. 절대로 AI가 하나의 패치에서 이 두 가지를 동시에 하게 두지 말 것.