테마 전환

Codex 테스트 주도 개발: AI가 먼저 테스트를 쓰고 모두 그린까지 고치게 하기

Easton editorial illustration: Codex project workflow bench

"OpenAI Codex Best practices는 Goal, Context, Constraints, Done when을 명확히 쓰고, 테스트, 체크, review를 완료 기준에 넣는 것을 권장합니다."

터미널에서 Codex가 “모든 테스트가 통과했습니다”라고 출력합니다. 그런데 자세히 보면 결론만 있습니다. 어떤 명령을 실행했는지, 어떤 테스트를 돌렸는지 나오지 않습니다. diff를 열어 보니 테스트 파일도 바뀌었습니다. assertion이 toEqual(42)에서 toBeTruthy()로 바뀌어 있습니다. 문제는 Codex를 못 믿는 것이 아닙니다. 증거 없는 “그린”을 믿을 수 없다는 점입니다. 이 글은 재사용 가능한 작업 템플릿, 증거 검수 체크리스트, 가짜 그린 방지 guardrail을 제공합니다. “AI를 믿기”가 아니라 “재현 가능한 증거를 보기”로 바꾸는 방식입니다.

Codex에서는 왜 테스트 선행이 더 중요한가

테스트 선행은 단순히 “테스트를 먼저 쓰고 코드를 나중에 쓰는 것”이 아닙니다. Codex를 사용할 때 테스트는 기계가 실행할 수 있는 인수 기준이 됩니다. Codex는 명령을 실행하고, 출력을 읽고, 파일을 수정할 수 있습니다. 하지만 “완료”의 기준은 미리 명확히 알려 줘야 합니다.

OpenAI Codex 공식 문서에 따르면, Codex는 작업을 검증할 수 있을 때 더 나은 결과를 내는 경향이 있습니다. 즉 어떻게 검증할지, 어떤 명령을 사용할지, 어떤 결과를 기대하는지 알려 줄수록 올바른 수정에 가까워집니다. 모호한 작업은 모호하게 처리되기 쉽습니다. 작은 작업일수록 테스트와 review가 쉽습니다.

red-green-refactor의 세 단계 루프는 다음과 같습니다.

단계Codex가 하는 일확인할 증거
Red테스트만 작성하고 구현은 쓰지 않음실패한 테스트 이름, 실패한 assertion
Green최소 구현만 하고 테스트는 변경하지 않음통과한 테스트, 수정 파일 목록
Refactor구조를 정리하고 테스트를 다시 실행여전히 그린이고 diff에 테스트 파일이 없음

이 세 단계는 생략하면 안 됩니다. red를 건너뛰면 테스트가 정말 새 동작을 검증하는지 알 수 없습니다. refactor를 건너뛰면 임시방편 코드가 남기 쉽습니다. Codex 기본기는 Codex 완전 입문 가이드를 참고하세요.

Red Phase: Codex가 실패 가능한 테스트만 쓰게 하기

Red phase의 핵심은 제약입니다. 테스트 파일만 수정하고 구현 코드는 쓰지 않습니다. Codex에게 먼저 테스트 목록을 나열하게 하고, 하나씩 생성하게 한 뒤, 마지막에 테스트를 실행해 red를 확인합니다.

Prompt 템플릿

테스트 파일만 수정하세요. 구현 코드는 수정하지 마세요.
[기능명]에 대해 다음 테스트 시나리오를 작성하세요.
1. [최소 동작]
2. [경계 조건]
3. [오류 경로]
완료 후 `npm test`를 실행하고 실패한 테스트 이름과 assertion을 붙여 주세요.

이 prompt에는 반드시 “구현 코드는 수정하지 마세요”가 들어가야 합니다. 이 문장이 없으면 Codex가 테스트를 쓰는 동시에 구현도 함께 작성해 red 검증을 깨뜨릴 수 있습니다.

Red 확인하기

Codex가 완료하면 전체 테스트 출력을 요구합니다. red는 기대한 assertion에서 실패해야 합니다. 컴파일 오류나 import 실패로 떨어지는 것은 좋은 red가 아닙니다. 다음처럼 보인다면:

FAIL src/utils/calculator.test.ts > add > should handle negative numbers
AssertionError: expected -1 to be 42

