Sol·Terra·Luna로 지뢰찾기 게임을 만들며 배운 멀티 에이전트 개발법

Sol·Terra·Luna 멀티 에이전트 개발법

AI Development · Multi-Agent Workflow

Sol·Terra·Luna로 지뢰찾기를 만들며 배운 멀티 에이전트 개발법

혼자 일하는 강력한 AI와 여러 AI가 역할을 나눠 일하는 방식 중 어느 쪽이 더 나을까? 브라우저 지뢰찾기를 실제로 만들며 직접 확인해 봤다.

먼저 결론부터 말하면, 멀티 에이전트의 진짜 장점은 여러 AI가 동시에 코드를 많이 쓰는 데 있지 않았다. 제품 결정, 구현, 검토의 책임을 분리했을 때 품질이 좋아졌다.

세 AI의 역할을 어떻게 나눴나

처음부터 멀티 에이전트를 쓰려고 했던 것은 아니다. 지뢰찾기를 구현하기 전에 다음과 같이 역할을 나누면 어떨지 논의한 것이 출발점이었다.

Sol 너는 전체적인 계획과 검토를 진행하고,
실제 구현은 Terra를 시켰으면 해.

여기에 Luna를 추가하면서 역할은 더 선명해졌다.

Sol   → 제품 결정 진행, 전체 구조 설계, 작업 분해, 코드 리뷰, 최종 품질 판단
Luna  → UX, 모바일 조작, 접근성, QA 사양
Terra → 게임 엔진, 저장소, UI, 테스트 등 실제 구현과 수정

권장 흐름은 다음과 같다.

사용자와 Sol이 사양 결정
          ↓
Luna가 UX·접근성 관점에서 사양 검토
          ↓
Sol이 구현 계획과 완료 기준 확정
          ↓
Terra가 구현
          ↓
Luna가 화면·조작성 QA
          ↓
Sol이 코드·테스트·전체 품질을 최종 검토

핵심은 구현자와 검토자를 분리한 것이었다. 각 모델의 이름보다 누가 어떤 결정을 내리고, 누가 결과를 승인하는지가 더 중요했다.

첫 단계: Luna에게 코딩이 아닌 질문을 맡겼다

서브에이전트 호출 기능은 Sol과 Terra만 지원한다고 한다. 그래서 아래의 이미지 처럼 Luna 모델의 별도 작업(새로운 채팅)을 생성하는 방식으로는 호출하였다.

Luna 모델의 별도 작업(새로운 채팅)을 생성하는 방식으로는 호출

지뢰찾기는 규칙이 단순해 보이지만 실제 제품으로 만들려면 결정할 것이 많다.

  • 첫 클릭은 어디까지 안전해야 하는가?
  • 우클릭으로 깃발과 물음표를 어떻게 전환할 것인가?
  • 숫자 주변을 한 번에 여는 코드(chording)를 지원할 것인가?
  • 모바일에서는 우클릭을 무엇으로 대체할 것인가?
  • 30×16 보드를 작은 화면에 어떻게 표시할 것인가?
  • 키보드와 스크린 리더 사용자는 어떻게 탐색할 것인가?
  • 진행 중인 게임과 최고 기록을 저장할 것인가?

Luna에게는 구현 대신 UX와 접근성 사양 검토를 맡겼다.

역할:
- 구현이나 파일 수정은 하지 않는다.
- 플레이 경험, 입력 방식, 접근성, 반응형 동작에 집중한다.
- 미결정 항목은 임의로 확정하지 않고 추천안과 대안을 구분한다.

검토 범위:
1. 마우스 좌클릭과 우클릭
2. 코드(chording)
3. 모바일 탭과 길게 누르기
4. 키보드 전용 조작
5. 스크린 리더 셀 상태
6. 44×44px 터치 목표
7. 작은 화면의 고급 보드
8. 타이머와 로컬 저장
9. 한국어·영어 문구
10. 수동 QA 체크리스트

검토 결과를 바탕으로 제품 결정을 먼저 고정했다.

초급: 9×9, 지뢰 10개
중급: 16×16, 지뢰 40개
고급: 30×16, 지뢰 99개

첫 클릭한 칸은 항상 안전
우클릭: 닫힘 → 깃발 → 물음표 → 닫힘
모바일: 깃발 모드 + 550ms 길게 누르기
키보드: 방향키, Enter/Space, F, M, C
진행 게임 1개와 난이도별 최고 기록을 localStorage에 저장
작은 화면에서는 44px 셀을 유지하고 보드 내부 스크롤 사용
승패는 모달 대신 인라인 결과 패널로 표시

이 단계에서 바로 코딩하지 않은 것이 중요했다. 모바일 조작이나 저장 정책을 나중에 바꾸면 엔진, UI, 저장 스키마, 테스트를 모두 다시 수정해야 하기 때문이다.

