MSW를 통한 API 모킹으로 생산적인 개발하기

태현·2023년 8월 15일
1
post-thumbnail

MSW가 프론트 개발에서 필요한 이유

이전의 프로젝트는 이미 API가 어느정도 윤곽이 잡힌 상태에서 개발에 투입이 되었지만, 현재 하고 있는 프로젝트는 기획이 완료됨과 동시에 프론트엔드 + 백엔드 개발이 동시에 시작이 되었다. 따라서 더미데이터를 만들어 개발하거나 UI만 개발하면서 API가 개발이 될 때까지 무한정 기다려야한다.

이를 극복하고자 검색하던 도중 초기 개발 단계에서 이러한 문제를 극복하고자 MSW로 개발하는 것을 알게 되었고 팀원에게 MSW를 소개하면서 우리 프로젝트에도 도입하기로 결정하였다.

MSWMock Service Worker 이다.
HTTP 요청을 보내면 MSW가 가로채 사전에 설정해둔 데이터로 응답을 보내는 도구다. 따라서 사전에 백엔드 개발자와 협의된 API 스펙이 있다면 수월하게 작업할 수 있을 것이다.

MSW 설치 과정

설치

개발 환경에서만 사용할 것이기 때문에 devDependencies로 설치한다.

npm i -D msw

yarn add -D msw

Service Worker 등록

브라우저에서 사용하기 위해서는 MSW를 서비스 워커에 등록해야한다.

MSW에서 제공하는 CLI을 통해 Service Worker를 등록할 수 있다.

npx msw init public/ --save

Worker 설정

mkdir src/mocks

touch src/mocks/handlers.js

Worker 생성

// src/mocks/browser.ts

import { setupWorker } from "msw";
import { handlers } from "@mocks/handlers";

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

export const worker = setupWorker(...authHandlers, ...studyHandler);
// 나는 api가 많아져서 관리하기 힘들어질 상황을 고려해서 밑에 방법을 택했다.

Next.js 추가 설정

Next.js는 브라우저와 노드 환경을 추가로 설정해주어야 한다.
https://github.com/vercel/next.js/tree/canary/examples/with-msw/mocks

브라우저 환경

mocks/browser.ts

import { SetupWorker, setupWorker } from 'msw';
import { handlers } from '@mocks/handlers/handlers';
import { studyHandlers } from '@mocks/handlers/studyHandlers';

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

노드 환경

mocks/server.ts

import { SetupServer, setupServer } from 'msw/node';
import { handlers } from '@mocks/handlers/handlers';
import { studyHandlers } from '@mocks/handlers/studyHandlers';

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

server + broswer 실행

mocks/index.ts

const initMocks = async (): Promise<void> => {
  if (typeof window === 'undefined') {
    const { server } = await import('@mocks/server');
    server.listen();
  } else {
    const { worker } = await import('@mocks/browser');
    worker.start();
  }
};

export { initMocks };

진입점 파일 worker 적용

마운트 되기 전에 worker가 실행될 준비가 완료되기 전에 실행되므로 에러가 발생한다. 그래서 _app.tsx에 worker를 지연시키기 위한 로직을 작성해야한다. 나 같은 경우는 로직이 너무 길어져서 커스텀 훅으로 분리해서 적용하였다.
관련 이슈 : https://github.com/mswjs/msw/discussions/1049

useIsWorker.ts

/**
 * @description msw worker를 지연시키기 위한 커스텀 훅입니다.
 * @returns { shouldRender } 모킹 사용가능 여부
 */
const useIsWorker = () => {
  const mockingEnabled = !!process.env.NEXT_PUBLIC_API_MOCKING;
  const [shouldRender, setShouldRender] = useState(!mockingEnabled);
  useEffect(() => {
    if (mockingEnabled) {
      import('../mocks').then(async ({ initMocks }) => {
        await initMocks();
        setShouldRender(true);
      });
    }
  }, []);

  return { shouldRender };
};

export default useIsWorker;

_app.tsx

// _app.tsx

export default function App(...){
  const { shouldRender } = useIsWorker();
  
  if (!shouldRender) return null;
  
  ...
}

Mock Data 작성 및 핸들러 정의

// src/mocks/data/todos.json

[
  {
    id: 1,
    text: '운동하기',
  },
  {
    id: 2,
    text: '공부하기',
  },
  {
    id: 3,
    text: '장보기',
  },
];
// src/mocks/handlers.ts

import { rest } from 'msw'

import { rest } from 'msw';
import todo from '@mocks/data/todo.json';

export const handlers = [
  rest.get('/api/todos', (req, res, ctx) => {
    return res(ctx.json(todo));
  }),
];

사용 결과

0개의 댓글