AI 개발에서 사양, 테스트 및 코드의 동기화 유지

AI 에이전트가 요구사항, 테스트 및 코드에서 벗어나는 것을 막으세요.

Page content

AI 코딩 에이전트는 기능을 빠르게 출시하지만, 명세서(specs), 테스트, 그리고 코드는 조용히 서로 다른 방향으로 이동(drift)합니다. 이 가이드는 추적 가능성(traceability) 모델, 명세서-테스트 및 명세서-코드 매핑, 그리고 병합 전에 이러한 드리프트를 포착하는 CI(CI) 검사를 다룹니다.

실행 중인 시스템과 다시 확인되지 않는 명세서는 전혀 없는 것보다 더 나쁩니다. 왜냐하면 이는 잘못된 확신을 생성하기 때문입니다. 검토자는 차이점(diff)보다 문서에 신뢰를 보내며, “기존 패턴을 따르도록” 지시된 AI 에이전트는 요구사항과 상충되더라도 코드가 실제로 수행하는 행동을 기꺼이 따릅니다.

해결책은 더 많은 문서화가 아닙니다. 대부분의 저장소에 이미 존재하는 네 가지 요소 간의 작고 강제 가능한 연결입니다. 즉, 요구사항, 그 뒤의 설계 결정, 이를 증명하는 테스트, 그리고 이를 변경한 커밋 또는 풀 리퀘스트(Pull Request)입니다.

traceability links connecting specs, tests, and code

이 연결이 공유된 이해도가 아닌 데이터로 존재하게 되면, 이를 쿼리할 수 있습니다. 테스트 커버리지 없는 요구사항, 더 이상 어떤 요구사항에도 매핑되지 않는 테스트, 그리고 일치하는 요구사항 ID 없이 변경된 파일이 포함된 풀 리퀘스트를 확인할 수 있습니다. 이 쿼리가 본문의 실제 산출물이며, 나머지 부분은 이미 사용하고 있을 도구를 사용하여 이를 구축하는 방법을 안내합니다.

드리프트 문제: 명세서, 테스트, 코드가 동기화에서 벗어나는 이유

드리프트는 네 가지 식별 가능한 형태로 나타납니다. AI 지원 팀은 수동으로 모든 코드를 작성하는 팀보다 이 네 가지 드리프트를 더 빠르게 경험하는 경향이 있습니다.

  • 명세서는 변경되지만, 코드는 변경되지 않음. 요구사항이 후속 대화나 댓글 스레드에서 명확해지지만, 구현을 다시 생성하거나 수정하여 이에 맞게 조정하는 사람은 없습니다.
  • 코드는 변경되지만, 명세서는 변경되지 않음. 에이전트나 개발자가 버그를 수정하거나 모듈을 리팩토링하지만, 명세서는 여전히 현재 상태인 것처럼 이전 동작을 기술합니다.
  • 테스트는 의도가 아닌 구현을 커버함. 단위 테스트는 코드가 현재 수행하는 동작을 확인하므로 순환적입니다. 코드가 잘못된 요구사항을 만족하더라도 빌드 통과로 인해 테스트가 통과합니다.
  • 풀 리퀘스트는 요구사항을 참조하지 않음. 검토자는 이에 대해 확인할 명시적인 주장이 없기 때문에 “보기에 합리적이다"는 이유로 차이점을 승인합니다.

AI 개발 프레임워크에 대한 최근 프로세스 연구는 에이전트가 코드 재생성을 빠르고 반복적으로 수행하며, 각 재생성은 명세서와 구현이 조금 더 발산할 수 있는 새로운 기회가 되기 때문에 명세서 드리프트를 반복적인 위험으로 식별합니다. 명세 기반 개발 대 vibes 코딩 논쟁은 실제로 동일한 실패 모드에 대한 논쟁입니다. 즉, 아무도 강제하지 않는 명세서는 추가적인 의식(ceremony)만 있을 뿐 명세서가 없을 때와 동일한 드리프트로 퇴보합니다.