두 번째 단계: Terra에게 완료 조건이 있는 구현 계약을 전달했다

Sol이 Terra를 서브 에이전트로 호출하는 장면
Sol이 Terra를 서브 에이전트로 호출하는 장면

사양이 확정된 뒤 Terra에게 단순히 “지뢰찾기를 만들어 줘”라고 하지 않았다. 범위와 금지 사항, 테스트 조건까지 계약처럼 전달했다.

구현 범위:

1. 순수 TypeScript 엔진
   - 첫 클릭 이후 지뢰 생성
   - 빈 칸 연쇄 공개
   - 깃발과 물음표
   - 코드와 잘못된 깃발의 패배 위험
   - 승리 시 남은 지뢰 자동 깃발

2. 안전한 로컬 저장
   - online-games:minesweeper:save:v1
   - online-games:minesweeper:records:v1
   - 손상된 데이터의 런타임 검증
   - 저장 실패가 게임을 중단하지 않도록 처리

3. 접근 가능한 React UI
   - 의미 있는 grid 구조와 roving tabindex
   - 키보드 단축키와 polite live region
   - 44×44px 터치 목표와 모바일 내부 스크롤

4. 사이트 통합
   - 게임 카탈로그, 라우트, 사이트맵, 메타데이터
   - 한국어·영어 가이드

완료 조건:
- npm test
- npm run lint
- npm run typecheck
- npm run build

핵심 엔진은 React와 브라우저 API를 사용하지 않는 순수 모듈로 분리했다.

src/features/minesweeper/
├─ engine/
│  ├─ game.ts
│  ├─ types.ts
│  └─ index.ts
├─ storage/
│  ├─ minesweeper-storage.ts
│  ├─ validation.ts
│  └─ types.ts
├─ components/
│  └─ minesweeper-game.tsx
├─ input.ts
└─ index.ts

Terra의 첫 구현은 자동 테스트, 타입 검사, lint, build를 모두 통과했다. 하지만 그것이 작업의 끝은 아니었다.

세 번째 단계: 테스트 통과 후 Sol의 리뷰가 시작됐다

자동 검증을 모두 통과한 코드에서도 실제 사용 중 문제가 될 수 있는 항목이 발견됐다.

Sol이 리뷰중 문제를 발견하면 Terra가 문제를 고치고 있다.
Sol이 리뷰중 문제를 발견하면 Terra가 문제를 고치고 있다.

1. 저장된 게임을 새 게임이 덮어쓸 가능성

화면이 열리면 기본 새 게임 상태가 먼저 만들어지고, 그다음 localStorage에서 진행 중인 게임을 읽는다. 이 사이에 자동 저장이 실행되면 기존 저장 게임을 덮어쓸 수 있었다.

export function shouldAutosaveMinesweeperGame(input: {
  hydrated: boolean;
  hasSavedGameChoice: boolean;
  phase: "ready" | "playing" | "won" | "lost";
}) {
  return (
    input.hydrated &&
    !input.hasSavedGameChoice &&
    input.phase !== "won" &&
    input.phase !== "lost"
  );
}

저장 게임 선택 화면이 남아 있는 동안 자동 저장을 중단하는 가드를 추가했다. 게임 규칙 테스트만으로는 발견하기 어려운 React hydration과 저장 시점의 조합 문제였다.

2. 모바일 길게 누르기 후 클릭이 다시 실행되는 문제

550ms 길게 누르기로 깃발을 표시한 뒤 브라우저가 일반 클릭까지 발생시키면 셀이 공개되거나 마크가 한 번 더 바뀔 수 있었다.

550ms 길게 누르기
→ 깃발 표시
→ 손가락을 뗌
→ click 이벤트 발생
→ 셀 공개 또는 마크 한 번 더 전환

길게 누르기 완료 여부를 추적해 바로 다음 클릭을 한 번 소비하도록 했다.

export function consumeMinesweeperPointerClick(
  interaction: MinesweeperPointerInteraction | null,
) {
  return {
    suppress: interaction?.longPressCompleted === true,
    nextInteraction: null,
  };
}

3. 일시정지 중 일부 입력이 허용되는 문제

첫 구현에서는 공개 동작은 막았지만 깃발, 물음표, 코드가 일부 실행될 수 있었다. 모든 보드 입력에 하나의 상호작용 조건을 적용했다.

const canInteract =
  game.phase === "ready" ||
  (game.phase === "playing" && running);

4. 저장 데이터의 형태만 맞으면 통과하는 문제

localStorage는 신뢰할 수 없는 입력이다. 타입과 배열 길이가 맞더라도 게임 상태 자체가 모순될 수 있다.

- 실제 지뢰 배치와 adjacent 숫자가 맞지 않는 보드
- 지뢰가 공개됐지만 phase가 playing인 보드
- generated=false인데 지뢰가 포함된 보드
- lost인데 explodedIndex가 지뢰를 가리키지 않는 보드
- won인데 안전한 칸이 남아 있는 보드

