시작하며
안녕하세요. 카카오페이 서비스그로스플랫폼팀 서버 개발자 Noah라고 합니다. 😀 서비스그로스플랫폼팀은 전사 서비스의 성장을 견인하는 Growth Platform(A/B 테스트 플랫폼, IFTTT1 플랫폼, 타겟팅 플랫폼, 콘텐츠 플랫폼)을 개발하고 있습니다. 이 글에서는 근무 형태, 조직 규모 등 다양한 업무 환경 변화 속에서도 좋은 품질의 코드를 유지하기 위한 저희 팀의 노력과 경험을 소개하고자 합니다.
기존 코드 리뷰 방법
코드 리뷰란 무엇일까요? 다른 사람이 작성한 코드를 보고, 문제가 될만한 상황을 찾아서 피드백을 주고 코드 품질을 높이고, 내 코드 -> 우리 코드
가 되어 가는 과정이라고 생각합니다. 코드 리뷰 방법은 여러 가지가 있을 수 있고 팀 내 개발자가 소수라면 어떤 방법도 가능합니다.
제가 속한 서비스그로스플랫폼팀은 초기에 개발자가 몇 안돼서 대면으로 코드 리뷰를 진행했었는데요. 팀 내 개발자가 늘어나고, 지난 몇 년간 지속된 코로나19로 재택근무가 보편화되면서 대면 리뷰는 더 이상 효율적인 방법이 아니라고 생각했습니다. 마침 전사적으로 Pull Request를 사용해서 코드 리뷰를 하고 있었고, 같은 방법으로 코드 리뷰해 보기로 했습니다.
그라운드 룰 도입 배경
근무 형태, 개발자 수, 프로젝트 규모 등 업무 환경의 변화로 기존보다 더 나은 코드 리뷰 방법이 필요하다고 생각했습니다. 환경이 변함에 따라 그동안 해왔던 리뷰는 생산성이 좋지 않게 되었고, 코드 리뷰가 생산성에 병목이 된다면 팀 문화로 자리 잡는데 어려움을 줄 수 있다고 생각했기 때문입니다.
특히 다음과 같은 2가지 이유로 팀 내 코드 리뷰 그라운드 룰이 필요함을 인지했습니다.
근무 형태의 변화
카카오페이도 코로나19를 겪으면서 재택근무를 시작했는데요. 짧을 것 같았던 재택근무는 근무 환경을 포함해 많은 것을 바꿨습니다. 코드 리뷰도 그중 하나였는데요. 궁금한 게 있으면 옆 자리 동료에게 쉽게 물어볼 수 있는 환경에서 메시지를 보내는 비동기 커뮤니케이션을 잘 할 필요가 있었고, 글로 이해가 잘 안 된다면 별도로 미팅 일정을 잡아야 하는 번거로움이 생기게 되었습니다.
그리고 ‘Pull Request로 진행하는 리뷰 방식이 우리에게 맞지 않는 걸까?’ 하는 고민도 있었는데요. 이 방식은 이미 대규모 오픈소스에서도 잘 사용하고 있는 방식으로, 방식의 문제가 아닌 팀 내 정확한 코드 리뷰 그라운드 룰이 없는 게 문제라는 생각이 들었습니다.
서로 다른 관점의 리뷰
팀에 개발자가 늘어남에 따라 코드 리뷰도 여러 관점으로 진행되었는데요. 다양한 의견을 존중할 필요는 있지만, 코드 리뷰에서 달성해야 할 객관적인 목표가 필요하다고 생각했습니다.
- 크루 A: 이건 코드 포맷팅이 제대로 안되어 있네요. 이거 바꾸는 게 어떨까요?
- 크루 B: 이건 너무 부수효과가 많은 코드네요. 함수형 스타일로 바꿔보면 좋을 것 같아요.
- 크루 C: Kotlin에서 Elvis Operator 제공하는데, 조건문 대신 써보면 좋을 것 같아요.
- 크루 D: 와… 코드 왜 이렇게 많아요?
맹인과 코끼리 예시처럼, 같은 사물이 앞에 있어도 겪어 온 환경에 따라 다르게 생각할 수 있습니다. 맹인들이 코끼리를 더듬으면서 나름의 코끼리를 상상하는데, 코끼리는 전체를 보면 비슷한 모양이지만, 각자의 관점으로 코끼리를 유추한다면 상상하는 모양은 서로 다를 겁니다.
이 사례를 코드 리뷰로 옮겨도 비슷할 것 같아요. 리뷰를 진행하는 개발자들은 그동안 겪어 온 환경이 다르기 때문에 저마다 집중하는 코드가 다를 수 있다고 생각합니다.
코드 리뷰에서 달성할 목표를 정하고 객관적인 관점으로 리뷰할 수 있는 기준을 마련한다면, 눈을 감고 코끼리를 만지더라도 같은 코끼리를 상상할 수 있겠지요.
그라운드 룰 논의 과정
위의 사례를 봤을 때 어떤 생각이 들었나요? 코드 리뷰 방법에 재정비가 필요하다는 생각이 들지 않나요? 카카오페이에는 밀려있는 기술 부채를 해소하고 정리하는 하우스키핑 데이2라는 개발 문화가 있는데요. 이 시간을 활용해 팀 내 개발자들이 모여서 코드 리뷰 그라운드 룰을 논의하는 시간을 가졌습니다.
그라운드 룰의 목표는 코드 리뷰를 잘하는 것입니다. 그러려면 객관적인 목표와 기준이 필요하고, 달성해야 할 목표 정의도 필요합니다. 이를 위해, 우선 Google Engineering Practices Documentation을 참고해서 우리 상황에 맞는 목표와 룰을 정해 보기로 했습니다.
목표와 객관적인 항목 정하기
- 설계(Design)
- 기능(Functionality)
- 복잡성(Complexity)
- 테스트(Tests)
- 명명 규칙(Naming)
- 주석(Comments)
- 언어별 스타일(Style)
- 문서화(Documentation)
코드 리뷰는 왜 하고, 무엇을 위해 할까요? 이 정의를 맞추는 것부터 필요하겠죠. 좋은 품질의 코드, 개인의 코드가 아닌 팀원의 코드가 되는 과정을 달성하려면 모두가 공감할 수 있는 목표 정의가 필요했습니다. Google Engineering Practices Documentation은 코드 리뷰 시 달성해야 할 목표를 8가지로 설명하고 있습니다. 객관적인 항목이 있다면, 방법은 달라도 일관성 있게 리뷰할 수 있을 테니까요.
그동안 우리가 했던 리뷰를 떠올리며, 위 항목들을 얼마나 잘 지켰는지 확인해 볼까요?
설계(Design)
전체적인 코드 설계가 잘못되었다면, 오랜 시간 개발한 것을 뒤엎고 다시 시작해야 할 수도 있습니다. 그래서 각 요소가 잘 설계되었는지, 트래픽 증가에도 영향이 없는지, 코드 확장성이 떨어지지는 않는지 등의 의견을 주고받으며 보다 나은 코드를 작성하려고 노력했습니다.
기능(Functionality)
코드 리뷰 시 쉽게 확인할 수 있는 항목이죠. 동작을 검증하는 것으로 리뷰를 할 수 있지만, 좋은 리뷰는 미래에 발생할 버그까지 잡아 주어야 합니다.
작성자는 놓칠 수 있지만, 리뷰를 진행하는 사람은 찾을 수 있는 예외 상황을 최대한 찾으려고 했습니다.
복잡성(Complexity)
코드가 변경 내용에 비해 복잡하다면, 뭔가 잘못되어 가고 있다고 생각해야 합니다. 코드 복잡도가 높으면 리뷰어가 이해하기 어려울 뿐 아니라, 시간이 지난 뒤 코드 작성자도 이해하는 데 많은 시간이 필요할 수 있습니다. 이러한 복잡도를 코드 리뷰 시점에 관리하고, 변경에 유연한 코드를 유지할 수 있는 관점으로 리뷰합니다. 모든 코드는 시간이 지남에 따라 노후화된다고 생각합니다. 예전에는 좋은 코드였지만, 기능이 변경되면서 지금은 비효율적인 코드일 수 있습니다. 코드 리뷰를 통해서 유지보수가 편리하고 복잡도가 낮은 코드로 유지하는 데 도움을 주었습니다.
Admin(FE) 개발 중 설정값 추가로 신규 개발 요청이 들어왔습니다. 기존 화면에 설정값 필드만 추가하면 되는 간단한 작업이었지만, 설정값이 추가될 것을 고려하지 않고 초기에 개발한 나머지 작업에 오랜 시간이 걸리고 코드 복잡도도 높아졌습니다. 개발 후 리뷰 과정에서 다음에도 설정값 추가 요청이 들어온다면 같은 과정을 겪을 수 있겠다는 이야기가 나왔습니다. 그래서 여러 개선 방안을 정리하였고, 해당 화면의 코드를 수정해 보다 단순한 형태로 개발할 수 있었습니다.
테스트(Tests)
코드의 신뢰성은 풍부한 테스트 코드로 뒷받침되기도 합니다. 팀 내에 여러 프로젝트가 있는데 테스트 코드에 신경을 많이 써서 커버리지가 높은 프로젝트도 있지만, 그렇지 않은 프로젝트도 있습니다.
Line coverage 92.04%를 유지하는 프로젝트가 있는 반면, 테스트 코드가 거의 없는 프로젝트도 있습니다. 풍부한 테스트 코드는 평소에 힘을 느끼기 어렵지만, 버그가 생기거나 대규모 리팩터링 시 큰 도움이 된다고 생각합니다. 하지만 모든 코드 리뷰에 테스트 코드가 있지 않았고, 뒤늦게 테스트 코드만 작성하는 날을 잡아서 밀려있던 테스트 코드를 작성하기도 했습니다.
새로운 그라운드 룰
새로운 그라운드 룰은 팀 내 개발자들과 여러 차례 논의를 통해 그동안 진행해오던 코드 리뷰를 보완하는 방식으로 접근했습니다. 모든 룰을 지켜야 좋은 리뷰는 아니지만, 효율적인 코드 리뷰를 위해 필요하다고 생각한 룰을 소개해 보겠습니다.
PR Template
어떤 코드는 변경 사항이 적어 이해가 쉬울 수 있지만 어떤 코드는 변경 사항도 많고 코드만 보고 이해하기 어려운 경우가 있습니다. 이럴 때 코드 외에 구체적인 설명이 있다면 리뷰에 도움을 줍니다. 그동안 리뷰 진행 시 이런 이야기가 종종 나오곤 했는데요.
이거 테스트 어떻게 해요? 이건 무슨 의미인가요?
이런 질문을 받으면서, 작성자의 명확한 의도, 코드 변경 사항, 리뷰 받고 싶은 항목을 명시해 효율적으로 리뷰할 수 있는 PR Template3의 필요성에 공감했습니다. PR Template에 누구에게 물어보지 않더라도 알 수 있는 구체적인 내용을 작성하게 하니, 시간이 지나서 보더라도 리뷰의 기억을 떠올릴 수 있었고 히스토리 관리에도 도움이 되었습니다.
코드 커버리지
코드 리뷰는 코드의 품질을 유지하기 위한 최소한의 수단입니다. 그래서 어느 정도 기계적으로 코드 품질을 유지할 필요가 있다고 생각했습니다. 코드 커버리지는 테스트 코드가 얼마나 많은 코드를 실행하는지를 나타내는 지표 중 하나로 코드 커버리지가 높을수록 많은 코드가 테스트로 검증되었음을 의미하는데요. 부끄럽게도 모든 프로젝트가 높은 코드 커버리지를 유지하지는 못했습니다.
코드 커버리지는 신뢰성과 관계가 있고, 안전하게 코드를 수정하기 위해선 어느 정도의 코드 커버리지가 뒷받침되어야 합니다. 팀에서 목표한 코드 커버리지가 있지만, 코드 리뷰를 등한시했던 것처럼 쉽게 타협의 대상이 되곤 했습니다. 코드 커버리지가 높다는 것은 풍부한 테스트 코드가 있다는 것을 의미하는데, 테스트 코드는 신뢰성 있는 코드를 유지해주는 장점도 있지만 코드 리뷰에 도움을 주기도 합니다.
작성한 코드가 복잡하다면? 코드의 흐름을 어떻게 따라가야 할지 보기 어렵다면?
이럴 때 코드 리뷰 시간이 오래 걸리곤 하는데, 테스트 코드가 있다면 작성자의 의도를 쉽게 파악할 수 있고, 리뷰에 더 집중할 수 있습니다. PR Template에 코드 커버리지 항목을 추가해서 일정 수준 코드 커버리지를 권장하면, 리뷰어들이 테스트 코드로 동작을 예상하고 잠재적인 버그를 찾아내는데 도움이 될 것이라고 기대했습니다.
Code Formatter 및 Lint
로직 변경은 많지 않지만, 줄 맞춤 등 불필요한 변경이 리뷰에 같이 올라오곤 했습니다. PR에 포함된 커밋 크기가 작다면 어려움이 없지만, 변경 내용이 많고 로직이 복잡하다면 상황은 다를 수 있겠죠.
그래서 각 언어들이 제공하는 코딩 컨벤션과 lint를 적용해서 코드 저장소에 일관된 코드를 유지할 수 있도록 했습니다. 이 과정에서 어려움도 있었는데요. 단순한 코드 포맷팅이더라도 이미 오랜 시간 유지된 코드를 한 번에 변경한다는 게 부담되어, 조심스럽게 접근했습니다.
그라운드 룰 도입 후 코드 리뷰
팀 내 코드 리뷰 그라운드 룰을 정하기 전과 비교해서 더 나은 코드 리뷰를 할 수 있었을까요? 위에서 언급한 8가지 목표를 모두 달성했을까요? 새로운 그라운드 룰이 팀 코드 리뷰에 어떤 영향을 주었는지 알아보도록 하겠습니다.
설계(Design)
그라운드 룰 논의 전에도 리뷰에서 꼼꼼히 검증하려고 했던 항목이지만, PR Template 항목을 통해 PR에 대한 자세한 설명을 적어줌으로써, 리뷰 시 도움을 받을 수 있었습니다. 개발 의도가 포함된 PR 덕분에 리뷰어는 리뷰 진행 전에 작성자가 어떤 의도를 가지고 설계를 했고, 코드를 작성했는지 쉽게 파악할 수 있게 되었습니다.
대화를 할 때도 배경지식 차이로 같은 이야기를 다르게 이해하곤 합니다. 그보다 추상화 단계가 높은 코드는 얼마나 이런 오해가 더 많이 생길까요? 코드만으론 파악하기 어려운 내용을 자세히 작성해 리뷰에서 집중적으로 봐야 할 부분이 명확해지니, 구성원 모두 이전보다 효율적으로 리뷰하는 모습을 찾아볼 수 있었습니다.
기능(Functionality)
테스트 코드는 일정에 따라 우선순위가 밀리곤 했습니다. 새로운 그라운드 룰에서 코드 커버리지 유지를 권장하면서 이전보다 테스트 코드를 작성해서 PR을 요청하는 빈도가 늘어났습니다. 위에서 이야기한 것처럼 테스트 코드의 목적은 신뢰성 있는 코드를 유지해주는 순기능도 있지만, 리뷰어가 코드 작성자의 의도를 쉽고 명확하게 파악하는데 도움을 주곤 합니다.
테스트 코드 없이 전체 코드를 읽고 기능을 파악할 때보다, 테스트 코드 기반으로 작성한 코드를 읽고 기능을 파악할 때 많은 시간이 단축되었습니다. 그리고 단축한 시간을 위험한 코드를 잡아주는데 활용하고 있습니다.
이제 부족한 건 없나요?
No Silver Bullet 늑대인간을 한 번에 죽일 수 있는 은총알을 찾기보단, 부족한 점을 하나씩 개선해 나가는 게 좋은 방법이라고 생각합니다. 위에서 코드 리뷰를 잘하기 위한 그라운드 룰을 정했지만, 이제 정말 모든 게 좋아졌을까요?
작은 커밋과 리뷰 요청
커밋의 크기가 항상 작으면 좋겠지만, 그렇지 않은 경우도 있으니 큰 변경 범위에서 꼭 봐야 할 코드, 우려되는 코드를 잘 리뷰해야겠다고 생각했습니다. 팀 내 개발자가 늘었다는 건 그만큼 팀에서 다루는 코드가 많아졌음을 의미합니다. 각자 맡은 범위가 다른데 개발해야 할 기능까지 크다면 리뷰에 올라오는 커밋의 크기가 꽤 커지곤 했습니다. 이 경우 리뷰하는데 시간도 오래 걸릴뿐더러 집중력도 떨어지게 됩니다.
그라운드 룰의 목표는 객관적이고 효율적인 코드 리뷰입니다. 테스트 코드가 많고, 설명이 잘 되어 있더라도 리뷰로 요청한 커밋이 크다면 짧은 시간에 리뷰를 마치기 어렵습니다. 아래는 변경된 코드 라인과 리뷰 소요 시간을 나타내는 그래프입니다. 코드 변경 수가 많을수록 리뷰 시간이 오래 걸리는 것을 알 수 있습니다.
저희도 변경 사항이 많은 리뷰가 종종 올라오는데, 그런 리뷰는 몇 시간에 끝나는 리뷰가 아니라 1차, 2차, 3차로 나누어서 며칠에 걸쳐 진행되기도 합니다. 리뷰를 잘하려면 적절한 크기로 커밋이 관리되어야 하는데, 큰 기능 개발이 시작되면 덩어리가 큰 리뷰가 종종 올라오곤 합니다.
여러 기능이 하나의 리뷰에 올라오거나, 수백 라인의 코드가 변경되는 경우 의미 있는 코드 리뷰가 쉽지 않았습니다. 아직은 변화를 작게 유지해서 응집도 있는 코드 리뷰를 할 수 있는 작은 커밋 유지하기가 부족하다고 생각합니다. 다음 하우스키핑 데이에는 작은 커밋 유지하기를 코드 리뷰 그라운드 룰에 녹일 수 있는 방법에 대해 팀원들과 논의해볼까 합니다
마치며
한 달 전에 짠 코드가 그때는 최선일 수 있지만 오늘은 아닐 수 있는 것처럼, 개발 환경과 팀 문화도 시간이 지나면 노후화된다고 생각합니다. 서비스그로스플랫폼팀은 적은 인원으로 시작해서 여러 개의 플랫폼을 개발하는 큰 조직으로 성장해왔는데요. 이 과정에서 코드 리뷰 효율성과 코드 품질을 높이기 위한 노력들을 소개했습니다.
코드 품질을 유지하기 위해 코드 리뷰는 꼭 필요한 절차이자 문화라고 생각합니다. 다만 일정에 쫓기고 여러 이유들로 코드 리뷰는 우선순위에서 밀릴 때가 많습니다. 우선순위에서도 밀리는데 리뷰 시간도 오래 걸리고 업무 효율성도 좋지 않다면, 코드 리뷰를 지속적으로 하는 건 더 어렵겠죠.
저희 팀은 코드 리뷰 그라운드 룰을 정의해서 조금이나마 효율성을 높이는 계기가 되었고, 시간이 지난 지금 코드 리뷰는 팀 내 문화로 자리 잡았습니다. 저희처럼 조직 규모, 근무 형태의 변화로 기존 코드 리뷰 방법에 변화를 고민하고 있는 조직에게 저희 이야기가 도움이 되었으면 합니다.