AI는 7곳 중 1곳만 고치고 "완료"라고 한다
- 변경을 시키면 AI는 눈앞의 한 파일만 고치고 "완료"라고 한다. 컴파일도 되고, 그것이 본 테스트도 통과한다. 며칠 뒤, 건드리지도 않은 곳에서 터진다.
- 논리적으로 하나인 변경(필드 이름, 기능 제거)이 실제로는 여러 물리적 자리에 흩어져 산다 — 백엔드·프론트 타입·생성된 클라이언트·env 샘플·테스트·이벤트 스키마. 이게 계약(contract)이다.
- AI의 시야는 국소적이라 repo 전체에 퍼진 계약을 못 본다. 게다가 1곳을 고쳤든 7곳을 고쳤든 같은 자신감으로 "완료"라 한다.
- 대응 셋: ① 자리를 줄여라(단일 출처·codegen) ② 자리를 세라(계약 체크리스트) ③ 고아를 찾아라(grep).
1.AI는 눈앞의 한 곳만 고친다
AI에게 변경을 시키면 — 필드 이름 하나 바꾸기처럼 사소한 것이라도 — 눈앞의 파일을 깔끔하게 고치고 "완료"라고 한다. 컴파일도 되고, 그것이 본 테스트도 통과한다. 그래서 넘어가게 된다.
그런데 며칠 뒤, 전혀 건드리지 않은 곳에서 터진다. 같은 필드가 거기에도 살고 있었는데, 그쪽은 옛 이름 그대로였던 것이다. 한쪽은 새 이름, 한쪽은 옛 이름 — 둘이 만나는 순간 조용히 어긋난다. "다 고쳤다"는 사실 "내가 본 한 곳은 다 고쳤다"였다.
2.하나의 변경, 여러 개의 물리적 자리
문제의 본질은 이거다. 논리적으로 하나인 변경이, 코드에선 여러 자리에 흩어져 산다. 필드 이름 하나, 기능 하나가 실제로는 이렇게 퍼져 있다.
- 백엔드 모델·스키마
- 프론트엔드 타입 정의
- 백엔드에서 자동 생성된 API 클라이언트 타입
.env.example같은 설정 샘플- 그 코드를 검증하는 테스트
- 서비스 간 주고받는 이벤트/메시지 스키마
- 문서·런북
이걸 계약(contract)이라 부른다 — 여러 파일·여러 서비스가 "이 이름은 이런 모양"이라고 암묵적으로 합의한 것. 한 군데만 바꾸면 합의가 깨지는데, 그 깨짐은 당장 에러로 안 나타난다. 두 쪽이 실제로 만나는 순간까지 잠복한다.
3.실화 — 도메인 하나를 지웠더니 자리가 8곳 나왔다
더 큰 버전도 흔하다. 제품에서 기능 도메인 하나를 통째로 들어내는 경우를 보자. AI는 가장 눈에 띄는 백엔드 코드를 지우고 "완료"라 한다. 그런데 같은 도메인의 흔적은 곳곳에 박혀 있다.
- 프론트엔드가 들고 있던 암호화된 세션 쿠키의 payload 모양에 (지운 도메인의 필드가 남아 있다)
- 자동 생성된 API 타입에
.env샘플과 설정 검증 코드에- 테스트 fixture와 mock에
- 그리고 — 좀 민망하지만 — 몇 주 전에 써둔 블로그 글에까지. 그 글은 이미 사라진 기능을 여전히 "현재 동작"으로 설명하고 있다.
마지막 항목이 핵심이다. 이건 AI만의 문제가 아니다 — 사람조차 같은 변경의 흩어진 자리를 다 기억하지 못한다. "하나를 바꾸면 어디까지 따라 바뀌어야 하나"는 사람의 머리로도 추적이 안 된다. 그래서 더더욱 시스템이 필요하다.
4.왜 — 모델의 시야는 국소적이다
AI가 게을러서가 아니다. 모델은 눈앞에 들어온 파일·조각을 보지, repo 전체에 퍼진 계약의 그물을 보지 못한다. 눈앞의 코드는 사람보다 잘 고친다 — 다만 "이 이름이 또 어디에 사는가"는 그 시야 밖이다.
그리고 한 가지가 이걸 위험하게 만든다. 1곳을 고쳤든 7곳을 고쳤든, AI는 똑같이 확신에 찬 "완료"를 출력한다. "3곳은 고쳤고 4곳은 못 찾았다"고 말해주지 않는다. 그래서 사람이 그 자신감을 신뢰하는 순간, 못 고친 곳들이 조용히 따라온다.
5.해법 1 — 자리를 줄여라: 단일 출처(codegen)
가장 좋은 대응은 애초에 7곳을 만들지 않는 것이다. 같은 계약이 여러 곳에 손으로 복제돼 있으면 언젠가 어긋난다. 단일 출처(single source of truth)를 정하고 나머지는 거기서 생성하면, 사람도 AI도 고칠 자리가 하나로 준다.
예를 들어 백엔드 스키마에서 프론트엔드 타입을 자동 생성(codegen)하면, 필드 이름을 백엔드 한 곳에서 바꾸고 재생성하는 것만으로 프론트 타입이 따라온다. 손으로 두 번 고칠 일이 없으니, 어긋날 자리도 사라진다.
AI에게 시킬 때 한 가지를 못박아야 한다 — 생성된 파일은 직접 고치지 말고 소스에서 재생성하라고. 안 그러면 AI가 생성물을 손으로 고쳐놓고, 다음 재생성 때 그 수정이 통째로 날아간다. 줄일 수 있는 자리는 줄이고, 줄일 수 없는 자리만 다음 두 해법으로 챙긴다.
6.해법 2 — 자리를 세라: 계약 체크리스트
단일 출처로 못 줄이는 자리는 남는다. 그래서 손대기 전에 "이 변경은 몇 곳에 사는가?"를 먼저 묻고, 자주 반복되는 변경은 건드리는 자리를 목록으로 박아둔다.
# "기능 하나 제거"가 건드리는 자리
- 백엔드 라우트·서비스·모델
- 프론트엔드 타입 + 자동 생성 API 타입(재생성 필요)
- .env.example + 설정 검증
- 테스트 fixture / mock
- 이벤트·메시지 스키마(구독자까지)
- 세션/쿠키 payload 같은 "숨은" 자리
- 문서·런북·(그리고 블로그 글)
핵심은 변경을 "파일 하나 수정"이 아니라 "계약 하나 갱신"으로 보는 시선이다. 계약은 늘 둘 이상의 자리에 걸쳐 있다.
7.해법 3 — 고아를 찾아라: grep
목록도 사람 기억에 의존하면 샌다. 그래서 더 단순하고 기계적인 안전망을 둔다 — AI가 "완료"라고 하면, 바꾼 이름을 repo 전체에서 grep한다. 남아 있는 옛 이름이 곧 AI가 놓친 자리(고아)다.
$ grep -rn "old_field_name" . --exclude-dir=node_modules apps/api/schema.py:42: old_field_name: str # ← 고침 apps/web/types/api.ts:88: old_field_name: string; # ← 안 고침 (고아) infra/.env.example:13:OLD_FIELD_NAME=... # ← 안 고침 (고아)
grep 한 줄이 "다 고쳤다"는 주장을 30초 만에 반증한다. 신뢰는 비싸고 grep은 싸다. AI의 "완료"는 결론이 아니라, 이제 grep해 볼 차례라는 신호로 받는 게 맞다.
8.정리
AI는 눈앞에 보이는 것을 고친다. 그런데 일관성은 repo 전체에 걸친 속성이라, 한 조각만 보는 모델에겐 구조적으로 안 보인다. 모델이 더 똑똑해진다고 이 시야가 repo 전체로 넓어지진 않는다 — 컨텍스트엔 늘 한계가 있으니까.
그래서 사람이 메운다. 자리를 줄이고(단일 출처), 남은 자리를 세고(계약 체크리스트), "완료" 뒤엔 grep으로 고아를 찾는다. "한 곳 고쳤다"와 "다 고쳤다"는 전혀 다른 말이고, 그 둘을 구분하는 건 — 적어도 아직은 — 모델이 아니라 사람이다.
'프로그래밍 > AI' 카테고리의 다른 글
| AI에게 규칙을 한 번만 가르치는 법 (0) | 2026.06.13 |
|---|---|
| 어제 고친 버그를 오늘 또 만드는 AI — 컨텍스트 엔지니어링 (0) | 2026.06.13 |
| AI는 일단 막으려 한다 — 권한은 주고, 압력은 가격으로 (0) | 2026.06.13 |
| AI가 짠 테스트는 다 통과한다 — 그게 함정이다 (0) | 2026.06.13 |
댓글