목공책 하나 들이셔요~

2013년 12월 17일 화요일

[Hg] 0. Subversion 사용자를 위한 Mecurial 안내서

저는 버전 컨트롤 시스템(Version Control System, VCM)으로 CVS와 subversion을 사용해 왔습니다. 최근에는 그 중에서 특히 subversion을 더 많이 사용해 왔습니다. 참 좋은 VCM이긴 한데 최근 들어 여러 문제가 생기기 시작했습니다. 버전이 올라가면서 이전 저장소와 호환이 되지 않는 문제도 있었고, 충돌을 제대로 해결하지 못하다가 결국에는 소스 일부를 날려먹는 일까지 생겼습니다.

그래서 최근 몇년 사이에 주목받고 있는 새로운 개념의 분산형 SCM 중 Mercurial을 검토하고 이걸로 갈아타려고 하고 있습니다. Mecurial로 갈아타려 공부를 해야 해서 좋은 텍스트를 찾아 번역하여 소개합니다. hginit.com에서 제공하는 Joel Spolsky의 Hg Init: Mercurial Tutorial을 6편의 글로 나누어서 연재합니다.

원문은 여기를 참조하세요. http://hginit.com/00.html


우리 회사의 프로그래머들이 subversion을 버리고 Mercurial로 바꾸겠다고 결정했을 때 전 당혹스러웠습니다.

왜 SCM을 바꾸면 안되는지에 대한 이유를 강변해야 했습니다. 그래서 "중앙 서버에 소스를 보관해야 안전하다"고 주장했죠. 하지만 제가 틀린 거였습니다. Mercurial은 각 개발자의 PC에 소스 레포지토리의 복사본들이 모두 저장되어 있기 때문에 실제로는 더 안전한 거였습니다. 심지어 Mercurial을 사용하는 팀들 대부분은 중앙 서버를 사용하고 이 곳에 백업을 받습니다.

그래서 저는 "분산형 버전 컨트롤 시스템은 브랜치를 까기가 너무 쉽다. 그리고 브랜치는 항상 문제를 일으킨다"고 주장했습니다. 하지만 이것도 틀린 거였습니다. subversion의 브랜치는 나중에 병합(merge)할 때 필요한 정보를 충분히 저장하지 않아서 문제가 많이 생겼지만 Mercurial의 병합은 편하고 쉽습니다. 그러므로 브랜치는 이제 상식이 되었고 더 이상 팀에 피해를 주지 않습니다.

마침내 저는 "좋아, 그걸 써 보도록 할게. 하지만 내가 그걸 공부하고 싶진 않아"라고 얘기했고 직원들에게 subversion에서 하는 작업과 동일한 Mercurial 명령어를 요약표(cheat sheet)로 만들어 달라고 요청했습니다. 그 요약표를 여러분께 보여드릴 수 있지만 그러진 않을 겁니다. 왜냐하면 그 요약표가 오히려 더 혼란을 주기 때문입니다.

만일 당신이 지금까지 subversion을 써왔다면 당신의 뇌는 음... 좀 점잖게 말해서 약간 손상되었습니다. 그러므로 당신은 새로 교육받아야 합니다. Mercurial을 잘 모를 때는 subversion보다 훨씬 복잡하다고 생각했습니다. 하지만 Mercurial을 잘 알게 되니 그렇게 단순할 수가 없습니다.

그래서 이 안내서를 쓰게 된 것입니다. 이 안내서에서는 subversion의 용어로 설명하지 않을 겁니다. 왜냐하면 당신의 뇌에 더이상 손상을 주고 싶지 않기 때문입니다. 이 안내서를 통해 손상된 뇌를 다시 복원시키고 완전히 깨끗한 상태에서 Mercurial에 대해서 배우는 게 좋습니다.

만일 subversion에 대해서 모르고 한번도 써본 적이 없다면 이 글은 건너 뛰어도 됩니다. 바로 다음 글로 가세요.

자 이제 준비되셨나요? 간단한 퀴즈부터 풀어보도록 하지요.

퀴즈 1. 당신은 한번에 완벽한 코드를 작성하나요?

만일 "예"라고 답했다면 당신은 거짓말을 한 것이고 속인 것입니다. 틀렸으므로 새로 문제를 풀어보세요.

새로운 코드는 버그가 있기 마련입니다. 그 코드가 제대로 돌기까지는 어느 정도의 시간이 필요합니다. 새 코드가 생기고 얼마 동안은 팀의 동료들에게 이 새 코드가 트라우마가 될 수도 있습니다.

subversion을 사용할 때는 어땠는지 기억을 더듬어 봅시다.

