RSS
개발 도구

Claude Code 원격 개발 환경: 상시 켜진 리눅스 기계 하나로 맥 두 대 묶기

작성자
듀오랩스 대표·8분 읽기

맥 두 대를 번갈아 쓰면서 작업 환경도 두 벌이 됐습니다. 한쪽 맥에서 하던 브랜치는 다른 맥에 없었고, Claude Code 대화 기록도 기계마다 따로 쌓였습니다. 텔레그램으로 지시를 받는 세션들이 도는 기계는 따로 있었는데, 그 기계는 빌드를 돌리기에 느렸습니다.

그래서 역할을 나눴습니다. 지시를 받고 보고하는 기계, 실제로 코드를 고치고 빌드하는 기계, 운영 배포를 하는 기계를 따로 둡니다. 이 글은 그중 개발을 맡은 리눅스 기계에 작업을 모은 하루의 기록입니다. 중간에 제가 만든 구성 때문에 작업 세션 하나가 통째로 꺼진 일도 있었습니다.

지시하는 기계와 일하는 기계를 나눈 이유

처음에는 느린 기계에서 ssh 로 빠른 기계에 붙어 일하는 방식을 생각했습니다. 그런데 텔레그램 세션과 명령 체계는 지금 기계에 그대로 두고 싶었습니다. 이미 세션 여러 개가 역할을 나눠 돌고 있었기 때문입니다.

결국 개발 기계에 독자적인 Claude Code 세션을 두는 쪽으로 정했습니다. 지시하는 쪽은 텔레그램 메시지를 받아 넘기고 결과를 보고만 합니다. 이 리모컨 세션은 싼 모델(Sonnet)로 돌리고, 코드를 고치는 작업 세션은 강한 모델(Opus)로 돌립니다. 보고를 위해 한 번 더 거치는 만큼 토큰은 조금 더 듭니다.

두 기계는 Claude Code 의 세션 간 메시지가 닿지 않았습니다. 같은 기계 안의 세션끼리만 주고받는 구조라서입니다. 그래서 리모컨은 ssh 너머의 tmux 로 작업 세션을 다룹니다.

ssh dev tmux send-keys -t work -l '블로그 메뉴 이름을 바꿔줘'
ssh dev tmux send-keys -t work Enter
ssh dev tmux capture-pane -p -t work | tail -40

글과 Enter 를 나눠 보내는 것이 요령입니다. 한 번에 보내면 입력 줄에 글만 들어가고 제출되지 않는 경우가 있었습니다.

서비스를 멈추자 같이 꺼진 작업 세션

리모컨을 개발 기계에서 지시하는 기계로 옮기던 중이었습니다. 순서는 단순했습니다. 개발 기계의 옛 리모컨 서비스를 멈추고, 지시하는 기계에 새 리모컨을 띄웁니다. 같은 텔레그램 봇을 두 기계가 동시에 받으면 409 충돌이 나기 때문에, 멈추는 쪽을 먼저 했습니다.

새 리모컨은 1분 안에 떴습니다. 몇 분 뒤 작업 세션 화면을 보려고 했더니 tmux 가 no server running 을 냈습니다. 작업 세션이 사라져 있었습니다.

원인은 작업 세션이 어디서 태어났는지에 있었습니다. 옛 리모컨은 systemd 사용자 서비스로 돌았고, 그 안의 Claude 가 스스로 tmux 세션을 열어 작업 세션을 띄웠습니다. tmux 서버는 데몬으로 떨어져 나가지만 cgroup 은 부모 서비스의 것을 그대로 물려받습니다. systemd 는 서비스를 멈출 때 기본값(KillMode=control-group)대로 그 cgroup 안의 프로세스를 전부 끝냅니다. 리모컨을 멈추자 그 안에서 태어난 tmux 와 작업 세션까지 함께 끝난 것입니다.

멈추기 전에 이걸 확인하지 않았습니다. 잃은 것은 입력 줄에 들어가 있던, 아직 제출하지 않은 지시 한 줄이었습니다. 코드는 마지막 커밋까지 push 된 상태였고 고치던 파일도 없었습니다. Claude Code 의 대화 기록은 디스크에 남아 있어서 claude --resume <대화 id> 로 같은 대화를 다시 열 수 있었습니다.

작업 세션만 담는 별도 서비스

급한 대로 systemd-run --user --scope 로 tmux 를 따로 띄웠습니다. 그런데 이 방식은 기계를 재부팅하면 사라집니다. 그래서 작업 세션만 담는 서비스를 따로 만들었습니다.

[Service]
ExecStart=/usr/bin/tmux -D -f %h/.config/claude-work/tmux.conf
ExecStartPost=/bin/sh -c 'sleep 1; %h/.local/bin/claude-work-up'
ExecStop=/usr/bin/tmux kill-server
Restart=always
RestartSec=30
MemoryMax=24G

tmux -D 는 tmux 서버를 데몬으로 떼지 않고 앞에서 돌립니다. 그래야 systemd 가 서버가 죽은 것을 알고 다시 띄웁니다. 세션이 하나도 없을 때 서버가 스스로 끝나지 않도록 설정 파일에 set -g exit-empty off 를 넣었습니다. claude-work-up 은 목록 파일에 적힌 리포마다, 떠 있지 않은 세션만 claude --continue 로 띄웁니다. 그 폴더의 마지막 대화를 이어서 여는 옵션입니다.

