"A 기능 개발 중인데, 갑자기 긴급 버그 수정 요청이 왔다." 이 상황에서 당신은 어떻게 하시나요?
들어가며: 당신은 어떤 방법을 쓰고 있나요?
실무를 하다 보면 반드시 이런 순간이 찾아옵니다. feature/payment 브랜치에서 결제 모듈을 한창 작업하고 있는데, 팀장님이 다가와 "운영 서버에 장애 났어요, 지금 당장 핫픽스 올려야 합니다"라고 합니다.
이 순간 대부분의 개발자는 다음 세 가지 방법 중 하나를 선택합니다.
방법
문제점
git stash 후 브랜치 전환
stash 충돌, 작업 컨텍스트 손실 위험
임시 커밋 후 브랜치 전환
커밋 히스토리 오염 (WIP, 임시 저장 커밋)
프로젝트를 새 폴더에 git clone
저장소 용량 2배, 클론 시간 낭비
세 방법 모두 불편하거나, 나중에 후폭풍이 남거나, 시간이 낭비됩니다. Git Worktree는 이 문제를 근본적으로 해결합니다.
Git Worktree란?
Git Worktree는 하나의 .git 저장소를 공유하면서, 여러 브랜치를 각각 다른 폴더에 동시에 체크아웃해서 작업할 수 있게 해주는 Git 내장 기능입니다.
Git 2.5 버전부터 내장 — 별도 설치 없이 바로 사용 가능
기존 작업 폴더와 내용은 절대 건드리지 않음
.git 폴더(커밋 히스토리, 설정 등)는 하나만 존재 — 저장소 용량 이중 소모 없음
my-api-project/ ← 원래 작업 폴더 (feature/payment 브랜치)
├── .git/ ← 이 하나의 .git을 두 폴더가 공유
├── src/
└── ...
my-api-hotfix/ ← worktree가 새로 만든 폴더 (hotfix/bug-01 브랜치)
├── src/ ← .git 폴더 없음. .git 파일(참조)만 존재
└── ...
🚨 시작 전 반드시 알아야 할 핵심 제약
동일한 브랜치를 두 개의 worktree에서 동시에 체크아웃할 수 없습니다.
이것이 Git Worktree의 유일하지만 가장 중요한 제약입니다.
# 현재 메인 폴더가 main 브랜치를 체크아웃한 상태라면
git worktree add ../my-api-copy main
# ❌ 아래 오류 발생
# fatal: 'main' is already checked out at 'C:/Dev/my-api-project'
각 worktree 폴더는 반드시 서로 다른 브랜치를 바라보고 있어야 합니다. 이 제약은 Git의 설계 원칙(하나의 브랜치는 하나의 HEAD만 가짐)에서 비롯된 것으로, 당연한 이유가 있습니다.
Step by Step 실전 가이드
시나리오
현재 위치: C:\Dev\my-api-project
현재 브랜치: feature/payment (한창 개발 중)
긴급 요청: main 기반으로 hotfix/bug-01 작성 필요
Step 1. 새 worktree 추가하기
상황에 맞는 명령어를 선택합니다.
상황 A: main을 베이스로 새 핫픽스 브랜치를 만들면서 작업 시작 (가장 흔한 케이스)
git worktree add -b hotfix/bug-01 ../my-api-hotfix main
-b hotfix/bug-01 : 새 브랜치를 생성
../my-api-hotfix : 생성할 폴더 경로 (현재 폴더 바깥에 생성)
main : 어느 브랜치를 기준점으로 삼을지 (베이스 커밋)
상황 B: 이미 원격 저장소에 존재하는 브랜치를 로컬에 열어야 할 때 (코드 리뷰, 협업)
# fetch를 먼저 실행해서 원격 정보를 최신화하는 것을 권장
git fetch origin
git worktree add ../my-api-hotfix origin/hotfix/bug-01
주의 (상황 B):origin/ 접두사 없이 브랜치명만 입력하면, 해당 이름의 로컬 브랜치가 없을 경우 오류가 발생합니다. origin/을 붙이면 원격 브랜치를 직접 참조합니다. 이 방식은 detached HEAD 상태로 시작되므로, 커밋이 필요하다면 git checkout -b hotfix/bug-01로 로컬 브랜치를 만들어야 합니다.
Step 2. 두 폴더를 동시에 IDE에서 열기
새로 생긴 my-api-hotfix 폴더를 VS Code나 Visual Studio에서 새 창(New Window) 으로 엽니다.
기존 창 → feature/payment 결제 기능 개발 계속
새 창 → hotfix/bug-01 긴급 버그 수정
⚠️ ASP.NET Core 개발자 필독: 포트 충돌 방지
두 프로젝트를 동시에 로컬에서 실행하면 같은 포트를 점유하려고 해서 오류가 발생합니다.
새 워크트리 폴더의 Properties/launchSettings.json에서 포트를 변경해 주세요.
// my-api-hotfix/Properties/launchSettings.json
{
"profiles": {
"MyApiProject": {
"commandName": "Project",
"applicationUrl": "https://localhost:5003;http://localhost:5002",
// ↑ 기존 5001/5000 대신 5003/5002로 변경
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
⚠️ dotnet restore 필요 여부
새 폴더는 물리적으로 새로 생성된 디렉토리이므로, bin/, obj/ 폴더가 존재하지 않습니다. 빌드 전에 한 번 실행해 주세요.
배포 후 릴리즈 브랜치(release/v2.1)에서 소규모 패치를 진행하면서, 동시에 develop 브랜치에서 다음 버전 개발을 이어가야 하는 경우에 이상적입니다.
마치며
Git Worktree는 화려한 기능이 아닙니다. 하지만 정확히 필요한 순간에 정확히 문제를 해결해 주는, 실무형 기능입니다.
git stash로 컨텍스트를 날리고, WIP 커밋으로 히스토리를 오염시키고, 이중 클론으로 저장소를 낭비하던 습관을 이 기능 하나로 깔끔하게 끊어낼 수 있습니다.
특히 ASP.NET Core나 Spring Boot 같은 백엔드 프레임워크 환경에서는 포트 충돌과 dotnet restore / mvn install 등의 초기 세팅만 신경 써주면, 이후에는 별다른 불편함 없이 사용할 수 있습니다.
Git 2.5 이상이라면 지금 당장 터미널을 열고 연습해 보시길 권합니다. 한번 손에 익히면 다시는 이전으로 돌아가기 어려운 기능입니다.
추후 긴급 패치 MAIN 원격지 PUSH 하고 나서, 추후에 이를 원격지에서 PULL 할 경우 충돌 발생이 있습니다. 이를 위한 가이드이며 순차 명령에 맞게 사용하시면 되겠습니다.
순차 명령 (설명 + 실제 명령)
현재 상태 확인: 로컬 변경 및 브랜치 상태 확인
git status --porcelain --branch
원격 정보 가져오기: 원격 변경만 가져옴
git fetch origin
원격 병합 시도(초기): 바로 풀하면 로컬 변경으로 중단됨
git pull origin main
로컬 변경 임시저장: 추적/미추적 파일 포함해서 stash
git stash push -u -m "auto-stash: before pull origin/main"
저장된 stash 확인:
git stash list
원격에서 변경 가져와 병합: stash 후 다시 pull(성공, fast-forward)
git pull origin main
stash 다시 적용: 로컬 변경 복원 시도(여기서 충돌 발생)
git stash pop
충돌 수동 해결: 충돌 편집 후 저장 (Views/Home/Index.cshtml 수동 편집)
해결된 파일 스테이징:
git add Views/Home/Index.cshtml
병합 충돌 해결 커밋:
git commit -m "Resolve merge conflict: Index.cshtml"
남은 stash 확인 및 제거(선택):
git stash list
git stash drop stash@{0}
병합 결과 원격 푸시:
git push origin main
최종 상태 확인:
git status --short --branch
왜 기존 방식 대신 워크트리를 쓰는지
생각하신 내용이 정확히 맞습니다! 만약 현재 하고 있던 작업을 임시 저장(git stash)하거나 커밋하는 데 부담이 없고, 잠시 main 브랜치로 전환해도 괜찮은 상황이라면 굳이 git worktree를 생성할 필요가 없습니다. 평소처럼 git checkout main(또는 git switch main)으로 넘어가서 작업하시면 됩니다.
하지만 git worktree가 해결하고자 하는 핵심 문제는 "현재 진행 중인 작업의 흐름(Context)을 완벽하게 유지한 채로, 동시에 다른 브랜치를 건드려야 할 때"입니다.
왜 기존 방식 대신 워크트리를 쓰는지, 실무에서 마주치는 구체적인 차이점을 비교해 드리겠습니다.
일반적인 브랜치 전환 (git switch / checkout)
현재 feature/login 브랜치에서 코드를 짜다가 main 브랜치로 넘어가야 한다고 가정해 보겠습니다.
작업 흐름의 단절: 작업 중이던 코드를 git stash로 임시 저장하고 main 브랜치로 전환하면, 폴더 내의 파일들이 main 상태로 싹 바뀝니다.
IDE 새로고침 (인덱싱): VS Code나 IntelliJ 같은 IDE가 파일 변경을 감지하고 전체 프로젝트를 다시 인덱싱(분석)하기 시작합니다. 프로젝트가 클수록 꽤 오랜 시간이 걸립니다.
환경 충돌: 로컬 서버를 띄워놓고 테스트 중이었다면, 브랜치를 바꾸는 순간 코드가 달라지므로 서버를 껐다가 다시 켜야 합니다.
복귀의 번거로움:main 작업을 마치고 다시 원래 브랜치로 돌아와 git stash pop을 했을 때 충돌(Conflict)이 나면 하던 작업의 맥락이 끊기고 스트레스가 발생합니다.
Git Worktree를 사용할 때의 장점
Worktree는 "물리적인 폴더를 아예 하나 더 파서, 독립된 작업 공간을 띄우는 것"입니다.
원래 하던 작업은 그대로 방치:feature/login 브랜치를 열어둔 IDE 창과 로컬 서버는 끄지 않고 그냥 둡니다. 파일도, 터미널 상태도 100% 그대로 유지됩니다.
새 창에서 완벽히 분리된 작업:main 브랜치용 워크트리 폴더를 만들고, IDE 새 창(New Window)으로 엽니다.
동시 비교 가능: 두 브랜치의 코드를 양쪽에 띄워놓고 직접 눈으로 비교하며 코딩할 수 있습니다. (기존 방식으로는 불가능합니다)
깔끔한 뒷정리: 핫픽스가 끝나면 새 창을 닫고, 새로 파둔 폴더만 지우면 끝입니다. 다시 원래 하던 창을 띄우면 1초 전까지 하던 상태 그대로 작업에 몰입할 수 있습니다.
💡 요약하자면
현재 작업 공간이 깔끔하거나, 흐름이 끊겨도 상관없을 때:👉git switch main(일반적인 방식)
지금 짜던 코드를 절대 건드리고 싶지 않거나, 두 브랜치를 동시에 띄워놓고 작업해야 할 때:👉git worktree