Devlery
Blog/AI

다중 에이전트 개발 패턴: 아키텍트-개발자-리뷰어 분리 전략

하나의 LLM에게 설계, 구현, 검증을 모두 맡기면 품질이 무너집니다. 역할을 분리하고 다중 모델을 조합하는 아키텍트-개발자-리뷰어 패턴과 5단계 검증 구조를 살펴봅니다.

Claude Code에 프롬프트를 던지고 코드를 받습니다. 결과가 마음에 안 들면 "다시 해줘"라고 합니다. 잘 되면 커밋하고, 안 되면 몇 번 더 시도합니다. 대부분의 AI 코딩이 이 패턴입니다.

문제는 이 방식이 규모가 커지면 무너진다는 점입니다. 하나의 LLM에게 설계, 구현, 검증을 전부 맡기면 자기 동의(self-agreement) 문제에 빠지게 됩니다. 같은 모델이 자기가 작성한 코드를 리뷰하면, 자기가 내린 판단을 다시 확인하는 것에 불과합니다. 이 글에서는 역할을 분리하고 다중 모델을 조합하는 패턴을 살펴보겠습니다.

단일 에이전트의 한계

하나의 LLM 세션에서 "이 기능을 설계하고 구현하고 테스트까지 작성해줘"라고 요청하면, 겉보기에는 잘 동작하는 결과물이 나옵니다. 하지만 세 가지 근본적인 문제가 있습니다.

자기 동의 편향. LLM에게 자기가 작성한 코드를 리뷰하라고 하면, 같은 강점과 약점을 가진 모델이 자기 판단을 다시 평가하는 구조입니다. Stavros Korokithakis는 이를 명확하게 지적합니다:

같은 모델에게 리뷰를 맡기면 대체로 같은 의견, 같은 강점, 같은 약점을 보입니다. 다른 모델에게 리뷰를 맡기는 것이 큰 개선으로 이어집니다.

역할 혼재. 설계 판단과 구현 세부사항이 하나의 컨텍스트에 뒤섞이면, 모델은 "큰 그림"과 "코드 한 줄"을 동시에 최적화하려다 양쪽 모두 품질이 떨어집니다. 아키텍처 결정은 넓은 시야가 필요하고, 구현은 좁은 집중이 필요한데, 이 두 가지를 하나의 프롬프트에서 동시에 요구하는 셈입니다.

검증 부재. 코드를 작성한 에이전트가 "완료됐습니다"라고 하면, 실제로 그런지 확인할 독립적인 검증 단계가 없습니다. 타입 체크와 테스트를 통과하더라도, 설계 의도와 맞는지, 보안 문제는 없는지 검증하는 별도의 눈이 필요합니다.

아키텍트-개발자-리뷰어 패턴

이 문제를 해결하는 구조가 역할 분리 패턴입니다. 소프트웨어 조직에서 시니어 엔지니어가 설계하고, 주니어가 구현하고, 다른 팀원이 리뷰하는 것과 같은 원리를 LLM 워크플로우에 적용한 것입니다.

Phase 1
아키텍트
설계 · 계획 · 판단
Phase 2
개발자
구현 · 코딩 · 테스트
Phase 3
리뷰어
검증 · 피드백 · 승인

핵심 원칙은 세 가지입니다:

  1. 각 역할은 독립된 세션에서 실행됩니다. 아키텍트의 컨텍스트가 개발자에게 넘어갈 때는 "계획 문서"만 전달됩니다.
  2. 역할 간 경계가 명확합니다. 개발자는 설계 판단을 하지 않고, 리뷰어는 구현을 수정하지 않습니다.
  3. 서로 다른 모델을 사용합니다. 자기 동의 문제를 구조적으로 차단합니다.

역할별 상세 설계

아키텍트: 설계와 판단

아키텍트는 가장 강력한 추론 모델이 맡습니다. Opus처럼 깊은 사고가 가능한 모델이 적합합니다. 이 단계에서 인간과 LLM이 함께 요구사항을 정제하고, 기술적 결정을 내리고, 파일·함수 단위의 상세 계획을 수립합니다.

Stavros의 워크플로우에서 이 단계는 최대 30분간 진행됩니다. 짧은 시간이 아니지만, 여기서 투자한 시간이 구현 단계의 삽질을 줄여줍니다.

