Esc
← 모든 글

Claude Code 멀티 세션으로 일하기: HANDOFF.md에서 SendMessage까지

한 레포를 세 세션이 나눠 만지면서 겪은 충돌과, 인수인계 문서·Orca·세션 간 메시징으로 정리해본 기록.

지난 2주 동안 Claude Code 세션 서너 개로 나눠서 진행했다. 데이터 전처리와 요소 매핑, 문서 작업이 각각 다른 세션에서 돌아갔고, 그중 셋은 같은 레포를 건드렸다.

처음에는 단순히 “컨텍스트가 길어지니 나누자” 정도였다. 그런데 며칠 지나자 세션 하나가 다른 세션이 아직 커밋하지 않은 파일 위에서 작업하고 있었고, 한 세션이 푸시한 게 다른 쪽 CI를 깨뜨려 force-push로 되돌아가는 일도 있었다.

이번 포스트는 모델이나 데이터 이야기가 아니라, 에이전트 세션을 여러 개 굴릴 때 세션끼리 어떻게 소통시킬 것인가에 대해 다뤄보고자 한다. 마침 이 기간에 Claude Code에 세션 간 메시징이 기능으로 들어왔고, 병렬 에이전트용 IDE라고 하는 Orca도 새롭게 써본 후기도 함께 정리해본다.

왜 세션을 나누게 되는가#

한 세션에서 다 하면 편하겠지만 여러 이유로 나누어 작업하는 것이 더 이점이 있다.

  • 컨텍스트 길이. 원천 데이터 진단, 스키마 설계, 적재, 검증을 한 대화에서 하면 요약(compaction)이 여러 번 일어나고, 초반에 정한 규약을 후반에 잊는다.
  • 작업 성격. “MEASURES 노드 1만 6천 건 적재”처럼 오래 걸리는 배치와, “이 관계는 무슨 근거로 만들었는지 pptx로 정리”처럼 대화가 많은 작업은 리듬이 다르다.
  • 병렬성. 다른 세션이 psql에서 학습 이력을 올리는 동안 이쪽은 정적 그래프 파이프라인을 확정할 수 있었다.

문제는 나눈 순간부터 세션들이 서로를 모른다는 점이다. 각 세션은 자기 대화만 알고, 파일 시스템과 git만 공유한다.

1단계: 파일 시스템으로 소통하기 — HANDOFF.md#

가장 먼저 시도한 일은 인수인계 문서를 레포 안에 두는 것이었다. docs/pipeline_v1/HANDOFF.md 하나에 다음 세션이 커밋하기 전에 반드시 알아야 할 것만 적었다.

구성은 대략 이랬다.

절내용
1. 레포 현황세 레포의 원격/로컬 상태, 태그 위치, 미푸시 커밋
2. 미커밋 변경이 세션이 만든 파일과 삭제한 파일 목록
3. 다른 세션과 겹치는 지점같은 레포에서 다른 세션이 만지고 있는 모듈
4. 사고 기록푸시 후 force-push로 되돌아간 원인 추정, 재푸시 금지 사유
R1~R7미해결 질문(학기 컬럼 존재 여부, 2022 성취기준 매핑 등)과 해소 여부

새 세션에는 이 파일 경로만 알려주면 됐다. 실제로 다른 세션이 이 문서를 읽고 “이 레포에 두 세션 작업이 섞여 있다 — 활동 파이프라인(그 세션) + 매핑(이 세션)“이라고 스스로 판단해서, git 트리를 건드리지 않고 자기 몫만 커밋했다. 다른 세션이 남긴 R7(2022 개정 문항–성취기준 매핑) 항목을 이쪽에서 확인하고 “해소”로 바꿔두는 식으로, 문서가 세션 간 공유 상태 역할을 했다.

이 방식의 장점은 명확하다.

  • 도구에 의존하지 않는다. 어느 에이전트든, 사람이든 읽을 수 있다.
  • git에 남는다. 나중에 왜 그 결정을 했는지 추적된다.
  • 세션이 죽어도 남는다.

