버전 관리
Version Control
개념
여러 사람이 같은 코드를 고쳐도 누가 무엇을 왜 바꿨는지 남고 서로의 수정이 안전하게 합쳐지도록 만든 체계입니다.
등장 배경
코드를 지키는 방법이 파일 복사이던 시절이 있었습니다. 큰 수정을 시작하기 전에 폴더를 통째로 복사해 두고 날짜를 붙였습니다. 그렇게 만들어진 이름이 쌓이면 폴더 목록이 최종, 최종2, 진짜최종, 최종_수정본 순으로 늘어났고, 두 달쯤 지나면 그중 어느 것이 실제로 서버에 올라간 물건인지 아무도 확신하지 못했습니다.
사람이 둘 이상이 되면 문제가 달라집니다. 각자 자기 컴퓨터에서 고친 파일을 서버에 올리는데, 올리는 방식이 덮어쓰기입니다. 태오가 오전에 올린 수정 위에 다른 개발자가 오후에 자기 파일을 올리면 오전 작업은 흔적 없이 사라집니다. 지워진 쪽은 당장 알아채지도 못합니다. 사나흘 뒤에 기능이 왜 원래대로 돌아가 있느냐는 질문이 나오고 나서야 무언가 덮였다는 사실을 알게 됩니다.
되돌릴 방법이 없다는 것이 더 큰 문제였습니다. 페이지덱 초기에는 태오가 금요일 저녁에 고친 파일을 서버로 올리고 화면이 뜨는 것만 확인한 뒤 퇴근했습니다. 월요일 아침에 수아가 고객사에서 결제가 안 된다는 연락을 받아 왔습니다. 지난주에 무엇이 바뀌었는지부터 알아야 하는데 남아 있는 기록은 바꾼 사람의 기억뿐이었습니다. 어제 잘 돌던 상태로 돌아가려면 어제 뜬 복사본이 있어야 하는데, 복사본을 남겼는지부터가 그날의 기분에 달려 있었습니다.
버전 관리는 관리 대상을 파일에서 변경으로 옮기면서 이 문제를 풀었습니다. 파일의 어느 순간을 통째로 보관하는 대신, 무엇을 어떻게 바꿨는지를 한 건씩 기록으로 쌓습니다. 기록마다 누가 언제 왜 바꿨는지가 붙으니 이력을 거슬러 읽는 것만으로 이 줄이 왜 이렇게 됐는지 알 수 있고, 원하는 시점의 상태를 언제든 다시 만들어 낼 수 있습니다.
합치는 방식도 함께 바뀌었습니다. 초기 도구는 한 사람이 파일을 잠그고 작업하는 동안 다른 사람이 손대지 못하게 막았습니다. 덮어쓰기는 막았지만 같은 파일을 만져야 하는 사람들이 줄을 서야 했고, 잠근 사람이 퇴근해 버리면 그날 작업이 끝났습니다. 지금 쓰는 도구는 반대로 갑니다. 각자 자유롭게 고치게 두고, 합치는 시점에 기계가 두 수정을 견줘 자동으로 이어 붙입니다. 서로 다른 줄을 고쳤으면 그대로 합쳐지고, 같은 줄을 다르게 고친 경우만 사람에게 묻습니다.
분야 구성
버전 관리 아래 묶인 말은 일곱 갈래로 나뉩니다. 무엇으로 하느냐가 도구, 무엇을 다루느냐가 단위, 무엇을 하느냐가 작업, 그러다 무엇에 걸리느냐가 문제, 어떻게 같이 하느냐가 협업, 팀 전체가 어떤 규칙으로 굴리느냐가 전략, 바깥에 변화를 어떻게 알리느냐가 규칙입니다.
도구는 이 갈래의 출발점입니다. Git은 변경 이력을 관리하는 도구 가운데 사실상 표준이 된 것으로, 아래 나오는 말 대부분이 이 도구가 쓰는 용어입니다. 분산형이라는 점이 특징입니다. 중앙 서버에만 이력이 있는 것이 아니라 각자의 컴퓨터에 전체 이력이 통째로 복사돼 있어서, 네트워크가 끊긴 비행기 안에서도 이력을 뒤지고 기록을 남길 수 있습니다.
단위는 무엇을 대상으로 삼는지를 가르는 갈래입니다. 셋이 크기 순으로 놓입니다.
- 저장소 — 코드와 그 코드의 모든 이력이 함께 담긴 공간입니다. 보통 한 제품이나 한 서비스가 저장소 하나를 씁니다.
- 커밋 — 변경을 한 덩어리로 묶어 기록한 지점입니다. 기록마다 무엇을 왜 바꿨는지 적는 메시지가 붙습니다. 되돌리거나 원인을 찾을 때 최소 단위가 이것이라, 관련 없는 수정 열 가지를 한 커밋에 몰아넣으면 나중에 그중 하나만 빼낼 수가 없습니다.
- 브랜치 — 본류에서 갈라져 나온 작업 줄기입니다. 본류를 건드리지 않고 따로 실험할 수 있어서, 결제 기능을 반쯤 만들어 둔 상태여도 다른 사람의 배포를 막지 않습니다.
작업은 갈라진 줄기를 다시 하나로 만드는 갈래입니다. 두 가지가 있습니다. 머지는 두 줄기의 변경을 합치면서 합쳤다는 사실 자체를 기록으로 남깁니다. 리베이스는 내 기록을 최신 본류 위로 옮겨 붙여 처음부터 거기서 작업한 것처럼 만듭니다. 결과물은 같아도 남는 이력의 모양이 다릅니다. 앞엣것은 갈라졌다 합쳐진 자취가 그대로 보이고, 뒤엣것은 한 줄로 곧게 펴집니다. 팀마다 어느 쪽을 쓸지 미리 정해 두는데, 섞어 쓰면 이력을 읽는 규칙이 사람마다 달라집니다.
문제는 자동으로 합칠 수 없는 상황을 가리키는 갈래입니다. 충돌은 두 사람이 같은 파일의 같은 줄을 다르게 고쳤을 때 일어납니다. 기계는 어느 쪽이 옳은지 판단하지 못하니 두 가지를 나란히 보여 주고 사람에게 고르게 합니다. 충돌이 난 파일을 열면 두 수정이 표시와 함께 나란히 들어가 있습니다. 위쪽에 내가 고친 줄, 아래쪽에 상대가 고친 줄이 놓이고 그 사이에 구분선이 그어져 있어서, 둘 중 하나를 남기거나 둘을 합쳐 한 줄로 고친 다음 표시를 지우면 끝납니다. 무서운 일처럼 들리지만 대개는 몇 분이면 끝나는 작업입니다. 다만 브랜치를 오래 묵힐수록 커집니다. 하루 만에 합치면 겹치는 줄이 두세 개지만 3주를 묵히면 파일 열 개가 통째로 어긋나 있습니다.
협업은 합치기 전에 사람을 한 번 거치게 만드는 갈래입니다. 둘이 짝을 이룹니다.
- 풀 리퀘스트 — 내 브랜치를 본류에 합쳐 달라고 올리는 요청입니다. 무엇을 왜 바꿨는지 설명이 붙고, 바뀐 줄이 한눈에 보이는 화면이 함께 열립니다.
- 코드 리뷰 — 그 요청을 동료가 읽고 의견을 남기는 절차입니다. 버그를 잡는 것이 첫 번째 목적이지만, 실제로 더 크게 작동하는 효과는 그 코드를 아는 사람이 둘이 된다는 점입니다.
이 둘이 자리를 잡으면 합치는 일이 개인의 결정에서 팀의 결정으로 바뀝니다. 승인 없이는 본류에 들어가지 못하게 막아 두는 것이 기본 설정이고, 그 덕에 아무도 모르는 사이에 기능이 덮이는 일이 사라집니다.
전략은 브랜치를 팀 전체가 어떻게 굴릴지 정한 약속입니다. 셋이 있습니다. 트렁크 기반 개발은 본류에 하루에도 여러 번 합치고 브랜치를 이틀 이상 두지 않습니다. 충돌이 커지기 전에 부딪히게 만드는 방식이라 자동 검사가 촘촘히 깔려 있어야 버팁니다. 깃 플로우는 반대로 개발용·출시용·긴급수정용 브랜치를 나눠 두고 정해진 경로로만 옮깁니다. 정해진 날짜에 버전을 묶어 내보내는 제품에 맞고, 대신 브랜치가 오래 살아 합칠 때 드는 값이 큽니다. 모노레포는 브랜치가 아니라 저장소 쪽 선택으로, 여러 프로젝트를 저장소 하나에 모아 둡니다. 공통 코드를 고칠 때 관련 프로젝트를 한 번에 바꿀 수 있는 대신 저장소가 무거워집니다.
규칙은 만들어진 결과물을 바깥에 어떻게 알릴지 정한 갈래입니다. 시맨틱 버저닝은 버전을 2.4.1 처럼 세 자리로 적고 자리마다 뜻을 정해 둔 약속입니다. 첫 자리가 오르면 기존 방식이 더는 통하지 않는다는 뜻이고, 둘째 자리는 기능이 늘었으나 쓰던 것은 그대로라는 뜻이며, 셋째 자리는 버그 수정만 들어갔다는 뜻입니다. 남이 쓰는 라이브러리나 API 를 내보낼 때 이 규칙이 있으면 상대가 숫자만 보고 올려도 되는지 판단할 수 있습니다. 받는 쪽에서는 2.4.1 을 2.4.2 로 올리는 일은 바뀐 내용을 읽지 않고 해도 되지만, 3.0.0 으로 올릴 때는 무엇이 끊기는지 먼저 확인하고 자기 코드를 함께 고쳐야 합니다.
일곱 갈래는 하나의 흐름으로 이어집니다. 도구가 저장소를 만들고, 저장소 안에서 커밋이 쌓이며 브랜치가 갈라지고, 갈라진 것을 합치다 충돌이 나고, 합치기 전에 리뷰가 붙고, 그 리뷰와 합치기를 언제 어떻게 할지가 전략이며, 그렇게 쌓인 결과에 번호를 붙여 내보내는 것이 규칙입니다.
실무 용례
"이 커밋 하나만 되돌릴게요. 어제 오후 배포 직전 상태면 됩니다."
문제가 생긴 변경을 특정했으니 나머지는 그대로 두고 그 한 건만 빼겠다는 뜻입니다.
"브랜치 딴 지 3주 됐죠? 먼저 본류를 당겨 오세요. 안 그러면 충돌 범위가 너무 커져요."
합치기를 미룬 만큼 겹친 수정이 쌓였으니 지금 한 번 맞춰 두라는 요청입니다.
"리뷰 코멘트 두 개만 반영해 주시면 승인하겠습니다."
기능 자체에는 이견이 없고 지적한 부분만 고치면 합쳐도 된다는 뜻입니다.
페이지덱에서 태오가 문서 목록의 정렬 방식을 고치는 작업이 하나 들어옵니다. 태오는 본류에서 브랜치를 하나 갈라 이틀에 걸쳐 커밋 다섯 개를 쌓습니다. 기준을 바꾸는 수정, 빈 목록 처리, 테스트 추가가 각각 다른 커밋입니다. 작업이 끝나면 풀 리퀘스트를 올리고, 바뀐 줄을 본 동료 개발자가 정렬 기준이 비어 있을 때의 처리를 묻습니다. 태오가 커밋 하나를 더 올려 답하면 승인이 붙고 본류에 합쳐집니다.
일주일 뒤 목록이 이상한 순서로 나온다는 제보가 들어옵니다. 지현이 언제부터였는지를 묻자 태오가 이력을 열어 그 파일을 건드린 커밋 넷을 추려 냅니다. 메시지와 바뀐 줄을 훑어 원인이 된 한 건을 찾는 데 20분이 걸렸고, 그 커밋만 되돌려 급한 불을 끕니다. 이번 주에 들어간 다른 작업은 하나도 영향을 받지 않습니다. 금요일 저녁에 기억을 더듬어 한 줄씩 고치던 시절과 갈리는 지점이 여기입니다.
흔한 오해
버전 관리를 백업으로 여기는 경우가 가장 많습니다. 예전 상태로 돌아갈 수 있다는 점이 같으니 자연스러운 오해입니다. 그러나 백업은 어느 시점의 파일을 통째로 보관하는 일이고 버전 관리는 변경 하나하나에 이유를 붙여 순서대로 쌓는 일입니다. 백업에서 꺼낼 수 있는 답은 "3일 전에는 이랬다" 하나지만, 이력에서는 "이 줄은 결제 오류를 고치려고 지난달에 이렇게 바뀌었다" 까지 나옵니다. 이 차이를 모르고 백업처럼만 쓰면 커밋을 하루 끝에 한 번씩 몰아서 하게 되고 메시지가 전부 "수정" 이 됩니다. 그러면 되돌릴 수 있는 최소 단위가 하루치 덩어리라, 그 안에서 문제가 된 한 가지만 빼내는 일이 다시 불가능해집니다. 도구는 갖췄는데 얻으려던 것은 그대로 못 얻는 상태입니다.
혼자 하는 작업에는 필요 없다고 생각하는 오해도 흔합니다. 합칠 사람이 없으면 충돌도 없으니 그렇게 보이기 쉽습니다. 합치기는 이 체계가 하는 일 가운데 하나일 뿐입니다. 혼자 쓰는 사람이 실제로 더 자주 쓰는 것은 되돌릴 지점과 변경 이유 쪽입니다. 반년 전의 자신은 사실상 남이라, 왜 이렇게 짰는지 기억나지 않는 코드 앞에서 이력이 유일한 설명이 됩니다. 이력이 없으면 실험 자체를 꺼리게 되는 것이 더 큰 손해입니다. 되돌릴 자리가 있어야 구조를 크게 바꿔 보는 시도를 하는데, 망가뜨리면 끝이라는 상태에서는 고칠 만한 것도 손대지 않고 그대로 둡니다.
같이 보면 좋은 페이지
하위 용어
마지막 업데이트 2026-09-16