AI가 틀릴 때, 먼저 모델을 의심하지 마라
AI 코딩 어시스턴트가 엉뚱한 함수 이름을 부르거나, 이미 deprecated된 API를 자신 있게 추천하거나, 분명히 말한 요구사항을 세 번째 프롬프트에서 슬며시 잊어버릴 때—대부분의 개발자는 모델을 탓한다. 모델이 나빠서, 컨텍스트 윈도우가 짧아서, 아직 GPT-5가 아니어서.
하지만 현장에서 AI 도구를 본격적으로 써본 사람이라면 금방 눈치채는 패턴이 있다. 모델이 틀리는 지점은 랜덤하지 않다. 오류는 항상 특정 구조적 공백 주변에 몰려 있다. 스펙이 없는 곳, 의도가 코드 밖에 있는 곳, 문서화되지 않은 비즈니스 규칙이 사는 곳. dev.to에 최근 올라온 세 편의 글이 서로 다른 각도에서 같은 진단을 내리고 있다. AI 코딩의 실패 원인은 모델 성능이 아니라 코드베이스와 워크플로우의 구조 문제다.
문제 1: 스펙 없이 바이브만 있으면 AI는 '마지막 프롬프트'에 최적화된다
"User authentication 만들어줘." AI가 300줄을 뱉는다. "여기에 RBAC 추가해줘." 절반이 바뀐다. "왜 세션 로직이 컴포넌트 안에 있어?" 또 리팩터링. 세 시간 후, 스파게티.
Andrej Karpathy가 2025년 초에 명명한 '바이브 코딩'은 프롬프트 퍼스트, 계획 미니멀, 모델이 빈칸을 채우는 방식이다. 탐색과 프로토타이핑에서는 강력하다. 하지만 천장이 있다. 스펙 없이 AI에게 지시하면, 모델은 당신의 최종 목표가 아니라 가장 최근 메시지에 최적화한다. 컨텍스트 드리프트는 필연이다. 프롬프트를 잘 쓰는 문제가 아니다. 방향을 잡아줄 문서가 없는 구조 문제다.
dev.to의 Vibe Coding Meets Spec-Driven Development 글이 제안하는 해법은 간단하다. Spec-Driven Development(SDD)와의 하이브리드. 코드를 치기 전에 15~30분짜리 마크다운 스펙을 먼저 쓴다. Goal, Scope(IN/OUT 명시), Data Model, API Contract, Acceptance Criteria. 그게 전부다. 이 짧은 문서가 이후 모든 AI 프롬프트의 앵커가 된다. 모델은 더 이상 의도를 추측하지 않고 계약을 실행한다.
핵심은 스펙을 '첫 프롬프트에만 붙이는 것'이 아니라는 점이다. Cursor나 Claude Code처럼 파일 기반 컨텍스트를 지원하는 도구에서는 스펙 파일을 프로젝트 룰로 등록해 모든 세션에 상시 주입한다. 요구사항이 바뀌면 코드 전에 스펙을 먼저 업데이트한다. 이 규칙 하나가 "AI가 멋대로 바꿨다"는 불평의 절반을 없앤다.
문제 2: 코드베이스가 불투명하면 AI는 '그럴듯한 거짓말'을 한다
Your LLM Is Wrong. Your Codebase Is Why. 제목부터 직설적인 이 글의 저자는 자신이 석 달 전에 작성한 함수를 AI에게 설명해달라고 했다가 존재하지 않는 함수 설명을 받았다. 완전한 환각은 아니었다. 함수는 실재했다. 다만 이름도, 파라미터도, 동작도 달랐다. 모델이 불명확한 신호들로 '그럴듯한 이야기'를 조립한 것이다.
이걸 저자는 comprehension debt라고 부른다. 기술 부채가 '바꾸기 어려운 코드'라면, comprehension debt는 '이해하기 어려운 코드'다. 로직은 돌아간다. 테스트도 통과한다. 하지만 코드 어디에도 이 함수가 왜 이렇게 동작하는지, 무엇을 절대 넘기면 안 되는지, 어떤 비즈니스 규칙을 따르는지가 없다. 그 지식은 누군가의 머릿속, 2개월 전 슬랙 스레드, 혹은 아무 데도 없다. AI는 슬랙 스레드에 접근할 수 없다. 소스만 읽는다.
AI가 코드베이스에서 틀리는 패턴은 다섯 가지로 정리된다. 존재하지 않는 함수명 호출(네이밍 일관성 부재), 파라미터 타입 오류(타입 어노테이션 누락), 이미 래핑된 패키지 직접 import(내부 추상화 문서화 부재), deprecated API 추천(인라인 @deprecated 미작성), 비즈니스 규칙 무시(코드 밖에 존재하는 의사결정). 각각이 구조적 공백의 증거다.
저자가 권하는 즉시 실행 가능한 감사법이 있다. 오래된 모듈 하나를 골라 AI에게 물어본다. "이 모듈이 하는 일을 두 문장으로 설명해줘", "exported 함수 목록과 파라미터, 리턴 타입을 알려줘", "이 모듈을 삭제하면 뭐가 깨져?". AI가 틀린 항목을 적는다. 그게 comprehension debt 목록이다. 저자가 내부 TypeScript 서비스에 적용했더니 12개 함수 중 41%가 틀렸다. "잘 유지되고 있다"고 생각했던 모듈이었다.
수정 방향도 명확하다. JSDoc에 구현이 아닌 의도를 쓴다. "validates user"가 아니라 "DB 저장 전 반드시 호출. 권한 체크는 하지 않음"처럼. deprecated 표시는 30초면 된다. 비즈니스 규칙은 Confluence가 아니라 해당 파일 안에 넣는다. 이건 사람을 위한 문서가 아니다. AI가 파싱할 수 있어야 AI가 올바르게 돕는다. 사람이 읽기 좋은 건 부수 효과다.
문제 3: 레거시 코드베이스에서 SDD는 '설계 전 단계'가 아니라 '변경 단위 계약'이다
스펙 주도 개발 이야기를 들을 때 가장 흔한 반응이 있다. "좋은 말이지만 우리 프로젝트는 4년 된 모놀리스인데요." dev.to의 SDD en proyectos brownfield 글은 이 반응을 정면으로 다룬다. 결론부터: SDD는 프로젝트 시작점을 위한 방법론이 아니다. 변경을 위한 방법론이다.
brown field 프로젝트에서 AI 에이전트가 반복적으로 저지르는 실수가 있다. 하나를 바꿨는데 다섯 개가 바뀐다. 에이전트가 무엇을 건드려도 되는지 모르기 때문이다. 스펙에 "Fuera de scope(범위 외)" 섹션을 명시적으로 넣으면 이게 극적으로 줄어든다. 에이전트는 영리해서 절제하는 게 아니라, 명령에 있으니까 안 건드린다.
이 글이 제안하는 brownfield SDD 흐름은 다섯 단계다. ① 건드릴 단위를 최소화한다(모듈 전체가 아니라 변경할 파트만). ② 원하는 상태보다 현재 상태를 먼저 정확히 기술한다. ③ 에이전트에게는 많은 컨텍스트가 아니라 선별된 컨텍스트를 준다. ④ "이거 맞아 보여?"가 아니라 "스펙이 말한 X가 구현됐어?"로 검증한다. ⑤ 스펙을 repo에 남긴다—노션이나 컨플루언스가 아니라, 해당 코드 옆에.
이 마지막 포인트가 장기적으로 가장 중요하다. 변경마다 남긴 미니 스펙이 쌓이면, 시스템이 메모리를 갖기 시작한다. 6개월 후 같은 모듈을 다시 건드릴 때—그게 본인이든 새 팀원이든—2분 만에 맥락을 복원할 수 있다. 이건 계획한 문서화 프로젝트가 아니라 방법론의 부산물이다.
팀 단위 실행 전략: 내일부터 바꿀 수 있는 것
세 글이 가리키는 공통 처방은 세 가지로 압축된다.
첫째, 스펙을 프롬프트 앞에 놓아라. 새 기능 개발 시작 전 15분 마크다운. Goal, Scope IN/OUT, Data Model, Acceptance Criteria. 팀 컨벤션으로 만들면 온보딩 비용도 동시에 줄어든다.
둘째, AI의 오답을 감사 도구로 써라. 모듈 하나씩 AI에게 설명을 요청하고, 틀린 답변을 comprehension debt 백로그로 전환한다. 별도 도구 없이 즉시 시작할 수 있는 코드베이스 감사다.
셋째, 변경 단위로 스펙을 코드와 함께 버전 관리하라. brownfield든 greenfield든, 스펙이 Confluence에 있으면 AI는 읽지 못한다. 코드 옆 마크다운 파일이 정답이다.
구조가 없으면 속도는 부채를 빠르게 쌓는 엔진이 된다
AI 코딩 도구가 강력할수록 구조 없는 속도의 대가가 빠르게 가시화된다. 프롬프트 한 번에 수백 줄이 생성되는 환경에서 스펙과 comprehension debt는 '나중에 챙길 것'이 아니라 지금 당장의 품질 제어 수단이다.
팀 리빌딩을 준비하거나 AI-First 워크플로우를 도입 중이라면, 도구 선택 이전에 이 두 가지를 먼저 설계할 것을 권한다. 어떤 단위로 스펙을 쓸 것인가. 코드베이스의 의도를 어디에 어떻게 남길 것인가. 이 두 질문에 답이 없으면, 더 좋은 모델을 써도 같은 자리에서 다시 막힌다. AI는 구조를 발명하지 않는다. 우리가 설계한 구조 위에서 작동할 뿐이다.