DDM(Datarize Dev Meetup)의 마지막 세션은 백엔드(Back-end) 챕터의 발표였어요. 이번 세션은 BE 챕터의 윤철님이 발표해주셨습니다. 데이터라이즈가 레거시 코드를 어떻게 받아들이고 해결해 나갔는지 전체적인 과정에 대해 이야기 해주셨어요.
먼저 데이터라이즈의 백엔드 엔지니어는 어떤 일을 하고 있는지 궁금해하실 것 같은데요. 우리가 개발하고 있는 어플리케이션을 CRM 마케팅이 진행되는 과정 속에 녹여 설명하겠습니다. 데이터라이즈 고객은 매출을 높이고자 하는 쇼핑몰입니다. 쇼핑몰에서는 고객이 방문을 하거나 구매를 하는 등 다양한 활동을 하게 되는데요. 저희는 그 활동의 결과로 남는 행동 로그나 메타 데이터를 수집합니다. 수집한 데이터를 분석한 후 유의미한 인사이트가 도출 되면 쇼핑몰에게도 제공합니다. 그리고 그 분석 결과를 활용해서 쇼핑몰을 방문하는 고객에게 캠페인을 실행합니다.
캠페인은 크게 두 종류입니다. 카카오톡, 이메일, LMS 문자와 같은 채널을 통해 메시지를 보내는 메시지 캠페인, 쇼핑몰의 배너 노출을 통한 온사이트 캠페인이 있습니다. 여기서 백엔드 챕터는 분석 결과를 서빙하고, 자동화된 메시지, 온사이트 캠페인을 실행하는 어플리케이션을 개발하고 있습니다. 이 외에 결제, 회원 관리 등 웹 기반 SaaS 솔루션이 갖춰야하는 기본적인 기능도 개발하고 있습니다.
2021년 1월에 데이터라이즈에서 정식 오픈한 서비스는 창업자인 CXO 분들과 초창기 개발자들의 주도로 구축 되었습니다. 오픈 이후 서비스가 성장세를 타면서 개발 리소스가 더 필요하게 되었고, 2021년 8월부터 신규 백엔드 엔지니어 채용을 시작했습니다. 서비스를 출시할 당시에는 백엔드에서 관리하는 어플리케이션들은 Django 기반의 모놀리식 구조였습니다. 기능을 빠르게 추가하는 일이 중요했기에 Django가 도움이 됐습니다. 하지만 속도가 최우선이 되다 보니 유지 보수성이나 확장성이 고려되지 않은 채 개발된 부분이 많았습니다.
초기 BE 챕터의 Pain Point
DB 테이블 중심 절차 지향적 방식으로 작성된 클래스는 로직을 이해하기 더 어렵게 만들었습니다. 데이터 검증, 외부 API 호출, API 응답 처리 등 한 클래스 메소드가 많은 책임을 지고 있다 보니 다양한 변경에 노출 된 상태이기도 했습니다. 무엇보다 외부 인프라에 직접적으로 의존하는 구조라 테스트도 까다롭고 실제로는 테스트 코드 자체가 존재하지 않았습니다. 기존 코드를 처음부터 작성하지 않은 신규 백엔드 멤버들은 자신이 추가하는 기능이 버그를 발생 시킬지도 모른다는 두려움이 있었습니다.
레거시 코드의 정의는 사람마다 다르고 주관적일 수 있는데요. ‘레거시 코드 활용전략’이라는 책에서는 레거시 코드의 정의를 명쾌하게 내리고 있습니다. 바로 테스트 루틴이 없는 코드입니다. 그 기준으로 봤을 때 기존 코드는 빠른 시간 안에 레거시 코드가 되어버렸습니다. 비즈니스 로직이 단순하고, 기능 추가가 많지 않다면 기존 코드베이스를 어떻게든 고쳐 써볼 수 있었을 것 같습니다. 하지만 품질로써 추가되어야 하는 기능과 시장에서 우위를 점하기 위해 추가되어야 하는 기능이 많이 쌓여 있었습니다.
이 그래프는 설계와 생산성의 관계를 보여주고 있습니다. 푸른색 그래프는 좋은 설계를 갖췄을 때 개발자의 생산성입니다. 붉은색 그래프는 그 반대입니다. 설계가 좋든 나쁘든 프로젝트 초반에는 생산성이 비슷하지만 시간이 지날수록 나쁜 설계를 한 프로젝트는 생산성이 급격히 떨어집니다. 당시 데이터라이즈의 현실은 붉은색 그래프에 해당하였고 점차 생산성의 위기를 느끼기 시작했습니다. 그래서 BE 챕터는 도메인 로직 식별이 쉽고 테스트하기 쉬우며 변경에 유연한 코드를 어떻게 만들 수 있을지 고민하기 시작했습니다.
해결 과정 : 도메인 주도 설계 & 클린 아키텍처
2021년에 ‘파이썬으로 살펴보는 아키텍처 패턴’이라는 책이 번역 출간되었습니다. 책에서는 프레임워크에 독립적인 Python 웹 어플리케이션 구현 방식을 제안하고 있습니다. 그리고 실제 구현 코드까지 제공하기 때문에 충분히 Django의 대안으로 검토할 만한 가치가 있다고 생각했습니다. 이 책에서는 비즈니스 핵심 로직을 모델링하는 방법으로 도메인 주도 설계, 그리고 그것을 지원하는 설계로 클린 아키텍처(실제로 책에서는 클린 아키텍처, Hexagonal 아키텍처와 원리가 비슷한 Onion 아키텍처를 기준으로 설명함)를 채택하고 있었습니다. 저희는 이 2가지를 스터디하고 프로젝트에 적용하기로 했습니다.
도메인 주도 설계는 도메인 전문가와 개발자가 활발히 소통하면서 도메인 개념과 용어를 정립하고 그것을 실제로 코드에 반영하는 개발 방법론입니다. 코드에서 비즈니스 로직을 파악하기 힘들었던 저희 상황에서는 활용 가치가 높은 방법이라고 생각 했습니다. 도메인 주도 설계를 실현하는 방법 중 이벤트 스토밍도 있는데요. 도메인 전문가와 개발자가 함께 모여 시스템의 비즈니스 프로세스와 이벤트를 시각적으로 모델링하는 활동입니다. 저희는 CXO분들을 포함해서 기획자, 디자이너까지 서비스와 관련된 모든 분이 모여 이벤트 스토밍을 진행했습니다. 도메인의 전체 그림을 파악하고 도메인 지식을 공유하며 시스템의 복잡한 프로세스를 이해하는 데 도움이 되었습니다. 그리고 도메인 주도 설계 요소인 Entity, Aggregate 레포지토리 개념을 활용해 새로운 코드로 구현해보기도 했습니다.
‘파이썬으로 살펴보는 아키텍처 패턴’ 책에서 도메인 주도 설계만큼 중요하게 다뤄지는 부분이 클린 아키텍처입니다. 클린 아키텍처는 변경에 유연하게 대처할 수 있는 아키텍처의 원칙 모음이라고 볼 수 있습니다. 여러 원칙 중에서 핵심적인 것은 관심사의 분리와 의존성의 방향입니다.
관심사의 분리는 말 그대로 코드를 관심사에 따라 계층으로 나누는 것입니다. 여기서 관심사란 코드가 가져야 하는 책임을 말하는데요. 이렇게 책임에 따라 코드를 격리시키게 되면 어떤 한 계층에 변동 사항이 생겨도 다른 계층은 영향을 받지 않게 됩니다. 그래서 기능을 추가하거나 변경할 때 수정되는 범위가 한정되고 한 부분의 문제가 전체로 퍼지는 문제를 방지할 수 있습니다.
클린 아키텍처 책에서 제안하는 계층 구조가 있는데요. 그대로 적용하기보단 원칙을 거스르지 않는 선에서 간략화했습니다. 그래서 도메인, 어플리케이션, 어댑터 세 계층을 만들었고 그 중 도메인 계층에서는 핵심적인 비즈니스 로직을 수행하는 코드들을 배치했습니다. 어플리케이션 계층은 도메인 계층과 어댑터 계층을 연결하는 역할을 수행합니다. Use case나 트랜잭션(Transaction) 제어, 외부 알림과 같이 코드가 어플리케이션으로서 동작할 수 있게 만드는 코드가 들어갑니다. 어댑터 계층에서는 웹 프레임워크나, DB, 외부 API와 같이 외부 세계와 상호작용하는 코드를 배치했습니다. 어플리케이션이나 도메인 계층의 인터페이스를 실제로 구현합니다.
개편된 시스템의 가장 큰 장점은 힘들게 파악했던 비즈니스 로직이 개념적으로 모델링 되고 이해하기 쉬워졌다는 점입니다. 그리고 저희만의 공통된 아키텍처를 가지게 되면서 직접 작업해보지 않았던 코드베이스여도 파악이 어렵지 않게 됐습니다. 또한, 개발 과정에서 테스트 코드를 필수적으로 작성하면서 코드 변경에 대한 두려움을 없앴습니다. 이로 인해 처음 개편을 시작할 때부터 테스트 커버리지를 90% 정도로 유지하고 있습니다.
사실 개발자들의 주도로 시간과 인력을 투자해 이런 개편을 진행하기는 쉽지 않습니다. 가장 어려운 부분이 명분을 만들고 경영진이나 다른 부서의 동료를 설득하는 것인데요. 저희는 창업자인 CXO 분들이 모두 엔지니어로서의 소양을 갖추고 있어 기술부채 해결의 중요성에 대한 이해도가 높은 편입니다. 그리고 탑다운으로 의사결정이 이루어지지 않습니다. 개발자도 충분한 권한을 위임받고 프로젝트를 진행할 수 있는 문화 덕분에 개편을 진행할 수 있었다고 생각합니다.
돌이켜보면 가장 중요한 건 신뢰라는 생각이 듭니다. 만약 저희가 개편을 한다고 코드를 대대적으로 변경하고 배포했는데 장애가 발생했다면 아마 개발자 스스로뿐 아니라 동료도 불안감을 느꼈을 것 같습니다. 하지만 저희는 안정성을 챙기기 위해 테스트 코드를 충실하게 작성했고 현재까지 별다른 장애 없이 안정적으로 운영하고 있습니다.
이대로 해피엔딩?
모든 아키텍처는 장단점이 있습니다. 저희가 채택한 방법은 코드의 유연성을 위해 어느 정도 복잡성을 감수해야 하는데요. 한 가지 예로 계층 구조를 유지하려면 계층 간에 데이터가 교환될 때 계속 매핑 시켜줘야 합니다. 그래서 매핑 시키기 위한 보일러플레이트 코드가 많아졌습니다. 이 때문에 생산성이 떨어지는 것 아니냐는 의견이 나오기도 했지만 아직까지 비용대비 이익이 많다고 생각합니다.
공통된 아키텍처를 새롭게 만들긴 했지만 아키텍처에 엄격한 컨벤션이나 제약사항이 없습니다. 그래서 작업하는 개발자의 아키텍처 수준에 따라 구현 방식이 달라져 서비스별로 통일성이 없는 코드가 작성되기도 합니다. 이런 이해의 차이가 발생할 때마다 워크샵을 열거나 토론을 통해 개념을 다시 정립하고 있습니다. 다같이 통일된 코드를 관리하기 위해 컨벤션을 정리하는 작업도 진행 예정입니다.
끝으로, 데이터라이즈는 폭발적으로 성장하고 있습니다. 이 수치는 1년간의 변화입니다. 개발자가 성장하는 환경에서 일하는 건 정말 중요하다고 생각합니다. 새로운 경험을 하기 좋은 환경이라 생각하기 때문입니다.
그리고 또 하나 우리는 글로벌로 나아가고 있습니다. 그래서 이렇게 할 일이 엄청나게 많습니다. 많은 경험을 할 수 있다는 뜻입니다. 하지만 지금까지의 과정에서 보셨듯 우리는 함께 고민하고 시도하고 개선하는 조직입니다. 모든 방향에 주먹구구식의 개발은 없을 것이라는 이야기를 꼭 하고 싶습니다. 건강한 조직에서 함께 고민하고 시도하고 개선하실 분을 꼭 찾고 싶습니다.