버전 관리란?
버전 관리는 소스 코드와 프로젝트 파일의 변경 사항을 기록하고 추적하는 체계다. 단순히 파일의 이전 상태를 저장하는 것을 넘어, 누가 어떤 이유로 코드를 변경했는지 파악하고 문제가 발생했을 때 안정적인 상태로 복구할 수 있도록 한다.
협업 환경에서는 여러 개발자가 동시에 작업하기 때문에 변경 사항을 통합하는 과정에서 충돌이 발생할 수 있다. 버전 관리 시스템은 이러한 충돌을 관리하고, 변경 사항을 검토하며, 개발과 배포의 안정성을 확보하는 기반이 된다.
대표적인 버전 관리 시스템인 Git은 분산형 구조를 사용한다. 각 개발자는 저장소의 전체 이력을 로컬에 보유하며, 원격 저장소와 변경 사항을 주고받을 수 있다.
버전 관리의 핵심 목적은 다음과 같다.
- 변경 추적: 코드가 언제, 누구에 의해, 어떤 이유로 수정되었는지 확인한다.
- 협업 관리: 여러 개발자의 작업을 독립적으로 진행하고 통합한다.
- 안정성 확보: 검증된 코드와 개발 중인 코드를 분리한다.
- 문제 복구: 오류가 발생한 변경 사항을 찾아 수정하거나 이전 상태로 복구한다.
- 책임과 의사결정 기록: 코드 변경의 목적과 기술적 판단을 기록한다.
Git 브랜치 전략
브랜치의 개념
브랜치(Branch)는 특정 시점의 커밋을 기준으로 독립적인 작업 흐름을 구성하는 기능이다. 개발자는 기존 코드에 직접 영향을 주지 않고 새로운 기능을 개발하거나 버그를 수정할 수 있다.
예를 들어 운영 환경에서 사용 중인 코드가 main 브랜치에 있다면, 새로운 기능은 별도의 기능 브랜치에서 개발할 수 있다. 작업이 완료되면 코드를 검토하고 테스트한 뒤 기존 브랜치에 통합한다.
이 구조는 개발 중인 코드가 안정적인 코드에 영향을 주는 것을 방지한다.
주요 브랜치 전략
Git Flow
Git Flow는 브랜치의 역할을 명확하게 구분하는 전략이다.
main: 운영 환경에 배포되는 안정적인 코드develop: 다음 릴리스를 준비하는 통합 개발 브랜치feature/*: 새로운 기능 개발release/*: 배포 준비와 최종 검증hotfix/*: 운영 환경의 긴급 버그 수정
릴리스와 유지보수 과정이 복잡하고 정기적인 배포 주기를 갖는 프로젝트에 적합하다. 다만 브랜치 구조가 복잡하고 변경 사항이 오랫동안 분리될 수 있어, 배포 주기가 짧은 프로젝트에서는 관리 비용이 증가할 수 있다.
GitHub Flow
GitHub Flow는 main 브랜치를 중심으로 작업하는 단순한 전략이다.
main은 항상 배포 가능한 상태를 유지한다.- 기능이나 버그 수정은 짧은 수명의 작업 브랜치에서 진행한다.
- 작업이 완료되면 Pull Request를 생성한다.
- 코드 리뷰와 테스트를 통과하면
main에 병합한다. - 필요에 따라 즉시 배포한다.
브랜치 구조가 단순하고 지속적 통합 및 지속적 배포(CI/CD)와 결합하기 쉽다. 웹 서비스와 게임 플랫폼의 서버 개발처럼 작은 변경 사항을 자주 배포하는 환경에 적합하다.
Trunk-Based Development
Trunk-Based Development는 개발자가 짧은 수명의 브랜치를 사용하거나 공유 브랜치에 작은 변경 사항을 자주 통합하는 전략이다.
핵심은 장기간 분리된 개발 작업을 줄이고 통합 주기를 짧게 유지하는 것이다. 자동화된 테스트와 지속적 통합이 중요하며, 아직 공개할 수 없는 기능은 Feature Flag를 사용해 배포 코드와 기능 활성화 여부를 분리할 수 있다.
대규모 개발 조직이나 지속적 배포 환경에서 통합 지연과 병합 충돌을 줄이는 데 효과적이다.
브랜치 전략 선택 기준
브랜치 전략은 유행이나 팀의 선호만으로 결정할 문제가 아니다. 프로젝트의 배포 주기, 팀 규모, 운영 안정성, 릴리스 관리 방식에 따라 선택해야 한다.
| 기준 | 적합한 방향 |
|---|---|
| 소규모 팀, 빠른 배포 | GitHub Flow |
| 정기 릴리스와 복잡한 유지보수 | Git Flow |
| 지속적 통합과 빈번한 배포 | Trunk-Based Development |
| 여러 버전의 장기 유지보수 | 릴리스 브랜치를 포함한 전략 |
브랜치를 많이 만드는 것이 곧 안정성을 의미하지는 않는다. 중요한 것은 브랜치의 개수가 아니라 변경 사항을 얼마나 빠르게 검증하고 통합할 수 있는지다.
브랜치 운영 예시
새로운 매칭 시스템을 개발한다고 가정하면 다음과 같은 흐름을 구성할 수 있다.
# 최신 원격 저장소 정보 가져오기
git fetch origin
# 최신 main 브랜치에서 기능 브랜치 생성
git switch main
git pull --ff-only origin main
git switch -c feature/matchmaking
# 변경 사항 확인
git status
git diff
# 변경 사항 커밋
git add src/matchmaking
git commit -m "feat: add matchmaking service"
# 원격 저장소에 브랜치 게시
git push -u origin feature/matchmaking
이후 원격 저장소에서 Pull Request를 생성하고, 코드 리뷰와 자동화된 테스트를 거쳐 병합한다.
실제 운영에서는 브랜치 이름과 커밋 메시지 형식을 팀 규칙으로 정하는 것이 좋다. 규칙이 일관되면 변경 이력을 검색하고 작업의 목적을 파악하기 쉬워진다.
Pull Request와 코드 리뷰
Pull Request란?
Pull Request(PR)는 한 브랜치의 변경 사항을 다른 브랜치에 통합하기 전에 검토와 논의를 요청하는 절차다.
단순한 병합 요청이 아니다. 변경 사항의 목적, 구현 방식, 영향 범위, 테스트 결과를 확인하고 코드 품질과 시스템 안정성을 검증하는 협업 과정이다.
Pull Request에는 일반적으로 다음 정보가 포함된다.
- 변경 목적과 해결하려는 문제
- 주요 구현 내용
- 관련 이슈나 작업 항목
- 테스트 방법과 결과
- 데이터베이스 변경, API 변경, 배포 시 주의 사항
- 성능과 보안에 미치는 영향
좋은 Pull Request는 변경 사항을 이해하는 데 필요한 정보를 제공하면서도 검토 범위를 명확하게 유지한다.
Pull Request 작성 원칙
변경 범위를 작게 유지한다.
하나의 Pull Request에는 가능한 한 하나의 목적을 담는다. 매칭 로직 수정과 무관한 코드 포맷 변경, 대규모 리팩터링을 동시에 포함하면 리뷰가 어려워지고 문제 발생 시 원인을 추적하기도 힘들어진다.
변경 이유를 설명한다.
무엇을 수정했는지만 설명하지 말고 왜 기존 방식으로는 부족했는지, 어떤 대안을 검토했는지, 현재 방식을 선택한 이유가 무엇인지 기록한다.
검증 가능한 근거를 제공한다.
테스트 결과, 재현 방법, 성능 측정 자료, 스크린샷 등을 제공하면 검토자가 구현의 정확성을 판단하기 쉽다.
미완성 상태를 명확하게 표시한다.
아직 논의가 필요한 작업은 Draft Pull Request로 등록할 수 있다. 이를 통해 조기 피드백을 얻으면서도 완성된 변경 사항과 구분할 수 있다.
코드 리뷰의 목적
코드 리뷰(Code Review)는 다른 개발자가 변경 사항을 검토해 오류와 설계 문제를 발견하고, 코드의 이해 가능성과 유지보수성을 높이는 과정이다.
코드 리뷰의 핵심은 작성자를 평가하는 것이 아니라 변경 사항의 위험을 줄이는 것이다.
주요 검토 항목은 다음과 같다.
- 정확성: 요구 사항을 충족하며 예외 상황에서도 올바르게 동작하는가?
- 동시성: 여러 요청이 동시에 실행될 때 데이터 정합성이 유지되는가?
- 보안: 인증, 권한 검증, 입력값 검증에 문제가 없는가?
- 성능: 불필요한 DB 조회, 메모리 사용, 블로킹 작업이 발생하지 않는가?
- 유지보수성: 코드의 책임이 명확하고 중복이나 과도한 결합이 없는가?
- 테스트: 정상 경로뿐 아니라 실패와 경계 조건도 검증하는가?
- 운영 안정성: 로그, 모니터링, 타임아웃, 재시도, 롤백 전략이 적절한가?
실시간 게임 서버에서는 특히 중복 보상 지급, 동시 매칭 요청, 연결 종료 처리, 재접속, 세션 소유권과 같은 상황을 집중적으로 검토해야 한다.
효과적인 코드 리뷰 방법
코드 리뷰는 취향과 필수 규칙을 구분해야 한다. 개인적인 선호를 필수 수정 사항처럼 요구하면 리뷰가 불필요한 논쟁으로 변한다.
검토 의견은 문제와 근거를 중심으로 작성한다.
예를 들어 다음과 같은 의견이 있다.
이 로직은 재시도 요청이 동시에 들어오면 동일한 보상이 중복 지급될 가능성이 있습니다. 트랜잭션이나 멱등성 키를 사용해 중복 처리를 방지할 수 있는지 검토해 주세요.
이 의견은 위험 요소와 발생 조건, 개선 방향을 구체적으로 제시한다.
반면 단순히 코드가 마음에 들지 않는다는 의견은 개선 근거가 부족하다.
코드 스타일, 포맷, 명명 규칙처럼 자동화할 수 있는 항목은 린터와 포매터로 검사하는 것이 효율적이다. 리뷰어는 자동화 도구가 처리할 수 없는 설계, 정확성, 보안, 운영 위험에 집중해야 한다.
리뷰 승인과 병합 정책
Pull Request의 승인 정책은 프로젝트의 위험 수준에 맞춰야 한다.
일반적인 정책은 다음과 같다.
- 필수 CI 테스트가 통과해야 한다.
- 승인된 리뷰가 있어야 한다.
- 충돌이 해결되어야 한다.
- 보호된 브랜치에 직접 푸시하지 못하도록 제한한다.
- 운영에 영향을 주는 변경은 추가 검증이나 담당자 승인을 요구한다.
모든 변경에 동일한 수준의 승인 절차를 적용하면 개발 속도가 저하될 수 있다. 반대로 중요한 코드까지 최소한의 검토만 거치면 운영 장애 가능성이 증가한다.
따라서 인증, 결제, 보상 지급, 데이터 마이그레이션 등 영향도가 큰 영역에는 강화된 검토 기준을 적용하는 것이 합리적이다.
변경 이력 관리
변경 이력이란?
변경 이력은 코드와 설정의 수정 내역을 시간순으로 기록한 정보다. Git에서는 커밋(Commit)을 통해 변경 사항을 저장하며, 각 커밋은 특정 시점의 프로젝트 상태를 재구성할 수 있도록 한다.
커밋에는 변경 내용뿐 아니라 작성자, 시각, 메시지, 부모 커밋 등의 정보가 포함된다. 이를 통해 특정 기능이 언제 추가되었는지, 어떤 변경이 장애와 관련되어 있는지 추적할 수 있다.
변경 이력의 가치는 단순한 보관에 있지 않다. 과거의 의사결정과 현재의 코드 상태를 연결해 문제 분석과 유지보수를 지원하는 데 있다.
좋은 커밋 메시지 작성
커밋 메시지는 변경 사항의 목적을 간결하게 설명해야 한다.
다음과 같은 메시지는 정보 가치가 낮다.
fix
update
수정
작업 완료
무엇이 변경되었는지 파악하기 어렵기 때문이다.
반면 다음과 같은 메시지는 변경 목적이 분명하다.
feat: add matchmaking timeout
fix: prevent duplicate reward claims
refactor: separate session management
test: cover reconnect failure cases
docs: document WebSocket lifecycle
팀에서 Conventional Commits 규칙을 사용하면 커밋 유형을 일관되게 관리할 수 있다.
feat: 새로운 기능 추가fix: 버그 수정refactor: 외부 동작을 바꾸지 않는 구조 개선test: 테스트 추가 또는 수정docs: 문서 수정chore: 빌드, 설정, 유지보수 작업
커밋은 가능한 한 하나의 논리적 변경을 표현해야 한다. 관련 없는 수정 사항을 하나의 커밋에 섞으면 변경 이력을 분석하거나 특정 수정만 되돌리기 어려워진다.
변경 사항 추적과 문제 분석
Git은 변경 이력을 조사하기 위한 다양한 명령을 제공한다.
# 커밋 이력 확인
git log --oneline
# 파일의 변경 이력 확인
git log -- path/to/file
# 변경 사항 비교
git diff
# 특정 커밋의 변경 내용 확인
git show <commit-hash>
# 특정 코드가 변경된 커밋 탐색
git blame path/to/file
# 특정 커밋 이전 상태와 비교
git diff <commit-hash>^ <commit-hash>
git blame은 각 줄의 마지막 변경 커밋을 확인하는 데 유용하지만, 특정 줄을 변경한 사람이 곧 문제의 책임자라는 의미는 아니다. 해당 코드가 다른 변경 사항이나 설계 결정의 영향을 받았을 수 있으므로 전체 이력과 맥락을 함께 확인해야 한다.
장애 발생 시에는 문제가 처음 나타난 시점, 최근 배포된 변경 사항, 관련 로그와 메트릭을 연결해 조사한다. 필요하면 git bisect를 사용해 문제가 발생한 커밋을 이분 탐색 방식으로 좁힐 수 있다.
Revert와 Reset의 차이
변경 사항을 되돌릴 때는 작업 환경과 원격 저장소 반영 여부를 고려해야 한다.
Revert
git revert는 기존 커밋을 제거하지 않고 그 변경 사항을 반대로 적용하는 새로운 커밋을 만든다.
공유 브랜치나 운영 환경에서 이미 공유된 변경을 되돌릴 때 적합하다. 기존 이력이 유지되므로 어떤 변경을 되돌렸는지도 확인할 수 있다.
Reset
git reset은 현재 브랜치의 참조를 다른 커밋으로 이동시킨다. 옵션에 따라 스테이징 영역이나 작업 디렉터리에도 영향을 줄 수 있다.
아직 공유하지 않은 로컬 커밋을 정리할 때 유용하지만, 공유된 이력을 재작성하면 다른 개발자의 작업과 충돌할 수 있다. 특히 강제 푸시는 협업 중인 브랜치의 이력을 훼손할 수 있으므로 주의해야 한다.
운영 장애 대응에서는 이력 재작성보다 원인을 명확하게 기록하면서 안전하게 되돌릴 수 있는 방법을 우선 검토해야 한다.
태그와 릴리스 관리
Git 태그(Tag)는 특정 커밋에 버전이나 릴리스 이름을 부여하는 기능이다.
예를 들어 v1.0.0과 같은 태그를 사용하면 특정 버전의 소스 코드를 쉽게 식별할 수 있다. 릴리스 노트와 빌드 결과물을 해당 태그에 연결하면 운영 환경에 배포된 코드의 출처를 추적하기 쉬워진다.
버전 번호를 관리할 때는 프로젝트의 호환성 정책에 따라 Semantic Versioning을 적용할 수 있다.
- MAJOR: 호환되지 않는 변경
- MINOR: 하위 호환성을 유지하는 기능 추가
- PATCH: 하위 호환성을 유지하는 버그 수정
다만 모든 프로젝트가 반드시 이 규칙을 사용해야 하는 것은 아니다. 내부 게임 서버나 독립적인 서비스는 배포 식별자, 빌드 번호, 커밋 해시를 함께 관리하는 방식이 더 적합할 수도 있다.
중요한 것은 버전 이름 자체보다 실제 배포된 코드와 설정을 정확하게 식별하고 재현할 수 있는가이다.
실시간 게임 서버에서의 적용
실시간 게임 플랫폼에서는 버전 관리가 코드 협업뿐 아니라 서비스 안정성과 직접 연결된다.
매칭 시스템, 인증 서버, 세션 관리, 포인트 정산 기능이 동시에 개발된다면 각 기능을 독립적인 브랜치에서 작업하고 Pull Request를 통해 통합할 수 있다.
코드 리뷰에서는 단순한 구현 정확성뿐 아니라 다음 사항을 확인해야 한다.
- 여러 서버 인스턴스에서 동일한 요청이 중복 처리되지 않는가?
- DB 트랜잭션과 Redis 상태가 일관성을 유지하는가?
- 기존 클라이언트와 API 호환성이 유지되는가?
- 배포 중 기존 WebSocket 연결에 미치는 영향은 무엇인가?
- 문제가 발생했을 때 이전 버전으로 복구할 수 있는가?
- 데이터베이스 스키마 변경이 롤백 가능한가?
특히 데이터베이스 변경은 애플리케이션 코드 롤백만으로 해결되지 않을 수 있다. 기존 코드와 새 코드가 일정 시간 함께 동작할 수 있도록 호환성을 유지하는 배포 전략을 고려해야 한다.
또한 Git 저장소에 비밀번호, API 키, 인증서 개인 키 등의 민감한 정보를 커밋해서는 안 된다. 환경변수나 비밀 관리 시스템을 사용하고, 민감한 값이 노출되었다면 파일에서 삭제하는 것만으로 끝내지 말고 해당 자격 증명을 폐기하거나 교체해야 한다.
실무 적용을 위한 운영 원칙
버전 관리 체계를 효과적으로 운영하려면 팀에서 지킬 수 있는 명확한 규칙을 정해야 한다.
- 브랜치 이름과 커밋 메시지 형식을 통일한다.
- 작업 범위가 명확한 작은 커밋을 유지한다.
- 공유 브랜치에 대한 직접 푸시를 제한한다.
- Pull Request에 변경 목적과 테스트 결과를 기록한다.
- 자동화된 테스트와 코드 검사를 병합 조건으로 설정한다.
- 코드 리뷰는 개인의 취향보다 정확성과 위험을 중심으로 진행한다.
- 운영 릴리스에는 버전 태그와 변경 내역을 연결한다.
- 장애가 발생하면 관련 커밋과 배포 이력을 추적하고 복구 과정을 기록한다.
- 비밀 정보와 생성된 빌드 산출물을 적절히 분리한다.