깃허브(GitHub)에서의 오픈 소스 프로젝트 기여를 위한 초보자 가이드

이번 글에서는 깃허브(Github)에서 오픈 소스 프로젝트에 기여하는 방법을 안내한다. 작지만 개인적으로 뜻깊었던 첫 기여 경험담을 예시로 삼았다. 거창한 작업이 아니라도 괜찮다. 사소한 용기가 많은 사람들에게 유익한 결과를 안겨줄 수 있다.

깃허브(GitHub)에서의 오픈 소스 프로젝트 기여를 위한 초보자 가이드

이 블로그에는 본문 검색 용도로 SearchinGhostEasy라는 플러그인이 붙어있다. 블로깅 플랫폼으로 쓰는 Ghost가 검색 기능을 지원하지 않아서 쓰게 된 것인데, 여러 모로 만족스러웠지만 미묘하게 불편한 점이 하나 있었다. iOS 환경에서 검색 입력폼을 띄웠을 때 아래와 같이 텍스트 입력 영역이 어긋나는 것이었다.

(좌) 수정 전의 화면, (우) 수정 후의 화면
(좌) 수정 전의 화면, (우) 수정 후의 화면

다행히 이 문제는 해당 검색폼의 <input>에 누락된 padding 값을 붙여주는 것으로 간단히 해결 되었고, 그렇게 혼자 만족한 채로 시간이 지났다. 그러다 두어 달 쯤 후의 어느날 문득, 이 수정사항이 원작자의 배포용 코드에 반영된다면 나와 비슷한 불편을 겪어왔을 다른 분들에게도 좋은 일이 되지 않을까 생각하게 되었다. 그래서 "오픈 소스 기여"를 소재로 여러 문서들을 찾아 보았고, 무수한 삽질을 거친 끝에 지난 3월에 처음으로 오픈 소스 프로젝트에 Pull Request를 보냈다. 누군가에게는 지극히 단순한 작업일 수 있겠지만, 깃허브를 더듬더듬 배워가던 사람으로서의 내겐 무척 흥분되는 경험이었다.

이번 글에서는 위의 경험담을 바탕으로 깃허브(Github)에서 오픈 소스 프로젝트에 기여하는 방법을 소개한다. 이 글은 Git의 개념을 어느 정도 알고 있고, 깃허브를 사용해본 적이 있으며, 오픈 소스에 기여하고 싶은 마음이 있지만 어떻게 시작해야 좋을지 막막하신 분들을 대상으로 한다.

사전 지식 숙지하기

만약 Git의 개념이 낯설거나 깃허브를 써본 적이 없다면 이들을 먼저 숙지해두어야 한다. 이에 대해서는 아래 문서와 온라인 강의를 둘러보도록 하자.

기여할 프로젝트를 선택하기

Git과 깃허브에 익숙해졌다면, 이제 내가 기여하고 싶은 프로젝트를 찾을 차례다.

