# Git Worktree 병렬 개발: 브랜치와 작업 디렉터리를 섞지 않는 표준 흐름

> Canonical: https://vxi.me/ko/posts/git-worktree-parallel-development/
> Locale: ko-kr
> Published: 2026-03-25
> Updated: 2026-03-25
> Markdown: https://vxi.me/ko/posts/git-worktree-parallel-development.md

여러 기능을 동시에 개발하려고 할 때 가장 먼저 떠올리는 방법은 branch를 여러 개 만들고 하나의 repository 디렉터리에서 계속 `checkout`하는 방식입니다. 짧게는 쓸 만하지만 새 기능을 개발하면서 서비스를 띄우고, 동시에 운영 bug까지 고치기 시작하면 금방 상태가 섞입니다.

저는 결국 다른 방식으로 거의 고정했습니다. **main 작업 디렉터리는 안정 branch만 유지하고, 실제 개발은 `git worktree`로 기능마다 별도 디렉터리를 만든다**는 방식입니다. 같은 머신에서 여러 feature branch를 동시에 다룰 수 있고 디렉터리, 실행 프로세스, build 산출물이 서로 덜 오염됩니다.

Git 병렬 branch 개발, Git worktree 사용법, Worktree 표준 workflow를 찾고 있다면 이 글이 바로 그 흐름을 정리한 것입니다.

## branch를 계속 바꾸는 대신 Worktree를 선택한 이유

진짜 어려운 것은 새 branch를 만드는 방법이 아닙니다. **지금 테스트하는 코드가 정말 그 branch인지, 실행 중인 서비스가 그 디렉터리를 사용하는지, commit할 때 다른 기능의 잔여물이 같이 들어가지 않는지**를 보장하는 것이 더 어렵습니다.

하나의 repository 디렉터리에서 branch를 계속 바꾸면 다음 문제가 자주 생깁니다.

- 새 기능을 테스트한다고 생각했는데 서비스는 이전 디렉터리에서 실행 중
- 한 기능의 build 결과가 다른 branch를 오염
- `git add .`로 불필요한 파일까지 한꺼번에 stage
- 운영 bug 수정과 새 기능 개발을 동시에 하다가 작업 디렉터리가 계속 더러워짐

`git worktree`의 장점은 branch를 더 빨리 만드는 데 있지 않습니다. **기능마다 독립적인 작업 디렉터리를 갖게 한다는 것**이 핵심입니다. 저는 이 격리감이 가장 중요했습니다.

## 제가 고정해서 쓰는 디렉터리 규칙

나중에 더 복잡해지지 않도록 몇 가지 규칙을 먼저 정합니다.

- main repository: `/Users/kuangyongjie/charting_library`
- worktree root: `/Users/kuangyongjie/worktrees`
- branch 이름: `codex/<feature-name>`
- main branch: `master`

main 디렉터리는 안정 상태 유지와 merge에만 사용합니다. 실제 기능 개발은 별도 worktree에 둡니다.

```text
/Users/kuangyongjie/worktrees/hotspot-ff
```

작은 규칙처럼 보이지만 경로만 봐도 지금 main repository에 있는지 feature 디렉터리에 있는지 바로 알 수 있어서 꽤 유용합니다.

## 표준 흐름은 몇 단계뿐이지만 생략하지 않는 편이 좋다

먼저 worktree root를 만듭니다.

```bash
mkdir -p /Users/kuangyongjie/worktrees
```

main repository에서 별도 worktree와 새 branch를 만듭니다.

```bash
REPO=/Users/kuangyongjie/charting_library
WT=/Users/kuangyongjie/worktrees/<feature-name>
BRANCH=codex/<feature-name>
BASE=master

# 如果远端可用，先更新 master
# git -C "$REPO" pull --rebase central "$BASE"

git -C "$REPO" worktree add "$WT" -b "$BRANCH" "$BASE"
cd "$WT"
git branch --show-current
```

정상이라면 마지막에 다음 branch가 보여야 합니다.

```text
codex/<feature-name>
```

이 순간부터 해당 기능의 코드 수정, build, test는 모두 `"$WT"` 안에서만 한다고 정하면 됩니다.

## 개발과 테스트는 feature worktree 안에서만 한다

프런트엔드를 수정하든 backend를 실행하든 로컬 검증을 하든 해당 worktree 디렉터리 안에서만 진행합니다.

예를 들어 frontend가 하위 디렉터리에 있다면 그 branch의 경로에서 build합니다.

```bash
cd "$WT/trendradar-service/trendradar-web"
npm run build
# 生产混淆构建
# npm run build:multi-layer
```

저도 자주 겪었던 문제 중 하나가 “코드는 바꿨는데 화면은 계속 예전 상태”였습니다. 나중에 확인해 보면 코드 문제가 아니라 서비스 자체가 다른 디렉터리에서 실행 중인 경우가 많았습니다.

그래서 “분명히 수정했는데 왜 안 바뀌지?”라는 상황이면 framework부터 의심하기보다 **현재 process의 `cwd`**를 먼저 봅니다.

