하반기 회사에서 했던 것 중 가장 뿌듯했고 공을 많이 들였던 프로젝트가 끝났다. 바로 '주문서 마이그레이션 및 개선' 프로젝트인데, vue 2로 되어있던 주문서를 Next.js 15로 마이그레이션했다.
성과
일단 마이그레이션 결과부터 말하자면, 상품 PDP에서 바로구매 버튼을 눌러 주문서에 진입하는 경우
FCP가 1.44초에서 0.41초로 70% 빨라졌으며, LCP는 1.44초에서 0.7초 즉 절반으로 줄어들었다.
TTFB는 1.06초에서 0.15초로 줄었다.
발단
원래 주문서는 vue 기반의 nuxt로 되어있었다. 팀에서는 오랫동안 주문서 진입이 느리다는 문제를 계속 인식하고 있었지만 우선순위에서 밀린 상태였다. 그래서 주문서 렌더링 개선 및 리팩토링 프로젝트는 본격적으로 시간과 인력을 할당받아하는 게 아니었고, 여러 프로젝트 중간중간 room이 생길때 팀 내부적으로 하기로 했었다. 그렇기에 기술 스택을 유지하면서 리팩토링과 주문서 내 렌더링 개선, 버그만 수정하기로 했었다.
주문서는 복잡한 로직이 있고 결제가 엮여있는 이커머스의 핵심 기능이기에 팀에서도 잘 못 건드리고 있던 부분인데, 성능이 안 좋았고 개선의 여지가 많았다. 그래서 이런 코어이면서도 레거시인 코드를 리팩토링해보고 싶어서 팀 리드한테 먼저 하고 싶다고 말했고, 동료 팀원 한 명과 하게 되었다.
작업 순서
주문서 개선 프로젝트는 아래의 순서로 진행되었다.
1. 성능 측정, 문제 파악
가장 먼저 라이트하우스, 개발자 도구로 주문서 진입 시의 성능을 측정했다. 주문서 윗부분과 아랫 부분을 나눠 네트워크 요청을 보냄에도 눈에 띄게 느렸다. 이 단계에서 파악한 문제로는 크게 렌더링 성능과 DX가 있었다.
첫번째 렌더링 성능 문제. props가 깊게 내려가고, Vue의 emit이 조상을 엄청 찾아간다. 카드 할부 정보를 가져오는 내용과 결제를 위핸 써드파티 라이브러리들을 준비하는 아랫 부분이 특히 느렸다. prop을 자식에서 부모 컴포넌트로 올리는 부분들이 속도 저하의 원인인 것 같았다.
두번째로 DX 문제.
기존 주문서의 코드는 2023년 초에 Php에서 로직 그대로 옮겨온 것이었다고 한다. 그 과정에서 더이상 안 쓰는 코드가 많이 남아있는 상태였다. 그리고 emit을 사용하는 부분들이 많아 코드를 추적하기 어려웠다. 그래서 결제 모듈들과 통신하는 데이터와 주문서에서만 다루는 데이터들을 나눠 관리하도록 리팩토링 방향을 정했었다.
2. TC 작성
3. 테스트 코드 설계, 작성
4. 이 단계에서 vue에서 개선하지말고 아예 next.js로 마이그레이션하기로 결정이 내려졌다. php에서 vue로 마이그레이션하면서, 아예 다 보여주거나 안 보여주는 'All or nothing' 전략이 vue에 그대로 묻어있었다. next의 suspense, streaming ssr을 활용하여 조금이라도 유저에게 주문서를 더 빨리 보여주기로 했다.
5. next.js 개발
테스트 코드를 짠 이유
주문서 코드를 건들기에 앞서 테스트 코드를 무조건 작성해야한다고 생각했다. 이유는 가격/할인/쿠폰/배송 등 복잡한 비즈니스 및 계산 로직이 있어 리스크가 큰 작업이었고, 리팩토링을 하기 전후 무조건 똑같이 동작해야한다고 생각했기 때문이다.
기존 vue 주문서는 테스트 케이스(TC)가 기획 부서에도, 개발 부서에도 없었기 때문에 내가 직접 작성했다. 주문서를 다 만져보고 눌러보면서 작성했다. 로그인 여부에 따라 달라지는 로직도 있고 상품 쿠폰, 판매자 쿠폰, 적립금, 상품권 등 고려할 게 많았는데 이 TC를 작성한 게 주문서의 정책을 파악하는데 꽤 많은 도움이 됐다.
그리고 이 과정에서 기존 주문서에 남아있는 버그와 개선의 요지가 있는 부분(안내 문구, 중첩된 alert 프로세스 )을 발견해서 이후에 내용을 정리해서 PM 측과 공유하고 의견을 받아 수정했다.
Playwright을 선택한 이유
TC를 기반으로 Playwright의 Testing Philosophy를 참고해 테스트 코드를 작성했다. 전 회사에서는 Cypress로 e2e 테스트를 짰었는데 이번에 Playwright을 선택한 이유는 2가지였다.
- Playwright은 Google Chrome for Android, Mobile Safari 테스트를 지원한다. 반면 Cypress는 2024년 8월 기준 Safari를 지원하지 않았다. 머스트잇 앱은 웹뷰라 Playwright이 테스트하기에 적합하겠단 생각이 들었다.
- cypress 쓸 때 Cypress 앱을 따로 깔아서 썼더니 무거운 느낌이 들었었다. 그래서 빠르게 VSCode 안에서 실행하고 콘솔 아웃풋과 테스트 결과를 확인할 수 있는 Playwright이 다른 팀원들이 테스트를 돌려보기에도 좋겠단 생각이 들었다.
e2e 테스트를 구현하면서 역시나 노가다라는 생각을 하긴 했지만 리팩토링 후
코드의 신뢰성을 보장해줄 게 테스트 코드뿐이었기 때문에 이 테스트 코드의 가치에는 의심이 전혀 들지 않았다.
테스트 코드를 다 작성한 이후엔 엔지니어링 리드의 제안으로, '주문서에 E2E 테스트 끼얹기'라는 제목의 간단한 발표를 했다. e2e 테스트 코드를 작성한 이유와 테스팅 툴 선택과정, 자세한 적용법 등의 내용을 담았다.
테스트코드가 주문서 페이지만 커버하기에 배포 속도가 길어지는거에 비해 효율이 나오지 않을 것 같아 CI/CD에 붙이진 않았다.
테스트 코드를 다 작성해가던 타이밍에, 아예 Next.js로 주문서를 띄워서 주문서 진입 성능부터 개선하자는 팀 내부 의견이 컨펌되어 본격적으로 Next.js로 주문서를 마이그레이션하게 되었다. 너무너무너무 좋은 기회였다.
Next.js 도입
머스트잇 앱은 원래 Vue로 돼있어서 회사 프로젝트에 Next.js를 도입하는 건 처음이었다. 앞으로 다른 영역들도 Next.js로 전환해야했기에 파일 디렉토리 구조나 로깅 방식 등 글로벌한 의사결정은 팀원 다같이 논의를 했다.
주문서를 마이그레이션하면서 가장 먼저 팀원들과 싱크를 맞췄던 부분은 '속도도 중요하지만 퀄리티를 놓치지 말자' 였다. 이 목표를 위해 우리는 이렇게 마이그레이션했다.
Keep
1. 불필요한 BFF API, 프로퍼티 제거
기존 앱은 BFF API를 두고 서버에서 내려준 데이터를 앱/웹 각각에 맞게 가공해 보내줬었다. 적지 않은 양의 Server-driven UI 코드도 있었다. 하지만 우리 앱은 몇달 전 풀 웹뷰로 전환되었기에 주문서 마이그레이션 때부터는 서버 api를 직접 찌르고 클라이언트에서 데이터를 뷰에 맞게 가공하기로 했다.
또 사라진 기능의 경우 전사 확인이 필요했다. 팀즈에 글을 올려 해당 기능이 여전히 유효한지, 만약 아니라면 아예 지워도 되는지 등을 전사적으로 확인 후 제거했다. 이 과정에서 더이상 사용하지 않지만 남아있던 API와 기능들을 걸러낼 수 있었다. 결제의 경우 DB 인서트하는 부분이 있어서 php 단의 일부 로직은 가져오지 못 한게 아쉽다.
2. 창의적으로 접근하는 시도하기
작업 초기, 상품+할인+장바구니가 엮여있는 로직을 가져와야했던 적이 있었다. BFF(node.js), 웹(vue) 2군데에 복잡하게 로직이 나뉘어져있어 실수하고 싶지 않아서 기존 코드 로직을 일단 가져와서 리팩토링을 하려고 했다. 그런데 이 부분에서 한 동료가 '기존 코드를 수정하더라도 일단 가져오게 되면 그 사고방식에서 벗어나기 힘들 것'이라는 피드백을 주었다. 그 말을 듣고 좀 더 대담하고 창의적으로 접근을 해야겠다고 생각했다.
방법은 코드에서 우리가 해야할 일을 먼저 파악한 후, 기존 코드의 구현 방법은 잊고 새롭게 짜보는 것이었다. 기존 로직을 최대한 가져오지 않고, 코드의 미션(구현해야하는 기능)을 이해하고 다시 짜다보니 작업 시간이 꽤 들었지만 동료의 이 조언은 그 때의 나에게 꼭 필요했던 것 같다.
3. 자주 싱크 맞추고 논의하기
주문서 마이그레이션/개선 프로젝트를 하면서 참여했던 팀원 모두가 느꼈던 건 '소통의 중요성'이었다. 이번 프로젝트는 우리 팀의 첫 Next.js 마이그레이션이었기에 디렉토리 구조, 공통으로 쓰이는 함수나 로직에 엄격해야했다. 한번 공통의 단계로 올라가면 다시 내리기 어렵기에, 공통으로 쓰이는 로직이라면 모두가 합의를 해야만 공통의 영역으로 올렸다.
뿐만 아니라 주문서는 상품, 할인 쿠폰, 가격, 배송, 적립금, 결제 수단 등 많은 요소(상태)가 강하게 엮여있다. 할인쿠폰 컴포넌트에서 A 쿠폰을 적용하면 적립금을 쓸 수 없게 되거나, 특정 배송방식을 선택하면 특정 결제수단(OO페이)를 쓸 수 없거나 하는 식이다.
또 정리된 정책서가 없고 작업자별 비즈니스 로직 이해도가 달랐기에 누군가 한 부분을 고쳤을 때 다른 부분이 영향을 받기 쉽상이었다. 이런 일이 몇번 일어나면서 우리는 어떤 작업, 수정을 할 때 다른 쪽에도 영향이 있을 것 같거나, 메이저 로직 작업이면 싱크를 다같이 맞추고 작업하기로 했다.
그래서 자리에서 간단히 얘기하다가 '잠깐 원팀에서 얘기좀 하시죠.'하고 회의실에서 더 길게 이야기하곤 했다. 지금까지 프로젝트 중에 가장 많이 팀원들과 기술적으로 소통한 것 같다고 느낀 동료도 있었다.
Problem & Try
반면 다른 프로젝트를 할 때 개선하면 좋을 점들은 아래와 같다.
1. 개발자들끼리 정책 이해도를 맞추기
우리 기술 챕터는 프로젝트 개발 착수 전 개발 프리뷰를 한다. 개발 시작 전 BE, FE 담당자가 각각 기획서와 코드를 검토하고 어떻게 개발할지 이야기를 해보는 시간이다. 주문서 마이그레이션 프로젝트는 TC 말고는 정책서가 따로 없었어서 담당자들끼리 정책을 모두 깊게 이해하고, 제대로 이해했는지 핏을 맞춰봤으면 좋았을 것 같다. 어쨌든 이 과정에서, 복잡하고 계속 업데이트되는 정책에 대응하기 위해선 파편화된 위키보다 개발자에게 접근성이 좋은 주석이 더 낫다는 것을 깨달아 주석을 많이 남겨두었다.
2. 좀 더 정확한 일정 산정
이번 프로젝트는 개발 착수 전 TC와 테스트 코드를 작성하면서 파악한 것보다 더 큰 공수가 필요했다. 챌린징하게 일정을 잡았어야해서 챌린징하게 잡았지만 로깅을 심는데에 앱과 관련해 이슈가 좀 있었고, 원인 파악하는데 시간이 걸려 Qa 일정이 조금 늘어났다. 앞으론 로깅 같이 테스트코드가 커버하기 어려운 부분들을 더 자세히 파악해서 일정을 산정해야겠다.
결론
주문서 마이그레이션&개선을 하면서 무신사 주문/클레임 pm의 환불서비스 개선 이야기를 인상깊게 읽었다. 이 글의 팀은 '장기적으로는 큰 임팩트가 있지만 단일 과제로는 임팩트가 부족한', 즉 이미 굴러가고는 있는 프로젝트를 이해관계자들을 설득해 시작하고, 의지로 해내고 나니 다음 단계로 나아갈 토대가 되었다고 한다. 우리의 주문서 프로젝트도 다른 작업을 병행하고, 야근과 주말 업무를 하면서도 팀원들의 의지로 해냈다. 또 해내고 나니 다른 영역도 Next.js로 전환할 수 있는 기회가 열렸다.
머스트잇에 입사하고 했던 프로젝트 중 가장 챌린징했던 만큼 성취감과 애정이 크다. 2024년 하반기 많은 시간과 에너지를 이 프로젝트에 쏟았는데 성능과 DX가 확실히 개선되고 기술 부채가 줄어 기쁘다. 또 우리 팀원들과 기획, 마케팅 등 다른 부서 분들과 깊게 협업할 수 있었어서 행복했다.