아키텍트에게 전달하는 시스템 프롬프트 예시를 살펴보겠습니다:

당신은 시니어 소프트웨어 아키텍트입니다.
사용자의 요구사항을 분석하고, 설계 질문을 던지고,
파일/함수 단위의 구현 계획을 작성합니다.

규칙:
- 구현 코드를 직접 작성하지 않습니다
- 모호한 부분은 반드시 질문으로 확인합니다
- 계획이 확정되면 "approved"를 명시합니다
- 각 태스크는 하나의 파일 또는 하나의 기능 단위로 분할합니다

아키텍트가 만드는 산출물은 이런 형태입니다:

## 구현 계획

### Task 1: 이메일 수신 핸들러 (src/handlers/email-inbound.ts)
- Cloudflare webhook으로 들어오는 이메일 파싱
- 발신자 allowlist 검증
- 기존 대화 스레드 매칭

### Task 2: 이메일 발송 서비스 (src/services/email-sender.ts)
- SMTP 클라이언트 설정
- 플레인 텍스트 변환
- 스레드 ID 기반 In-Reply-To 헤더 설정

### Task 3: 발신자 인증 (src/auth/email-allowlist.ts)
- 이메일 주소 및 도메인 와일드카드 매칭
- glob 패턴 지원 (*@example.com)
- 보안: 로컬 파트에 와일드카드 금지

(이하 생략)

상태: approved

이 계획이 "approved" 상태가 되어야 개발자 단계로 넘어갑니다. 인간이 계획을 검토하고 승인하는 게이트가 반드시 필요합니다.

개발자: 계획 실행

개발자 역할은 비용 효율적인 모델이 맡습니다. Sonnet이나 GPT-4o-mini처럼 빠르고 저렴한 모델이 적합합니다. 핵심은 설계 판단을 하지 않는 것 입니다. 아키텍트가 정한 계획을 그대로 구현합니다.

개발자에게 전달하는 시스템 프롬프트 예시를 보겠습니다:

당신은 구현 전문 개발자입니다.
아래 계획을 정확히 따라 구현합니다.

규칙:
- 계획에 없는 설계 결정을 하지 않습니다
- 계획과 다른 방향으로 구현해야 할 경우, 즉시 중단하고 보고합니다
- 각 태스크 완료 후 결과를 요약합니다
- 테스트 코드도 계획에 명시된 대로 작성합니다

[아키텍트 계획 첨부]

이 분리가 주는 이점은 비용입니다. 아키텍트(Opus)로 30분 대화하는 비용보다, 개발자(Sonnet)로 수백 줄 코드를 생성하는 비용이 훨씬 적습니다. 판단이 필요한 곳에만 비싼 모델을 쓰고, 실행은 저렴한 모델에 맡기는 구조입니다.

리뷰어: 독립적 검증

리뷰어는 반드시 다른 모델 이 맡아야 합니다. 같은 모델이 자기 코드를 리뷰하면 자기 동의 편향에 빠지기 때문입니다. 이상적으로는 여러 모델이 동일한 코드를 검토해 다양한 관점을 확보합니다.

모델마다 리뷰 성향이 다릅니다:

모델리뷰 성향적합한 리뷰 영역
Codex꼼꼼하고 까다로움코드 품질, 엣지 케이스, 규칙 준수
Gemini대안적 해법 제시설계 개선, 다른 접근법 탐색
Opus설계 의도와의 일치 판단아키텍처 정합성, 비즈니스 로직 검증

리뷰어에게 전달하는 프롬프트 예시를 살펴보겠습니다:

아래 코드 diff와 원래 설계 계획을 비교해 리뷰합니다.

검토 기준:
1. 계획과의 일치: 설계 의도대로 구현되었는가?
2. 버그/보안: 명백한 버그나 보안 취약점이 있는가?
3. 엣지 케이스: 누락된 예외 처리가 있는가?
4. 테스트 커버리지: 핵심 로직이 테스트되고 있는가?

의견이 불일치하거나 설계 변경이 필요한 경우,
아키텍트에게 에스컬레이션을 요청합니다.

[설계 계획 첨부]
[코드 diff 첨부]

