본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 08. 21:05

Codex の Worktree モードに痺れて、3 本並列の AI 開発環境を整えた

요약

Codex 앱의 Worktree 모드는 여러 AI 개발 환경을 동시에 구축하는 과정을 버튼 클릭 한 번으로 간소화하여, 기존의 복잡했던 `tmux`, `git worktree add`, `direnv` 등의 수동 작업을 획기적으로 줄여줍니다. 하지만 이 기능만으로는 `.envrc` 같은 gitignored 파일이나 DB 포트 충돌과 같은 실제 개발 환경의 모든 문제를 해결할 수는 없습니다. 본 글은 Codex가 놓치는 부분을 커스텀 setup 스크립트와 `docker-compose` 설정을 결합하여 보완함으로써, 여러 브랜치에서 '진정한 의미'로 병렬 개발이 가능한 완벽한 워크플로우를 구축하는 방법을 제시합니다.

핵심 포인트

  • Codex Worktree 모드는 AI 개발 환경의 기반(worktree 생성 및 스레드 관리)을 버튼 하나로 자동화하여, 복잡했던 수동 설정 과정을 획기적으로 개선함.
  • Worktree 모드가 놓치는 `.envrc` 같은 gitignored 파일이나 `node_modules` 등의 필수 파일을 커스텀 setup 스크립트를 이용해 자동으로 주입할 수 있음.
  • 여러 브랜치에서 동시에 개발 서버를 실행할 때 발생하는 포트 충돌 문제는, 환경 변수 기반으로 포트 설정을 변경하고 해시 자동 할당 로직을 추가하여 해결해야 함.
  • Codex의 Worktree 기능은 단순한 모델 사용 인터페이스가 아니라, '병렬 개발 워크플로우' 자체를 관리하는 강력한 플랫폼 역할을 수행함.

1. はじめに

最近は GPT モデルの存在感が高まってきましたが、皆さんは Codex のデスクトップアプリを使ったことがありますか?

私は Worktree モードというのがお気に入りで、AI と並列で開発する手軽さがさらに一段上がった気がします。

複数のブランチを同時に動かしたいとき、ターミナル時代は

  • tmux で画面を分割して、
  • git worktree add

して、 - 各ペインで
direnv allow

して……

という段取りが必要でした。これがスレッド作成画面で 「Worktree」を選ぶだけ に圧縮されます。1 クリックです。

ただし、Worktree モードだけで並列開発が完成するわけではありません。.envrc
などの gitignored ファイルはコピーされないですし、DB 周りも 1 つの環境で複数の作業が動いていると競合が起きる可能性があります。このあたりは Codex の手が届かないので、自分で埋める必要があります。

この記事では、Codex App の Worktree モードに足りないものを setup スクリプトと skill、そして docker-compose 側の工夫で埋めて、Codex と複数ブランチを「真の意味で」並列で回せる環境を組み上げた話を書きます。

2. Codex の Worktree モードがすごい

最近はもっぱら Claude Codeよりは Codex を使っているのですが、理由としては Worktree モードのボタンを押すだけで並列開発の基盤が出来上がるから、というのが大きいです。

Claude Code をターミナルで使っていた頃、複数ブランチを並列で動かそうとすると、毎回こんな段取りが必要でした。

  • tmux や cmux で複数セッションを並べる
  • それぞれで
    git worktree add

する - worktree ごとに
.envrc

をコピーして direnv allow

  • どのセッションがどのブランチかを頭で覚えておく

ツール自体は強力ではあるものの、並列で動かす足場は全部自前なので、地味な作業を繰り返すのがネックでした。

Codex の Worktree モードなら、この作業がボタン 1 つ押すだけになるので、とても便利です。

スレッド作成時に Local / Worktree / Cloud から選び、Worktree を選択。Codex が裏で git worktree を切り、スレッドはその worktree に紐付いた状態で起動します。worktree のパスもブランチ名も意識する必要はありません。サイドバーに「どのスレッドがどの worktree にいるか」が並ぶので、5 本同時に走らせても迷子になりません。

さらにHandoffで、スレッドを Local モードと Worktree モードの間で行き来できます。Worktree で雑に試して、いけそうなら Local に持ち込んで仕上げる、という運用もできます。