음수 입력 동작을 검증하고 있고, 기대한 위치에서 실패했다고 볼 수 있습니다. TypeError: Cannot find module 'calculator'만 보인다면 그것은 컴파일 오류이지 테스트 실패가 아닙니다.

테스트 목록 정렬하기

Codex에게 한 번에 수십 개 테스트를 만들게 하지 마세요. 최소 동작, 경계 조건, 회귀 bug, 오류 경로부터 시작해 하나씩 진행합니다. 테스트 목록은 AGENTS.md나 별도 문서에 두고 Codex가 순서대로 처리하게 할 수 있습니다.

단위 테스트 기본기는 Vitest 단위 테스트와 TDDVitest 실전 가이드를 참고하세요.

Green Phase: 최소 구현으로 모두 그린까지 고치기

Green phase의 강한 guardrail은 테스트 파일을 수정하지 않는 것입니다. 테스트가 실패하면 Codex는 구현만 고칠 수 있습니다. 구현에 맞추려고 테스트를 고치면 안 됩니다.

Prompt 템플릿

구현 파일만 수정하세요. 테스트 파일은 수정하지 마세요.
테스트를 통과시키기 위한 최소 변경만 하세요.
완료 후 `npm test`를 실행하고 통과 요약을 붙여 주세요.

이 prompt에는 반드시 “테스트 파일은 수정하지 마세요”가 들어가야 합니다. 이 문장이 없으면 Codex가 assertion을 바꾸거나, 테스트를 삭제하거나, case를 skip해 겉보기 그린을 만들 수 있습니다.

통과 요약과 diff 확인하기

Codex가 완료하면 테스트 수, 통과 수, 소요 시간을 요구합니다.

PASS src/utils/calculator.test.ts (1.2s)
  add
    ✓ should add two numbers (5ms)
    ✓ should handle negative numbers (3ms)
  2 tests passed

그다음 diff를 확인합니다. 변경 파일 목록에 테스트 파일이 나타나면 그 green phase는 거절합니다.

가짜 그린 방지 guardrail

가짜 그린은 TDD의 가장 큰 위험입니다. 다음 6가지 신호를 반드시 경계해야 합니다.

가짜 그린 신호막는 방법
테스트 파일이 수정됨diff를 확인하고 테스트 파일을 포함한 green phase는 거절
assertion이 toEqual(42)에서 toBeTruthy()로 변경됨Codex에게 전체 assertion을 붙이게 하고 사람이 비교
test.skip() / test.only()가 새로 추가됨테스트 파일을 grep해 신규 skip/only를 금지
matcher가 느슨해짐 (toBetoBeTruthy)테스트 diff를 비교하고 matcher 완화를 금지
fixture가 “정답”으로 바뀜fixture diff를 확인하고 입력 데이터 변경을 금지
단위 테스트만 실행하고 관련 통합 테스트를 실행하지 않음Done when에 관련 테스트 명령을 모두 포함

이 guardrail은 Codex가 자동으로 지켜 주지 않습니다. review할 때 사람이 직접 확인해야 합니다. 자동화가 잘 된 팀이라면 일부를 CI나 pre-commit hook으로 옮길 수 있습니다.

Refactor Phase: 모두 그린 이후에만 정리하기

Refactor phase는 그린 이후에만 시작합니다. 테스트가 통과하지 않으면 리팩터링하지 않습니다. 리팩터링 후에는 반드시 테스트를 다시 실행해야 합니다. diff만 보고 판단하면 안 됩니다.

Prompt 템플릿

모든 테스트가 통과했습니다. 이제 리팩터링만 하세요.
- 의도가 더 분명하도록 변수 이름 변경
- 중복 코드 제거
- 함수 추출
테스트 파일은 수정하지 마세요. 완료 후 `npm test`를 다시 실행하세요.

이 단계에서 Codex가 바꾸는 것은 구조이지 동작이 아닙니다. 리팩터링 중 새 로직이 들어가면 더 이상 refactor가 아니라 새 동작에 대한 green phase입니다.

테스트를 다시 실행해 검증하기

