안녕하세요. 일본 최대 규모의 음식 배달 서비스 Demaecan(出前館, 이하 데마에칸) 프로덕트를 담당하는 김영재라고 합니다. 어느덧 프로덕트를 쇄신한 지 2년 반이 되어가고 있습니다. 이번 글은 레거시를 해소하면서 서비스를 혁신해 나가는 데마에칸의 여정 중 기록을 남기는 의미로 작성했습니다.
레거시의 정의는 다양합니다. 단순히 오래된 코드를 의미하는 경우도 있고, 새로운 서비스 구현에 제약이 되는 기존 구조를 말하기도 합니다. 하지만 사전에 실려 있는 '유산'이라는 긍정적인 의미처럼, 레거시는 지금까지 서비스가 생존할 수 있게 해준 기반이기도 합니다.
레거시에 관한 재미있는 표현도 많습니다.
- 어제 내가 작성한 코드는 레거시이다.
- 사용자가 한 명이라도 있는 시스템은 레거시이다. 그 한 명이 바로 당신이다.
- 레거시를 고칠 바엔 다시 만드는 게 더 빠르다.
위와 같은 표현이 셀 수 없이 많을 정도로 엔지니어는 레거시 때문에 웃고 울곤 합니다.
데마에칸도 여느 회사처럼 레거시가 있습니다. 아니, 많습니다. 데마에칸은 업력이 20년이 넘은 회사입니다. 다시 말해 20여 년의 레거시가 쌓여 있습니다. 레거시는 코드나 시스템만 의미하지는 않습니다. 스펙이나 업무 프로세스에도 레거시가 있습니다. 예를 들어 점포의 속성 중에 '2-in-1'이라는 것이 있는데요. 이것을 정의하고 구현한 모습은 세 가지가 있었습니다. 회사 안에서도 '2-in-1 점포'라고 말할 때 부서마다 다르게 이해하고 있는 것이죠. 이 또한 레거시라고 말할 수 있습니다.
현재 데마에칸은 앱부터 백엔드까지, 백엔드부터 운영 프로세스까지 하나씩 재작성하면서 레거시를 쇄신하는 장기 프로젝트를 진행하고 있습니다. 쇄신하는 방법은 여러 가지가 있는데요. 이번 글에서는 일반적인 방법 외에 조금 색다른 세 가지 방법을 말씀드리고자 합니다.
레거시를 해소하는 대표적인 방법
레거시를 해소하는 대표적인 방법으로는 Strangler Fig 패턴이 있습니다. 아래 그림과 같이 새로운 시스템을 마련한 후 조건에 따라 옛 시스템에서 새로운 시스템으로 이행하다가 새로운 시스템이 잘 작동한다는 확신이 들면 옛 시스템으로 가는 경로를 없애고 폐쇄하는 방법입니다. 직접 본 적은 없지만 Strangler Fig라는 나무가 이와 같은 방식으로 생장한다고 합니다.
데마에칸에서도 이 방법으로 백엔드의 여러 컴포넌트를 활발히 개선하고 있습니다. 가장 안전하면서도 결과와 일정을 예측하기 쉬운 방법이기에 운영 중인 서비스라면 어디든 적용할 수 있습니다. 데마에칸에서는 결제, 주문, 회원 정보 등 이커머스로서 가장 핵심적인 도메인에 이 방법을 사용하고 있습니다.
이 방법의 단점으로는 차근히 개선해 나가는 방식이므로 꽤 오랜 시간이 걸린다는 점과, 안정성을 중시하는 만큼 레거시의 가장 핵심적인 부분은 교체하지 못하는 경우가 많다는 점을 꼽을 수 있습니다. 그래서 보다 극단적인 방법들도 쓰이는데요. 어떤 방법이 있는지 하나씩 사례와 함께 소개하겠습니다.
보다 과격하게 레거시를 해소하는 세 가지 방법
어떤 방법을 사용하든 레거시를 해소하는 것은 어느 지점에서 레거시를 단절하고 새 시스템으로 교체하는 일입니다. 그 단절을 어느 정도로 깊게 만드는지에 따라 작업 난이도와 규모가 달라집니다. 다시 말해 '과격하다'는 것은 그만큼 깊고 명확하게 레거시와 단절하려고 노력했다는 의미이기도 한데요. 데마에칸에서 서비스를 개선하기 위해 직접 사용해 본 과격한 레거시 해소 방법 세 가지를 하나씩 사례와 함께 살펴보겠습니다.
- 인프라 단절하기
- 코드 재작성하기(Recode)
- 사양 경량화하기
1. 인프라(infrastructure) 단절하기
한 단위의 시스템은 인프라와 그 인프라에서 작동하는 데이터베이스 및 애플리케이션, 이들을 잇는 메시징 등 여러 컴포넌트로 구성됩니다. 이 중에서 레거시의 상당수는 데이터 구조에 기인합니다. 그렇기에 데이터 구조는 그대로 둔 채 코드와 패턴의 일부만 수정하는 작업은 레거시를 해소하는 노력 대비 성과가 그다지 크지 않은 경우가 많습니다. 즉 리팩토링으로 더 좋은 패턴을 적용하는 것도 유의미한 변화이지만, 데이터 구조와 이를 활용하는 데이터 구성 방법을 바꾸는 것만큼 극적인 변화를 만들기는 어렵습니다.
3계층(3-tier) 구조든 헥사고날(hexagonal) 구조든 도메인과 데이터 레이어, 애플리케이션 등은 소프트웨어 이론에서 각각을 명확히 구분하는 개념이지만, 현실에서는 모호한 지점이 있기 마련입니다(아마도 그래서 수십 년간 도메인 설계에 대한 책이 그렇게나 많이 나온 것인지도 모릅니다). 즉, 다이어그램에서는 별도의 컴포넌트처럼 그려져 있지만 실제로 코드는 뒤섞여 있는 경우가 많은데요. 이때 각각을 이론처럼 명확히 구분하려면 어떻게 하면 좋을까요?
가장 좋은 방법은 물리적으로 구분하는 것입니다. 인프라를 단절하면 외부 인터페이스로만 통신해야 합니다. 예를 들어 WebAPI로 JSON 또는 Protocol Buffers를 활용할 수 있겠죠. 이렇게 하면 자신이 만든 시스템을 하나의 거대한 SaaS(Software as a Service)로 취급하는 효과를 얻을 수 있습니다. 다시 말해, 서로가 외부 시스템으로 취급할 수밖에 없도록 강제하게 되는 것입니다. 이를 통해 기존의 데이터 구조를 생각하지 않은 채, 두 환경 사이의 통신을 위한 인터페이스와 그 안에 담을 데이터를 가장 이상적인 모습으로 만들도록 유도합니다(두 환경 사이의 인터페이스를 고려할 때는 저의 이전 글 오픈소스답게 설계하기에서 확장성 - 참여를 위한 외부 인터페이스를 참고하시기 바랍니다).
이 방법이 성공하려면 팀 조직 및 운영 관점에서도 처음부터 겸직이나 다른 팀과의 업무 중복 없이 하나의 팀이 서비스 사양부터 인프라 셋업 및 개발까지 독립적으로 작업할 수 있도록 분리할 필요가 있습니다.
인프라 단절 방법의 사례
데마에칸에서는 2022년 Delivery 도메인 전체를 인프라부터 다른 도메인과 단절해 만드는 작업을 진행했습니다. Delivery3.0이라는 프로젝트로, 아래 그림은 Delivery 3.0 전환 과정에서 두 시스템 사이의 인증 흐름을 간단히 나타낸 것입니다. 시스템을 교체해도 모두 로그아웃을 당하는 일 없이 인증을 유지하면서 작업을 진행하고 싶었기 때문에, 앱에서는 레거시 시스템으로 로그인한 후 새로운 시스템의 토큰을 다시 발급받는 흐름을 만들었습니다.
릴리스할 때는 지역 단위로 반년간 4단계에 걸쳐서 적용 지역을 확대했습니다. 도쿄에서 선정된 점포를 대상으로 릴리스를 시작해서 그다음으로 서일본 전역, 그 이후 도쿄 전역, 마 지막으로 일본 전체로 전개하기까지 각 2개월 단위로 릴리스를 진행했습니다. 다행히 데마에칸은 점포 하나 단위로만 주문할 수 있다는 서비스의 특징이 있었기 때문에 점포 단위로 새로운 시스템의 적용 대상을 제어할 수 있었습니다.
데마에칸은 실시간으로 사람이 움직이고 물건과 돈도 실시간으로 오가는 굉장히 역동적인 서비스입니다. 따라서 지역 단위로 적용할 때마다 서비스 품질이 다를 수 있다 보니 CS(customer service) 상황에도 주의를 기울일 필요가 있었습니다. 시스템을 만드는 입장에서는 단 한 번만 성공해도 이후의 모든 흐름 역시 100% 성공한다고 확신할 수 있지만, 사용자 입장에서는 그렇지 않기 때문입니다. 서비스 사용에 익숙지 않은 가맹점주의 조작 실수나 해당 지역에 배달 가능한 배달원이 없는 등의 여러 가지 이유로 항상 모든 흐름이 100% 성공할 수 없고, 이는 소비자나 가맹점, 배달원의 세 엔드 유저 중 누군가가 CS로 문의하는 상황이 발생합니다.
음식 배달은 전 세계적으로 '30분 이내 배달'이 중요한 서비스 지표 중 하나인데요. 데마에칸은 Delivery 3.0 프로젝트의 론칭부터 이를 달성해 온 것은 물론 2% 미만의 배달 지연, 90%가 넘는 배달 만족도로 세계적인 수준의 배달 품질을 달성하고 있습니다.
정리하자면 환경을 단절해 쇄신하는 방법은 아래와 같은 장점이 있습니다.
- 새로운 기능을 선보임과 동시에 과거의 시스템을 자신 있게 삭제할 수 있다.
- 데이터 구조를 새로운 요구사항에 맞게 다시 구성하고 더 좋은 패턴과 구조를 도입할 수 있다.
- 기존 시스템의 운영 비용이 너무 높다면 전환 즉시 비용을 크게 줄일 수 있다.
하지만 모든 기술 선택에는 트레이드 오프가 있기에 유의할 점도 있습니다. 인프라 환경을 단절하는 방법은 성공 확률이 낮은데요. 그 이유는 아래와 같습니다.
- 높은 수준의 팀워크가 필요하다.
- 집중력을 유지할 수 있게 팀을 유지 및 보존하기 어렵기 때문에 그만큼 많은 투자가 필요하다.
- 처음부터 새로 만드는 만큼 시행착오가 발생할 수 있어서 예정보다 일정이 하염없이 지연될 수 있다.
- 사람도 레거시가 될 수 있다. 새롭게 만들 기회가 있어도 기존의 익숙한 지식에 기대 이전과 비슷하게 만드는 경우가 많다.
2. 코드 재작성하기(Recode)
Recode는 제가 이름 붙인 방법인데요. 레거시의 스펙은 그대로 유지하는 상태에서 코드를 처음부터 재작성하는 방법을 의미합니다. 앞서 소개한 인프라 단절 방법은 예전 시스템이 새로운 기능을 도저히 소화할 수 없을 때 유용한데요. 이번 섹션의 Recode는 그와 다르게 구 버전과 완전히 동일한 사양으로 만든다는 특징이 있습니다.
Recode를 하는 이유와 Recode의 장단점
언뜻 이해가 가지 않습니다. 도대체 왜 동일한 사양으로 다시 만들까요? 보통 세 가지 상황이 있습니다.
- 사양서(스펙 문서)가 유실되어 코드와 프로그램만 있는 경우
- 담당 팀이 바뀌면서 기존 팀이 작업한 이력을 파악하기 어렵거나 접근 권한이 없는 경우
- 사용한 기술보다 진보된 기술이 나와서 새로운 기술 세트로 전환이 필요한 경우
이 방법의 장점은 아래와 같습니다.
- 기존 코드를 분석하는 것보다 새로 만드는 게 사양을 이해하는 데 더 집중할 수 있는 작업이고, 결과물에 대한 확신도 더 높아집니다.