๐Ÿงช
๊ฐœ๋ฐœโ€ขํ…Œ์ŠคํŒ…

MSW ์‚ฌ์šฉ๋ฒ• ์ •๋ฆฌ ๋ฐ ํ›„๊ธฐ

2024.08.23

ํšŒ์‚ฌ์—์„œ ์šด์˜ํ•˜๋Š” ์Šคํ†ก ์„œ๋น„์Šค์—์„œ ์œ ์ €์˜ ํƒ์ƒ‰ ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์—˜๋ผ์Šคํ‹ฑ ์„œ์น˜๋ฅผ ๋„์ž…ํ•˜๊ฒŒ ๋๋Š”๋ฐ, api ๊ฐœ๋ฐœ ์™„๋ฃŒ ์ „ ๋ฏธ๋ฆฌ MSW๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ชฉ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค์–ด ์ž‘์—…ํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

Mock Service Worker(MSW)๋Š” ๋ธŒ๋ผ์šฐ์ €์™€ Node.js๋ฅผ ์œ„ํ•œ API ๋ชจํ‚น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค.

์„ค์ •๋ฐฉ๋ฒ•

์šฐ์„  msw๋ฅผ ์‚ฌ์šฉํ•  ํ™˜๊ฒฝ์„ธํŒ…๊ณผ msw ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•œ๋‹ค. ๋‚˜๋Š” Vite๋ฅผ ์‚ฌ์šฉํ•ด ํ™˜๊ฒฝ์„ ์„ธํŒ…ํ–ˆ๋‹ค.

pnpm create vite

Vite ํ…œํ”Œ๋ฆฟ์œผ๋กœ ๊ตฌํ˜„๋œ ํ”„๋กœ์ ํŠธ ์ปจํ…์ธ 

npm install msw@latest --save-dev

๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๋œ๋‹ค.

์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ๋™์ž‘ํ•˜๋Š” node์™€ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋™์ž‘ํ•˜๋Š” browser ์„ค์ •์ด ์žˆ๋‹ค.

์šฐ์„  ์„œ๋น„์Šค์›Œ์ปค๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค. ์—ฌ๊ธฐ๋ฅผ ๋ณด๋ฉด ์›Œ์ปค์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๋Š” cli๊ฐ€ ์žˆ์–ด์„œ ๋‚˜๋Š” ๊ทธ๊ฑธ ์‚ฌ์šฉํ•ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ƒ์„ฑํ–ˆ๋‹ค.

npx msw init <PUBLIC_DIR> --save

์ด ๊ฒฝ์šฐ npx msw init ./public --save ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

๋ช…๋ น๋ฌธ์„ ์‹คํ–‰ํ•˜๋ฉด public ํด๋”์— mockServiceWorker.js๊ฐ€ ์ƒ๊ธด๋‹ค.

์ดํ›„ ์‚ฌ์šฉ๋ฒ•์— ๋งž๊ฒŒ /src/mocks ์— browser.ts, handler.ts, node.ts๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

/**
 * main.tsx์— ์ž‘์„ฑ๋œ ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ
 * index.html์˜ ์Šคํฌ๋ฆฝํŠธ๋กœ ๋ Œ๋”๋œ๋‹ค.
 * <script type="module" src="/src/main.tsx"></script>
 */

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";

async function enableMocking() {
  if (process.env.NODE_ENV !== "development") {
    return;
  }

  const { worker } = await import("./mocks/browser");

  // `worker.start()` returns a Promise that resolves
  // once the Service Worker is up and ready to intercept requests.
  return worker.start();
}

enableMocking().then(() => {
  createRoot(document.getElementById("root")!).render(
    <StrictMode>
      <App />
    </StrictMode>,
  );
});

์„œ๋ฒ„์—์„œ์˜ ๋™์ž‘๋„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด mock ์„œ๋น„์Šค์›Œ์ปค/์„œ๋ฒ„๋ฅผ ์ดˆ๊ธฐํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

// mocks/index.ts
async function initMocks() {
  if (typeof window === 'undefined') {
    const { server } = await import('./server');
    server.listen();
  } else {
    const { worker } = await import('./browser');
    worker.start();
  }
}

initMocks();

export {};

์œ„ ๋ฐฉ์‹์œผ๋กœ ๋ชจ๋“ˆ์„ ๋งŒ๋“ค๊ณ  dev/prod์— ๋”ฐ๋ผ ์กฐ๊ฑด๋ถ€๋กœ ์‹คํ–‰ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ์ฝ˜์†”์„ ํ™•์ธํ•˜๋ฉด

MSW ์‹คํ–‰์‹œ ์ถœ๋ ฅ๋˜๋Š” ๋กœ๊ทธ

ํ›„๊ธฐ

์ดˆ๊ธฐ์—๋Š” express ํ˜น์€ next.js ์ฒ˜๋Ÿผ ์„œ๋ฒ„๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•  ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•ด์„œ ๊ด€๋ จ ์ž๋ฃŒ๋ฅผ ๋งŽ์ด ์„œ์นญํ•ด๋ดค๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ MSW๋Š” ์• ์ดˆ์— ํด๋ผ์ด์–ธํŠธ์—์„œ ๋™์ž‘ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜€๊ณ  ์™ธ๋ถ€์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก next.js ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๊ฒฐํ•ฉํ•˜์…” ๊ตฌํ˜„์€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•  ์ด์œ ๊ฐ€ ์—†์—ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ํ•˜๋‚˜์˜ ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ mock์„œ๋ฒ„๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ ๊ฐ™์•˜๋‹ค.

๋‚ด๊ฐ€ ์ž‘์—…ํ•˜๋Š” ํšŒ์‚ฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋ชจ๋…ธ๋ ˆํฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋กœ์ ํŠธ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐœ๋ฐœํ•˜๊ณ  ์ปจํŠธ๋กค๋Ÿฌ๋งŒ ์‚ฌ์šฉํ•˜๋ ค๋Š” ํ”„๋กœ์ ํŠธ์—์„œ import ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๊ตณ์ด ๊ทธ๋ ‡๊ฒŒ ํ•  ์ด์œ ๊ฐ€ ์žˆ์–ด๋ณด์ด์ง€๋Š” ์•Š์•˜๋‹ค.

๊ด€๋ จ ์ž๋ฃŒ๋ฅผ ์ฐพ์•„๋ณด๋‹ค๊ฐ€ ssr / csr์—์„œ msw ์ดˆ๊ธฐ ๋กœ๋“œ ๊ด€๋ จ ์ด์Šˆ ๊ด€๋ จ ๋ธ”๋กœ๊ทธ ๋‚ด์šฉ์ด ์ข€ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์•˜๋Š”๋ฐ, ๊ทธ ๋‹จ๊ณ„๊นŒ์ง€๋Š” ์‹œ๋„ํ•ด๋ณด์ง€ ์•Š์•˜๋‹ค. ์–ธ์  ๊ฐ€ ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ mock ์„œ๋ฒ„๋ฅผ ํ™œ์šฉํ•ด๋ณด๊ฒŒ ๋œ๋‹ค๋ฉด ๊ตฌํ˜„ํ•ด ๋ณผ ์ƒ๊ฐ์ด๋‹ค.