React에서 NextJS로 마이그레이션

Next | 2024년 01월 02일

운영되고있는 서비스를 어떻게 새로운 환경으로 마이그레이션했는지 소개합니다.

서비스를 마이그레이션하는 과정속에서 어떠한 선택을 했고 그 선택이 어떻게 이어졌는지 소개합니다.

제품이 마주한 문제

2017년 Wanted가 인수한 Wanted Insight는 2022년까지 유지 관리되지 않던 프로젝트였습니다. Wanted Insight가 리브랜딩을 결정하면서 팀 내부에서는 빠르게 마켓 핏을 확인하는 것을 최우선 과제로 삼았고, 프로젝트의 빠른 변화에 맞춰 아키텍처를 재설계할 필요성을 느꼈습니다.

우리는 프로젝트가 가지고 있는 문제점들을 크게 다음과 같이 정의했어요.

  • 미흡한 코드 컨벤션으로 인한 파편화된 코드: 제품의 코드가 국가 데이터를 활용하다보니 특정 데이터를 처리하기 위한 코드들이 많이 존재했고 해당 기능을 수정하거나 새롭게 만들어야하는 과정에서 개발자가 스스로 판단하기 점점 어려워졌고.

    피쳐를 빠르게 해결해야하는 팀 특성상 가장 큰 문제로 다가왔어요.

  • 커스텀 Express로 해결하지 못하는 SEO에 대한 니즈: SEO 향상을 위해 SSR (Server-Side Rendering)이 필요하지만, 기존의 커스텀 Express로 해결할 수 있지만 효과적으로 처리하기 어려울 뿐더러 또 다른 레거시를 낳을 수 있다고 판단했어요.

  • 무분별한 라이브러리 사용으로 인한 오래 소요되는 빌드 타임과 느린 서비스: 다양한 라이브러리의 무분별한 사용으로 빌드 시간이 길어지고, 서비스 속도가 느려짐.

이러한 문제들을 해결하기 위해 우리가 선택한 방법과 그 결과에 대해 자세히 소개할게요.

문제해결 방법 결정하기

해당 문제를 해결하기에는 크게 2가지 방법을 꼽았습니다.

새로 만들기

어떻게 보면 가장 쉽고 재밌을 수 있어요. 저희가 문제로 꼽았던 SSR처리에 대한 니즈와 코드 컨벤션을 정하기도 가장 쉬운 방법이에요. 하지만 앞서 말씀드렸듯이 이미 서비스가 달려가고있었고, 새로운 피쳐들도 매 스프린트마다 나오는 상황이여서 자칫하면 계속해서 레거시 코드를 생산해서 성공하지 못했을 경우 리스크가 가장 클 수 있어요.

현재 프로젝트에서 개선하기.

가장 현실적인 방법이면서, 가장 어려운 방식이라고 생각해요. 기존 프로젝트를 잘 이해하고있는 팀원들이 필요하고 새로운 피쳐들을 쳐내면서 어떤 방식으로 어떤 순서대로 리팩토링을 할지 명확하게 정의하고 달려가야해요.

놀랍게도 저희는 이 2가지 방법을 모두 사용했어요. (이게 무슨 말인지는 어떤 과정들로 진행했는지 아래에서 소개할게요)