시험으로 tmux kill-server 를 쳐 봤습니다. 40초 뒤에 보니 작업 세션이 다시 떠 있었고, 화면에는 직전 대화가 그대로 있었습니다. 이제 작업 세션과 리모컨은 cgroup 이 나뉘어서, 한쪽을 멈춰도 다른 쪽은 살아 있습니다. MemoryMax=24G 는 세션마다가 아니라 이 서비스 전체에 걸리는 한도입니다. 같은 기계의 다른 프로세스를 누르지 않게 하려고 둔 상한입니다.

휴대폰으로 받는 화면 사진

텔레그램으로 「바꿨습니다」라는 보고만 받으면 검수가 어렵습니다. 그래서 두 가지 화면을 사진으로 받게 했습니다.

하나는 작업 세션의 터미널 화면입니다. tmux capture-pane -e 는 색과 굵기를 ANSI 코드로 함께 내줍니다. 이 코드를 해석해 글자를 칸 격자에 하나씩 놓은 SVG 를 만들고, resvg 로 PNG 를 뽑습니다. 한 장에 3초쯤 걸립니다. 처음 뽑은 그림은 한글이 전부 띄어 쓴 것처럼 보였습니다. 한글은 터미널에서 두 칸을 차지하는데, 글꼴의 실제 폭은 두 칸보다 좁아서 왼쪽에 붙이면 사이가 벌어집니다. 두 칸의 가운데에 놓도록 고쳤습니다. 몇몇 기호는 아직 빈 네모로 나옵니다.

다른 하나는 웹 페이지입니다. 개발 기계에 playwright-core 와 크로미움 헤드리스 셸을 사용자 폴더에만 설치했습니다. sudo 없이 되고, 한글 글꼴까지 합쳐 278MB 입니다. 데스크톱(1440×900)과 모바일(390×844, 3배) 두 장을 찍는 데 7초쯤 걸립니다. 찍는 곳이 개발 기계라서 127.0.0.1 에만 붙인 개발 서버도 그대로 찍힙니다.

Orca 로 붙어 보니 하나가 된 작업 환경

평소에는 Orca 로 여러 에이전트를 나눠 돌립니다. Orca 에는 SSH 호스트를 추가해 그 기계에서 에이전트를 돌리는 기능이 있습니다. 호스트를 추가하는 창에 「개인 키 파일」 칸이 있어서, 처음에는 개인키를 서버에 넣어야 하는 줄 알았습니다. 그 칸에 적는 것은 내 맥 안에 있는 키의 위치입니다. 비워 두면 맥의 ssh agent 를 쓰고, 키는 맥을 떠나지 않습니다.

붙고 나서 프로젝트 목록에 같은 리포가 세 줄로 보였습니다. 개발 기계의 원래 폴더, 맥에 있던 옛 사본, 그리고 Orca 가 새로 만든 작업 트리(git worktree)였습니다. 작업 트리는 임의의 이름을 단 브랜치로 만들어져서 원래 폴더의 main 을 건드리지 않습니다. 텔레그램 리모컨이 넘기는 작업은 원래 폴더에서, 제가 Orca 로 하는 작업은 작업 트리에서 하도록 나눴습니다. 작업 트리 하나에는 세션 하나만 붙입니다. 둘을 붙이면 같은 파일을 동시에 고치게 됩니다.

이렇게 하고 나니 맥 두 대가 같은 기계를 봅니다. 코드와 브랜치, Claude 대화 기록이 모두 한곳에 있어서 다른 맥에서 붙어 claude --resume 하면 하던 대화가 이어집니다. 맥을 덮고 나가도 에이전트는 계속 일합니다. 제게는 이게 빌드 속도보다 큰 차이였습니다.

한 기계에 몰린 만큼 남은 숙제

작업이 한곳에 모인 만큼, 그 기계가 멈추면 다 멈춥니다. 자동 복구 서비스는 tmux 로 띄운 작업 세션만 지킵니다. Orca 가 ssh 로 띄운 세션은 그 대상이 아닙니다. push 를 자주 하는 것이 지금으로서는 가장 확실한 백업입니다.

같은 리포를 두 기계에서 번갈아 고치면 매번 pull 을 해야 합니다. 그래서 리포마다 고치는 기계를 하나로 정했습니다. GitHub 웹에서 고치거나 PR 을 합친 경우를 위해, 작업 세션이 뜰 때 고치던 것이 없으면 git pull --ff-only 를 먼저 돌리게 할 생각입니다. 아직 넣지는 않았습니다.

밖에 나가서 Orca 로 붙는 방법은 아직 정하지 않았습니다. 그동안은 텔레그램 리모컨으로 일을 시키고 화면 사진으로 확인합니다.

우리 서비스에 적용할 계획이 있으신가요?

기술 자문·개발 범위 상담 살펴보기 →

마지막 수정:

공유하실 때는 출처(Duolabs)와 원문 주소를 표시해 주세요.