검증 로직이 각 안전 셀의 주변 지뢰 수를 다시 계산하고, 저장된 숫자와 비교하도록 강화했다.

최종 검증 결과

Terra의 수정 이후 Sol이 동일한 검증을 독립적으로 다시 실행했다.

npm test
npm run lint
npm run typecheck
npm run build
130tests passed
Passedlint
PassedTypeScript
Passedbuild

브라우저에서는 첫 클릭 안전, 키보드와 우클릭 깃발, 게임 복원, 타이머 재개, 한국어·영어 전환, 방향키 포커스 이동을 확인했다. 390×844 화면에서도 페이지 가로 넘침 없이 고급 보드가 내부 스크롤되고, 셀 크기는 44×44px로 유지됐다.

최종 변경은 다음 커밋으로 정리했다.

62de01d feat(minesweeper): add accessible classic game

멀티 에이전트의 진짜 장점

Luna와 Terra가 있었기 때문에 무조건 더 빨라졌다고 말하기는 어렵다. 역할 전달, 검토, 수정 요청에는 조정 시간이 든다.

Luna 사양 검토
→ 사용자 결정
→ 제품 문서 작성
→ Terra 구현
→ Sol 검토
→ Terra 수정
→ Sol 재검토
→ 자동 검증
→ 브라우저 QA

하지만 품질 면에서는 분명한 이점이 있었다. 혼자 작업하면 설계자, 구현자, 검토자가 모두 같아진다. 자신이 작성한 코드는 의도를 이미 알기 때문에 잘못된 코드도 의도대로 읽는 경향이 있다.

혼자 작업: Sol이 설계 → Sol이 구현 → Sol이 자신의 구현을 검토

역할 분리: Luna가 UX 검토 → Terra가 구현 → Sol이 독립 검토

저장 덮어쓰기와 길게 누르기 중복 입력은 작성자와 검토자가 달랐기 때문에 비교적 쉽게 찾을 수 있었던 대표적인 사례다.

모든 작업에 멀티 에이전트가 필요한 것은 아니다

문구 한 줄, 단일 CSS 오류, 작은 타입 변경, 명확한 회귀 버그는 한 에이전트가 직접 처리하는 편이 효율적이다. 위임 계약을 쓰고 결과를 다시 검토하는 비용이 작업 자체보다 커질 수 있다.

반면 다음 조건에서는 역할 분담이 효과적이다.

  • 제품 규칙이 아직 확정되지 않은 기능
  • 엔진, 저장소, UI가 함께 바뀌는 기능
  • 모바일과 데스크톱 입력 방식이 다른 기능
  • 접근성과 다국어 지원이 필요한 기능
  • 잘못 구현하면 저장 데이터가 손상될 수 있는 기능

추천하는 기본 흐름은 다음과 같다.

  1. Sol이 제품 질문과 작업 경계를 정한다.
  2. Luna가 UX·접근성·테스트 관점에서 사양을 검토한다.
  3. 사용자가 중요한 제품 결정을 확정한다.
  4. Terra가 구현과 자동 테스트를 담당한다.
  5. Sol이 diff와 실제 동작을 독립적으로 검토한다.
  6. 문제는 원래 구현자인 Terra에게 돌려보낸다.
  7. Sol이 전체 검증 후 커밋한다.

하나의 작업 디렉터리에서는 실제 코드를 쓰는 에이전트를 한 명으로 제한하는 편이 안전하다. 분석과 검토는 병렬로 진행할 수 있지만 같은 파일을 동시에 수정하면 충돌과 책임 불분명이라는 새 문제가 생긴다.

결론

멀티 에이전트 개발의 핵심은 AI를 많이 호출하는 것이 아니다. 각 역할 사이에 명확한 책임과 승인 단계를 만드는 것이다.

Luna: 무엇을 만들어야 하는가?
Terra: 그것을 어떻게 구현할 것인가?
Sol: 구현 결과를 신뢰해도 되는가?

이번 지뢰찾기 개발에서 가장 큰 성과는 코드 작성 속도가 아니라 제품 결정, 구현, 검토가 서로 분리됐다는 점이었다. 결국 멀티 에이전트 개발에서 중요한 것은 병렬성보다 독립적인 검토다.

태그: AI 코딩 · Codex · 멀티 에이전트 · 지뢰찾기 · 소프트웨어 테스트 · 코드 리뷰

댓글

이 블로그의 인기 게시물

클로드 Gmail 연동 방법: 이메일 분류·요약·답장 자동화

Claude Code 토큰 절약 가이드

Claude Code vs Codex vs Antigravity 비교: 2026년 AI 코딩 에이전트 무엇을 선택할까?