![]()
도입
AI가 코드를 꽤 잘 짠다. 그런데도 경우의 수가 많은 기능은 자꾸 꼬인다. “이거 만들어줘” 하면 첫 구현은 그럴듯한데, 엣지에서 깨지고, 패치하면 다른 분기가 깨지고, 그러다 처음 의도가 흐려진다.
이번에 분기가 수십 개로 얽힌 기능을 AI와 만들면서, 어느 순간 코드를 멈추고 모든 경우의 수를 한 장의 매트릭스로 먼저 짰다. 그러고 나니 꼬임이 사라졌다. 이 글은 그때 쓴 세 가지 장치를 정리한 것이다. 결론부터: AI의 강점(망라성·속도)은 끌어쓰되, 약점(환각·”됐다는 착각”)은 워크플로 구조로 막는다.
왜 꼬이나
만든 기능은 추상적으로 이렇다. 한 객체의 “타입”을 바꾸면, 그 타입에 따라 다른 도메인의 상태 전이가 연동돼야 한다. 어떤 항목을 “전이 시작” 타입으로 바꾸면 상태 전이 절차가 시작되고, 이미 진행 중이면 거기 연결되고, 값이 있으면 옮겨지고, 반대 방향이면 해제되고… 이런 식이다.
문제의 본질은 이 분기들을 코드를 짜면서 발견한다는 것이다. AI는 “정상 경로”는 빠르게 짜지만 “이미 한 번 거쳐간 객체인데 옛 기록이 남아 있으면?” 같은 분기는 짜다가 만나거나, 아예 안 만난다. 사람이 리뷰에서 하나씩 찾아 패치하면 — 그 패치가 또 다른 분기를 건드린다. 전형적인 “패치 반복 = 설계 누락의 신호”다.
1. 코드 전에 “전 경우의 수 매트릭스”를 짠다
핵심 전환은 코드를 짜기 전에 모든 분기를 한 장에 동결한 것이다. 단, 한 방에 완벽한 매트릭스를 그리려 하지 않았다(그것도 환상이다). 여러 단계로 수렴시켰다.
- 디자인 옵션 비교 — 타입을 바꾸는 컨트롤 UI를 A안/B안으로 나란히 놓고 하나를 골랐다.
- 변경 매트릭스 + 가드 — 어떤 타입 → 어떤 타입이 허용/차단인지 표로 형식화.
- 동작 오케스트레이션 — 전이 타입으로 바꿀 때 무슨 일이 일어나는지 흐름으로.
- 전 케이스 망라 스펙 — 위를 종합해 빈 칸 없이 모든 경우의 수를 채운 단일 매트릭스. 케이스마다 코멘트를 달아 두 라운드 돌려 문구·동작을 확정했다.
마지막에 나온 매트릭스는 (도메인을 가린 버전으로) 이렇게 생겼다.

