큰 작업의 거처를 챗봇 UI에서 로컬 하네스로 — 옆에서 함께 옮긴 이야기
주변에 외부 챗봇의 커스텀 기능 위에서 꽤 진지한 작업을 굴려보던 분이 한 분 있었습니다. 그분은 이런저런 인스트럭션을 정성껏 다듬고, 첨부 파일도 매번 빠짐없이 올리면서, 거의 같은 흐름을 반복하고 있었습니다. 그런데 옆에서 한참 지켜보다 보니, 같은 자리에서 같은 답답함이 반복된다는 생각이 들었습니다. 처음에는 모델 탓이라고만 여겼는데, 가만히 그분의 작업을 들여다보니 모델의 문제가 아니라 대화창이라는 그릇 자체의 한계였습니다.
그래서 그분과 함께 작업의 거처를 조금씩 옮기기로 했습니다. 그분이 매번 부딪히던 어떤 다단계 창작 워크플로 하나를, 챗봇 UI에서 빼내어 Claude Code 위에 로컬 하네스로 다시 짜는 일을 옆에서 도왔습니다. 이 글은 옆에서 본 그분의 네 가지 막힘, 그리고 그것을 풀기 위해 함께 갖춰둔 여섯 가지 구조에 대한 회고입니다.
옆에서 본 네 가지 막힘
큰 작업을 챗봇 UI 안에서 굴리던 그분은 매번 같은 자리에서 막혔습니다. 돌이켜 생각해보면 그 막힘은 네 갈래로 나뉘었습니다.
분기와 diff가 없는 한 줄 대화
그분은 같은 단계에서 안 A, 안 B, 안 C를 나란히 비교하고 싶어 했습니다. 그런데 챗봇은 결국 한 줄로 흐르는 대화입니다. 안 A를 시도해보고 마음에 안 들면, 새 대화를 열어 처음부터 인스트럭션을 다시 쌓아 올리는 식으로 갈라치기를 해야 했습니다.
그분은 어제 만든 안과 오늘 만든 안을 한 화면에서 차분히 비교하고 싶어 했는데, 그런 시각화도 어디에도 없었습니다. 결국 안끼리의 거리감은 그분의 머릿속에만 남고, 어딘가에 정직하게 보존되지는 않았습니다.
매번 처음부터 시작하는 세션
외부 챗봇의 커스텀 기능은 본질적으로 이번 인스트럭션 + 이번 첨부 파일이라는 평면 위에서 매 대화를 처음부터 다시 시작합니다. 그분은 어제 합의했던 룰, 어제 폐기했던 안, 어제 내려둔 결정사항을 다음 대화에서 또 처음부터 설명해야 했습니다.
옆에서 보기에 누적되는 정형 룰이 많아질수록 이 비용은 가팔라졌습니다. 체크리스트, 금기 영역, 정형 문구 사전 같은 것들이 자동으로 매 대화 앞에 깔리지 않으니, 결국 그분 본인이 매번 같은 컨텍스트를 인간 캐시처럼 다시 떠다 부어야 했습니다.
단일 거대 프롬프트의 일관성 문제
"한 번에 다 해줘" 식의 거대 프롬프트는, 모델이 중간에 게을러지거나 한쪽으로 치우치게 만든다는 생각이 들었습니다. 그분의 결과물도 그렇게 흘러간 적이 적지 않았습니다. 단계마다 잘게 쪼개서 각 단계 전용 전문가 프롬프트를 박는 편이 품질이 훨씬 안정적이라는 것이, 옆에서 보면서 점점 분명해졌습니다.
그런데 챗봇 UI 안에서는 그 모든 단계가 어쩔 수 없이 하나의 대화 흐름에 들어옵니다. 그분은 단계와 단계 사이를 정말로 분리하는 것이 아니라, 한 흐름 안에서 마음속으로만 분리하고 있던 셈이었습니다.
강제 게이트가 없는 흐름
"이 단계가 끝나면 X가 반드시 들어있는지 확인하고, 다음 단계로 넘어가라"는 강제 게이트가 챗봇 UI 안에는 없었습니다. 모델이 알아서 챙겨주기를 기대해야 하는데, 정형화된 룰이 많은 콘텐츠 파이프라인일수록 길어진 대화에서 누락이 잦았습니다.
그분이 한두 번은 손으로 잡아낼 수 있었지만, 같은 누락이 매번 반복되는 것을 옆에서 보다 보니, 결국 도구의 형태 자체를 바꿔야 한다는 결론에 도달하게 되었습니다.
함께 갖춰둔 여섯 가지 패턴
네 가지 막힘을 그대로 두고는 다음 단계로 나아갈 수가 없었습니다. 그래서 그분과 함께 Claude Code 위에 작업 전용 하네스를 따로 짜기 시작했고, 짜는 동안 자연스럽게 여섯 가지 패턴이 자리를 잡았습니다.
패턴 1 — git 폴더가 작업 단위가 된다
함께 가장 먼저 정한 것은 작업 단위 하나에 폴더 하나라는 규칙이었습니다. 폴더 안에는 단계별 산출물이 파일 하나씩 쌓이도록 했습니다.
{작업단위}/
├─ 0_brief.md # 단계 0 산출물
├─ 1_outline.md # 단계 1 산출물
├─ 2_draft.md # 단계 2 산출물
└─ meta.json # 상태 스냅샷
그리고 분기는 git branch로, 안끼리의 비교는 git diff로 옮겨두었습니다. 어제 폐기한 안도 그분의 머릿속에만 남기지 않고 commit 히스토리에 정직하게 보존되도록 했습니다. 한 줄로만 흐르던 챗봇 대화가, 이렇게 옮겨오자 비로소 평면이 아닌 입체가 되었다는 생각이 들었습니다.
패턴 2 — 상태 스냅샷 파일로 세션을 잇는다
매 단계가 끝날 때마다 meta.json 같은 작은 파일에 현재 phase, 다음 action, decision 히스토리를 자동으로 갱신하도록 그분과 합의했습니다.
{
"phase": 2,
"next_action": "draft 검토 후 references 동기화",
"decisions": [
"안 B 채택, 안 A·C 폐기",
"외부 챗봇의 커스텀 기능 의존 제거"
]
}
그분이 새 세션을 열면 이 파일 하나만 읽어도 어디까지 했는지 무손실로 복원되었습니다. 매번 처음부터라는 챗봇 UI의 가장 큰 비용을, 이 작은 스냅샷이 정면으로 해소해주었습니다.
패턴 3 — phase별 전담 sub-agent
그분의 다단계 파이프라인을 phase 단위로 잘게 쪼개고, 각 phase에 전담 sub-agent를 정의했습니다. 창의가 필요한 단계는 큰 모델에, 정형 변환이 필요한 단계는 작은 모델에 맡기는 식으로 나누어두었습니다. 비용과 품질을 동시에 맞추는 가장 솔직한 방법이라는 생각이 들었습니다.
오케스트레이터 하나가 phase와 phase 사이에 사용자 승인 게이트를 박아둡니다. "이 phase 산출물 확인하셨습니까"가 끝나야 다음 단계로 넘어가도록요. 그러자 그분이 거대 프롬프트 하나에 욱여넣던 흐름이, 짧고 분명한 토막들의 연속으로 정리되었습니다.
패턴 4 — 원전과 추출본, 두 층 구조
레퍼런스를 다루는 방식도 처음부터 두 층으로 나눠 잡았습니다.
| 층 | 위치 예시 | 성격 |
|---|---|---|
| 1차 원전 | docs/research/ |
리서치 노트, 학술 자료, 1차 출처 인용본. 평소엔 건드리지 않음 |
| 2차 추출본 | references/ |
매 작업마다 자동 로드되는 정형 룰, 체크리스트, 금기 사전 |
그분이 새 사실을 발견하면 원전을 먼저 갱신하고, 추출본 한 번을 동기화한 뒤 한 커밋으로 묶도록 했습니다. 이렇게 분리해두자 매번 LLM 컨텍스트에 거대한 원전을 통째로 던지지 않게 되었고, 가벼운 추출본만 자동으로 앞에 깔리게 되었습니다. 결국, 컨텍스트의 무게도 설계 대상이라는 것을 옆에서 함께 깨달았습니다.
패턴 5 — 오케스트레이터가 강제 룰을 검증한다
phase 전환 시점마다 "X가 들어있는지", "Y 카테고리에서 N개 이상 등장하는지" 같은 체크리스트를 코드 수준에서 강제하도록 박아두었습니다. 모델의 성실함에 의존하지 않고, 다음 phase 진입을 구조적으로 차단해두는 것입니다.
이 게이트가 박혀 있는 것과 없는 것의 차이는 생각보다 컸습니다. 챗봇 UI 시절 그분이 가장 자주 겪던 누락이, 이 한 줄의 강제로 거의 사라졌습니다.
패턴 6 — standalone, 글로벌 의존성 없음
마지막으로 가장 중요하게 지킨 것은, 이 하네스가 그분의 머신에만 돌아가는 셋업이어서는 안 된다는 원칙이었습니다. 글로벌 ~/.claude/CLAUDE.md, 외부 vault의 절대 경로, OS 특정 명령에 기대지 않도록 의도적으로 잘라냈습니다.
- 다른 머신, 다른 팀원에게
git clone만 하면 그대로 동작 - macOS, Windows(git bash), Linux 어디에서든 같은 흐름
- bash 명령은 POSIX 표준 범위 안에서만 사용 (
ls -t,cat,grep,find)
이 원칙을 지키느라 몇 군데 편의는 포기해야 했지만, 옆에서 본 바로는 그 손해가 곧 회수되었습니다.
보너스 — 자연어 진입과 폴더 상태 자동 감지
여섯 가지 패턴이 자리를 잡고 나니, 그분의 진입 방식까지 자연스럽게 바뀌었습니다. /foo나 --resume <slug> 같은 명령어를 외워둘 필요가 없어진 것입니다.
"이 폴더 다음 단계", "어제 만든 거 이어서" 같은 자연어 표현만으로도, 스킬 description의 트리거 매칭을 통해 적절한 흐름이 자동으로 호출되었습니다. 그리고 폴더 안 파일 상태가 현재 phase를 결정하도록 묶어두었습니다.
0_*.md 없음 → phase 0 (착수)
0_*.md 있고 1_*.md 없음 → phase 1
...
n_*.md 까지 있음 → 완료에 가까움
챗봇 UI 안에서 "이 대화 어디까지 했더라" 하고 헤매던 모습과 정확히 반대편의 풍경이었습니다. 폴더 자체가 진행 상태의 source of truth가 되니, 그분이 머리에 들고 다닐 컨텍스트가 한결 가벼워졌습니다.
standalone이 결정적이었던 이유
여섯 패턴 중에서도 마지막 패턴, 즉 standalone 원칙이 시간이 지날수록 가장 결정적이라는 생각이 들었습니다. 처음에는 단순한 호환성 문제 정도로 여겼는데, 옆에서 그분의 작업이 누적되는 것을 보다 보니 그렇지 않았습니다.
한 사람의 머신에만 맞춰진 셋업은, 다음 작업으로 옮겨갈 때 또 처음부터 짜야 합니다. 누군가에게 보여주려고 해도 "제 환경에선 이 경로가 필요한데요"라는 단서가 매번 붙습니다. 그러면 그 하네스는 결국 일회용 작업 환경에 그치고, 다음 작업의 자산으로 누적되지 않습니다.
반대로 standalone하게 짜둔 하네스는 폴더 단위로 통째로 들고 다닐 수 있습니다. 다른 머신에 옮겨도, 옆 사람에게 건네줘도, 다른 OS에서 열어도 같은 흐름으로 작동합니다. 그제서야 "그분이 짠 작업 도구"가 "그분이 가진 자산"으로 바뀌었다는 감각이 들었습니다.
도구가 자산이 되려면, 그 도구가 한 사람의 책상을 떠나서도 돌아가야 합니다.
마무리 — 작업의 거처를 옮겨드린 것
이 글은 챗봇 UI를 폄훼하기 위한 글이 아닙니다. 빠르게 한 번 묻고 답을 받는 1-shot 회화에는 여전히 챗봇 UI가 더 적합합니다. 가볍게 떠오른 생각을 한두 번 던져 다듬어보는 자리에는 굳이 git도, 폴더도, sub-agent도 필요 없습니다.
다만 옆에서 그분의 작업을 한참 지켜본 끝에, 다단계 큰 맥락이 누적되는 워크플로는 그릇 자체가 달라야 한다는 것을 분명히 알게 되었습니다. 분기와 diff가 필요한 작업, 어제의 결정이 오늘의 전제가 되는 작업, 단계마다 다른 전문성이 필요한 작업, 강제 게이트로 누락을 막아야 하는 작업이 그렇습니다.
결국 챗봇 UI와 로컬 하네스 사이의 선택은 양자택일이 아니었습니다. 작업의 성격에 따라 거처를 옮긴 것일 뿐입니다. 1-shot 회화는 그분도 챗봇 UI 쪽에 그대로 두고, 누적과 게이트와 분기가 필요한 큰 작업만 로컬 하네스로 옮겨드렸습니다. 그제서야 그동안 반복되던 답답함이 도구 탓이 아니라 도구의 거처 탓이었음을, 옆에서 같이 인정할 수 있게 되었습니다.
관련 글
Claude Code 하네스: 리서치부터 글쓰기까지, 하나의 구조로 확장하기
Claude Code 하네스를 리서치, 디자인, 글쓰기, 분석, 데이터 파이프라인에 확장한 사례. 팬아웃, 파이프라인, 생성-검증 세 가지 아키텍처 패턴과 5개 실전 하네스를 공유합니다.
Claude Code 하네스: 1인 개발자가 AI 에이전트 팀을 만드는 법
1인 개발자가 Claude Code로 전문가 AI 팀을 구성한 경험. 에이전트, 스킬, 오케스트레이터로 이루어진 하네스 구조와 모노레포 풀스택 개발 자동화 사례를 공유합니다.
claude -p 를 LaunchAgent 에 붙일 때 만난 7가지 함정
대화 세션에서 잘 돌아가던 Claude Code 가 LaunchAgent 의 자식 프로세스로 들어가면 전혀 다른 얼굴이 됩니다. 인증·도구 억제·타임아웃 등 실측 기반 7가지 함정을 정리했습니다.