개발자에게 Git을 설명해본다면? (Git 1편)

eddy_song·2022년 3월 3일
30

1 Concepts in 3 Levels

목록 보기
3/4
post-thumbnail

1 Concept in 3 Levels
Git의 기초 개념을 각각 초등학생, 대학생, 개발자의 눈높이에 맞춰서 설명해보자.

Git이라는 버전 관리 시스템은 개발자라면 반드시 배워야 합니다.
하지만 한번에 다 소화하기엔 복잡하고 어렵죠.

하지만 초등학생도 이해할 수 있게 단순화시킨 설명을 들어보고,
대학생, 개발자로 차츰 더 수준을 높여간다면 훨씬 더 이해가 쉽지 않을까요?


🧑‍💻 개발자에게 설명하기

버전 관리 시스템이란?

🧑‍🏫 Git은 버전 관리 시스템이야. 버전 관리가 뭔지 알아?

🧑‍💻 알죠. 우리가 코딩을 하면서 생기는 소스 코드 파일들의 변화를 시간에 따라 기록해두는 거잖아요. 나중에 다시 볼 수도 있고, 동시에 여러 버전을 작업할 수도 있고요.

🧑‍🏫 오, 역시 개발자는 이해가 빠르네.
맨 처음 버전 관리 시스템은, 간단하게 로컬에 DB를 설치해서 파일 변화를 기록하는 방식을 썼었대.

🧑‍🏫 프로젝트를 개발하다보면 하지만 여러 명의 협업을 해야하는 경우가 많지.
협업을 하기 위해서 여러 명이 같이 쓰는 버전 관리 시스템이 등장했어.

중앙 버전 관리와 분산 버전 관리

🧑‍🏫 협업을 하기 위해서 처음에는 별도의 중앙 서버를 하나 뒀어.

클라이언트(로컬 컴퓨터)에 있는 코드 파일을 해당 서버에 업데이트했고.
클라이언트가 특정 버전을 받아와서 또 개발을 하는 시스템이었어.

🧑‍💻 웹의 서버-클라이언트 구조와 비슷했네요.

🧑‍🏫 근데 중앙 버전 관리 시스템은 문제가 있었어. 왜나하면 모든 버전 정보가 서버에 기록돼있잖아. 그래서 작업에 네트워크가 항상 필요했고, 그만큼 느린 거야.

거기다 만약 서버가 장애라도 일으키면 모든 작업은 멈춰버리는 거지. 더 나아가서 서버의 저장 공간이 날아가기라도 하면...?

🧑‍💻 오 Shit... 생각하기도 싫은데요?

🧑‍🏫 그런 단점을 해결하려고 나온 게 분산 버전 관리 시스템이야. 분산 버전 관리시스템은 모든 컴퓨터에 똑같이 버전 관리 시스템이 설치돼.

그래서 클라이언트는 다른 저장소의 특정한 버전을 가져오는 게 아니고, 저장소 전체를 통째로 복사(Clone)해오지.

🧑‍💻 그래서 git을 시작할 때 Clone을 하는 거군요.

🧑‍🏫 딩동댕!
파일 변화는 각자 로컬에 있는 DB에 기록해.

그렇기 때문에 git 같은 분산 관리 시스템의 작업은 버전 히스토리를 보거나 새로운 버전을 저장할 때 네트워크를 거칠 필요가 없어. 덕분에 매우 가볍고 빠르지.

🧑‍💻 아하, 서버에 있는 원격 저장소와 '동기화'를 할 때만 네트워크가 필요하겠네요. 나머지는 다 로컬 작업이고요. 확실히 빠를 수밖에 없겠군요.

🧑‍🏫 요즘 개발자들은 중앙 버전 관리 시스템을 써본적이 거의 없잖아? 2000년대 초반 개발자들은 중앙 버전 관리 시스템을 쓰다가 답답한 적이 많았나봐. 2000년대 초에 BitKeeper라는 분산 버전 관리 시스템이 나왔었는데, 무척 인기를 끌었대.

🧑‍💻 git이 분산 버전 관리를 처음 구현한 거 아니었네요?

🧑‍🏫 아니더라고. 가장 큰 오픈소스 프로젝트 중 하나인 리눅스 커뮤니티도 2002년부터 BitKeeper를 쓰기 시작했어.

🧑‍🏫 근데 BitKeeper는 기업이 개발하는 상용 소프트웨어였어.
그러다보니 비영리 커뮤니티인 리눅스가 BitKeeper를 무료로 계속 사용하는 게 불가능해졌고.

