msw (api mocking)
January 04, 2024
사용시 이점
- 중간에 service worker를 통해서 네트워크 요청을 가로채고 모의 응답을 받아 반환하기 때문에 기존 data fetch 로직을 그대로 사용하여 mock 데이터를 반환 받을 수 있다.
- 그렇기 때문에 api 가 혹시라도 늦게 나온다고 하더라도 사전에 api spec 만 미리 맞춰놓는다면 네트워크 요청 로직을 미리 작성해 놓고 그대로 사용하여 테스트할 수 있다.
- 데이터를 하드코딩 하거나 mock server를 만든다고 하더라도 추후에 url 및 엔드포인트 변경이라던가 네트워크 요청 로직을 다시 작성할 필요가 있는데, msw는 그에 비해 on/off 하듯이 기존에 필요한 로직을 그대로 사용하여 원하는 데이터를 조작하여 사용할 수 있어 백엔드와 개발 시점이 겹친다고 하더라도 일정이 미뤄지지 않고 개발할 수 있다.
- 원하는 테스트를 진행하기 위해 db 조작을 따로 요청할 필요 없이 mock 데이터를 생성해 테스트할 수 있다.
- header, cookie 등 네트워크 요청시 사용하는 것들은 대부분 조작 가능해서 프로덕션에서만 가능하고 로컬에서는 테스트가 불가능하던 것 까지 테스트가 가능하다.
- storybook, 브라우저 에서 구동되는 페이지, nodejs 까지도 지원될 정도로 범위가 넓다.
설정
- 라이브러리 설치
yarn add msw --dev
- 기반 코드 자동 생성
npx msw init public/ --save
- 요청이 들어왔을 경우 임의의 응답을 해주는 핸들러 코드 작성
- src/mocks에 handler 작성
import { http, HttpResponse } from "msw";
import { BATCH_API } from "src/constants/api";
const batchBaseUrl = process.env.NEXT_PUBLIC_BATCH_API;
const batchResponse = {
code: "DONE",
data: {
},
};
const getBatchList = http.get(`${batchBaseUrl}${BATCH_API.schedules}`, () =>
HttpResponse.json(batchResponse),
);
export const handlers = [getBatchList];
- 서비스 워커 생성 (브라우저 환경 설정)
//src/mocks/worker.ts
import { setupWorker } from "msw/browser";
import { handlers } from "src/mocks/handlers";
export const worker = setupWorker(...handlers);
- 서버 생성 (node 환경 설정)
//src/mocks.server.ts
import { setupServer } from "msw/node";
import { handlers } from "src/mocks/handlers";
export const server = setupServer(...handlers);
- msw 동작 방식 설정
import React, { type PropsWithChildren, useState, useEffect } from "react";
import tw from "twin.macro";
const IS_MOCKING_MODE = process.env.NEXT_PUBLIC_API_MOCKING;
const MSWComponent = ({ children }: PropsWithChildren) => {
const [mswReady, setMswReady] = useState(!IS_MOCKING_MODE);
useEffect(() => {
const init = async () => {
const initMocks = await import("src/mocks/index").then(
(res) => res.initMocks,
);
await initMocks();
setMswReady(true);
};
if (!mswReady) {
init();
}
}, [mswReady]);
if (!mswReady) {
return null;
}
return <Container>{children}</Container>;
};
export default MSWComponent;
//src/mocks/index.ts
export const initMocks = async () => {
if (process.env.NODE_ENV === "development") {
if (typeof window === "undefined") {
(async () => {
const { server } = await import("src/mocks/server");
return server.listen();
})();
} else {
(async () => {
const { worker } = await import("src/mocks/worker");
return worker.start();
})();
}
}
};
//.env.local
NODE_ENV=development
NEXT_PUBLIC_API_MOCKING=enabled
- 동작 방식 적용
- 최상단 root에서 작성한 도작 방식대로 msw가 적용되도록 컴포넌트로 감싸준다.
//pages/_app.tsx
function App(){
return(<MSWComponent>
{...children}
</MSWComponent>
)
}