* 새로운 코드를 체크인하면 다른 모든 사람이 그 코드를 받게 됩니다.

그런데 당신이 새로 만든 코드는 당연히 버그가 있기 때문에 둘 중 하나를 택해야 합니다.

* 당신은 버그가 섞인 코드를 체크인 해서 다른 동료를 돌아버리게 만들 수 있거나...
* 새 코드의 버그가 완전히 잡힐 때까지 체크인을 하지 않거나...

subversion은 항상 이 끔찍한 딜레마를 겪게 합니다.

subversion을 사용하는 팀의 멤버들은 며칠 혹은 수주일 동안 체크인을 하지 않기도 합니다. 특히 아직 미숙한 신입사원들은 체크인하기를 매우 두려워 합니다. 잘못된 코드를 체크인하면 컴파일이 안되기도 하고 다른 동료를 화나게 할 수도 있기 때문입니다. 신입사원이 잘못된 코드를 넣어 전체 코드가 빌드되지 않으면 어떤 상사는 "넌 해고야!"라고 소리치면서 그 신입사원의 책상을 싹 치워버리는 경우도 있습니다.

이런 두려움 때문에 팀원들은 몇주 동안 체크인을 하지 않으며, 따라서 버전 컨트롤 시스템의 잇점을 전혀 취하지 못하게 됩니다. 신입이 체크인을 하려면 선배 사원을 불러 코드를 점검해 달라고 부탁해야 합니다. 버전 컨트롤을 할 수 없다면 도대체 이걸 왜 사용하는 겁니까?

subversion을 사용하는 경우를 그림으로 표현해 보았습니다.


Mercurial에서는 각 개발자가 자신의 레포지토리를 가지고 있습니다.


그러므로 당신은 아무 부담없이 개인 전용의 레포지토리에 커밋을 할 수 있고 버전 컨트롤 시스템의 잇점을 사용할 수 있습니다. 어느때라도 코드가 좀 더 살이 붙고 버그가 고쳐지고 좋아졌다면 커밋할 수 있습니다.

새로 만든 코드가 어느정도 검증이 되면 당신은 당신의 코드를 다른 이들도 사용했으면 할 겁니다. 이럴 때는 당신의 코드 변화를 중앙 레포지토리에 push를 하면 됩니다. 다른 사람들은 중앙 레포지토리로부터 pull을 하여 받아냅니다. 그래서 당신의 코드가 다른 이에게 전파됩니다.

그래서 Mercurial은 당신의 새로운 코드를 커밋하는 것이 다른 사람에게 영향을 미치지 않게 합니다.

즉 당신은 hg com 명령으로 당신의 코드를 언제든 커밋할 수 있으며, 많은 변화가 이루어졌고 안정화되었다면 hg push 명령으로 중앙 레포지토리에 밀어넣을 수 있습니다.

또 하나의 큰 개념적 차이

어떻게 모든 거리마다 이름이 있는지 아시나요?

근데 일본에서는 그렇지 않은 것 같습니다. 일본은 각 블록마다 번호를 가지고 아주 큰 도로만 이름을 가지는 것 같습니다. (우리나라의 옛주소 방식과 동일합니다)


Subversion과 Mecurial도 이와 비슷한 차이점이 있습니다.

Subversion은 리비젼(revision)이라는 개념을 사용하며, 이 리비젼은 스냅샷 개념처럼 특정 시점에서 파일 시스템상의 파일들이 어떤 형태인지를 기록합니다.

Mecurial에서는 체인지셋(changeset)이라는 개념을 사용하며, 이 체인지셋은 각 리비젼간의 변경된 내용을 리스트로 정리한 것을 의미합니다.

6/1이나 12/2나 같다구요? 이 둘의 차이가 있을까요?

차이는 분명 있습니다. 당신과 내가 같이 코딩을 한다고 생각해 봅시다. 각각이 코드를 브랜치해서 상당한 양의 변경을 했다고 합시다. 그래서 당신의 코드와 제 코드가 상당부분 달라졌다고 합시다.

우리가 이 두 코드를 병합(merge)해야 할 때, Subversion은 당신의 수정본과 저의 수정본 두개의 리비젼을 하나로 뭉뚱그려 하나의 큰 골치덩어리로 만들어 냅니다. 대부분의 경우 병합은 실패로 끝나며 수 페이지에 이르는 병합 충돌(merge conflict)이라는 메시지를 쏟아냅니다. 하지만 이들은 실제로 충돌이 일어난게 아닙니다. 단지 Subversion이 어떻게 병합해야 할지 모를 뿐입니다.