리팩터링 후 Codex는 같은 테스트 세트를 다시 실행해야 합니다. 테스트가 계속 그린이면 기존 동작을 깨뜨리지 않았다고 볼 수 있습니다. 실패하면 리팩터링이 동작을 바꾼 것이므로 되돌리거나 수정해야 합니다.

diff도 확인합니다. 변경 파일 목록에 테스트 파일이 나타나면 안 됩니다. 나타난다면 refactor의 경계를 넘은 것입니다.

리팩터링 사례는 AI 리팩터링과 테스트 안전망을 참고하세요.

증거 패키지: Codex가 review 가능한 증거를 내놓게 하기

각 단계가 끝나기 전에 Codex는 다음 증거를 내놓아야 합니다.

완료 전 인수 체크리스트

  • 테스트 명령과 exit code
  • 실패/통과 요약
  • 수정 파일 목록
  • 테스트 파일 수정 여부
  • CI status checks(있는 경우)

완료 답변 템플릿

Codex가 다음 형식으로 답하게 하세요.

### 테스트 결과
- 명령: `npm test`
- Exit code: 0
- 통과: 42개 테스트
- 실패: 0
- 소요 시간: 1.2s

### 변경 파일
- src/utils/calculator.ts
- (테스트 파일은 수정하지 않았습니다)

### 남은 리스크
- 미커버 경계 조건: 음수 입력

이 형식은 증거를 읽기 쉽고, 비교하기 쉽고, 보관하기 쉽게 만듭니다. Codex가 “테스트가 통과했습니다”라고만 답하면 정말 테스트를 실행했는지, 테스트 파일을 바꿨는지, 경계 조건을 놓쳤는지 알 수 없습니다.

테스트 계층 선택과 명령 예시

검증하려는 내용에 따라 알맞은 테스트 계층이 달라집니다. 처음부터 전체 E2E로 가지 않아도 됩니다. snapshot test만 믿는 것도 위험합니다.

테스트 계층 판단표

테스트 계층언제 사용하나명령 예시Codex가 내야 할 증거
단위 테스트단일 함수를 빠르게 검증npm run test:unit 또는 vitest run 또는 pytest실패한 테스트 이름, assertion
통합 테스트모듈 간 상호작용 검증npm run test:integration실패한 모듈, 인터페이스
E2E 테스트사용자 흐름 검증npx playwright test실패 시나리오, 스크린샷
타입 체크컴파일 시점 오류 검출npm run typecheck 또는 tsc --noEmit오류 파일, 행 번호
Lint코드 규칙 확인npm run lint 또는 eslint오류 파일, 규칙 이름
CI팀 gateGitHub ActionsStatus checks 페이지

명령 예시는 각 프로젝트의 명령으로 바꿔야 합니다. 프로젝트마다 Jest, Vitest, Pytest, Playwright, GitHub Actions가 다릅니다. 테스트 프레임워크 배경은 Next.js Jest 테스트 가이드를 참고하세요.

단위 테스트는 Codex의 첫 검증 계층으로 적합합니다. 빠르게 실행되고, 실패 정보가 명확하며, AGENTS.md에 테스트 명령을 적기 쉽기 때문입니다. 통합 테스트와 E2E 테스트는 여러 모듈의 상호작용과 사용자 흐름을 검증하는 데 적합하지만, 실패 원인 파악이 더 어렵습니다. 타입 체크와 Lint는 컴파일 오류와 코드 규칙 문제를 잡는 보조 검증입니다. CI는 팀의 마지막 gate입니다. 로컬 테스트가 모두 그린이어도 merge 전에는 CI status checks 통과를 기다려야 합니다.

AGENTS.md와 prompt에 규칙 고정하기

TDD 규칙을 AGENTS.md에 쓰면 Codex가 매번 작업 전에 읽습니다. 같은 prompt를 매번 반복할 필요가 줄어듭니다.

AGENTS.md 작은 예시

프로젝트 루트나 국소 디렉터리에 다음과 같은 내용을 둡니다.

## Test commands
- Run tests: `npm test`
- Run unit tests: `npm run test:unit`
- Run typecheck: `npm run typecheck`

## TDD rules
- Red phase: only modify test files
- Green phase: only modify implementation files
- Refactor phase: must re-run tests after cleanup