세상엔 정말 무수히 많은 오픈 소스 프로젝트들이 존재한다. 자칫하면 모래사장에서 바늘 찾는 일이 될 수 있으니, 나만의 선택 기준을 미리 생각해두어야 한다. 초심자로서 내가 결정한 선택 기준은 다음과 같다.

  1. 적어도 한 번 이상은 사용해 본 적이 있어야 한다.
    무언가를 개선하려면 우선 그 대상을 잘 알아야 한다. 어느 정도의 사용 경험은 있어야 해당 프로젝트가 가진 이슈를 올바르게 파악할 수 있다. 경험 많은 기여자라면 저장소에 올라온 Issue 항목들 가운데 원하는 항목을 골라 작업할 수 있겠지만, 그렇지 않다면 일단 손에 익은 프로젝트부터 살펴보는 것이 낫다고 생각한다.
  2. 사용자의 기여 활동이 활발한 프로젝트여야 한다.
    해당 프로젝트가 사용자의 참여를 독려하는 분위기인지, 모르는 사람의 이슈 제기나 기여 요청에도 적극적으로 응답하는지 알아야 한다. 해당 저장소의 IssuePull Requests 목록을 살펴보면 대강의 분위기를 짐작할 수 있다.
  3. 마지막으로 커밋(Commit) 된 시기가 1년 이내인 프로젝트여야 한다.
    만약 최근 1년 이내에 코드 커밋 기록이 없는 프로젝트라면 더 이상 유지보수가 이루어지지 않는 상황일 수 있다. 이런 프로젝트엔 애써서 기여 작업을 하더라도 반영되지 못할 가능성이 클 것이다.
  4. 처음부터 너무 거대한 프로젝트를 고르지 않는다.
    대중적으로 유명한 프로젝트들은 대개 수십 명에 달하는 컨트리뷰터들의 손을 거쳐 많은 부분이 고도화 된 상태이기에 오픈된 이슈들의 맥락을 파악하거나 향후 업데이트 방향성을 알아보는 과정에 많은 노력이 들어간다. 기여 활동에 익숙해지기 전까지는 작은 프로젝트부터 선택해서 시작하는 것이 좋다고 생각한다.

초심자 입장에서 가장 신속하면서도 간편하게 오픈 소스 기여를 시작할 수 있는 방법은 바로 내가 현재 사용 중인 프로젝트를 고르는 것이다. 해당 프로젝트를 직접 사용해 봤다면 개선이 필요한 부분도 빠르게 찾아낼 수 있고, 내가 개선한 작업의 효과를 내 환경에서 즉시 체감할 수 있기에 개인적인 효용감이 무척 크다.

수행할 작업을 선택하기

기여할 프로젝트를 찾았다면, 다음으로는 어떤 기여 작업을 할 것인지 정해야 한다.

복잡한 코드 작업 만이 의미 있는 기여인 것은 아니다. 내 능력으로 할 수 있는 일을 찾아 가능한 만큼만 해내면 된다. 어떤 것이든 개선의 대상이 될 수 있다. 이미 많은 분들께서 강조하신 것처럼, 매뉴얼 문서나 코드 주석에 적힌 사소한 오탈자, 잘못되거나 존재하지 않는 번역을 고치는 것 또한 훌륭한 기여 작업이다. 때로는 위에서 소개한 일화처럼 간단히 수정 가능한 버그를 발견하여 고치는 작업도 가능할 것이다.

만약 해당 프로젝트의 어떤 부분부터 들여다 보아야 할지 막막하다면, 해당 프로젝트 저장소의 Issue 목록을 살펴보자. 프로젝트에 대한 질의응답이나 버그 제보, 개선 방안에 대한 논의가 이곳에서 이루어진다. 여기서 내가 기여할 수 있을 만한 항목을 찾아 댓글로 의견을 남기거나, 반대로 내가 개선 방안을 먼저 제시하는 것도 가능하다.

본격적으로 도전하기

기여하고 싶은 대상과 기여의 방향이 모두 정해졌으니, 이제 본격적으로 작업을 시작해보자. 저장소 포크(Fork)부터 시작하여 작업용 브랜치(Branch) 생성, 작업 내용의 커밋(Commit)과 푸쉬(Push)를 거쳐 원본 저장소(Repository)에 대한 PR(Pull Request) 전송에 이르는 과정을 단계 별로 살펴볼 것이다.

깃허브에서의 작업은 기본적으로 여러 사람들이 원격으로 비동기적인 협업을 이어간다는 전제로 이루어진다. 따라서 작업 내용이 간단하더라도 따라야 할 절차의 단계가 조금 많은 편이다. 단계의 숫자가 많을 뿐이지, 실제로 해보면 크게 어렵지는 않으니 차근차근 진행해보자.

1. 기여할 프로젝트 저장소를 포크(Fork)하기

