바이너리 머지 전략
비디오 게임 업계에서는 아직 여러 브랜치에 걸쳐 바이너리 파일을 머지하는 전략은 여전히 골칫거리입니다. 이 문제는 왜 이리 해결하기 어려울까요. 또 어떻게 문제를 좀 완화할 수 있을까요.

전부터 이 블로그에 올라온 글을 살펴보신 분들이라면 제가 깃을 굉장히 싫어한다는 걸 아실 수도 있습니다. 개인적으로는 좀 더 험한 표현을 사용해 깃을 씹기도 했고 또 공개된 장소에 ‘깃은 토이 프로젝트에 어울린다’는 말을 했다가 두고두고 까인 적도 있습니다. 실은 깃은 2024년 여름 현재 세계적으로 가장 널리 사용되는 형상관리도구이고 분산환경, 마이크로커밋과 마이크로브랜칭 같은 전략이 널리 사용되도록 만든 도구이기도 하며 현대 인터넷을 떠받치는 가장 중요한 운영체제 프로젝트 전체를 지탱하고 있다는 사실을 잘 알고 있습니다. 하지만 그런 대단한 도구라 하더라도 한 프로젝트에 여러 직군이 함께 일하며 코드 뿐 아니라 바이너리 데이터를 쉴 새 없이 커밋해야 하는 환경에서 깃은 잘 어울리지 않는다는 생각을 여러 차례 해 왔습니다. 특히 지난 프로젝트에서 처음으로 언리얼 엔진과 깃을 함께 사용했는데 그 경험이 너무나 파멸적이어서 앞으로 언리얼 엔진 기반으로 개발하려는데 형상관리도구로 깃을 사용하자는 사람이 나타나면 제가 직접 그 사람과 담판을 지어야겠다는 다짐을 했습니다.
깃은 GNU Linux 같은 거대한 프로젝트를 떠받치며 아무렇지도 않게 수많은 사용자의 동시 작업을 커버하고 있습니다. 제가 아무리 깃에 대해 불평을 늘어놓으며 이 도구로는 도저히 대형 프로젝트를 지탱할 수 없을 거라고 투덜거리더라도 이 도구는 현대 인터넷을 지탱하는 거대한 프로젝트를 뒷받침하고 있으며 이 사실은 전혀 변하지 않습니다. 하지만 이 도구는 이와 어울리지 않는 프로젝트에 이를 사용하려 할 때 온갖 이상한 문제를 일으킵니다. 가령 게임 프로젝트는 코드 뿐 아니라 다양한 바이너리 파일을 함께 관리해야 합니다. 게임디자인 관점에서 바이너리 파일에는 우선 전통적인 엑셀 파일이 있습니다. 형상관리 이야기를 할 때 엑셀 파일을 바이너리 포멧이라고 이야기할 때 이에 동의하지 않는 분이 계실 수 있다는 사실을 알고 있습니다. 하지만 지난 2007년 이후 엑셀 파일이 xml 파일 여럿을 압축한 압축 파일일 뿐이라 하더라도 어쨌든 그 결과인 xlsx 포멧 자체는 바이너리 포멧이며 이를 바이너리로 다루지 않으려면 엔지니어들이 그리 달가워 하지 않을 복잡한 접근이 필요합니다. 또 언리얼 엔진을 사용한다면 다양한 uasset 포멧 파일들이 있습니다. 게임디자인 경계를 넘어서면 수많은 사운드 파일, 스프라이트, 애니메이션, 스켈레탈메시, 애님몽타주 등 셀 수 없이 많고 또 그 용량 역시 엄청난 바이너리 포멧과 마주치지 않을 수 없습니다.
일단 깃은 이 도구가 처음 만들어질 때 이 세계에는 바이너리 포멧으로 된 에셋도 커밋 될 필요가 있음을 그리 깊이 인식하지는 않은 것 같습니다. 그도 그럴 것이 현대 인터넷을 지탱하는 리눅스 OS는 대부분 서버 환경에서 GUI 없이 동작하며 여기에는 바이너리 포멧으로 된 어떤 그래픽 에셋도 필요하지 않은 것 같습니다. 덕분에 바닐라 깃은 1메가보다 더 큰 파일을 받아들이지 않도록 만들어졌고 오랜 세월에 걸쳐 이 제한은 딱히 문제를 일으키지 않아 왔습니다. 또한 텍스트로 이루어진 코드의 세계에서 한 파일의 크기가 1메가를 넘긴다는 것 자체가 뭔가 잘못되었다는 신호 역할을 합니다. 설계가 잘못되어 한 파일에 너무 많은 기능이 포함되었거나 함수 단위로 분리해야 할 기능이 그냥 한 모듈 안에서 돌고 있을 가능성이 있어 1메가를 넘기지 않도록 파일을 관리하다 보면 자연스럽게 나쁜 설계 실수를 피할 수도 있었을 겁니다. 그런데 비디오 게임 프로젝트의 세계로 이동하면 바이너리 에셋 파일들은 순식간에 수 십에서 수 백 메가 단위가 당연해집니다. 아이패드에서 게임을 플레이 하다가 스크린샷을 찍으면 스크린샷 하나가 30메가에 달하는 PNG 파일인데 그 화면을 만들기 위해서는 수 십 메가 짜리 스켈레탈메시 여러 개, 이들을 움직이는 수 메가 짜리 애님몽타주, 이들이 움직일 때 나는 소리가 포함된 사운드 파일, 그리고 이들이 주고 받는 대미지를 연산하는데 필요한 수 십 메가에 달하는 엑셀 파일과 이를 변환한 데이터 파일들이 관여하고 있습니다. 30메가자리 스크린샷 한 장으로 표현되는 게임 화면 하나를 만들기 위해 이미 바닐라 깃의 제한을 아득히 넘어서는 거대한 바이너리 파일들이 필요합니다.
지난 프로젝트 경험으로부터 깃은 회사 프로젝트 뿐 아니라 개인적으로도 활용하기 거의 불가능하다는 결론을 내렸습니다. 덕분에 개인 버전관리에도 돌고 돌아 퍼포스를 선택했고 이후 아주 평화롭게 아무 문제도 일어나지 않고 개인 버전관리, 여러 기계 간의 파일 동기화, 기계에 따른 선택적 동기화 따위에 아무런 불편함도 없이 잘 유지해 오고 있습니다. 특히 퍼포스를 사용하기 시작하면서 파일을 모두 퍼포스의 클래식 디팟(Classic Depot) 형태로 저장하고 있는데 여러 사람이 동시에 작업하는 현대적인 환경에는 잘 어울리지 않을 수 있지만 사용자가 한 명이거나 한 자릿수인 환경에서는 경로 모양의 브랜치 모델이 훨씬 단순해 이해하고 사용하기에 편해 개인적으로는 클래식 디팟을 추천하는 편입니다. 한편 퍼포스를 본격적으로 대규모 프로젝트에 사용하려면 현대에는 스트림 디팟(Stream Depot) 형태로 저장하는데 클래식 디팟과 스트림 디팟의 가장 큰 차이는 스트림 디팟은 퍼포스 서버 관점에서 일종의 서버 수준의 워크스페이스에 가까운 개념으로 스트림 하위에 다른 스트림을 포함할 수 있다는 점입니다. 클래식 디팟의 주소 단위 브랜칭 모델은 각 워크스페이스가 원하든 원하지 않든 모든 브랜치를 동기화하고 이 상태를 피하고 싶으면 사용자 각각이 설정을 바꿔야 했지만 스트림 기반의 브랜칭 모델은 각각의 스트림을 다른 워크스페이스에 할당해 서로 다른 워크스페이스에서 원하지 않는 스트림을 워크스페이스 각각의 설정 변경 없이 받지 않을 수 있습니다.
그런데 퍼포스가 깃에 비해 비디오 게임 프로젝트에 넘쳐나는 바이너리 파일을 잘 처리하고 스트림 디팟을 통해 다중 사용자 환경에서 현대적인 브랜칭 모델을 잘 지원하고 있음에도 비디오 게임 프로젝트에서는 전통적으로 브랜칭 모델을 잘 사용하지 않아 온 것 같습니다. 그도 그럴 것이 퍼포스를 포함해 어떤 형상관리도구를 사용하더라도 비디오 게임 프로젝트에서는 브랜칭 비용이 다른 프로젝트에 비해 극적으로 높기 때문입니다. 일단 물리적으로 비디오 게임 프로젝트는 그 크기가 거대하기 때문에 브랜치 각각을 유지하는데 물리적으로 큰 스토리지가 필요합니다. 가령 오늘 글을 다 쓴 다음 플레이 하려고 미리 다운로드 해 둔 어떤 게임은 설치 용량이 25기가인데 이 말은 이 프로젝트 개발에 참여한 사람들 각각은 최소한 25기가보다 더 큰 빠른 스토리지가 필요했다는 의미입니다. 개인적으로 프로젝트에 참여하는 사람들이 형상관리도구로부터 다운로드 해야 하는 파일 크기는 설치 용량의 최소 5배 이상으로 추측하는데 이 추측에 근거하면 개발에 참여하는 모든 사람들이 단 하나의 브랜치를 유지하기 위해 최소 125기가짜리 빠른 스토리지를 사용해야만 한다는 이야기이기도 합니다. 여기서 필요에 따라 브랜치를 만들어 관리하기 시작하면 이 용량에 브랜치 수를 곱한 만큼의 스토리지가 필요합니다.
브랜치에는 코드 뿐 아니라 바이너리 모양으로 된 에셋도 모두 포함되어야 하기 때문에 프로젝트의 일부만 브랜칭 할 수 있도록 만들기는 아주 어렵습니다. 그래서 개인적으로 경험한 여러 프로젝트에서 언리얼 엔진, 퍼포스 조합으로 개발하며 최대한 소극적으로 브랜치를 만들곤 했습니다. 가령 개발기간 내내 단일 브랜치, 가령 trunk
나 main
같은 이름의 브랜치 하나만 사용해 개발하다가 서비스 직전에 가서야 브랜치를 나누기 시작하곤 합니다. 심지어 서비스 전에 사내 테스트나 포커스 그룹 테스트용 빌드를 만들 때도 브랜칭 하지 않고 단일 브랜치에서 필요에 따라 기능을 비활성화 해 표시되지 않게 하고 또 나오기를 원하지 않는 에셋을 일시적으로 데이터에서 제거해 단일 브랜치에서 특정 목적의 빌드를 만들어냅니다. 당연히 여기에는 큰 댓가가 따랐는데 가장 큰 문제는 빌드를 만들어낼 때까지 이 빌드에 포함되지 않는 기능의 개발에 제약이 생긴다는 것입니다. 개발이 불가능하지는 않았지만 기본적으로 기능을 숨긴 다음 특별한 조작이나 CLI를 통해 커맨드를 입력해야만 기능이 활성화 되도록 만들어 단일 브랜치에서 특정 빌드를 만드는데 방해가 되지 않도록 했는데 이는 글자로 써 놓고 보면 그럴듯해 보이지만 실제로는 이번 빌드에 포함되지 않는 기능을 개발하는 담당자들이 소극적으로 개발하게 만듭니다. 또 원하든 원하지 않든 특정 목적의 빌드에 포함되지 않아야 하는 숨겨진 기능에 관여하는 코드가 빌드 동작에 영향을 쉽게 끼칠 수 있고 이는 코드 뿐 아니라 에셋, 데이터 모두 마찬가지입니다.
단일 브랜치로 특정 목적의 빌드를 만들면 특정 체인지리스트를 기점으로 브랜치를 만들게 되는데 아무리 단일 브랜치 모델에서 빌드를 만들어낸다 하더라도 어느 시점에는 브랜치를 만들어 특정 목적의 빌드를 기존 단일 브랜치로부터 분리해 내지 않으면 안 됩니다. 다만 여느 프로젝트에서 브랜치는 목적에 따라 사용된 다음 다시 메인에 머지되는데 비해 비디오 게임 프로젝트에서 특정 목적의 빌드를 만들기 위해 생성한 브랜치는 그 사용 목적이 끝나면 그대로 버려지는 형태일 때가 많았습니다. 가령 포커스 그룹 테스트를 위해 메인 브랜치에서 개발하다가 어느 시점에 체인지리스트를 기점으로 브랜치가 나뉘고 나면 포커스 그룹 테스트 빌드를 집중적으로 테스트 해 이 빌드에서 생긴 문제는 브랜치에서 해결하고 같은 문제를 가지고 있을 메인 브랜치에도 함께 수정합니다. 이 과정을 반복해 포커스 그룹 테스트를 마치고 나면 이 브랜치에서 메인 브랜치에 적용되어야 할 변경사항 거의 대부분이 이미 반영된 상태이므로 브랜치를 메인에 머지하지 않고 브랜치를 그냥 폐기한 다음 다시 메인 브랜치에서 개발을 이어가곤 했습니다. 그리고 다른 빌드가 필요하면 다시 메인에서 특정 체인지리스트를 기점으로 브랜치를 나눈 다음 똑같이 대응하고 버리기를 반복합니다.
하지만 서비스를 시작할 때가 다가오면 더 이상 쓰고 버릴 용도의 브랜치로 빌드를 만들 수 없는데 이전까지 쓰고 버리는 브랜치 모델을 사용할 수 있었던 이유는 실제로 그 빌드는 목적을 달성하고 나면 버려졌기 때문입니다. 포커스 그룹 테스트 빌드는 테스트가 끝나면 더 이상 그 빌드를 유지보수 할 필요가 없습니다. 그래서 테스트 빌드의 수정사항을 그때그때 메인에 머지한 다음 테스트가 끝나면 브랜치 전체를 버려도 아무 일도 일어나지 않았습니다. 하지만 서비스를 시작하면 이 브랜치를 통한 빌드를 계속해서 유지보수 해야 하며 동시에 메인 브랜치에서는 개발을 계속해 나가야 하기 때문에 더 이상 브랜치를 버릴 수 없습니다. 그래서 서비스 브랜치에 수정사항이 발생하면 먼저 이를 서비스 브랜치에 적용한 다음 같은 수정사항을 반드시 메인 브랜치에도 반영해야 하는데 여기까지는 기존과 별로 다르지 않지만 이 브랜치는 이제 버려지지 않고 서비스가 중단되는 그 순간까지 유지되어야 한다는 점이 다릅니다. 비디오 게임 업계에서 브랜치 비용이 높기 때문에 최대한 브랜치를 만들지 않은 채 개발하고 서비스 시점처럼 어쩔 수 없는 상황이 되기 전까지 최대한 버티곤 한다고 이야기했는데 덕분에 서비스 직전에 가서 시작한 브랜치 관리는 팀에 심각한 혼란을 일으킬 수 있습니다. 가령 이전에는 메인 브랜치 하나만 신경 쓰고 어떤 문제가 생겨도 메인 브랜치에서만 해결하면 모든 일을 끝낼 수 있었지만 더 이상 버릴 수 없는 서비스 브랜치가 생기면 이때부터는 같은 작업을 여러 브랜치에 걸쳐 반복해야 하기 때문입니다. 사용자에 따라서는 이런 상황을 이해하거나 납득하지 못할 수 있습니다.
서비스 직전에 한참 바쁠 때 이런 혼란이 일어나면 수습하기 쉽지 않기 때문에 팀에 따라서는 서비스 시점보다 훨씬 이른 시점에 브랜치 모델을 도입해 팀 전체를 훈련 시키려는 시도를 하기도 합니다. 개인적으로 비디오 게임 프로젝트에서 브랜치가 다른 프로젝트에 비해 압도적으로 비용이 높다 하더라도 팀이 미리 훈련 되어 있으면 서비스 직전의 혼란을 없앨 수 있다는 점에서 이른 시점의 브랜치 모델 도입에 긍정적인 입장입니다. 그런데 브랜치 전략을 수립하는 사람들이 거의 대부분 엔지니어 그룹이어서 그런지 브랜치 모델을 도입할 때 비 엔지니어 그룹이 겪는 당혹스러운 문제를 잘 상상하지 못할 때가 있는 것 같습니다. 가장 대표적인 것이 바이너리 파일을 다루는 문제인데 엔지니어들이 직접 편집하고 브랜치 사이에 머지하는 파일 대부분은 텍스트 포멧입니다. 텍스트 포멧 관점에서 브랜치 사이에 같은 수정사항을 적용하는 비용은 그리 높지 않습니다. 물론 그마저도 같은 파일에 대한 동시 작업을 최소화 하는 모양으로 코드 구성을 변경해 더욱 편안하게 머지하도록 만들 여지도 있습니다. 반면 바이너리 파일을 주로 다루는 비 엔지니어 입장에서 같은 변경사항을 여러 브랜치에 걸쳐 머지해야 한다는 말은 다시 말해 여러 브랜치에 걸쳐 똑같은 작업을 여러 번 반복해야 한다는 의미입니다. 그나마 내용을 어렵지 않게 붙여 넣을 수 있을 가능성이 높은 엑셀 스프레드시트나 언리얼 데이터에셋을 주로 다루는 게임디자인 직군은 이 상황을 버틸 수 있을지 모르지만 아트 직군은 이 상황을 납득하기 쉽지 않을 수 있습니다.
게임디자인 직군에서 대표님 보고 빌드를 별도 브랜치로 만들어 개발하는 과정을 상상해 봅시다. 기존 trunk
브랜치만으로 일할 때는 브랜치에 신경 쓸 필요 없이 그냥 수정사항을 테스트한 다음 서브밋 하면 됐습니다. 그런데 Daepyonim-Bogo-Build
브랜치가 새로 생기자 작업 과정이 극적으로 복잡해집니다. 먼저 대표님 보고 빌드는 굉장히 중요하므로 이 빌드 위주로 테스트가 이루어지는데 이 때 발생한 여러 가지 수정사항은 이 브랜치에 직접 반영합니다. 게임디자인 관점에서 퀘스트 보상 아이템을 새로 만들어 퀘스트에 반영한 상황을 생각해 보면 아이템 데이터를 추가하고 이를 보상 데이터에 추가한 다음 퀘스트 보상이 보상 데이터를 가리키도록 데이터를 작성합니다. 이 때 여러 개의 엑셀 파일과 이들을 변환한 csv 파일이 생길 겁니다. 이들을 로컬에서 테스트 한 다음 정상 동작하는 것 같으면 서브밋 하는데 이 때 서브밋 할 브랜치는 Daepyonim-Bogo-Build
, 줄여서 DB2
브랜치일 겁니다. 그런데 이 변경사항은 아마도 이번 보고가 끝난 다음 trunk
에 반영되어 동일하게 유지되어야 할 것 같습니다. 그러면 이미 DB2
브랜치에 반영한 수정사항을 trunk
에도 반영해야 합니다. 문제는 여기서 시작됩니다.
만약 게임디자이너인 우리들이 엔지니어가 사용하는 텍스트 기반 환경에서 이런 수정을 하고 있었다면 똑똑한 머지 프로그램은 DB2
브랜치와 trunk
브랜치 사이에 차이를 비교한 다음 이를 보여주고 어느 부분이 이번 수정에 의한 것인지 물어본 다음 사용자가 선택한 부분만 trunk
브랜치로 보낸 결과를 새 파일로 만들어 주고 이 파일을 서브밋 하면 모든 작업이 끝날 겁니다. 그런데 게임디자이너인 우리들은 엑셀 파일을 포함한 바이너리 포멧을 사용하고 이들은 똑똑한 머지 프로그램이라도 차이를 비교해 보여주고 원하는 부분만 선택해 다른 브랜치로 보낼 수가 없습니다. 엑셀 파일에 변경사항을 두 브랜치에 반영하는 이상적인 시나리오는 먼저 DB2
브랜치의 엑셀 파일을 수정한 다음 각 엑셀 파일의 수정사항을 다른 새 엑셀 파일에 붙여 넣은 다음 trunk
브랜치로 전환해 잠시 새 엑셀 파일에 붙여 놨던 변경사항을 각각의 파일에 붙여 넣고 테스트한 다음 현재 브랜치에 서브밋 하는 것입니다. 그나마 엑셀 파일은 다른 엑셀 파일에 수정사항을 일시적으로 붙여 넣기 쉽지만 만약 이 사이에 언리얼 데이터 에셋 같은 별도 장소에 잠시 붙여 넣어 두기 좀 불안한 파일들은 사실상 같은 작업을 두 번 수행해야 합니다. 가령 캐릭터 한 마리를 구성하는 여러 에셋을 직접 가리키고 있는 데이터 에셋의 값을 하나 바꿨다면 이를 복사해 메모장 따위에 붙여 넣어 둘 수 있지만 여러 프로퍼티를 수정했다면 이렇게 하기 어렵습니다. 결국 각 브랜치마다 서로 다른 에디터를 실행해 서로 다른 브랜치의 같은 데이터 에셋에 같은 수정을 두 번 작업해야만 합니다.
그냥 DB2
브랜치에서 수정한 파일을 trunk
브랜치에 복사해 버릴 수는 없을까요? 보다 적은 인원이 개발하거나 한 사람이 개발하고 있다면 가능한 시나리오입니다. 하지만 우리는 이미 세 자리 수 사람들이 모여 개발하고 있고 같은 파일을 하루에도 몇번씩 여러 사람이 열어 수정하고 있습니다. 이런 상황에서 DB2
브랜치에서 엑셀 파일 하나에 라인 하나를 추가한 다음 이를 trunk
브랜치에 파일 째 복사하려고 하면 trunk
브랜치에서는 이미 여러 사람에 의해 여러 가지 수정이 일어나 파일을 그냥 붙여 넣으면 그 사이에 일어난 여러 수정사항이 사라질 수 있습니다. 파일을 직접 복사하는 방식의 머지는 분명 바이너리 파일을 비 엔지니어 관점에서 머지하는 꽤 괜찮은 방법이지만 여러 사람이 작업하는 환경에서 원하지 않는 수정사항 유실을 일으킬 수 있습니다.
하지만 비 엔지니어 관점에서 엔지니어들이 사용하는 텍스트 기반의 똑똑한 머지 프로그램의 도움 없이 바이너리 파일에 대해 같은 작업을 서로 다른 브랜치마다 여러 번 반복해서 수행해야 한다는 현실을 설득하기는 당연하면서도 쉽지 않습니다. 애초에 이전에 경험해 온 여러 프로젝트는 아마도 최대한 늦은 시점에 브랜치를 도입했을 테고 그 시점에 잘 훈련되지 않은 개발팀을 지탱하기 위해 이런 개념과 필요성에 공감하는 누군가가 팀이 겪을 고통과 혼란을 홀로 짊어지고 문제가 드러나지 않도록 하고 있었을 수 있습니다. 그래서 아주 쉽게 이전에 다른 프로젝트에서는 이런 거 안 해도 됐는데 왜 이런 걸 하는지 모르겠다는 불만을 들을 수 있습니다. 최대 다수의 최대 행복 이론에 근거해 누구 한 명의 희생으로 모든 사람이 행복한 결과는 올바르지 않은 것 같습니다. 때문에 팀이 겪을 고통과 혼란에 대비해 미리 모든 사람이 훈련해 소수의 희생에 기반하지 않도록 하는 것은 중요합니다. 다만 비 엔지니어 직군들이 바이너리 파일을 만드느라 똑같은 수정을 여러 브랜치에 걸쳐 수행하는 과정은 개선할 여지가 있다고 생각합니다.
마치 엔지니어들이 같은 파일에 머지가 자주 일어나지 않도록 하기 위해 담당자 별로 모듈을 분리하고 모듈에 따라 파일을 분리해 머지가 일어나더라도 같은 사람이 작업하는 파일 단위로 머지가 일어나도록 코드를 관리하는 것과 비슷한 전략을 바이너리 파일에도 적용해 나가야 합니다. 가령 아이템 데이터는 여러 사람에 의한 수정이 일어나는 대표적인 파일인데 어떤 사람은 퀘스트 보상을 위해, 또 어떤 사람은 던전 보상을 위해, 또 다른 누군가는 업적 시스템이나 전투 시스템을 지탱할 아이템을 입력하기 위해 같은 아이템 데이터를 수정합니다. 이런 파일을 여러 브랜치에 따라 머지하기 위해서는 정말 수정한 라인을 직접 붙여 넣는 것 이외에는 답이 없을 뿐 아니라 심지어는 라인 단위로 붙여 넣었음에도 브랜치에 따라 작업 유실이 일어날 가능성이 있습니다. 하지만 같은 파일을 여러 사람들이 수정하는 시나리오를 잘 살펴보면 서로 꽤 잘 구분된 목적에 따라 파일을 수정하고 있음을 알 수 있는데 만약 아이템 데이터가 워크시트에 따라 구분되어 있다면 누군가는 물약 아이템 워크시트만 수정하고 또 다른 누군가는 업적 데이터에 사용할 특수 아이템 워크시트만 수정하고 있을 가능성이 높습니다. 마치 여러 사람들이 작업하는 코드가 한 파일에 들어 있어 한 파일에 대한 머지가 일어나는 것과 마찬가지로 여러 사람이 서로 다른 목적으로 서로 다른 부분을 수정하고 있지만 이들이 모두 같은 파일에 저장되어 한 파일에 대한 머지가 일어나야만 하는 상황입니다. 이 때 파일을 작업 목적 단위, 심지어 작업자 단위로 구분해 라인 단위 머지가 일어나지 않도록 할 수 있습니다.
만약 DB2
브랜치에서 몬스터 체력을 수정한 다음 이를 trunk
브랜치로 가져오려고 할 때 trunk
브랜치의 몬스터 체력을 포함한 데이터를 오직 나 혼자만 수정하고 있음이 확실하다면 라인 단위로 머지하는 대신 그냥 DB2
브랜치의 파일을 직접 복사해 trunk
브랜치에 파일 단위로 머지할 수 있습니다. 작업자는 나 한 사람 뿐이어서 다른 사람의 작업이 유실될 가능성이 거의 없고 만약 누군가 trunk
브랜치의 파일을 수정했다 하더라도 빈도가 아주 적어 유실된 데이터를 쉽게 복구할 수 있을 겁니다. 작업자 개개인이 바이너리 파일 단위로 직접 파일을 복사해서 머지할 수 있도록 한 파일을 목적 단위, 또는 작업자 단위로 구분해 놓는 방법은 좀 무식해 보일 수 있습니다. 하지만 텍스트 파일을 주로 다루는 똑똑한 머지 도구의 도움을 받을 수 없는 입장에서 가장 쉬운 머지 방법은 파일을 직접 복사하는 방식으로 수행하는 머지이며 이 때 일어날 데이터 유실을 최소화 하기 위해 특정 파일에 대한 작업자 수를 최소화 하는 방식으로 파일을 구분하는 것은 바이너리 파일을 다뤄야 하는 작업자들이 브랜치의 세계에서 그나마 살아남아 서비스 전까지 생산성을 너무 많이 희생하지 않은 채 훈련할 수 있는 나쁘지 않은 방법이라고 생각합니다.
물론 서비스 단계에 돌입하면 서비스 브랜치를 대상으로는 절대 이렇게 작업해서는 안되며 그 때는 꼼짝 없이 모든 텍스트를 직접 하나하나 확인해 머지해야만 합니다. 그나마 게임디자인 직군은 이 상황에서 살아남을 여지가 있는 것이 서비스 브랜치에는 엑셀 파일이 없을 가능성이 아주 높기 때문에 우리들도 엔지니어들이 사용하는 똑똑한 텍스트 기반 머지 프로그램의 도움을 받을 수 있기 때문입니다. 대신 이 때는 서비스 브랜치에 반영되는 모든 수정사항 하나하나가 반드시 목적에 정확히 일치하는 데이터만 들어가도록 여러 사람이 확인하는 절차가 필요하며 이 때는 파일 단위 머지가 결코 하용되지 않을 겁니다. 물론 이 상황에서도 여전히 텍스트 기반의 똑똑한 머지 프로그램의 도움을 받을 수 없는 uasset 파일을 브랜치 사이에 머지 하는 일은 만만하지 않지만 이 역시 여러 브랜치 사이에 파일이 달라지지 않도록 하는 관리 방법을 동원해 어려움을 최소화 시킬 여지가 있습니다.
상업용 소프트웨어 개발에서 브랜치는 당연히 필요하지만 비디오 게임 프로젝트는 바이너리 파일이 많고 또 브랜치의 필요성이나 이를 다루는데 익숙하지 않은 비 엔지니어 직군이 포함되어 있다는 특징 때문에 전통의 소프트웨어 개발 프로젝트에서 쉽게 도입할 수 있는 브랜치 전략이 잘 통하지 않기도 하는 것 같습니다. 우리가 에픽이나 마이크로소프트가 아닌 이상 언리얼 엔진이 사용하는 다양한 에셋 파일 포멧과 엑셀 파일 포멧을 텍스트로 바꿀 수 있을 가능성은 낮기에 우리가 처한 상황 안에서 적당한 방법을 찾아야 하고 그 결과는 좀 무식해 보일 수 있지만 그나마 주어진 상황을 완화하는 방법이라고 생각합니다. 또 브랜치 전략을 수립할 때 프로젝트에 바이너리 파일을 주로 다루는 비 엔지니어 직군이 꽤 많다는 점을 충분히 감안해야 팀을 미리 성공적으로 훈련 시켜 서비스 직전에 고통과 혼란을 줄일 수 있을 겁니다.