Module Federation을 이용하여 Next.js 앱간의 개발 효율성을 높이기(1)

kern yoo
6 min readAug 28, 2023

--

Photo by Kaleidico on Unsplash

현재 한화생명 LMP 프로덕션 팀에서 Next.js 기반의 앱에서 module federation을 적용한 기록과 그 이유를 기록합니다. — 금번 1편에서는 왜 프론트파트에서 module federation을 만들어야 했는지, 대략적으로 어떤 구조로 만들게 되었는지를 설명합니다.

현재 저는 한화생명의 LMP 프로덕션 팀의 프론트엔드파트에 속해 있습니다. LMP 프로덕션 팀에서 프론트엔드 파트는 LIFEPLUS TRIBES라는 앱의 어드민 / 앱 내의 웹뷰 페이지를 담당하고 있는 중입니다.

현재 2.0 버전 개발로 정신없이 진행하고 있는데요, 이 전에 제 포스팅에서도 보셨듯, 현재 저희 파트는 내부 어드민, 외부 크리에이터가 사용하는 어드민, 웹뷰 세가지 앱을 사용해야 하므로 Next.js 기반으로 monorepo를 구축하여 사용하고 있는 중입니다.

앱 간의 컴포넌트 및 소스를 공유하기 위한 방안 — monorepo 구축

프로젝트 구축의 시작은 앱 간의 컴포넌트를 공유하고, 그외의 소스를 구축하기 위해 시작됩니다. UI에 관련된 컴포넌트 들이 실제 어드민과 웹에서 공통적으로 사용되어야 하는 부분들이 존재하고, 이 컴포넌트 들을 각각의 앱에서 일일이 구현하는 것이 아닌 한 개의 ui 라이브러리를 구축하고, 공통화된 props를 사용하여 다른 앱을 구축할때에도 손쉽게 사용하기 위해 ui 라이브러리를 구축하고, API 통신 및 모델을 공통으로 사용하는 부분들이 어드민 — 웹간에 존재하므로 해당 모델 구축과 usecase, dao를 core라는 패키지로 구축하여 사용하고 있습니다.(core 구축은 별도의 포스팅을 다룰 예정입니다.)

또다른 앱의 추가 — 외부 크리에이터 어드민

LIFEPLUS TRIBES에는 내부 직원분들이 앱에 혜택 등을 게시하는 어드민 외에, 내부적으로 계약한 크리에이터 및 외부 직원 분들이 혜택및 피드를 업로드하는 영역이 별도로 존재합니다. 이 분들이 내부 직원용 어드민을 사용하는 것은 보안상의 이슈가 존재할 수 있고, 페이지 접근에 대한 이슈도 있을 수 있기 때문에 별도의 파트너용 외부 어드민이 필요했습니다.

위의 내용처럼, 내부 어드민에서 사용되고 있는 회원, 혜택, 지표 등의 서비스 일부를 외부 어드민으로 옮겨가야 했고, 사용하는 컴포넌트 및 UI는 동일하지만, 각 앱에 따라서 작동되는 비즈니스 로직 등이 앱마다 조금씩 다른 구조입니다. (승인 정책 등)

1차 분리 — 각 서비스별로 패키지로 분리

처음 구상은 monorepo의 이점을 활용하여 각 서비스들을 패키지로 분리하고, 각자 번들링하여 번들링된 패키지를 각 앱에서 import 해서 사용하는 방안으로 설계 후, 작업을 진행하였습니다.

패키지 분리는 비교적 수월하게 진행되었습니다. 그러나, 이렇게 패키지로 분리하여 배포하는 경우 React context를 사용하는 App에서 context provider를 공유하지 못하는 이슈가 발생하게 됩니다.

저희파트는 어드민은 antd를, HTTP request는 SWR을 사용하고 있었는데, 각 앱마다 antd option을 바인딩하는 provider, HTTP request에 대한 option을 공통으로 적용하는 provider가 필요했습니다. (특히 antd 5버전의 경우 테마를 커스터마이징 하기 위한 token 및 다국어 처리를 위해 provider가 필요합니다.)

다만, 서비스들을 묶어 패키지로 번들링 하는 과정에서, provider의 scope를 공유하지 못하고, 별개의 provider로 인식되어 앱에서 설정하는 provider를 하위 컴포넌트들이 상속받지 못하는 이슈가 발생합니다.

2차 분리 — 어드민 App에서 Module Federation 사용

1차 분리 시점에 provider scope에 대한 이슈를 발견 한 이후,아예 어드민 앱에서 파트너 앱으로 분리할 페이지들을 전체적으로 모듈로 구성, module federation 기능을 이용하여 파트너 앱에 삽입하는 것으로 결정하게 됩니다.

이 경우, 각 service에 따라 번들링을 신경쓰지 않아도 되며, 각 앱에서는 Next.js의 publicRuntimeConfig를 활용하여 어느 앱에서 호출되는지를 확인하여 비즈니스로직 및 API 등을 분리하여 처리할 수 있도록 하였습니다.

어드민 — 파트너 어드민간 module federation 처리 과정

대략적인 구조는 다음과 같이 모듈을 공유합니다.

  • next 빌드시 공유해야 하는 페이지는 module federation으로 처리하고, 만들어지는 파일은 AWS S3저장을 위해 static/chunks/remoteEntry.js 로 빌드하여 처리 및 deploy함. (static/chunks는 next.js에서 번들링되는 파일의 정적 저장소)
  • 파트너 어드민에서는 remoteApp에 대한 요청을 {cloudfront도메인}/_next_/static/chunks/remoteEntry.js 로 module federation의 remote url을 설정
  • 파트너 어드민 페이지에서는 remoteEntry의 module의 가져오고자 하는 모듈을 명시하고, 명시한 모듈을 next/dynamic을 이용하여 원본 페이지에 dynamic import시킴
  • next.js의 publicRuntimeConfig는 어드민 / 파트너 어드민간의 공통의 property및 규약을 설정하고, 설정한 규약에 따라 config property를 설정함.
  • 각 모듈 페이지에서는 해당 config값을 이용하여 어드민 / 파트너 어드민을 분기할 수 있도록 기타 비즈니스로직에 대한 공통화 및 정리

다행히 module-federation에서 nextjs에 대한 page module 을 만들기 위한 @module-federation/nextjs-mf plugin 이 있습니다.

다음 2편에서는 플러그인을 이용하여 nextjs page를 module federation으로 분리하는 방법 + 각 앱에 대한 public config를 이용하여 분기하는 방법에 대한 내용을 다루겠습니다.

--

--