## Done when
- All tests pass
- Test files are not modified in green/refactor phase
- Diff contains only expected changes

이 파일은 Codex에게 프로젝트의 테스트 명령, 각 단계에서 수정 가능한 범위, 완료 조건을 알려 줍니다. AGENTS.md 작성법은 이 시리즈의 Codex 프로젝트 규칙 글에서 다룹니다.

국소 디렉터리에는 더 구체적인 규칙을 둘 수 있습니다. 예를 들어 src/utils/AGENTS.md에는 src/utils/의 테스트 시나리오, 경계 조건, 알려진 bug를 적을 수 있습니다.

AGENTS.md에 secret, token, 민감한 설정을 쓰지 마세요. Codex는 이 파일을 읽지만 민감 정보를 자동으로 필터링하지 않습니다.

로컬에서 팀으로: CI gate

로컬 테스트가 모두 그린이라고 해서 merge할 수 있는 것은 아닙니다. 팀에는 CI gate가 있습니다. required status checks가 통과해야 하고 PR review도 완료되어야 합니다.

흐름 체크리스트

  1. 로컬 테스트 모두 그린: Codex가 세 단계를 완료하고 증거 패키지를 제출
  2. review pane diff 확인: 테스트 파일이 수정되었는지 확인
  3. PR 생성: feature branch로 push
  4. required status checks 통과 필요: CI가 전체 테스트, typecheck, lint 실행
  5. 사람 review: diff, 증거 패키지, 남은 리스크 확인

테스트 모두 그린은 자동 merge가 아니다

GitHub Docs는 protected branch에 merge하려면 required status checks가 통과해야 한다고 설명합니다. 다만 위험이 하나 있습니다. skipped job은 success로 보고되며, required check여도 PR merge를 막지 않습니다. 즉 일부 check가 실제로 실행되지 않아도 PR이 merge 가능한 것처럼 보일 수 있습니다.

workflow가 skip되어 생기는 가짜 그린을 막으려면 GitHub Checks 페이지를 열고 모든 required checks가 실제로 실행되었는지, skipped가 아닌지 확인해야 합니다.

review pane 사용하기

Codex app의 review pane에서는 diff에 테스트 파일 변경이 포함되었는지 확인할 수 있습니다. file 또는 hunk 단위로 stage, unstage, revert할 수 있습니다. 테스트 파일이 바뀌었다면 그 변경을 revert하고 구현 파일 변경만 남깁니다.

CI 배경은 GitHub Actions CIGitHub Actions Workflow 기초를 참고하세요. PR review 흐름은 이 시리즈의 Codex AI 코드 리뷰 글에서 다룹니다.

CI 실패 자동 수정의 경계

codex exec와 Codex GitHub Action은 CI 실패 테스트를 처리할 수 있습니다. 하지만 안전 경계가 필요합니다.

실패 테스트 자동 수정 흐름

Codex non-interactive 문서에 따르면 CI 실패 자동 수정 흐름은 다음과 같습니다.

  1. 먼저 테스트를 실행해 실패를 재현
  2. Codex에게 최소 수정 수행 요청
  3. patch artifact 생성
  4. 수정 코드를 분리한 PR 생성

npm test 2>&1 | codex exec "실패 원인을 요약하고 최소 수정안을 제안해 줘"를 사용하면 테스트 출력을 Codex에 전달해 실패 요약과 최소 수정안을 받을 수 있습니다.

안전 원칙

Codex에게 같은 job에서 저장소 쓰기 권한과 secret 접근 권한을 동시에 주지 마세요. 최소 권한 sandbox를 사용합니다. 기본은 read-only, 쓰기가 필요하면 작업 디렉터리에 한정한 workspace-write, danger-full-access는 통제된 환경에서만 사용합니다.

patch artifact 생성과 PR 생성을 분리해 신뢰할 수 없는 코드에 API key가 노출되지 않게 합니다.

여기서는 완전한 GitHub Actions YAML까지 확장하지 않습니다. 원칙은 최소 권한, patch 분리, 사람의 merge입니다. 자동화 내용은 이 시리즈의 codex exec 자동화 글에서 다룹니다.

정리

