Next.js 14(app router), MSW(mock service worker) 셋업 단계 정리

이성진·2025년 2월 5일
1
post-thumbnail

참고 사항

  • MSW + Next.js 예제를 참고했다.
  • Next.js 프레임워크를 사용했다.
  • 프로젝트의 구조는 FSD 아키텍쳐를 사용했다.
    - FSD 아키텍처를 구성하는 기본 레이어 중 app pages는 Next.js의 예약 디렉토리 이름과 겹치기에 아래와 같이 수정하였습니다.
    • FSD appcore
    • FSD pagesscreens
  • 주요 패키지 정보는 아래와 같다.
  • node 버전은 v22.11.0 이다.
  • npm 버전은 10.9.0 이다.
  • 패키지 매니저는 pnpm을 사용했다.
// package.json
{
  "dependencies": {
    "react": "^18.3.1",
    "next": "14.1.3",
    "msw": "^2.7.0",
    "typescript": "^5"
  }
}

1. MSW 설치

1-1. MSW 설치

https://mswjs.io/docs/getting-started

pnpm install msw -D

1-2. MSW 초기화

https://mswjs.io/docs/cli/init/

// Next.js는 정적 폴더가 /public 이므로 /public 하위 mock 설정 파일을 설치한다.
npx msw init ./public

1-3. package.json 설정 (1-2 에서 명령어로 이미 설치했다면 생략)

{
  // ... dependencies
  "msw": {
    "workerDirectory": [
      "public"
    ]
  }
}

기대 결과물

  • /package.json에 msw가 dependencies에 추가된다.
  • /package.json에 msw가 workerDirectory에 추가된다.
  • /public에 mockServiceWorker.js 파일이 추가된다.

2. Next.js 환경 설정

2-1. next.config.mjs 파일 설정

const nextConfig = {
  // ...
  experimental: {
    instrumentationHook: true, // instrumentation.ts 파일을 사용할 것이므로 true로 설정한다.
  },
  webpack: (config, { isServer }) => {
	// Webpack의 resolve.alias를 사용하여, 서버와 클라이언트 환경에서 각각
    // msw/browser 또는 msw/node 모듈을 로드하지 않도록 설정합니다.

    // 서버 환경에서는 msw/browser 모듈을 사용하지 않도록 설정합니다.
    if (isServer) {
      if (Array.isArray(config.resolve.alias)) {
        config.resolve.alias.push({ name: 'msw/browser', alias: false });
      } else {
        config.resolve.alias['msw/browser'] = false;
      }
    } 
    // 클라이언트 환경에서는 msw/node 모듈을 사용하지 않도록 설정합니다.
    else {
      if (Array.isArray(config.resolve.alias)) {
        config.resolve.alias.push({ name: 'msw/node', alias: false });
      } else {
        config.resolve.alias['msw/node'] = false;
      }
    }

    return config;
  }
}

2-2. src/middleware.ts 파일 설정 (미들웨어를 사용하지 않았거나 굳이 필요하지 않으면 생략 가능)

export async function middleware(req) {
  // ...
}

// 매칭된 패턴의 라우트는 미들웨어를 거치지 않음
export const config = {
  // /mockServiceWorker.js 경로가 포함된 요청은 Middleware에서 제외한다.
  matcher: ['/((?!mockServiceWorker.js).*)'],
};

3. Mock Handler 설정

3-1. 핸들러 설정

// src/core/Mock/handlers/handler.ts

import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get(`https://your_api_domain/api/users`, () => {
    return HttpResponse.json({
      code: 200,
      status: 'OK',
      message: 'OK',
      data: {
        id: '1',
        name: 'Dunkun',
        email: 'example@example.com',
      },
    });
  }),
];

3-2. msw setup

// src/core/Mock/handlers/server.ts

// 서버 환경(node) 에서는 'msw/node' 의 모듈을 사용합니다.
import { setupServer } from 'msw/node';

import { handlers } from './handlers';

export const server = setupServer(...handlers);
// src/core/Mock/handlers/browser.ts

// 클라이언트 환경(browser) 에서는 'msw/browser' 의 모듈을 사용합니다.
import { setupWorker } from 'msw/browser';

import { handlers } from './handlers';

export const worker = setupWorker(...handlers);

3-3. 서버 msw 환경 설정

// src/instrumentation.ts

// 런타임 환경이 'node.js' 라면 직전에 설정한 Mocks/server 에서 worker를 가져와 mocking을 설정합니다.
export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    const { server } = await import('./core/Mock/handlers/server');

    server.listen({
      // MSW가 모킹(mocking)되지 않은 API 요청이 발생하면
      // 이를 그대로 실제 네트워크 요청으로 보내기 위해 'bypass'를 설정합니다.
      
      // 'bypass': 모킹되지 않은 요청은 실제 서버로 요청됨 (기본 API 동작 유지)
      // 'warn': 콘솔에 경고 메시지를 출력하지만 요청은 실제 서버로 전달됨
      // 'error': MSW가 처리하지 않는 요청이 발생하면 오류를 발생시킴
      onUnhandledRequest: 'bypass',
    });
  }
}

3-4. 클라이언트 msw 환경 설정

// src/core/Mock/providers/MockBrowser.provider.ts

'use client';

let triggered = false;

async function initMock() {
  // window가 undefiend가 아니라면 직전에 설정한 Mocks/browser 에서 worker를 가져와 mocking을 설정합니다.
  if (typeof window !== 'undefined') {
    const { worker } = await import('../handlers/browser');

    await worker.start({
      onUnhandledRequest: 'bypass',
    });
  }
}

export const MockProvider = () => {
  if (!triggered) {
    triggered = true;
    throw initMock();
  }

  return null;
};
// src/app/layout.tsx

import { PropsWithChildren } from 'react';
import { MockProvider } from '@/core/Mock/providers/MockBrowser.provider.ts';

export default function RootLayout({ children }: PropsWithChildren) {
  return (
    <html>
      <body>
        <MockProvider />
        {children}
      </body>
    </html>
  );
}

4. MSW 응답 확인

  • next를 개발 환경에서 실행하여 콘솔에 [MSW] Mocking enabled 이 찍히는지 확인한다.

  • handlers에 설정한 URL에 요청했을 때, Mocking한 json이 응답오는지 확인한다.

만약 위 두가지 모두 성공적으로 응답이 온다면 MSW를 정상적으로 설치한 것이다.
이후에는 원하는 handler를 handlers에 추가하면 된다.

profile
개발보다 회사 매출에 영향력을 주는 개발자

0개의 댓글