단점도 명확하다.

  • 비동기다. 상대가 파일을 읽어야 전달된다. “지금 이 파일 건드리지 마”는 전달이 안 된다.
  • 사람이 라우터다. 어느 세션이 뭘 하는지, 누가 먼저 커밋할지는 결국 내가 터미널을 오가며 조율했다.
  • 문서 갱신을 잊으면 곧바로 낡는다.

돌이켜보면 이 기간에 내가 세션에 가장 많이 한 말이 “다른 세션에서 ~하고 있어”, “그쪽 끝나면 커밋할게”, “그 세션에 전달할 정보 정리해줘”였다. 사람이 메시지 버스 역할을 한 셈이다.

2단계: 세션이 서로를 찾게 하기 — ListAgents / SendMessage#

Claude Code는 원래 한 세션 안에서 서브에이전트(Agent tool)를 띄우고 그들과 SendMessage로 대화하는 구조는 있었다. 이번에 바뀐 건 그 범위가 세션 밖으로 넓어졌다는 점이다.

공식 changelog 기준으로 흐름은 이렇다.

  • 2.1.224 (8/7) — “Claude Code sessions can now message each other, on any of your machines, with ListAgents to discover them (macOS and Linux).”
  • 2.1.225 (8/8) — 다른 머신의 Remote Control 세션에도 이름으로 먼저 말을 걸 수 있게 됨. ListAgents가 name [ref] 형태로 보여준다.
  • 2.1.229 (8/12) — ListAgents가 끊긴 원격 세션을 offline, 클라우드 세션을 cloud로 표시.
  • 2.1.232 (8/13) — 프롬프트에서 @세션이름으로 멘션하면 Claude가 알아서 SendMessage로 그 세션에 전달. 이름이 유일하면 확인 없이 바로 보낸다.

즉 지금은 터미널 A에서 @B 이 스키마 바꿨으니 로더 쪽 확인해줘 라고 치면, B 세션이 그 메시지를 받는다. 실제로 8/11에 세션 하나가 ListAgents를 호출했다가 “No reachable agents.”를 받은 기록이 남아 있는데, 그때는 다른 세션들이 아직 구버전이었거나 아직 안정화 전이었던 것 같다. 며칠 뒤부터는 상대 세션이 목록에 잡혔다.

써보면서 느낀 이 기능의 성격은 “공유 워크스페이스”가 아니라 핸드오프 채널이다. 메시지를 보내도 상대 세션이 내 대화 기록, 열어둔 파일, 이미 승인한 권한을 물려받지는 않는다. (참고로 단일 머신 내에서는 로컬 Unix 소켓을 통해 메시지가 전달되므로 외부 서버를 거치지 않는다는 보안상 장점도 있다.) 그래서 잘 맞는 용도는 다음 정도다.

  • 변경 요약을 던지기: “Question 노드에 has_conflict 속성 추가했다. 로더 쪽 스키마 확인 필요.”
  • 질문하고 같은 턴에 답 받기: “psql learning_activity_qitem_link 적재 끝났어?” → 상대가 답하면 이어서 진행.
  • 영향 알리기: “내가 지금 --all 재실행 중이라 Neo4j 비었다가 다시 찬다. 30분 동안 검증 쿼리 돌리지 마.”

반대로 “이 작업 전체를 네가 맡아라” 같은 소유권 이전에는 여전히 HANDOFF.md 같은 문서가 낫다. 메시지는 짧게 사라지고, 문서는 남는다.

3단계: 세션 관리 자체를 도구에 맡기기 — Orca#