리뷰어가 "설계 자체를 바꿔야 한다"는 판단을 내리면, 구현을 수정하는 게 아니라 아키텍트에게 되돌려 보냅니다. 이 에스컬레이션 경로가 패턴의 핵심입니다.

다중 모델 전략

왜 여러 모델을 써야 할까요? 단순히 "더 많은 눈"의 문제가 아닙니다. 각 모델은 서로 다른 학습 데이터, 다른 추론 패턴, 다른 편향을 가지고 있습니다. 하나의 모델이 놓치는 문제를 다른 모델이 잡아냅니다.

실제로 효과적인 조합 예시를 보겠습니다:

아키텍트: Claude Opus — 깊은 추론, 복잡한 설계 판단
개발자:   Claude Sonnet — 빠른 코드 생성, 비용 효율
리뷰어 1: Codex — 꼼꼼한 코드 품질 검증
리뷰어 2: Gemini Flash — 대안적 접근법 제시

이 조합이 동작하는 이유는 모델 간의 인지적 다양성 때문입니다. Codex가 "이 변수명이 일관성 없다"고 지적하는 동안, Gemini가 "이 로직 자체를 다른 방식으로 접근하면 더 간결해진다"고 제안합니다. 같은 모델 두 개로는 이런 다양성을 얻을 수 없습니다.

주의할 점이 있습니다. 이 패턴을 적용하려면 여러 모델에 접근할 수 있는 도구가 필요합니다. Claude Code나 Codex CLI 같은 1사 도구는 자사 모델만 지원하므로, OpenCode나 직접 구축한 하네스를 사용해야 합니다. 또한 에이전트 간 정보 전달이 자동화되어야 합니다. 아키텍트의 계획을 복사해서 개발자에게 수동으로 붙여넣는 방식은 곧 지치게 됩니다.

다층 검증 구조: 스위스 치즈 모델

역할 분리만으로는 부족합니다. 각 레이어가 완벽하지 않더라도, 구멍이 겹치지 않도록 여러 겹의 검증을 쌓는 것이 핵심입니다. 이를 스위스 치즈 모델이라 부릅니다. 치즈 한 장에는 구멍이 있지만, 여러 장을 겹치면 빛이 통과하지 못하는 것과 같은 원리입니다.

5단계 검증 레이어

1
다중 옵션 경쟁
3개 에이전트가 독립적으로 구현, 최적 결과 선택
2
결정론적 가드레일
테스트, 타입 체크, 린트 — 의견이 아닌 사실 기반 검증
3
BDD 수용 기준
인간이 Given-When-Then으로 정의, 에이전트가 구현
4
권한 시스템
에이전트 접근 범위 세분화, 위험 변경 시 자동 에스컬레이션
5
적대적 검증
코딩 에이전트 ≠ 검증 에이전트, Red/Blue team 자동화

각 레이어를 상세히 살펴보겠습니다.

Layer 1: 다중 옵션 경쟁

하나의 에이전트에게 하나의 구현을 맡기는 대신, 여러 에이전트가 독립적으로 같은 문제를 풀게 합니다. 소프트웨어 역사상 옵션을 만드는 비용이 가장 낮은 시대 입니다. 에이전트 3개를 동시에 돌려도 비용은 미미합니다.

선택 기준은 명확합니다:

  • 테스트 통과율
  • diff 크기 (작을수록 좋습니다)
  • 외부 의존성 추가 여부
  • 기존 코드 패턴과의 일관성

Layer 2: 결정론적 가드레일

테스트, 타입 체크, 계약 검증 — 의견이 아닌 사실만 다루는 것들입니다.

이 레이어는 LLM의 판단에 의존하지 않습니다. TypeScript의 타입 시스템, ESLint 규칙, 유닛 테스트 같은 결정론적 도구가 자동으로 검증합니다.

// CI에서 자동 실행되는 가드레일 예시
const guardrails = {
  // 하드코딩된 시크릿 탐지
  noHardcodedSecrets: "grep -rn 'password\\s*=' --include='*.ts'",
  // 타입 안전성
  typeCheck: "tsc --noEmit",
  // 조직 전체 린트 규칙
  lint: "eslint . --max-warnings 0",
  // 테스트 통과
  test: "vitest run --coverage",
};

