Claude Code Skills로 Git Worktree 병렬 개발 환경 자동화하기
하나의 레포, 여러 작업을 동시에
모노레포에서 개발하다 보면 한 가지 불편한 상황을 자주 마주칩니다. 기능 개발 중인데 급한 버그 수정 요청이 들어오거나, 리팩토링을 진행하면서 동시에 새로운 기능을 검증해야 하는 경우입니다. git stash로 컨텍스트를 전환하는 것은 번거롭고, 실수로 작업 내용을 날리기도 합니다.
git worktree는 이 문제를 깔끔하게 해결합니다. 하나의 레포에서 여러 브랜치를 각각 독립된 디렉토리로 체크아웃할 수 있습니다. 하지만 워크트리를 만드는 것 자체는 간단해도, 실제 개발 환경을 갖추려면 해야 할 일이 많습니다. 환경변수 복사, 포트 충돌 방지, Docker DB 격리, 의존성 설치 등 매번 반복되는 설정 작업이 뒤따릅니다.
이 글에서는 Claude Code Skills를 활용하여 이 전체 과정을 자동화한 경험을 공유합니다. /worktree 한 번이면 격리된 개발 환경이 갖춰지고, 작업 진행상황 추적과 Slack 알림까지 포함된 워크플로우를 만들었습니다.
Claude Code Skills란
Claude Code Skills는 ~/.claude/skills/ 디렉토리에 마크다운 파일로 정의하는 재사용 가능한 명령입니다. 사용자가 /worktree를 입력하면 Claude Code가 스킬 파일의 지침에 따라 순차적으로 작업을 수행합니다. 코드를 직접 실행하는 스크립트가 아니라, Claude가 따라야 할 절차를 기술하는 것이 특징입니다.
~/.claude/skills/worktree/
└── SKILL.md # 스킬 정의 파일
스킬 파일은 YAML frontmatter로 메타데이터를 정의하고, 본문에 실행 절차를 마크다운으로 기술합니다.
---
name: worktree
description: Create a new git worktree for parallel development.
user_invocable: true
---
워크트리 스킬이 해결해야 하는 문제들
단순히 git worktree add만으로는 부족합니다. 실제 개발 환경을 갖추려면 다음 문제들을 해결해야 합니다.
1. 환경변수 파일이 복사되지 않습니다
.env, .env.local 같은 파일은 .gitignore에 포함되어 있어서 워크트리 생성 시 자동으로 포함되지 않습니다. 원본 레포에서 복사해야 합니다.
# gitignored 환경변수 파일들을 원본에서 복사
for envfile in .env packages/backend/.env packages/personal/web/.env.local; do
if [ -f "$REPO_ROOT/$envfile" ]; then
mkdir -p "$WORKTREE_PATH/$(dirname "$envfile")"
cp "$REPO_ROOT/$envfile" "$WORKTREE_PATH/$envfile"
fi
done
그런데 여기서 한 가지 더 주의할 점이 있습니다. 원본의 .env에는 운영 DB 자격증명이 들어있을 수 있습니다. 워크트리에서 로컬 Docker DB를 사용할 예정이라면, 복사 후 포트뿐 아니라 유저/비밀번호도 Docker 기본값으로 교체해야 합니다.
# Before (운영 DB)
MYSQL_PORT=3306
MYSQL_USER=prod_user
MYSQL_PASSWORD=운영비밀번호
# After (로컬 Docker)
MYSQL_PORT=14306
MYSQL_USER=dev_user
MYSQL_PASSWORD=local_dev_password
2. 포트가 충돌합니다
main 브랜치에서 백엔드(3000), 웹(3001), 어드민(5173)을 띄우고 있는데, 워크트리에서도 같은 포트를 사용하면 당연히 충돌합니다. Next.js나 Vite는 포트 충돌 시 자동으로 +1을 해주지만, 그러면 어떤 포트에서 떠있는지 예측할 수 없게 됩니다.
결국 모든 서비스의 포트를 명시적으로 지정하는 것이 답이었습니다. 워크트리 번호에 따라 포트 대역을 할당하는 규칙을 만들었습니다.
| 서비스 | main | 워크트리 #1 | 워크트리 #2 |
|---|---|---|---|
| Backend | 3000 | 4000 | 5000 |
| Web | 3001 | 4001 | 5001 |
| Admin | 5173 | 4002 | 5002 |
| MongoDB | 27018 | 27019 | 27020 |
| MySQL | 13306 | 14306 | 15306 |
| Redis | 6379 | 7379 | 8379 |
이를 위해 백엔드의 main.ts에서 포트를 하드코딩 대신 환경변수로 읽도록 변경했습니다.
// Before
await app.listen(3000);
// After
const port = process.env.PORT || 3000;
await app.listen(port);
3. 데이터베이스를 격리해야 합니다
여러 워크트리가 같은 로컬 Docker DB를 공유하면, 한쪽에서 스키마를 변경했을 때 다른 쪽이 영향을 받습니다. 워크트리마다 별도 Docker 컨테이너를 띄워야 합니다.
Docker Compose의 -p (project name) 플래그와 포트 환경변수를 조합하면 깔끔하게 격리할 수 있습니다.
# 워크트리 #1 전용 MySQL + Redis
cd packages/saas/blog
MYSQL_HOST_PORT=14306 REDIS_HOST_PORT=7379 docker compose -p wt1 up -d
이를 위해 기존 docker-compose.yml의 포트를 환경변수화하고, 명시적 container_name과 volume name을 제거했습니다. -p wt1을 사용하면 Docker Compose가 자동으로 wt1-mysql-1, wt1_geulryeok-mysql-data 같은 이름을 생성합니다.
# docker-compose.yml - 포트 환경변수화
services:
mysql:
image: mysql:8.0
ports:
- "${MYSQL_HOST_PORT:-13306}:3306"
# container_name 제거 → -p 플래그로 자동 분리
새 Docker DB는 빈 상태이므로, MySQL의 경우 Drizzle ORM으로 스키마를 적용해야 합니다. 다만 drizzle:push는 인터랙티브 프롬프트를 띄우는데, Claude Code의 Bash 도구에서는 stdin을 전달할 수 없습니다. --force --strict false 플래그로 비인터랙티브 실행이 필요합니다.
# 올바른 방법: 플래그로 확인 생략
npx drizzle-kit push --force --strict false
# 안 되는 방법: stdin 파이핑
echo "yes" | pnpm drizzle:push # 무시됨
4. E2E 테스트가 하드코딩된 포트를 참조합니다
Playwright E2E 테스트 설정과 spec 파일에 http://localhost:3000이 하드코딩되어 있으면 워크트리에서 테스트를 실행할 수 없습니다. 환경변수로 오버라이드할 수 있도록 수정했습니다.
// playwright.config.ts
const BACKEND_PORT = process.env.PORT || '3000';
const ADMIN_PORT = process.env.ADMIN_PORT || '5173';
export default defineConfig({
use: {
baseURL: `http://localhost:${ADMIN_PORT}`,
},
webServer: [{
command: `PORT=${BACKEND_PORT} pnpm --filter backend dev`,
url: `http://localhost:${BACKEND_PORT}/health`,
}],
});
워크트리에서 E2E 실행 시 환경변수만 전달하면 됩니다.
PORT=4000 ADMIN_PORT=4002 MONGO_HOST_PORT=27019 pnpm test:e2e
진행상황 추적: worktree-status.md
워크트리에서 작업할 때 가장 아쉬운 점은 컨텍스트 단절입니다. Claude Code 세션이 끝나면 어디까지 했는지, 다음에 뭘 해야 하는지를 기억하지 못합니다.
이를 해결하기 위해 .claude/worktree-status.md라는 진행상황 파일을 워크트리 생성 시 자동으로 만듭니다. .claude/ 디렉토리는 이미 git에서 untracked이므로 .gitignore 수정이 불필요합니다.
# Worktree Status
## 기본 정보
- 브랜치: feature/new-dashboard
- 기준: main (a1b2c3d)
- 경로: /Users/username/project-feature-new-dashboard
- 생성일: 2026-02-10
## 포트
| 서비스 | 포트 |
|---------|-------|
| Backend | 4000 |
| Web | 4001 |
| MySQL | 14306 |
## 진행상황
| # | 작업 | 상태 | 비고 |
|---|--------------------|-------------|------|
| 1 | API 엔드포인트 추가 | done | |
| 2 | 프론트 컴포넌트 | in_progress | |
| 3 | E2E 테스트 | pending | |
Claude Code 세션이 시작되면 이 파일을 읽어서 이전 컨텍스트를 복원하고, 작업이 진행될 때마다 상태를 업데이트합니다.
Slack 알림: 작업 완료를 놓치지 않기
워크트리에서 Claude Code가 자율적으로 작업을 진행할 때, 완료 시점을 알 수 없다는 문제가 있었습니다. 이미 일일 리포트에서 사용하고 있는 Slack Webhook URL을 활용하여, 작업 완료나 확인이 필요한 시점에 알림을 보내도록 구성했습니다.
# .env에서 webhook URL 읽기
WEBHOOK_URL=$(grep "^SLACK_WEBHOOK_URL=" .env | cut -d= -f2)
# 작업 완료 알림
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"attachments": [{
"color": "#2196F3",
"blocks": [
{"type": "header", "text": {"type": "plain_text", "text": "워크트리 작업 완료"}},
{"type": "section", "text": {"type": "mrkdwn", "text": "*브랜치*: feature/new-dashboard\n*작업*: API + 프론트 구현 완료"}}
]
}]
}'
백엔드 서버 구동 없이 curl로 직접 Webhook을 호출하는 방식이라 어떤 환경에서든 동작합니다.
전체 스킬 실행 흐름
/worktree를 실행하면 다음 9단계가 순차적으로 진행됩니다.
Step 1: 레포 정보 파악 (루트 경로, 현재 브랜치, 기존 워크트리 목록)
Step 2: 작업 목표 확인 → 브랜치명 자동 생성 (feature/, fix/, refactor/)
Step 3: 형제 디렉토리에 워크트리 생성
Step 4: gitignored 환경변수 파일 복사 + 자격증명 교체
Step 5: 포트 할당 (앱 서비스 + Docker DB)
Step 6: Docker DB 컨테이너 구동 + 스키마 적용
Step 7: pnpm install
Step 8: .claude/worktree-status.md 생성
Step 9: 결과 안내 → 바로 작업 시작
Step 9 이후에는 새 세션을 열 필요 없이 바로 구현 작업을 시작할 수 있습니다. 셋업 과정에서 이미 워크트리 경로로 이동되어 있기 때문입니다.
필요했던 사전 코드 수정
스킬이 실제로 동작하려면 코드 수정이 필요한 부분이 있었습니다. 스킬 문서만으로는 해결되지 않는, 인프라 레벨의 변경들입니다.
| 파일 | 변경 내용 |
|---|---|
packages/backend/src/main.ts | PORT 환경변수 지원 |
docker-compose.yml (2개) | 포트 환경변수화, container_name 제거 |
playwright.config.ts (2개) | 포트 환경변수 지원 |
| E2E spec 파일 (3개) | 하드코딩된 localhost:3000 제거 |
이 변경들은 기존 동작에 영향을 주지 않습니다. 환경변수가 설정되지 않으면 기존 기본값을 사용하도록 fallback이 있기 때문입니다.
돌이켜보며
처음에는 단순히 git worktree add를 자동화하는 것이라 생각했는데, 실제로 만들다 보니 환경변수, 포트, Docker DB, E2E 테스트, 진행상황 추적, 알림까지 신경 써야 할 것이 많았습니다. 하나의 개발 환경을 완전히 격리하려면 생각보다 많은 레이어를 건드려야 한다는 것을 느꼈습니다.
하지만 한번 스킬로 정리해두니, 이후에는 /worktree 한 번으로 모든 것이 갖춰집니다. Claude Code Skills의 좋은 점은 코드가 아닌 절차를 기술하는 것이기 때문에, 프로젝트 구조가 바뀌면 마크다운만 수정하면 된다는 점입니다.
1인 개발에서 병렬 작업이 필요한 분들에게 참고가 되었으면 합니다.
관련 글
Claude Code Skills 실전: 브랜치 정리를 자동화한 cleanup-branch 스킬
병합 완료된 브랜치를 정리할 때마다 워크트리 해제, 디렉토리 삭제, 로컬/리모트 브랜치 삭제를 순서대로 기억해야 했습니다. Claude Code Skills로 이 과정을 자동화한 경험과 SKILL.md 전문을 공유합니다.
Claude Code Skills - AI 코딩 도구에 행동 규칙을 심는 법
Claude Code에 Skills를 설치하면 AI가 코드를 작성하는 방식 자체가 바뀝니다. TDD를 강제하고, 버그를 체계적으로 추적하고, 보안을 실시간으로 검토하게 만드는 과정을 기록했습니다.
Admin 패널에 Git 브랜치 뷰어를 만든 이유
GitHub에서는 볼 수 없는 로컬 브랜치와 worktree 상태를 웹 브라우저에서 한눈에 확인하고 싶었습니다. NestJS 백엔드에서 git CLI를 실행하고 결과를 파싱하는 방식으로 Admin 패널에 Git 대시보드를 구현한 과정을 정리합니다.