복잡한 프로젝트에 기여하기
원문은 Mitchell Hashimoto님이 에 게재했습니다. 이 블로그 구독하기
자주 오픈소스를 유지보수하고 기여하는 사람으로서 저는 종종 이런 질문을 받습니다. 도대체 어디서부터 시작해야 하죠? 의미 있는 변화를 만들겠다는 목표로 새로운 프로젝트에 어떻게 접근하나요? 복잡한 프로젝트의 내부를 도대체 어떻게 이해할 수 있죠?
이런 질문은 오픈소스든 프로프라이어터리든, 취미든 업무든 모든 소프트웨어 프로젝트에 적용됩니다. 제가 취하는 접근법은 어떤 경우든 동일합니다. 다만 업무로서 하는 프로젝트와의 결정적인 차이는, 다른 엔지니어들에게 직접 도움을 청할 수 있고 그들은 기꺼이 — 아니, 의무적으로라도 — 도와준다는 점인 반면, 오픈소스 프로젝트에서는 대부분 혼자 헤쳐나가야 한다는 점입니다.
저는 복잡한 프로젝트에 접근하는 반복적인 패턴을 정립했고 이 글에서 그 내용을 정리합니다. 이 패턴이 모든 사람에게 통하리라 기대하지는 않지만, 다른 분들이 복잡한 프로젝트를 배우고 기여하는 데 도전할 자신감을 얻는 데 도움이 되길 바랍니다.
저는 복잡한 프로젝트라는 용어를 구현 내용을 자명하게 이해할 수 없는 모든 소프트웨어 프로젝트를 설명하는 데 사용합니다. 이 정의는 주관적이며, 어떤 사람은 복잡하다고 보는 프로젝트를 다른 사람은 그렇지 않다고 볼 수도 있고 그 반대도 마찬가지입니다.
1단계: 사용자가 되기
어떤 프로젝트의 내부를 이해하기 위한 첫 번째 단계는 그 프로젝트의 사용자가 되는 것입니다. 전문가 수준의 사용자가 될 필요는 없지만, 이 단계를 마쳤다고 보는 저만의 기준은 그 프로젝트를 이용해 실제로 무언가를 만들어 보는 것이며, 그것이 작거나 단순하더라도 상관없습니다. 예를 들어, Zig 프로그래밍 언어에 기여하기 전에 저는 몇 개 의 실제 라이브러리를 만들었습니다.
사용자로서 프로젝트가 할 수 있는 일을 폭넓게 이해하게 됩니다. 레퍼런스 문서를 읽는 것과 실제로 프로젝트를 사용해 보는 것 사이에는 큰 차이가 있으며, 토이 프로젝트를 만들어 보는 것은 이론적 이해와 실제적 이해 사이의 간극을 메우는 중요한 단계입니다.
또한 프로젝트의 관용구를 익히기 시작하는데, 이는 프로젝트의 문화적 저변을 이루고 왜 프로젝트가 그런 방식으로 동작하고 그런 기능들을 갖게 되었는지를 이해하도록 돕습니다. 이는 프로젝트에 참여하는 다른 사람들에 대한 공감을 형성하는 데 도움이 되고, 어떤 변경이 프로젝트에 어울리고 어울리지 않는지를 판단하는 기준이 되기 때문에 중요합니다.
이 시점에서 커뮤니티에 참여하는 것을 적극 추천합니다. IRC나 Discord에 참여하고, 로컬 밋업에 참석하고, 발표 영상을 보는 등 여러 활동을 해보세요. 한동안은 말하기보다 듣는 데 더 많은 시간을 쓰세요. 여기서의 목표는 공감을 얻고 프로젝트가 어떻게 동작하는지 배우는 것입니다. 저는 단순히 다른 사람들이 배우는 모습을 지켜보는 것만으로도 얼마나 많은 것을 배우게 되는지에 늘 놀랍니다.
2단계: 프로젝트 빌드하기
프로젝트를 빌드하는 방법을 익혀 동작하는 바이너리(또는 그에 상응하는 결과물)를 얻으세요. 빌드 시스템이나 의존성 등을 이해하려고 애쓸 필요는 없습니다. 그저 가이드나 웹사이트 등, 소스 코드에서 내 시스템에서 실행 가능한 바이너리로 안정적이고 반복적으로 변환하기 위해 필요한 것은 무엇이든 그대로 따라 하세요.
프로젝트를 빌드하는 방법을 익히기 전에 코드를 읽지 마세요. 프로젝트를 빌드하는 법을 배우기도 전에 소스 코드를 이해하려다 발목 잡히는 경우를 너무 자주 봅니다. 저에게 학습 과정의 일부는 실험하고 부숴보는 것이며, 빌드할 수 없으면 소프트웨어 프로젝트를 실험하고 부숴보기가 어렵습니다.
완벽하게 기능을 갖춘 빌드에 신경 쓰지 마세요. 복잡한 프로젝트는 올바른 의존성, 올바른 시스템, 올바른 설정 등을 갖춰야만 사용할 수 있는 기능들을 가진 경우가 많습니다. 그런 경우라면 그런 것들에 신경 쓰지 마세요. 목표는 내 시스템에서 충분히 잘 동작하는 바이너리를 얻는 것입니다. 이후 단계들을 거치면서 경험을 쌓고 자신감을 얻게 되면, 더 완전한 기능의 빌드를 추구하기 시작할 수 있습니다.
이 단계에서는 테스트 스위트를 실행하는 방법을 익히고 통과시키는 것도 추천합니다. 이렇게 해두면 이후 단계에서 실험하고 부숴보는 것이 더 쉬워집니다. 복잡한 프로젝트는 테스트 스위트도 복잡한 경우가 많으므로, 때로는 테스트 스위트 중 일부만 실행하는 것을 의미하기도 합니다 — 실험하기에 충분한 만큼만이라도.
3단계: 핵심 경로 내부 익히기
내부를 배우기 위해 저는 “trace down, learn up”이라고 부르는 접근법을 즐겨 씁니다.
Trace Down
저는 하나의 기능이나 사용 사례에서 시작해, 바깥에서 안쪽으로 그 기능이 따르는 코드 경로를 추적합니다. 이 과정에서 지나가는 파일, 라인, 함수에 대해 메모하지만, 아직 어떤 것이 어떻게 동작하는지 이해하려고 시도하지는 않습니다. 이것이 “trace down” 단계입니다.
예를 들어, Zig 컴파일러를 공부할 때 저는 Zig 소스 코드로부터 실행 파일을 빌드하는 zig build-exe 명령을 추적하는 것부터 시작했습니다. 이 추적 과정은 zig CLI의 소스, build-exe 서브커맨드, 그리고 lexer, parser 등을 호출하는 “Compilation” 서브시스템으로 저를 이끌었습니다. 저는 경로를 추적하는 데 필요한 것 이상으로 구현 세부사항을 읽지 않았습니다.
추적 메모를 통해 보통 어떤 것이 어떻게 동작하는지에 대한 “큰 그림”을 얻을 수 있습니다. 파일명, 함수 등을 기반으로 보통 프로젝트의 주요 서브시스템들을 구분하기 시작할 수 있습니다. 이는 나중에 학습 과정을 보다 다루기 쉬운 크기의 조각들로 나누는 데 도움이 됩니다.
모든 것을 배우려 하지 마세요. 사람들이 흔히 저지르는 실수는 프로젝트 전체를 한 줄 한 줄 읽으려다 수주 혹은 수개월 동안 헤매다가 결국 의욕을 잃는 것입니다. 집중력을 유지하고 기능 하나씩 배워 나가세요.
팁: 기능을 고를 때는 사용자로서 익숙한 것을 선택하세요. 또한 가능하다면 겉보기에 단순해 보이는 기능을 고르도록 하세요. 예를 들어, 컴파일러를 배우기 위해 제가 처음 추적해 본 Zig 프로그램은 두 수를 더하고 출력은 전혀 없는 프로그램이었습니다.
Learn Up
하나의 기능을 추적한 뒤에는 실제로 매핑해 둔 다양한 서브시스템들이 어떻게 동작하는지 배울 차례입니다. 추적 단계에서는 CLI나 API 호출 같은 가장 바깥 지점에서 시작하는 반면, 학습 단계에서는 가장 안쪽 지점에서 시작하는 경향이 있습니다.
가장 안쪽 지점에서 시작하는 이유는 그곳이 보통 가장 근본적이고 추상화가 가장 적기 때문입니다. 계층을 올라갈수록 추상화 수준이 높아지는 경향이 있어, 구성 요소들을 이해하지 못하면 배우기가 더 어려워집니다.
특정 서브시스템을 배우기 시작할 때는 재귀적으로 “trace down, learn up”을 적용합니다. 공개되어 외부로 노출된 API 표면부터 살펴본 뒤, 각 API 호출이 어떻게 동작하는지 배웁니다. 상위 계층이 이 서브시스템을 사용하는 방식이 바로 그것이므로, 이는 학습 방향에 대한 가이드를 제공할 뿐 아니라 스택을 따라 올라갈수록 더 명확하게 이해하도록 돕습니다.
실험하고 부숴보기
“trace down, learn up” 과정 동안 저는 무언가가 어떻게 동작하는지 배우기 위해 실험하고 부숴보는 것이 매우 도움이 된다고 생각합니다. 그렇기 때문에 내부를 읽으려 시도하기 전에 프로젝트를 빌드하는 법을 배우는 것이 중요합니다.
새로운 로그 문을 추가하거나, 아주 작은 단위의 새 기능을 구현하거나, 기존 기능을 변경하는 등 다양한 시도를 한 뒤 프로젝트를 다시 빌드해서 무슨 일이 일어나는지 확인하세요. 이는 무언가가 어떻게 동작하는지에 대한 자신의 이해를 진정으로 시험해 보는 좋은 방법이기도 합니다.
예를 들어, Zig 토크나이저를 배우는 동안 저는 새로운 토큰을 추가했고, 토큰화는 되지만 파서가 실패하는 것을 확인했습니다. 다음 시스템(파서)에 도달했을 때 저는 새로 만든 토큰이 뭔가 동작을 하도록 만들었습니다. 이런 식으로 계속해 나갑니다.
자료로 보완하기
이 단계 전반에 걸쳐, 세세한 코드 탐색을 책, 영상, 블로그 글 등 가용한 모든 자료로 보완하세요. 내부를 다루는 기존 문헌이 있다면 읽으세요!
하지만 이런 자료들만으로 전문가 수준에 도달하리라 기대해서는 안 됩니다. 1단계의 “사용자가 되기”와 마찬가지로, “메인테이너가 되기”를 시도할 때는 직접 손을 더럽히고 실제 소스 코드로 이것저것 해보는 것을 대신할 보완재는 없습니다. 이는 이론과 실제 적용의 차이에 대한 또 다른 예입니다.
팁: 내부를 배울 자료가 존재하지 않는다면 직접 써보세요! 저는 Zig에 대해 그런 최신 자료를 찾을 수 없어 Zig 컴파일러 내부 구조에 대해 직접 글을 썼습니다. 무언가에 대해 글을 쓰는 것은 학습을 강화하는 좋은 방법이며, 미래의 기여자들에게도 도움이 됩니다.
4단계: 최근 커밋 읽고 재구현하기
내부를 학습하는 마지막 단계로, 제가 공부한 서브시스템과 관련된 최근 커밋들을 읽고 그 변경이 왜 이루어졌는지 완전히 이해했는지 스스로 시험해 봅니다. 이는 학습에서 “교과서 뒤편의 연습 문제를 푸는” 단계에 해당합니다.
저는 프로젝트 전체의 커밋 히스토리를 보거나, 공부한 서브시스템과 관련된 특정 파일이나 폴더의 커밋 히스토리를 봅니다. 그런 다음 먼저 해답(커밋의 변경 내용)을 공부하거나 혹은 그 커밋이 고친 버그를 확인하고 직접 고쳐본 뒤 비슷한 해결책에 도달하는지 확인합니다.
“문제를 풀기” 위해 저는 공부 중인 커밋 바로 이전 커밋 시점으로 저장소를 체크아웃합니다. (버그 수정이라면) 그 커밋이 고치는 버그를 재현한 뒤, 직접 해결책을 구현해 봅니다. 마지막으로 제 작업물을 메인테이너나 기여자가 만든 커밋과 비교합니다.
제가 스스로에게 주는 유일한 힌트는 필요한 변경 규모(VCS diff의 +/- 라인 수)뿐입니다. 처음에는 변경 라인 수가 50~100줄을 넘는 커밋은 피하는 것을 추천합니다.
5단계: 작게 시작하는 변경 만들기
저는 작게 시작해 점진적으로 더 크고 큰 작업을 맡는 것을 좋아합니다. 이 단계에서는 프로젝트의 기술적인 구성 요소를 이해한 상태이며, 이제 사람과 관련된 구성 요소를 배울 차례입니다. 여기서의 목표는 작은 변경을 만들고 기여 및 리뷰 과정을 익히는 것입니다.
가장 어려운 부분은 보통 만들 만한 작은 변경을 찾는 것입니다. 여기에 은탄환은 없습니다. 저는 이슈들을 둘러보며 기여자가 시도하기 좋아 보이는 것을 찾습니다. 보통 몇 번 헛발질을 하거나 이슈를 완전히 포기하고 다른 것을 시도합니다. 결국 하나를 찾게 됩니다. 이슈를 찾거나 고치는 데 걸리는 시간이 답답할 정도로 길 수도 있지만, 이는 입장료와 같은 것입니다. 많은 프로젝트가 새로운 기여자를 돕기 위해 "contributor friendly" 라벨을 제공합니다.
요즘 대부분의 프로젝트는 기여 과정을 잘 문서화해 두므로, 변경을 구현한 뒤에는 그 과정을 정확히 따르세요. 앞선 단계에서 커뮤니티에 참여했다면, 누군가에게 말을 걸어 도움을 요청하거나 과정을 한 번 더 확인해 달라고 부탁하기 좋은 기회입니다.
예를 들어, 제가 Zig에 처음 기여한 것은 세 줄짜리 변경으로, 이틀 저녁에 걸쳐 대략 네다섯 시간이 걸렸습니다(이전 단계들에 든 시간은 제외하고). 이 버그가 오늘 발생한다면 저는 몇 분 만에 고칠 수 있겠지만, 그 정도의 숙련도에 도달하는 데는 시간이 걸립니다.
성공
이 시점에서 당신은 복잡한 프로젝트를 학습했고 성공적으로 기여했습니다!
복잡함을 두려워하지 마세요. 너무 많은 엔지니어들이 프로그래밍 언어, 브라우저, 데이터베이스 등 전형적으로 복잡하다고 여겨지는 프로젝트를 마법처럼 여기거나 더 뛰어난 존재들만이 할 수 있는 일이라고 생각한다고 봅니다. 저는 모든 프로젝트가 다른 사람들에 의해 시작되었다는 점을 기억하려고 합니다. 그들이 할 수 있었다면, 저도 할 수 있습니다. 당신도 마찬가지입니다.
제 과정을 공유함으로써 다른 분들이 복잡한 프로젝트를 더 가깝게 느끼게 되길 바랍니다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기