1인 회사 시스템 build

git worktree로 배포본과 작업본 분리하기

git worktree로 배포 브랜치와 작업 브랜치를 디렉토리째 분리하는 법. git stash·checkout 왕복 없이 핫픽스에 대응하고, add·list·remove 세 명령과 .env·빌드 산출물이 worktree마다 분리되는 점까지 1인 안전 배포 기준으로 정리합니다.


혼자 빌드하고 혼자 배포하면 브랜치 전환이 잦아집니다. 새 기능을 짜다가 main에 핫픽스가 필요해지면, 작업 중인 변경을 git stash로 밀어 두고 git checkout main으로 옮긴 뒤 고치고, 다시 돌아와 stash를 풀어야 합니다. 한 디렉토리 안에서 브랜치를 갈아끼우는 한 이 왕복은 사라지지 않습니다.

방법은 브랜치를 옮기는 대신, 브랜치마다 디렉토리를 따로 두는 것입니다. git worktree는 한 레포에서 여러 브랜치를 각각 별도 디렉토리로 동시에 체크아웃합니다. 배포 중인 코드는 한 디렉토리에 그대로 두고, 새 작업은 다른 디렉토리에서 합니다. stash도, 브랜치 전환도 필요 없습니다.

worktree가 stash·브랜치 전환과 다른 점

git stashgit checkout은 작업 디렉토리 하나를 공유합니다. 그래서 한 번에 한 브랜치만 펼쳐 둘 수 있고, 옮길 때마다 작업 트리 전체가 그 브랜치 상태로 바뀝니다. 빌드 산출물도 함께 덮어쓰여서, 돌아오면 다시 빌드해야 하는 경우가 많습니다.

worktree는 디렉토리를 추가로 만듭니다. .git 저장소는 하나를 공유하지만, 작업 트리는 디렉토리마다 독립입니다. main 디렉토리에서 빌드한 결과는 그대로 남고, feature 디렉토리에서 따로 빌드합니다. 두 브랜치를 동시에 열어 두고 한쪽에서 IDE를, 다른 쪽에서 빌드를 돌릴 수 있습니다.

기본 명령 세 개

핵심은 add, list, remove 셋입니다.

# 현재 레포 옆에 feature 브랜치를 별도 디렉토리로 펼친다
git worktree add ../core-feature feature/worktree-x

# 새 브랜치를 만들면서 동시에 펼친다
git worktree add -b feature/new-x ../core-new-x

# 현재 펼쳐진 worktree 목록 + 각자 어느 브랜치인지
git worktree list

# 작업이 끝난 worktree를 정리한다
git worktree remove ../core-feature

git worktree list의 출력은 이렇게 나옵니다.

/Users/me/work/core           a1b2c3d [main]
/Users/me/work/core-feature   e4f5g6h [feature/worktree-x]

원래 레포가 main worktree고, add로 만든 디렉토리가 추가 worktree입니다. 디렉토리를 rm으로 직접 지우면 메타데이터가 남으니, 정리는 git worktree remove로 하거나 직접 지웠다면 git worktree prune으로 흔적을 청소합니다.

1인 안전 배포에 어떻게 쓰나

운영은 단순합니다. main worktree는 늘 배포 가능한 상태로 두고, 기능 개발은 항상 별도 worktree에서 합니다.

  • 배포본 격리 — main 디렉토리는 배포 중인 코드 그대로. 여기서 빌드한 산출물을 건드리지 않습니다.
  • 핫픽스 즉시 대응 — 작업 중인 feature worktree는 그대로 두고, main worktree로 가서 바로 고치고 배포합니다. stash로 밀고 돌아오는 왕복이 없습니다.
  • 병렬 빌드 — 한쪽에서 긴 빌드가 도는 동안 다른 worktree에서 계속 작업합니다.
  • 리뷰·검수 — 리뷰할 브랜치를 별도 디렉토리로 펼쳐 두면, 내 작업 브랜치를 흩트리지 않고 돌려 볼 수 있습니다.

핫픽스 흐름을 명령으로 적으면 이렇게 됩니다.

# feature 작업 중 main에 긴급 수정이 필요해졌다
# 작업 디렉토리는 그대로 두고, main worktree로 이동만 한다
cd ../core            # main worktree
git pull
# 수정 → 빌드 → 배포
# 끝나면 작업하던 디렉토리로 돌아온다
cd ../core-feature    # 작업은 손대지 않은 그대로

빌드 산출물과 .env는 worktree마다 따로

