TECH 으로 돌아가기
TECH HACKER NEWS 오늘 9분 읽기 25 READS

tmux로 AI 코딩 에이전트 여러 개 돌리기, 그리고 네 개에서 부딪히는 벽

tmux로 AI 코딩 에이전트 여러 개 돌리기, 그리고 네 개에서 부딪히는 벽
SOURCE IMAGE · HACKER NEWS

AI 코딩 에이전트는 터미널과 계속 대화하려는 장기 실행 프로세스다. 이런 성격의 프로그램을 다룰 때 tmux가 자주 등장하는 이유는 세 가지 속성이 정확히 맞아떨어지기 때문이다. 첫째, 세션의 pty를 소유하는 것은 터미널 창이 아니라 tmux 서버이므로 창을 닫거나 SSH 연결이 끊기거나 노트북이 절전에 들어가도 에이전트는 계속 돌아가고, 다시 접속하면 그대로 남아 있다. 둘째, 네 개의 에이전트를 네 개의 창으로 흩어놓는 대신 이름 붙인 네 개의 세션으로 묶어 키 입력 한 번으로 전환할 수 있다. 셋째, 외부에서 스크립트로 조작할 수 있다. 어떤 셸에서든 실행 중인 에이전트에 타이핑을 하거나 화면을 읽어올 수 있어 별도의 IPC나 API를 만들 필요가 없다. 에이전트 오케스트레이션 스크립트에 tmux가 반복해서 등장하는 것은 바로 이 세 번째 속성 때문이다.

손으로 짜기 전에 확인할 것

직접 설정을 만들기 전에 내장 경로부터 확인하는 편이 낫다. 최근 버전의 Claude Code는 claude --worktree --tmux로 워크트리와 tmux 세션을 한 번에 만들어 준다. 다만 두 가지 세부사항을 알고 써야 한다. --tmux는 --worktree 없이는 오류를 내므로 이것은 세션을 만들어 주는 워크트리 기능이지 범용 세션 플래그가 아니다. 또 iTerm2에서는 tmux 대신 네이티브 분할을 사용하는데, 보기에는 더 깔끔하지만 이 경우 나머지 tmux 명령들이 붙을 대상이 사라진다. 세션에 스크립트를 걸 계획이라면 --tmux=classic을 넘겨 진짜 tmux를 밑에 깔아야 한다. 단일 에이전트를 단일 브랜치에서 돌리는 경우라면 이것으로 충분하다. 반대로 한 세션에서 여러 에이전트를 돌리거나, 팬을 배치해 지켜보거나, 스크립트로 입력을 넣거나, 어떤 에이전트가 사용자를 기다리는지 알아내거나, 원격 서버에 이 모든 것을 세팅하는 일은 내장 기능이 닿지 않는 영역이고, 사람들이 tmux를 직접 쓰기 시작하는 지점이다.

세션은 저장소 단위로, 입력은 send-keys로

운영을 지속 가능하게 만드는 조직 원칙은 하나다. 태스크나 에이전트가 아니라 저장소 하나당 세션 하나를 두고 저장소 이름을 붙인다. 태스크는 오고 가지만 프로젝트는 안정적인 단위이기 때문이다. -A 옵션(attach-or-create)을 쓰면 같은 명령을 반복 실행해도 안전해 셸 함수로 감싸기 좋다. 세션 이름에는 .이나 :을 쓸 수 없는데 tmux가 이를 대상 구분자로 쓰기 때문이다. 세션 안에서는 적극적으로 지켜보지 않는 에이전트에게 각자 창을 주는 편이 화면을 차지하지 않아 유리하고, 지금 나란히 보고 싶은 두세 개만 팬으로 둔다. 새 팬을 열 때 #{pane_current_path}를 빠뜨리면 실제 작업 중인 디렉터리가 아니라 세션이 시작된 디렉터리에서 열려 엉뚱한 저장소를 가리키게 되니 주의해야 한다.

