나는 코딩 에이전트에게 구현을 맡기기 전에 기획서와 스펙을 쓴다. 화면 동작과 실패 상황을 친구에게 설명하듯 풀어 쓴 문서도 만든다. 혼자 “마이프랜 스펙”이라고 부르던 방식이다. GSD Core와 GitHub Spec Kit를 읽으면서 이 문서들이 스펙 주도 개발의 일부라는 것을 알았다.
문서를 먼저 만들어도 실패는 생겼다. 구현 중에 데이터 제약이 발견돼 결정을 바꿨지만 SPEC에는 이전 방식이 남았다. 다음 세션은 오래된 문서를 현재 요구사항으로 읽었다. 코드와 테스트는 새 결정을 따르고 기획서는 옛 화면을 설명하는 상태가 됐다.
스펙 주도 개발을 운영하려면 문서의 수보다 현재 상태를 맞추는 규칙이 필요하다. 각 문서가 답할 질문과 갱신 시점, 소유자를 정하고 테스트 결과를 요구사항과 연결해야 한다.
네 문서에 서로 다른 질문을 맡긴다
기획서와 구현 스펙을 한 파일에 넣으면 사용자 목적과 기술 제약이 섞인다. “쉽게 사용한다”는 문장 아래에서 오류 처리와 데이터 보존 조건을 찾기 어렵다. 나는 다음 구조를 쓴다.
BRIEF.md 누구의 어떤 문제를 풀고 성공을 어떻게 관찰하는가
SPEC.md 외부 행동, 상태, 예외, 불변 조건은 무엇인가
PLAN.md 어떤 순서와 파일 경계로 구현하고 무엇을 검사하는가
STATE.md 완료한 항목, 현재 결정, 남은 위험과 다음 시작점
BRIEF.md에는 사용자와 상황, 현재 대안, 성공 지표를 적는다. 기술 프레임워크와 파일명은 넣지 않는다. 제품 목표가 바뀌지 않는 한 자주 수정하지 않는다.
SPEC.md는 사용자가 관찰할 행동을 적는다. 입력과 출력, 권한, 실패 상태, 기존 데이터 처리까지 포함한다. “로그인이 된다”에서 멈추지 않고 세션 만료와 잘못된 비밀번호, 작성 중 입력 보존을 설명한다.
PLAN.md는 구현 기간에 가장 많이 바뀐다. 대상 파일과 의존성, 마이그레이션, 테스트와 롤백을 기록한다. STATE.md는 세션이 끝날 때 다음 작업자가 읽을 현재 상태다. 긴 회고보다 완료 항목과 검사 결과, 막힌 결정에 집중한다.
마이프랜 스펙으로 이해도를 확인한다
마이프랜 스펙은 별도 권위 문서로 만들지 않는다. SPEC의 행동을 기술 용어 없이 설명한 버전이다. 다음 질문에 답하지 못하면 구현을 시작하지 않는다.
사용자는 어떤 화면에서 무엇을 누르는가
성공하면 무엇이 달라지는가
실패하면 무엇을 보고 다시 시도하는가
기존 데이터는 유지되는가
누가 이 기능을 사용할 수 있는가
되돌리면 사용자는 어떤 상태로 돌아가는가
설명 중에 “시스템이 적절히 처리한다” 같은 표현이 나오면 결정을 더 내려야 한다. 오류 메시지와 재시도, 데이터 보존 방식을 명시한다. 친구에게 설명하기 어렵다는 사실은 코드 문제가 생기기 전 요구사항의 빈칸을 보여 준다.
GSD의 단계 루프에서 가져온 것
GSD Core는 각 milestone에서 Discuss, Plan, Execute, Verify, Ship의 다섯 단계를 반복한다. 무거운 조사와 계획, 실행을 새 컨텍스트에서 처리하고 STATE.md와 CONTEXT.md 같은 산출물로 세션을 연결한다.
내 작업에는 두 원칙을 적용했다.
- 결정과 미해결 질문을 대화 기록 밖의 파일에 남긴다.
- 구현자가 보고한 완료를 검증 단계에서 다시 판정한다.
새 컨텍스트는 이전 세션의 불필요한 시행착오를 받지 않는다. 필요한 결정까지 빠지면 같은 질문을 반복한다. 작업자는 현재 단계에 필요한 SPEC, PLAN 구간과 관련 파일, STATE를 선별해 제공해야 한다.
컨텍스트 크기를 줄이려고 핵심 제약을 요약에서 빼지 않는다. 삭제와 외부 시스템 쓰기, 개인정보처럼 위험한 조건은 각 단계의 입력에 반복해서 넣는다. 세션 분리는 문서 품질이 있을 때 도움이 된다.
구현 전에 모호함을 질문으로 만든다
에이전트에게 계획부터 작성하게 하면 빈칸을 자기 가정으로 채울 수 있다. 먼저 결과를 바꿀 질문을 요청한다.
SPEC을 구현하기 전에 사용자 행동, 데이터 보존, 권한,
실패 처리와 배포 결과를 바꿀 모호함을 질문해줘.
답변에서 확정한 결정과 보류한 결정을 분리해줘.
질문을 모두 받았다고 구현을 시작하지 않는다. 답변을 SPEC에 반영하고 변경된 완료 조건을 확인한다. 보류한 결정이 구현 경로를 바꾸면 작업을 작은 조사 단계로 나눈다.
PLAN에는 선택한 접근과 버린 대안을 짧게 적는다. 다음 세션이 같은 대안을 다시 검토할 필요가 없고, 조건이 바뀌었을 때 결정을 재평가할 근거가 생긴다.
결정: archive 글은 기존 URL에서 /archive/{slug}로 308 이동
이유: 기존 링크를 보존하고 검색 노출을 분리
대안: 원래 URL에 noindex를 유지
영향: 라우팅, canonical, sitemap, 관련 글
요구사항에 ID와 증거를 붙인다
SPEC의 행동 문장에 ID를 붙이면 테스트와 검토가 쉬워진다.
REQ-01 검토된 primary 글만 /posts에 표시한다.
REQ-02 archive 글은 검색 색인을 허용하지 않는다.
REQ-03 기존 post URL은 archive URL로 영구 이동한다.
REQ-04 primary 글에만 광고 스크립트를 로드한다.
PLAN과 테스트 이름이 같은 ID를 참조한다.
REQ-01 -> primary allowlist audit
REQ-02 -> archive HTML robots assertion
REQ-03 -> HTTP 308 redirect smoke test
REQ-04 -> page source ad script check
요구사항 하나에 증거가 없으면 구현 완료로 표시하지 않는다. 테스트로 판정하기 어려운 문체와 사용자 이해도는 사람 검토 항목으로 남긴다. 자동 검사와 수동 승인을 같은 표에 기록할 수 있다.
구현 중 발견한 제약을 STATE에 남긴다
코드를 쓰면서 문서에서 예상하지 못한 제약을 발견한다. 데이터베이스가 아직 새 필드를 갖지 않았거나 호스팅 환경이 로컬 파일을 포함하지 않을 수 있다. 구현자가 혼자 우회하면 다음 세션이 그 이유를 알지 못한다.
STATE.md에는 다음 형식을 사용한다.
발견: 배포 DB에 visibility 열이 없음
영향: primary/archive 조회가 배포 전 실패할 수 있음
결정: primary 20편은 Git Markdown에서 읽고 DB는 fallback 사용
후속: migration 적용 후 DB와 파일 결과 비교
검증: build, runtime route smoke test
결정이 사용자 행동을 바꾸면 SPEC도 고친다. 구현 세부만 바뀌면 PLAN과 STATE를 갱신한다. 어느 문서를 바꿀지 분명해야 같은 내용을 여러 파일에 복사하지 않는다.
세션 종료 시에는 completed, in_progress, pending 상태를 사용한다. “거의 끝남” 같은 표현은 다음 작업자가 완료 범위를 판단하기 어렵다. 실행한 검사와 실행하지 못한 외부 작업을 분리한다.
스펙과 코드의 어긋남을 찾는 검토
검토자는 코드 스타일보다 SPEC의 행동을 따라간다.
각 REQ에 구현과 증거가 있는가
SPEC에 없는 사용자 행동이 추가됐는가
오류와 빈 상태가 문서대로 보이는가
기존 데이터와 URL이 보존되는가
승인하지 않은 권한과 외부 쓰기가 생겼는가
STATE의 미해결 위험이 배포 전에 처리됐는가
실제 화면과 API 응답을 확인하고 테스트 이름만 믿지 않는다. 테스트가 구현의 현재 행동을 복사해서 기대값으로 사용하면 잘못된 스펙도 통과할 수 있다. 대표 사용자 흐름을 수동으로 걷고 자동 검사 결과와 함께 남긴다.
코드 변경이 끝난 뒤 SPEC의 모든 문장을 현재형으로 읽는다. 더 이상 지원하지 않는 행동을 삭제하고 변경 이유는 Git 이력이나 결정 로그에 보존한다. 오래된 문장을 “나중에 참고”라는 이유로 본문에 남기면 다음 에이전트가 구현 대상으로 읽는다.
작업 크기에 맞춰 문서를 줄인다
한 파일의 오타와 작은 버그를 고치려고 문서 네 개를 만들 필요는 없다. 나는 다음 기준을 사용한다.
| 작업 조건 | 필요한 기록 |
|---|---|
| 한 세션, 한 파일, 데이터 영향 없음 | 이슈 설명과 테스트 |
| 여러 파일, 기존 행동 변경 | SPEC 구간과 PLAN |
| 데이터 마이그레이션 또는 외부 배포 | BRIEF, SPEC, PLAN, STATE |
| 여러 세션과 여러 작업자 | 요구사항 ID, 결정 로그, 인수인계 |
문서 작성 시간이 구현과 검토 시간을 넘으면 형식을 줄인다. 위험한 작업에서 기록을 생략해 속도를 맞추지는 않는다. 작업의 기간과 가역성, 참여자 수가 문서 수준을 정한다.
스펙은 구현을 시작할 방향을 제공한다. STATE와 테스트는 현재 코드가 그 방향에 있는지 보여 준다. 나는 문서마다 답할 질문과 갱신 시점을 정하고, 요구사항 ID를 실행 증거와 연결한다. 이 동기화가 유지돼야 다음 에이전트가 오래된 설명을 현재 명령으로 오해하지 않는다.