누구나 그럴싸한 에이전트 오케스트레이션 하나쯤은 가지고 있잖아?
개발자라면 누구나 자기만의 LLM 개발 환경을 조금씩 만들고 있을 것이다. 나도 그렇다. 예전에는 답답한 부분이 생기면 검색부터 했다. 요즘은 자꾸 생각이 바뀐다. "그냥 직접 만들어 쓰면 안 되나?"
그게 가능해진 건, 결국 AI 모델의 발전 때문이라고 생각한다.

"우리는 답을 찾을 것이다. 늘 그랬듯이." (인터스텔라, 2014)
서론
요즘 에이전틱 코딩, 혹은 조금 더 가볍게 말해 바이브 코딩을 하다 보면 사람마다 자기만의 방식이 생긴다. 어떤 사람은 Claude Code를 터미널에 붙여두고, 어떤 사람은 Cursor나 Copilot을 IDE 중심으로 쓰고, 어떤 사람은 ChatGPT/Codex/Claude를 역할별로 나눠서 쓴다. 프롬프트도 다르고, 컨텍스트를 먹이는 방식도 다르고, 모델을 믿는 정도도 다르다.
나도 마찬가지였다. 처음에는 하나의 에이전트에게 일을 시키고, 결과를 확인하고, 다시 지시하는 방식으로 충분했다. 그런데 프로젝트가 조금씩 커지자 벽이 생겼다. 백엔드, 프론트, 문서, 배포, 데이터 마이그레이션, 테스트가 한 덩어리로 엉키기 시작했다. 한 에이전트가 모든 맥락을 붙들고 있기에는 컨텍스트가 너무 커졌고, 사람이 중간에서 계속 라우팅하기에는 손이 너무 많이 갔다.
결국 생각이 여기로 왔다.
오케스트레이션이 필요하다.
오케스트레이션이라는 말이 거창하게 들리지만
여기서 말하는 오케스트레이션은 대단한 분산 시스템 얘기가 아니다. 내가 원한 건 훨씬 소박했다.
- 백엔드 에이전트는 백엔드 워크트리에서 계속 일한다.
- 프론트엔드 에이전트는 프론트엔드 워크트리에서 계속 일한다.
- 메인 에이전트는 전체 방향을 보고 필요한 파트에게 일을 보낸다.
- 각 에이전트는 자기 세션과 파일 맥락을 잃지 않는다.
- 사람이 모든 메시지를 복사해서 전달하지 않아도 된다.
말하자면 "한 명의 천재 에이전트"가 아니라 "각자 자기 책상에 앉아 있는 여러 명의 보통 에이전트"를 연결하고 싶었다. 이미 좋은 도구들은 많다. 문제는 내 작업 방식과 딱 맞는 연결 방식이 없었다는 점이다.
좋은 서비스들을 먼저 써봤다
직접 만들기 전에 당연히 기존 서비스들을 봤다. 이미 똑똑한 사람들이 만든 좋은 도구들이 있고, 내가 놓친 답이 있을 수 있으니까.
OpenHands
OpenHands는 예전 OpenDevin으로 알려졌던 오픈소스 소프트웨어 개발 에이전트 플랫폼이다. 논문과 문서를 보면 방향이 분명하다. 에이전트가 사람 개발자처럼 코드를 쓰고, 커맨드라인을 다루고, 브라우저를 쓰고, 샌드박스 안에서 작업하도록 만드는 플랫폼이다. 여러 에이전트 구현, 안전한 실행 환경, 평가 벤치마크까지 포함하려는 꽤 큰 그림의 프로젝트다.
좋은 점은 명확했다. "에이전트를 실행 환경까지 포함한 하나의 제품으로 다룬다"는 접근이다. 직접 손으로 터미널을 열고 컨텍스트를 정리하는 대신, 에이전트가 일할 수 있는 작업 공간을 통째로 제공한다.
하지만 내가 원한 것과는 조금 달랐다. 나는 이미 내 로컬 개발 환경을 가지고 있었다. 레포별 워크트리, tmux 세션, 각 세션에 붙어 있는 에이전트, 내가 익숙하게 쓰는 셸과 스크립트가 있었다. OpenHands식 통합 환경으로 들어가는 순간, 내가 이미 만들어 둔 작업 리듬을 일부 포기해야 했다. 도구가 나쁘다는 뜻이 아니다. 내 문제는 "새 작업장을 하나 더 만들기"가 아니라 "이미 굴러가는 작업장들을 연결하기"에 가까웠다.
Paperclip
Paperclip도 써봤다. 솔직히 인상은 강했다. 잘 만든 서비스였다. 화면과 흐름이 매끈했고, "아, 이 사람들 진짜 많이 고민했구나" 싶은 부분이 있었다. 이런 걸 만들 수 있는 사람들은 늘 대단하다.
그런데 내 사용 환경에서는 모델과 맥락이 병목이 됐다. 모델 버전이 낮거나 로컬 모델을 붙이면, 복잡한 개발 맥락을 계속 잡고 가는 능력이 떨어졌다. 처음 몇 턴은 잘 가다가도 어느 순간 요구사항의 핵심을 놓치거나, 이미 결정한 구조를 다시 흔들거나, 레포의 실제 상태보다 자기 기억을 더 믿는 식의 문제가 생겼다.
에이전트 오케스트레이션은 UI가 좋아도 모델이 맥락을 잃으면 금방 무너진다. 특히 코딩은 "그럴듯한 계획"보다 "방금 바뀐 파일, 실패한 테스트, 기존 설계의 제약"을 붙드는 능력이 중요하다. 내 경우에는 이 부분이 아쉬웠다.
Claude Teamwork
Claude Teamwork 계열 접근도 해봤다. 여러 Claude 세션을 역할별로 나누고, 메인 세션이 하위 세션에게 일을 시키는 방식이다. 아이디어는 좋다. 실제로도 작은 문제에서는 꽤 잘 된다. 역할을 나누면 한 에이전트가 모든 걸 떠안지 않아도 되고, 작업을 병렬로 진행하는 느낌이 난다.
문제는 토큰이었다.
메인 에이전트가 전체 맥락을 들고 있고, 각 서브 에이전트에게 일을 설명하고, 서브 에이전트가 다시 결과를 길게 보고하고, 메인이 그걸 읽고 다음 지시를 만든다. 이 구조는 생각보다 빨리 비싸진다. 특히 각 에이전트가 독립된 세션이라면 같은 프로젝트 설명, 같은 설계 배경, 같은 파일 맥락이 반복해서 들어간다. 협업은 됐지만, 모든 대화가 장문의 회의록처럼 쌓였다.
토큰을 쓰는 게 문제는 아니다. 좋은 결과를 위해 필요한 토큰은 써야 한다. 하지만 "에이전트끼리 서로 깨우고 상태를 확인하는 비용"까지 매번 큰 프롬프트로 치르는 건 아까웠다. 나는 더 얇은 메시지 레이어가 필요했다.
문제가 나일까?
이쯤 되면 당연히 이런 생각을 한다.
내가 이상하게 쓰고 있는 건가? 내가 원하는 방식이 너무 특이한가? 그냥 내가 도구를 잘 못 쓰는 건가?
어느 정도는 맞을 수 있다. 개발자마다 작업 스타일이 다르고, 내가 편한 방식이 남에게는 이상할 수 있다. 하지만 계속 걸리는 지점은 비슷했다.
- 나는 이미 로컬에 영속적인 에이전트 세션을 띄워두고 있었다.
- 각 에이전트는 자기 워크트리와 터미널 맥락을 가지고 있었다.
- 필요한 건 거대한 새 IDE가 아니라 세션 간 메시지였다.
- 모든 협업을 LLM 프롬프트로만 처리하면 토큰이 너무 많이 들었다.
- 사람 손으로 메시지를 복사해 전달하는 건 금방 한계가 왔다.
정리하면, 나는 "에이전트를 새로 실행하고 관리하는 플랫폼"보다 "이미 살아 있는 에이전트들을 깨우고 연결하는 레이어"가 필요했다.
그럼 토큰 좀 태워볼까? 만들어볼까?
이미 내 환경은 어느 정도 정해져 있었다. 나는 워크트리를 이용해 1레포, 1에이전트에 가까운 방식으로 개발하고 있었다. 각 에이전트는 자기 세션 안에서 파일을 읽고, 테스트를 돌리고, 이전 작업 흐름을 어느 정도 유지했다. 이 방식의 장점은 영속성이다. 에이전트가 매번 새 방에서 시작하지 않는다. 자기 책상 위에 파일과 터미널 기록이 남아 있다.
그렇다면 여기에 필요한 것은 뭘까?
에이전트들을 새로 만들 필요는 없다. 이미 있다. 각 에이전트가 사람처럼 Slack에 들어올 필요도 없다. 그냥 서로에게 짧은 메시지를 보낼 수 있으면 된다. 그리고 메시지가 오면 잠자고 있던 세션이 깨어나면 된다.
여기서 RelayRoom의 첫 아이디어가 나왔다.
"메시지는 서버에 저장하고, wake는 아주 얇게 전달하자."
대화 전문을 매번 큰 프롬프트로 복사하지 않는다. 필요한 파트에게 제목과 본문을 보내고, 받는 에이전트는 자기 inbox를 확인한다. 맥락이 더 필요하면 스레드를 열어본다. 일이 끝나면 닫는다. 사람은 대시보드로 상태를 보고, 에이전트는 MCP 도구로 메시지를 주고받는다.
클로드의 역습
타이밍도 영향을 줬다. 나는 Claude Max 요금제를 쓰고 있었는데, 2026년 6월 중순을 전후로 요금제와 사용량 정책 변경 이야기가 나왔다. 헤드리스 사용(claude -p)이나 자동화된 사용이 어떻게 과금될지 신경 쓰이는 상황이었다. 나 같은 개인 개발자에게 토큰과 사용량은 그냥 숫자가 아니다. 실험을 계속할 수 있느냐의 문제다.
물론 답은 "토큰을 안 쓰자"가 아니다. AI 코딩을 하면서 토큰을 아끼기만 하면 품질이 떨어진다. 하지만 모든 것을 고급 모델의 긴 대화로 해결할 필요도 없다. 상태 저장, 메시지 라우팅, 읽음 처리, wake 같은 일은 제품 레이어가 맡을 수 있다. LLM은 판단과 코딩에 집중하고, 시스템은 협업 배선을 담당하면 된다.
어딘가에서 본 그 대사처럼, 우리는 답을 찾을 것이다. 늘 그랬듯이.
단, 이번에는 블랙홀로 들어가는 게 아니라 tmux로 들어갔다.
tmux
tmux는 터미널 멀티플렉서다. 하나의 터미널 안에서 여러 세션, 창, pane을 유지할 수 있고, SSH가 끊겨도 세션이 살아 있다. 개발자들이 오래 걸리는 작업을 돌려두거나, 여러 서버와 작업 공간을 동시에 유지할 때 자주 쓴다.
내게 중요했던 건 tmux send-keys였다.
tmux send-keys는 특정 tmux pane에 키 입력을 보낼 수 있다. 사람이 직접 키보드로 치지 않아도, 외부 프로세스가 세션에 텍스트를 입력하고 Enter를 누르게 만들 수 있다. 즉 이미 열려 있는 에이전트 세션에게 "메시지 왔어. inbox 확인해" 같은 짧은 신호를 보낼 수 있다.
개념은 단순하다.
tmux send-keys -t relayroom:web \
'relayroom inbox --unread' Enter실제로는 세션 찾기, 중복 wake 방지, 메시지 예산, idle 상태, 보안 같은 것들이 붙는다. 하지만 핵심은 이 한 줄이다. 이미 살아 있는 세션을 다시 깨울 수 있다. 새 에이전트를 시작하는 것이 아니라, 잠시 멈춰 있던 동료의 어깨를 톡 치는 것이다.
테스트
처음 테스트는 아주 원시적이었다. tmux 세션을 하나 띄워두고, 외부에서 send-keys로 문장을 밀어 넣었다. 잘 들어갔다. Enter도 눌렸다. 에이전트는 입력을 받았다.
그 순간 "이거 되겠는데?"라는 생각이 들었다.
물론 "입력이 들어간다"와 "제품이 된다" 사이에는 거리가 있다. 메시지를 어디에 저장할지, 어떤 에이전트에게 보낼지, 같은 메시지로 여러 번 깨우지 않으려면 어떻게 할지, 사람이 대시보드에서 어떤 상태를 봐야 할지, self-host 환경에서 네트워크는 어떻게 뚫을지 같은 문제가 줄줄이 나왔다.
하지만 첫 번째 고리는 맞았다. 에이전트는 이미 로컬에 살아 있고, tmux는 그 에이전트를 깨울 수 있다. 그러면 나머지는 메시징 시스템의 문제다.
그래서 만들어보기로 했다
RelayRoom은 그렇게 시작했다. 거대한 agent runtime을 만들겠다는 생각은 아니었다. 오히려 반대였다. 이미 각자 잘 돌고 있는 에이전트들을 얇게 연결하고 싶었다.
내가 만들고 싶었던 것은 이런 것이었다.
- 에이전트가 MCP 도구로 다른 part에게 메시지를 보낸다.
- 메시지는 서버에 남고, 수신자는 inbox에서 확인한다.
- 새 메시지가 오면 pager가 tmux 세션을 깨운다.
- 대화가 끝나면 스레드를 닫아 불필요한 wake를 막는다.
- 사람은 대시보드에서 누가 살아 있고, 어떤 스레드가 열려 있고, 토큰이 어디서 쓰이는지 본다.
- self-host로도 돌릴 수 있어야 한다.
결과물이 얼마나 아름다울지는 모르겠다. 어쩌면 꽤 투박할 수도 있다. 하지만 내가 실제로 겪은 답답함에서 출발했기 때문에, 적어도 문제는 진짜다.
개발하면서 머리를 쥐어뜯는 일은 이미 생겼고, 앞으로도 생길 것이다. 리버스 프록시가 SSE를 버퍼링해서 wake를 삼킨 일, Host 허용목록이 정상 도메인을 403으로 막은 일, compose 파일이 이미지 업데이트를 따라오지 않은 일, arm64 빌드가 QEMU에서 너무 느렸던 일 같은 것들이다. 그래서 이 글 뒤에는 그 삽질들을 하나씩 정리할 생각이다.
런칭 후에 정리해서 올리는 글이라 실제 사용 중 버그가 나올 수도 있다. 그래도 예쁘게 봐주셨으면 한다. 이건 완성된 정답이라기보다, "내 작업 방식에 맞는 에이전트 협업 레이어를 직접 만들어보자"에서 시작한 기록이다.
그리고 아마 많은 개발자들이 비슷한 생각을 하고 있을 것이다.
누구나 그럴싸한 에이전트 오케스트레이션 하나쯤은 가지고 있잖아?
참고 자료
- OpenHands, OpenHands: An Open Platform for AI Software Developers as Generalist Agents
- Model Context Protocol, Tools specification
- tmux,
send-keysmanual