에이전트를 cron이나 git 훅, 또는 다른 에이전트에서 조종하는 핵심 도구가 send-keys다. 여기서 세 가지가 자주 어긋난다. Enter, C-c, Escape, Space 같은 단어는 텍스트가 아니라 키 이름으로 해석되므로, 프롬프트에 이런 단어가 단독 인자로 들어가면 키 입력으로 전송된다. -l 옵션으로 리터럴 문자열을 보내야 하며, -l은 키 이름 조회를 꺼버리기 때문에 줄바꿈은 별도의 두 번째 호출로 보내야 한다. 또 텍스트 끝의 세미콜론은 tmux 명령 구분자로 읽히므로 \;로 이스케이프해야 한다. 마지막으로 많은 에이전트 입력창은 붙여넣기 시 디바운스하거나 다시 렌더링하므로, 텍스트와 같은 프레임에 도착한 Enter는 삼켜지기 쉽다. 텍스트를 먼저 보내고 잠시 뒤 줄바꿈을 보내는 패턴이 안정적이다. 화면을 읽어오는 짝이 capture-pane인데, -J 옵션이 생각보다 중요하다. 이것이 없으면 터미널 폭에서 줄바꿈된 한 줄이 두 줄로 돌아와 이음매마다 grep이 어긋난다. 스냅숏이 아니라 연속 로그가 필요하면 pipe-pane으로 팬 출력을 명령에 흘려보낸다.

상태 표시줄 모니터와 그 한계

에이전트를 여럿 돌리다 보면 누구는 일하고 누구는 기다리는지 알려주는 상태 표시줄을 결국 만들게 된다. 원리는 상태 표시줄의 #()로, 셸 명령을 실행해 그 출력을 status-interval초마다 끼워 넣는 것이다. 만들 가치는 있지만 그 실체는 분명히 봐야 한다. 이것은 TUI가 렌더링한 프레임에 대한 문자열 매칭을, 그것도 5초 폴링으로, 열려 있는 모든 팬에 대해 수행하는 일이다. 매칭 문자열은 에이전트 버전이 이번 주에 우연히 출력하는 그 문자열이다. 더 가벼운 대안은 스크래핑 대신 tmux 자체 활동 추적을 쓰는 것으로, monitor-silence는 팬이 N초간 아무 출력도 내지 않으면 알림을 울린다. 다만 침묵은 완료의 보고가 아니라 대용물일 뿐이어서, 큰 파일을 골똘히 들여다보는 에이전트도 똑같이 조용하다.

진짜 강점과 모델의 끝

tmux가 대체하기 어려운 영역은 신뢰할 수 없는 링크 너머 원격 서버에서 에이전트를 돌릴 때다. 연결이 작업 도중 끊겨도 같은 명령을 다시 실행하면 에이전트가 여전히 돌아가는 같은 세션으로 돌아온다. 링크가 나쁘면 SSH 자체가 자꾸 죽으므로 mosh와 짝지으면 좋다. 로컬과 원격 양쪽에서 tmux를 돌리면 둘 다 같은 프리픽스를 잡는 중첩 문제가 생기는데, 원격에 다른 프리픽스를 주거나(set -g prefix C-a) 프리픽스를 두 번 눌러 전달하는 방법으로 푼다. 다만 tmux 서버는 사용자가 소유한 하나의 프로세스일 뿐이어서 재부팅을 견디지 못하고, 서버가 멈추면 스크롤백도 사라진다. 기록이 중요하면 pipe-pane으로 파일에 남기거나 히스토리를 스스로 보존하는 도구를 써야 한다.

정리하면 tmux는 2007년부터 멀티플렉싱과 영속성을 거의 최선으로 풀어온 좋은 소프트웨어이지만, 에이전트에 특화된 일은 하나도 하지 않는다. 그 모델이 설계되던 시점에 에이전트는 존재하지 않았기 때문이다. 승인 프롬프트를 기다리는 에이전트와 골똘히 생각 중인 에이전트, 20분 전에 끝난 에이전트가 모두 '텍스트가 담긴 팬'으로 똑같이 보인다. 완료를 알리는 이벤트는 없고, 화면 스크래핑은 UI가 바뀔 때마다 깨지며, 세션 간 공유 상태도 없어 에이전트끼리 서로의 작업을 넘겨받게 하려면 파일과 잠금과 폴링으로 직접 만들어야 한다. 이는 결함이 아니라 '키보드 앞에 앉은 한 사람을 위해 셸을 다중화하도록 만들어진 도구'의 가장자리다. 원격 서버와 서버의 장기 실행 프로세스, SSH 링크가 가장 취약한 상황이라면 tmux를 그대로 쓰면 된다. 다만 관리 대상이 각자 예측 불가능한 순간에 사람의 결정을 요구하는 여섯 개의 에이전트가 되는 순간, 문제는 더 이상 멀티플렉싱이 아니게 된다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://crystl.dev/guides/tmux-for-ai-agents/
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기 →
처리 중...