🧑‍🏫 근데 리눅스 개발자들이 한번 분산 버전 관리를 경험해보니까 돌아갈 수가 없는거야.

'하.. 이거 너무 좋은데... 무료가 없다고?'

🧑‍🏫 그 상황이 되니까 리눅스 개발의 리더이자 살아있는 레전드 토발즈 형님이 나섰지.

'아... 난 커널 개발하고 싶은데 자꾸 성가시게 하네. 그까이꺼 하나 만들지 뭐'

해서 만든게 바로 Git이야.

🧑‍💻 와... 역시 괜히 레전드가 아니네요. 리눅스만 해도 대단한데 git까지?

🧑‍🏫 Git은 2005년에 처음 나왔는데, Bitkeeper보다도 훨씬 발전한 형태였어. 토발즈 형님이 설계가 얼마나 기깔났던지. 엄청나게 빠른 속도, 수천 갈래의 동시다발/비선형적 개발이 가능했지. 리눅스 같은 대형 프로젝트가 쓰기에도 문제가 없었고.

🧑‍💻 그렇게 해서 Git이 지금은 전세계 표준이 됐군요... 신기하네요.

🧑‍🏫 더 자세한 얘기는 Git 10주년에 한 리누스 토발즈 인터뷰를 한번 읽어봐.

Git에서 버전 기록하기

🧑‍🏫 Git의 특징은 변화가 아니라 전체 상태를 기록한다는 거야.

🧑‍🏫 예전의 버전 컨트롤 시스템은, 초기 파일 A가 있고, A에 대한 변경 사항들을 '버전'으로 저장했어.
이런 걸 델타 기반 버전 관리 시스템이라고 해.

🧑‍🏫 근데 Git은 파일의 전체 상태를 그대로 기록해서 '버전'으로 저장해. Git의 독특한 특징이지.

🧑‍💻 그게 Git만 그런 거였어요? 다른 걸 써본 적이 없어서 몰랐네...

🧑‍🏫 뭐 요즘은 누구나 Git을 쓰니까 차별점이 중요한 건 아니고, 이 개념을 이해하는 게 앞으로 나올 Git의 여러가지 작동을 이해하는데 도움이 돼.

🧑‍🏫 Git의 Staging area에 대해서 알고 있어?

🧑‍💻 네, Add를 하면 Staging area에 추가되고, 거기서 또 commit을 하면 commit에 현재 파일들이 저장되는 거잖아요.

🧑‍🏫 맞아. Working Directory / Staging area / .git repository 이렇게 3개의 단계가 있지.

🧑‍🏫 사실 Staging area는 git 디렉토리 안에 있는 단순한 파일이야. git add를 하면 Working directory의 특정 파일 (저장 대상인 파일) 정보가 저장되지.

🧑‍🏫 그 상태에서 git clone을 하면 Staging area에 있는 파일을 Commit 객체로 묶은 다음, 변하지 않는 스냅샷으로 만들어서 git repository에 저장하게 돼.

git 내부 파일의 4가지 상태

🧑‍💻 근데 git status를 하면 파일이 Untracked, Modified 이런 식으로 나오잖아요? 그건 정확히 어떤 원리죠?

🧑‍🏫 아주 중요한 질문이야. git의 파일에는 총 4가지 상태가 있어.

🧑‍🏫 맨 처음 추가한 파일은 Untracked 상태야.
한번도 Stage나 Commit에 올라가지 않은 파일이지.

🧑‍🏫 그 외에는 모두 Tracked 상태. Stage나 Commit에 한번이라도 올라갔다는 뜻이야. 다른 말로 하면 git이 '알고 있는' 파일이라는 뜻이지.

🧑‍🏫 이 Tracked 상태는 총 3가지가 있어.

🧑‍🏫 Stage나 Commit에 올라간 적이 있으면서, 그 이후로 내용이 1이라도 바뀐 파일은 'Modified'.

🧑‍🏫 Modified 중에서 Staging area에 저장이 된 상태는 'Staged'.

🧑‍🏫 이전 Commit에 포함이 되었고, 그 후로 내용이 변경되지 않은 파일은 'Unmodified' 야. 'Commited' 상태라고 생각해도 좋아.

git add 와 git ignore

🧑‍💻 git add . 를 입력하면 Modified 파일이 Staged가 되는 거네요?

🧑‍🏫 그렇지. 근데 그뿐만 아니라, add 명령은 Untracked 파일을 Tracked/Staged 상태로 바꿔.

🧑‍💻 아, 변경된 파일 + 새로운 파일을 모두 캡처 범위(Stage)에 추가하는 거군요.