가장 먼저 해야 할 일은, 기여하고자 하는 프로젝트의 원격 저장소(Repository)를 내 깃허브 계정으로 포크(Fork)하는 것이다. 여기서 포크(Fork)란, 다른 프로젝트의 저장소를 깃허브의 내 계정으로 복제해 오는 것을 의미한다.

PC에서 깃허브에 로그인 한 상태로, 내가 기여하고자 하는 프로젝트의 저장소 화면에서 상단 우측의 Fork 버튼을 눌러보자.

그러면 내 계정에 해당 프로젝트가 포크(Fork)되어 온 것을 확인할 수 있다. 이때 해당 저장소에는 forked from (원본 프로젝트명) 형태의 설명이 함께 따라붙는다. 이제 포크(Fork)된 저장소를 git clone (포크된 저장소 경로) 명령으로 내 로컬 환경에 복사하면 기초적인 작업 준비가 끝난 것이다.

왜 이런 작업이 필요할까? 깃허브로 관리되는 오픈 소스 프로젝트의 저장소는 대개 권한을 가진 일부 사용자에게만 직접 접근을 허용하는 편이다. 아무나 와서 이상한 코드로 프로젝트를 망가뜨린다면 곤란할테니까. 그래서 나의 작업 사항을 내 계정으로 포크(Fork)해 온 저장소에 우선 기록한 후, 내가 고친 내용이 원본 저장소에 반영되도록 요청(PR; Pull Request)하는 방식으로 일을 진행하는 것이다.

클론(Clone)과 포크(Fork)의 차이

클론(Clone)포크(Fork)는 둘 다 다른 원격 저장소의 내용을 복제하여 가져온다는 점에서 비슷하지만, 아래와 같은 차이가 있다.

  • 클론(Clone)원격 저장소를 내 로컬 컴퓨터로 복사하여 새 저장소를 만드는 것이다. 이렇게 만들어진 저장소에서의 변경 내용은 push 명령으로 처음에 복사해 왔던 원격 저장소에 직접 보낼 수 있지만, 그 저장소에 대한 쓰기 권한이 없다면 불가능하다.
  • 포크(Fork)원격 저장소의 복사본을 내 깃허브 계정에 만드는 것이다. 이 복사본은 해당 원격 저장소와의 연결성을 갖게 된다. 원본 저장소에 변화가 생겼다면 이를 내 복사본 저장소에 fetch 또는 rebase 명령으로 적용시킬 수 있다. 반대로 내 복사본 저장소에 올려 둔 수정 사항이 해당 원격 저장소에 반영(pull)되도록 요청(pull request)을 보낼 수도 있게 된다.

2. 포크(Fork)해 온 내 저장소를 최신화하기

처음 포크(Fork)해온 시점에는 원본의 최신 코드가 그대로 반영되어 있겠지만, 기여 작업을 이어가다 보면 다른 누군가에 의해 변경된 코드가 원본 프로젝트에 새로 업데이트 되어 있을 수 있다. 이런 상황에서 갱신되지 않은 옛 버전 기반으로 내가 고친 코드를 Pull Request로 보내려 한다면... 아래와 같은 메시지를 만나게 될 것이다.

다른 프로젝트에서 PR을 보냈다가 발생한 Conflict 상황. 보는 순간 마음이 덜컹거리게 된다.
PR을 보내려다 Conflict 메시지를 보게 되면 마음이 덜컹거리게 된다.

포크(Fork)해온 저장소의 최신화는 그래서 중요하다. 시간이 걸리는 기여 작업이라면, 저장소 최신화에 반드시 신경쓰도록 하자.

앞서 1번 단계에서 포크(Fork)된 프로젝트 저장소를 git clone 명령으로 로컬 환경에 복사했었다. 해당 경로에서 다음과 같이 입력한다.

# 명령문 포맷 : git remote add (리모트명) (리모트할 경로 url)
git remote add upstream (원본 프로젝트 저장소 경로)

