Git이라는 버전 관리 시스템은 개발자라면 반드시 배워야 합니다.
하지만 한번에 다 소화하기엔 복잡하고 어렵죠.
하지만 초등학생도 이해할 수 있게 단순화시킨 설명을 들어보고,
대학생, 개발자로 차츰 더 수준을 높여간다면 훨씬 더 이해가 쉽지 않을까요?
🧑🏫 저번에 git이 뭔지 얘기했었지? 기억나?
👩🦱 네. 개발자들이 쓰는 분산 버전 관리 시스템이요. 각자 자기 컴퓨터에 버전 기록이 있고, 중간중간 동기화하는 거.
🧑🏫 오, 잘 기억하네. 그때 마지막에 너가 좋은 질문을 했거든.
👩🦱 제가요? 뭐라고 했죠?
🧑🏫 "각자 자기 컴퓨터에서 버전을 저장하다보면, 서로 저장한 버전 이력이 달라질 텐데 뭘 기준으로 하는 거냐"고 물어봤잖아.
👩🦱 아 맞다. 그건 어떻게 해요?
🧑🏫 그걸 이해하려면 먼저 '가지치기(Branch)'라는 개념을 알아야 해.
🧑🏫 팀플 상황을 다시 생각해보자. 너네 조 4명이서 PPT를 만들고 있어.
근데 너가 보기에 지금 PPT 디자인이 좀 별로야. 전체적으로 색감을 싹 바꿔보고 싶은 거지.
👩🦱 맞아요 그런 거 많죠. 근데 함부로 바꿨다가 다른 조원들이 "뭐야 이게" 하면 곤란하잖아요.
🧑🏫 그렇지. 그렇다고 아예 안 해볼 수도 없고.
이럴 때 어떻게 해?
👩🦱 음... 사본을 하나 만들어서 거기서 먼저 해보겠죠. "디자인 시안 v2" 이런 식으로.
🧑🏫 바로 그거야. Git에서 Branch가 딱 그 역할이야.
지금 다같이 작업하고 있는 버전이 있잖아? 이걸 '메인(main)' 이라고 불러. 팀플로 치면 '발표용 최종본'이지.
여기에 바로 이것저것 시도하면 조원들이 멘붕하잖아.
그래서 메인을 건드리지 않고, 메인에서 갈라져 나온 별도의 작업 공간을 만드는 거야. 이게 Branch, 즉 '가지'야.
👩🦱 구글 슬라이드에서 '사본 만들기' 같은 건가요?
🧑🏫 비슷하지만 중요한 차이가 있어. 구글 슬라이드에서 사본을 만들면 파일 전체가 통째로 복사되잖아.
근데 Git에서 Branch를 만들면 파일을 복사하는 게 아니야. 커밋 위에 '이름표'를 하나 더 붙이는 거야.
👩🦱 이름표요?
🧑🏫 저번에 커밋이 기차처럼 줄줄이 연결된다고 했잖아?
![커밋이 연결된 모양 이미지]
지금 커밋이 A → B → C 순서로 쌓여있다고 해보자. 여기서 main이라는 건 사실 "C 커밋을 가리키는 이름표"야.
Branch를 새로 만든다는 건, 같은 C 커밋에 이름표를 하나 더 붙이는 거야. 예를 들면 design-v2라는 이름표를.
![같은 커밋에 이름표가 2개 붙은 이미지]
👩🦱 어, 그러면 파일이 2배가 되는 건 아니네요?
🧑🏫 맞아. 이름표 하나 추가하는 거니까 거의 비용이 없어. 가지를 10개 만들어도 저장 공간이 10배가 되지 않아.
👩🦱 오, 진짜 가볍네요.
🧑🏫 그래서 Git에서는 가지를 부담 없이 만들 수 있어. 뭔가 시도하고 싶으면 가지를 만들고, 필요 없으면 지우면 되니까.
🧑🏫 자, 이제 design-v2라는 가지를 만들었어. 이 가지 위에서 작업을 시작하는 거야.
근데 여기서 하나 더 알아야 할 게 있어. HEAD라는 개념이야.
👩🦱 HEAD요? 머리?
🧑🏫 ㅋㅋ HEAD는 "지금 내가 서 있는 위치"를 가리키는 특별한 이름표야.
너가 main에서 작업하고 있으면 HEAD는 main을 가리키고 있어.
design-v2로 전환하면 HEAD가 design-v2를 가리키게 되지.
👩🦱 아 '지금 내가 보고 있는 가지'를 표시해주는 거네요.
🧑🏫 정확해. 가지를 전환하면 작업 폴더에 있는 파일들이 그 가지의 마지막 커밋 상태로 바뀌어.
👩🦱 파일이 바뀐다고요? 진짜요?
🧑🏫 응. 예를 들어서 design-v2에서 발표 자료 색상을 파란색으로 바꿔서 커밋을 했다고 치자. 그 상태에서 main으로 전환하면? 파일이 원래 색상으로 돌아가.
👩🦱 와... 그러면 가지별로 완전히 다른 상태를 왔다갔다 할 수 있는 거네요.
🧑🏫 그렇지. 그래서 팀플에서도 이렇게 쓸 수 있어.
조원 4명이 각자 가지를 만들어서 작업하는 거지. 한 명은 intro-part, 한 명은 data-analysis, 한 명은 conclusion. 메인에 직접 작업하는 사람은 없어.
![조원들이 각자 가지에서 작업하는 이미지]
👩🦱 각자 자기 가지에서만 작업하면 다른 사람 작업을 망칠 일이 없겠네요.
🧑🏫 바로 그거야. Branch의 핵심은 격리된 작업 공간이야.
👩🦱 근데 결국에는 발표를 해야 되잖아요. 각자 가지에서 작업한 걸 하나로 합쳐야 하는 거 아니에요?
🧑🏫 맞아. 가지를 메인에 합치는 걸 머지(Merge)라고 해.
근데 합칠 때 상황에 따라 방식이 좀 달라져. 총 3가지가 있어.
🧑🏫 제일 쉬운 경우부터 보자.
너가 design-v2 가지에서 커밋을 3개 했어. D, E, F.
근데 그 사이에 main에는 아무런 변화가 없었어. 여전히 C에 머물러 있는 거지.
![fast-forward 이전 이미지: main은 C, design-v2는 F를 가리킴]
👩🦱 그러면 main이 뒤쳐져있는 거네요.
🧑🏫 맞아. 이 상황에서 머지를 하면 어떻게 될까?
main 이름표를 그냥 F로 옮기면 끝이야.
![fast-forward 이후 이미지: main과 design-v2 둘 다 F를 가리킴]
👩🦱 어? 그냥 이름표만 옮기는 거예요? 뭔가 합치는 과정이 없네요.
🧑🏫 없어. 왜냐하면 main에서 갈라진 이후로 main 쪽에 새로운 게 없거든. 그냥 '앞으로 빨리감기'를 하면 되는 거야.
그래서 이걸 빨리감기 머지(Fast-forward merge)라고 불러.
👩🦱 진짜 빨리감기네요. 영상 빨리감기처럼 그냥 쭉 앞으로 가는 거잖아요.
🧑🏫 딩동댕.
🧑🏫 이번에는 좀 다른 상황이야.
너가 design-v2에서 색상을 바꾸는 동안, 다른 조원이 main에 서론을 수정해서 이미 머지를 해놓은 거야.
![양쪽에 커밋이 있는 이미지: main에도 새 커밋, design-v2에도 새 커밋]
👩🦱 아, 양쪽 다 변화가 있는 거네요. 이번에는 이름표만 옮기면 안 되겠다.
🧑🏫 맞아. 이름표를 옮기면 한쪽 변화가 사라지니까.
이럴 때 Git은 양쪽의 변화를 비교해. "main에서 뭐가 바뀌었지?" "design-v2에서 뭐가 바뀌었지?"
너는 색상을 바꿨고, 다른 조원은 서론을 고쳤어.
서로 건드린 부분이 다르니까, Git이 알아서 둘을 합쳐서 새로운 커밋을 하나 만들어줘.
![머지 커밋이 생긴 이미지: 양쪽에서 합쳐지는 모양]
👩🦱 오, Git이 자동으로 합쳐주는 거예요?
🧑🏫 응. 이렇게 합쳐서 새로 생긴 커밋을 머지 커밋(Merge Commit)이라고 해.
이 머지 커밋은 좀 특별해. 다른 커밋은 직전 커밋이 1개인데, 머지 커밋은 직전 커밋이 2개야. 양쪽 가지에서 온 거니까.
👩🦱 아하, 벌어졌던 길이 다시 하나로 만나는 지점이네요.
🧑🏫 아주 좋은 비유야. 그리고 머지가 끝나면 design-v2 가지는 역할을 다한 거니까 삭제해도 돼. 가지에서 작업했던 커밋 기록은 머지 커밋을 통해 남아있으니까.
👩🦱 근데 궁금한 게 있어요. 구글 독스에서는 여러 명이 동시에 편집해도 실시간으로 반영되잖아요. Git은 왜 이렇게 나눠서 합치는 방식을 쓰는 거예요?
🧑🏫 좋은 질문이야. 문서는 글자를 바꿔도 다른 글자에 영향이 없잖아.
근데 코드는 다르거든. 내가 함수 이름을 바꾸면, 그 함수를 호출하는 코드 수십 줄이 한꺼번에 영향을 받아. 이런 걸 '의존성'이라고 하는데, 코드는 이 의존성이 아주 복잡해.
그래서 실시간으로 동시에 편집하면 서로의 코드가 충돌하면서 프로그램이 터질 수가 있어. 각자 격리된 환경에서 작업하고, 의도적으로 합치는 게 훨씬 안전한 거지.
👩🦱 아... 그래서 일부러 가지를 나누는 거군요. 코드가 PPT랑은 차원이 다르긴 하네요.
🧑🏫 마지막 상황. 이게 처음 겪으면 좀 당황스러운데, 원리를 알면 별 거 아니야.
너가 design-v2에서 결론 슬라이드의 핵심 문장을 이렇게 고쳤어.
"따라서 A 전략이 가장 효과적이다."
근데 같은 시간에 다른 조원이 main에서 똑같은 결론 문장을 이렇게 고친 거야.
"따라서 B 전략을 우선 검토해야 한다."
👩🦱 아... 같은 줄을 서로 다르게 고친 거네요. 😱
🧑🏫 이 상황에서 Git은 합칠 수가 없어. "A 전략"으로 해야 할지 "B 전략"으로 해야 할지 Git은 판단을 못 하거든.
이런 상황을 충돌(Conflict)이라고 해.
👩🦱 충돌이요? 그러면 에러가 나고 끝인 건가요?
🧑🏫 아니야. Git은 "나 이거 못 합치겠으니까 네가 직접 골라줘"라고 알려줘.
충돌이 난 파일을 열어보면 이런 식으로 표시가 돼있어.
<<<<<<< HEAD (main)
따라서 B 전략을 우선 검토해야 한다.
=======
따라서 A 전략이 가장 효과적이다.
>>>>>>> design-v2
👩🦱 오, 양쪽 버전을 둘 다 보여주는 거네요.
🧑🏫 그렇지. =======를 기준으로 위가 main 쪽, 아래가 네 가지 쪽이야.
너는 이 두 버전을 보고 판단을 내리면 돼.
"A가 맞다" 싶으면 B를 지우고, "B가 맞다" 싶으면 A를 지우고, 아니면 둘을 적절히 섞어서 새로운 문장을 만들어도 되지.
👩🦱 아, 그러면 그 표시들(<<<<<<<, =======, >>>>>>>)도 지우고 최종 버전만 남기면 되는 거예요?
🧑🏫 정확해. 그렇게 정리한 다음에 다시 커밋을 하면 머지가 완료되는 거야.
👩🦱 생각보다 무섭진 않네요. 그냥 "둘 중에 뭘로 할래?" 하고 물어보는 거잖아요.
🧑🏫 맞아. 처음에 충돌이 뜨면 좀 당황스러운데, 알고 보면 그냥 선택의 문제야.
실제로 개발자들은 매일같이 충돌을 해결해. 팀으로 일하면 피할 수 없거든. 그래서 "충돌이 나는 건 실수가 아니다. 합치기 전에 리뷰하는 과정일 뿐이다." 이렇게 생각하는 게 좋아.
👩🦱 오... 충돌이 일어나는 게 자연스러운 거군요.
🧑🏫 응. 오히려 "충돌이 한번도 안 나는 팀"은 진짜 소통이 잘 되는 팀이거나, 혼자 다 하고 있는 거야. ㅋㅋ
🧑🏫 오늘 내용을 정리해볼까?
👩🦱 네!
🧑🏫 Branch는 메인을 건드리지 않고 따로 작업할 수 있는 공간이야. 파일을 복사하는 게 아니라 커밋 위에 이름표를 붙이는 거라서 매우 가벼워.
Merge는 가지를 메인에 합치는 거고, 상황에 따라 3가지 방식이 있어.
👩🦱 정리하니까 깔끔하네요.
근데 한 가지 더 궁금한 거. 이 가지 만들고 합치는 걸 지금은 제 컴퓨터에서 하는 건데, 다른 조원들한테는 어떻게 공유하는 거예요? push, pull 이런 거 들어본 적 있거든요.
🧑🏫 오, 역시 좋은 질문이야. 그게 바로 원격 저장소(Remote)와 동기화하는 방법인데.
다음에 알아보도록 하자!
🔗 초등학생에게 Git을 설명해본다면? (Git 1편)
🔗 대학생에게 Git을 설명해본다면? (Git 1편)
🔗 개발자에게 Git을 설명해본다면? (Git 1편)
🔗 초등학생에게 Git branch와 merge를 설명해본다면? (Git 2편)