つまり「並列開発のための環境構築」のうち、

  • worktree を作る
  • スレッドと紐付ける
  • スレッド間を行き来する

の 3 つは Codex が丸ごと面倒を見てくれるようになりました。Codex のデスクトップアプリが単に Codex のモデルを使用するためというよりは、並列開発のインターフェースとして機能してくれているイメージですね。

3. 3 つの詰まりどころを埋める

Worktree モードのおかげで、worktree を作るところは完全に楽になりました。が、実際に並列で動かしてみると、すぐ別の壁にぶつかります。

私が良く触るのは docker compose up
一発で API・フロント・DB が立ち上がる Rails / Next.js のプロジェクトです。これを 2〜3 ブランチで同時に動かそうとすると、3 つの壁が見えてきました。それぞれ順番に潰していきます。

空な gitignored ファイルは、setup スクリプトで埋める

Worktree モードで作られる worktree には、git 管理下のファイルしか入っていません。.envrc
node_modules
vendor/bundle
も、全部空です。

そのままだと direnv allow
も通らず、yarn dev
bundle exec
も動きません。スレッドを作るたびにメイン worktree から手でコピーするのも面倒です。

ここは Codex のlocal environment機能を使えば自動化できます。プロジェクトルートに.codex/local-environment/setup.sh
を置いておくと、Codex が新しい worktree を切ったあとに自動でそれを叩いてくれる、という仕組みです。

環境についての設定ページ。プロジェクトごとに設定することができる

やりたいのは「メイン worktree にある gitignored ファイルを、今いる worktree にコピーする」だけ。20 行ほどで収まります。

#!/usr/bin/env bash
# .codex/local-environment/setup.sh
set -euo pipefail
...

設計のポイントは 3 つ。

メイン worktree のパスはgit worktree list --porcelain
の最初の行から引く -
コピー対象は whitelist で明示——全 gitignored を取り込むとビルドキャッシュまで巻き込む -
——シンボリックリンクや権限の扱いが rsync -a
で再帰コピー cp -R
より安定

これで worktree 作成直後から direnv allow
が通り、yarn dev

동작 상태가 됩니다. Docker 환경은 아직 다른 과제가 있으므로 다음 단계로 넘어갑니다.

포트 경쟁은 env화 + 해시 자동 할당으로 해결

메인 브랜치에서 Rails 가 localhost:3002
를 점유한 상태에서, 별도의 브랜치 worktree 에서 docker 컨테이너를 시작하려 하면 포트 충돌로 에러가 발생합니다.

.envrc
을 복사하는 것만으로는 해결되지 않으며, 각 브랜치마다 다른 포트 번호를 할당해야 합니다. 프론트엔드 dev 서버와 DB 호스트 포트도 동일합니다.

먼저 docker-compose.yml
의 포트 지정을 환경 변수 기반으로 변경합니다.

services:
api:
ports:
...

:-3002
의 기본값이 있으므로, 메인 브랜치에서 .envrc
이 설정되지 않더라도 기존과 같이 작동합니다. worktree 에서 API_PORT=3456
.envrc
에 작성하면 해당 값이 우선시됩니다.

문제는 포트 번호를 어떻게 할당할 것인가입니다. 수동으로 순번을 부여하는 운영 방식을 시도했지만,

  • 브랜치를 생성할 때마다 다음 번호를 기억해야 함
  • 삭제된 브랜치의 포트를 회수하는 것이 번거로움
  • 다른 worktree 와 겹치지 않는지 항상 확인 필요

등, 병행 수가 증가할수록 파탄났습니다. 결국 브랜치名の 해시에서 자동 계산하기로 결정했습니다.

