필자는 이번 프로젝트에 있어 버전 관리 시스템으로 Git을 사용했고 원격 저장소로써 github를 사용했다. github는 단순히 프로젝트 코드를 저장하는 것 뿐만 아니라, 다양한 기능을 제공했다. 나는 github의 모든 기능을 사용할 순 없었지만, 어떤 프로세스를 통해 협업과 프로젝트를 진행하는지에 대한 기본적 이해는 할 수 있었다. 개인 프로젝트였기에 완전한 협업을 할 순 없었지만, 큰 흐름으로 보면 gitFlow라는 개발 프로세스를 채택했다. gitFlow는 브랜치 전략이다. 브랜치란 '작업 흐름의 단위'이다.
브랜치는 분기되는 것이다. 현업으로 가게 되면 여러 명이서 프로젝트를 진행한다. 다른 팀원이 A라는 기능에 대한 개발을 진행하고 있다고 가정해보자. 프로젝트의 무결성을 유지하기 위해, 그 팀원이 A 기능 구현을 마칠 때까지 기다린다면 어떻게 될까? 팀원은 여러 명이지만 실제 인력은 한 명 수준 밖에 되지 않을 것이다. 여러 명이서 다른 작업(기능 구현, 버그 수정, UI 수정 등등)을 동시에 진행할 수 있어야 한다.
오늘은 분기(동시에 여러 작업 진행)와 통합에 대한 이야기가 될 것이고, Git 버전 관리 시스템에서 이에 해당되는 것이 '브랜치'라는 것이다. 그리고 이 브랜치를 어떻게 운용할지에 대한 전략이 필요하다. 해당 전략을 어떻게 두는지가 회사마다 다른 것이다. 현재 많은 회사들이 이러한 브랜치 전략으로써 git flow를 채택하여 사용하고 있다. git flow란 어떤 것인지, 어떤 브랜치들이 생성되고, 각 브랜치에서 어떤 작업 흐름을 진행하는지에 대해 살펴보자.
git을 사용해봤다면 master라는 브랜치를 본 적이 있을 것이다. 우리가 프로젝트를 생성하고 git system을 초기화하면 브랜치 하나가 생성된다. 해당 브랜치의 이름은 master인데, git flow의 브랜치는 이 브랜치와 동일한 녀석이다. 하지만 git flow에선 master 브랜치가 갖는 역할의 무게감이 더욱 중대해진다. master 브랜치는 git Flow 시스템에서 가장 메인이 되는 브랜치이며 '실제 출시가 이뤄지는 릴리즈 버전'의 프로젝트 상태가 태그를 통해 저장돼있다. master 브랜치를 보자. 플레이 스토어에 출시되는 1.0.0 버전과, 1.0.1 버전의 프로젝트 상태가 master 브랜치에 저장돼있다.
하지만 단순히 저장된다는 개념이 아니다. master 브랜치는 릴리즈 버전들이 저장되는 브랜치기 때문에, 실질적인 개발은 master 브랜치에서 일절 진행되지 않는다. 그렇다면 1.0.0 버전과 1.0.1 버전의 사이 개발은 어디서 진행됐을까? 바로 develop 브랜치에서 진행된다. 그리고 release 브랜치를 함께 소개하겠다.
위에서 master 브랜치는 실제 출시되는 버전에 대한 브랜치라고 했다. 그렇다면 실제 개발 흐름은 어떤 브랜치에서 진행될까? 바로 develop 브랜치에서 진행된다. Tag 1.0.0을 develop 브랜치로 가져와 다양한 기능 구현, UI 작업, 버그 수정, 갖가지 작업을 진행하다 보면 1.0.1 버전의 출시가 될 것이다. 이 때 바로 master 브랜치로 병합할까? 그렇지 않다.
이 때 release 브랜치가 사용된다. 처음 git flow의 release 브랜치를 알게 됐을 때 'develop 브랜치에서 개발하면 되는 것이 아닌가? release 브랜치에서 작업할 것이 그리 많지 않을 것 같은데..' 라는 생각이 들었다. 하지만 아니다. 출시는 신중해야 한다. 릴리즈 버전은 말 그대로 유저가 사용하는 버전이기에 해당 버전을 출시하기 전엔 신중과 신중을 가해야 한다.
최종적인 테스트 진행, 테스트를 통해 갑작스럽게 발견되는 버그의 수정, 최종 점검을 하며 보이는 UI 수정 등, release라는 작업이 지닌 중요성이 git flow 프로세스에 그대로 반영된 것이라고 생각된다. 따라서 1.0.1 버전을 출시할 준비가 거의 되면, release 1.0.1 브랜치를 생성하여, 이 곳에서 출시 전 마지막 작업들을 수행한다. 즉 develop 브랜치에서 실질적인 개발을 진행하고, 릴리즈 준비가 거의 진행되면 해당 버전의 release 브랜치에서 최종적 검토 및 수정 단계를 진행한다는 것이다. 이러한 개발 프로세스를 사진으로 보면 다음과 같다.
위의 develop 브랜치에서 1.0.1 버전을 준비하는 작업 흐름을 볼 수 있었다. develop 브랜치는 다음 버전의 여러 기능들을 구현하는 과정들을 포함할 것이다. 브랜치 사용은 어떤 장점을 가질지에 대해 생각해보면, 각 작업 흐름을 명확히 분기하고 구현하는 과정을 논리적으로 기록하는 것이라고 생각한다. 각 기능 구현 또한 그렇다. A라는 기능을 구현하는 과정, B라는 기능을 구현하는 과정 또한 명확히 분기하여 개발을 진행하면, git 시스템이 선사하는 명확한 작업 흐름 분리의 즐거움을 맛 볼 수 있게 될 것이다. 이는 각 기능 구현 과정에도 적용할 수 있다. 바로 feature 브랜치를 사용해서 말이다. 따라서 git Flow 시스템에서는 A 기능을 구현하기 전에, 'feature A'라는 브랜치를 생성하고, 해당 브랜치에서 A 기능 구현 개발을 진행한다. 이후 B, C라는 다능 기능의 구현 과정 또한 같을 것이다. 그리고 나중에 A라는 기능 사용에서 문제가 발생했고, 해당 과정을 복기해야 할 타이밍이 왔다고 가정하다. feature A 브랜치로 들어가서 A라는 기능 구현 과정에만 집중하여 복기할 수 있을 것이다. 위의 기능 구현 과정을 사진으로 보자.
1.0.1 버전 준비하던 중, 이미 출시했던 1.0.0 버전에서 문제가 발생했다. 미뤄둘 일이 아니라 긴급하게 수정하여 재배포를 해야 하는 상황이다. 출시 버전에 대한 브랜치인, master 브랜치에서 긴급 수정 과정을 거쳐 다시 배포하면될까? 아니다. git flow는 철저하게 역할에 따른 브랜치 분기를 목적으로 하는 개발 프로세스이다. 따라서 '긴급 수정' 또한 특정 브랜치에서 작업을 수행한다. 바로 hotfix 브랜치이다. hotfix 브랜치는 앞서 언급했 듯이 '긴급 수정에 대한 작업 흐름 브랜치'이다. 출시한 버전 수준에서 문제가 발생해서 긴급하게 수정-업데이트를 해야 할 상황이 발생하면, hotfix 브랜치를 생성한다. 그리고 hotfix 브랜치에서 버그를 수정한 뒤, 다시 master 브랜치로 병합하면 된다.
hotfix 브랜치까지 알게 됐다면, git flow의 주요한 브랜치에 대해서 모두 알게 된 것이다. 다음에 진행할 프로젝트에 있다면, git flow를 적용해보자. 처음에는 어떤 작업을 하기 전 브랜치를 만드는 것이 귀찮을 수도 있겠지만. 개발이 어느정도 진행된 뒤에 뒤를 돌아보면 해당 프로젝트의 작업 흐름들이 브랜치별로 잘 정리정돈되어 있는 것을 확인할 수 있을 것이다.
브랜치라는 작업 흐름의 단위를 어떻게 분기하고, 어떻게 운용할지에 대한 방법론을 알아봤다. git flow 말이다. 필자는 이번 '반성 식탁' 프로젝트 진행에 있어 개발 프로세스 하나를 더 적용했다. 바로 이슈 기반 버전 관리 워크플로우이다. 이슈에 대해 알아볼 필요가 있다. 이슈는 github에서 해당 프로젝트에서 발생하는 다양한 이슈를 기록하는 게시판 역할을 한다고 보면 된다. 이 워크플로우 프로세스는 결과적으로 이슈 게시판에 특정 작업 흐름을 이슈로써 기록해두는 것이라고 생각하면 된다.
git flow와 issue workflow 를 함께 사용하여
좋아요 기능을 구현하는 작업 흐름 프로세스를 살펴보자.
특정 작업에 대한 이슈 생성
기능 구현에 관한 작업 흐름이기 때문에 feature 브랜치를 생성해 작업할 것이다. 따라서 feature 브랜치에서 작업하겠다고 라벨을 붙였고, 이후 작업이 끝나고 develop 브랜치로 병합할 예정이기 때문에, merge request 라벨을 동시에 붙여놨다.
해당 이슈를 열고 번호를 확인한다.
해당 이슈를 작성하고 Submit new issue를 클릭하면 새로운 이슈가 생성된다. 해당 이슈를 클릭하면 이슈 제목의 오른편에 '#숫자' 형식으로 숫자가 표시되는데, 이것이 해당 이슈의 이슈 번호이다. 이 이슈 번호를 사용함으로써 issue 기반의 작업 프로세스 진행이 가능하다.
로컬 저장소에 새로운 브랜치를 생성한다. 형식은 “이슈 번호-설명”
필자는 git flow에 따라 새로운 feature 브랜치를 생성했다. 브랜치 이름은 'feature/firebase_like'이다. 아, 참고로 이와 같이 브랜치를 생성하면, feature 폴더 내에 firebase_like라는 브랜치가 생성된다. feature 브랜치 폴더 내에 각 브랜치들을 생성함으로써, 기능 브랜치들을 한 눈에 파악할 수 있게 된다.
이슈에 적어둔 목표를 해결한다. 이 때, 오직 이슈에 적힌 내용만 작업한다"
별다른 설명은 없다. 새로운 작업 브랜치에서 해당 기능 구현을 진행하면 된다.
작업을 테스트하여 제대로 완료됐는지 확인한다.
수정 사항을 커밋하고 푸시한다.
github이 커밋을 추적할 수 있도록 커밋 메시지 안에 이슈 번호를 적어야 한다.
이렇게 커밋-푸시를 진행하면 해당 이슈에 커밋 내역들이 표시된다.
즉, 해당 이슈에 대한 작업 흐름들이 자동으로 기록되는 것이다.
개발에 있어 히스토리가 기록된다는 것은 중요하다.
작업이 잘 완료됐다면 작업 브랜치를 develop 브랜치에 병합(merge)한다.
이 때 pull(merge) request를 하면 자연스럽게 병합 과정도 기록될 수 있다.
이슈에 모든 내용이 잘 기록됐는지 확인하고 이슈를 닫는다.
이 과정에서 수동으로 이슈를 닫아줄 수도 있지만, 경우에 따라 커밋 메세지에서 이슈 처리를 할 수 있다.
커밋 메세지의 앞에서 '키워드+이슈번호'를 작성하면 된다.
따라서 커밋 메세지를 달면서 동시에 해당 이슈를 클로즈 하고 싶다면,
커밋 메세지 앞에 close #55 를 작성해주면 된다.
이슈 상태를 어떻게 변경할지에 따라 여러 키워드들이 존재하는데 다음과 같은 키워드들이 존재한다.
이슈가 쌓이면
정말 빈약하지만, 프로젝트에 해당 프로세스를 적용해봤다.
이슈가 쌓이면 기록이 된다. 그리고 프로젝트에 있어 잘 정돈된 기록은 중요하다.
물론 수박 겉핥기 수준이지만, 필자는 위의 개발 프로세스들을 적용해서 프로젝트를 진행했다. 실제 협업하는 것처럼 issue에서 대화를 나누거나, PR을 통해 다른 팀원에게 코드 리뷰를 받거나, 여러 이유로 팀원 간 협업을 해보진 못했다. 하지만
정도를 생각할 수 있게 되었다.