현대적인 스펙 키트(spec-kit) 스타일의 워크플로우에서는 이를 점차 **명세서 부패(specification rot)**로 규정합니다. 명세서는 권위적으로 보이지만 시스템이 실제로 수행하는 동작과의 연결을 조용히 상실합니다. 명세 기반 개발의 핵심 정의는 명세서를 진리의 원천(source of truth)으로 취급하지만, 진리의 원천은 현실과 지속적으로 비교 검증될 때만 그 진실을 유지할 수 있습니다.

AI 지원 개발을 위한 추적 가능성 모델

실용적인 추적 가능성 모델은 비즈니스 요구사항을 구현된 코드 행 및 풀 리퀘스트까지 연결하는 여섯 가지 식별자가 필요합니다. 대부분의 팀은 이미 이 중 세 가지나 네 가지가 존재합니다. 누락된 것은 일반적으로 설계 결정 ID 및 테스트와 커밋에서 명시적으로 되돌아가는 연결입니다.

식별자 위치 예시
요구사항 ID requirements.md 또는 명세 도구 REQ-014
설계 결정 ID ADR / 결정 기록 ADR-0032
작업 ID 작업 분해 또는 이슈 트래커 TASK-014-3
테스트 ID 테스트 파일 또는 테스트 이름 test_req_014_password_reset
커밋 / PR 링크 Git 히스토리 PR #482
변경된 파일 Git diff auth/reset.go, auth/reset_test.go

이러한 식별자 간의 관계는 직선이 아닌 그래프를 형성합니다. 하나의 요구사항에서 여러 작업이 파생될 수 있고, 하나의 풀 리퀘스트가 동시에 여러 요구사항에 영향을 미칠 수 있기 때문입니다.

