코딩이란 게 물 흐르듯 계획대로 이루어지는 경우는 많지 않기 때문에 소스 관리는 꽤 중요합니다. 작업하다 보면 한 모듈의 작업이 끝나지 않은 상태에서 다른 기능을 손보게 되기도 하고, 갑자기 새로운 아이디어가 떠올라서 지금까지 하던 것들을 다 뒤집고 다시 코드를 쓰는 경우도 있죠. 코딩을 하는 건 글을 쓰는 것과 비슷해서 '쓰고', '고쳐 쓰고', '또다시 쓰고'의 연속이니까요.
당나라의 시인 가도가 '새는 못가 나무에 자고 중은 달빛 아래 문을 미는구나'라는 구절을 써놓고, '밀다'와 '두드리다' 중 어떤 표현을 쓸까 고민하던 것을 두고 '퇴고推敲'라는 말이 만들어졌다죠. 개인적으로는 고민할 것도 없이 '미는구나'의 손을 들어주겠지만, 가도가 행차길을 막아서는 바람에 그의 고민을 듣게 된 한유(중국 당을 대표하는 문장가)는 '두드리다'를 추천했다는... 그게 중요한 건 아니고, 어쨌든 코드를 쓸 때도 마찬가지로 엄청난 (논리적) 퇴고를 하게 된다는 거예요.
그런 이유로 코딩을 할 때, 어느 단계를 적당히 마무리했다는 느낌이 들면 스냅숏을 만들어두면 좋겠죠? 이후 다른 작업을 하다가 마음에 안 들면 다시 그 스냅숏부터 시작할 수 있을 겁니다. 그런 작업을 형상관리 혹은 버전 관리라고 합니다. 그런 도구 중 기존의 SVN을 거의 대체해버린 것이 Git이죠. Git의 가장 큰 특징은 소스의 분산형 관리입니다.
먼저 Git의 구조에 대해 알아보겠습니다. 개발자들이 사용하는 로컬 머신에 현재 작업하고 있는 물리적인 소스가 저장된 영역이 바로 Working Directory입니다. 탐색기나 파인더로 들어갔을 때 실제로 파일이 존재하는 영역이에요. 개발도구에서 저장하면 이 영역에 저장이 되죠. 형상관리 시스템을 사용하지 않고 있다면, 이 영역만 존재하겠죠? Working Directory에서는 저장작업을 하면 이전 버전은 사라집니다. 여기에 Git을 적용하면 로컬에는 Staging Area와 Local Git Repository 영역이 생기게 되고, 스냅숏은 Local Git Repository에 저장하게 됩니다. 이 작업을 Commit이라고 합니다. 그리고, 다시 특정 시점의 소스로 돌아가려면 이 Local Git Repository에서 해당 시점의 스냅숏을 꺼내게 되죠. 이 작업을 check out이라고 해요. 중간에 존재하는 Staging Area는 Working Directory에서 작업을 진행하다가 아직 commit을 할 만큼 완전하지 않을 때, 중간 작업분을 저장해두는 영역이라고 생각하시면 됩니다.(코딩 도구에 따라 이 Staging Area는 명시적으로 보이지 않는 경우도 많음)
여기서 가장 오른편에 있는 Remote Git Repository가 원격 저장소가 되겠죠. 이쪽에 올려둬야 랩탑이 아작 나도 다시 소스를 살릴 수가 있습니다. 그리고, 단독개발이 아닌 협업 개발을 할 때에도 이 저장소를 통해 서로 소스를 교환하게 됩니다. Local Repository에서 이 Remote Repository로 밀어 넣는 작업을 push라고 합니다. 반대로 Remote Repository에서 소스를 당기는 작업을 Pull이라고 해요. 그런데, push와 정반대의 작업은 사실 pull이 아니라 fetch입니다. fetch 작업을 하면 Local Git Repository까지 소스를 내리죠. 하지만, 보통 Remote에서 소스를 가져올 때는 이어서 코드를 쓸 목적이기 때문에 Working Directory까지 소스를 내려주는 pull을 많이 사용하게 됩니다. 이런 원격 저장소 서비스를 제공하고 있는 사이트가 유명한 GitHub(github.com)입니다.
이제 협업할 때를 생각해보겠습니다. 이때는 소스의 마스터를 관리하는 사람을 명확하게 지정해두는 게 좋겠죠. 함께 개발을 하기 위해서는 우선 마스터(이를 base repository라고 합니다)에서 소스를 가져와야 합니다. 이 작업은 원격 서버 Remote Repository 간에 이루어지게 되며, 이를 fork라고 합니다. 이때 해당 branch의 가장 마지막 commit 된 버전을 가져오게 돼요. 이를 head라고 합니다.(정확하게는 head란 해당 brench의 마지막 커밋에 대한 포인터를 이야기함)
이후는 위의 개인작업과 동일합니다. fork 해 온 버전을 Working Directory까지 끌고 와서 그 위에 자신에게 할당된 작업을 수행하는 거죠. 해당 작업이 끝나면 자신의 Remote로 Push 하여 올려두고, 소스를 fork 해 온 서버 쪽으로 pull request를 날립니다. 그러면, 마스터 관리자는 해당 소스에 문제가 없는지, 이후 작업된 부분과 conflict는 없는지 확인한 후 이를 base repository에 머지하게 되는 거예요.
내용은 간단해 보이지만 실제로 협업을 수행할 때에는 어느 정도 가이드라인을 만들어두고, 실제 진행을 하며 가장 적절한 운영방법을 찾아야 할 겁니다. fork와 Pull Request를 수행할 base의 branch도 서로 헛갈리지 않게 명시적으로 정의할 필요가 있겠고, 너무 자주 Pull Request를 하는 것도 피하는 게 좋겠죠? 하지만, 역시 진행되는 프로젝트의 성격과 협업 구조, 그리고 개발자들의 능력에 따라 매번 적정 가이드라인은 달라질 거예요.
중요한 건 역시 입만 터는 게 아니라 실제로 돌려보는 것이겠죠? ;)
다음에는 xcode로 개발을 할 때, xcode 내의 Source Control 메뉴를 사용하여 여러 명이 협업을 하는 방법을 이야기해볼까 합니다.