Github로 멋지게 협업하기 by 신성환(github.com/blueStragglr)
*이 글은 깃 초심자에게 깃허브 사용법을 알려주기 위한 내용이 아닙니다! 기본적인 깃의 동작 방식을 알고 있는 사람들 중 목적성과 당위성을 바탕으로 한 사용을 원하는 사람들에게 경험을 공유하기 위해서 작성되었습니다.
들어가며
회사에서 이미 쓰고 있어서, 누가 좋다고 그래서, 아니면 어떤 이유로든 일단은 쓰고 있는 깃과 깃허브. 여러분은 깃을 왜 쓰고 계신가요?
스스로 말하기 조금 부끄러운 이야기이지만, 저는 기억력이 꽤 좋은 편입니다. 별 관심이 없는 사람의 음식 취향 같은것을 기억했다 오해를 샀던 적도 있었구요. 공들여 작성한 코드와 전체적인 구조를 기억하는 것은 스쳐지나가는 이야기를 기억하는 것 보다 훨씬 쉬웠고, 손도 제법 빠르다 보니 혼자 작은 프로젝트를 작업할 때는 버전관리나 개발 상태에 대한 필요가 생기기 전에 프로젝트를 마무리하는 경우가 더 많았습니다.
하지만 프로젝트 크기가 커지고 다양한 사람과 함께 작업하게 되면 될수록 기억력에 의존하는 협업은 커뮤니케이션 비용의 증가와 인간관계에서의 스트레스를 불러일으켰습니다. 내가 기억하고 있는 걸 누군가는 기억하지 못하고 있고, 다른사람이 말한 걸 잘못 이해하고 있는 경우도 있었습니다. 자연스레 금방 할 일을 하루종일 하게 되고 감정도 상하게 됐죠.
저와 당시 저의 팀원들은 이러한 생산성의 문제를 해결하기 위해서 문서화에 좀 더 노력을 쏟았고, 모호한 소통방식에서 오는 비용을 상당부분 제거할 수 있었습니다. 그 중에서 깃허브를 적극적으로 이용하는 것을 통해 개발적인 협업이 많이 개선되는 것을 느꼈고, 그 경험을 공유해보고자 합니다.
Git과 Github
Git과 Github가 무엇인지 간단하게 짚고 넘어가보도록 하겠습니다.
Git은 분산형 버전 관리 시스템 중 가장 유명한 오픈소스입니다. 분산형 버전 관리 시스템이란 말이 거창해 보일 수 있지만, 사실 여러 대의 컴퓨터에서 동시에 사용하는(분산된) 버전 관리용 툴이라고 생각할 수 있습니다. Github은 이렇게 여러 곳에 분산된 버전들을 조율하고 합치는 허브인 셈입니다.
- 왜 쓰나요?
물론 사람마다 목적은 다르겠지만, 협업 측면에서 생각해 봤을 때
- 개발 내용을 기록하고
- 개발 내용을 주고받으며
- 개발 내용을 합치기 위해
사용한다고 이야기하고 싶습니다. 궁극적으로는 좀 더 생산적인 개발 협업을 목적으로 한다고 할 수 있을 것 같습니다.
Commit
- Commit?
커밋이란 작업 내용을 주고받기 위한 최소 대화 단위라고 할 수 있습니다. 커밋을 남기는 시점까지 작업된 작업물들에 대한 스냅샷인 셈입니다.
- Atomic commit: 커밋 크기의 중요성
즐거운 마음으로 하는 일상 대화에서도 한번에 서너개의 주제로 이야기할 수는 없습니다. 하물며 일할 때 하는 개발은 오죽할까요. 커밋 하나가 포함한 주제가 여러 개가 되는 순간 커밋의 목적을 파악하기도 쉽지 않고이는 곧바로 커뮤니케이션 비용으로 직결됩니다.
물론 굉장히 큰 이슈를 주제로 잡고 하나의 주제라고 주장할 수도 있지만, 이런 주장이 커뮤니케이션 비용을 줄이는데 도움을 주지는 않습니다. 가급적이면 아래와 같이 하나의 커밋이 하나의 변경사항을 포함하도록 깔끔하게 작성되는 것이 유리합니다.
Fork & Pull-request
- Upstream과 fork
본격적인 협업으로 들어가면 업스트림 레포지토리를 생성하고 각자 포크를 통해 수정사항을 작성하게 됩니다. 업스트림은 단어 뜻 그대로 “원류” 라는 뜻으로, “오염되지 않은” 소스코드가 저장되는 곳입니다.
오염되지 않았다는 것은 관리자(혹은 팀 전체)가 협의한 관리 규칙을 통과한 소스가 존재한다는 것을 뜻합니다. 변수 명에 대한 선언 규칙이나 디렉토리 구조, 코드 스타일 등에 대한 협의를 PR 과정을 통해 정돈된 코드만을 업스트림에 머지하는 것입니다.
- Pull-request
이들 사이의 관계를 좀 더 직관적으로 표현하자면 아래와 같은 그림으로 그릴 수 있습니다.
작업자 각각의 원격 저장소로써 Upstream을 포크한 Origin을 사용하고 어느 정도 작업이 마무리된 경우 Upstream에 내 작업을 합쳐 달라는 요청인 pull-request를 보냄으로써 모두와 함께 코드를 검토하고 merge하게 됩니다.
Rebase
이 때, 원활한 merge를 위해서 pull-request 이전에 해야 할 것이 있습니다. 좀 더 정확하게는 깔끔한 이력을 관리하기 위해서입니다.
Rebase는 말 그대로 base를 변경하는 것입니다. 내 작업이 시작된 지점을 변경하는 것이죠. 변경하는 이유는 아래와 같은 상황을 방지하기 위해서입니다.
리베이스를 하지 않고 merge하게 되면 “이전 커밋”을 정의할 수 없는 상황이 발생합니다. ‘버전’이라고 부르는 이력이 더이상 직선이 아니게 되는 것이죠. 그 이유는 같은 곳을 수정한 두 커밋을 머지할 때의 상황에서 찾을 수 있습니다.
같은 코드를 두 커밋이 다르게 수정한 경우, 리베이스 없는 머지의 경우에는 conflict resolve에 대한 커밋이 머지 커밋에 찍힙니다. 만약 이 때, 수정한 코드로 인해서 버그가 발생했다면 어떤 커밋으로 돌아가야 할까요? 여기서 둘 중 어떤 커밋이 정상적으로 작동하는지 모르기 때문에, 어떤 코드로 돌아갈지 모르는 상황이 됩니다. 원래의 목적인 버전관리가 안되는 셈이죠.
Review
이제 모든 과정이 마무리되었다면 서로의 코드를 읽어보고 리뷰할 일만 남게 됩니다. 리뷰 자체는 크게 다룰 것이 없습니다. 코드를 읽고, 코멘트를 남기고, 소통하면서 의문과 걱정을 해소하면 됩니다.
리뷰를 할 때, 중요하게 생각해야 하는 것이 있습니다. 리뷰는 시험이나 청문회가 아니라는 거죠. 각자 자신의 철학과 프로페셔널리티를 갖추고 있기 때문에 코드에 대한 생각이 다를수도 있고, 혹은 누군가가 어떤 이유로 객관적으로 안좋은 코드를 남길 수 있습니다. 하지만 우리가 협업하는 이유가 협업을 통한 개발물의 완성인 만큼, 우리는 일련의 과정에서 비난이 섞인 비판을 바탕으로 의견을 관철하는 것 보다는 생산성에 집중할 필요가 있습니다.
오픈소스 개발에 기여하는 과정에서는 생산적이고 건강한 의사소통의 필요성이 더욱 올라갑니다. 여러 사람이 참여하여 더 멋진 개발물을 만들 수 있다는 오픈소스의 철학을 바탕으로 지속가능한 개발을 위해서는 모두가 행복한 의사소통이 필수불가결합니다.
저는 성격이 상당히 단단한(?) 편입니다. 의견이 부딪혔을 때 상대방의 의견이 납득되지 않는다면 강하게 의견을 관철하는 자세를 많이 보이곤 했고, 친하게 지내던 직장 동료에게 ‘너는 왜 항상 마음대로만 하려고 하느냐’ 라는 솔직한 이야기를 들은 적도 있습니다. 이후에 여러 과정을 통해 갖추게 된 생산적으로 소통하기 위한 협업규칙에 대해서는 다음 포스트에 다뤄 보겠습니다.