대규모 기술 프로젝트를 만드는 나만의 방식
새 프로젝트를 처음부터 만들든, 큰 기능을 구현하든, 대규모 리팩터링을 시작하든, 기술 프로젝트를 끝까지 완수하면서 동기를 유지하기는 어려울 수 있습니다. 저에게 아주 잘 맞는 방법은 실제 결과를 계속 확인하고, 그에 따라 작업 순서를 정하는 것입니다.
새 프로젝트를 시작할 때 느끼는 설렘은 누구나 경험해 봤을 겁니다. 처음 몇 주 동안은 컴퓨터 앞에 앉아 작업하고 싶어 안달이 납니다. 그러다 시간이 지나면서 조금씩 다른 일에 정신이 팔리거나 핑계를 만들어 내고, 프로젝트에 투자하는 시간이 줄어듭니다. 직업상 하는 일이라면 억지로 몸을 끌고 결승선까지 가겠지만, 하루하루가 고통스럽습니다. 재미로 하는 일이라면 몇 년 뒤에 돌아보며 ‘이렇게 될 수도 있었는데’ 하고 아쉬워하게 됩니다.
저는 큰 작업을 눈에 보이는 전진으로 이어지는 덩어리로 나누면, 프로젝트 내내 흥미를 유지하면서 작업을 끝까지 마치는 경향이 있다는 걸 배웠습니다. 사람마다 동기를 얻고 추진력을 발휘하는 방식은 다르므로 이 방법이 여러분에게도 맞으리라는 보장은 없습니다. 하지만 넓게 일반화해 보자면, 좋은 데모를 보고 설레지 않는 엔지니어는 아직 만나 보지 못했습니다. 그러니 목표는 언제나 스스로에게 좋은 데모를 보여 주는 것입니다.
이 글에서 말하는 내용이 새롭다고 주장하는 것은 아닙니다. 잘 알려진 여러 소프트웨어 엔지니어링이나 관리 방식과 분명히 겹치는 부분이 있습니다. 그저 제가 규모가 큰 기술 작업에 접근하는 방식과, 왜 그렇게 하는지를 공유하려는 것입니다.
이 글 전체에서 제 터미널 에뮬레이터 프로젝트를 예로 들겠습니다. 현실적이고 구체적인 경험을 공유할 수 있기 때문입니다. 사용할 만한 다른 프로젝트도 많았지만, 이 프로젝트는 제 직업과 관련이 없고 시작한 지 그리 오래되지 않아 기억이 생생하므로 이 사례를 골랐습니다.
프로젝트를 완성하지 못했다고 해서 누구를 비난하려는 것이 아니라는 점을 분명히 해 두고 싶습니다. 재미를 느끼고 성취감을 얻고 있다면(혹은 그냥 아무렇지도 않다면), 잘된 일이고 계속 그렇게 하시면 됩니다. 이 글은 프로젝트를 더 자주 끝내고 싶은 사람, 또는 그저 제가 프로젝트를 더 많이 완성하기 위해 어떤 노력을 하는지 알고 싶은 사람을 위한 글입니다.
출발선
처음에는 커다란 프로젝트가 하나 주어지고, 어떻게 시작할지 정해야 합니다. 저에게는 이 부분이 가장 어렵습니다. 적절한 시작점을 두고 몇 시간, 때로는 며칠씩 망설이기도 합니다.
터미널 에뮬레이터를 만들 때는 이 프로젝트를 정말 끝내려면 여러 큰 구성 요소가 필요하다는 걸 알고 있었습니다. 터미널 파싱, 셸 프로세스 실행 및 관리, 폰트 렌더링, 그리드 렌더링, 입력 처리(키보드/마우스) 등이었습니다. ‘완성’에 이르기까지는 비교적 규모가 큰 하위 프로젝트가 수백 개나 있습니다.
처음부터 Neovim을 실행할 수 있는 터미널을 띄우는 것을 목표로 했다면 큰일이었을 겁니다. 알 수 없는 미지의 요소들까지 고려하면, 이 목표는 그저 너무 커 보입니다. 그 과정에 GUI 렌더링, 프로세스 실행, 터미널 파싱과 상태 관리 같은 구성 요소가 많다는 사실을 직관적으로 알 수 있습니다. 이건 나쁜 목표입니다. 너무 크고, 한두 달쯤 지나면 아마 흥미를 잃을 겁니다.
대신 최대한 빨리 결과를 볼 수 있는 현실적인 프로젝트가 무엇인지 생각하려고 합니다. 이 기준을 적용하면 실행 가능한 하위 프로젝트의 수가 급격히 줄어듭니다. 예를 들면 다음과 같습니다.
- VT 파싱 - 터미널 이스케이프 시퀀스 파싱
- 빈 창 렌더링 - 창을 열고 빈 캔버스 그리기
- 자식 프로세스 실행 - bash, zsh, fish 같은 자식 셸을 실행하고 TTY를 설정한 뒤 그 출력(초기 셸 프롬프트 등)을 읽을 수 있게 만들기
이 단계에서 규모가 큰 하위 프로젝트를 전부 나열하려고 하지는 않습니다. 프로젝트가 어떤 대략적인 형태를 띠게 될지 감을 잡고, 다른 부분과 분리해서 만들 수 있으면서 실제 결과를 눈으로 확인할 수 있는 것 하나를 찾을 뿐입니다.
이 단계에서는 경험이 가장 큰 도움이 됩니다. 경험이 많은 엔지니어일수록 프로젝트가 어떤 대략적인 형태를 띠게 될지 더 효과적으로 그려 내는 경우가 많습니다. 다양한 하위 구성 요소를 더 정확히 파악하고, 서로 어떻게 맞물리는지도 알 수 있습니다. 경험이 부족하거나 익숙하지 않은 분야라면, 저는 최선의 추측을 해 보고 언젠가 작업물을 버리게 될 가능성이 더 높다고 생각합니다.
초기 결과
초기 작업은 눈에 잘 보이지 않는 경우가 많아서, 구체적인 결과를 확인하기가 어려워 보입니다. 예를 들어 터미널의 VT 파싱 작업을 하기로 했다면, 어떤 형태로든 UI를 연결하지 않고서는 제대로 작동하는 모습을 볼 수 없습니다. 다른 프로젝트에서 데이터베이스 스키마와 최소한의 API를 만들기로 한 경우에도, CLI나 GUI 클라이언트를 함께 작성하지 않으면 마찬가지로 그 작업의 결과를 볼 수 없습니다.
처음 선택한 하위 프로젝트가 UI라면 물론 빠르게 결과를 볼 수 있습니다. 하지만 여러 이유로 저는 프런트엔드부터 시작하는 경우가 드물고, 대개 백엔드부터 시작합니다. 어떤 경우든 결국 백엔드에 도달하게 되고 비슷한 문제를 만나게 됩니다.
이 단계를 넘어서는 데 가장 좋은 도구는 자동화 테스트입니다(이 단계에서는 보통 단위 테스트입니다). 자동화 테스트를 사용하면 실제로 코드를 실행해 제대로 작동하는지 확인할 수 있고, 코드 위생을 지키는 데도 도움이 됩니다.
따라서 처음 몇 가지 작업을 고를 때 따를 기준이 하나 더 생깁니다. 그래픽으로 확인할 수 없는 작업이라면, 큰 수고를 들이지 않고 테스트할 수 있는 것을 골라 결과를 확인할 수 있어야 합니다.
제 터미널에서는 먼저 VT 파싱부터 시작하기로 했습니다. 당시 터미널의 여러 부분 중 제가 잘 모르는 부분이었고, 아주 쉽게 테스트할 수 있을 것 같았기 때문입니다. 문자열로 된 예제 입력을 주고, 파싱된 동작이나 이벤트를 출력으로 기대하면 됐습니다.
“테스트 1개 통과”, “테스트 4개 통과”, “테스트 13개 통과”처럼 진행되는 모습을 보는 일은 저에게 정말 신나는 일입니다. 제가 작성한 코드를 실행했고 제대로 작동하고 있습니다. 게다가 더 큰 프로젝트의 핵심 하위 구성 요소를 향해 나아가고 있다는 것도 알 수 있습니다.
데모를 향한 질주
초기 하위 프로젝트의 목표는 완성된 하위 구성 요소를 만드는 것이 아닙니다. 충분히 괜찮은 하위 구성 요소를 만들어 데모로 가는 길의 다음 작업으로 넘어가는 것입니다. ✨
이런 절충은 기능에만 나타나는 것이 아닙니다. 알고리즘이나 설계 측면에서도 나타날 수 있습니다. 예를 들어 언젠가는 실제 데이터베이스나 정교한 자료 구조를 사용해야 하고 스트리밍 데이터를 지원해야 한다는 것을 알고 있을 수 있습니다. 하지만 초기 작업에서는 메모리 안의 데이터만 사용하고, 딕셔너리 같은 내장 자료 구조를 쓰며, 모든 입력과 출력을 미리 받도록 해도 됩니다.
이 절충은 중요하다고 생각하므로 다시 강조하겠습니다. 완벽함이 진보의 발목을 잡게 두지 마세요. 더 나아가, 언젠가 반드시 개선해야 한다는 것을 알고 있는 미래의 개선 작업 때문에 다음 일로 넘어가지 못하는 상황도 피해야 합니다. 목표는 데모에 도달하는 것입니다.
무엇을 작업하든 저는 앞 절에서 설명한 자동화 테스트의 피드백을 중간중간 활용하면서 일주일에 한두 개의 데모를 만들려고 합니다.
데모를 만들면 제품에 대한 귀중한 피드백도 얻을 수 있습니다. 완전히 작동하지 않더라도 무언가가 괜찮게 느껴지는지 빠르게 직감할 수 있습니다. 이것들은 ‘최소 기능 제품’이 아닙니다. 실제로 사용할 수 있는 수준이 아니기 때문입니다. 그래도 엔지니어가 스스로를 돌아보는 데 필요한 귀중한 정보를 얻기에는 충분합니다.
이 부분에서는 경험이 오히려 방해가 된다고 생각합니다. 완벽한 것을 만들려다 꼼짝 못 하는 시니어 엔지니어들을 본 적이 있습니다. 그러다 데모를 만들 때쯤에는 형편없다는 사실을 깨닫습니다. 구현이 형편없는 것은 아닙니다. 제품이나 기능 자체가 정말 형편없는 것입니다.
터미널에서 처음 선택한 작업이 VT 파싱이었다는 것을 기억해 주세요. 초기에는 자동화 테스트가 작동하는 모습만 볼 수 있었습니다. 첫 데모에 도달하기 위해 셸 스크립트를 하나 만들었습니다. 이 스크립트는 명령을 실행하고, 출력을 캡처한 다음, VT 파서에 전달하고, 파싱한 내용(또는 파싱하지 못한 내용)을 전부 출력했습니다. 시간이 지나면서 이 CLI를 첫 번째 ‘UI’로 계속 발전시켰습니다. 터미널 그리드를 ASCII로 렌더링했습니다.
man이나 ls 같은 간단한 프로그램부터 vim 같은 복잡한 프로그램까지 실행해 파서가 작동하는 모습(또는 망가지는 모습도 나름대로 똑같이 신났습니다)을 볼 수 있어서 큰 만족을 얻었습니다.
이 상황에서 제가 작성한 CLI는 장기적으로는 비교적 쓸모가 없었습니다(결국 얼마 지나지 않아 버렸습니다). 하지만 데모로 삼기 위해 들인 하루나 이틀의 작업은 제가 진전하고 있다는 중요한 감각을 주었고, 무언가가 작동하는 모습을 볼 수 있었다는 점이 계속 동기를 유지하는 데 도움이 됐습니다.
나 자신을 위해 만들기
이 절은 업무로 할당된 프로젝트보다 개인 프로젝트에 더 잘 적용됩니다. 다른 사람을 위한 소프트웨어를 출시하고 싶더라도, 필요한 것을 필요할 때만 만들고 가능한 한 빨리 직접 사용하세요.
저는 언제나 스스로 겪고 있는 문제를 해결할 때 더 큰 동기를 얻습니다1. 그리고 자신을 위해 설계한 제품이 정작 자신에게 제대로 작동하지 않는다면, 다른 사람에게도 잘 작동하지 않을 가능성이 매우 큽니다. 따라서 데모에서 실제로 쓸 수 있는 제품으로 나아가는 제 경로는, 제게 필요하다고 생각하는 기능만 만드는 가장 짧은 길을 찾는 것입니다.
제 터미널의 경우에는 먼저 셸 설정(fish)을 불러올 수 있게 하고, 그다음 Neovim을 실행해 사용할 수 있게 만드는 일이었습니다. 그래서 필요한 기능만을 향해 모든 작업을 일직선으로 진행했습니다. 해당 프로그램들이 사용하는 이스케이프 시퀀스만 지원하고, 제가 매일 사용하는 폰트만 렌더링하는 식이었습니다. 처음에는 스크롤, 마우스 선택, 검색, 탭/분할 등을 생략했습니다.
그런 다음 제 터미널을 매일 주력으로 사용하기 시작했습니다. 이 단계에서는 대개 몇 번의 시행착오가 있습니다. 생략했거나 잊어버린 기능이 실제로 필요했다는 것을 깨닫게 됩니다. 처음 터미널을 사용했을 때는 방향키가 아무 일도 하지 않았고, 작업 흐름을 망가뜨릴 만큼 미묘한 렌더링 버그도 있다는 것을 알게 됐습니다. 그래서 사용을 포기하게 되지만, 그 덕분에 다음에 작업할 구체적인 과제가 생겼습니다.
게다가 직접 작성한 코드로 만든 소프트웨어를 사용하면 늘 큰 자부심을 느끼고, 그 자부심은 대개 작업을 계속할 동기를 유지하는 데 도움이 됩니다.
정리
큰 문제를 작은 문제로 나눕니다. 중요한 점은 각각의 작은 문제에 대해 작업 결과를 확인할 수 있는 분명한 방법이 있어야 한다는 것입니다.
큰 문제의 데모로 이어지는 부분을 진행할 수 있을 만큼만 작은 문제를 해결한 다음, 다음 작은 문제로 넘어갑니다.
소프트웨어를 실행 가능한 데모로 만들기 시작할 수 있을 만큼만 작은 문제를 해결한 다음, 더 많은 기능을 계속 반복해서 추가합니다. 가능한 한 자주 데모를 만드세요.
가능하다면 자신이 만든 소프트웨어를 직접 사용할 수 있게 해 주는 기능을 우선합니다(개인 프로젝트, 실제로 자신이 겪는 문제를 해결하는 업무 프로젝트 등). 그런 다음에도 자신의 문제부터 계속 해결합니다.
앞으로 개선해야 할 필요가 생길 때마다 각 구성 요소로 돌아가 반복해서 개선하고, 필요에 따라 이 과정을 되풀이합니다.
맺음말
대략 이게 전부입니다. 개인 프로젝트, 팀 프로젝트, 업무 프로젝트, 학교 프로젝트 등에서 이 일반적인 패턴을 따라왔고, 이것이 제가 계속 동기를 유지하는 방법입니다2.
제가 많은 것을 언급하지 않았다는 점에 유의해 주세요. 출시 이야기는 하지 않았습니다. 출시가 동기를 부여한다고 느끼는 사람이 많다는 것을 알고 있습니다. 하지만 프로젝트를 출시해야만 성공하는 것은 아니라고 생각합니다. 그리고 저는 출시를 장기적인 동기로 삼기에는 너무 큰 사건이라고 느낍니다. 도구 이야기도 하지 않았습니다(Git 워크플로, CI 등). 여러 직장에서 이 과정을 사용했고, 각 조직에 이미 정착된 프로세스에 맞춰 적용했습니다. 그 밖에도 많습니다.
이런 점이 이 방식이 얼마나 개인적인 과정인지 보여 준다고 생각합니다. 누구나 건강한 방식으로 동기를 북돋울 수 있는 자기만의 과정을 찾아야 한다고 생각합니다. 저는 결과를 보는 일이 제게 매우 강한 동기를 준다는 사실을 깨달았고, 그에 맞춰 작업 방식을 만들었으며, 지금까지는 꽤 잘 작동했습니다.
각주
글을 무작위로 읽기