대조적으로 Mercurial로 브랜치된 두 코드를 작성할 때, Mecurial은 체인지셋의 리스트를 계속 유지합니다. 나중에 브랜치된 두 코드를 병합할 때는 Mecurial은 어떤 코드가 변경되어고 어떻게 변경을 적용할 수 있는지 등의 아주 많은 정보를 바탕으로 병합을 수행하게 됩니다. Subversion은 단지 두개의 마지막 스냅샷을 바탕으로 병합을 시도하려 하기 때문에 어려운 상황에 빠지는 것과 대조적입니다.

예를 들어 제가 어떤 함수를 약간 고치고 이의 위치를 옮겼다고 할 때 Subversion은 이런 변경 절차를 기록하지 않기 때문에 나중에 병합할 때 이 함수가 갑자기 새로 나타난 것이라고 생각합니다. 반면 Mercurial은 이 함수가 약간 변경되고 위치가 옮겨진 것을 알기 때문에 나중에 병합할 때 성공할 확률이 더 높습니다.

Mercurial은 모든 것을 체인지셋이라는 개념으로 생각하기 때문에 이 체인지셋으로 할 수 있는 일에 관심을 가지게 될 겁니다. 그래서 당신은 중앙 레포지토리 서버에 올리지 않고도 특정 동료에게 당신의 수정본을 개별적으로 푸쉬하여 테스트해 보게 할 수도 있습니다.

이 모든 것이 혼란스럽다 할지라도 걱정할 필요는 없습니다. 이 안내서를 보는 동안 완전하게 Mercurial에 대해 깨닫게 될 것이기 때문입니다. 지금 이 순간 알아야 할 가장 중요한 사실은 Mercurial은 리비젼이 아니라 체인지셋이라는 개념을 사용하며 이는 Subversion보다 훨씬 더 병합을 잘 할 수 있게 해준다는 것입니다.

병합이 더 이상 악몽이지 않기 때문에 당신은 부담없이 브랜치를 할 수 있기도 합니다.

재밌는 사실을 더 알려드릴까요? 대부분의 Subversion을 사용하는 팀들은 약간씩 다르긴 하지만 대부분 비슷한 이야기들이라 "Subversion 이야기 #1"이라고 이름 붙일 수도 있는 것입니다. 이야기는 이렇습니다. 어떤 시점에 그들은 코드를 브랜치하기를 원했습니다. 보통 고객에게 전달되어야 할 안정화된 버전을 개발자들이 지금 한창 수정하고 있는 버전과 브랜치로 분리하게 됩니다. 그리고 각 개발팀에서 개발되어서 테스트되고 잘 동작하는 코드들에 대해 보고합니다. 하지만 이들을 병합하는 순간 문제가 발생하기 시작하고 악몽이 시작됩니다.

그래서 여섯명의 프로그래머가 5분동안 작업하면 끝날 일을 하나의 컴퓨터에서 2주 동안 수작업으로 일일이 버그 픽스들을 수정하여 새 코드가 적용된 안정화 버전을 실제로 안정화하는 절차를 거쳐야 하게 되는 그런 이야기입니다.

그리고 대부분의 Subversion을 사용하는 팀 멤버들은 다시는 브랜치를 까지 않겠다고 다짐하게 됩니다. 그리고는 하나의 트렁크에서 모두 작업하면서 커다란 #ifdef 블록을 만들게 되고, 고객들은 모두 디버그되기 전까지는 새로운 코드의 기능을 맛볼 수 없게 됩니다. 솔직히 말해 참으로 이상한 상황입니다.

안정화 버전과 개발 버전을 분리하는 것은 사실 버전 컨트롤 시스템이 지원해야 하는 가장 기본적인 기능입니다.

Mercurial을 사용하게 되면 그것에 대해 신경쓰지 않아도 브랜치는 언제가 가능하고 병합하는데 두려움을 가질 필요가 없습니다.

또한 몇몇 프로그래머들이 새로운 기능을 테스트할 수 있는 소수만을 위한 레포지토리를 만들 수도 있다는 의미이며 이를 메인 레포지토리에 병합시킬 수도 있다는 의미입니다.

예를 들어 QA 레포지토리를 만들어서 QA 팀이 새로운 코드를 점검할 수 있게 할 수 있습니다. 잘 동작하게 되면 QA팀은 이를 중앙 레포지토리에 푸쉬할 수 있습니다. 이는 곧 중앙 레포지토리는 잘 동작하고 테스트된 코드만이 보관된다는 의미입니다.

이는 또한 분리된 레포지토리에서 당신은 여러가지 실험을 마음대로 할 수 있으며 그것이 성공하면 중앙 레포지토리에 옮길 수 있음을 의미합니다. 만일 잘 동작하지 않으면 버리면 그만입니다.