그림: “타입을 바꾸면 상태 전이가 연동되는” 기능의 전 분기. 정방향(일반→전이)은 현재 상태 × 기존 기록 × 전이 가능 여부로 연결/시작/이동/복구/차단을 가르고, 역방향(전이→일반)은 진행 중이냐 완료냐로 해제/안내가 갈리며, 전이↔전이는 둘의 합성이다.
사실 위 그림은 단순화한 한 조각이다. 실제로 동결한 스펙은 훨씬 컸다 — 변경을 정방향·역방향·전이↔전이 합성 세 군으로 나눠 케이스 20개를 빈칸 없이 채웠고, 각 칸은 현재 상태 × 기존 기록 × 전이 가능 여부 세 축으로 또 갈렸다. 그래서 “타입을 바꾼다”는 한 번의 동작이 최종 13가지 결과 분기로 판정됐다 — 연결·시작·이동·복구·차단·해제, 그리고 역방향 해제와 정방향이 겹칠 때의 합성 5종. preview가 고른 분기를 execute가 재판정해 그사이 상태가 바뀌면 거부하는 레이스 가드까지 넣었고, 이 판정 로직만 테스트 44개로 고정했다.
이 매트릭스가 곧 계약이 됐다. AI는 이 동결된 표에 맞춰 구현하고, 사람은 이 표를 기준으로 리뷰한다. “이 케이스는 어떻게 동작해야 하지?”를 코드 리뷰에서 묻지 않는다 — 셀에 이미 적혀 있다.
여기서 사람과 AI의 분업이 분명해진다. 경우의 수 망라와 구현은 AI가, 각 셀의 도메인 결정은 사람이 한다. AI는 “생각할 수 있는 모든 조합을 나열해줘”에 강하고, 사람은 “이 칸은 이렇게, 저 칸은 막아”에 강하다. 빈 칸을 채우는 건 사람이지만, 빈 칸이 어디인지 드러내 주는 건 AI다.
옵션 비교 → 규칙 형식화 → 동작 정의 → 전 케이스 동결. 정답을 한 번에 그리는 게 아니라 단계적으로 좁혀 마지막에 빠진 칸이 없게 만든다.
2. 리뷰는 두 번, 독립·적대적으로
구현 후 리뷰를 두 번, 독립적으로 돌렸다.
- 1차: 정해진 4관점(정확성 / 컨벤션 / 보안·엣지 / 완결성)으로 변경 코드를 훑는다.
- 2차: 별도의 깨끗한 컨텍스트에서, 1차의 결론을 입력으로 받아 “반박하라”는 지시로 돈다.
같은 맥락에서 같은 리뷰를 두 번 하면 같은 anchoring으로 같은 결론이 나온다. 그래서 2차는 컨텍스트를 격리하고 적대적으로 세팅했다. 결과 — 2차가 1차+자기 신규 발견 4건 중 2건을 뒤집었다.
- 한 건은 “이 삭제 경로가 연관 데이터를 같이 날린다”는 위험 지적이었는데, 실제 구현은 하드 삭제가 아니라 soft-delete라 연관 데이터가 보존됐다. 프레임워크 동작을 추측한 거짓 위험이었다.
- 다른 한 건은 “이 시간 변환이 타임존을 명시 안 해 위험”이었는데, 코드베이스의 기존 표준 패턴과 동일했다. 내 코드만 바꾸면 오히려 불일치가 났다.
교훈 둘. 첫째, AI 리뷰도 환각한다. “프레임워크가 이렇게 동작할 것이다”를 검증 없이 finding으로 올리면 거짓 양성이 된다 — 프레임워크 동작에 기댄 지적은 소스/문서로 검증한 뒤에만 보고하게 했다. 둘째, 반박당했다고 즉시 항복하지 않는다. 의심받으면 근거를 재검증하고, 틀렸으면 어디가 틀렸는지 대고 철회, 맞으면 근거를 대고 유지한다. AI는 사용자가 의문을 표하면 반사적으로 “죄송합니다, 제가 틀렸습니다”로 무너지는데, 그게 리뷰의 신뢰를 깎는다.
3. “코드 있음 ≠ 동작함” — 도달성 검증
마지막이 가장 값졌다. 구현·리뷰·테스트가 다 통과했는데, 실제 화면에서 끝까지 클릭해 보니 두 가지가 드러났다.
- 역방향 분기가 도달 불가였다. 다이얼로그와 로직 코드는 멀쩡히 있었지만, 상위 라우팅이 그 항목 클릭을 가로채 다른 화면을 열어 버렸다. 즉 내가 만든 UI에 들어갈 경로가 없었다. 코드를 읽어선 안 잡힌다 — 컴포넌트는 존재하니까.
- 매트릭스에서 합의한 한 칸은 아예 구현되지 않은 채였다. 그 경우를 켜는 진입 UI 자체가 빠져 있었다. “매트릭스에서 합의됨 ≠ 구현됨”이다.
둘 다 단위 테스트로는 안 잡힌다(테스트는 함수를 직접 부르니 “도달”을 검증하지 않는다). 코드 리뷰로도 안 잡힌다(코드는 다 있으니까). 실제 사용 경로를 끝까지 밟는 e2e로만 드러난다.
이건 새로운 게 아니다 — 어디에 서 있나
세 장치 모두 발명이 아니다. 이미 있는 것들을 1인+AI 루프에 맞춰 조합·구체화했을 뿐이다.
- 1번(코드 전 매트릭스) 은 2026년 들어 뜨거운 Spec-Driven Development(스펙이 코드의 단일 진실, “vibe coding의 drift를 막자”)의 한 형태다. 내가 더한 건 수렴하는 목업 시퀀스와, 스펙을 결정 테이블(decision table) 로 굳힌 점이다. 결정 테이블은 조건 조합을 빠짐없이 나열하는 40년 된 블랙박스 테스트 기법인데, 그걸 설계 단계 산출물로 당겨 쓰고 열거는 AI가·각 칸의 결정은 사람이 하도록 분업한 게 요지다. (조건이 적어 전수 매트릭스가 가능했고, 더 컸으면 pairwise 같은 조합 축약이 필요했을 것이다.)
- 2번(적대적 2차 리뷰) 은 멀티에이전트 적대적 토론·투표로 LLM 환각·거짓양성을 줄이는 연구 흐름과 같은 결이다(AI 코드 리뷰의 환각은 실측으로도 경고된다). 거창한 N-에이전트 투표 대신 독립 2차 1명이 1차를 반박하게 한 가벼운 버전이다.
- 3번(도달성 검증) 은 새 기법이랄 게 없다 — “통과한 테스트·리뷰”가 “사용자가 그 기능에 도달함”을 보장하지 않는다는 오래된 사실의 재확인이다.
요컨대 SDD·결정 테이블·적대적 리뷰를 한 사람이 AI와 굴리는 작은 루프로 엮은 것이다.
배운 점
세 장치는 결국 AI 협업에서 감독의 위치를 옮기는 일이었다.
- 스펙(매트릭스)을 코드 앞에 두고 수렴시킨다 — 분기 결정을 코드 리뷰가 아니라 매트릭스 단계에서, 사람이.
- 리뷰를 한 번 더, 독립·적대적으로 — AI 리뷰의 환각을 전제하고, 반박엔 근거로 응수.
- “됐다”를 코드가 아니라 실제 도달로 검증 — 컴포넌트 존재가 아니라 사용자 경로로.
“AI한테 시켜서 잘 됐다”가 아니다. AI의 망라성·속도는 그대로 쓰되, 환각과 “됐다는 착각”이라는 두 약점을 워크플로의 구조로 막은 것이다. 사람의 감독은 코드 한 줄씩 검토에서 — 매트릭스를 합의하고, 리뷰를 반박하고, 도달을 확인하는 쪽으로 이동한다. 코드가 싸질수록 오히려 더 중요해지는 자리다.
References
- Spec-Driven Development with AI Coding Agents (2026) · SDD: The 2026 Guide (BCMS)
- Decision Table Testing (ZetCode) · Decision Table Based Testing (GeeksforGeeks)
- Adversarial debate & voting to reduce LLM hallucinations (MDPI, 2025) · adversarial-review (GitHub) · LLM hallucinations in AI code review (diffray)
- Kent Beck, “Make the change easy, then make the easy change” — 구조 변경과 동작 변경의 분리
Comments