핸드오프 주도 개발 (Handoff-Driven Development)
요약
사양 주도 개발(SDD)을 개선한 '핸드오프 주도 개발' 방법론을 소개합니다. 계층적 명세 맵을 통해 AI 모델의 컨텍스트를 최적화하고, 작업 간 전환 시 발생하는 비용과 인지 부하를 줄이는 실무적인 워크플로를 제안합니다.
핵심 포인트
- 컨텍스트 청결성 유지: 불필요한 정보를 배제하여 모델의 품질 향상 및 비용 절감
- 계층적 명세 맵 활용: 루트에서 서브프로젝트로 이어지는 맵을 통해 필요한 정보만 탐색
- 도달 가능성 불변량: 모든 명세 파일은 마크다운 링크 체인을 통해 연결되어야 함
- 효율적인 작업 전환: 세션 및 작업 간 컨텍스트 유실 문제를 구조적으로 해결
사양 주도 개발 (Spec-driven development, SDD)의 개선판입니다. 이것은 SDD와 핸드오프 (handoffs)의 결합으로, 개인 개발자와 소규모 팀 모두에게 최첨단 베스트 프랙티스 (best practice)입니다.
이것은 에이전트 군단이 워크플로 (workflows)를 통해 서로 동기화하며, 추상적인 "계획(plan) → 코드(code) → 리뷰(review) → 테스트(test) → 배포(deploy)" 루프를 통해 자율적으로 제품을 출시하는 이야기가 아닙니다. 우리에게는 전용 데이터 센터나 토큰 비용으로 쓸 수 있는 50만 달러가 없습니다.
마지막에는 여러분이 클론 (clone)하거나 단순히 가리키기만 하면 되는 템플릿 리포지토리 (template repository) 링크가 있습니다. 그리고 Opus (Fable)에게 "나를 위해 동일한 사양 시스템 (specification system)을 설정해줘"라고 말하면 됩니다. 나머지는 알아서 처리합니다.
우리가 해결하는 문제들
1. 컨텍스트 (Context)의 청결성. 모델의 컨텍스트에는 작업에 필요한 내용만 포함되어야 하며, 그 외의 불필요한 내용은 포함되지 않아야 합니다. 불필요한 자료는 인지 부하 (cognitive load)를 높이고 솔루션의 질을 저하시킵니다. 올바른 컨텍스트가 올바른 정답, 올바른 코드, 올바른 결정을 만듭니다. 또한 컨텍스트는 유한하며 비용이 발생합니다. 따라서 "무엇을 로드할 것인가"만큼이나 "무엇을 로드하지 않을 것인가"라는 질문도 중요합니다.
2. 작업 간의 전환 (Switching between tasks). 단일 작업의 컨텍스트는 프롬프트 (prompt) 내에서 수동으로 구성할 수 있습니다. 하지만 보통 여러 작업이 병렬로 진행됩니다. 서버 코드를 감사하고 수정하는 일, 클라이언트 앱을 설계하는 일, 새로운 CI/CD로 마이그레이션하는 일, 새로운 기능의 개념적 계획 수립 등이 그것입니다. 각 작업은 고유한 관련 문서 세트, 완료/남은 상태, 수락된 결정 사항을 가지고 있습니다.
세션 (Sessions) 또한 유한합니다. 하나의 작업 내에서도 세션 시작 시 제공된 컨텍스트는 다른 세션으로 이어질 때 더 이상 최신 상태가 아닙니다. 작업 간, 그리고 세션 간의 전환은 시간, 품질, 그리고 토큰 비용을 소모하게 만듭니다.
제1부: SDD
맵 (Maps)
시작 위치: 애플리케이션, 마이크로서비스 (microservices), 공유 패키지 (shared packages), 데브옵스 매니페스트 (devops manifests) 등 다양한 프로젝트가 섞여 있는 솔루션 모노레포 (monorepo).
각각의 서브프로젝트 (subproject)는 자체적인 로컬 명세 (local specification)와 로컬 맵 (local map)을 포함하는 고유의 specs/ 폴더를 가집니다. 그리고 루트 (root)에는 서브프로젝트 맵들로 연결되는 **루트 명세 맵 (root specification map)**이 위치합니다. 그 결과 계층 구조가 형성되며, 이는 핵심적인 이점을 제공합니다. 즉, 루트에서 솔루션의 어떤 부분에 대해서도 프롬프트 (prompt)를 작성할 수 있다는 점입니다. 모델은 중간에 불필요한 정보를 로드하지 않고, 2~3번의 홉 (hop)을 통해 맵을 따라 내려가며 필요한 것을 스스로 찾아냅니다.
2~3단계의 계층 구조: 루트 → 서브프로젝트 → 패키지 (package). 그 이상의 단계는 의문스럽습니다. 모든 단계는 독자(LLM)가 실제 콘텐츠에 도달하기 전에 거쳐야 하는 홉 (hop)이기 때문입니다.
도달 가능성 불변량 (The reachability invariant): 모든 명세 파일은 적어도 하나의 맵으로부터 마크다운 링크 (markdown links) 체인을 통해 도달 가능해야 합니다 (단순 텍스트 경로는 링크로 간주되지 않으며, IDE나 감사 (audit) 도구에서도 인식되지 않습니다). 도달할 수 없는 파일은 고아 (orphan) 파일입니다. 아무도 찾을 수 없는 문서는 존재하지 않는 것과 같습니다. 고아 파일은 전용 감사 스크립트 (audit script)에 의해 포착됩니다.
장르 (Genres)
모든 명세 파일은 특정 장르에 속합니다. 장르라는 개념은 Diátaxis에서 가져왔지만, 분류 체계 (taxonomy)는 자체적으로 구축한 것입니다.
as-built — 아키텍처 개요를 포함하여 기존 코드에 대한 설명입니다. 코드를 반드시 따라야 하며, 그렇지 않을 경우 정보가 오래되었다 (stale)고 솔직하게 명시해야 합니다 (이에 대해서는 아래에서 자세히 다룹니다). 상세 수준은 C4 모델의 용어로 생각하면 편리합니다. 루트 맵과 루트 as-built 명세는 시스템 컨텍스트 (System Context) 및 컨테이너 (Containers) (솔루션 전체와 그 컨테이너 애플리케이션)에 해당합니다. 서브프로젝트 명세는 컨테이너 (Container) (하나의 컨테이너 내부: 도메인 (domains), 서비스 (services), 스토어 (stores))에 해당하며, 세 번째 레벨은 컴포넌트 (Component)입니다. 코드 (Code) 레벨은 명세의 대상이 아닙니다. 코드는 스스로를 가장 잘 설명하기 때문입니다.
plan (계획) — 특정 트랙(track)이나 기능(feature)을 위한 구현 계획(implementation plan)입니다. 워킹 세션(working session)의 핵심이며, 구현은 이 계획을 중심으로 이루어집니다. 근본적으로 일시적입니다. 완료된 계획이 현재 문서인 것처럼 남아있는 것은 잘못된 정보(disinformation)를 제공하는 것입니다. 구현이 끝나면 계획은 소멸합니다. 기본적으로는 tombstone (이미 그 가치가 as-built 및 백로그(backlog)로 추출되었으므로 아래의 tombstone 참조), 후속 계획이 있는 경우 superseded (대체됨), 또는 결정 이력으로서 참조 가치가 있는 경우 archive (보관) 상태가 됩니다.
reference (참조) — 코드 상위 레벨의 참조 자료로, 조사 내용(research), 근거(rationales), 작업 노트(working notebooks) 등이 포함됩니다. as-built와 유사하지만 코드를 다루지는 않습니다. 현실을 반드시 따를 의무는 없으며, 자료로서의 가치를 지닙니다.
vision (비전) — 제품이나 기능에 대한 코드 상위 레벨의 비전입니다. 코드는 아직 존재하지 않거나 비전에 부합할 의무가 없습니다. 비즈니스 계획(business plans)과 제품 가설(product hypotheses)도 여기에 포함됩니다. 즉, "어떻게 작동하는가(how it works)"가 아니라 "우리가 무엇을 만들고 있는가(what we are building)"에 관한 모든 것입니다. 대략적으로 계획(plan)과 비슷하지만, 코드를 위한 것은 아닙니다.
log (로그) — 아키텍처 결정 기록, 즉 ADR (Architecture Decision Records)입니다. 추가 전용(Append-only) 방식입니다. 각 항목은 "맥락(context) → 결정(decision) → 결과(consequences)"로 구성되며, 각 항목은 자체적인 상태(status)를 가집니다. 이는 "왜 이곳은 이렇게 이상하게 만들어졌는가"라는 영원한 질문에 대해, git 히스토리를 통한 고고학적 탐사가 아닌 단 한 단락으로 답을 제공합니다.
Statuses (상태)
장르(genre)와 직교하는(orthogonal) 두 번째 축으로, 문서가 생명 주기(lifecycle) 중 어디에 있는지를 나타냅니다.
| Status | 의미 |
|---|---|
draft | 브레인스토밍/제안, 아직 수락되지 않음 |
| ... |
중요! 표에서 가장 가치 있는 상태는 stale (오래된/최신이 아닌)입니다. "코드를 업데이트할 때 항상 문서를 함께 업데이트하라"는 고전적인 요구사항은 항상 충족될 수 없습니다. HDD 규칙은 더 정직합니다. 코드와 그 as-built 명세는 동일한 세션에서 편집됩니다. 지금 당장 명세를 업데이트할 수 없다면 — stale로 표시하십시오. 부채를 탕감한 것은 아니지만, _명시적으로 표시_한 것입니다. stale 상태를 인지하는 모델은 다음과 같이 판단합니다: "이 문서를 신뢰하지 마라, 코드를 통해 다시 검증하라." 이로 인해 잘못된 정보(disinformation)가 발생한 것이 아니라, 신뢰할 수 없다는 정보(information about unreliability)가 발생한 것입니다.
따라서 모든 명세서(specification) 파일의 제목 아래에는 다음과 같은 한 줄이 포함됩니다.
Status: as-built / current · verified: 2026-07-03
장르(Genre), 상태(status), 그리고 검증 날짜(verification date)입니다. 주의할 점은, verified:는 텍스트를 수정한 날짜가 아니라, 콘텐츠를 코드와 대조하여 일치시킨(reconciliation of the content against the code) 마지막 날짜라는 것입니다. 오타를 수정하는 것은 검증이 아닙니다. "어제 검증된 현재 상태(Current, verified yesterday)"와 "6개월 전에 검증된 현재 상태(current, verified six months ago)"는 매우 다르게 읽힙니다.
묘비 (Tombstones)
문서에는 생명 주기(lifecycle)가 있으며, 따라서 반드시 죽음도 있어야 합니다. 만약 문서가 (
이것이 AI에게 왜 좋은가. 열려 있는 백로그(open backlog)는 "아직 완료되지 않은 것이 무엇인가"에 대한 압축적이고 항상 최신 상태를 유지하는 답변이며, 전체를 컨텍스트(context)에 로드하는 비용이 저렴합니다. 해결된 백로그(resolved backlog)는 선례 기반(precedent base)이 됩니다. 버그를 수정하기 전에 모델은 해결된 항목을 grep하여 유사한 무언가가 이미 어떤 방식으로 수정되었음을 발견할 수 있으며, 이를 통해 해결책을 재발명(and not reintroduce the old bug, 즉 이전 버그를 다시 유발하는 것)하지 않을 수 있습니다. 또한 단일 위치 불변성(single-location invariant)은 하나의 파일에서는 "열림(open)"으로, 다른 파일에서는 "닫힘(closed)"으로 동시에 표시되는 일이 없음을 보장합니다. 모델은 인간보다 이러한 상황에 더 취약합니다. 모델에게 있어 git 내의 이러한 트래커는 갑자기(사실 명백하게) MCP를 통한 Jira보다 더 편리합니다. 파일은 한 번의 읽기로 컨텍스트에 로드되고 오프라인에서 grep할 수 있는 반면, MCP를 통한 Jira는 작업당 하나의 도구 호출(tool call), 페이지네이션(pagination), 그리고 JSON 래퍼(wrapper)를 필요로 하여 토큰, 지연 시간(latency), 노이즈를 발생시키며, 이는 컨텍스트 품질과 해결책 품질을 저하시킵니다. 가장 중요한 것은 원자성(atomicity)입니다. 작업은 코드 및 사양(spec)과 동일한 커밋에서 종료되며, 이는 단일한 진실의 트랜잭션(transaction of truth)이 됩니다. 이해관계자와 그들의 대시보드를 위해 Jira 자체를 계속 사용하는 것을 막는 것은 아무것도 없습니다. 정직한 주의 사항을 덧붙이자면, 동기화(synchronization)는 HDD의 범위 밖입니다.
제2부: 핸드오프 (HANDOFF)
SDD가 사양(specifications)을 저장하고 조직화하는 것에 관한 것이라면, 핸드오프(Handoff)는 컨텍스트 관리(context management), 즉 세션 간 및 작업 간에 컨텍스트를 전달하는 것에 관한 것입니다. 여기서 LLM은 사실상 교대 근무(watch)를 이어받는 것입니다. 이를 위해 우리에게는 근무 기록(watch log)이 필요합니다.
인덱스: TRACKS.md
트랙(track)은 하나의 작업입니다. 이는 진화하고, 후속 작업에 의해 대체되며, 하위 작업(새로운 트랙)으로 분할됩니다. 결국에는 단순히 완료됩니다.
루트 specs/ 디렉토리는 활성 트랙의 인덱스인 TRACKS.md를 보유하며, 트랙당 한 줄로 구성됩니다:
- **Server audit fixes** — [handoff](server/specs/handoff-server-audit-fixes.md) —
active, 2026-07-02, next: error channel in concept.service.ts
한 문장으로 요약된 핵심(gist), 핸드오프(handoff)로의 링크, 상태(active / paused — 이유 포함 / blocked — 차단 사유 포함), 마지막 수정 날짜, 다음 단계. 내용은 없습니다 — 오직 포인터(pointers)만 존재합니다. 파일 전체를 읽는 데 1초도 걸리지 않으며, "현재 무엇이 진행 중이고 무엇을 이어받아야 하는가"에 대한 답을 줍니다. 태스크(task) 간의 전환은 곧 핸드오프를 선택하는 것입니다.
핸드오프 (The handoff)
핸드오프 자체는 해당 작업 영역(work zone) 옆에 위치하는 handoff-<slug>.md 파일입니다. 서버 트랙(server track)의 경우 서버 스펙(server specs)에, 제품 트랙(product track)의 경우 루트(root)에 위치합니다. 트랙당 하나의 파일을 생성하며, 해당 위치에서 직접 편집합니다. 버전 관리는 git이 담당합니다. 파일 내부 구성은 다음과 같습니다:
- 세션 시작 시 로드할 항목 (What to load at session start) — 각 항목에 대한 "이유(why)"
트랙 종료 (Closing a track). 핸드오프 (handoff)는 삭제되지만, 증류 (distillation) 과정을 거칩니다. 즉, "재결정하지 말 것 (do not re-decide)"에 대한 결정 사항과 유용한 모든 정보는 영구적인 명세 (specs)로 이동하며, 한두 문장의 요약은 종료된 트랙의 저널인 TRACKS-LOG.md에 기록되고, 인덱스에서 해당 라인은 제거됩니다. 새로운 핸드오프는 템플릿으로부터 복제되어 프롬프트의 작업 내용으로 채워집니다. 오래된 핸드오프는 삭제됩니다. 운영 활동이 저장소(repository)를 심하게 오염시키기 때문입니다.
이 시스템은 에이전트 설정(Claude Code의 경우 CLAUDE.md) 내의 단일 지침에 의해 폐쇄됩니다: 트랙 작업 시작 시 — 해당 핸드오프를 로드할 것; 세션 종료 시 — 핸드오프와 TRACKS.md의 해당 라인을 업데이트할 것. 이 방식은 효과적이며, Claude는 점점 더 규율 (discipline)을 잘 지키게 됩니다.
Spec Kit 및 Kiro와의 차이점
명세 주도 (Spec-driven) 방식은 현재 GitHub Spec Kit 및 Amazon Kiro와 연관되어 있습니다. 그렇다면 HDD는 무엇이 다를까요? Kit와 Kiro의 경우, 명세는 **생성 입력 (generation input)**입니다: 명세 지정 → 계획 (plan) → 작업 (tasks) → 기능 코드 (feature code). 명세는 앞을 내다보며, 머지 (merge) 이후에는 사실상 소멸합니다. 반면 HDD에서 명세는 기존 시스템을 바라보며 반드시 **유지 (remain)**되어야 하는 의무를 가지며, 핸드오프는 완료되지 않은 작업에 대한 진실을 보유합니다. Kit/Kiro의 흐름은 HDD의 plan 장르에 부합하지만, 구현 후에 계획은 소멸(tombstone)하는 반면, 실제 구축된 내용(as-built)은 계속 살아남는다는 차이가 있습니다.
HDD의 차용 요소들: 생명주기 상태 (lifecycle statuses) — ADR/RFC 관행에서; 장르 (genres) — Diátaxis에서; 지도 확대 수준 (map zoom levels) — C4에서; "저장소 내 문서화 + 기계적 감사 (mechanical audit)" — 있는 그대로의 docs-as-code에서 가져왔습니다. 여기서 독창적인 부분은 다음과 같습니다: 모든 파일에 적용되는 "장르 × 상태 × 검증됨 (genre × status × verified)" 트리플, 도달 가능성 불변량 (reachability invariant), 그리고 핸드오프 계층 (handoff layer) 전체입니다.
한계점 (Limitations)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기