```bash
lsof -nP -iTCP:<port> | sed -n '1,5p'
ps -p <pid> -o pid,ppid,command
lsof -p <pid> | rg "cwd"
```

최종적으로 `cwd`가 다음 경로를 가리키는지 확인합니다.

```text
/Users/kuangyongjie/worktrees/<feature-name>/...
```

아니라면 지금 테스트하는 것은 애초에 그 branch가 아닐 가능성이 큽니다.

## commit할 때는 거의 `git add .`를 쓰지 않는다

병렬 worktree 개발에서는 일부러 보수적으로 stage합니다.

로컬 실행 산출물, cache, snapshot 등이 있을 수 있기 때문에 `git add .` 한 번으로 불필요한 파일이 같이 들어오기 쉽습니다.

예를 들어 다음 파일은 흔합니다.

- `__pycache__`
- `*.db`
- `snapshot_*`
- `.DS_Store`

저는 목표 파일만 명시적으로 추가합니다.

```bash
cd "$WT"
git add <file1> <file2> <file3>
git status --short
git commit -m "feat/fix: <summary>"
```

commit 전에 `git status --short`를 한 번 보는 작은 습관만으로도 저수준 실수를 많이 막을 수 있습니다.

## main branch로 merge하는 작업은 main 디렉터리에서만 한다

기능이 끝나면 merge는 main repository 디렉터리에서만 실행합니다.

```bash
cd /Users/kuangyongjie/charting_library
git branch --show-current   # 必须是 master

# 如远端可用，先更新
# git pull --rebase central master

git merge --no-ff codex/<feature-name>
```

중요한 것은 `merge --no-ff` 자체보다 **정말 main 디렉터리와 master branch에 서 있는지 확인하는 것**입니다.

다음 오류가 나온다면,

```text
MERGE_HEAD exists
```

이전 merge가 끝나지 않았다는 뜻일 가능성이 큽니다.

```bash
git merge --abort
```

그 뒤 pull과 merge를 다시 진행합니다.

remote에 push해야 한다면 다음 단계를 추가합니다.

```bash
cd /Users/kuangyongjie/charting_library
git push <remote> master
```

환경에 따라 remote 이름은 `origin`이 아니라 `central`일 수 있습니다. 이건 Worktree 문제가 아니라 각 repository 설정 차이입니다.

## merge 기록을 남기지 않아도 된다면 Fast-forward를 쓸 수 있다

“feature branch를 별도로 실행해서 테스트하고, 통과하면 merge commit 없이 main에 반영”하는 흐름이라면 fast-forward 방식도 가능합니다.

```bash
cd /Users/kuangyongjie/charting_library
git checkout master
git pull --rebase central master

git worktree add /Users/kuangyongjie/worktrees/hotspot-ff -b codex/hotspot-ff master
```

서비스도 반드시 feature worktree에서 실행합니다.

```bash
cd /Users/kuangyongjie/worktrees/hotspot-ff/trendradar-service
./venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 9544
```

다른 terminal에서 process가 정말 그 branch 디렉터리를 사용하고 있는지 확인합니다.

```bash
lsof -nP -iTCP:9544 -sTCP:LISTEN
ps -p <PID> -o pid,ppid,command
lsof -a -p <PID> -d cwd -Fn
```

이 확인은 꽤 중요합니다. “branch 테스트가 통과했다”는 결론이 사실은 다른 작업 디렉터리를 테스트한 결과인 경우가 생각보다 자주 있습니다.

## 장기간 쓰면서 고정한 규칙

Worktree를 계속 사용할 생각이라면 최소한 다음 규칙은 강하게 유지하는 편이 좋습니다.

- 기능 하나당 worktree 디렉터리 하나
- 하나의 대화나 task는 하나의 worktree에만 대응
- 작업 시작 전에 `git branch --show-current`
- commit 전에 `git status --short`
- merge는 main 디렉터리의 `master`에서만 수행

평범한 규칙처럼 보이지만 병렬 개발이 무너지지 않게 하는 것은 결국 이런 기본 동작입니다.

## 자주 발생하는 문제

### 로컬 화면이 계속 이전 버전으로 보일 때

코드부터 의심하지 말고 서비스 process의 `cwd`가 목표 worktree인지 확인합니다. 그다음 해당 branch에서 frontend를 다시 build했는지도 봅니다.

### `fatal: 'origin' does not appear to be a git repository`

Git 자체가 망가진 경우보다 remote 이름이 다른 경우가 많습니다.

```bash
git remote -v
```

실제 remote 이름으로 push하면 됩니다. 이 환경에서는 `central`을 사용하는 경우가 있습니다.

### `MERGE_HEAD exists`

이전 merge가 끝나지 않았다는 뜻입니다.

```bash
git merge --abort
```

그 뒤 다시 merge 흐름을 진행합니다.

### frontend만 바꿨는데 backend를 재시작해야 할까

보통은 아닙니다. 정적 자원을 다시 build하고 배포했다면 cache를 새로고침하는 것만으로 충분한 경우가 많습니다.
