FEDev Story

[스크랩] 개발 노트 Git, 가장 쉽게 사용하기 - (1) 심플함이 답이다 본문

Web.Dev

[스크랩] 개발 노트 Git, 가장 쉽게 사용하기 - (1) 심플함이 답이다

지구별72 2018. 9. 7. 11:34

세상이 복잡해짐에 따라 필요로 하는 시스템도 점점 복잡해졌다. 시스템이 복잡해질수록, 여러 사람이 어떻게 잘 나누어 개발할 것인지 고민하게 되었고, 효율적인 협업과 버전관리에 대한 수많은 solution이 제시되었다. 그 중 세계에서 가장 많이 사용되는 solution이 단연 git이라는 사실에 의심을 갖는 사람은 없을 것이다. git의 단점에 대해 이야기하는 사람도 있겠지만, 세계에서 가장 많은 사람들이 사용한다는 것만으로도 공신력 있는 유용한 도구라 생각해도 되지 않을까?

하지만 아직도 git의 진입장벽을 쉽게 넘지 못하여 git을 유용한 도구가 아닌 장애물로 여기는 사람들이 적지 않다. 이 글에서는 git을 사용해보려 했지만 그 장벽에 부딪혀 포기해버린 사람들을 위해, git이 어렵게 느껴지도록 하는 문제들을 하나하나 해결해 보고, git을 가장 쉽게 사용할 수 있는 방법이 무엇인지 살펴보도록 하겠다.

git basic

 자세한 이야기를 하기 전에, 일단 git에 대한 기본 개념은 알아야 하지 않겠는가?

Remote repository & Local repository

많은 사람들이 꼽는 git의 특징은 remote repository와 local repository가 각각 존재한다는 것이다. 
대부분 서버에 저장소(remote repository)가 있고 팀 구성원들이 그 서버에 각각 작업한 내용들을 반영했다면, git은 서버 저장소를 그대로 자신의 PC(local repository)에 복제해와서 작업한 내용들을 자신의 PC에 반영한 후 다시 그것을 서버에 반영하는 방식이다. 얼핏보면 단순히 중간에 local repository에 반영하는 절차가 추가되어 불편해 보이지만, local repository는 remote repository와 완전히 독립적으로 운영이 가능하여 모든 팀원이 각자 자신의 방식으로 형상관리를 해가며 개발할 수 있고, 개발이 완료된 후 서버에 반영해야 할 내용들만 선택적으로 반영하는 것이 가능하다. 결국 개개인의 개발을 좀 더 편리하게 할 수 있도록 지원해 주는 것이다. 그리고, 설사 remote repository에 문제가 생기더라도 팀원들이 가지고 있는 local repository 중 하나를 골라 복제하여 remote repository를 복원할 수도 있다. 즉, local repository를 통해 완벽한 분산시스템을 구현한 것이다.

세가지 상태 - modified/staged/commited

두 번째로 알아야 할 개념은, modified, staged, committed 이다. 이 세가지 개념은 변경점들이 어떠한 상태에 있는가에 대한 개념이라고 생각하면 된다. 

· committed
git에서는 변경점의 집합(실제로는 snapshot형태)을 database에 저장하는 행위를 commit이라 한다. committed는, 변경점들이 commit되어 database에 저장된 상태이다.

· modified
말 그대로 변경된 상태이다. git이 기억하고 있던 상태에서 무언가 변경이 일어난 파일은 modified 상태가 된다. 파일의 추가나 삭제도 기존 상태에서 변경이 일어난 것이므로 modified 상태에 포함된다.

· staged
modified 상태의 내용들 중에 실제 commit의 대상이 될 수 있도록 선택된 상태이다. Tool에 따라서는 파일단위가 아닌 line단위로도 staged상태로 보낼 수 있다. 아무리 사용자가 소스를 수정했어도 staged 상태로 변경되지 않은 내용들은 git에 commit되지 않는다. 대부분의 경우에 번거로운 중간과정이 될 수 있어, staged를 생략하고 modified에서 바로 commit 할 수 있는 옵션도 제공된다.

Branch, '가지'가 아닌 '냇물'이라 생각하자

