게임 개발 버전 관리 심화: Git, Git LFS, Perforce 용량 최적화와 브랜치 전략
게임 프로젝트에서 대용량 에셋과 잦은 병합으로 저장소가 비대해지는 문제를 Git, Git LFS, Perforce 관점에서 분석하고 용량 최적화와 실전 브랜치 전략을 단계별로 정리합니다.

게임 프로젝트의 버전 관리 문제는 코드보다 대용량 바이너리 에셋에서 먼저 커진다. 텍스처, 사운드, 애니메이션, 레벨 파일은 변경 이력을 누적할수록 Git 저장소를 빠르게 비대하게 만들며 이 문제는 Git LFS의 포인터 관리 또는 Perforce의 스트림·워크스페이스 설계로 완화할 수 있다.
이번 버전 관리 구조 점검의 결론은 명확했다. 소규모 코드 중심 팀은 Git과 Git LFS를 엄격하게 분리해 사용하고 대형 바이너리 파일을 자주 동시 편집하는 팀은 Perforce를 우선 검토해야 한다. 어떤 도구를 쓰든 브랜치 수를 줄이고 잠금이 필요한 파일을 식별하는 일이 용량과 병합 비용을 동시에 낮춘다.
게임 프로젝트에서 저장소 용량은 왜 급격히 늘어나는가?
Git은 파일의 변경 이력을 커밋 객체로 보존한다. 텍스트 소스 코드는 줄 단위 차이가 작아 압축과 델타 저장에 유리하지만 PNG, PSD, WAV, FBX, Unreal Engine의 .uasset, Unity의 대형 바이너리 파일은 작은 수정도 새 바이너리 덩어리처럼 저장될 수 있다.
저장소 증가량은 대략 다음처럼 생각할 수 있다.
- : 에셋 i의 평균 파일 크기
- : 에셋 i의 의미 있는 변경 횟수
- : 커밋 메타데이터, 패키지 객체, 기타 오버헤드
예를 들어 500MB 시네마틱 영상이 열 번 교체되면 작업 트리에는 최신 파일 하나만 보여도 이력은 수 GB 단위로 커질 수 있다. 따라서 “현재 프로젝트 폴더 크기”와 “클론할 때 내려받는 전체 이력 크기”는 다르다.