위 명령문은 upstream이라는 이름으로 원본 프로젝트 저장소의 URL을 가리키는 새로운 원격 연결을 추가시키는 것이다. 이를 실행한 뒤 git remote -v 명령으로 현재 내 로컬 환경과 연결된 원격 저장소 경로들을 살펴본 결과는 다음과 같다.

  • origin : 내가 프로젝트의 원본 저장소로부터 fork해온 내 깃허브 계정의 저장소이자, 내가 로컬 환경으로 clone해 온 대상을 의미한다. 내가 수정한 코드들이 올라갈 곳이다.
  • upstream : 내가 기여하고자 하는 프로젝트의 원본 저장소 경로다. 만약 원본 저장소의 코드가 새로 업데이트 되었다면, 이곳으로부터 최신 코드를 받아와서 내 저장소에 반영해야 한다.

이제 내 저장소를 최신화시켜 보자. 아래의 명령들을 차례로 실행시켜 준다.

# 'upstream'으로 지정된 저장소로부터 최신 코드를 받아온다.
git fetch upstream

# 원본 프로젝트의 최신 코드를 적용할 내 작업용 브랜치로 checkout한다.
git checkout master

# 앞서 fetch 명령으로 받아온 최신 코드와 내 작업용 브랜치의 코드를 병합한다.
# git merge (원본 원격 저장소명/내 브랜치명)
git merge upstream/master

# 내 깃허브 계정의 저장소도 최신화 시켜준다.
git push origin master

아래 스크린샷은 실제로 내가 저장소 최신화 작업을 실행하던 당시의 화면이다. 여기서는 원본 저장소를 fork해 오자마자 fetch를 수행했으므로 코드 변화 없이 Already up to date.란 메시지만 출력되었다. 따라서 내 깃허브 계정의 저장소도 최신화할 필요가 없으므로 git push 명령도 생략했다. 만약 원본 저장소가 새로 업데이트 된 경우라면, git merge를 수행하는 단계에서 어떤 파일이 어떻게 변화되었는지 확인할 수 있게 된다.

3. 내 저장소에 작업용 브랜치(Branch) 만들기

일부 문서의 오탈자 수정과 같은 작은 스케일의 작업이라면 원래 주어진 대로 main(또는 master) 브랜치에서 바로 작업해도 무방하다. 하지만 기능 개선이나 추가, 버그 수정 등의 작업이라면, 이것이 어떤 이슈를 해결하려는 작업인지 알아보기 쉽도록 별도의 브랜치로 분리하여 주는 것이 좋다.

내 경우에는 원본 프로젝트의 특정 템플릿(backpack)에 대한 CSS 수정 작업이었으므로, fix-backpack-css라는 이름의 브랜치를 생성하여 관리했다.

# 원하는 이름으로 새 브랜치를 생성한다.
git branch fix-backpack-css

# 생성한 브랜치로 전환한다.
git checkout fix-backpack-css

# (옵션) 위의 두 명령은 아래와 같이 하나로 합칠 수 있다.
git checkout -b fix-backpack-css

4. 내 작업 내용을 내 저장소에 올리기

이제 코드 작업을 시작하자. 위의 단계들을 거쳐 만든 내 저장소의 작업용 브랜치에 나의 작업 내용을 커밋(commit)하고 푸시(push)하도록 하자. 내 경우에는 작업 내용이 무척 단촐하여 단 하나의 커밋 만으로 마무리했다.

5. 원본 프로젝트에 Pull Request 보내기

이제 내 작업용 저장소에 웹으로 접속해보면, 아래 스크린샷과 같이 Compare & pull request 버튼이 활성화된 것을 볼 수 있다.

위의 버튼을 누르면, 내가 처음 fork 해왔던 원본 프로젝트 저장소에 PR(Pull request)open 할 수 있는 화면이 나타난다.