여기서 헷갈리기 쉬운 부분이 환경입니다. worktree는 추적되는 파일만 브랜치 상태로 채웁니다. .gitignore에 걸린 것은 복사되지 않습니다. 그래서 빌드 결과 디렉토리(예: build/, node_modules/)와 .env 같은 비추적 파일은 새 worktree에 없습니다.

장점은 격리입니다. feature worktree에서 빌드해도 main의 빌드 산출물을 덮어쓰지 않습니다. 비용은 초기 세팅입니다. 새 worktree를 만들면 의존성 설치나 .env 배치를 한 번 더 해 줘야 합니다. Kotlin 멀티모듈처럼 빌드가 무거우면, worktree마다 빌드 캐시가 따로 쌓이는 점도 디스크에서 고려할 부분입니다.

.env는 복사보다 공통 위치를 심볼릭 링크로 거는 편이 관리가 낫습니다.

ln -s ../core/.env ../core-feature/.env

주의점 두 가지

첫째, 같은 브랜치를 두 worktree에서 동시에 체크아웃할 수 없습니다.

git worktree add ../core-2 main
# fatal: 'main' is already checked out at '/Users/me/work/core'

main이 이미 main worktree에 펼쳐져 있으면, 다른 디렉토리에서 main을 또 열 수 없습니다. 작업용 디렉토리는 항상 별도 브랜치로 만들면 이 충돌을 피합니다.

둘째, worktree를 지우기 전에 그 안의 변경을 먼저 챙겨야 합니다. 커밋·푸시하지 않은 변경이 있는 worktree를 remove하려 하면 git이 막습니다. 의도적으로 버릴 때만 --force를 씁니다. 정리 습관을 들이지 않으면 디렉토리와 메타데이터가 쌓이니, 작업이 끝난 worktree는 그때그때 remove합니다.

직접 써 본 메모

Kotlin 멀티모듈 모노레포를 혼자 빌드하고 배포하면서 이 구조로 옮겼습니다. main worktree는 배포 상태로 고정하고, 기능 작업은 별도 worktree에서 합니다. stash 왕복이 줄고, 빌드를 다시 안 해도 되는 점이 가장 컸습니다.

실제 워크플로우와 절감 효과는 더 써 보면서 정리하겠습니다.

도구는 완벽해서가 아니라 매일 쓰면서 막힌 데를 고치며 자리를 잡습니다. worktree도 그렇게 운영에 붙였습니다.

정리

핵심은 브랜치를 옮기는 대신 브랜치마다 디렉토리를 두는 것입니다. main worktree는 배포본으로 고정하고, 작업은 별도 worktree에서 하면, 핫픽스가 끼어들어도 작업 트리를 흩트리지 않고 대응할 수 있습니다. add·list·remove 세 명령이면 충분하고, 빌드 산출물과 .env가 worktree마다 분리된다는 점만 기억하면 됩니다.

혼자 회사 시스템을 만들면서 내린 이런 선택들을 매주 한 번 뉴스레터로 정리해 보냅니다. 더 깊은 기록이 필요하면 뉴스레터 구독으로 받아보셔도 좋겠습니다.

자주 묻는 질문

git worktree는 git stash·checkout과 뭐가 다른가요?

stash와 checkout은 작업 디렉토리 하나를 공유해 한 번에 한 브랜치만 펼칠 수 있습니다. worktree는 브랜치마다 별도 디렉토리를 만들어 여러 브랜치를 동시에 체크아웃하므로, 전환할 때 작업 트리가 덮어쓰이지 않고 재빌드도 없습니다.

worktree를 만들면 .env나 빌드 결과도 같이 복사되나요?

복사되지 않습니다. worktree는 git이 추적하는 파일만 채우고, .gitignore에 걸린 .env·build·node_modules 같은 비추적 파일은 새 디렉토리에 없습니다. 의존성 설치나 .env 배치를 한 번 더 해야 하며, .env는 공통 위치를 심볼릭 링크로 거는 편이 관리가 낫습니다.

같은 브랜치를 두 worktree에서 동시에 열 수 있나요?

없습니다. 한 브랜치는 하나의 worktree에만 체크아웃됩니다. 작업용 디렉토리를 항상 별도 브랜치로 만들면 이 충돌을 피할 수 있습니다.

다 쓴 worktree는 어떻게 정리하나요?

git worktree remove 명령으로 정리합니다. 디렉토리를 rm으로 직접 지웠다면 git worktree prune으로 남은 메타데이터를 청소합니다. 커밋·푸시하지 않은 변경이 있으면 git이 remove를 막으니, 의도적으로 버릴 때만 --force를 씁니다.

#git worktree#git#브랜치 전략#배포#1인 개발