토큰 낭비 방지: AI 에이전트를 위한 가볍고 명세 중심적인 (Spec-Driven) 워크플로우
요약
AI 코딩 에이전트의 토큰 낭비를 방지하고 효율성을 높이기 위한 가볍고 명세 중심적인(Spec-Driven) 워크플로우를 제안합니다. 에이전트가 프로젝트 전체를 매번 다시 이해하지 않도록 계획, 탐색, 구현, 검토의 단계를 구조화하여 주의력을 유지하는 것이 핵심입니다.
핵심 포인트
- 에이전트의 토큰 낭비와 주의력 분산 문제 해결
- 계획(Plan), 탐색(Explore), 구현(Implement), 검토(Review)의 단계적 접근
- 도구에 구애받지 않는(Tool-agnostic) 워크플로우 원칙
- 에이전트를 소프트웨어 프로세스로 취급하는 관점의 전환
프로젝트가 성장함에 따라 AI 코딩 에이전트가 집중력을 유지하고, 효율적이며, 검토 가능하도록 만드는 가볍고 명세 중심적인 (Spec-Driven) 워크플로우입니다.
지난 2월부터 저는 AI 에이전트를 활용한 코딩을 위한 작은 워크플로우를 다듬어 오고 있습니다.
더 많은 프로세스를 원해서가 아닙니다.
토큰 (Tokens)을 낭비하는 것에 지쳤기 때문입니다.
실제 사이드 프로젝트에서 코딩 에이전트를 사용해 보셨다면, 아마 그 패턴을 알고 계실 것입니다. 처음에는 모든 것이 거의 너무 좋게 느껴집니다. 프로젝트 규모가 작습니다. 에이전트가 대부분의 내용을 읽을 수 있습니다. "계획하고, 그다음에 구축하라 (Plan, then build)"는 방식이 충분히 잘 작동합니다.
그러다 프로젝트가 성장합니다.
갑자기 동일한 루프가 비용이 많이 드는 것처럼 느껴지기 시작합니다. 에이전트는 필요하지 않은 파일들을 읽습니다. 이미 내린 결정을 다시 발견합니다. 계획과 구현을 뒤섞어 버립니다. 그리고 이 모든 과정을 거친 후에도, 당신의 머릿속에 이미 있던 의도를 놓치기도 합니다.
저는 spec-kit이나 BMAD와 같은 기존의 명세 중심적 (Spec-driven) 워크플로우들을 살펴보았고, 왜 사람들이 그것들을 좋아하는지 이해했습니다. 그것들은 에이전트의 작업에 더 많은 구조를 부여합니다. 하지만 제가 개발하는 방식에 있어서는, 계속 진행하기 전에 거쳐야 할 절차가 너무 많아 다소 무겁게 느껴졌습니다.
워크플로우는 당신을 일반적인 틀에 강제로 맞추는 것이 아니라, 개발자와 특정 프로젝트의 요구 사항에 직접적으로 부합할 때 최상의 성능을 발휘합니다. 중요한 것은 경직된 프레임워크가 아니라, 에이전트가 집중력을 유지하게 만드는 핵심 아이디어입니다.
제가 이러한 경계(Boundaries)를 실험하기 시작했을 때, 저는 opencode를 사용하고 있었습니다. 결국 저는 일상적인 개발 루프를 위한 GUI 경험을 선호하여 codex로 전환했습니다. 최종적으로 저는 이를 기반으로 가볍고 명세 중심적인 워크플로우 확장 기능을 구축했지만, 이 설정의 근저에 깔린 원칙들은 완전히 도구에 구애받지 않습니다(Tool-agnostic). 즉, 당신이 선호하는 거의 모든 현대적인 코딩 에이전트 환경에 적용될 수 있습니다.
이 워크플로우는 몇 가지 집중된 작업들을 중심으로 설계되었습니다:
- 코드를 수정하기 전에 작업을 명시적으로 계획 (Plan) 합니다.
- 기능을 구축하기 전에 더 넓은 범위의 기능을 안전하게 탐색 (Explore) 합니다.
- 한 번에 정확히 하나의 작업만 구현 (Implement) 합니다.
- 해당 단일 작업을 명세 (Spec)에 따라 검토 (Review) 합니다.
- 연구 노트를 작성 (Write) 하고 순수 함수 (Pure functions)를 퍼즈 테스트 (Fuzz-test) 합니다.
근본적인 아이디어는 간단합니다: 에이전트에게 매번 프로젝트 전체를 다시 이해하라고 요구하지 마세요. 저장소 (Repository)를 관료주의적으로 만들지 않으면서도, 다음 단계가 신뢰할 수 있도록 충분한 프로젝트 메모리 (Project memory), 작업 의도 (Task intent), 그리고 품질 검사 (Quality checks)를 제공하는 것입니다.
문제는 코드 생성 (Code Generation)이 아니다
에이전트는 코드를 작성할 수 있습니다. 더 어려운 문제는 주의력 (Attention) 입니다.
저는 에이전트를 의인화의 관점에서 바라보지 않습니다. 그들은 인간의 역할을 수행하는 디지털 직원이 아니라, 소프트웨어 프로세스 (Software processes)입니다. 그들은 특정 입력 상태 (Input state)를 읽고, 추론된 의도 (Inferred intent)를 바탕으로 출력 상태 (Output state)를 생성합니다.
에이전트를 데이터 파이프라인 (Data pipelines)으로 바라보면, 기본 설정된 계획/구축 (Plan/build) 루프가 명백히 결함이 있다는 것을 알 수 있습니다. 이 루프는 단일 실행 루프가 한 번에 너무 많은 변수를 처리하도록 요구합니다:
코드베이스 이해 (Understand Codebase) ➔ 제품 의도 추론 (Infer Product Intent) ➔ 변경 사항 설계 (Design Change) ➔ 구현 (Implement) ➔ 테스트 (Test) ➔ 검토 (Review) ➔ 컨텍스트 기억 (Remember Context)
프로젝트 데이터 발자국 (Data footprint)이 작을 때는 이 방식이 작동합니다. 하지만 프로젝트에 이력 (History)이 쌓이기 시작하면 상황은 훨씬 악화됩니다.
에이전트는 컨텍스트 (Context)를 너무 광범위하게 처리합니다. 실행 (Execution) 대신 방향 설정 (Orientation)에 토큰 예산 (Token budget)을 소모합니다. 이전 세션에서는 명확했던 결정 사항들을 잊어버립니다. 그리고 대부분의 의도가 휘발성인 채팅 기록 (Chat history)에 존재하기 때문에, 모든 새로운 세션은 더 약하고 모호해진 입력 파라미터 (Input parameters)와 함께 시작됩니다.
그 결과는 단순히 작업 속도가 느려지는 것에 그치지 않습니다. 에이전트의 제한된 컨텍스트 창 (Context window)이 잘못된 곳에 사용되고 있기 때문에 작업의 질 자체가 떨어집니다. 이것은 입력 필터링 (Input filtering)의 문제입니다.
단위는 작업 명세 (Task Spec)이다
이 루프의 형태를 바꾸려면, 에이전트가 수행하는 모든 의미 있는 작업은 작업 명세 (Task spec)로 시작해야 합니다. 거대한 요구사항 문서 (Requirements document)도 아니고, 영구적인 신뢰할 수 있는 단일 출처 (Source of truth)도 아닙니다. 그저 일시적인 계약 (Temporary contract)일 뿐입니다.
작업 명세 (Task spec)는 매우 모듈화되어 있으며 일반적으로 다음을 포함합니다:
| 구성 요소 | 목적 |
|---|---|
| 기능적 목표 및 비목표 (Functional Goal & Non-Goals) | 무엇을 건드리지 말아야 할지에 대한 명시적인 경계 |
| ... |
그 작은 문서 하나가 개발 루프 (Development loop)의 전체적인 느낌을 바꿉니다.
한 세션에서 작업을 계획하고, 다른 세션에서 구현하며, 세 번째 세션에서 검토할 수 있습니다. 구현 에이전트 (Implementation agent)는 사용자의 의도를 추측할 필요가 없습니다. 검토자 (Reviewer)는 가공되지 않은 git diff로부터 의도를 재구성할 필요가 없습니다. 다음 세션에서는 왜 변경이 이루어졌는지 알아내기 위해 지저분한 채팅 기록을 뒤질 필요가 없습니다.
작업은 인수인계의 수단이자, 더 중요하게는 **컨텍스트 필터 (Context filter)**가 됩니다. 에이전트가 프로젝트 전체를 헤매게 두는 대신, 작업은 에이전트에게 특정 내용은 읽고 다른 내용은 무시해야 할 엄격한 이유를 제공합니다. 진정한 토큰 절약은 바로 여기서 나옵니다. 즉, 컨텍스트를 의도적으로 만드는 것입니다.
의도 명세 (Intent Specs) vs. 프로젝트 명세 (Project Specs)
워크플로우를 빠르게 유지하고 싶다면 매우 중요한 차이점이 하나 있습니다: 작업 명세는 의도 명세 (Intent specs)이지, 프로젝트 명세 (Project specs)가 아닙니다.
이들은 특정 시점에 프로젝트에 무엇이 필요했는지를 설명합니다. 작업이 계획, 구축 및 검토되는 동안 매우 가치 있는 역할을 합니다. 하지만 이들이 영원히 유지 관리해야 하는 불멸의 살아있는 아키텍처 문서가 되어서는 안 됩니다.
만약 작업이 지속적인 지식을 생성한다면, 그 지식은 즉시 지속 가능한 어딘가로 이동해야 합니다:
💡 지속적인 지식이 위치해야 할 곳:
소스 코드 주석 (Source code comments) •MAP.md파일 • 패키지 맵 (Package maps) • 공식 아키텍처 문서 •README업데이트
작업은 작업이 진행되는 동안 _무엇이 일어나야 하는지_를 말합니다. 프로젝트는 _무엇이 사실이 되었는지_를 기록합니다. 이 둘을 분리함으로써 프로세스가 번거로운 작업처럼 느껴지는 것을 방지할 수 있습니다.
MAP.md는 에이전트의 첫 5분입니다
이 퍼즐의 또 다른 핵심 요소는 프로젝트나 패키지의 루트에 MAP.md 파일과 같은 오리엔테이션 레이어 (Orientation layer)를 유지하는 것입니다. 이 파일은 프로젝트가 무엇을 하는지, 중요한 파일들이 무엇을 담당하는지, 그리고 특정 동작이 어디에 존재하는지를 설명합니다.
이것은 코드를 읽는 행위를 대체하는 것이 아니라, 처음 코드를 읽을 때 덜 무작위적으로 느껴지게 만들어 줍니다.
이는 혼자 작업할 때도 유용합니다. 누구나 특정 파일에서 몇 주간 떨어져 있으면 특정 동작이 어디에 있는지 잊어버리기 때문입니다. 에이전트(Agent)에게 이것은 엄청난 승수 효과 (Force Multiplier)를 제공합니다. 에이전트가 거대한 저장소 (Repository)를 뒤지며 수천 개의 토큰을 낭비하기 전에, 지도를 먼저 읽고 믿을 수 없을 정도로 정확한 첫 번째 추측을 할 수 있게 합니다.
에픽 (Epics): 대규모 기능이 하나의 거대한 프롬프트가 되는 것을 방지
더 큰 규모의 기능을 구현할 때는 컨테이너인 **에픽 (Epic)**을 사용하는 건강한 워크플로우가 필요합니다. 에픽은 단순히 거대한 작업이 아닙니다. 에이전트가 구축과 계획을 동시에 수행하지 않도록 막아주는 탐색 경계 (Exploration Boundary)입니다.
워크플로우는 자연스럽게 다음과 같은 별도의 단계로 분리됩니다:
- 더 넓은 목표를 위한 에픽을 생성합니다.
- 코드베이스 (Codebase)를 탐색하고 격리된
context.md를 작성합니다. - 에픽을 매우 집중된 작은 단위의 작업들로 나눕니다.
- 한 번에 하나의 격리된 작업만을 구현하고 검토합니다.
탐색은 그 자체의 산출물 (Artifact)을 가집니다. 구현은 작은 작업들로 나뉩니다. 검토는 비교할 수 있는 구체적인 대상을 가집니다. 에픽 컨텍스트 (Epic context)는 해당 특정 작업 영역에 대한 공유 메모리 (Shared memory)가 되어, 에이전트가 코드를 작성하는 동안 전체 시스템 아키텍처 (System architecture)를 머릿속에 담아두려고 시도하는 것을 방지합니다.
검토 파이프라인 (Review Pipeline): 실행 상태의 디커플링 (Decoupling)
검토 단계는 이러한 입출력 (Input/Output) 접근 방식의 진가가 발휘되는 지점입니다. 작성된 작업 명세 (Task spec)가 없다면, 에이전트의 출력을 평가할 때 흔히
단일 에이전트 세션(Agent session)에 코드 구현과 자신의 변경 사항 검토를 동시에 요청하면, 컨텍스트 윈도우 (Context window)가 혼탁해집니다. 생성 편향 (Generation bias)이 평가를 오염시키기 때문입니다.
작업 명세 (Task spec)와 그 결과로 나온 코드 디프 (Code diff)를 쓰기 권한이 없는 새롭고 격리된 프로세스로 전달함으로써, 평가의 성격을 바꿀 수 있습니다. 이 프로세스의 처리 루프 (Processing loop)는 다음 코드 줄을 어떻게 작성할지 고민하는 것이 아니라, 디프를 데이터로 파싱하고 이를 명세 요구 사항 (Spec requirements), 기술 계획 (Technical plan), 그리고 검증 증거 (Verification evidence)에 따라 검증하는 것에만 집중합니다.
**생성 파이프라인 (Generation pipeline)**을 **검증 파이프라인 (Validation pipeline)**으로부터 분리하면 훨씬 더 높은 예측 가능성을 얻을 수 있습니다.
프로젝트에 맞춘 계약 (Contract) 설계
모든 프로젝트는 서로 다르기 때문에, 좋은 워크플로우 (Workflow)는 마법 같거나 경직되어서는 안 됩니다. 워크플로우의 철학을 특정 기술 스택 (Stack)에 매핑해 주는 어댑터 레이어(Adapter layer), 즉 로컬 계약(예: .agents/workflow-config.md)이 필요합니다.
어떤 프로젝트는 make test를 사용하고, 다른 프로젝트는 npm test를 사용할 수 있습니다. 어떤 리포지토리 (Repository)는 docs/research/ 아래에 연구 노트를 저장할 수도 있고, 어떤 곳은 노트를 아예 생략할 수도 있습니다. 어댑터는 단순히 이 워크플로우가 이 특정 프로젝트에서 어떻게 작동하는지를 알려줌으로써, 모든 리포지토리에 동일한 레이아웃을 강요하지 않고도 워크플로우의 높은 이식성 (Portability)을 유지해 줍니다.
핵심 요약
AI 코딩을 효율적으로 만들기 위해 거대한 프로젝트 관리 프레임워크를 채택할 필요는 없습니다. 그저 에이전트에게 몇 가지 유용한 경계 (Boundaries)를 제공하기만 하면 됩니다.
- 매핑 (Map): 에이전트가 즉시 방향을 잡을 수 있도록 프로젝트 레이아웃을 매핑합니다.
- 작성 (Write): 구현을 시작하기 전에 엄격한 의도 (Intent)를 기록합니다.
- 구축 (Build): 한 번에 정확히 하나의 격리된 작업 (Isolated task)만 수행합니다.
- 검토 (Review): 출력을 검증해야 할 데이터로 취급하며, 작성된 의도에 따라 검토합니다.
만약 에이전트 세션이 길고 비용이 많이 들며 좌절감을 주는 재발견 루프 (Rediscovery loops)로 계속 변질된다면, 에이전트에게 코드 한 줄을 생성하도록 요청하기 전에 작업에 대한 명시적인 계약 (Explicit contract)을 먼저 부여해 보십시오.
이 구성 계층 (Configuration layer)과 작업 생명주기 (Task lifecycle)가 구조적으로 어떻게 구현되었는지 예시를 확인하고 싶다면, gtindo/gt-workflow에 있는 오픈 소스 저장소 패턴을 확인해 보십시오.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기