graph TD REQ["Requirement
REQ-014"] --> ADR["Design Decision
ADR-0032"] ADR --> TASK["Task
TASK-014-3"] TASK --> CODE["Code Change
auth/reset.go"] TASK --> TEST["Test
test_req_014_password_reset"] CODE --> PR["Pull Request
#482"] TEST --> PR PR --> COMMIT["Commit history"]

이 그래프를 구조화된 데이터로 저장하는 것이 나중에 이를 쿼리할 수 있게 해줍니다. GitHub의 Spec Kit 생태계는 정확히 이 방향으로 이동했습니다. spec-kit-trace와 같은 확장 기능은 명세서 파일과 테스트 파일에 내장된 REQ-XXX 토큰을 스캔하고 해당 리터럴 텍스트 매칭에서 결정론적인 행렬을 생성하며, 침묵하는 위양성(false positive)을 생성하는 모호한 이름 기반 추측을 의도적으로 피합니다.

명세서-테스트 매핑: 수용 기준을 테스트 케이스로 전환

명세서의 모든 수용 기준(acceptance criterion)은 구조상 행위적 주장입니다. 즉, 이 상태에서 행위자가 이 동작을 수행하면 시스템은 그와 같이 응답해야 합니다. 이는 이미 테스트 케이스의 형태이므로, 가장 강력한 SDD(명세 기반 개발) 워크플로우는 코드를 생성하는 에이전트가 사후에 자신의 테스트를 발명하도록 요청하는 대신, 코드를 생성하는 것과 동일한 수용 기준에서 테스트를 생성합니다.

이러한 기준을 작성하는 데 널리 사용되는 형식은 EARS(Easy Approach to Requirements Syntax)로, 각 요구사항을 “When <trigger>, the system shall <response>“와 같이 모호하지 않고 테스트 가능한 패턴으로 강제합니다. 이 구조는 모든 요구사항이 가져야 하는 네 가지 유형의 테스트로 깔끔하게 매핑됩니다.

  • 양성 테스트(Positive tests) — 요구사항이 명시적으로 설명하는 이상적인 경로(happy path).
  • 음성 테스트(Negative tests) — 요구사항에서 거부해야 한다고 명시된 입력 또는 상태.
  • 경계 테스트(Boundary tests) — 수용 기준에 언급된 범위, 제한, 임계값의 가장자리.
  • 마이그레이션 테스트(Migration tests) — 요구사항 이전의 데이터 또는 상태에 대한 동작으로, 오래된 레코드가 새 규칙을 조용히 우회하지 않도록 합니다.
요구사항 유형 추가할 테스트 카테고리 흔한 실수
“시스템은 X를 거부해야 함” 음성 테스트 수용 경로만 테스트됨
“제한은 N개 항목” 경계 테스트 N-1, N, N+1이 모두 커버되지 않음
“새 필드가 기존 필드를 대체함” 마이그레이션 테스트 새 필드가 없는 오래된 레코드가 침묵하며 충돌
“60초 이내” 경계 + 타이밍 테스트 실제 시간 예산이 아닌 로직만 테스트로 확인

이렇게 작성된 단위 테스트는 여전히 피라미드의 빠르고 저렴한 계층으로 중요합니다. 이를 구조화하는 실용적인 패턴은 Go 단위 테스트 가이드Python 단위 테스트 가이드에서 다루고 있습니다. 추적 가능성이 추가하는 것은 테스트 이름이나 테스트 댓글에 내장된 리터럴이고 안정적인 요구사항 토큰입니다. 이를 통해 후속 쿼리가 REQ-014에 커버리지가 있음을 가정하는 것이 아닌 증명할 수 있습니다.

명세서-코드 매핑: 설계 계획에서 추적 테이블로

명세서-테스트 매핑은 동작을 증명합니다. 명세서-코드 매핑은 범위를 증명합니다. 이는 다른 질문에 답합니다. 즉, 이 요구사항을 위해 실제로 변경되어야 할 파일은 무엇이었으며, 차이점(diff)이 해당 경계 내에 머물렀는지 아니면 관련 없는 모듈로 스며들었는지 여부입니다.

앞서 영향을 받는 파일을 나열하는 설계 계획(조악한 목록이라도)은 나중에 실제 풀 리퀘스트와 비교할 수 있는 기준을 제공합니다. 코드 내의 주석은 검토자가 명세서 자체에서 얻을 수 없는 정보를 추가할 때만 요구사항 ID를 참조해야 합니다. 요구사항 텍스트를 그대로 반복하는 주석은 소음(noise)이지만, // enforces REQ-014 boundary: max 5 reset attempts per hour는 숫자가 차이점에서 보이지 않기 때문에 그 자리를 차지할 가치가 있습니다.

생성된 추적 테이블은 이를 검토자가 두 문서를 나란히 읽어서 재구성해야 할 것이 아니라 몇 초 만에 검토할 수 있는 무엇으로 변환합니다.

요구사항 설계 결정 변경된 파일 테스트 상태
REQ-014 ADR-0032 auth/reset.go, auth/reset_test.go test_req_014_* (4) 커버됨
REQ-015 ADR-0032 auth/reset.go 없음 격차(Gap)
REQ-016 auth/notify.go test_notify_basic 고아 명세서 링크

이 단일 테이블은 가장 흔한 두 가지 실패 패턴을 한눈에 드러냅니다. REQ-015는 일치하는 테스트 없이 코드가 변경되었고, REQ-016에 연결된 테스트는 실제로 요구사항 ID를 참조하지 않으므로 명세서가 누락되었거나 테스트가 잘못 분류되었음을 의미합니다.

풀 리퀘스트 워크플로우: 명세서, 코드, 테스트 차이점 함께 검토

추적 가능성을 중심으로 구축된 풀 리퀘스트는 하나 대신 세 가지 차이점을 나란히 검토합니다. 즉, 명세서에서 변경된 부분, 코드에서 변경된 부분, 그리고 테스트에서 변경된 부분입니다. 검토 질문은 “이것이 맞습니까?“에서 훨씬 더 구체적인 “이 변경은 어떤 요구사항을 만족시키며, 증거가 이를 증명합니까?“로 바뀝니다.

sequenceDiagram participant Dev as Developer or Agent participant PR as Pull Request participant CI as CI Pipeline participant Rev as Reviewer Dev->>PR: Open PR with spec diff + code diff + test diff PR->>CI: Trigger traceability checks CI->>CI: Verify REQ-ID present in PR description CI->>CI: Run spec-to-test coverage check CI->>CI: Run spec-to-code file-scope check CI-->>PR: Post trace report as PR comment Rev->>PR: Review against "which requirement does this satisfy?" Rev->>PR: Approve or request changes

검토자는 마감 압박 하에 긴 체크리스트를 건너뛰기 때문에, 여기서 짧고 구체적인 검토자 체크리스트가 더 효과적입니다.

  1. PR 설명에 충족하는 요구사항 ID가 명시되어 있습니까?
  2. 변경된 모든 파일이 설계 계획의 영향 파일 목록에 나타나 있거나, 추가 범위가 설명되어 있습니까?
  3. 이 PR이 접한 각 요구사항 ID를 최소 하나의 새 테스트 또는 기존 테스트가 참조하고 있습니까?
  4. 명세서가 변경된 경우, 코드와 테스트가 동일한 PR에서 변경되었습니까? 아니면 추적되는 후속 작업이 있습니까?

CI에서 추적 가능성 자동화

수동 검토는 검토자가 찾아보기를 기억할 때만 드리프트를 포착하므로, 위의 검사들은 아무도 다시 읽지 않는 위키 페이지가 아닌 CI에 속합니다. 빌드 및 테스트 작업에 이미 사용하는 동일한 GitHub Actions 치트시트 패턴이 여기서 직접 적용됩니다. 추적 가능성 검사는 동일한 파이프라인의 또 다른 작업일 뿐입니다.

노력 순서대로 대략적인 자동화 아이디어:

  • 명세서 파일에 대한 CI 검사 — 동일한 PR에서 대응하는 코드 또는 테스트 변경 없이 명세서 파일이 편집되었거나 그 반대의 경우 빌드를 실패시킵니다.
  • PR 제목 또는 설명에서 요구사항 ID 필수화 — 가벼운 정규식 검사(REQ-\d+)가 구현 내용을 명시하지 않는 병합을 차단합니다.
  • 에이전트 생성 추적 요약 — 에이전트가 PR이 접한 요구사항의 짧은 요약을 생성하도록 하여, 사람이 처음부터 작성하는 대신 확인하게 합니다.
  • 수용 기준별 테스트 커버리지 — 라인별이 아닌 — 라인 커버리지는 코드가 실행되었음을 알려주지만, 요구사항 커버리지는 주장이 확인되었음을 알려줍니다.
  • 陳腐化 명세서 경고 — 연결된 파일을 접한 N개의 커밋 동안 건드리지 않은 명세서를 플래그 처리합니다. 오랫동안 침묵하는 명세서가 조용히 부패할 가능성이 가장 높기 때문입니다.

GitHub의 Spec Kit 위에 구축된 확장 기능은 이미 기계적으로 이 중 몇 가지를 구현합니다. 하나는 명세서 및 테스트 파일 전반에 걸쳐 리터럴 REQ-XXX 토큰을 스캔하여 행렬을 구축하고 고아 테스트를 플래그 처리하며, 더 엄격한 V-모델 중심 패키지는 이를 한 단계 더 나아가 각 개발 명세서에 대해 페어 테스트 명세서를 생성하고 IEC 62304 또는 ISO 26262와 같은 규제 프레임워크 하에서 작업하는 팀을 위해 여러 추적 가능성 행렬을 생성합니다. 대부분의 프로젝트에는 이러한 수준의 의식이 필요하지 않지만, 근본적인 아이디어인 결정론적이고 스크립트 생성 행렬(수동 유지 관리 스프레드시트가 아님)은 규모가 커질 때와 마찬가지로 작아질 때도 잘 확장됩니다.

추적 가능성을 위한 AI 에이전트 사용, 오라클로서가 아닌

AI 에이전트는 추적 가능성의 기계적 부분에 적합하지만, 요구사항이 실제로 충족되었는지에 대한 최종 심판자로서는 적합하지 않습니다. 세 가지 작업은 에이전트의 강점에 직접적으로 부합합니다.

  • 명세서와 차이점 비교 — 에이전트에게 PR에서 접한 명세서 파일에서 언급된 모든 요구사항을 나열하고, 대응하는 코드를 찾지 못한 모든 요구사항을 나열하도록 요청합니다.
  • 커버되지 않은 요구사항 찾기 — 에이전트에게 테스트 스위트에서 요구사항 토큰을 스캔하고 명세서에서 어느 요구사항이 커버리지 없는지 보고하도록 요청합니다.
  • 명세서로 설명되지 않은 코드 감지 — 에이전트에게 요구사항을 가진 모듈에 접근하지만 차이점의 어떤 요구사항 ID에도 대응하지 않는 변경된 파일 또는 함수를 플래그 처리하도록 요청합니다.

수호해야 할 실패 모드는 에이전트의 요약을 검토자의 시작점이 아닌 사실(ground truth)로 신뢰하는 것입니다. 에이전트는 주석을 오독하거나, 두 파일에 걸쳐 분할된 요구사항 토큰을 놓치거나, 코드 경로를 표면적으로만 테스트하는 테스트에 대해 자신감 있게 커버리지를 선언할 수 있습니다. 에이전트 생성 추적 보고서를 주니어 검토자의 검토 결과처럼 취급합니다. 유용하고 빠르지만 병합을 차단하기 전에 두 번째 확인이 필요하다는 것입니다. 이는 AI 기반 개발을 위한 결정 기록에 적용되는 동일한 주의사항입니다. 기록은 작성한 에이전트 외에 다른 것이 최종적으로 확인하지 않으면 신뢰할 수 있는 상태로 유지되지 않습니다.

복사할 수 있는 최소 추적 가능성 템플릿

시작하기 위해 무거운 프레임워크가 필요하지 않습니다. 설명하는 코드 옆에 저장소에 체크인된 다섯 파일 템플릿이 필수 요소를 커버합니다.

docs/
  requirements.md     # EARS 스타일 수용 기준을 갖춘 REQ-ID
  design.md           # ADR-ID, 영향 파일, 아키텍처 결정
  tasks.md            # 하나 이상의 REQ-ID에 매핑된 TASK-ID
  tests.md            # 어떤 테스트 파일/함수가 어떤 REQ-ID를 참조하는지
  traceability.md     # 생성된 테이블: REQ -> ADR -> TASK -> files -> tests -> PR

requirements.md, design.md, tasks.md명세 기반 개발 워크플로우에서 이미 설명한 것처럼 인간과 에이전트가 함께 작성하거나 편집합니다. tests.mdtraceability.md는 생성되어야 하며, 생성기가 테스트 디렉터리와 명세서 파일 전반에 걸쳐 REQ-\d+를 grep하는 짧은 스크립트일지라도 수동으로 유지 관리되어서는 안 됩니다. 수동으로 유지 관리되는 추적 테이블은 마감 압박 하에 아무도 스프레드시트를 업데이트하지 않기 때문에 자체적으로 드리프트 위험의 한 형태입니다.

결론

명세 기반 개발은 에이전트에서 코드가 나오는 순간 완료되는 것이 아닙니다. 코드가 테스트 및 명세와 함께 시간이 지남에 따라 PR, 리팩토링, 그리고 달 단위 간격으로 도착하는 요구사항 변경을 통해 서로를 정직하게 유지할 때만 유용합니다. 여섯 가지 평범한 식별자로 구축되고, 소수의 CI 검사로 강제되며, 짧은 PR 체크리스트로 검토되는 추적 가능성 모델은 전체 컴플라이언스 프레임워크의 오버헤드 없이 대부분의 이점을 제공합니다. 최소 템플릿으로 시작하고, 가장 저렴한 CI 검사(PR 설명의 요구사항 ID)를 먼저 연결한 후, 그 습관이 자리 잡으면 추적 테이블 및 부패 명세서 경고를 추가하십시오.

추적 가능성은 프로덕션 앱 아키텍처 클러스터 전반에 걸쳐 다루어진 더 큰 테스트 및 문서화 규율의 한 부분이며, AI 개발 도구 클러스터에서 탐구된 도구질(tooling) 질문과 함께 팀이 표준화할 에이전트 워크플로우를 선택하는 데 도움이 됩니다.

구독하기

시스템, 인프라, AI 엔지니어링에 관한 새 글을 받아보세요.