AI가 작성한 코드는 'β 버전': 설계서부터 구현 및 단위 테스트까지 에이전트에게 맡기는 시스템
요약
본 글은 설계서(Markdown)를 기반으로 AI 에이전트에게 코드 생성부터 단위 테스트까지 맡기는 체계적인 개발 시스템을 소개합니다. 핵심은 AI가 만든 결과물(β 버전)이라도 사람이 전 과정을 리뷰하고, 5단계의 검증 프로세스를 거쳐 품질과 책임 소재를 확보하는 것입니다.
핵심 포인트
- AI 생성 코드는 'β 버전'으로 간주하며, 모든 결과물에 대한 사람의 최종 리뷰가 필수적입니다.
- 개발은 계획-코드-테스트-검토-PR로 이어지는 5단계(Phase)를 거쳐 진행됩니다.
- 모든 규칙과 진실은 `guidelines/`와 같은 중앙 저장소에 모아 관리하는 것이 중요합니다.
- AI 에이전트의 역할 분담을 명확히 하고, 사람의 검증 단계를 반드시 포함해야 합니다.
AI에게 코드를 작성하게 하면 놀라울 정도로 빠르게 결과물이 나옵니다. 하지만 동시에 '정말로 설계서대로인가?', '테스트는 의미 있는 것을 확인하고 있는가?'와 같은 불안감도 남아있습니다.
본 글에서는 설계서(Markdown)로부터 Spring Boot의 배치(Batch) 및 API 코드는 물론 단위 테스트(Unit Test)까지 AI에게 작성하게 하고, 기계 검증과 사람의 리뷰를 통해 품질을 확보하는 시스템을 소개합니다. 실제 구현물은 ai-coding 리포지토리에 공개되어 있습니다. 샘플 업무(회원/주문)와 ID는 모두 가상의 데이터입니다.
이 시스템에서는 처음에 역할을 명확히 구분하고 있습니다.
AI (GitHub Copilot의 에이전트)는 설계서로부터 β 버전 소스 코드와 단위 테스트를 만드는 도구입니다. 최종적인 품질 확보는 사람이 담당합니다.
AI를 사용하더라도 결과물에 대한 책임 소재가 바뀌지는 않습니다. 그 위에서, 품질 확보의 원칙을 5가지로 정립했습니다.
- 실행 지시 및 결과물 저장소 관리는 사람의 역할이다. 어떤 설계서로, 어떤 공정을 돌릴지 여부는 사람이 결정합니다.
- AI가 생성한 결과물(β 버전)은 전량 리뷰한다. 그대로 채택하지 않습니다.
- AI 리뷰는 보조 수단이다. 사람의 리뷰를 대체할 수 없습니다.
- 과제는 티켓으로 추적한다. 리뷰에서 발견된 수정 사항에 대한 누락을 방지합니다.
- 설계서에 AI가 필요한 정보를 기재한다. 파라미터 파일명, 메시지 ID, 테이블명 등이 해당됩니다.
다섯 번째 원칙은 시리즈의 첫 글에서 다룬 '입력 부족' 문제 그 자체입니다. AI가 추측으로 채우기 쉬운 정보들을 처음부터 설계서에 명시해 둡니다.
리포지토리는 역할별로 저장 위치를 분리하고 있습니다.
| 역할 | 저장소 | 내용 |
|---|---|---|
| 규칙 (유일한 진실) | guidelines/ | 규약, 구성, 품질 게이트, 리뷰 관점, AI 활용 원칙 |
| 검증 | template/, tools/spec-trace, scripts/ | |
| 기계로 판정하는 품질 게이트 | ||
| 실행 | .github/agents/, .github/prompts/ | 역할별 에이전트와 호출용 프롬프트 |
핵심은 규칙의 진실을 guidelines/ 한 곳에 모아두는 것입니다. AI든 사람이든, 이곳만을 근거로 삼습니다. 에이전트에 대한 지시에도 '여기에 적혀있지 않은 규칙을 만들지 말라. 막히면 추측하지 말고 확인 사항으로 사람에게 되돌려주라'고 명시했습니다.
개발은 5단계(Phase)로 진행됩니다. 단계가 바뀔 때마다 사람의 리뷰(RP1~RP5)를 거칩니다.
| 단계 | Copilot Chat 명령어 | 수행 내용 | 사람의 리뷰 |
|---|---|---|---|
| 1 계획 | /ai-plan | 생성할 파일 목록과 요구사항 대응표 작성 | RP1 계획 |
| 2 코드 생성 | /ai-code | 소스 코드를 만들고 기계 검증을 통과시킴 | RP2 코드 |
| 3 테스트 생성 | /ai-test | 단위 테스트와 API 시나리오 테스트를 작성 | RP3 테스트 |
| 4 대조/검토 | /ai-review | 설계서와 코드의 판정표를 만듦 | RP4 최종 확인 |
| 5 CI + PR | /ai-pr | 모든 검증을 재실행하고, PR 본문을 초안 작성함 | RP5 PR |
현재 어느 단계에 있는지 알 수 없을 때는 /ai-status로 확인할 수 있습니다.
.github/agents/에는 역할별 에이전트가 배치되어 있습니다.
- planner: 계획을 세움
- coder: 코드를 작성함 (테스트는 작성하지 않음)
- tester: 테스트를 작성함
- reviewer: 설계서와 코드를 대조함
- orchestrator: 현재 단계를 판단하고, 다음 담당자와 사람의 리뷰로 이관함 (스스로는 작성하지 않음)
코드를 작성하는 담당과 테스트를 작성하는 담당을 분리한 것이 핵심입니다. 같은 AI가 두 가지 모두를 작성하면, 테스트가 구현에 끌려가기 쉽습니다.
AI는 '구현했습니다', '테스트가 통과했습니다'라고 보고합니다. 하지만 실제로는 함수가 없거나, 테스트가 아무것도 확인하지 못하는 경우가 발생합니다.
그래서 상태를 기계적으로 뒷받침하도록 설계했습니다. 중심이 되는 것이 바로 요구사항-구현-테스트의 대응표입니다.
spec_id: "FEAT-JOB0001"
source: "samples/design-docs/batch/JOB0001_order-import.md"
source_sha256: "..." # 사람이 승인한 후 spec-trace pin으로 기록
...
요건별로 상태가 3단계가 있습니다.
| 상태 | 누가 부여하는가 | 조건 (도구가 확인) |
|---|---|---|
| pending | planner | 없음 |
| implemented | coder | implements 파일과 함수가 실제로 존재하는 경우 |
| proven | tester | proofs 테스트가 실재하고, 어설션(assertion)이 있는 경우 |
에이전트가 상태를 올리는 것은 문제가 없습니다. 다만 조건을 충족하지 못했다면 CI의 spec-trace check가 실패합니다. 단순히 '만들었다'고 적는 것만으로는 통과할 수 없다는 의미입니다.
대응표에는 사람이 승인한 시점의 설계서 해시(source_sha256)를 기록합니다. 만약 설계서가 나중에 수정되면, 해시가 일치하지 않게 되어 CI에서 감지됩니다. 차이점을 보고 영향을 받는 요건을 pending으로 되돌려 다시 진행해야 합니다.
머지(Merge)하기 전에는 기계로 판정하는 체크(결정론적 게이트, deterministic gate)를 모두 통과하는 것이 필수입니다.
| ID | 내용 | ID | 내용 |
|---|---|---|---|
| D1 | 컴파일 (Java 17) | D7 | 테스트의 실재와 어설션 |
| ... | |||
| N1 |
같은 판정은 로컬에서도 실행할 수 있습니다.
./scripts/verify-gates.sh --final # Windows: .
pm scriptserry-gate.ps1 -Final
포함된 샘플은 5단계가 완료된 상태(모든 요건이 proven)입니다. template/의 코드를 일부러 조금 망가뜨려서 실행해 보면, 어느 게이트에서 멈추는지 확인할 수 있습니다.
AI에게 체크를 통과하도록 요청하면, 오히려 체크 자체를 무력화시켜 버리는 경우가 있습니다. 그래서 다음 변경 사항은 명확하게 금지하고 있습니다.
- 테스트의 기대값을 설계서가 아닌 구현에 맞춰 수정하는 것
- 어설션(assertion)을 삭제하거나 약화시키는 것 (
assertTrue(true)등), 실행만 하는 테스트로 만드는 것 - Checkstyle 또는 SpotBugs의 제외 처리나
@SuppressWarnings를 이유 없이 추가하는 것 - 테스트나 시나리오를
@Disabled/@ignore등으로 비활성화하는 것 - 대응표에서 요건을 삭제하거나, 근거 없이 상태를 올리는 것
- 커버리지(coverage) 임계값을 낮추는 것
이러한 금지 사항은 에이전트 지침과 CI 체크 양쪽에 모두 포함되어 있습니다. 지침만으로는 AI가 잊어버릴 수 있기 때문에, 기계적으로도 막는 이중의 장치입니다.
테스트 작성 규칙은 테스트 파일을 편집할 때만 로드되는 지침 파일(.github/instructions/test.instructions.md)에 작성되어 있습니다 (발췌).
---
applyTo: "template/src/test/**"
---
...
에이전트는 체크가 통과하지 못하면 원인을 고쳐서 재실행합니다. 다만, 다음 중 하나에 해당할 경우 즉시 멈추고 사람에게 되돌립니다라고 결정했습니다.
- 같은 단계에서 3번을 수정해도 통과하지 않는 경우
- 같은 오류가 2번 연속 발생하는 경우 (수정하지 못하는 경우)
- A → B → A 와 같이, 동일한 수정을 반복하는 경우
- 고칠 때마다 에러가 증가하는 경우
세 번째 항목은 이 시리즈의 첫 기사에서 작성했던 'A이면 B, B이면 A' 루프입니다. 왔다 갔다 하는 것은 판단에 필요한 전제가 부족하다는 신호였습니다. 그래서 AI에게 매달리게 하기보다는 사람에게 되돌려 전제를 채웁니다.
사람에게 되돌릴 때는 '각 회차에서 수정한 내용', '오류의 추이', '수렴하지 않는다고 판단한 이유'를 첨부하도록 합니다. 보고서에는 실행했던 명령어와 결과도 반드시 포함해야 합니다. '실행하지 않은 것을 '확인 완료'라고 적지 않는다'는 것도 규칙에 포함되어 있습니다.
-
개발 환경(Java 17 및 Rancher Desktop)과 Python 3.12를 준비합니다. 환경은 dev-environment에서 준비할 수 있습니다.
-
리포지토리를 clone하고, 게이트를 실행합니다.
git clone https://github.com/shoyo-maya-bot/ai-coding.git && cd ai-coding pip install -e tools/spec-trace ./scripts/verify-gates.sh --final
모든 게이트가 통과하는지 확인한다.
template/
내부의 테스트에서 어설션(assertion)을 하나 제거하고 다시 실행한다.
어느 게이트에서 멈추는지 본다.
고장 내서 멈추는 지점을 한 번 살펴보면, "기계가 무엇을 보호해주고 있는지"를 체감할 수 있습니다.
AI의 보고를 그대로 믿지 않기
"테스트가 통과했습니다"라고 해도, 실제로 실행되지 않은 경우가 있습니다. 보고서에는 실행한 커맨드와 결과를 첨부하게 하고, 상태는 CI(Continuous Integration)로 검증합시다.
규칙이 여기저기 흩어지는 것
프롬프트, 지시 파일, 위키에 각각 규칙을 작성하면 내용이 서로 충돌하게 됩니다. 규칙의 정식은 한 곳에서 결정하고, 다른 장소에서는 그곳을 참조하도록 합시다.
체크를 통과하는 것이 목적이 되는 경우
에러를 없애기 위해 체크(check) 자체를 느슨하게 만드는 것은 흔한 실수입니다. 금지 사항으로 명문화하고, CI에서도 감지할 수 있도록 합니다.
AI에게 몇 번이고 수정하게 하는 것
같은 에러를 반복적으로 주고받는 것은 전제가 부족하다는 신호입니다. 횟수의 상한을 정하고, 일찍 사람에게 되돌려줍시다.
AI에게 코드를 작성하게 할 때 중요한 것은, AI를 똑똑하게 만드는 것보다 AI의 출력을 확인할 수 있는 시스템을 만드는 것입니다.
- AI가 생성한 결과물은 베타(β) 버전으로 간주하고, 사람이 전체 검토를 한다.
- "구현했다", "테스트했다"는 대응표(mapping table)와 CI로 뒷받침한다.
- 체크의 우회(bypass)를 금지하고, 루프가 발생하면 사람에게 되돌린다.
이 시스템은, 하네스(Harness) 기사에서 작성한 생각을 실제 개발 공정에 적용한 것입니다. 모델을 바꾸기 전에, 먼저 확인하는 시스템부터 정비해 보세요.
이 글은 Zenn에도 게시되어 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기