Codex 테스트 주도 개발의 핵심은 세 단계 작업 템플릿입니다. Red는 테스트만 쓰고, Green은 구현만 고치며, Refactor는 정리한 뒤 테스트를 다시 실행합니다. 각 단계는 review 가능한 증거를 내야 합니다. 명령, 실패/통과 요약, 수정 파일 목록입니다.

가짜 그린 방지 guardrail이 이 글의 핵심입니다. 테스트 파일이 수정되었는지, assertion이 느슨해졌는지, test가 skip되었는지, fixture가 바뀌었는지, 통합 테스트가 필요한데 단위 테스트만 돌렸는지, CI workflow가 skip되었는지 확인해야 합니다.

팀에서 사용할 때는 로컬 그린만으로 충분하지 않습니다. CI status checks, PR review, protected branch 조건을 만족해야 merge할 수 있습니다.

다음에 Codex에게 코드 수정을 맡길 때는 먼저 테스트를 쓰게 하고, red를 확인한 다음 증거를 보세요. “테스트가 통과했습니다”라는 말에서 멈추지 마세요.

Codex로 TDD 한 사이클 돌리기

작업을 red, green, refactor 세 라운드로 나누고, Codex가 먼저 실패하는 테스트를 쓰게 한 뒤 최소 구현으로 통과시키고 테스트 출력, diff, CI로 검증합니다.

  1. 1

    Step 1: 테스트 목록 작성하기

    Codex에게 최소 동작, 경계 조건, 회귀 시나리오를 나열하게 합니다. 구현 코드는 쓰지 않게 합니다.
  2. 2

    Step 2: 실패하는 테스트 작성하기

    Codex가 테스트 파일만 수정하게 하고, 관련 테스트를 실행해 red 상태를 확인합니다.
  3. 3

    Step 3: 최소 구현하기

    Codex가 테스트 assertion을 바꾸지 않고 구현 코드만 수정한 뒤 같은 테스트를 다시 실행하게 합니다.
  4. 4

    Step 4: 그린 이후 리팩터링하기

    테스트가 통과한 뒤에만 구조를 정리하고, 다시 테스트를 실행합니다.
  5. 5

    Step 5: 증거와 diff 확인하기

    명령 출력, 테스트 파일 변경, review pane 또는 PR diff, CI status checks를 확인합니다.

FAQ

Codex가 단위 테스트를 작성할 수 있나요?
가능합니다. Codex는 테스트 코드를 생성할 수 있지만, red 테스트가 기대한 assertion에서 실패하는지, 경계 조건을 커버하는지는 사람이 확인해야 합니다.
Codex가 실제로 테스트를 실행하게 하려면 어떻게 해야 하나요?
Done when에 테스트 명령과 출력 요구를 명확히 적습니다. `npm test 2>&1 | codex exec "실패 원인을 요약하고 최소 수정안을 제안해 줘"`처럼 테스트 출력을 Codex에 전달할 수도 있습니다.
테스트가 실패했을 때 Codex가 테스트를 고쳐도 되나요?
Green phase에서는 안 됩니다. 테스트가 실패하면 먼저 Codex에게 실패 원인을 요약하게 하고, 그다음 최소 구현 수정을 하게 합니다.
커버리지 숫자를 품질 기준으로 봐도 되나요?
커버리지는 신호일 뿐 기준은 아닙니다. 커버리지가 높아도 assertion이 잘못된 동작을 검증하고 있을 수 있습니다.
프론트엔드 화면은 어떻게 검증하나요?
Playwright나 browser test를 사용합니다. Codex가 `npx playwright test`를 실행하고 실패 시나리오와 스크린샷을 증거로 붙일 수 있습니다.
언제 Codex를 TDD로 움직이는 것이 적합하지 않나요?
탐색형 프로토타입, 일회성 스크립트, 안정적인 테스트 프레임워크가 없는 프로젝트는 엄격한 TDD에 맞지 않습니다. 이런 경우에는 먼저 코드를 수정하고 나중에 테스트를 보강하는 편이 낫습니다.

3분 읽기 · 게시일: 2026년 7월 30일 · 수정일: 2026년 7월 30일

댓글

GitHub로 로그인하여 댓글을 남기세요

Easton BlogEaston Blog