MSW 완벽 가이드 | API Mocking·Service Worker·테스트·실전 활용

MSW 완벽 가이드 | API Mocking·Service Worker·테스트·실전 활용

이 글의 핵심

MSW로 API Mocking을 구현하는 완벽 가이드입니다. Request Handlers, Response Resolver, Browser/Node 통합, Testing까지 실전 예제로 정리했습니다.

실무 경험 공유: 백엔드 API 없이 프론트엔드 개발을 진행하면서, MSW로 개발 속도가 2배 향상된 경험을 공유합니다.

들어가며: “백엔드 API를 기다려야 해요”

실무 문제 시나리오

시나리오 1: 백엔드 개발이 늦어요
API를 기다려야 합니다. MSW로 Mock API를 만듭니다.

시나리오 2: 테스트에서 실제 API를 호출해요
느리고 불안정합니다. MSW로 Mock 응답을 반환합니다.

시나리오 3: 에러 상황 테스트가 어려워요
실제로 에러를 만들기 어렵습니다. MSW로 쉽게 시뮬레이션합니다.


1. MSW란?

핵심 특징

MSW (Mock Service Worker)는 Service Worker 기반 API Mocking 라이브러리입니다.

주요 장점:

  • Service Worker: 네트워크 레벨 Mocking
  • 브라우저 + Node: 개발 + 테스트
  • 타입 안전: TypeScript 지원
  • 실제와 동일: 실제 HTTP 요청
  • Zero Config: 간단한 설정

2. 설치 및 설정

설치

npm install -D msw

Service Worker 생성

npx msw init public/ --save

3. Handlers 정의

다음은 typescript를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: 1, name: 'John', email: '[email protected]' },
      { id: 2, name: 'Jane', email: '[email protected]' },
    ]);
  }),

  http.post('/api/users', async ({ request }) => {
    const newUser = await request.json();
    return HttpResponse.json(
      { id: 3, ...newUser },
      { status: 201 }
    );
  }),

  http.delete('/api/users/:id', ({ params }) => {
    return HttpResponse.json(
      { success: true },
      { status: 200 }
    );
  }),
];

4. 브라우저 통합

아래 코드는 typescript를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// src/mocks/browser.ts
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';

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

// src/main.tsx
import { worker } from './mocks/browser';

if (process.env.NODE_ENV === 'development') {
  worker.start();
}

5. Node 통합 (테스트)

아래 코드는 typescript를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// src/mocks/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);

// src/setupTests.ts
import { server } from './mocks/server';

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

6. Response Resolver

동적 응답

아래 코드는 typescript를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

http.get('/api/users/:id', ({ params }) => {
  const { id } = params;

  if (id === '1') {
    return HttpResponse.json({ id: 1, name: 'John' });
  }

  return HttpResponse.json(
    { error: 'User not found' },
    { status: 404 }
  );
});

지연

아래 코드는 typescript를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

import { delay } from 'msw';

http.get('/api/users', async () => {
  await delay(2000);
  return HttpResponse.json([]);
});

7. 에러 시뮬레이션

아래 코드는 typescript를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

http.get('/api/users', () => {
  return HttpResponse.json(
    { error: 'Internal Server Error' },
    { status: 500 }
  );
});

http.get('/api/users', () => {
  return HttpResponse.error();
});

8. 테스트에서 사용

다음은 typescript를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// src/components/UserList.test.tsx
import { render, screen } from '@testing-library/react';
import { server } from '../mocks/server';
import { http, HttpResponse } from 'msw';
import UserList from './UserList';

test('loads and displays users', async () => {
  render(<UserList />);

  expect(await screen.findByText('John')).toBeInTheDocument();
  expect(screen.getByText('Jane')).toBeInTheDocument();
});

test('handles error', async () => {
  server.use(
    http.get('/api/users', () => {
      return HttpResponse.json(
        { error: 'Failed to fetch' },
        { status: 500 }
      );
    })
  );

  render(<UserList />);

  expect(await screen.findByText('Failed to fetch')).toBeInTheDocument();
});

9. GraphQL Mocking

다음은 typescript를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import { graphql, HttpResponse } from 'msw';

const handlers = [
  graphql.query('GetUser', ({ variables }) => {
    return HttpResponse.json({
      data: {
        user: {
          id: variables.id,
          name: 'John',
        },
      },
    });
  }),

  graphql.mutation('CreateUser', async ({ variables }) => {
    return HttpResponse.json({
      data: {
        createUser: {
          id: 3,
          ...variables.input,
        },
      },
    });
  }),
];

정리 및 체크리스트

핵심 요약

  • MSW: Service Worker 기반 API Mocking
  • 브라우저 + Node: 개발 + 테스트
  • 타입 안전: TypeScript 지원
  • 실제와 동일: 실제 HTTP 요청
  • 에러 시뮬레이션: 쉬운 에러 테스트
  • GraphQL: GraphQL Mocking

구현 체크리스트

  • MSW 설치
  • Handlers 정의
  • 브라우저 통합
  • Node 통합
  • Response Resolver 구현
  • 에러 시뮬레이션
  • 테스트 작성
  • GraphQL Mocking

같이 보면 좋은 글

  • Jest 완벽 가이드
  • Cypress 완벽 가이드
  • Testing Library 완벽 가이드

이 글에서 다루는 키워드

MSW, Mock Service Worker, API Mocking, Testing, Service Worker, Frontend

자주 묻는 질문 (FAQ)

Q. 프로덕션에 포함되나요?

A. 아니요, 개발/테스트 환경에서만 사용됩니다.

Q. 실제 API 호출을 가로채나요?

A. 네, Service Worker를 통해 네트워크 레벨에서 가로챕니다.

Q. GraphQL도 지원하나요?

A. 네, GraphQL Query/Mutation을 모두 지원합니다.

Q. 프로덕션에서 사용해도 되나요?

A. 네, 많은 기업에서 개발/테스트 도구로 사용하고 있습니다.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3