이런저런 사건사고가 많았기 때문일까, 2020년은 유독 느리게 흘러간 것 같다. 그럼에도 시간은 착실히 흘러 어느덧 2년이라는 연차가 쌓였다. 그동안 나는 신입 티를 완전히 벗었고, 좀 더 개발자다워진 것 같다.
2020년 때 주로 배운 건 코드를 잘 짜는 법은 아니었다. 내가 작성한 코드의 퀄리티가 작년 말에 비해 그다지 늘지는 않았다. 올해는 개발 및 설계 방법론이나 회사에서 사용하는 여러 라이브러리에 대한 공부를 많이 하지는 않았기 때문이다.
대신, 올해는 작년보다 훨씬 더 다양한 경험을 했고, 이를 통해 개발자가 해야 하는 다양한 역할을 더 잘 수행할 수 있게 된 것 같다.
개발자의 역할 체화
2년간 회사를 다닌 경험을 바탕으로, 나는 프로덕트 개발에 있어서 개발자의 역할을 크게 세 가지라고 생각한다.
- 기획 의도를 이해하고, 이를 달성하기 위해 기술적으로 가능한 옵션과 이에 대한 일정을 산출하여 제시한다.
- 기획과 일정에 맞게 올바른 소프트웨어를 개발한다.
- 앞으로의 사업 및 프로덕트의 방향성에 대해 소프트웨어를 유연하게 유지한다.
위 세 가지는 모두 “비즈니스 가치를 가장 크고 빠르게 시장에 전달한다”라는 목적을 달성하기 위함이다.
이를 머리로 아는 것과 실제로 잘 수행해내는 것 사이에는 큰 간극이 존재하는데, 이 간극은 바로 익숙함, 즉 경험의 부재에서 발생한다. 타인과의 커뮤니케이션에서 그 사람의 의도를 집어내고, 일정 산출의 정확도를 올리고, 여러가지 기술적인 대안을 떠올리고, 빠르게 코드를 작성하고, 사업의 방향성을 적절한 수준으로 고려하기 위해서는 이러한 일들을 충분히 반복 연습하여 숙달하는 것이 필요하다.
내게 2020년은 정확히 이 반복 연습을 하는 시간이었다. 다양한 업무를 맡으며 다양한 경험을 쌓았다. 바닥부터 새로 개발하는 새 프로덕트, 전략적으로 매우 중요하여 급박하게 작업해야 하는 신사업, 기획과 개발을 내가 같이 해야 하는 어드민 개발, 다른 회사의 API를 연동하는 작업, 모든 서버에 영향을 미치는 DevOps 작업, 새로 입사하신 분에 대한 멘토링. 이 과정에서 나는 1. 회사의 일은 어떤 방식으로 굴러가는지, 2. 그 안에서 내가 해야 하는 역할이 무엇인지를 보다 깊게 체화할 수 있었다.
무엇보다 중요한 포인트는 책임지고 주도하는 것이다. 프로젝트가 크든 작든 관계 없이 시니어가 없는, 즉 본인이 주도해서 일을 진행하는 경험은 주니어의 성장에 매우 중요하다. 시니어가 없어지면 지금까지 시니어가 처리해줘서 알지 못했고, 또 알 필요도 없었던 “코드 작성 외에 개발자가 해야 하는 다양한 작업”과 직면하게 된다. 기획 회의, 일정 산정, 일을 팀원과 분배할 수 있도록 적절한 규모로 쪼개는 작업, PM팀과의 기획적인 흥정과 협상, QA 및 배포 전략 수립, 출시 일정 조정, 출시와 모니터링, 회고에 이르는 프로젝트의 전 과정을 생생하게 겪을 수 있다. 또한, 프로젝트에서 발생하는 다양한 이슈에 대한 모든 고민과 결정을 스스로 내려야 한다. 물론 그 결정에 대한 책임 - 서비스 장애를 낸다든가 내가 친 사고 때문에 동료가 야근을 하는 등 - 도 스스로 져야 한다.
이런 다양한 경험과 책임감이 주니어를 성장하게 만든다. 주니어의 관심은 이제 기획서대로 동작하고 기술적인 문제를 잘 회피하는 안전한 코드를 짜는 것을 넘어서서 비즈니스적 결정과 이에 대한 맥락, 그리고 서비스를 운영하고 유지보수하는 것으로 확장되기 시작한다. 기획의 의도와 사업의 방향성을 고려하여 어디를 얼마나 유연하게 개발해야 하고 어디를 얼마나 빠르고 대충 작업할 것인가를 고민하게 된다. 서비스를 유지보수하면서 좋은 코드를 짜야 하는 이유를 몸소 깨닫는다. 주니어는 점점 시니어의 케어가 필요 없어지게 되고, 더 큰 프로젝트에 투입될 수 있게 되며, 팀의 다른 신입을 케어해줄 수 있는 여유를 갖추게 된다.
백엔드 시스템 대한 이해
올해의 경험 중 가장 중요한 것을 하나 고르자면 EKS 버전 업그레이드 작업을 꼽을 것이다. 타다 서버가 떠 있는 EKS 클러스터의 버전이 12월부로 deprecate 되기 때문에, 우리 서버팀은 12월까지 새 버전으로 EKS의 버전을 올려야만 하는 상황이었다. 이 작업을 내가 맡아서 진행했고, 11월 중순부터 약 한 달 정도를 할애했다.
업그레이드 작업은 AWS 콘솔의 버전 업그레이드 버전을 이용하는 대신 카나리 배포 형식으로 진행했는데, 덕분에 타다 EKS 클러스터를 바닥부터 한땀한땀 띄워볼 수 있었다. 테라폼을 eks module을 사용하도록 변경하고, 직접 terraform apply와 destroy를 하고, deprecated 된 k8s api에 대응하고, 인프라 구성에 필요한 각종 컴포넌트 - 빌드 시스템, 메트릭 수집 & 모니터링 시스템, 서비스 메시, 보안 정책 등 - 를 배포했다. 무중단으로 노드를 업데이트하기도 했다.
이 작업을 하면서 내가 느꼈던 것은, 지금까지 내가 타다라는 ‘시스템’에 대해 알고 있었던 건 빙산의 일각에 불과하다는 점이었다. 내가 작성한 비즈니스 코드가 어떤 과정을 통해서 테스트되고, 이미지로 말리고, 쿠버네티스 클러스터에 배포되고, 트래픽에 대응하여 auto-scaling 되고, 어떻게 메트릭이 수집되어 grafana의 그래프에 표시되고, 어떻게 외부의 보안 공격으로부터 보호 받고 있는지를 파악할 수 있었던 훌륭한 경험이었다. 이 경험을 통해 나는 백엔드 시스템이 갖춰야 할 기본적인 요소들에 대한 큰 그림을 그릴 수 있었고, 이 밑그림은 두고두고 써먹게 될 것이다.
또한, 인프라 작업을 하면서 지식적인 측면 외에 배웠던 중요한 교훈들도 있다.
- 자동화의 중요성 - security group 설정 하나만 실수해도 전체 서비스가 다운될 수 있는 인프라 작업을 하면서 자동화의 중요성을 느꼈다. 사람은 언젠가 반드시 실수하기 때문에 사람의 손을 최대한 덜 태우도록 자동화를 하는 것이 매우 중요하다.
- 기술 뉴스 트래킹의 중요성 - 이번에 인프라 작업을 하면서 docker pull rate limit이 도입된 것 때문에 크게 골치를 앓았다. 이런 새로운 기술 뉴스에 관심을 기울이고 있었다면 훨씬 더 원활하게 EKS 버전 업그레이드 작업을 마칠 수 있었을 것이다.
- stateles한 서버의 중요성 - 서버의 stateless함은 운영적인 측면에서 매우 중요하다. 아무 걱정 없이 scale out/in을 할 수 있고 두 클러스터에 동시에 서버를 띄울 수 있다는 성질은 업그레이드 작업을 상당히 편하게 만들어줬다.
좋은 코드에 대한 생각 - 시기적절한 코드
올해 4월 즈음에 나는 ‘좋은 코드는 미래의 변화에 대해 유연한 코드이다’라는 취지의 글을 쓴 적이 있었다. 비즈니스의 맥락에 관심을 기울이고, 서비스가 어떤 방향으로 발전해 나갈지를 적절히 예상하여 그 방향에 대해 유연한 소프트웨어, 즉 비즈니스의 방향성과 align 된 소프트웨어를 작성해야 한다는 내용이었다.
이번 년도의 하반기를 보내면서 나는 ‘개발자를 쉽게 대체 가능하게 만드는 코드’ 역시 중요하다는 것을 배웠다. 계기는 다른 개발 문화를 겪어본 사람들이 팀에 합류한 것이었다.
우리 팀은 신입을 제외하고는 타다 초창기 때부터 쭉 있었던 사람들로 구성되어 있었다. 덕분에 팀 사람들은 타다 서버 코드 베이스와 팀이 일하는 방식에 완전히 익숙해져 있었다. 중간중간에 신입 몇 분이 들어오셨지만 다른 코드, 다른 문화를 본 적이 없었기 때문에 그냥 우리가 일하는 방식에 적응해버렸다.
그러던 와중, 이번 가을에 처음으로 경력직 분들이 이직해 오셨다. 외부에서 다른 경험을 쌓아온 그분들이 타다 서버 코드 베이스에 대해 왜 이렇게 구현했는지 의문을 품고, 왜 이런 프로세스로 일을 하고 있는지 의견을 제시하는데, 거기에 대해 내가 할 수 있는 말이라고는 “그냥 예전부터 그랬어요”밖에 없었다. 불편함에 익숙해져서 불편함을 잊어버렸고, 인지하지 못하니 개선을 위한 고민을 하지 않았다. 아주 부끄러운 경험이었다.
다방면으로 뼈를 맞았지만, 가장 크게 느꼈던 타다 서버 코드 베이스의 문제는 “아는 사람만 건들 수 있다”는 것이었다. 타다의 코드 베이스는 개인의 기억과 능력에 너무 크게 의존하고 있었다. 요구사항의 변화에 따라 리팩토링을 하지 않아 현재 서비스의 기획과는 잘 맞지 않는 구조의 코드가 그대로 남아 있다. 모듈화가 제대로 되어 있지 않아 영수증에 새로운 항목 하나를 추가하려면 파일 십수개를 고쳐야 한다. 한 클래스의 책임이 점점 커져서 이곳저곳에서 마구잡이로 사용되고 있다. 기존 팀 멤버는 기억에 의존해서 금방금방 코드 베이스를 변경할 수 있지만, 새로운 사람이 적응하기에는 너무나도 어려운 코드였다. 또한, 기존 팀 멤버 역시 수정을 빠르게 할 수는 있더라도, 실수 한 번으로 큰 버그가 날 위험에는 여전히 노출되어 있었다.
덕분에 나는 새로운 사람이 쉽게 이해하고 적응할 수 있는 코드를 작성할 필요성을 깨달았다. 중복을 최대한 줄이고, 특정 기능의 스펙이 변경되면 하면 코드의 한 부분만 변경하면 되도록 한다. 복잡한 상세 로직은 최대한 숨기고, 로직 자체는 최대한 직관적으로 짠다. 책임과 역할이 분명히 드러나도록 변수명/클래스명을 쓴다. 코드 베이스 내에서 코딩 컨벤션을 맞추고, 자주 나타나는 구현 패턴에 대해서는 best practice를 남겨둔다. 조금 느리더라도 이해하기 쉽고 고치기 안전한 코드를 작성해야 하는 시기이다.
한편, 나는 여전히 지금의 코드 베이스를 만들어낸 타다 베이직 시절의 개발 방법론은 적절했다고 생각한다. 속도를 중시하여 필요한 것만 가볍게 개발하는 서버팀의 방식은 서비스의 폭발적인 성장과 서버팀원의 충원이 급하지 않은 상황에 적합했다. 지금 타다 코드 베이스가 문제가 되는 이유는 운영하는 서비스가 많아졌기 때문인데, 1. 회사 조직이 점점 목적조직화됨에 따라 각 서비스마다 독립적으로 기능을 개발하고 배포하고 싶은 니즈가 생겼고, 2. 새로운 개발자를 빠르게 확충할 필요가 생겼다.
이전에 읽었던 이규원 님의 글이 생각난다. 무조건 좋은 코드는 없다. 특정 상황에 더 적절한 코드가 있을 뿐이다.
2021년에 하고 싶은 것
올해 읽었던 글 중 ‘슈퍼개발자는 10배의 생산성을 가진 사람이 아니라 타인의 생산성을 10배 늘려주는 사람이다’라는 이야기가 참 인상깊었다. 그래서 2021년은 다른 팀원의 생산성을 늘려줄 수 있는 방법에 대해 고민하고 배우는 한 해가 됐으면 좋겠다. 마침 내년에 서비스 하나를 담당하여 리딩(?)하는 역할을 맡게 됐으니, 배운 것을 실제로 적용해보면 더욱 좋을 것이다.
이러한 맥락에서, 내년에는 소프트웨어 개발의 이론적인 측면에 대해 공부하는 것을 최우선 목표로 잡으려고 한다. 지금까지는 개인 공부 시간의 대부분을 실무에 필수적인 지식을 익히는 데에 사용했다. 언어의 문법, 프레임워크 및 라이브러리의 사용 방법, DB 트랜잭션과 락, Spring Reactor, k8s, helm, prometheus 등, 모르면 아예 코드를 작성할 수 없거나 버그가 날 수 있는 그런 지식 말이다. 이제는 한 단계 눈을 높여서 좀 더 추상적인 지식을 공부할 필요를 느낀다. SRE, GoF의 Design Pattern, DDD, branching workflow 등 여러 개발 방법론과 개념들을 익히는 것은 유지보수의 생산성을 늘리는 코드를 작성하는 데에 큰 도움이 될 것이다.
또한, 팀의 생산성을 위해 고민하는 시간을 확보하는 것도 중요할 것이다. 올해 1달간 훈련소에서 지루한 시간을 보내면서, 그리고 새로운 팀원이 들어와서 느낀 것은 정신없이 달리는 동안은 주변이 잘 보이지 않는다는 것이다. 멈춰서서 여유를 가지고 달려온 길을 뒤돌아봐야만 비로소 달리는 방향이 잘못되었다는 것을 인지할 수 있다. 목표가 무엇인지, 일하는 방식이 정말 효율적인지, 익숙해져서 느끼지 못하는 불편함이 있지는 않은지, 종종 의도적으로 멈춰서서 점검할 필요가 있다.
마지막으로, 내년에는 좀 더 많은 글을 쓸 수 있으면 좋겠다. 올해 새롭게 익힌 중요한 지식과 경험이 많은데, 이를 글로 남기지 못한 게 참 아쉬웠다. 작년에 만들어둔 ‘All about Building an Application’ 문서도 AWS 부분만 조금 채우고 거의 작성하지 못했다. 배우고 느낀 것을 글로 남겨서 다른 사람들이 나와 같은 문제를 겪지 않도록 도와주는 것도 타인의 생산성을 늘리는 일일 것이다.