
최근 AI 코딩 도구를 쓰는 방식이 꽤 달라졌다.
예전에는 개발자가 코드를 작성하다가 막히는 부분을 AI에게 물었다.
이 함수가 왜 느린지 설명해줘.
이 ViewModel을 리팩토링해줘.
이 테스트가 실패하는 원인을 찾아줘.
AI는 옆자리 조수에 가까웠다.
하지만 지금의 코딩 에이전트는 파일을 직접 찾고, 코드를 수정하고, 터미널에서 테스트를 실행한다. 작업 범위가 명확하면 기능 하나를 통째로 맡길 수도 있다.
이 이슈를 읽어라.
관련 파일을 찾아라.
수정 계획을 세워라.
코드를 고쳐라.
테스트를 추가하라.
실패 원인을 분석하라.
변경 내용을 보고하라.
이제 AI는 답변기가 아니라 작업자가 됐다.
여기서 많은 팀이 바로 다음 단계로 넘어간다.
가장 성능 좋은 AI 하나를 고른다.
그 AI에게 계획부터 구현, 리뷰, 테스트까지 전부 맡긴다.
처음에는 빠르다.
AI 하나가 요구사항을 읽고, 관련 코드를 찾고, 수정하고, 테스트까지 해준다. 개발자는 마지막 결과만 확인하면 될 것처럼 보인다.
문제는 그다음부터다.
구현한 AI가 자기 코드를 다시 리뷰한다.
처음 세운 방향이 틀려도 같은 판단을 반복한다.
작은 버그를 고치다 관련 없는 공통 모듈까지 수정한다.
테스트를 실행하지 못했는데도 작업이 끝났다고 판단한다.
그래서 최근 AI 개발 환경에서 중요한 것은 가장 강한 모델 하나가 아니다.
계획하는 AI, 구현하는 AI, 검토하는 AI의 책임을 분리하는 구조다.
이 글에서는 Claude Code, Codex, Cursor를 단순히 번갈아 쓰는 것이 아니라 하나의 AI 개발팀처럼 구성하는 방법을 다룬다.
핵심은 제품 비교가 아니다.
누가 계획할 것인가
누가 구현할 것인가
누가 독립적으로 검토할 것인가
누가 최종 결과를 확인할 것인가
각 역할이 어디까지 접근할 수 있는가
이 질문에 답할 수 있어야 AI 코딩 에이전트를 팀 단위로 운영할 수 있다.
코드 자동완성과 코딩 에이전트는 비슷해 보이지만 일하는 단위가 다르다.
자동완성은 현재 열려 있는 파일과 커서 주변을 본다.
개발자가 코드를 작성한다.
AI가 다음 코드 몇 줄을 제안한다.
개발자가 선택한다.
에이전트는 목표를 받는다.
목표를 분석한다.
저장소를 탐색한다.
관련 파일을 선택한다.
코드를 수정한다.
명령을 실행한다.
결과를 확인한다.
필요하면 다시 수정한다.
예를 들어 iOS 앱에서 결제 버튼을 빠르게 여러 번 탭했을 때 주문 생성 요청이 중복 실행되는 문제가 있다고 해보자.
자동완성 도구는 submitOrder() 안에 다음 코드를 제안할 수 있다.
guard !isSubmitting else { return }
코딩 에이전트는 더 넓게 움직인다.
CheckoutView 확인
CheckoutViewModel 확인
CreateOrderUseCase 확인
PaymentRepository 확인
관련 테스트 확인
중복 진입 원인 분석
ViewModel 수정
테스트 추가
빌드 또는 테스트 실행
변경 결과 정리
에이전트가 더 강력한 이유다.
동시에 에이전트가 더 위험한 이유이기도 하다.
자동완성은 개발자가 승인한 코드만 들어간다.
에이전트는 작업 범위가 넓고, 여러 파일을 실제로 수정할 수 있다.
따라서 최근 AI 경쟁에서 개발자가 봐야 할 핵심은 단순한 코드 생성 능력이 아니다.
얼마나 긴 작업을 수행할 수 있는가
얼마나 정확하게 필요한 파일을 찾는가
도구 호출을 얼마나 안정적으로 이어가는가
실패했을 때 작업 상태를 얼마나 잘 유지하는가
허용된 범위 안에서 멈출 수 있는가
AI가 작업자가 됐다면, 이제 AI에게도 조직과 권한이 필요하다.
개발자 한 명이 요구사항을 정리하고, 코드를 구현하고, 자기 코드를 리뷰하고, QA까지 전부 담당한다고 생각해보자.
불가능한 일은 아니다.
하지만 빠뜨리는 부분이 생기기 쉽다.
AI도 마찬가지다.
하나의 에이전트에게 다음 작업을 전부 맡기면 책임 경계가 사라진다.
요구사항 분석
설계
파일 선택
코드 수정
테스트 작성
테스트 실행
코드 리뷰
최종 승인
가장 먼저 생기는 문제는 자기 승인이다.
AI가 처음에 “이 문제는 Repository에서 해결해야 한다”고 판단했다고 해보자.
이후 구현 중 문제가 생겨도 처음 판단을 유지하려는 흐름이 생길 수 있다.
초기 판단:
Repository에서 중복 요청을 막자.
구현:
PaymentRepository에 상태 추가.
문제 발생:
Repository가 UI 제출 상태까지 갖게 됨.
자기 리뷰:
중복 요청이 방지되므로 적절한 구현이라고 판단.
독립적인 Reviewer가 있었다면 이렇게 볼 수 있다.
중복 탭은 화면 상태 문제다.
Repository가 isSubmitting과 같은 UI 상태를 소유하면 안 된다.
CheckoutViewModel에서 먼저 막아야 한다.
구현자와 검토자의 관점은 달라야 한다.
결제 버튼 중복 탭 문제를 한 AI에게 통째로 맡겼다고 해보자.
처음에는 CheckoutViewModel의 문제였다.
하지만 AI가 코드를 읽다 보면 더 개선할 부분이 계속 보인다.
CheckoutView 버튼 상태 개선
CheckoutViewModel 리팩토링
CreateOrderUseCase 인터페이스 변경
PaymentRepository 상태 추가
NetworkClient 재시도 정책 수정
DesignSystem 버튼 컴포넌트 개선
Analytics 이벤트 추가
AI 입장에서는 모두 논리적인 개선일 수 있다.
하지만 원래 작업은 이것이었다.
결제 버튼을 여러 번 눌러도
주문 생성 요청이 한 번만 실행되게 한다.
작은 버그를 고치기 위해 일곱 개 영역이 바뀌면 리뷰 비용이 커진다. 회귀 위험도 높아진다.
에이전트가 강할수록 “더 많이 할 수 있는 능력”보다 “여기까지만 하는 능력”이 중요해진다.
AI는 코드를 읽고 논리적으로 테스트 통과 여부를 예상할 수 있다.
하지만 예상과 실행은 다르다.
테스트가 통과할 것으로 보입니다.
컴파일 오류는 없을 것으로 예상됩니다.
현재 변경은 기존 동작에 영향을 주지 않습니다.
실무에서 이 문장은 증거가 아니다.
필요한 것은 다음이다.
실행한 명령
종료 코드
통과한 테스트
실패한 테스트
실행하지 못한 이유
한 에이전트가 구현과 완료 판정을 함께 담당하면, 실제 실행보다 자신의 코드 해석을 신뢰할 수 있다.
그래서 Tester 역할을 분리해야 한다.
하나의 세션에서 계획, 구현, 실패 수정, 리뷰를 전부 이어가면 컨텍스트에 많은 정보가 쌓인다.
초기 요구사항
첫 번째 계획
읽은 파일
첫 번째 구현
컴파일 오류
수정 시도
테스트 실패
두 번째 수정
자기 리뷰
시간이 지날수록 현재 기준이 무엇인지 흐려질 수 있다.
이미 폐기한 접근이 계속 컨텍스트에 남고, 오래된 오류 로그가 최신 상태와 섞인다.
역할을 나누면 각 에이전트에게 필요한 정보만 전달할 수 있다.
Planner:
요구사항과 프로젝트 규칙
Implementer:
승인된 계획과 수정 가능 파일
Reviewer:
계획과 실제 diff
Tester:
테스트 명령과 현재 변경분
전체 대화를 공유하는 것이 아니라 역할에 맞는 작업 패킷을 전달하는 것이다.
AI 개발팀은 AI를 여러 개 실행한다고 만들어지는 것이 아니다.
역할과 권한이 나뉘어야 한다.
기본 구조는 다음과 같다.
Developer
│
▼
Orchestrator
│
├── Planner
├── Context Builder
├── Implementer
├── Reviewer
├── Security Reviewer
├── Tester
└── Final Judge
여기서 Orchestrator는 더 똑똑한 AI 모델이 아니다.
작업을 전달하고 상태를 관리하는 실행 구조다.
어떤 역할을 먼저 실행할지 결정
각 역할에 필요한 컨텍스트 전달
수정 권한 확인
산출물 저장
다음 역할로 결과 전달
중단 조건 확인
최종 결과 보고
1편에서는 자동 호출 코드보다 먼저 조직도를 설계한다.
자동화보다 역할과 계약이 먼저다.
Planner의 역할은 구현이 아니다.
문제를 작은 작업으로 바꾸는 것이다.
Planner가 받아야 하는 정보는 다음 정도다.
현재 문제
기대 동작
프로젝트 아키텍처
관련 파일 후보
보호된 영역
테스트 정책
Planner는 이를 바탕으로 다음을 결정한다.
어떤 파일을 먼저 읽을지
어떤 파일을 수정할 수 있는지
어떤 파일은 수정하면 안 되는지
작업 위험도가 어느 정도인지
필요한 테스트가 무엇인지
어떤 조건에서 멈춰야 하는지
예를 들어 결제 중복 요청 문제라면 Planner의 출력은 이런 형태가 된다.
{
"summary": "CheckoutViewModel의 중복 제출 상태를 제어한다.",
"risk": "medium",
"files_to_read": [
"CheckoutView.swift",
"CheckoutViewModel.swift",
"CreateOrderUseCase.swift",
"CheckoutViewModelTests.swift"
],
"files_to_edit": [
"CheckoutViewModel.swift",
"CheckoutViewModelTests.swift"
],
"files_not_to_edit": [
"PaymentAPI.swift",
"PaymentRepository.swift",
"NetworkClient.swift",
"DesignSystem/**"
],
"steps": [
"현재 제출 상태 관리 방식을 확인한다.",
"중복 진입 방지 조건을 추가한다.",
"실패 후 재시도 가능한 상태로 복구한다.",
"중복 호출 방지 테스트를 추가한다."
],
"tests": [
"여러 번 호출해도 주문 생성은 한 번만 실행된다.",
"실패 후 다시 제출할 수 있다."
],
"human_approval_required": false
}
Planner에게 코드 수정 권한을 주지 않는 이유는 분명하다.
계획 단계에서 구현까지 시작하면 아직 승인되지 않은 판단이 코드에 반영된다.
Planner가 할 일:
계획 작성
Planner가 하면 안 되는 일:
소스 코드 수정
의존성 추가
작업 범위 확대
최근 모델들은 긴 컨텍스트를 지원한다.
그래서 저장소 전체를 넣으면 더 잘할 것 같지만 실제로는 항상 그렇지 않다.
작은 작업에 다음 정보를 전부 넣는다고 생각해보자.
전체 Sources 디렉터리
전체 Tests 디렉터리
3년치 Git history
모든 아키텍처 문서
전체 빌드 로그
이전 실패 시도
모델이 읽을 수는 있다.
하지만 중요한 것과 덜 중요한 것을 구분하는 비용이 커진다.
Context Builder는 작업별로 필요한 정보만 모은다.
결제 중복 요청 문제라면 다음 정도로 시작한다.
CheckoutView.swift
CheckoutViewModel.swift
CreateOrderUseCase.swift
CheckoutViewModelTests.swift
ios-architecture.md
coding-rules.md
test-commands.md
Context Builder가 지켜야 할 기준은 세 가지다.
CheckoutView.swift는 UI에서 버튼이 어떻게 호출되는지 이해하기 위해 읽을 수 있다.
하지만 실제 수정은 CheckoutViewModel.swift 안에서 끝낼 수 있다.
Read First:
문제를 이해하기 위해 읽는 파일
Allowed to Edit:
실제로 수정할 수 있는 파일
Do Not Edit:
현재 작업 범위 밖이거나 위험한 파일
읽을 수 있다는 이유로 수정해도 되는 것은 아니다.
프로젝트 전체에서 항상 적용되는 규칙은 공통 문서에 둔다.
.ai/context/architecture.md
.ai/context/coding-rules.md
.ai/context/security-rules.md
.ai/context/test-commands.md
이번 작업에만 필요한 정보는 작업 폴더에 둔다.
.ai/tasks/checkout-duplicate-submit/task.md
.ai/tasks/checkout-duplicate-submit/files.md
.ai/tasks/checkout-duplicate-submit/tests.md
.ai/tasks/checkout-duplicate-submit/done.md
이 구분이 중요한 이유는 공통 문서가 작업마다 복사되는 것을 막기 위해서다.
Context Builder는 현재 작업에 필요한 최신 정보만 남겨야 한다.
유지:
현재 task
승인된 plan
현재 diff
최신 테스트 실패
요약:
이전 수정 시도
긴 빌드 로그
제외:
관련 없는 파일
오래된 성공 로그
폐기된 제안
반복된 설명
컨텍스트는 많이 넣는 것보다 잘 정리하는 것이 중요하다.
Implementer는 실제 코드를 작성한다.
하지만 설계를 새로 결정하는 역할은 아니다.
입력은 명확해야 한다.
승인된 plan.json
task.md
files.md
tests.md
프로젝트 코딩 규칙
Implementer의 작업 원칙은 다음과 같다.
files_to_edit에 포함된 파일만 수정한다.
작은 diff를 유지한다.
계획을 임의로 바꾸지 않는다.
새 의존성을 추가하지 않는다.
테스트를 실행하지 않았다면 통과했다고 말하지 않는다.
수정 중 계획이 잘못됐다는 사실을 발견할 수도 있다.
예를 들어 CheckoutViewModel만 수정하려 했지만 실제 중복 요청이 CreateOrderUseCase 내부에서 발생한다고 해보자.
이때 Implementer가 혼자 범위를 넓히면 안 된다.
잘못된 방식:
UseCase까지 알아서 수정하고 완료
권장 방식:
현재 plan으로 해결할 수 없다고 보고
필요한 파일과 이유를 반환
Implementer의 출력은 다음처럼 구조화한다.
{
"status": "completed",
"summary": "중복 제출 방지와 실패 후 상태 복구를 추가했습니다.",
"files_changed": [
"CheckoutViewModel.swift",
"CheckoutViewModelTests.swift"
],
"commands_run": [
"xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16'"
],
"tests_passed": [
"CheckoutViewModelTests"
],
"tests_failed": [],
"blocked_reasons": [],
"remaining_risks": [
"서버 측 idempotency는 이번 작업 범위에 포함되지 않았습니다."
],
"not_changed": [
"PaymentAPI",
"PaymentRepository",
"NetworkClient",
"DesignSystem"
]
}
not_changed가 중요하다.
AI가 무엇을 했는지만 보고하는 것이 아니라, 작업 경계를 지켰다는 증거를 남긴다.
Reviewer는 코드를 직접 수정하지 않는다.
구현자의 결과를 검토하고 문제를 분류한다.
Reviewer가 받아야 하는 정보는 다음이다.
task.md
plan.json
diff.patch
tests.md
아키텍처 규칙
보호 파일 정책
구현자의 긴 대화나 중간 판단은 주지 않는 편이 좋다.
Reviewer는 변명이 아니라 실제 변경분을 봐야 한다.
확인할 항목은 명확하다.
요구사항을 만족했는가
허용된 파일만 수정했는가
작업 범위를 넓히지 않았는가
아키텍처 규칙을 지켰는가
실패 상태를 처리했는가
테스트가 추가됐는가
테스트가 실제로 실행됐는가
민감 정보 로그가 생기지 않았는가
새 의존성이 추가되지 않았는가
출력은 이런 형식이 좋다.
{
"verdict": "needs_changes",
"confidence": 0.91,
"issues": [
{
"severity": "medium",
"file": "CheckoutViewModel.swift",
"category": "bug",
"description": "성공 경로에서 isSubmitting이 true로 유지됩니다.",
"required_fix": "화면 전환 실패 가능성을 고려해 상태 종료 시점을 명확히 해야 합니다."
}
],
"missing_tests": [
"주문 성공 이후 추가 호출이 차단되는지 확인하는 테스트"
],
"policy_violations": [],
"human_review_focus": [
"주문 완료 화면 전환과 제출 상태의 생명주기"
]
}
Reviewer에게 수정 권한을 주지 않는 이유는 책임을 분명하게 하기 위해서다.
Reviewer가 직접 코드를 고치기 시작하면 Implementer와 경계가 사라진다.
Reviewer:
문제를 발견하고 근거를 제시
Implementer:
승인된 리뷰 항목을 수정
모든 작업에 별도 Security Reviewer가 필요한 것은 아니다.
하지만 인증, 결제, 개인정보, 로그, 권한과 가까운 작업에는 독립적인 보안 검토가 필요하다.
Security Reviewer는 다음 항목만 집중해서 본다.
민감 정보가 로그에 들어갔는가
토큰이나 인증 정보 접근이 추가됐는가
보호 파일이 수정됐는가
결제 결과를 클라이언트가 임의로 확정하는가
서버 에러 원문이 사용자에게 노출되는가
권한 검증이 약해졌는가
예를 들어 이런 코드가 추가됐다면 즉시 차단해야 한다.
print("payment token: \(paymentToken)")
보안 리뷰 결과도 구조화한다.
{
"verdict": "blocked",
"findings": [
{
"severity": "critical",
"category": "sensitive_log",
"file": "CheckoutViewModel.swift",
"description": "결제 토큰이 콘솔 로그에 출력됩니다.",
"required_fix": "해당 로그를 제거하고 민감 정보가 포함되지 않은 이벤트명만 기록합니다."
}
],
"human_approval_required": true
}
Security Reviewer 역시 코드를 직접 수정하지 않는다.
Tester의 역할은 단순히 테스트 코드를 생성하는 것이 아니다.
현재 변경이 실제로 검증됐는지 증명하는 것이다.
Tester에게는 허용된 명령만 제공한다.
swift test
xcodebuild test
swiftlint
swiftformat --lint
git diff
실행하지 못한 테스트가 있다면 이유를 기록해야 한다.
{
"status": "partial",
"commands_run": [
"xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16'"
],
"passed": [
"CheckoutViewModelTests"
],
"failed": [],
"not_run": [
{
"suite": "CheckoutUITests",
"reason": "테스트 계정과 결제 샌드박스 환경이 현재 실행 환경에 없습니다."
}
],
"evidence": [
"test-results/CheckoutViewModelTests.xcresult"
]
}
Tester가 설계를 바꾸거나 기능 코드를 수정하면 안 된다.
실패하면 현재 실패를 압축해서 다음 단계에 넘긴다.
실패한 테스트
핵심 오류
기대 동작
관련 파일
실행 명령
Final Judge는 Planner, Reviewer, Security Reviewer, Tester의 결과를 모은다.
하지만 모든 판단을 자동으로 확정하는 역할은 아니다.
최종적으로 사람이 어디를 확인해야 하는지 정리한다.
계획대로 구현됐는가
Reviewer의 이슈가 해결됐는가
테스트가 실제로 실행됐는가
남아 있는 위험이 무엇인가
사람 승인이 필요한가
Cursor는 이 단계의 Control Room으로 활용하기 좋다.
개발자는 에디터 안에서 다음을 함께 확인할 수 있다.
현재 diff
plan.json
review.json
test-result.json
남아 있는 위험
최종 출력은 짧아야 한다.
## Verdict
WAITING_FOR_HUMAN
## Summary
결제 버튼 중복 탭으로 주문 생성 요청이 여러 번 실행되는 문제를 ViewModel 상태 제어로 수정했습니다.
## Files Changed
- CheckoutViewModel.swift
- CheckoutViewModelTests.swift
## Validation
- ViewModel unit tests passed
- UI tests not run
## Remaining Risk
서버 측 주문 idempotency는 이번 작업에 포함되지 않았습니다.
## Human Inspection
- 주문 성공 후 화면 전환 시점
- 실패 후 재시도 UX
- 서버 중복 방지 후속 이슈 필요 여부
AI 오케스트라의 마지막 단계는 자동 머지가 아니다.
사람이 빠르게 판단할 수 있는 결과를 만드는 것이다.
여기서 중요한 것은 특정 제품이 항상 특정 역할에 가장 좋다고 단정하지 않는 것이다.
모델과 도구는 계속 바뀐다.
다만 현재 워크플로를 설계할 때 다음처럼 시작할 수 있다.
Claude Code에는 다음 역할을 맡긴다.
긴 요구사항과 아키텍처 문서 비교
작업 범위 정리
반대 관점의 독립 리뷰
설계상의 누락 확인
리스크와 후속 이슈 정리
특히 구현 세션과 분리된 새 세션에서 Reviewer 역할을 맡기는 방식이 중요하다.
Codex에는 다음 역할을 맡긴다.
저장소 탐색
승인된 범위의 코드 수정
테스트 추가
허용된 명령 실행
리뷰에서 발견된 항목의 제한 수정
중요한 점은 “알아서 전체를 고쳐라”가 아니라 승인된 plan과 수정 파일 목록을 전달하는 것이다.
Cursor에는 다음 역할을 맡긴다.
현재 diff 시각적 확인
프로젝트 Rules 적용
plan과 실제 변경 비교
리뷰 결과와 테스트 결과 확인
개발자의 직접 수정
최종 승인 또는 거절
Cursor 자체를 최종 의사결정자로 두기보다는 개발자가 AI 결과를 검토하는 작업 공간으로 보는 편이 안전하다.
이 구조는 다음처럼 설계해야 한다.
roles:
planner:
provider: claude
implementer:
provider: codex
reviewer:
provider: claude
final_judge:
provider: cursor
핵심은 planner: claude가 아니다.
planner라는 역할이 먼저 존재한다는 점이다.
역할:
Planner
현재 공급자:
Claude
나중에 가능한 변경:
Codex
다른 모델
사내 모델
도구가 바뀌어도 조직 구조가 유지돼야 한다.
이제 프로젝트 안에 AI 개발팀의 작업 공간을 만든다.
.ai/
├── agents/
│ ├── planner.md
│ ├── context-builder.md
│ ├── implementer.md
│ ├── reviewer.md
│ ├── security-reviewer.md
│ ├── tester.md
│ └── final-judge.md
│
├── context/
│ ├── architecture.md
│ ├── coding-rules.md
│ ├── security-rules.md
│ └── test-commands.md
│
├── policies/
│ ├── protected-files.yaml
│ ├── tool-allowlist.yaml
│ └── stop-conditions.yaml
│
├── evals/
│ └── pr-review.checklist.md
│
└── tasks/
└── checkout-duplicate-submit/
├── task.md
├── files.md
├── tests.md
└── done.md
각 폴더의 역할은 명확하다.
각 역할이 할 수 있는 일과 할 수 없는 일을 정의한다.
planner.md
- 계획만 작성
- 코드 수정 금지
implementer.md
- 승인된 파일만 수정
reviewer.md
- diff 검토만 수행
- 코드 수정 금지
tester.md
- 허용된 테스트 명령만 실행
프로젝트 전체에서 재사용하는 공통 지식이다.
architecture.md
- View, ViewModel, UseCase, Repository 책임
coding-rules.md
- 코드 스타일과 아키텍처 규칙
security-rules.md
- 로그, 개인정보, 인증, 결제 규칙
test-commands.md
- 실행 가능한 테스트 명령
AI가 넘어가면 안 되는 경계다.
# protected-files.yaml
never_read:
- ".env"
- "**/*.p8"
- "**/Secrets.plist"
read_only:
- "Sources/Auth/**"
- "Sources/Payment/**"
- ".github/workflows/**"
requires_approval:
- "Package.swift"
- "Tuist/**"
- "Sources/Network/**"
- "Sources/Analytics/**"
# tool-allowlist.yaml
allowed_commands:
- "git status"
- "git diff"
- "swift test"
- "xcodebuild test"
- "swiftlint"
blocked_patterns:
- "rm -rf"
- "git push"
- "cat .env"
- "printenv"
- "curl * | sh"
- "deploy"
완료 여부를 판단하는 기준이다.
# PR Review Checklist
## Scope
- 허용된 파일만 수정했는가
- Not Goal 영역을 건드리지 않았는가
- 보호 파일을 수정하지 않았는가
## Behavior
- 기대 동작을 만족하는가
- 실패 상태가 처리됐는가
- 중복 실행이 차단됐는가
## Tests
- 필요한 테스트가 추가됐는가
- 테스트가 실제로 실행됐는가
- 실행하지 못한 이유가 기록됐는가
## Security
- 민감 정보가 로그에 추가되지 않았는가
- 인증·결제 영역이 승인 없이 수정되지 않았는가
작업별 Context Pack이다.
task.md
- 현재 문제와 기대 동작
files.md
- 읽을 파일, 수정할 파일, 금지 파일
tests.md
- 필요한 테스트
done.md
- 완료 기준과 최종 보고 형식
AGENTS.md에는 저장소 전체에서 항상 적용되는 핵심 규칙만 둔다.
# AGENTS.md
## Workflow
For non-trivial work:
1. Read the current task documents.
2. Produce a plan before editing.
3. Edit only approved files.
4. Run allowed tests.
5. Review the final diff.
6. Report risks and unchanged areas.
## Global Rules
- Keep diffs small.
- Do not modify unrelated files.
- Do not add dependencies without approval.
- Do not change production configuration.
- Do not expose sensitive data.
- Do not claim tests passed unless executed.
.ai/context/에는 더 구체적인 프로젝트 지식을 둔다.
AGENTS.md:
모든 에이전트가 따라야 할 짧은 전역 규칙
.ai/context/:
역할이나 작업에 따라 선택해서 읽는 상세 문서
AGENTS.md에 모든 내용을 밀어 넣으면 너무 길어진다.
전역 규칙은 짧게 유지하고, 구체적인 아키텍처와 보안 규칙은 별도 문서로 분리한다.
AI끼리 자유롭게 대화하게 만들면 운영하기 어렵다.
Claude:
이 방식이 맞습니다.
Codex:
다른 방식이 더 좋습니다.
Claude:
그 의견도 타당합니다.
Codex:
두 방식을 합쳐보겠습니다.
대화는 자연스럽지만 무엇이 최종 결정인지 불명확하다.
대신 각 역할은 파일을 산출한다.
Planner
→ plan.json
Implementer
→ diff.patch
→ implementation.json
Reviewer
→ review.json
Tester
→ test-result.json
Final Judge
→ final-report.md
이 방식의 장점은 분명하다.
결과를 기계적으로 검사할 수 있다.
실행 기록이 남는다.
다음 역할에 필요한 정보만 전달할 수 있다.
작업이 중단돼도 이어서 시작할 수 있다.
누가 어떤 판단을 했는지 추적할 수 있다.
AI 오케스트라는 단체 채팅방이 아니다.
계약된 산출물을 다음 역할에 전달하는 작업 파이프라인이다.
이 구조를 처음 도입할 때 Python Orchestrator부터 만들 필요는 없다.
먼저 수동으로 역할을 분리해도 충분하다.
1. Claude에게 task.md를 전달해 plan.json 작성
2. 개발자가 plan 확인
3. Codex에 승인된 plan 전달
4. Codex가 코드와 테스트 수정
5. git diff를 diff.patch로 저장
6. 새로운 Claude 세션에서 diff 리뷰
7. Cursor에서 결과와 테스트 확인
8. 개발자가 최종 승인
이 과정을 몇 번 반복하면 팀에 필요한 정책이 보인다.
AI가 자주 건드리는 불필요한 파일
자주 빠뜨리는 테스트
반드시 승인이 필요한 영역
에이전트가 멈춰야 하는 조건
그다음 자동화한다.
처음부터 오케스트라 코드를 만들면 잘못된 프로세스를 자동화할 수 있다.
AI 오케스트라가 필요한 작업과 그렇지 않은 작업을 구분해야 한다.
단순한 작업에는 하나의 에이전트로 충분하다.
문구 변경
포맷 수정
README 일부 수정
명확한 한 줄 버그
기존 테스트 이름 변경
AI 개발팀이 효과적인 작업은 다음과 같다.
여러 계층이 함께 바뀌는 기능
중간 이상의 위험도를 가진 작업
AI가 자주 변경 범위를 넓히는 작업
인증·결제·개인정보와 가까운 작업
독립적인 리뷰가 필요한 작업
테스트와 정책 검증이 중요한 작업
AI가 많다고 항상 결과가 좋아지는 것은 아니다.
역할 분리로 얻는 검증 효과가 실행 비용보다 클 때 사용해야 한다.
AI 개발팀을 만든다고 해서 Claude, Codex, Cursor를 동시에 실행하면 되는 것은 아니다.
세 도구에 같은 작업을 던지고 결과를 비교하는 것도 오케스트라라고 보기 어렵다.
진짜 필요한 것은 책임 분리다.
Planner는 계획만 세운다.
Context Builder는 필요한 맥락만 모은다.
Implementer는 승인된 범위만 수정한다.
Reviewer는 diff만 검토한다.
Security Reviewer는 위험만 본다.
Tester는 실행 결과를 증명한다.
Final Judge는 사람이 볼 결과를 정리한다.
각 역할은 자기 권한을 넘어가면 안 된다.
구현자가 자기 코드를 최종 승인하면 안 된다.
Reviewer가 리뷰하면서 코드를 마음대로 고치면 안 된다.
Tester가 테스트 실패를 보고 설계를 바꾸면 안 된다.
그리고 특정 제품에 조직을 종속시키면 안 된다.
Claude가 항상 Planner인 것은 아니다.
Codex가 항상 Implementer인 것도 아니다.
Cursor가 항상 Final Judge일 필요도 없다.
제품은 바뀔 수 있다.
하지만 다음 구조는 쉽게 바뀌지 않는다.
계획
→ 구현
→ 독립 리뷰
→ 테스트
→ 사람 승인
최근 AI 모델들이 강해질수록 중요한 것은 가장 뛰어난 모델 하나를 고르는 일이 아니다.
강한 모델들이 서로 다른 책임 안에서 일하고, 한 모델의 실수를 다른 역할이 잡아낼 수 있도록 만드는 일이다.
한 줄로 정리하면 이렇다.
AI를 여러 개 붙이는 것이 AI 개발팀은 아니다.
계획, 구현, 검토, 테스트의 책임을 나누고
각 역할이 다른 역할의 권한을 침범하지 못하게 해야 한다.
다음 편에서는 이번에 설계한 조직도를 실제로 연결한다.
Claude 계획
→ Codex 구현
→ Claude 독립 리뷰
→ Codex 제한 수정
→ 테스트 실행
→ Cursor 최종 확인
→ 사람 승인
중앙 Orchestrator, Provider Adapter, 상태 머신, Model Router, Git worktree, Diff-first Review를 사용해 서로 다른 AI가 같은 작업을 순차적으로 검증하는 구조를 구현한다.