여기에서 아래 스크린샷과 같이 PR을 보내려는 원본 프로젝트 저장소와 브랜치명, 그리고 수정사항이 담긴 내 저장소와 브랜치명을 각각 확인하고 지정해준다.

이제 PR 게시물의 제목과 내용을 채워보자. 가급적이면 다음과 같은 원칙을 지켜서 작성하도록 하자.

  • 제목은 해결하려는 이슈가 무엇인지 누구나 파악할 수 있도록 간명하게 작성한다. 만약 관련된 이슈(Issue)가 있을 경우 해당 이슈의 게시물 번호(예: #00)를 함께 명시해주면 더 좋다.
  • 본문의 경우 대개의 프로젝트에서는 작성 포맷을 지정해 놓거나 따로 공지해 놓은 경우가 많다. 이를 반드시 따르도록 하자.
  • 만약 별도의 지정 포맷이 없다면, 아래 내용이 포함되는 선에서 자유롭게 작성하자.
    - 해결하려는 이슈의 간략한 내용
    - (버그일 경우) 버그의 재현 환경
    - 수정/개선 방향
    - 수정/개선 전후의 차이 (스크린샷 등)
  • 가급적 공손한 어투로, 무례하거나 공격적으로 느껴지지 않도록 작성하자.

6. 소통하고, 반영하기

PR을 올리고 나면, 이것이 과연 해당 프로젝트의 개선에 도움이 되는 내용인지, 혹시 부족하거나 잘못된 내용이 있진 않은지 코어 개발자 또는 메인테이너(Maintainer)의 검토 의견을 댓글로 받게 된다. 함께 댓글을 주고 받으며 내가 간과했던 부분을 파악할 수도 있고, 혹은 같은 수정 사항에 대한 더 나은 코드를 구상하여 이를 반영하게 될 수도 있다.

내 경우에는 불행히도 버그 재현 환경의 명시를 간과했다가 코어 개발자에게 약간의 혼란을 야기하고 말았다. 해당 이슈에 대한 Android 기기에서의 테스트 결과를 누락했던 것이다. 이처럼 디테일을 돌보지 않는 실수는 검토자의 불필요한 수고를 야기할 수 있으니 주의하자.

뼈아픈 실수의 현장. 이런 실수를 잘 수습하는 것도 커뮤니케이션의 과정이다.
코어 개발자의 댓글에서 드러난 뼈아픈 실수의 현장. 이런 실수를 잘 수습하는 것도 커뮤니케이션의 과정이다.

7. Pull Request가 Merge된 것을 확인하기

PR 내용이 최종적으로 잘 수용되어 원본 저장소에 merge 되었다면, 해당 PR 게시글의 하단에서 아래와 같은 반가운 메시지를 볼 수 있을 것이다.

환희의 순간.
환희의 순간.

이로써 내가 수정한 코드가 오픈 소스 프로젝트에 반영되었고, 해당 프로젝트의 기여자(Contributor) 목록에 이름을 올리게 되었다.

해당 PR이 공식적으로 Closed 처리 되었다면 안내 메시지에 따라 내 작업용 브랜치를 삭제할 수 있다. 더 이상 같은 프로젝트로 작업할 일이 없다면 fork해온 내 저장소 역시 삭제해도 좋다.

이것만은 꼭 지키기

오픈 소스에 기여하는 작업도 결국 사람과 사람 간의 일이다. 작업물의 품질 못지 않게 중요한 것이 작업에 임하는 마음가짐이라고 생각한다. 첫 기여 작업을 하면서 세운 나만의 원칙은 다음과 같다.

기대와 다른 반응에 서운해하지 말자

모든 기여가 항상 열렬한 환영으로 이어지는 것은 아니다. 내가 공들여 올린 PR이 거절된 상태로 Closed될 수도 있고, 때로는 PR 자체가 Open된 채로 누구에게도 검토받지 못한 채 잊혀질 수도 있다.

내 노력이 반영되지 않았거나 무시 당했다고 해서 서운해하지 말자. 대개 오픈 소스 프로젝트들은 이렇다 할 대가 없이 선의로 움직이는 사람들에 의해 운영된다. 우리가 일상에서 접하는 많은 서비스들이 이러한 사람들로부터 유무형의 빚을 지고 있다는 점을 유념하자. 이런 세계에 기여하고자 한다면, 해당 프로젝트를 응원하고 후원하는 사람으로서 겸허한 마음으로 접근하는 것이 바람직하다고 생각한다.

정해진 규칙을 따르자

대개의 오픈 소스 프로젝트는 기여 희망자를 위한 규칙을 정해두고 있다. 이러한 규칙은 문서로 직접 명시된 경우도 있고, 때로는 코어 개발자나 메인테이너들에 의해 암묵적으로 지켜지는 경우도 있다. 원활한 기여 작업을 위해서는 해당 규칙을 잘 숙지하고 참여하는 것이 좋다. 예를 들어 어떤 프로젝트에서는 반드시 Issue를 먼저 등록한 후에 메인테이너 확인을 거쳐 PROpen해야 할 수도 있다.

아울러 Issue 또는 PR 메시지를 작성할 때 따라야 할 규격을 세세히 정해놓은 경우도 존재한다. 여럿이 함께 협업하는 원격 환경에서 원활한 커뮤니케이션을 위해 지켜야 할 규칙으로 이해하고 받아들이는 것이 좋다.

PR 게시물 작성에는 언제나 신중하자

PR(Pull Request)은 결국 나의 작업을 프로젝트 원본 저장소에 반영해달라는 요청과 같다. 따라서 기여자에게는 해당 작업이 그 프로젝트에 매끄럽게 수용될 수 있도록 PR 내용을 충실히 작성해야 할 의무가 있다.

앞서 소개했던 것처럼, 나는 PR 게시물에서 버그 재현의 환경 조건을 제대로 명시하지 않았다가 예기치 못한 혼란을 만들었다. 코어 개발자의 너그러운 양해 덕분에 문제 없이 잘 넘어갔지만, 여러 명이 빠듯하게 작업 중인 프로젝트에서는 이런 실수가 큰 커뮤니케이션 비용으로 이어질 수 있다. PR 게시물은 언제나 신중한 자세로, 예의 바르게 작성해야 한다.

맺음말

#sge-input {
	...
	width: 100%;
	padding: 0 0.25em 0 0;
	...
}