Git 저장소 비대화의 대표 원인
| 원인 | 발생 방식 | 영향 | 우선 대응 |
|---|---|---|---|
| 대형 바이너리 직접 커밋 | PSD, WAV, MP4, FBX를 Git 객체로 저장 | clone·fetch 시간 증가 | Git LFS 또는 외부 에셋 저장소 사용 |
| 삭제 파일의 이력 잔존 | 현재 브랜치에서 지워도 과거 커밋에는 남음 | 저장소 전체 크기 유지 | 이력 정리 전 영향 범위 확인 |
| 생성물 커밋 | Library/, Temp/, DerivedDataCache/, 빌드 산출물 포함 | 불필요한 변경과 충돌 증가 | .gitignore 정비 |
| 대형 레벨 파일 동시 편집 | 하나의 바이너리 맵을 여러 사람이 수정 | 병합 불가 또는 손실 위험 | 파일 잠금과 작업 분할 |
Git LFS는 대용량 게임 에셋을 어떻게 관리할까?
Git LFS(Large File Storage)는 대형 파일 본문을 일반 Git 객체 대신 별도 LFS 스토리지에 저장한다. Git 저장소에는 실제 파일이 아니라 파일 내용의 SHA-256 OID를 가리키는 작은 포인터 파일이 커밋된다.
version https://git-lfs.github.com/spec/v1
oid sha256:0123456789abcdef...
size 524288000
이 구조는 Git 커밋과 브랜치 기록을 유지하면서도 일반 Git 객체 데이터베이스의 팽창을 줄인다. 다만 LFS도 파일의 각 버전을 보관하므로 대형 파일을 반복 교체하면 LFS 스토리지와 전송량은 계속 증가한다. Git LFS는 용량 문제를 없애는 도구가 아니라 저장 위치와 전송 방식을 분리하는 도구다.
Git LFS 적용 절차
-
추적할 확장자를 선정한다.
프로젝트에서 실제 원본으로 관리할 바이너리만 고른다. 예를 들어.psd,.fbx,.wav,.mp4는 후보가 될 수 있지만 빌드 중 생성되는 파일은 추적 대상이 아니다. -
.gitattributes에 LFS 규칙을 커밋한다.
*.psd filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
git lfs install
git lfs track "*.psd" "*.fbx" "*.wav" "*.mp4"
git add .gitattributes
git commit -m "chore: track large art assets with git lfs"
- 이미 들어간 대형 파일은 별도 계획으로 이전한다.
기존 Git 이력을 LFS로 옮기는git lfs migrate import는 커밋 SHA를 바꾼다. 보호 브랜치, CI 캐시, 외부 포크, 릴리스 태그에 영향을 주므로 개인 판단으로 실행하지 않고 백업·공지·재클론 계획을 먼저 만든다.
# 예시: 실행 전 미러 백업과 팀 공지가 필요하다.
git lfs migrate import --include="*.psd,*.fbx,*.wav" --everything
Git LFS 용량을 줄이는 운영 규칙
- 원본 PSD와 게임 런타임용 PNG·DDS를 모두 무조건 저장하지 않는다. 빌드 파이프라인에서 재생성 가능한 결과물은 배포 아티팩트나 에셋 패키지로 분리한다.
- 머지 가능한 텍스트 데이터는 가능한 한 텍스트 직렬화로 유지한다. Unity는 팀 설정과 파일 형태에 따라 YAML 기반 에셋 병합을 활용할 수 있다.
- LFS 잠금이 필요한 파일을 명시한다. 한 파일을 동시에 수정할 수 없다면 낙관적 병합보다 잠금이 안전하다.
git lfs lock Assets/Maps/Stage_03.umap
git lfs locks
git lfs unlock Assets/Maps/Stage_03.umap
- CI에서는 전체 LFS 객체가 항상 필요한지 확인한다. 코드 검사만 수행하는 잡이라면 LFS 다운로드를 생략해 전송량과 실행 시간을 줄일 수 있다.
Perforce는 Git LFS와 무엇이 다른가?
Perforce Helix Core는 중앙 서버 기반의 변경 목록(changelist) 모델을 사용한다. 대형 바이너리와 아티스트 중심 워크플로를 오래 전부터 주요 사용 사례로 다뤄 왔으며 파일 체크아웃과 배타적 잠금이 작업 흐름에 자연스럽게 포함된다.
| 기준 | Git + Git LFS | Perforce Helix Core |
|---|---|---|
| 기본 모델 | 분산형 Git + 별도 LFS 객체 저장소 | 중앙 서버 기반 변경 목록 |
| 오프라인 커밋 | 가능 | 제한적이며 서버 연결 의존도가 높음 |
| 대형 바이너리 | LFS 설정과 저장소 비용 관리 필요 | 대형 파일과 부분 동기화에 강점 |
| 파일 잠금 | Git LFS Locking으로 가능 | 체크아웃·잠금 워크플로가 기본적 |
| 브랜치 | 가볍고 빠르지만 에셋 중복·동기화 비용 주의 | Streams로 흐름을 구조화 가능 |
| 적합한 팀 | 코드 중심, 소규모~중간 규모 | 대형 에셋, 다수 아티스트, 대규모 팀 |
Perforce를 선택해도 모든 파일을 전체 동기화하면 로컬 디스크와 네트워크 비용이 커진다. 워크스페이스 뷰와 스트림을 이용해 담당자가 필요한 디렉터리만 받도록 설계해야 한다.
Perforce 워크스페이스 용량 최적화 체크리스트
- 워크스페이스 뷰를 역할별로 분리한다. 클라이언트 프로그래머가 고해상도 시네마틱 원본까지 받을 필요가 없다면 해당 경로를 제외한다.
- 스트림을
main,development,release처럼 제한적으로 운영한다. 기능마다 장기 스트림을 만들면 통합 비용이 커진다. - 대형 단일 파일은 잠금 규칙을 적용한다. Unreal Engine의 월드 에셋, 시퀀서 파일, 바이너리 머티리얼 같은 충돌 불가 파일이 우선 대상이다.
- 프로젝트 생성물은 서버에 제출하지 않는다. Unreal Engine의
DerivedDataCache,Intermediate,Saved와 플랫폼별 패키징 결과물은 보통 제외 대상이다.

