채팅에서 백로그로의 변화: 나의 AI 작업 계획이 3개월 동안 어떻게 진화했는가
요약
작성자가 3개월 동안 채팅 기반의 작업 방식을 Claude Code, Codex, Grok과 같은 코딩 에이전트를 활용한 Postgres 백로그 시스템으로 진화시킨 과정을 다룹니다. 계획이 채팅창에 머물 때 발생하는 휘발성과 작업 손실 문제를 지적하며, 계획을 명시적인 사양(spec)으로 관리하는 중요성을 강조합니다.
핵심 포인트
- 채팅 기반 계획은 휘발성이 높아 작업 손실을 초래함
- Claude Code, Codex 등 에이전트를 활용한 백로그 시스템 구축
- 계획(Planning)은 실시간 결정의 불확실성을 줄이는 압축 과정임
- 에이전트의 높은 처리량을 뒷받침하기 위한 체계적인 관리 필요
3개월 전, 나의 전체 작업 관리 시스템은 탭을 닫으면 사라져 버리는 채팅창이었습니다. 오늘날 그것은 Claude Code, Codex, Grok이라는 세 가지 서로 다른 코딩 에이전트(coding agents)가 자율적으로 작업을 가져가고, 기여도를 표시하며, git 히스토리에 따라 작업을 완료하는 Postgres 백로그(backlog)입니다. 나는 프로젝트 관리 시스템을 만들기로 결정한 적이 없습니다. 그저 벽에 부딪히고, 그것을 패치(patching)하고, 그 패치가 드러낸 다음 벽에 부딪히는 과정을 반복했을 뿐입니다.
하지만 이 전체 과정을 깔끔하게 읽어낼 수 있는 방법이 있으며, 그것은 단 하나의 변수로 귀결됩니다. 바로 계획이 어디에 존재하는가입니다. 이 부분을 주목하면, 왜 당신이 아마도 끝에 도달하기 훨씬 전에 멈추고 싶어 할 것인지를 포함하여 모든 단계가 이해될 것입니다.
설정 (The setup)
나는 Furnace라고 이름 붙여진 128GB Strix Halo 박스 위에서 Nexus라는 이름의 셀프 호스팅(self-hosted) 개인 데이터 플랫폼을 운영하며, 약 100개의 리포지토리(repos)에 둘러싸여 있습니다: MCP 서버, 인제스션 파이프라인(ingestion pipelines), iOS 앱, 콘텐츠 툴링 등입니다. 나의 실행 도구는 Claude Code와 Codex CLI입니다. 작업은 폭발적으로 일어납니다. 봄철 35일 동안 나는 이 리포지토리들에 걸쳐 대략 557K줄의 코드를 배포했으며, 이러한 처리량(throughput)은 각 계획 방식(planning approach)을 차례로 무너뜨린 압박 요인이었습니다. 더 차분한 속도라면 나중에 똑같은 벽에 부딪히겠지만, 어쨌든 부딪히게 될 것입니다.
1단계: 계획이 채팅에 존재하는 단계 (3월 중순~말)
시작 단계에서는 작업 시스템이 없었습니다. 계획 자체가 곧 대화였습니다. 나는 채팅창을 열고, 아키텍처 문제에 대해 소리 내어 생각하고, 일관된 결과물에 도달하면 그것을 구축하러 갔습니다. 계획의 산물은 기록된 것이 아니라 내 머릿속의 더 나은 멘탈 모델(mental model)이었습니다.
참고로, 이것은 모든 모범 사례(best-practice) 가이드가 당신에게 하라고 말하는 바로 그 방식이며, 그것은 옳습니다. 수학적 계산은 잔혹하고 잘 알려져 있습니다: 만약 Claude가 단일 결정에 대해 80%의 확률로 올바른 판단을 내린다면, 20개의 결정 지점이 있는 기능은 0.8^20의 확률로 모든 결정이 맞게 됩니다. 이는 약 **1%**에 불과합니다. 계획(Planning)은 이 20개의 실시간 결정들을 이미 결정된 사항들이 포함된 검토된 사양(spec)으로 압축합니다. 나는 계획 우선(plan-first) 본능을 결코 포기하지 않을 것입니다. 그것은 이 전체 이야기에서 결코 변하지 않은 유일한 습관입니다.
문제는 더 국한되어 있었습니다. 스레드(thread)가 끝나면 계획이 증발해 버린다는 것이었습니다. 3월 말, Nexus를 위한 멀티 에이전트 시스템 (multi-agent system)을 설계하던 세션에서는 정말 훌륭한 아키텍처 (architecture)가 도출되었습니다. 부하 상황에서의 결정론적 동작 (deterministic behavior), 통제 불능 상태로 빠지는 대신 스스로를 조절하는 에이전트 (agents), 적응형 임계값 (adaptive thresholds) 등이 그것이었습니다. 하지만 이 중 그 어떤 것도 다음 날 아침에 바로 실행에 옮길 수 있는 곳에 남아 있지 않았고, 오직 나의 기억과 스크롤백 버퍼 (scrollback buffer)에만 존재했습니다. 하루에 기능 하나씩 처리하는 속도라면 버틸 수 있겠지만, 나의 속도에서는 이것이 작업 손실을 직접적으로 초래하는 손실적 (lossy)인 방식으로 작용했습니다.
장벽: 채팅 기록에만 존재하는 계획은 나중에 실행할 수 없고, 서로 우선순위를 매길 수 없으며, 대화를 기억하는 버전의 당신 외에는 그 무엇에게도 전달될 수 없습니다.
2단계: 계획이 파일에 존재함 (4월 중순~하순)
첫 번째 지속 가능한 해결책은 부끄러울 정도로 간단했습니다. 각 리포지토리 (repo)에 TODO.md 파일을 만드는 것이었습니다. 하지만 내가 정착한 '구조'야말로 가져갈 만한 가치가 있는 부분인데, 왜냐하면 그것은 단순한 체크리스트가 아니었기 때문입니다. 각 항목은 하나의 작은 명세 (spec)였습니다. 내 broadside/TODO.md에 여전히 남아 있는 실제 사례를 하나 소개하겠습니다.
## 발행 작업에서의 멱등성 (Idempotency on publish operations)
**상태 (Status):** 캡처됨 — 2026-04-26 reality-sync 세션 중에 표시됨.
...
다음 네 가지 요소가 이 리스트를 단순한 체크박스 이상의 무게감을 갖게 만들었습니다.
- 상태 + 날짜. 모든 항목에는 그것이 캡처된 시점이 찍혀 있습니다. 사소해 보이지만, 나중에 정보의 노후화 (staleness)를 판단할 수 있게 해주는 핵심 요소입니다.
- 우선순위가 아닌 트리거 (Trigger). P1/P2/P3 대신, 각 항목은 어떤 '조건' 하에서 긴급해지는지를 기록합니다. "에이전트가 감독 없이 발행하도록 허용하기 전에"라는 문구는 미래의 나에게 이것을 언제 꺼내 써야 할지 정확히 알려줍니다. 반면 "높은 우선순위"라는 말은 아무것도 알려주지 않습니다.
- 작업이 미리 결정됨. 그 번호가 매겨진 리스트는 내가 문제를 가장 잘 이해했을 때 캡처된 계획입니다. 3주 후에 Claude Code에게 이 리스트를 건네주면, 20개의 결정 사항은 이미 내려져 있습니다. 즉, 1단계의 모든 가치가 지속되는 것입니다.
- 리스크가 기록됨. "재발행은 때때로 의도적인 것이다"라는 메모는 내가 잊어버렸을 법한, 그리고 에이전트가 짓밟아버렸을 법한 정확한 예외 케이스 (edge case)입니다.
반복되는 문구인 **"reality-sync session (현실 동기화 세션)"**에 주목하십시오. 구체적으로 이는 보통 계획 수립 단계(planning block) 직전에 진행하는 20분 정도의 과정이었습니다. 각 저장소(repo)의 TODO.md를 최근 git log와 나란히 열어두고, 커밋 내역에서 이미 배포된 것으로 확인되는 항목은 닫으며, 아직 열려 있는 항목은 한눈에 오래된 것과 살아있는 것을 구분할 수 있도록 날짜를 재설정하는 작업입니다. 정기적으로 실제 상황(ground truth)과 계획을 대조하는 것 — 이 습관이 결국 3단계(Phase 3)의 모든 것을 만드는 씨앗이 되었습니다.
장벽: TODO.md는 저장소별로 존재합니다. 약 100개의 저장소를 관리하다 보니, 모든 저장소를 통틀어 다음에 무엇을 작업해야 할지 알려주는 단일 인터페이스가 없었고, 전역적으로 우선순위를 정할 방법도 없었으며, 에이전트가 큐(queue)로서 가져올(pull from) 수 있는 것도 없었습니다. 계획은 내구성은 있었지만 파편화되어 있었습니다.
3단계: 계획이 게이트가 있는 데이터베이스에 존재함 (5월 초~중순 이후)
이것은 구조적인 도약입니다. 작업 상태(task state)가 평면 파일(flat files)에서 벗어나 Nexus의 Postgres로 이동하여 **운영자 백로그 (Operator Backlog, OB)**가 되었습니다. 이는 단순한 목록이 아닌, 실제 유입부터 실행까지의 라이프사이클(lifecycle)을 갖게 되었음을 의미합니다.
구조:
- 작업은 아직 실제 작업이 아닌
pending상태의 **후보(candidate)**로 유입됩니다. 후보는 내가 Discord의#pmo-review흐름을 통해 승인하기 전까지는 OB 항목이 되지 않습니다. - 승인되면
operator_backlog_items에OB-#####행이 생성됩니다. - 항목들은 상태 레인 (status lanes) —
requires_triage(분류 필요) →requires_decision(결정 필요) →requires_investigation(조사 필요) →requires_clickops(수동 조작 필요) →autonomous_safe(자율 실행 가능) — 을 통해 이동하며, 에이전트들이 이 레인들을 처리(drain)합니다. - 모든 커밋은 자신의 OB ID를 참조하여, 백로그를 git 히스토리에 직접 연결합니다.
OB-27081 H2/M2 — close /register on RS는 내 여러 저장소에 걸쳐 나타나는 실제 커밋 제목입니다.
차선(lanes)이 흥미로운 부분인데, 이는 Anthropic이 Building Effective Agents에서 구분한 개념과 거의 정확하게 일치합니다. 즉, 에이전트가 자율적으로 수행할 수 있는 작업과 "되돌릴 수 없는 행동을 하기 전"에 인간의 체크포인트가 필요한 작업 사이의 차이입니다. autonomous_safe는 에이전트가 그냥 수행할 수 있는 차선입니다. requires_decision은 내가 필요한 차선입니다. 백로그(backlog)는 단순한 저장소가 아닙니다. 인간의 판단이 얼마나 필요한지에 따라 작업을 분류하는 라우터(router)입니다.
하지만 이 단계에서 가장 중요한 단일 추가 사항은 차선이 아니었습니다. 그것은 바로 **승인 게이트(approval gate)**였습니다. 2단계에서는 무차별적으로 캡처했습니다. 작업처럼 보이는 것은 무엇이든 기록되었습니다. 3단계에서는 필터를 추가했는데, 필터가 없을 때 실패하는 것을 직접 목격했기 때문에 그 이유를 정확히 알고 있습니다. 6월 3일의 실제 세션 로그에서 가져온 내용입니다:
requires_triage+requires_decision큐 소진 (19개 항목 → autonomous_safe/closed/ignored); 8개의 결정 완료; 자동 분류기(auto-filer)가 서술 중의 부드러운 산문(예: "blocked on" → 정규 표현식(regex)을 강화하기 위해 OB-4736으로 분류)을 과도하게 캡처함을 발견; 우선순위 차선 기아(priority-lane starvation) 현상(skillopt-train이 PM 작업을 기아 상태로 만듦)을 OB-4715에서 진단.
자동화된 프로세스가 내 세션 서술에서 작업을 스크래핑(scraping)하다가, 대화 중에 사용된 "blocked on"이라는 문구를 실제 차단 요소(blocker)로 오해한 것입니다. 시스템이 스스로의 백로그에 쓰레기 데이터를 파일링하고 있었던 것입니다. 해결책(OB-4736) 자체도 게이트를 통해 OB 항목으로서 파일링되었습니다. 백로그는 자기 교정(self-correcting) 능력을 갖추게 되었습니다. 자체적인 입력 버그가 다른 모든 것과 동일한 기질(substrate) 내에서 추적됩니다.
동일한 로그 항목은 이 단계에서 정착된 일상적인 리듬을 보여줍니다. "큐 소진(Draining queues)"은 말 그대로 반복되는 작업이 되었습니다. 특정 차선의 항목들을 가져와서, 결정을 내리고, autonomous_safe로 이동시키거나 종료하는 것입니다. 한 번의 훑기(sweep)에 8개의 결정이 이루어졌습니다. 이는 마치 온콜(on-call) 근무처럼 읽히는데, 실제로 그것이 바로 그것이기 때문입니다. 나는 운영자이고, 백로그는 큐(queue)입니다.
장벽: 거버넌스 게이트(governance gate)가 있는 데이터베이스 기반 큐(queue)는 훌륭하지만, 이는 체계적인 유입(intake)과 누군가 차선(lanes)을 비워낸다는 것을 전제로 합니다. 볼륨이 커지면서 제가 병목 현상(bottleneck)이 되었습니다. 백로그는 제가 개인적으로 실행할 수 있는 양보다 더 많은 작업을 담을 수 있었습니다.
4단계: 여러 에이전트가 백로그를 비움 (5월 말 → 현재)
가장 최근의 변화는 작업이 어떻게 추적되는지에 관한 것이 아닙니다. 그 부분은 3단계에서 해결되었습니다. 핵심은 누가 작업을 실행하며, 본인 혼자가 아닐 때 어떻게 책임을 유지(accountable)하느냐에 관한 것입니다.
이제 OB 백로그는 여러 러너(runner)가 가져가는 공유 작업 큐(work queue)입니다: Claude Code, Codex, 그리고 Grok이 있으며, 각 에이전트에는 태그가 붙어 있어 사후에 어떤 에이전트가 어떤 항목을 처리했는지 알 수 있습니다. 3단계에서 사용했던 것과 동일한 상태 차선(status lanes)이 안전을 보장합니다. 에이전트는 오직 autonomous_safe에 있는 작업만 가져가며, requires_decision에 머물러 있는 작업은 절대 건드리지 않습니다. 충돌은 임대(lease) 방식으로 처리됩니다: 러너가 항목을 점유하면, 두 번째 러너는 이미 점유되었음을 확인하고 건너뜁니다. 만약 첫 번째 에이전트가 중단되면 임대가 만료되어 항목은 다시 자유 상태가 됩니다.
귀속 스탬핑(Attribution stamping)은 이 시스템을 지탱하는 핵심 요소입니다. 각 커밋(commit)과 OB 해결 사항이 러너별로 태그가 지정되기 때문에, 세 명의 에이전트가 약 100개의 리포지토리(repo)를 다루더라도 "누가 이것을 왜 했는가"에 대해 답변할 수 있습니다. 규칙은 사람이 커밋을 소유하되, 러너 태그는 백로그에만 존재하며 git 히스토리에는 절대 위조되어 들어가지 않는 것입니다. 각 실행은 고립된 개별 git 워크트리(worktree)에서 실행되므로, 병렬로 작동하는 에이전트들이 동일한 파일에 동시에 접근하는 일은 발생하지 않습니다.
이것이 전체 메커니즘입니다. 이것이 저에게 이론이 아닌 현실로 다가온 순간입니다. OB-1623은 "보고서 전달 시 모델 출처(model-provenance) 푸터(footer)를 연결할 것"이라는 작업이었습니다. Claude Code는 5월 30일에 이 작업을 수락하고 작업을 시작했습니다 — 그러더니 완료하기를 거부했습니다. 이 작업의 전제가 잘못되었다는 것을 발견한 것입니다. 작업에서 명시한 함수는 10개의 호출자가 있는 공유 프리미티브(primitive)였고, 해당 함수가 가리키는 파일에는 푸터에 필요한 데이터조차 들어있지 않았습니다. 다른 9개의 호출 지점을 조용히 망가뜨릴 수정 사항을 강제로 적용하는 대신, 에이전트는 해당 항목을 차단(block)하고, 수정된 전제 조건을 새로운 OB로 등록한 뒤, 정확한 이유를 설명하는 노트를 남기고 자신의 점유를 해제했습니다. 이틀 후, 전제 조건이 반영된 후, Codex는 Claude Code의 실행 기록과 공유된 메모리 없이 — 오직 백로그(backlog) 행과 차단 노트만을 가지고 — 동일한 OB를 처음부터 이어받아 엔드 투 엔드(end to end)로 완료했습니다: PR이 병합되었고, Nexus가 c584d2a8에서 Furnace와 Crucible 모두에 배포되었으며, 라이브 푸터 렌더러가 올바른 마커를 방출하는 것을 확인했습니다.
이 시퀀스를 다시 읽어보십시오. 왜냐하면 이것이 단 하나의 항목에 담긴 4단계(Phase 4)의 핵심이기 때문입니다. 두 개의 서로 다른 모델이 인간의 중재 없이 인수인계를 수행했고, 시스템이 그 간극을 넘어 스스로를 교정(self-corrected)했습니다 — 한 에이전트가 잘못된 일을 하기를 거부한 것이 다른 에이전트의 깔끔한 승리로 이어졌습니다. 거부의 이유가 두 에이전트 모두가 볼 수 있는 단 한 곳에 기록되었기 때문입니다. 이것은 병렬 처리(parallelism)가 아닙니다. 이것은 백로그가 유능한 엔지니어링 팀이 하는 일을 수행하는 것입니다: 잘못된 가정이 배포되기 전에 잡아내고, 그 수정 사항을 다음 작업자를 위해 전달하는 것입니다.
그 밑바탕에 깔린 패턴
전체 흐름을 표로 정리했습니다. "해결된 문제(Fixed)"와 "새로운 한계(New limit)" 열을 하나의 사슬로 읽으십시오. 각 단계의 새로운 한계가 다음 단계가 존재해야 하는 이유가 됩니다.
| 단계 | 계획이 머무는 곳 | 해결된 문제 | 새로운 한계 |
|---|---|---|---|
| 1. 대화형 (Conversational) | 채팅 기록 (Chat history) | 의사결정 품질 (계획 우선) | 계획이 증발함 |
| ... |
이 표에서 뽑아낼 만한 두 가지 사항이 있습니다.
첫째: 대부분의 가치를 얻기 위해 반드시 4단계(Phase 4)에 도달할 필요는 없습니다. 2단계(Phase 2)의 TODO.md 형식 — 상태(status), 트리거(trigger), 사전 결정된 단계(pre-decided steps), 리스크(risks) — 만을 취하십시오. 그 외에는 필요 없습니다. 이것은 텍스트 파일이며, 비용이 들지 않습니다. 그리고 그 이후의 모든 과정은 단지 캡처된 계획(captured-plan)이라는 동일한 아이디어를 더 많은 리포지토리(repos)와 더 많은 실행자(executors)로 확장하는 것뿐입니다. 만약 이 글에서 한 가지만 가져간다면, 바로 이것을 가져가십시오.
둘째, 그리고 이 부분은 제가 가장 강력하게 옹호할 부분입니다: 하나의 습관이 네 단계 모두에 걸쳐 있으며 도구(tooling)가 생기기 전부터 존재했습니다. 나는 계획을 세우기 전에 실제 상태(real state)를 바탕으로 다시 근거를 잡습니다. 이것은 4월의 "현실 동기화 세션(reality-sync session)"입니다. 후보군을 진실과 대조하여 조정하는 3단계(Phase 3)의 게이트(gate)이기도 합니다. 그리고 분석 과정에서 요령을 피우는 것을 발견했을 때, 거의 토씨 하나 틀리지 않고 이 습관이 나타납니다. 제가 6월에 진행한 워크플로우 리뷰(workflow review)에서 누군가 가공된 테이블 대신 편리하게 미리 요약된 뷰(views)에 의존했을 때, 저의 반응은 단호했습니다:
이것은 PMO 프로세스나 프로젝트 개시(project initiation)에 대해 전혀 인지하지 못하고 있는 것 같습니다... 실제 원시 데이터 수집 테이블(raw ingestion tables)을 모두 살펴보았습니까?
이 4개월 동안 도구(tooling)는 더 정교해졌지만, 원칙은 변하지 않았습니다: 검증된 현재 상태(verified current state)를 바탕으로만 계획하십시오. 결국 백로그(backlog)는 그 상태를 유지하기 위해 제가 찾아낸 가장 내구성이 있는 장소일 뿐입니다. 그래야만 저나 에이전트(agent)가 계획을 세울 때 추측이 아닌 기초부터 시작할 수 있습니다. 모두가 인용하는 '계획 우선(plan-first)'의 수학적 논리는 계획이 진실된 전제(premises) 위에 놓여 있을 때만 유효합니다. 오래된 컨텍스트(context)를 기반으로 완벽하게 구조화된 명세서(spec)는 계획이 아예 없는 경우만큼이나 확실하게 20개의 의사결정 모두를 실패하게 만듭니다. 단지 더 빠르게, 그리고 더 확신을 가지고 실패할 뿐입니다. 여기서의 모든 단계는 근본적으로 동일한 질문에 대한 더 나은 답변이었습니다: 계획이 의존하는 진실을 어디에 보관할 것인가?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기