HASH=$(echo "$BRANCH" | shasum | cut -c1-4)
OFFSET=$((16#$HASH % 900))
API_PORT=$((3100 + OFFSET))
...

shasum
의 앞 4 자릿수를 16 진수에서 10 진수로 변환하여 900 으로 나누어 얻은 나머지를, 31003999 / 81008999 / 5600~6499 범위로 할당합니다. 같은 브랜치명이면 같은 포트가 되므로, "방금 전에 만든 브랜치는 몇 번이었나"를 기억할 필요가 없습니다.

900 범위를 4~5 개 동시에 실행하면, 기적으로 충돌하는 확률이 수% 정도 남습니다. 충돌이 발생하면 .envrc
을 수동으로 덮어쓰면 된다고 결정한 결과, 현재까지 실제 피해는 발생하지 않았습니다.

COMPOSE_PROJECT_NAME 으로 물리적으로 분리

DB 의 충돌은 가장 어려운 문제였습니다.

브랜치 A 에서 db:migrate
를 실행한 직후 브랜치 B 로 전환하면, 브랜치 B 의 Rails 는 브랜치 A 의 migration 이 적용된 DB를 찾습니다. 스키마가 예상과 다르면 테스트가 실패하거나, 레코드가 예상치 못한 구조로 저장되어 지루한 사고가 발생합니다.

처음에는 "각 브랜치마다 pg_dump
하여 별도의 DB 에 restore 해야 할 것"이라고 생각했지만, 결론적으로 불필요했습니다. Docker 의 메커니즘을 사용하면 DB 내용 전체를 브랜치별로 분리할 수 있습니다.

먼저 DB 이름을 .envrc
에서 변경 가능하도록 database.yml
을 변경합니다.

development:
primary:
database: <%= ENV.fetch('DB_NAME', 'api_development') %>

이로 DB_NAME=api_my_feature
.envrc
에 작성하면, 각 브랜치별 논리 DB 에 연결되도록 했습니다. 하지만 이것만으로는 해결되지 않습니다.

docker-compose.yml
의 끝부분을 확인합니다.

volumes:
postgres-db:
driver: local

postgres-db
이라는 볼륨 하나만 선언한 것으로, 특별한 지정은 없습니다. 그러나 docker volume ls
으로 Docker 가 실제로 어떻게 처리하는지 살펴보면 다른 측면이 보입니다.

DRIVER VOLUME NAME
local my_project_postgres-db

Docker 는 볼륨의 실제 이름을 <프로젝트명>_<볼륨명>
으로 만듭니다. 프로젝트명은 기본적으로 "compose 파일이 있는 디렉터리명"으로 결정되므로, 동일한 프로젝트명으로 시작하는 한 볼륨도 동일한 것을 공유한다는 것입니다.

DB 이름을 .envrc
에서 변경해도 실제 PostgreSQL 데이터가 들어있는 볼륨은 하나입니다. 여러 논리 DB 가 그 안에 공존하는 상태이므로, 어떤 브랜치에서 실행된 migration 의 영향이 다른 브랜치에 남는 사고가 발생할 수 있습니다.

해결책은 간단합니다. COMPOSE_PROJECT_NAME.envrc 에서 각 브랜치별로 변경하는 것만으로도 됩니다.

# .envrc
export COMPOSE_PROJECT_NAME=my_project_my_feature

이것으로 Docker 측의 볼륨명이 자동으로 분기됩니다.

DRIVER VOLUME NAME
local my_project_postgres-db
local my_project_my_feature_postgres-db
...

각 브랜치마다 새로운 볼륨이 자동으로 생성되어 DB 내용이 물리적으로 분리됩니다. migration 도 seed 도 모두 브랜치 로컬이 됩니다.

DB 이름만 변경하면 된다고 생각하지 않았던 것은, 실제로 시도해보지 않으면 발견하기 어려운 함정입니다. Docker Compose 가 프로젝트명 포함을 고려하여 볼륨을 관리한다는 규격 덕분에, 병렬 개발 경험이 많이 달라질 것 같습니다.

/worktree-setup 기술

全部まとめる ここまでの流れで、ブランチごとに必要な工程は 4 つです。

  • ブランチ名からポートと DB 名と COMPOSE_PROJECT_NAME

を計算 -
.envrc

に追記 direnv allow

  • DB コンテナを起動して
    db:create db:migrate

毎回手でやるには工程が多いので、Codex の skill にまとめてあります。.envrc

への追記はマーカーで冪等化しています。

# 想定:PROJECT_NAME / API_PORT / FRONT_PORT / DB_PORT / DB_NAME はこの直前で計算済み
# worktree-env セクションが既にあれば消してから書き直す
if grep -q '# worktree-env' .envrc; then
...

# worktree-env start

# worktree-env end

で囲んでおくことで、skill を何度流しても既存セクションだけが置き換わります。手書き部分と自動生成部分が共存できるので、.envrc

に API キー等を書いている場合でも安心です。

skill の冒頭にはリポジトリ検証のステップも入れています。他のリポジトリで間違って走らせるとポート番号の割り振り前提が崩れるので、git remote get-url

でリポジトリを確認し、想定と違ったら即中断する、というものです。本来であればどのプロジェクトでも通用するように設計すべきですが、私がメインで触るコードがこの 1 つのプロジェクトだけなので、念のためのガードレールとして入れています。

4. 実運用フロー

ここまでの仕込みが終わると、新しいタスクを始めるときの手順がだいぶ短くなります。実際の作業フローを書いておきます。

スレッドを Worktree モードで作る

Codex のサイドバーから新規スレッドを作り、モードにWorktreeを選択。ベースブランチ(main)と新しいブランチ名(例:feature/add-search-filter

)を指定して開始します。

これだけで、Codex が裏で worktree を切り、その worktree に紐付いたスレッドが起動します。

setup スクリプトが自動で走る

worktree が作られた直後、.codex/local-environment/setup.sh

が自動実行されます。

メイン worktree から .envrc

node_modules

vendor/bundle

などがコピーされて、新しい worktree も「最初から動く状態」になります。ここではユーザーが何かをする必要はありません。

/worktree-setup skill で初期化する

スレッドの最初のメッセージで /worktree-setup

を打ちます。skill が以下を順にやってくれます。

  • ブランチ名から
    COMPOSE_PROJECT_NAME

/ 各ポート / DB 名を計算 -
.envrc

# worktree-env

セクションを書き換え direnv allow

docker compose up -d db

bin/rails db:create db:migrate

ここまで完了すれば、このブランチ専用の DB と環境変数が揃った状態。docker compose up

を打てば全コンテナが立ち上がります。

「この工程もスクリプト化してしまえば楽なのでは?」という見方もあるとは思いますが、以下の理由から分離させています。

  • 必ずしも DB セットアップしなくてもよい作業がある

  • PR レビューをブランチチェックアウトして行いたいとき、など

  • 「コンテナ立ち上げる = コンテナのビルドが走る」なので、時間がかかる。そこまで待つのが面倒な場合が多い

AI に実装を任せる

あとは普通に実装タスクを投げるだけです。「issue #123 の機能を実装して」でも「このバグの原因を調査して」でもよく、Codex は worktree の中で自由にコードを書きます。

メインブランチで動いている docker compose up

には影響しないので、別のスレッドで他のタスクを並行させても、ポートも DB もコンテナも全部独立しています。私の場合は頭のキャパも考えて同時に走らせる上限を 3 本にしていますが、マシンリソースと脳内リソースが許せばもっと走らせられます。

片付ける

スレッドの作業が終わったら、/worktree-cleanup

skill を叩きます。skill は順番にこれをやります。

  • 現在のブランチが main / master でないことを確認。念のための措置です
  • docker compose down -v

でコンテナと volumeを一括で落とす - メイン worktree に戻る

  • git worktree remove

git branch -D

で worktree とブランチを削除

中身の主要部分はこんな感じです。

# main / master では実行しない(ガード)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [[ "$BRANCH" == "main" || "$BRANCH" == "master" ]]; then
...

docker compose down -v

-v

が地味に重要で、これでブランチ専用に作った volume も一緒に削除されます。COMPOSE_PROJECT_NAME

분지하여 두어 있으므로, 다른 브랜치의 volume 는 무방합니다.

main 에서 잘못 치면 main 의 volume 을 불식해버리므로, 처음에 git rev-parse

으로 브랜치명을 확인하고, main / master 라면 즉시 중단하도록 하고 있습니다.

습관지면 이 정도가 5 분 정도로 끝나갑니다. 스레드를 만드는 → skill 을 치는 → 구현을 맡기는 → 정리하는, 이러한 흐름에 탑승하면, 3 본 병렬로 작동해도 인지부하가それほど 높아지지 않습니다.

5. Trade-off

다음에, 이 방식의 단점도 적어두겠습니다.

DB volume 이 계속 증가합니다

COMPOSE_PROJECT_NAME

으로 브랜치별로 volume 을 분리하고 있으므로, 정리하는 것을 잊으면 브랜치를 자르는 때마다 volume 이 늘어납니다.

/worktree-cleanup

을 치면 문제없지만, worktree 를 지워버린 채 달력을 넘어가면, docker volume ls

에서 "이것은 어떤 volume だっけ" 라는 것이 대량으로 나옵니다.

지워진 worktree 에 연결된 volume 을 자동으로 찾아서 삭제하는 스크립트를 작성하면 일괄 청소할 수 있지만, 저는 주말에 docker volume ls

을 바라보고, 명확히 오래된 것을 docker volume rm

으로 지우는 정도로 끝냅니다.

포트가 가끔 충돌합니다

해시로 자동 번호를 할당하는都合, 4~5 본 동시에 작동하면 수%의 확률로 같은 포트로 부딪힙니다.

실상은 현재까지 나타나지 않았고, 부딪히면 .envrc

을 손으로 덮어씁니다만, 빈번하게 병렬수를 늘리는 사람은, 충돌 감지하고 다른 오프셋으로 백업하는 처리를 skill 에 추가하는 것도 괜찮다고 생각합니다.

Codex 의 Worktree 모드에 의존해버립니다

Codex 가 만드는 worktree 의 경로는 Codex 관리하므로, 다른 터미널이나 IDE 에서 접근할 때 약간 혼란스럽습니다. 실제 경로는 Codex 의 UI 에서 복사할 수 있으므로 치명적이지는 않지만, 터미널 메인으로 작업하는 사람이라면違和感이 있을 수 있습니다.

그리고 Handoff 로 Local ↔ Worktree 를 오가게 하는 기능, 처음에는 동작을 읽지 못했습니다. git 의 상태를 뒤에서 움직여주므로 편리하지만, 처음 보이면 "지금 어디에 있는 건가" 가 됩니다.

##マシンリソース

병렬수를 늘릴수록 당연히 리소스를 먹습니다. Rails / Next.js / Postgres 를 3 세트 동시에 시작하면, 메모리가 20GB 근처까지 채워집니다.

메모리에 여유가 없는 환경에서는, 병렬로 작동하는 것은 2 본까지 정도에 억제하는 것이 현실적입니다. Codex 자체의 인지부하도 병렬수에 비례하므로, 억지로 늘리면 효율이 떨어집니다.

6. 요약

처음에는 "Codex 의 Worktree 모드 정말 놀라" 에서 시작된取り組み였습니다. tmux 의 세션 분할이나 git worktree add

의 작업이 사라져서, 여러 브랜치를 동시에 AI 에 맡기는 감각이ガラッと 변했습니다.

하지만 실제로 병렬로 작동해보면, Codex 의 손에 닿지 않는 영역이 몇 개 있었습니다. gitignored 파일이 빈으로 생기는 것, 브랜치별로 환경변수를 바꾸는 것, 그리고 가장 불행한 DB 의 공유 문제 등. 이러한 기반周りのところ를 만지는 데는あまり 익숙하지 않아서 한 번 포기하려고 했지만, AI 와 벽打ち하고 "COMPOSE_PROJECT_NAME

으로 volume 의 이름 공간을 분리하는 것으로만 해결된다" 는 것을 깨달았고, 자신의 원하는 형태로 해결되었습니다.

从那부터 setup 스크립트 1 본과 skill 2 개를 추가하여, 현재의 구성에 정착했습니다. 총합 100 행 정도이며, 라이브러리에 의존하지 않으므로 유지보수는 거의 제로입니다.

Codex 는 AI 병렬 개발 도구로서, 현재 시점에서 가장 날카로운 것 중 하나일 것입니다. Worktree 모드의 좋은 점을 활용하면서, 부족한 부분은 스스로 100 행 씩 쓰는. 이 정도의 노력으로 AI 코딩의 편의성이 향상된다면, 충분히 원이 돌아간 투자라고 생각합니다. 여러분도 AI 코딩 도구, 잘하게 사용하겠습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
2

댓글

0