Branch라는 단어의 뜻은 '나뭇가지' 혹은 '지점/분점'이다. 무언가 원래의 줄기에서 분기되어 나온 것을 뜻하는 말이다. 우리가 개발을 하다보면 근간이 되는 소스가 있지만 간혹 그것과는 별개로 독립적으로 개발되어야 할 필요가 생긴다. 그 때 우리는 소스 전체를 복사하여 독립된 개발을 시작하게 되는데, 이것이 바로 브랜치다. 모든 버전관리 tool에서 브랜치를 지원하지만, git은 특히 브랜치 사용을 권장하고, 브랜치를 매우 가볍게 사용할 수 있도록 만들어져있다. 수시로 브랜치를 만들어서 개발하고, 개발이 완료되면 본래의 소스와 합치는 형태를 권장한다. 나는 그래서 git의 브랜치를 '나뭇가지' 보다는 '냇물(stream)'에 비유하기를 좋아한다. 냇물을 보면 기본적으로 본류(mainstream)가 흐르고, 간혹 흐름이 갈라지기도 하고(sub-stream) 다시 본류와 합쳐지기도 한다.

출처: Git Beginner's Guide for Dummies

이러한 흐름을 잘 control하면 개발이 정말 편해질 수 있다. 그것이 브랜치 전략이고, 일반적인 개발에 적합하게 일반화 해놓은 브랜치 전략을 git-flow라고 한다. git을 쉽게 사용하려면 브랜치 전략을 명료하게 가져가는 것도 중요하다. git-flow에 대해서는 다음 연재시에 자세히 알아보겠다.

여섯가지 명령어 (Process)

최소한의 개념들을 알아보았으니, 최소한의 사용법을 알아볼 차례다. git은 clone, checkout, pull, add, commit, push, 이렇게 여섯가지 명령어만 알면 사용이 가능하다. 

· git clone
remote repository를 복제하여 local repository를 생성한다. 서버에 저장되어 있는 git으로 관리되는 소스와 개발이력을 전부 가져와 local에서 작업을 시작할 준비를 하는 단계이다.

· git checkout
브랜치를 전환한다. 브랜치를 전환하면 소스도 해당 브랜치의 것으로 전환된다. 실제적으로 작업을 시작할 수 있는 상태를 만들어 준다.

· git pull
현재 브랜치의 최신 상태를 remote repository로부터 가져온다. 즉, local repository내의 현재 checkout된 브랜치 소스를 최신상태로 만들어 준다.

· git add
modified 상태에 있는 변경 내용들을 staged 상태로 만들어 준다.

· git commit
staged 상태에 있는 변경 내용들을 repository에 저장한다. 이 때 commit message를 통해 이 변경내용에 대한 description을 기입할 수 있다.

· git push
local repository에 commit된 내용들을 remote repository에 보낸다. 즉, 나의 작업내용을 팀원들 전체가 바라보는 서버에 반영(공개)한다.

git 사용을 어렵게 하는 진입장벽

이제 git을 사용할 수 있을 정도의 최소한의 설명은 다 했다. 하지만 이 것만 가지고 git을 사용할 수 있는 사람은 아마 없을 것이다. 나의 경험과 주변의 git을 어려워하는 사람들의 이야기 속에서 git의 진입장벽을 몇 가지 발견할 수 있었다.

비가시성

