tmux는 AI 코딩 에이전트를 이름이 지정되고 분리 가능한 세션에서 실행하며, 터미널을 닫거나 SSH 연결이 끊겨도 유지됩니다. 하나의 창 안에 여러 에이전트가 들어있게 되며, 스크립트에서 그중 어느 곳이든 타이핑할 수 있는 send-keys와 내용을 읽어올 수 있는 capture-pane 기능을 제공합니다. 바로 이 조합 때문에 사람들이 이를 사용합니다.
직접 구축하기 전에 내장된 경로를 확인하세요. 최신 버전의 Claude Code는 claude --worktree --tmux 명령어로 작업 트리를 만들고 tmux 세션을 자동으로 생성할 수 있습니다. 본 가이드는 먼저 그 부분을 다루고, 그다음 도달하지 못하는 모든 영역, 마지막으로 tmux 자체가 멈추는 지점까지를 다룹니다.
왜 tmux와 에이전트가 함께 사용되는가
코딩 에이전트는 터미널과 계속 소통하려는 장기 실행 프로세스입니다. tmux의 세 가지 속성이 이러한 형태에 부합합니다:
연결 끊김에도 살아남는다. tmux 서버가 pty(pseudo-terminal)를 소유하며, 사용자의 터미널 창이 아닙니다. 창을 닫거나 SSH 링크를 잃거나 노트북을 절전 모드로 두어도 에이전트는 계속 작동하며 재접속 시에도 그 자리에 있습니다.
하나의 창에 여러 세션을 담는다. 네 개의 에이전트가 앱 스위처에서 찾아다닐 네 개의 창 대신, 키 입력을 통해 전환하는 네 개의 이름 지정된 세션이 됩니다.
외부에서 스크립팅 가능하다. 어느 곳에 있는 어떤 셸이라도 실행 중인 에이전트에 타이핑하거나 그 화면을 읽을 수 있습니다. 구축할 IPC(Inter-Process Communication)가 필요 없고, 연결할 API도 필요 없습니다. 이것이 사람들이 기반으로 삼는 속성이며, tmux가 에이전트 오케스트레이션 스크립트에 계속 등장하는 이유입니다.
무언가를 만들기 전에 내장 플래그 사용하기
Claude Code는 단일 브랜치에 대한 에이전트 케이스를 위해 이 기능이 연결되어 제공됩니다. 최신 버전의 claude --help에서 볼 수 있는 두 가지 플래그가 있습니다:
-w, --worktree [name] 이 세션을 위한 새로운 git 작업 트리를 생성합니다 (선택적으로 이름을 지정할 수 있음)
--tmux 작업 트리를 위한 tmux 세션을 생성합니다
...
따라서
따라서 이는 세션 자체를 만드는 worktree 기능이지, 일반적인 세션 플래그는 아닙니다. iTerm2의 경우 tmux 대신 네이티브 페인(pane)을 사용하므로 보기 좋고, 이 페이지 나머지 부분에 있는 tmux 명령어들은 붙일 대상이 없습니다. --tmux=classic 플래그를 사용하면 실제 tmux가 작동합니다. 이는 세션에 대해 스크립트를 작성할 계획이라면 필요합니다.
단일 브랜치에서 단일 에이전트만 실행하는 경우, 그것이 답입니다. 이것을 사용하고 건너뛰세요.
이것이 하지 않는 것들: 하나의 세션에서 여러 에이전트를 실행하거나, 이들을 감시할 수 있도록 페인을 배치하거나, 스크립트에서 실행 중인 에이전트에 타이핑할 수 있게 하거나, 어떤 에이전트가 사용자 입력을 기다리고 있는지 알려주거나, 또는 SSH를 통해 원격 박스에 이러한 것들 중 어느 것을 설정하는 것입니다. 이들은 본 가이드의 나머지 부분이며, 각각의 경우 사람들이 직접 tmux를 작성하게 되는 지점입니다.
Git worktrees에서 병렬 개발을 위한 해당 플래그의 worktree 절에 대한 더 많은 내용은 참고하세요.
프로젝트별 세션 이름 지정하기
이것을 사용 가능하게 유지하는 구성 규칙은 다음과 같습니다: 리포지토리당 하나의 tmux 세션을 만들고, 그 이름을 리포지토리에 따라 지정합니다. 작업(task)별이나 에이전트별이 아닙니다. 작업은 오가지만, 프로젝트는 안정적인 것입니다.
# 프로젝트 디렉터리에서 분리된 세션 시작하기
tmux new-session -d -s api -c ~/code/api
# 여기에 붙기 (존재하지 않으면 생성)
...
-A는 attach-or-create(붙이거나 만들기)의 약자입니다. 이는 쉘 함수에서 원하는 것, 즉 반복적으로 실행해도 안전한 단일 명령을 만들어줍니다:
# ~/.zshrc
agent() {
local dir="${1:-$PWD}"
...
세션 이름에는 .나 :를 포함할 수 없습니다 (tmux는 이들을 대상 구분자로 사용합니다). 그래서 tr을 사용합니다.
그러면 tmux ls가 실제 인벤토리 목록이 됩니다:
$ tmux ls
api: 3 windows (created Fri Jul 31 09:12:04 2026)
web: 2 windows (created Fri Jul 31 09:40:11 2026)
...
포맷 문자열을 사용하여 조금 더 추가할 수 있습니다:
tmux list-sessions -F '#{session_name} windows=#{session_windows} attached=#{session_attached}'
여러 에이전트를 감시하기 위한 레이아웃
세션 내부에서, 각 에이전트에게 작업 이름으로 지정된 전용 창을 할당하세요:
tmux new-window -t api -n auth -c ~/code/api
tmux new-window -t api -n tests -c ~/code/api
에이전트를 직접 관찰하지 않는 경우에는 창(pane)보다 창(window)을 사용하는 것이 더 좋습니다. 왜냐하면 창은 화면 공간을 차지하지 않기 때문입니다. 패널(Panes)은 지금 당장 나란히 두고 싶은 두세 개의 에이전트용입니다:
tmux split-window -h -c '#{pane_current_path}'
tmux split-window -v -c '#{pane_current_path}'
...
#{pane_current_path}는 생략해도 괜찮습니다. 이것이 없으면 새 패널은 현재 세션이 시작된 디렉터리에 열리며, 실제로 사용자가 위치한 디렉터리가 아니므로 실행되는 에이전트가 잘못된 리포지토리를 가리키게 됩니다.
두 가지 레이아웃 이름: 모든 에이전트를 균일하게 배치하는 tiled와 나머지 에이전트를 옆에 쌓고 가장 큰 패널 하나(사용자가 제어하는 에이전트)를 두는 main-vertical입니다.
tmux select-layout main-vertical
tmux set-window-option main-pane-width 60%
main-pane-width의 백분율 값은 tmux 3.2 이상이 필요합니다. 구형 빌드에서는 절대 열(column) 개수(main-pane-width 120)를 사용하세요. tmux -V로 확인하십시오.
갖추면 유용한 .tmux.conf
에이전트 작업에 특히 중요한 라인들입니다. 전체 설정 파일은 아니지만, 에이전트 군집을 운영하면서 필요하게 되는 기본값과의 차이점(delta)들이 있습니다.
# 에이전트는 출력이 많으므로 스크롤하여 내용을 확인하고 싶을 것입니다
set -g history-limit 100000
# 24비트 색상으로, 에이전트 TUI가 설계된 대로 렌더링되도록 합니다
...
에이전트 TUI는 터미널 제목을 설정하는 것을 좋아합니다. 따라서 allow-rename off가 없으면 신중하게 이름 지은 auth 창이 에이전트가 마음대로 부르는 이름으로 바뀌게 됩니다.
escape-time 10은
# 대상 구문: session:window.pane
tmux send-keys -t api:auth "run the test suite and fix what fails" Enter
여기서 세 가지 문제가 발생하며, 빈도는 다음과 같습니다:
빈 인수는 키 이름으로 해석됩니다. Enter
, C-c
, Escape
, 그리고 Space
은 텍스트가 아닌 키입니다. 프롬프트에 이 단어들 중 하나가 독립적인 인수로 포함되면 키 입력으로 전송됩니다. 리터럴 문자열을 보내려면 -l를 사용하세요:
tmux send-keys -t api:auth -l "press Enter to continue was the old flow"
tmux send-keys -t api:auth Enter
-l는 키 이름 조회를 완전히 비활성화하므로, 줄 바꿈은 두 번째 호출이 되어야 합니다.
세미콜론은 tmux 명령을 종료합니다. 텍스트 끝에 붙은 ``;는 명령어 구분자로 읽힙니다. 이를 이스케이프하려면 ;`를 사용하세요.
Agent TUI는 텍스트와 줄 바꿈 사이에 간격이 필요합니다. 많은 Agent 입력 상자는 붙여넣기 시 디바운싱(debounce)되거나 다시 렌더링되며, 텍스트가 들어온 프레임과 동시에 도착하는 Enter는 먹혀버립니다. 신뢰할 수 있는 패턴은 다음과 같습니다:
tmux send-keys -t api:auth -l "$PROMPT"
sleep 0.3
tmux send-keys -t api:auth Enter
실행 중인 Agent를 중단하는 것도 동일한 메커니즘입니다:
tmux send-keys -t api:auth C-c
capture-pane을 사용하여 Agent 내용을 읽기
capture-pane은 패널의 내용을 표준 출력(stdout)으로 덤프합니다. 이는 이 쌍의 읽기 절반입니다.
# 보이는 화면
tmux capture-pane -p -t api:auth
# 마지막 200줄, 줄 바꿈된 라인은 다시 합쳐짐
...
-J는 보이는 것보다 더 중요합니다. 이것이 없으면 터미널 너비에서 줄 바꿈된 라인이 두 개의 라인으로 돌아오고, 출력에 대해 작성하는 모든 grep은 경계면(seams)에서 틀립니다.
스냅샷보다는 연속적인 로그의 경우, pipe-pane은 패널이 생성하는 모든 것을 명령어에 스트리밍합니다:
tmux pipe-pane -o -t api:auth 'cat >> ~/agent-logs/api-auth.log'
# 중지하기
tmux pipe-pane -t api:auth
-o는 토글(toggle) 기능을 하므로, 두 번 실행하면 꺼집니다. 출력은 이스케이프 시퀀스를 포함하여 원시(raw) 상태이므로, 나중에 읽기 쉽게 만들려면 sed -E 's/\[[0-9;]*[a-zA-Z]//g'와 같은 것으로 파이프해야 합니다.
상태 표시줄 모니터링
tmux에서 여러 에이전트를 실행하는 사람이라면 누구나 이런 것을 만듭니다: 어떤 에이전트가 작동 중이고 어떤 것이 대기 중인지 알려주는 상태 표시줄입니다.
그 메커니즘은 상태 표시줄의 #() 안에 있으며, 쉘 명령을 실행하고 그 출력을 보간(interpolate)하며, status-interval 간격마다 새로 고침됩니다.
set -g status-interval 5
set -g status-right '#(~/bin/tmux-agent-status) | %H:%M'
set -g status-right-length 100
그리고 각 페인에서 생존 신호를 포착하는 스크립트:
#!/usr/bin/env bash
# ~/bin/tmux-agent-status
out=""
...
이것은 작동하며, 구축할 가치가 있습니다. 이것이 무엇을 하는지 명확하게 이해해야 합니다: 열려 있는 모든 페인에 대해 5초마다 TUI(Text User Interface)의 렌더링된 프레임과 문자열 매칭을 수행하는 것입니다. 일치하는 문자열은 이번 주에 사용자의 에이전트 버전이 출력하는 것이 무엇이든 간에 그 내용입니다.
좀 더 가벼운 버전은 스크래핑 대신 tmux 자체의 활동 추적 기능을 사용합니다. monitor-silence는 페인에서 N초 동안 출력이 없을 때 알림을 발생시키는데, 이것이 “이 에이전트가 멈췄다”에 가장 가까운 네이티브 신호입니다:
set-window-option -g monitor-silence 30
set -g visual-silence on
여기에 액션을 걸 수 있습니다:
set-hook -g alert-silence 'run-shell "osascript -e \"display notification \\"agent idle\"\""'
침묵(Silence)은 완료(done)의 대용품일 뿐, 그 보고서가 아닙니다. 큰 파일에 대해 깊이 생각하는 에이전트 역시 침묵합니다.
분리(Detach), 재연결(Reattach), 그리고 무엇이 살아남는가
tmux detach-client # 또는 프리픽스 키, 그 다음 d
tmux attach -t api
tmux switch-client -t web # 다른 세션 내부에서
...
살아남는 것:
| 이벤트 | 에이전트가 계속 실행되는가? |
|---|
| 터미널 창 닫기 | 예 |
| ... | tmux kill-session |
tmux kill-server | 아니오 |
| 재부팅 | 아니오 |
| macOS의 재시작 후 “창 다시 열기” | 아니오 |
tmux 서버는 단지 사용자가 소유한 프로세스일 뿐입니다. tmux가 재부팅을 견디게 하는 것은 아무것도 없으며, 세션의 스크롤백은 서버가 멈출 때 손실됩니다. 기록(transcript)이 중요하다면, pipe-pane
파일로 저장하거나 자체적으로 히스토리를 유지하는 도구를 사용하세요.
사람들이 실수하기 쉬운 한 가지 세부 사항이 있습니다: 분리된(detached) 세션의 창들은 마지막으로 연결했던 클라이언트 크기, 또는 그런 클라이언트가 없었다면 80x24로 크기가 조정됩니다. 에이전트 TUI가 당신이 자리를 비운 동안 80열 패널에 렌더링되더라도, 당신이 200열에서 다시 연결할 때까지는 잘못되어 보이다가 재그려지면서 정상화됩니다. set-window-option -g aggressive-resize on
여러 클라이언트가 다른 크기로 연결될 때 도움이 됩니다.
SSH를 통한 tmux 사용
이것이야말로 tmux가 진정으로 대체하기 어려운 부분입니다. 신뢰하지 않는 링크를 통해 원격 장치에서 에이전트를 실행하는 경우:
# tty 할당과 함께, 하나의 명령어로 연결하거나 생성합니다.
ssh devbox -t tmux new-session -A -s agents
작업 도중에 연결이 끊기면, 같은 명령어를 다시 실행하고 에이전트가 계속 작동하는 동일한 세션으로 돌아갈 수 있습니다. 만약 링크 상태가 너무 안 좋아서 SSH 자체도 계속 죽는다면 mosh와 함께 사용하세요.
중첩된 tmux 문제: 로컬에서 tmux를 실행하고 원격 장치에서도 실행하면, 둘 다 같은 접두사(prefix)를 사용하게 됩니다. 두 가지 해결책은 원격지에서 다른 접두사를 설정하는 것(set -g prefix C-a)이거나, 접두사를 두 번 누르는 것인데, 이는 bind C-b send-prefix가 제공합니다.
당신 앞에 있는 장치가 아닌 기계에서 에이전트를 실행하려면 원격 개발(remote development)을 참고하세요.
tmux의 한계
tmux는 좋은 소프트웨어입니다. 멀티플렉싱과 지속성(persistence) 문제를 해결하는 데 있어서 가능한 최선으로 해결하며, 2007년부터 이 역할을 해왔습니다. 하지만 에이전트와 관련된 특정한 기능은 아무것도 하지 못합니다. 왜냐하면 에이전트는 이 모델이 설계되었을 때 존재하지 않았기 때문입니다.
어떤 에이전트가 대기 중인지 가시성이 없습니다. 승인 프롬프트를 기다리는 에이전트는 생각하고 있는 에이전트와 똑같이 보이고, 20분 전에 끝난 에이전트와도 똑같이 보입니다. 이 세 가지 모두
가장 가까운 것이며, 어떤 이유로든 조용한 pane에서 발동됩니다. tmux는 에이전트가 무엇인지 알지 못하기 때문에 “에이전트가 완료됨”이라는 이벤트가 없습니다.
화면 스크래핑(Screen scraping)은 구조적으로 취약합니다. capture-pane
은 렌더링된 프레임을 제공합니다: 스피너, 박스 드로잉, 잘린 열, ANSI. 에이전트가 말한 것이 아니며, 어떤 도구를 호출했는지도 아니고, 그 호출이 성공했는지 여부도 아닙니다. 여기에 맞춰 작성하는 모든 파서는 에이전트가 UI 변경을 전송할 때 깨집니다.
세션 간 공유 상태(shared state)가 없습니다. 두 개의 tmux 세션에 있는 두 에이전트는 서로를 볼 방법이 없습니다. 만약 한 에이전트가 다른 에이전트의 기록을 읽거나 작업을 인계받기를 원한다면, 파일과 잠금장치(locks), 폴링(polling) 등을 이용해 직접 구축해야 합니다.
여러 스트리밍 에이전트를 위한 스크롤백은 비용이 많이 듭니다. history-limit 100000