터미널 탭 서너 개를 오가는 게 불편해질 무렵 Orca를 알게 됐다. Orca는 Y Combinator 출신의 스타트업 Stably AI가 만든 MIT 라이선스 오픈소스로, 스스로를 IDE가 아니라 ADE(Agent Development Environment) 라고 부른다. “IDE는 사람이 코드를 편집하려고 만든 것이고, ADE는 사람이 코드를 편집하는 에이전트들을 지휘하려고 만든 것”이라는 포지셔닝이다.

핵심 모델은 단순하다.

  • 태스크 하나 = git worktree 하나 = 에이전트 터미널 하나. 같은 .git 객체를 공유하되 작업 디렉터리와 브랜치가 분리된다.
  • Claude Code, Codex, Gemini CLI 등 30여 개 CLI 에이전트를 그대로 띄운다. 프록시 없이 내 구독을 그대로 쓴다.
  • 각 에이전트에 hook을 심어(~/.orca/agent-hooks/claude-hook.sh) 세션 상태를 Orca 쪽으로 보고하게 한다. 그래서 어느 카드가 “작업 중 / 입력 대기 / 완료”인지 한 화면에서 보인다.
  • orca CLI로 worktree 생성, 터미널에 프롬프트 보내기(terminal send), 출력 기다리기(terminal wait)를 스크립트할 수 있고, 실험 기능으로 orca orchestration이라는 정식 태스크 디스패치/worker_done 대기 계층도 있다.

내가 실제로 얻은 이점은 두 가지였다.

  • 어떤 세션이 멈춰 있는지 보인다. 세션 하나가 권한 승인을 기다리는 채로 20분 방치되는 일이 줄었다.
  • worktree 분리가 강제된다. 앞서 말한 “다른 세션의 미커밋 파일 위에서 작업” 문제는 같은 디렉터리를 공유해서 생긴 것이었는데, worktree로 나누면 그 자체가 없어진다.

다만 이번 프로젝트는 레포가 셋이고 원천 데이터가 OneDrive 폴더에 흩어져 있어서, worktree 단위로 깔끔하게 나뉘지 않는 작업이 많았다. Orca의 오케스트레이션 계층까지는 쓰지 않았고, 세션 상태 대시보드 + 터미널 관리 용도에 머물렀다.

세 층을 어떻게 나눠 쓸까#

정리하면 세션 간 소통은 세 층으로 나뉜다.

층수단성격잘 맞는 것
상태 공유HANDOFF.md, git 태그/브랜치영속, 비동기소유권 이전, 사고 기록, 미해결 질문 목록
메시지ListAgents / SendMessage / @세션휘발, 준실시간변경 알림, 짧은 질문–응답, “지금 건드리지 마”
관제Orca 같은 ADE시각화, 격리누가 뭘 하는지 보기, worktree로 충돌 원천 차단

이번에는 1층만 있는 상태로 시작해서 2·3층을 중간에 얹었는데, 순서를 바꿨어야 했다. worktree로 먼저 격리하고, 문서로 상태를 남기고, 메시지는 그 위에서 짧게 쓰는 게 맞다. 메시지가 있다고 문서를 안 쓰면 세션이 죽었을 때 아무것도 남지 않는다.

What I learned#

  • 세션은 대화 기록만 알고, 파일 시스템과 git만 공유한다. 이 사실을 잊으면 같은 파일 위에서 두 세션이 겹친다.
  • 인수인계 문서는 도구가 생겨도 없어지지 않는다. 메시지는 흐르고 문서는 남는다.
  • SendMessage는 소유권 이전이 아니라 핸드오프다. 짧은 사실·질문·경고에 쓴다.
  • 충돌은 소통으로 푸는 것보다 격리(worktree)로 안 생기게 하는 게 싸다.
  • 사람이 라우터 역할을 하고 있다면, 그건 도구가 없어서가 아니라 아직 구조를 안 잡아서다.

다음 프로젝트에서는 시작할 때 세션 수와 worktree 경계를 먼저 정하고, HANDOFF.md 템플릿을 레포에 넣어둔 뒤 세션을 띄울 생각이다.

참고#