git을 처음 사용할 때 ‘멘붕’에 빠지는 이유는 수많은 command들이 무엇을 하는지, 소스들이 지금 어떻게 관리가 되고 있는 건지 감이 오지 않기 때문이다. 인터넷 검색을 통해 git을 시작해보지만, 어디서는 git init을 해야 된다고 하고, 어디서는 git clone을 해야 된다고 한다. 공식 매뉴얼(https://git-scm.com/docs/user-manual.html )을 살펴봐도 쉽게 감이 잡히지 않는다.

너무나 직관적이어서 딱! 보기만 해도 바로 시스템을 이용할 수 있으면 정말 좋겠지만, git은 그렇게 호락호락 하지 않은 것이 사실이다. 공부가 필요한 tool이다. 하지만, 회사는 많은 시간 공부를 하고 있도록 놔두지 않는다. 그래서 빨리빨리 인터넷을 보고 영문도 모른 채 따라하다가 난관에 부딪히면 공부하는 것 보다 더 많은 시간을 허비하게 되기도 한다.

형상 관리를 위한 도구라는 측면에서 보면 git은 복잡하고 내용이 방대할지도 모른다. 하지만 사실 몇 가지 개념만 이해하면 의외로 simple한 것이 git이다. 그 개념을 이해하는데 GUI tool들이 많은 도움을 준다. 특히 모든 commit을 graph로 표시해 주기 때문에 branch 개념을 이해하는데 유용하다. 그리고 모든 GUI tool이 그러하듯 각 command의 자세한 사용법을 몰라도 직관적으로 command의 사용법을 이해할 수 있게 해준다. 

git을 지원하는 GUI tool이 많이 있지만, 무료로 제공되는 것들 중에서는 Atlassian에서 만든 SourceTree 와 tortoiseSVN으로 익숙한 tortoiseGit을 추천한다.

tortoiseGit

tortoiseGit은 윈도우 탐색기에서 마우스 우클릭을 하면 나타나는 context menu 기반으로 되어있다. 각 메뉴를 클릭하면 해당 기능을 제공하는데 필요한 창이 뜨기 때문에 불필요한 graphic이 없어 가볍고 빠르다. 특히 강력한 점은, 특정 폴더/파일에 대한 변경이력만 볼 수 있다는 것이다. 특정 모듈에서 이슈가 발생했을 때, 해당 모듈의 변경이력만 추려서 볼 수 있다라는 것은 이슈 해결에 매우 큰 도움을 준다. 하지만, tortoiseGit은 윈도우 탐색기를 기반으로 하기 때문에 윈도우 환경에서만 사용 가능하다는 점과, git에 대한 기본적인 지식이 있어야 사용할 수 있는 다소 직관적이지 않은 명령어 기반의 UI라는 점이 조금 아쉽다.

SourceTree

SourceTree는 git을 이해하는데 필요한 기본적인 개념들을 한 화면에 잘 표현해 놓았다. 좌측에 branch 구조가 표현되어 있고 main frame에 git graph와 commit 내용들이 표시되어 있으며, 그 아래에 변경사항들을 바로 볼 수 있도록 해놓았다. 기본적으로 화면에 표시된 내용이 알차기 때문에 상단 메뉴바도 비교적 simple하다. 그리고 무엇보다, 최근 개발자들이 즐겨쓰는 Mac OS를 지원한다! (필자는 윈도우에서는 tortoiseGit을, Mac OS에서는 SourceTree를 사용한다) 그 외에, Atlassian 제품이기 때문에 Bitbucket이나 GitHub에서 약간의 연동메뉴를 제공한다는 이점도 있다.

이 글의 목적에 맞게, 일단은 다소 무겁더라도 조금 더 직관적인 SourceTree를 추천한다. git을 처음 시작하거나, 아직도 많이 어렵게 느껴진다면 우선 https://www.sourcetreeapp.com/ 에서 SourceTree를 설치해보자. 한결 git이 쉬워질 것이다.

관성

사람들에게 가장 편리한 도구는 무엇일까? 필자는 자신에게 익숙해서 가장 잘 다룰 수 있는 도구가 가장 편리한 도구라고 생각한다. 아무리 좋은 도구가 있다고 한들 손에 익지 않아 잘 사용할 수 없다면 그  도구는 결코 편리하지 않은 것이다. 많은 사람들이 바로 이점 때문에 git을 쉽게 시작하지 못한다. git이 손에 익지 않아 불편하기 때문이다.

내가 이전 회사에 있을 때의 일이다. 전사적으로 git이 도입되기 이전, 우리 팀에서는 정말 원시적으로 윈도우의 파일시스템을 이용해서 형상관리를 했다. 공용 서버에 ‘프로젝트명_버전명_날짜’ 형태로 폴더를 나누고, excel 파일로 변경 이력을 관리하고, merge tool(Kdiff3, WinMerge, beyond compare, araxis merge 등의 파일간의 차이점을 보여주고 상호간 merge를 할 수 있게 도와주는 tool로 diff tool 이라고도 함)을 사용하여 변경점을 수작업으로 merge했다. 

파일 시스템을 이용한 형상관리
beyond compare 를 이용한 merge 작업

지금 생각하면 정말 어처구니 없는 일이지만, 그 당시 우리 팀원들은 이미 작업 중이던 여러 소스코드로부터 몇 몇 기능들을 뽑아와 새로운 버전을 만들어 내는 일을 merge tool을 이용하여 해낼 정도로 그 작업에 능숙했다. 그리고 그러한 원시적인 형상관리 방법에 아무도 불편을 느끼지 못하고 있는 듯이 보였다. 누군가 git을 써보자고 들고 왔을 때, git 사용의 미숙함이 불러오는 잦은 오류상황과 그것을 해결하기위한 노력이 오히려 작업을 더디게 했고, 결국 git의 learning curve를 극복하지 못하고 한참 동안 다시 원시적인 형상관리를 했던 기억이 난다.

사람들은 평소에 불편하다고 느꼈던, 혹은 어렵다고 느꼈던 일을 어떠한 도구를 통해 쉽게 해결했을 경우 그 도구를 편리하다고 평가한다. 사용하기 어려운 도구는 좀처럼 편리하다고 평가되지 않는다. git은 초반 개념잡기가 쉽지않고, 오류상황에 직면했을 경우 특히 어렵게 느껴지기 때문에 불편한 도구라고 생각되기 쉽다. 하지만, GUI tool의 도움을 받고 몇 가지 사항만 주의하여 사용한다면 분명히 다른 어떠한 형상관리 도구보다 편리하고 강력하다는 것을 느끼게 될 것이다. 처음에는 Photoshop이 그림판보다 불편하게 느껴지지만, 결국 조금만 어려운 작업을 하게되면 Photoshop이 대부분의 경우 더 편리하다는 것에 모두들 동의하지 않는가?!

복잡성

초반에 설명했지만, git에는 local repository와 remote repository 의 두 개념이 존재한다. 단순히 물리적으로 보아도 remote repository만 있는 것보다 복잡도가 두 배가 되었고, local repository와 remote repository의 관계에서 생기는 복잡성까지 보태져 git이 다른 시스템들에 비해 확연히 복잡해 보인다. 브랜치라도 많아지는 날에는 아래와 같은 무시무시한 그래프를 보게 될 수도 있다.

정말 복잡하다. 좋게 이야기 하자면 이런 복잡한 형태의 형상관리까지 해 낼 수 있다고 할 수도 있겠다. 하지만, 이렇게 되면 히스토리 파악이나 형상관리가 매우 힘들어진다. 위와 같은 일이 발생하지 않게 하기 위해서는

1. 브랜치를 깔끔하게 유지해야 하고,
2. 합리적인 브랜치 전략을 세워야 한다.

브랜치 전략은 다음 글에서 git-flow를 통해 알아볼 것이고, 브랜치를 깔끔하게 유지하는 방법을 지금부터 살펴보기로 하자.

복잡한 git, 이 7단계만 기억하자

git graph에는 크게 두가지 요소가 존재한다. 바로 '점'과 '선'이다. 점은 commit을 나타내고, 선은 흐름을 나타낸다. 선을 따라가면 히스토리를 알 수 있고, 한 점에 집중해서 보면 해당 변경내역을 알 수 있는 구조다. 흐름은 초반에 설명한 브랜치 개념과 거의 일치한다. 한 상태에서 서로 다른 두 개의 수정이 생기면 흐름이 갈라진다. 의도적으로 갈라지는 흐름을 만든 것이 브랜치이고, 의도하지 않았지만 갈라지는 경우도 있다. 의도하지 않았지만 갈라지는 경우가 생기면 그래프가 복잡해진다.
 

좌측과 유사한 형태의 그래프를 자주 접했을 것이다. 이것이 바로 의도하지 않았는데 흐름이 갈라지는 경우인데, commit message에 'Merge branch...'라고 남아있는 것을 볼 수 있다. 이것은 나는 의도하지 않았지만 git이 필요에 의해 임시적으로 브랜치를 만들었고 그것을 내가 작업중이던 브랜치에 머지해주었다는 것을 의미한다. 이러한 현상이 발생하는 이유는 내가 commit 2에서 수정을 하고 있는데, 다른 사람이 commit 2에서 무언가를 수정해서 commit 4를 이미 서버에 반영(push)했기 때문이다. 이런 경우에 그래프를 우측과 같이 깔끔하게 만들어주고 싶다면, local에서 commit하기 이전에 반드시 pull을 해야 한다. 문제없이 pull이 되면 좋지만, 그렇지 않은 경우나 이미 commit을 해버린 경우는 다음과 같은 방법을 사용해야 한다.

1. patch생성

패치는 작업내용을 다른곳(다른 branch, 혹은 다른 repository)에 반영할 수 있는 형태로 만들어 주는 것이다. 충돌과 같은 문제가 발생하지 않는 이상 어디에든 반영이 가능하다. 지금은 작업 중이던 내용을 임시적으로 저장해 놓는 용도로 사용하기 때문에 일종의 backup이라고 생각해도 좋다.

Source Tree에서는 다음과 같이  "Create Patch..." 메뉴를 통해 패치를 만들 수 있다.

맨 위의 탭을 통해 아직 commit하지 않은 내용을 패치로 만들 수도, 이미 commit한 내용을 피치로 만들 수도 있다. 위에서 보다시피 여러개의 commit을 하나의 패치로 만들 수도 있는데, "Create a separate patch file per commit" 옵션을 주면 각각의 commit별로 패치 파일을 따로 만들 수 있다. 

2. reset

git의 현재 상태를 특정 시점으로 되돌리는 기능이다. 되돌아가고 싶은 커밋을 선택하여 우클릭하면 리셋 메뉴가 나온다.

Local에서 아무런 commit도 하기전의 상태를 선택해서 돌아가면 된다. 리셋에는 soft / mixed / hard 세 종류가 있지만, 이미 패치를 만들었기 때문에 모든 변경사항을 깔끔하게 되돌려주는 hard reset만 사용하기로 한다.

3. pull

내가 작업하기 이전 상태로 돌려놨으니, pull을 통해 다른사람들의 최신 작업내용들을 remote repository로 부터 다운받아 local repository의 내용을 최신으로 업데이트 한다.

4. patch적용

1단계에서 생성한 패치를 반영하는 단계이다. 일종의 restore 라고 생각하면 된다. "Apply Patch..." 메뉴를 선택하면 Mode를 고르는 화면이 나오는데, 이미 commit한 것을 패치로 만들었다면 "Import as full commit"을, 작업중이던 내용을 패치로 만들었다면 나머지 아무거나 선택하면 된다.(modified 상태로 반영할지, staged 상태로 반영할지 선택하는 것이다)

 

5. conflict 처리

A라는 commit에서 나는 B라는 commit을, 다른 사람은 C라는 commit을 만들었는데 이 두 commit이 같은 파일의 독립적이지 않은 부분을 서로 다르게 수정했다면 git은 이것을 어떻게 처리해야 할지 알 수 없게 된다. 이것이 바로 conflict(충돌)다. 충돌이 발생하면 git에서 자동으로 다음의 표준 형식으로 충돌이 발생한 부분을 표시해 주며, 이를 에디터나 각종 merge tool(3way-merge 가 지원되는 tool이면 좋다)을 이용하여 적절하게 수정해 주어야 한다.

git 의 충돌표현 표준형식
<<<<<<< HEAD C commit에 의해 변경된 내용입니다. ======= B commit에 의해 변경된 내용입니다. >>>>>>> branch name

충돌 해결에 관한 자세한 내용은 이 글의 주제에서 벗어나므로, 더 자세히 알고 싶다면 인터넷 검색을 이용하길 바란다. SourceTree를 활용한 브랜치 합치기(merge)브랜치와 Merge의 기초 #충돌의 기초 

6. commit
7. push

위에서 본 7단계의 일련의 작업은 매우 유용하다. 여기서는 브랜치를 깔끔하게 유지하기 위한 방법으로서 살펴봤지만, git을 사용하다 무언가 문제에 직면했을 때 위 7단계를 적절히 사용하면 어떤 문제든 해결할 수 있다. 위 작업을 요약하면 변경점 백업 - 문제없는 버전으로 리셋 - 최신버전으로 업데이트 - 변경점 복원 이다.

Local repository에서 생긴 모든 문제는 당연히 이 것으로 해결이 가능하고, remote repository에서 문제가 생겨도 관리자가 이 방법으로 해결이 가능하다. 단 한가지, 문제없는 버전을 찾는 것이 중요한데, 잘 모르겠거든 다음의 한가지만 기억하자!

공통의 조상으로 돌아가라!
1부를 마치며..

처음에 말했듯이, git은 전 세계에서 가장 많이 사용되는 형상관리 tool이다. 가장 많이 사용되는 데에는 분명한 이유가 있다. 필자는 그럼에도 불구하고 learning curve를 뛰어넘지 못해 git에서 손을 놓은 사람들이 조금이라도 쉽게 사용할 수 있었으면 하는 바램에서 이 글을 쓰기 시작했다. 

주저리주저리 얘기했지만, 결국은 정형화된 패턴(7단계의 문제해결 방법)을 적용해서 가장 심플한 형태를 유지하며 쓰자는 얘기다. 여기까지 읽으면서 어느정도 기본 개념이 잡혔다면, 2부에서는 git이 좀더 강력한 도구로 작용할 수 있도록 해주는 commit전략과 브랜치 운영 전략(git-flow)에 대해서 살펴볼 예정이다. 하루 빨리 git의 고수가 되어 어느 조직에 있던지 사람들과 자유롭게 협업하고, 심지어는 그 들이 직면한 git관련 문제도 해결해 줄 수 있는 슈퍼히어로가 되길 바란다!!


Comments