Git 워크플로우 패턴은(는) 무엇을 하나요?
Git 버전 관리, 브랜칭 전략 및 협업 개발을 위한 모범 사례
활성화 시기
- 새 프로젝트의 Git 워크플로우 설정
- 브랜칭 전략(GitFlow, Trunk-Based, GitHub Flow) 결정
- 커밋 메시지 및 PR 설명 작성
- 병합 충돌 해결
- 릴리스 및 버전 태그 관리
- 새 팀원의 Git 관행 온보딩
브랜칭 전략
GitHub Flow (간단, 대부분에 권장)
지속적 배포와 소규모~중간 규모 팀에 가장 적합합니다.
main (보호됨, 항상 배포 가능)
│
├── feature/user-auth → PR → main에 병합
├── feature/payment-flow → PR → main에 병합
└── fix/login-bug → PR → main에 병합
규칙:
main은 항상 배포 가능한 상태main에서 기능 브랜치 생성- 검토 준비가 되면 Pull Request 생성
- 승인 및 CI 통과 후
main에 병합 - 병합 후 즉시 배포
Trunk-Based Development (고속 팀)
강력한 CI/CD와 기능 플래그를 사용하는 팀에 가장 적합합니다.
main (trunk)
│
├── 단기 기능 브랜치 (최대 1-2일)
├── 단기 기능 브랜치
└── 단기 기능 브랜치
규칙:
- 모든 구성원이
main또는 매우 짧은 브랜치에 커밋 - 기능 플래그로 미완성 작업 숨김
- 병합 전 반드시 CI 통과
- 하루에 여러 번 배포
GitFlow (복잡, 릴리스 주기 기반)
예정된 릴리스와 엔터프라이즈 프로젝트에 가장 적합합니다.
main (프로덕션 릴리스)
│
└── develop (통합 브랜치)
│
├── feature/user-auth
├── feature/payment
│
├── release/1.0.0 → main과 develop에 병합
│
└── hotfix/critical → main과 develop에 병합
규칙:
main은 프로덕션 준비 코드만 포함develop은 통합 브랜치develop에서 기능 브랜치 생성,develop에 병합develop에서 릴리스 브랜치 생성,main과develop에 병합main에서 핫픽스 브랜치 생성,main과develop에 병합
각 전략 사용 시기
| 전략 | 팀 규모 | 릴리스 주기 | 적합한 대상 |
|---|---|---|---|
| GitHub Flow | 모든 규모 | 지속적 | SaaS, 웹 앱, 스타트업 |
| Trunk-Based | 5명 이상 숙련자 | 하루 여러 번 | 고속 팀, 기능 플래그 |
| GitFlow | 10명 이상 | 예정된 릴리스 | 엔터프라이즈, 규제 산업 |
커밋 메시지
Conventional Commits 형식
<type>(<scope>): <subject>
[선택적 본문]
[선택적 각주]
타입
| 타입 | 용도 | 예시 |
|---|---|---|
feat | 새 기능 | feat(auth): add OAuth2 login |
fix | 버그 수정 | fix(api): handle null response in user endpoint |
docs | 문서 | docs(readme): update installation instructions |
style | 포맷팅, 코드 변경 없음 | style: fix indentation in login component |
refactor | 코드 리팩토링 | refactor(db): extract connection pool to module |
test | 테스트 추가/업데이트 | test(auth): add unit tests for token validation |
chore | 유지보수 작업 | chore(deps): update dependencies |
perf | 성능 개선 | perf(query): add index to users table |
ci | CI/CD 변경 | ci: add PostgreSQL service to test workflow |
revert | 이전 커밋 되돌리기 | revert: revert "feat(auth): add OAuth2 login" |
좋은 예 vs 나쁜 예
# 나쁜 예: 모호함, 맥락 부족
$ git commit -m "수정함"
$ git commit -m "업데이트"
$ git commit -m "작업 중"
# 좋은 예: 명확하고 구체적이며 이유 설명
$ git commit -m "fix(api): 503 Service Unavailable 시 재시도
외부 API가 피크 시간에 가끔 503 오류를 반환합니다.
최대 3회 시도하는 지수 백오프 재시도 로직을 추가했습니다.
Closes #123"
커밋 메시지 템플릿
저장소 루트에 .gitmessage 파일 생성:
# <type>(<scope>): <subject>
# # Types: feat, fix, docs, style, refactor, test, chore, perf, ci, revert
# Scope: api, ui, db, auth, etc.
# Subject: 명령형, 마침표 없음, 최대 50자
#
# [선택적 본문] - 무엇이 아닌 이유를 설명
# [선택적 각주] - Breaking changes, closes #issue
활성화: git config commit.template .gitmessage
병합(Merge) vs 리베이스(Rebase)
병합 (히스토리 보존)
# 병합 커밋 생성
git checkout main
git merge feature/user-auth
# 결과:
# * merge commit
# |\
# | * feature commits
# |/
# * main commits
사용 시기:
- 기능 브랜치를
main에 병합할 때 - 정확한 히스토리를 보존하려는 경우
- 여러 사람이 브랜치에서 작업한 경우
- 브랜치가 푸시되었고 다른 사람이 이를 기반으로 작업할 수 있는 경우
리베이스 (선형 히스토리)
# 기능 브랜치의 커밋을 대상 브랜치 위로 재작성
git checkout feature/user-auth
git rebase main
# 결과:
# * feature commits (재작성됨)
# * main commits
사용 시기:
- 로컬 기능 브랜치를 최신
main으로 업데이트할 때 - 깔끔하고 선형적인 히스토리를 원할 때
- 브랜치가 로컬 전용인 경우(푸시되지 않음)
- 자신만 브랜치에서 작업하는 경우
리베이스 워크플로우
# PR 전에 기능 브랜치를 최신 main으로 업데이트
git checkout feature/user-auth
git fetch origin
git rebase origin/main
# 충돌 해결
# 테스트가 여전히 통과하는지 확인
# 강제 푸시 (자신만 기여하는 경우에만)
git push --force-with-lease origin feature/user-auth
리베이스 금지 상황
# 다음 브랜치에서는 절대 리베이스하지 마세요:
- 공유 저장소에 푸시된 브랜치
- 다른 사람이 기반으로 작업한 브랜치
- 보호된 브랜치(main, develop)
- 이미 병합된 브랜치
# 이유: 리베이스는 히스토리를 재작성하여 다른 사람의 작업을 망가뜨립니다.
Pull Request 워크플로우
PR 제목 형식
<type>(<scope>): <description>
예시:
feat(auth): add SSO support for enterprise users
fix(api): resolve race condition in order processing
docs(api): add OpenAPI specification for v2 endpoints
PR 설명 템플릿
## 무엇
이 PR이 수행하는 작업에 대한 간략한 설명.
## 이유
동기와 맥락을 설명합니다.
## 방법
강조할 가치가 있는 주요 구현 세부 사항.
## 테스트
- [ ] 단위 테스트 추가/업데이트
- [ ] 통합 테스트 추가/업데이트
- [ ] 수동 테스트 수행
## 스크린샷 (해당하는 경우)
UI 변경 사항에 대한 전후 스크린샷.
## 체크리스트
- [ ] 코드가 프로젝트 스타일 가이드를 따름
- [ ] 자체 검토 완료
- [ ] 복잡한 로직에 주석 추가
- [ ] 문서 업데이트
- [ ] 새로운 경고 없음
- [ ] 로컬에서 테스트 통과
- [ ] 관련 이슈 연결됨
Closes #123
코드 리뷰 체크리스트
리뷰어용:
- 코드가 명시된 문제를 해결하는가?
- 처리되지 않은 엣지 케이스가 있는가?
- 코드를 읽고 유지보수하기 쉬운가?
- 충분한 테스트가 있는가?
- 보안 문제는 없는가?
- 커밋 히스토리가 깨끗한가?(필요시 squash)
작성자용:
- 리뷰 요청 전 자체 검토 완료
- CI 통과 (테스트, 린트, 타입체크)
- PR 크기가 적절함 (500줄 미만 권장)
- 단일 기능/수정과 관련됨
- 설명이 변경 사항을 명확히 설명함
충돌 해결
충돌 식별
# 병합 전 충돌 확인
git checkout main
git merge feature/user-auth --no-commit --no-ff
# 충돌이 있으면 Git이 표시:
# CONFLICT (content): Merge conflict in src/auth/login.ts
# 자동 병합 실패; 충돌을 해결하고 결과를 커밋하세요.
충돌 해결
# 충돌 파일 확인
git status
# 파일 내 충돌 마커 확인
# <<<<<<< HEAD
# main의 내용
# =======
# 기능 브랜치의 내용
# >>>>>>> feature/user-auth
# 옵션 1: 수동 해결
# 파일 편집, 마커 제거, 올바른 내용 유지
# 옵션 2: 병합 도구 사용
git mergetool
# 옵션 3: 한쪽 선택
git checkout --ours src/auth/login.ts # main 버전 유지
git checkout --theirs src/auth/login.ts # 기능 버전 유지
# 해결 후 스테이징 및 커밋
git add src/auth/login.ts
git commit
충돌 예방 전략
# 1. 기능 브랜치를 작고 수명이 짧게 유지
# 2. 자주 main으로 리베이스
$ git checkout feature/user-auth
$ git fetch origin
$ git rebase origin/main
# 3. 공유 파일 수정에 대해 팀과 소통
# 4. 장기 브랜치 대신 기능 플래그 사용
# 5. PR을 신속하게 검토 및 병합
브랜치 관리
명명 규칙
# 기능 브랜치
feature/user-authentication
feature/JIRA-123-payment-integration
# 버그 수정
fix/login-redirect-loop
fix/456-null-pointer-exception
# 핫픽스 (프로덕션 이슈)
hotfix/critical-security-patch
hotfix/database-connection-leak
# 릴리스
release/1.2.0
release/2024-01-hotfix
# 실험/POC
experiment/new-caching-strategy
poc/graphql-migration
브랜치 정리
# 병합된 로컬 브랜치 삭제
git branch --merged main | grep -v "^\*\|main" | xargs -n 1 git branch -d
# 삭제된 원격 브랜치에 대한 원격 추적 참조 제거
git fetch -p
# 로컬 브랜치 삭제
git branch -d feature/user-auth # 안전 삭제 (병합된 경우만)
git branch -D feature/user-auth # 강제 삭제
# 원격 브랜치 삭제
git push origin --delete feature/user-auth
Stash 워크플로우
# 작업 저장
git stash push -m "WIP: user authentication"
# stash 목록 보기
git stash list
# 가장 최근 stash 적용
git stash pop
# 특정 stash 적용
git stash apply stash@{2}
# stash 삭제
git stash drop stash@{0}
릴리스 관리
시맨틱 버저닝
MAJOR.MINOR.PATCH
MAJOR: 호환되지 않는 변경 사항
MINOR: 새 기능, 하위 호환
PATCH: 버그 수정, 하위 호환
예시:
1.0.0 → 1.0.1 (패치: 버그 수정)
1.0.1 → 1.1.0 (마이너: 새 기능)
1.1.0 → 2.0.0 (메이저: 호환성 깨짐)
릴리스 생성
# 주석 태그 생성
git tag -a v1.2.0 -m "Release v1.2.0
Features:
- 사용자 인증 추가
- 비밀번호 재설정 구현
Fixes:
- 로그인 리디렉션 문제 해결
Breaking Changes:
- 없음"
# 태그를 원격에 푸시
git push origin v1.2.0
# 태그 목록 보기
git tag -l
# 태그 삭제
git tag -d v1.2.0
git push origin --delete v1.2.0
체인지로그 생성
# 커밋에서 체인지로그 생성
git log v1.1.0..v1.2.0 --oneline --no-merges
# 또는 conventional-changelog 사용
npx conventional-changelog -i CHANGELOG.md -s
Git 설정
필수 설정
# 사용자 정보
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
# 기본 브랜치 이름
git config --global init.defaultBranch main
# Pull 동작 (병합 대신 리베이스)
git config --global pull.rebase true
# Push 동작 (현재 브랜치만 푸시)
git config --global push.default current
# 오타 자동 수정
git config --global help.autocorrect 1
# 향상된 diff 알고리즘
git config --global diff.algorithm histogram
# 컬러 출력
git config --global color.ui auto
유용한 별칭
# ~/.gitconfig에 추가
[alias]
co = checkout
br = branch
ci = commit
st = status
unstage = reset HEAD --
last = log -1 HEAD
visual = log --oneline --graph --all
amend = commit --amend --no-edit
wip = commit -m "WIP"
undo = reset --soft HEAD~1
contributors = shortlog -sn
Gitignore 패턴
# 의존성
node_modules/
vendor/
# 빌드 출력
dist/
build/
*.o
*.exe
# 환경 파일
.env
.env.local
.env.*.local
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS 파일
.DS_Store
Thumbs.db
# 로그
*.log
logs/
# 테스트 커버리지
coverage/
# 캐시
.cache/
*.tsbuildinfo
일반 워크플로우
새 기능 시작
# 1. main 브랜치 업데이트
git checkout main
git pull origin main
# 2. 기능 브랜치 생성
git checkout -b feature/user-auth
# 3. 변경 사항 및 커밋
git add .
git commit -m "feat(auth): implement OAuth2 login"
# 4. 원격에 푸시
git push -u origin feature/user-auth
# 5. GitHub/GitLab에서 Pull Request 생성
새 변경 사항으로 PR 업데이트
# 1. 추가 변경
$ git add .
$ git commit -m "feat(auth): add error handling"
# 2. 변경 사항 푸시
$ git push origin feature/user-auth
포크를 업스트림과 동기화
# 1. 업스트림 원격 추가 (한 번)
git remote add upstream https://github.com/original/repo.git
# 2. 업스트림 가져오기
git fetch upstream
# 3. 업스트림/main을 내 main에 병합
git checkout main
git merge upstream/main
# 4. 내 포크에 푸시
git push origin main
실수 되돌리기
# 마지막 커밋 되돌리기 (변경 사항 유지)
git reset --soft HEAD~1
# 마지막 커밋 되돌리기 (변경 사항 삭제)
git reset --hard HEAD~1
# 원격에 푸시된 마지막 커밋 되돌리기
git revert HEAD
git push origin main
# 특정 파일 변경 되돌리기
git checkout HEAD -- path/to/file
# 마지막 커밋 메시지 수정
git commit --amend -m "새 메시지"
# 잊어버린 파일을 마지막 커밋에 추가
git add forgotten-file
git commit --amend --no-edit
Git Hooks
Pre-Commit Hook
#!/bin/bash
# .git/hooks/pre-commit
# 린팅 실행
npm run lint || exit 1
# 테스트 실행
npm test || exit 1
# 비밀 정보 확인
if git diff --cached | grep -E '(password|api_key|secret)'; then
echo "비밀 정보가 감지되었습니다. 커밋이 중단되었습니다."
exit 1
fi
Pre-Push Hook
#!/bin/bash
# .git/hooks/pre-push
# 전체 테스트 스위트 실행
npm run test:all || exit 1
# console.log 문 확인
if git diff origin/main | grep -E 'console\.log'; then
echo "푸시하기 전에 console.log 문을 제거하세요."
exit 1
fi
안티 패턴
# 나쁜 예: main에 직접 커밋
git checkout main
git commit -m "버그 수정"
# 좋은 예: 기능 브랜치와 PR 사용
# 나쁜 예: 비밀 정보 커밋
git add .env # API 키 포함
# 좋은 예: .gitignore에 추가, 환경 변수 사용
# 나쁜 예: 거대한 PR (1000줄 이상)
# 좋은 예: 작고 집중된 PR으로 나누기
# 나쁜 예: "업데이트" 커밋 메시지
git commit -m "업데이트"
git commit -m "수정"
# 좋은 예: 설명적인 메시지
git commit -m "fix(auth): 로그인 후 리디렉션 루프 해결"
# 나쁜 예: 공개 히스토리 재작성
git push --force origin main
# 좋은 예: 공개 브랜치에는 revert 사용
git revert HEAD
# 나쁜 예: 장기 기능 브랜치 (몇 주/몇 달)
# 좋은 예: 브랜치를 짧게 유지 (며칠), 자주 리베이스
협업 규칙
브랜치 보호 규칙
main브랜치는 직접 푸시 금지, PR을 통한 병합만 허용main브랜치에 병합하기 전에 CI 통과 필수- 최소 1명의 리뷰어 승인 필요
- 브랜치가 최신 상태인지 확인 (outdated 브랜치 차단)
Git 에티켓
- PR 검토는 빠르게: 1-2일 이내에 검토 완료
- PR 크기 제한: 원칙적으로 500줄 미만 유지
- 커밋 그룹화: 관련 없는 변경 사항을 분리하여 커밋
- 의사소통: 변경 사항, 특히 공유 파일에 대해 팀에 알리기
- 책임감: 자신의 PR을 모니터링하고 피드백에 응답
Git Bash 셸 팁
# 현재 브랜치 확인
git branch
# 변경 사항 요약 보기
git status
# 로그 간략히 보기
git log --oneline --graph --decorate --all
# 특정 커밋에서 파일 변경 내용 보기
git show <commit-hash> --stat
# 커밋 간 차이점 보기
git diff <commit1> <commit2>
# 원격 정보 보기
git remote -v
Git 워크플로우 FAQ
Q: 기능 브랜치를 삭제해야 하나요?
A: 병합 후 로컬 및 원격 기능 브랜치를 정리하는 것이 좋은 관행입니다.
Q: 커밋 메시지를 다시 작성해야 하나요?
A: PR 병합 전에는 git commit --amend 또는 git rebase -i로 다시 작성하는 것이 좋습니다. 공개 히스토리는 다시 작성하지 마십시오.
Q: 어떤 병합 전략을 사용해야 하나요?
A: 대부분의 팀은 --no-ff 병합(병합 커밋 생성)을 선호합니다. 리베이스는 로컬 브랜치에 사용하세요.
Q: 기능 브랜치가 너무 오래되었습니다. 어떻게 해야 하나요?
A: main으로 리베이스하여 충돌을 해결하세요. 또는 새 브랜치를 만들고 변경 사항을 선택적으로 적용하세요.
Q: 잘못된 브랜치에 커밋했습니다.
A: git cherry-pick <hash>를 사용하여 올바른 브랜치로 커밋을 이동하고 git reset HEAD~1로 원래 브랜치에서 제거하세요.