Layer 3: BDD 수용 기준

Behavior-Driven Development가 AI 시대에 다시 부상하고 있습니다. 인간이 자연어로 기대 동작을 정의하면, 에이전트가 구현하고, BDD 프레임워크가 자동으로 검증합니다.

Feature: 이메일 수신 처리

  Scenario: 허용된 발신자의 이메일 수신
    Given 발신자 "user@example.com"이 allowlist에 등록되어 있다
    When 해당 발신자로부터 이메일이 수신된다
    Then 기존 대화 스레드에 메시지가 추가된다
    And 수신 확인 응답이 발송된다

  Scenario: 미등록 발신자의 이메일 수신
    Given 발신자 "stranger@unknown.com"이 allowlist에 없다
    When 해당 발신자로부터 이메일이 수신된다
    Then 이메일이 무시된다
    And 로그에 거부 사유가 기록된다

이 수용 기준은 코드가 아닌 스펙 입니다. 에이전트가 어떤 방식으로 구현하든, 이 시나리오를 통과하면 정상입니다. 인간의 역할이 "코드를 검토하는 것"에서 "기대 동작을 정의하는 것"으로 이동하게 됩니다.

Layer 4: 권한 시스템

모든 에이전트에게 전체 코드베이스 접근 권한을 주는 것은 위험합니다. 에이전트의 접근 범위를 세분화하고, 위험한 변경은 자동으로 에스컬레이션하는 구조가 필요합니다.

에스컬레이션이 필요한 변경은 어떤 것들이 있을까요?

  • 인증/인가 로직 수정
  • 데이터베이스 스키마 변경
  • 새 외부 의존성 추가
  • 환경 변수나 시크릿 관련 코드 수정

이런 변경은 에이전트의 자신감 수준과 무관하게 반드시 인간의 확인을 거쳐야 합니다.

Layer 5: 적대적 검증

코드를 작성한 에이전트와 검증하는 에이전트를 구조적으로 분리 합니다. 검증 에이전트의 목표는 구현을 깨뜨리는 것입니다. Red team과 Blue team의 원리를 자동화한 것입니다.

Blue Team (구현 에이전트):
- 기능을 정상적으로 구현합니다
- 일반적인 테스트 케이스를 작성합니다

Red Team (검증 에이전트):
- 엣지 케이스를 찾아 공격합니다
- 경쟁 조건, 메모리 누수, 입력 검증 우회를 시도합니다
- "이 코드를 어떻게 깨뜨릴 수 있는가?"에 집중합니다

이 5개 레이어를 모두 적용할 필요는 없습니다. 프로젝트 규모와 위험도에 따라 선택적으로 조합하면 됩니다. 중요한 것은 단일 레이어에 의존하지 않는 것 입니다.

실전 적용: 이메일 기능 구현 사례

Stavros Korokithakis가 자신의 개인 AI 비서 Stavrobot에 이메일 기능을 추가한 과정을 살펴보겠습니다. 이 사례는 아키텍트-개발자-리뷰어 패턴이 실제로 어떻게 동작하는지 잘 보여줍니다.

아키텍트 단계. Stavros가 "이메일 기능을 추가하고 싶다"고 말하자, 아키텍트(Opus)가 7개의 설계 질문을 던졌습니다:

  • 인바운드 이메일 수신 방식은? (Cloudflare webhook)
  • 아웃바운드 발송 방법은? (SMTP)
  • 양방향 대화를 지원할 것인가?
  • HTML 이메일 처리 방식은?
  • 스레딩 방식은?
  • 첨부 파일은?

이 질문들에 답하며 아키텍처가 결정되었고, 아키텍트는 6개 태스크로 분할된 상세 계획을 작성했습니다.

개발자 단계. Sonnet이 6개 태스크를 순서대로 구현했습니다. 새 파일과 기존 파일 수정을 포함해 421개의 테스트가 통과했습니다.

리뷰와 개선. Stavros가 코드를 확인하며 버그를 발견했습니다. owner 이메일이 인식되지 않는 문제였는데, 채널별로 하드코딩된 목록이 원인이었습니다. "공유 채널 이름 목록을 추출해서 한 곳에서 관리하자"고 제안했고, 에이전트가 이 리팩토링을 수행했습니다.