마지막 큰 개념적 차이

Subversion과 Mecurial의 마지막 큰 개념적 차이는 사실 별 중요하지 않은 것일 수도 있고 잘 알아차리지도 못하는 것일 수도 있습니다.

Subversion은 기본적으로 파일에 대한 리비젼 컨트롤을 합니다. 반면에 Mercurial의 리비젼 컨트롤은 서브 디렉토리를 포함한 전체적인 디렉토리 구조를 포함합니다.

Subversion에서는 어떤 서브 디렉토리로 들어가서 어떤 변화를 커미했다면 그건 단지 자신과 그 아래 디렉토리의 변화만을 커밋함을 의미합니다. 이는 다른 계층의 서브 디렉토리에서의 변화를 커밋하는 것을 빠뜨릴 수도 있다는 가능성을 의미하기도 합니다.

반면 Mercurial에서는 모든 커맨드는 전체 트리에 대해 적용됩니다. 만일 코드가 c:\code에 있다면 hg commit 커맨드를 날리게 되면 c:\code뿐 아니라 그 아래 모든 디렉토리에도 같은 효과가 발생하게 됩니다.

이게 큰 문제가 아닐 수도 있습니다. 하지만 당신이 기가급의 큰 레포지토리를 운영하고 있고 소수가 특정 서브 디렉토리만을 체크아웃해서 개발하는 상황이라면 Mercurial이 적합치 않을 수 있습니다. 그래서 Mercurial을 사용할 경우에는 각 프로젝트를 실제 개발단위로 쪼개어 작은 레포지토리로 만들어 사용하는 것이 좋습니다.

마지막으로...

Mecurial은 Subversion보다 좋습니다.

Mercurial은 팀 단위의 소스코드 개발에도 좋고, 혼자 개발할 때도 좋습니다.

그냥 더 좋습니다.

Mercurial이 어떻게 동작하는지 이해하고, Mercurial이 하는 방식대로 당신이 작업하고, 그에 대항하지 않고, 옛날 Subversion 쓰던 방식대로 하지 않고, Mecurial이 예측하는대로 일하는 법을 배운다면 당신은 행복할 것이고 성공할 것입니다. 그건 그저 TV 리모컨을 동작하는 것처럼 쉬울 겁니다.

저는 당신이 처음 몇일 동안 Mercurial을 포기하고 Subversion으로 돌아가야 겠다는 유혹을 받을 거라는 걸 알고 있습니다. 왜냐하면 Mercural을 사용하는 건 마치 외국에 사는 것처럼 이상하고, 향수병에 시달릴 것이고, Mecurial의 작업 디렉토리가 더 많은 디스크를 사용한다고 자기 합리화를 할 겁니다. 하지만 실제로는 Subversion보다 Mecurial이 더 작은 디스크를 사용합니다. 진짜로요.

그러고 나서 나중에 다시 Subversion으로 돌아가게 되면 Mercurial 식으로 브랜칭하고 레포지토리를 클로닝(cloning)하고 여러 작업을 하다보면 Subversion이 더 이상 좋은 솔루션이 아니라는 걸 깨닫게 될 겁니다.

동료가 준 "Subversion 사용자를 위한 Mercurial 요약표"에 의존하여 석달동안 hg fetch는 svn up가 같다고 믿고 실제 hg fetch가 뭘하는 건지 모르는 상태에서는 계속해서 문제에 부닥칠 것이고 Mercurial을 흉보게 될 겁니다. 하지만 Mercurial이 어떻게 동작하는지 이해하지 못한 당신 스스로를 비난해야 합니다.

아마 제가 겪었던 이 모든 걸 당신이 하게 될 겁니다.

제가 겪었던 실수를 반복하지 마세요. Mercurial을 배우고, Mercurial을 믿으세요. 그리고 Mercurial이 어떤 식으로 동작하는지 알아내세요. 그러면 당신은 소스 관리에 있어서 완전하게 한 세대를 앞서가게 될 겁니다. 당신의 경쟁자가 병합의 충돌을 해결하기 위해 몇주를 소모하는 동안 당신은 hg merge를 입력하곤 "오호~ 대단한 걸? 그냥 돼잖아?"라고 외치게 될겁니다.

(역시 IT 분야의 글은 조크도 많고 비유도 많아서 번역이 참 어렵습니다. 일일이 번역하지 않고 그 뜻만을 추려서 한국어식으로 바꾸어 의역했음을 양해 바랍니다. 좋은 글을 주신 Joel Spolsky에게 감사드리며, 이어지는 연재 기대해주세요)

댓글 없음:

댓글 쓰기