문제 해결하기

  1. 아키텍쳐 설계하기 리팩토링을 하기에 앞서 팀이 일하는 방식과 가장 어울리는 구조를 만들기 위해 스터디를 진행했어요.

    그 결과 다음과 같은 아키텍처를 Wanted Insight에 구조로 결정했어요 컴포넌트를 바라보는 시각과 미래지향적인 프론트엔드 아키텍처 코드에서 가장 문제가 많다고 판단한 부분은 상태관리하는 부분이였고 상태관리에 대해서 새로운 룰을 세우고 새롭게 구현하는 코드에 대해서 새로운 룰을 적용해 구현했어요. (상태관리에 대한 자세한 내용은 Zustand로의 여정 복잡한 Redux 구조를 단순화하기 을 참고해주세요.)

  2. 불필요한 라이브러리 제거와 Typescript로 변환 앞서 프로젝트는 Javascript로 구현되어있었고 Wanted의 메인 스택인 Typescript로의 변화를 원했어요. 모든 코드를 리팩토링하면 물론 좋겠지만 점진적으로 코드 내부에서 우선순위를 정하고 우선순위대로 리팩토링을 진행했어요.

    신규 피쳐 개발과 피쳐 수정의 경우 해당 파일을 Typescript로 구현하였고, 공유되는 컴포넌트와 유틸 함수는 우선순위를 높여 Typescript로 구현했어요. 이 외에 기존에 피쳐들은 리스트화 후에 매 스프린트마다 포함하여 따로 리팩토링을 진행했어요.

    또한 팀 내부에서는 불필요한 라이브러리가 많이 사용된다고 판단했고, 몇 가지 기준을 세우고 기준에 부합하는 라이브러리를 제거했어요.

    • UI 라이브러리
    • Lodash
    • Date 라이브러리

    리팩토링을 하면서 가장 큰 문제점은 테스트였어요. 기존 라이브러리를 대체하기 위해 직접 구현해야하는 경우가 대부분이였는데, 이 과정에서 버그를 유발해 서비스에 문제를 일으킬 수 있었기 때문이에요. 그래서 저희는 Util함수에 대해서 Typescript로 구현 후에 테스트코드를 작성해 이 부분에 대한 걱정을 조금이라도 덜 수 있었어요.

    UI 라이브러리의 경우 테스트 코드를 작성하지는않고 테스트맵에 추가시켜 QA팀분들과 테스트를 진행했어요 (QA팀 분들에 노고에 정말 감사합니다..)

  3. 대망의 Next JS로 전환과 지표 공유

    1번과 2번의 작업을 피쳐를 진행하며 Typescript전환률과 새로운 시스템으로 변환이 많이됐을 시점에 Next JS로의 전환을 준비했어요.

    Wanted는 큰 스프린트가 끝날 때 메이커 (개발자, 디자이너, QA)들의 요청으로 2주간 스프린트를 되돌아보고 피쳐를 치면서 놓쳤던 부분을 다시 작업하는 그루밍 스프린트를 진행할 수 있는데 그루밍 스프린트를 활용해서 전환을 하기로 결정했습니다. Next JS의 맞게 변경헀던 폴더 구조와 Typescript코드들과 Util함수들을 옮기고 전체적인 내부 테스트를 진행 후에 배포 프로세스까지 확립후에 배포를 진행했어요. 물론 배포 과정에서 치명적인 버그가 발생하여 서비스에 악영향을 끼칠 수 있기 때문에 Devos팀과 협력하여 카나리 배포를 진행하여 비율을 조정하면서 기존 코드와 새로운 코드에 테스트를 진행했어요 모든 고려 사항과 논의 사항을 공유할 수는 없지만, 이러한 단계를 통해 Wanted Insight는 NextJS로의 마이그레이션을 성공적으로 완료했어요.

    Next JS로 전환 후에 스쿼드 내부와 프론트엔드 챕터에 지표와 함께 공유하면서 해당 작업은 전부 마무리가 되었어요.

    Wanted-Insight Legacy Wanted-Inisght for Next
    Bundle Size 5.21MB 700KB
    LCP 1.5s ~ 1.7s 0.6 ~ 0.8s
    SEO 노출 지표 0 월 23만

    지표에서도 볼 수 있듯이 레거시 코드를 정리하는 과정에서 많은 코스트를 얻을 수 있었어요. 무분별하게 사용되던 UI 라이브러리들을 제거하고, SSR 환경에서 필요한 데이터 초기 렌더를 통해 LCP의 속도도 개선했구요. 무엇보다 가장 중점을 두었던 SEO 노출 지표도 6개월만에 월 23만에 달하는 지표를 얻을 수 있었어요. 기회가 된다면 SEO 개선 과정과 결과에 대해서도 공유 드릴게요.

맺으며

NextJS로의 마이그레이션은 단순히 프로젝트 스택을 변경하는 것 이상의 의미를 저희에게 가져다 줬어요. 새로운 규칙을 설계하고, 타입세이프 코드로 변경하고, 서비스의 미래를 설계하고 서비스가 목표로하는 목표치에 다다를 수 있도록 엔진을 바꿔줬다고 생각해요. 이 과정에서 정말 다양한 부서와 소통하고 많은 도움을 받았어요 원팀이 되어야만 이런 큰일을 해낼 수 있다는 점도 깨달았어요.

각 팀마다 상황이 다르기에 팀에서 수용할 수 있는 레거시의 크기가 다르다고 생각해요. 그렇기에 각자 팀이 수용할 수 있는 크기를 넘어서서 관리가 되고있지는 않은지 끊임없이 돌아보며 관리하는게 중요하다고 생각해요.

이 글에서의 경험은 정말 특수하고 다시는 하기 어려운 경험이라고 생각해요. 하지만 혹시나 저희가 했던 경험과 유사한 일을 하셔야한다면 도움이 되었으면 좋겠습니다.