기능 확장. 개발 중간에 "SMTP 설정 없이 수신만 가능한 모드도 필요하다"는 요구사항이 추가됐습니다. 아키텍트가 "간단하다"고 판단했고, SMTP 필드를 optional로 만드는 것으로 해결됐습니다.

보안 고려. 도메인 와일드카드(*@example.com) 기능을 추가할 때, 이메일 스푸핑 방지를 위해 로컬 파트에는 와일드카드를 금지하는 규칙을 명시적으로 추가했습니다. "me@good.com"@evil.com 같은 공격을 차단하기 위해서입니다.

전체 과정이 약 1시간 만에 완료됐습니다. 완전한 인바운드/아웃바운드 이메일 시스템이 설계, 구현, 테스트, 보안 검토까지 마무리된 것입니다.

이 패턴이 실패하는 경우

이 패턴이 만능은 아닙니다. 명확한 한계가 있습니다.

기술 스택을 이해하지 못할 때. Stavros는 이를 직접 경험했습니다:

기반 기술을 충분히 이해하지 못하는 프로젝트(예: 모바일 앱)에서는 코드가 금세 나쁜 결정의 누적으로 엉망이 됩니다.

아키텍트 역할에서 인간의 기술적 판단력이 핵심입니다. LLM의 설계 제안을 평가하려면 해당 도메인을 이해해야 합니다. 모르는 기술 스택에서 LLM에게 설계를 전적으로 맡기면, 잘못된 설계가 누적되어 나중에 수습할 수 없는 상태가 됩니다.

나쁜 결정의 누적 신호. 이 상황에 빠졌다는 확실한 신호가 있습니다. LLM에게 "안 되는데"라고 말하면 "아, 원인을 알겠습니다! 수정하겠습니다"라고 하면서 상황이 더 나빠지는 패턴입니다. 이때는 코드를 버리고 아키텍처 단계부터 다시 시작하는 게 낫습니다.

엔지니어링 능력의 이동. 이 패턴이 코딩 능력이 불필요해졌다는 뜻은 아닙니다. Stavros가 정확히 표현했습니다:

코드를 올바르게 작성하는 방법을 더 이상 알 필요는 없지만, 시스템을 올바르게 설계하는 방법을 이해하는 것은 이전보다 훨씬 중요해졌습니다.

엔지니어링 능력이 사라진 게 아니라, 구현 레벨에서 설계 레벨로 이동 한 것입니다.

언제 이 패턴을 적용해야 하는가

모든 작업에 3단계 분리가 필요한 것은 아닙니다.

적합한 경우:

  • 새로운 기능 개발 (설계 판단이 필요한 작업)
  • 복잡한 리팩토링 (영향 범위가 넓은 변경)
  • 보안에 민감한 코드 변경
  • 팀에서 AI 생성 코드의 품질을 보증해야 하는 경우

과잉인 경우:

  • 단순 버그 수정 (원인이 명확한 경우)
  • 텍스트 수정, 설정 변경
  • 프로토타이핑 (빠른 검증이 목적인 경우)

핵심은 설계 판단의 비중 입니다. 설계 판단이 필요 없는 작업이라면 단일 에이전트로 충분합니다. 설계 판단이 핵심인 작업이라면, 그 판단을 별도의 단계로 분리하는 것이 이 패턴의 가치입니다.

마무리

AI 도구를 단순한 코드 생성기로 쓰는 것과, 역할을 분리한 다중 에이전트 시스템으로 운영하는 것은 전혀 다른 결과를 만들어냅니다. 아키텍트가 설계를 책임지고, 개발자가 구현에 집중하고, 독립적인 리뷰어가 검증하는 구조는 인간 조직의 소프트웨어 개발 프로세스를 LLM 워크플로우에 그대로 적용한 것입니다.

여기에 스위스 치즈 모델의 다층 검증을 더하면, 단일 레이어의 구멍을 다른 레이어가 보완합니다. 이 패턴이 발전하면 인간의 역할도 바뀌게 됩니다. 코드를 한 줄씩 읽는 대신, 스펙을 정의하고, 아키텍처를 판단하고, 검증 규칙을 설계하는 것이 개발자의 핵심 역량이 됩니다.