🧑‍🏫 응. .gitignore는 이 부분을 막아주는 역할을 해.
git add 를 했을 때 untracked 파일을 모두 stage로 추가하는데, .gitignore에 적혀있는 파일들은 그냥 'Untracked'로 놔두도록 만들지.

🧑‍💻 git이 무시한다는 의미는, 파일 상태가 Staged로 바뀌지 않는다는 뜻이군요. 작업 폴더에는 그대로 있고요.

🧑‍🏫 응. gitignore 파일은 'untracked' 상태의 파일이 추가되는 것만 막는 거야. 이미 'staged'가 한번이라도 된 파일을 git ignore 해본 적이 있어?

🧑‍💻 맞아요. 'staged'를 실수로 했을 때는 gitignore가 먹히지 않더라고요.

🧑‍🏫 이미 그 파일이 'tracked' 상태로 옮겨갔기 때문이지. 그럴 때는 git rm --cached를 사용해서 staged에서 해당 파일을 지우고, untracked 상태로 바꿔줘야 해.

git commit

🧑‍💻 그럼 이제 git commit을 하면 어떻게 되죠?

🧑‍🏫 git은 'Staged' 상태 파일들을 모아서 커밋 객체로 만들어.

이 커밋 객체에는
1) 직전 커밋에 대한 포인터 참조,
2) 현재 파일 상태를 저장한 DB에 대한 포인터,
3) 커밋한 사람 ID와 메시지가 들어가 있어.

🧑‍💻 그런데 파일 상태 전체를 스냅샷을 찍는다고 했잖아요?
그러면 변화하지 않은 'Unmodified' 파일도 커밋에 다시 한번 저장하는 건가요?

🧑‍🏫 git은 커밋을 할 때 폴더 구조를 tree 형식으로 저장하고,
파일 데이터는 blob이라는 포맷으로 별도 저장해.
커밋에는 이 저장한 위치에 대한 참조를 넣게 되있어.

🧑‍🏫 'Unmodified' 파일의 경우는 이미 똑같은 내용이 blob으로 저장되어있겠지?
그러니 이전 커밋에서 저장해둔 blob 위치 참조를 똑같이 살포시~ 넣어주면 되는 거야.

🧑‍💻 아하... 만약 특정 커밋으로 checkout을 했을 때 git은 그 commit에 저장된 '참조'를 모두 사용해서 blob 파일 데이터를 불러오는 거군요. 중복 저장이 필요 없겠네요.

🧑‍🏫 맞아. 그래서 하나의 커밋에는 변경사항만 있는 게 아니라, 해당 시점의 전체 파일 데이터가 모두 들어가있는 거야.

git은 파일이 아주 조금이라도 변경되면 'Modified'로 바꿔버리고, 바로 다시 전체를 캡쳐해서 저장하니까.
아까 델타가 아닌 스냅샷이라고 말했지?

🧑‍💻 호오... commit이 끝나면 어떻게 되죠?

🧑‍🏫 Commit에 포함된 모든 파일은 다시 'Unmodified' 상태가 돼. 이 상태에서는 add 자체가 되지 않아.

🧑‍🏫 하지만 여기서 1이라도 내용이 바뀌면 다시 'Modified' 상태가 되고, 다시 변경사항을 'staged'로 올려서 커밋에 포함시킬 수 있는 거지.

🧑‍🏫 한번 'Tracked'가 되면, 이렇게 'Staged' -> 'Unmodified' -> 'Modified' -> 'Staged'로 계속 돌게 되는 거야.

🧑‍💻 그리고 그 결과로 커밋이라는 스냅샷의 DB에 차곡차곡 쌓이는 거구요.

🧑‍🏫 그렇지. 하나의 스냅샷은 이전 커밋(스냅샷)을 기반으로 하고 있으니까. 순서대로 연결되어서 이력(History)이 되지. git log로 볼 수 있고.

🧑‍🏫 하지만 하나의 커밋(스냅샷)을 기반으로 하는 스냅샷을 2개 이상 만들 수도 있어. 그게 바로 가지치기(branching)이야.

이건 다음 글에서 알아보도록 하자.

관련 링크

🔗 초등학생에게 Git을 설명해본다면? (Git 1편)
🔗 대학생에게 Git을 설명해본다면? (Git 1편)
🔗 개발자에게 Git을 설명해본다면? (Git 1편)
🔗 초등학생에게 Git branch와 merge를 설명해본다면? (Git 2편)

profile
개발 지식을 쉽고 재미있게 설명해보자.

0개의 댓글