위의 코드가 내가 처음으로 오픈 소스 프로젝트에 보낸 Pull Request의 내용이다. 단 두 줄의 CSS 코드가 전부다. 그러나 이걸 원본 프로젝트에 반영시키기 위해 거쳐야 했던 과정이 비경험자로서는 그리 간단치 않았다. 매 단계마다 내가 몰라도 너무 모른다는 막막함에 시달렸다. 똑같은 어려움을 반복하고 싶지 않아서 모든 과정을 상세히 기록했다. 그 기록이 이 글의 재료가 되었다. 가이드의 형식을 빌고 있는 이 글은, 사실 나의 부끄러운 첫 경험에 대한 회고이기도 하다.

선량한 마음만으로 이루어지는 일은 없다. 모든 일에는 방법과 규칙이 있다. 여럿이 함께 하여 만들어지는 오픈 소스의 세계에는 그 협업이 생산적인 방향으로 이어지도록 하기 위해 나름의 방법과 규칙이 정해져 있다. 이들을 잘 파악하고 따르면서 의미 있는 협업을 시도해 보는 일, 그리하여 같은 코드에 의존하는 다른 누군가의 불편을 해소해 주고, 그럼으로써 전체 프로젝트의 품질을 향상시키는 일에 도전하길 원하지만 어떻게 시작해야 할지 막막한 분들께 이 가이드가 새로운 시작점이 되어주면 좋겠다.