게임 개발 브랜치 전략은 어떻게 설계할까?
게임 프로젝트의 브랜치 전략은 “기능 브랜치가 많을수록 안전하다”는 방식으로 시작하면 실패하기 쉽다. 코드와 바이너리 에셋이 함께 움직이는 프로젝트에서는 오래 살아 있는 브랜치가 최신 레벨·프리팹·애니메이션 변경과 멀어져 통합 비용을 키운다.
권장 기준은 짧은 기능 브랜치, 보호된 통합 브랜치, 명확한 릴리스 브랜치다.
flowchart LR
F[feature/* 짧은 작업 브랜치] --> D[develop 또는 integration]
D --> R[release/* 안정화 브랜치]
R --> M[main 릴리스 태그]
M --> H[hotfix/*]
H --> M
H --> D
권장 브랜치 역할
| 브랜치 | 역할 | 병합 규칙 | 수명 |
|---|---|---|---|
main | 배포 가능한 기준선 | 승인된 릴리스·핫픽스만 병합 | 장기 |
develop 또는 integration | 다음 빌드 통합 | CI 통과와 코드 리뷰 필요 | 장기 |
feature/* | 작은 기능 또는 버그 수정 | 빠르게 통합 브랜치로 병합 | 짧게 |
release/* | QA, 로컬라이징, 배포 준비 | 수정 범위를 안정화 작업으로 제한 | 릴리스 기간 |
hotfix/* | 운영 중 긴급 수정 | main과 통합 브랜치에 모두 반영 | 짧게 |
병합 충돌을 줄이는 3단계
- 작업 소유권을 먼저 나눈다. 하나의 레벨 파일이나 프리팹을 여러 명이 동시에 수정해야 한다면 파일 경계를 더 작은 단위로 쪼개거나 잠금을 적용한다.
- 최신 통합 브랜치를 자주 반영한다. 기능 브랜치가 며칠 이상 방치되지 않도록 작은 단위로 리베이스 또는 병합한다. 팀의 히스토리 정책에 맞춰 한 가지 방식을 일관되게 사용한다.
- 병합 전 게임 빌드와 에셋 검증을 자동화한다. 컴파일 성공만으로는 부족하다. 참조가 끊긴 에셋, 누락된 LFS 파일, 패키징 실패를 CI에서 확인한다.
Git과 Perforce를 함께 써도 될까?
가능하다. 다만 “같은 파일을 두 버전 관리 시스템에서 동시에 진실의 원본으로 관리하지 않는다”는 원칙이 필요하다. 예를 들어 게임 클라이언트 코드는 Git에서 대규모 Unreal Engine 콘텐츠 저장소는 Perforce에서 관리할 수 있다. 이때 빌드 시스템은 특정 Git 커밋 SHA와 특정 Perforce changelist 번호를 함께 기록해야 재현 가능한 빌드를 만들 수 있다.
Build 1.8.0
- Git commit: 4f3a91c
- Perforce changelist: 184205
- Unreal Engine: 5.4.x
- Target platform: Windows
이 메타데이터가 없으면 버그가 발생한 빌드가 어떤 코드와 어떤 에셋 조합으로 만들어졌는지 추적하기 어렵다.
자주 묻는 질문 (FAQ)
Git LFS를 쓰면 Git 저장소 용량 문제가 완전히 해결되나요?
아니다. 일반 Git 객체의 팽창은 줄지만 LFS 스토리지와 전송량은 파일 버전 수만큼 증가한다. 불필요한 생성물을 제외하고 큰 원본 파일의 교체 횟수와 보관 정책을 함께 관리해야 한다.
Unity 프로젝트의 Library 폴더를 Git에 커밋해야 하나요?
대부분 커밋하지 않는다. Library는 로컬에서 재생성되는 캐시 성격의 폴더다. 다만 팀의 엔진 버전과 패키지 잠금 정보는 별도 설정 파일을 통해 일관되게 유지해야 한다.
Perforce가 있으면 브랜치 전략이 필요 없나요?
필요하다. Perforce Streams는 통합 흐름을 관리하는 도구일 뿐이다. 안정화 브랜치와 개발 브랜치의 역할, 병합 시점, 파일 잠금 규칙이 없으면 스트림 수가 늘어나고 통합 비용도 함께 커진다.
정리: 도구보다 파일 흐름을 먼저 설계한다
Git LFS와 Perforce의 선택은 팀 규모만으로 결정되지 않는다. 대형 바이너리의 비중, 동시 편집 빈도, 오프라인 작업 필요성, CI·배포 환경을 함께 평가해야 한다.
이번 점검에서 다음 운영 원칙을 기준선으로 삼았다.
- 재생성 가능한 파일과 원본 에셋을 분리한다.
- 바이너리 충돌이 불가능한 파일은 분할하거나 잠근다.
- 기능 브랜치는 작고 짧게 유지한다.
- 릴리스 빌드에는 코드 커밋 SHA와 에셋 변경 번호를 함께 남긴다.
이 기준을 먼저 세운 뒤 Git LFS 또는 Perforce를 적용하면 저장소 용량 문제를 단순한 디스크 비용이 아니라 팀의 통합 비용까지 포함한 문제로 다룰 수 있다.



