next.js (app router) 에 msw 도입시 에러 해결

I n ru n·2024년 4월 24일

ssr 문제

msw 와 nextjs 연결을 위해 msw 공식 문서로 설정을 완료 했다.

하지만, 오류가 발생했다. 이미 유명한 오류였다. (해당 이슈)
nextjs 서버 사이드에서 msw 모킹 불가 문제로 이해했다.
서버사이드 프로세스에 server.listen() 실행이 문제다.

해결 방법

두가지 방법이 issue 에 있다.

  • express 로 서버를 띄워서 msw worker 대신 서버로 모킹한다.
  • nextjs 의 instrumentation 기능을 사용한다. Instrumentation에 nextjs 서버 시작시 한번 실행되는 함수를 정의할수있다.

나는 두번째 방법으로 해결했다.

먼저 msw 공식문서 설정을 해야한다. msw 문서 (node 와 browser 모두해줘야함)

instrumentationHook 설정

next config 파일에 필드를 설정한다.

// next.config.mjs
const nextConfig = {
  experimental: {
    instrumentationHook: true,
  },
};

export default nextConfig;

server 설정

app 폴더 라인에 instrumentation.ts 파일을 추가한다.

// src/instrumentation.ts 
// app 폴더와 같은 위치에 정의되어야한다.

// nextjs 서버에서 instrumentation 기능을 이용하여 server 를 한번 실행시킨다.
export async function register() {
  console.log(
    '[instrumentation] server.listen()...',
    process.env.NEXT_RUNTIME, 
    typeof window,
  );

  if (process.env.NEXT_RUNTIME === 'nodejs') {
    const { server } = await import('@/src/mocks/node'); // setupServer 로 정의한 인스턴스
    server.listen({
      onUnhandledRequest: 'bypass', 
    });
  }
}

worker 설정

initMsw.ts 에 worker 를 실행시키기위한 함수를 정의한다.

// src/mocks/initMsw.ts
export const initMsw = async () => {
  if (process.env.NEXT_RUNTIME !== 'nodejs') { // 브라우저 환경에서만 worker 를 실행시킨다.
    const { worker } = await import('./browser');
    await worker.start({
      onUnhandledRequest: 'bypass',
    });
  }
};

initMsw 함수 로 worker 가 실행 완료후 하위 컴포넌트를 렌더링시킨다.

// 정의한 initMsw를 실행시킨다.
'use client';

import { useEffect, useState } from 'react';

import { initMsw } from '@/src/mocks/initMsw';

export const MSWComponent = ({ children }: { children: React.ReactNode }) => {
  const [isInit, isSetInit] = useState(false);

  useEffect(() => {
    if (!isInit) {
      initMsw().then(() => isSetInit(true)); 
    }
  }, [isInit]);

  if (!isInit) {
    return null;
  }

  // worker 가 실행 완료되면 하위 컴포넌트들을 실행시킨다.
  return <>{children}</>;
};
<MSWComponent>
  {children}
</MSWComponent>

참고

https://github.com/depromeet/10mm-client-web/pull/239

0개의 댓글