MSW

MSW(Mock Service Worker)

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์„œ๋ฒ„ API ํ˜ธ์ถœ์„ Mocking(๊ฐ€์งœ๋กœ ๊ตฌํ˜„)ํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

์˜คํ”„๋ผ์ธ ์ž‘์—…๋“ฑ์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•œ ์„œ๋น„์Šค ์›Œ์ปค์˜ ๊ธฐ๋Šฅ์„ ์œ ์šฉํžˆ ํ™œ์šฉํ•œ ๊ฒƒ

์„œ๋ฒ„ ์‘๋‹ต์„ ๊ฐ€์งœ ๋ฐ์ดํ„ฐ๋กœ ๋Œ€์ฒดํ•˜์—ฌ ์„œ๋ฒ„ ์˜์กด์„ฑ ์—†์ด ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ฑฐ๋‚˜ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

npm i -D msw

Service Worker

๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์™€ ๋ณ„๋„๋กœ ์‹คํ–‰๋˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ

์ผ๋ฐ˜์ ์œผ๋กœ ๋„คํŠธ์›Œํฌ ํ”„๋ก์‹œ ๊ฐ™์€ ์—ญํ• ์ด๋‚˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…, ์บ์‹ฑ, ์˜คํ”„๋ผ์ธ์„ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.

Worker

์›น ๋ธŒ๋ผ์šฐ์—์„œ ๋™์ž‘ํ•˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ ๋ฉ”์ธ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋œ๋‹ค.

์›น์—์„œ ์ œ๊ณตํ•˜๋Š” Worker๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ๊ฐ€๋กœ๋ง‰์ง€ ์•Š๊ณ  ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ non-blocking ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„์‹ผ ์—ฐ์‚ฐ์„ worker ์—์„œ ์‹คํ–‰ํ•˜๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ์ข‹์•„์ง„๋‹ค.

Window ์™€ Document ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค.

ํ”„๋ก์‹œ(Proxy)๋ž€?

ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ์‚ฌ์ด์—์„œ ์ค‘๊ณ„ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์„œ๋ฒ„

๋„คํŠธ์›Œํฌ ํ”„๋ก์‹œ๋ž€ ๋„คํŠธ์›Œํฌ ํ™˜๊ฒฝ์—์„œ ์ธํ„ฐ๋„ท ์ ‘์†์„ ์ œ์–ดํ•˜๊ณ  ์ค‘๊ณ„ ์—ญํ• ์„ ํ•˜๋Š” ์„œ๋ฒ„๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

Service Worker๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ

  • ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰๋˜๋Š” ๋…๋ฆฝ์ ์ธ ์Šค๋ ˆ๋“œ

  • ๋„คํŠธ์›Œํฌ ์š”์ฒญ ๊ฐ€๋กœ์ฑ„๊ธฐ (ํ”„๋ก์‹œ ์—ญํ• )

  • ์บ์‹œ ์ €์žฅ์†Œ ์ œ์–ด

  • ํ‘ธ์‹œ ์•Œ๋ฆผ ์ œ๊ณต

  • ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋™๊ธฐํ™” ์ž‘์—… ์ˆ˜ํ–‰

  • ์˜คํ”„๋ผ์ธ ์ƒํ™ฉ์—์„œ๋„ ์ž‘๋™ ๊ฐ€๋Šฅํ•œ ์˜คํ”„๋ผ์ธ ๊ธฐ๋Šฅ ์ œ๊ณต

MSW ํ™˜๊ฒฝ ์„ธํŒ…ํ•˜๊ธฐ

src/mocks/

server.ts

mock server๋ฅผ ๊ตฌ์„ฑํ•  ํŒŒ์ผ

import { setupServer } from 'msw/node';

import handlers from './handlers';

const server = setupServer(...handlers);

export default server;

handler.ts

์‚ฌ์šฉํ•  api ์ŠคํŽ™์„ ์ž‘์„ฑํ•˜๋Š” ๊ณณ

๊ฐ„๋‹จํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ ๋ฐ ๋ชฉ ๋ฐ์ดํ„ฐ๋กœ์„œ ์‚ฌ์šฉํ•  ์ •๋„๋กœ๋งŒ ๊ฐœ๋ฐœํ•˜์ž. ๋„ˆ๋ฌด ์น˜์ค‘ํ•˜๋ฉด ๋ฐฑ์—”๋“œ๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์ด๋‹ˆ ๋งค๋ชฐ๋˜์ง€๋ง์ž

import { rest } from 'msw';

const BASE_URL = 'http://localhost:3000';

const handlers = [
  rest.get(`${BASE_URL}/products`, (req, res, ctx) => {
    const products = [
      {
        category: 'Fruits', price: '$1', stocked: true, name: 'Apple',
      },
    ];

    return res(
      ctx.status(200),
      ctx.json({ products }),
    );
  }),
];

export default handlers;

MSW with Jest

src/setupTests.ts

import server from './mocks/server';
// ํ…Œ์ŠคํŠธ ์ „ ๋ชฉํ‚นํ•œ msw ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐ
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
// ๊ฐ ํ…Œ์ŠคํŠธ ํ›„ ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด ์—ฐ๊ฒฐ๋œ ํ•ธ๋“ค๋Ÿฌ ์ดˆ๊ธฐํ™”
afterEach(() => server.resetHandlers());
// ๋ชจ๋“  ํ…Œ์ŠคํŠธ ์™„๋ฃŒ ํ›„ ์—ฐ๊ฒฐ ๋Š๊ธฐ
afterAll(() => server.close());

jest.config.js ํŒŒ์ผ์˜ setupFilesAfterEnv ์†์„ฑ์— setupTests.ts ํŒŒ์ผ ์ถ”๊ฐ€

module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: [
    '@testing-library/jest-dom/extend-expect',
    '<rootDir>/src/setupTests.ts', // <-- ์ถ”๊ฐ€
  ],
  transform: {
    '^.+\\.(t|j)sx?$': ['@swc/jest', {
      jsc: {
        parser: {
          syntax: 'typescript',
          jsx: true,
          decorators: true,
        },
        transform: {
          react: {
            runtime: 'automatic',
          },
        },
      },
    }],
  },
};

Testing with MSW

MSW๋กœ ์„œ๋ฒ„ API ์˜์กด์„ฑ์„ ๋Œ€์ฒดํ•˜์—ฌ ํ…Œ์ŠคํŒ…์ด ๊ฐ€๋Šฅํ•˜๋‹ค. Jest๊ฐ€ ์ œ๊ณตํ•˜๋Š” mock ์•ˆ์จ๋„ ๋จ

ReferenceError: fetch is not defined

์‚ฌ์šฉ ์ค‘์ธ Node์˜ ๋ฒ„์ „์ด ๋‚ฎ์„ ๊ฒฝ์šฐ, fetch API๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์•„์„œ msw๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ReferenceError: fetch is not defined ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์ด ๊ฒฝ์šฐ์— polyfill(ํด๋ฆฌํ•„)์„ ์‚ฌ์šฉํ•ด์„œ ํ•ด๊ฒฐํ•ด์•ผ ํ•œ๋‹ค.

npm i -D whatwg-fetch

ํ•ด๋‹น ํด๋ฆฌํ•„์„ ์‚ฌ์šฉํ•˜๋Š” ๊ณณ์—์„œ import 'whatwg-fetch' ๋ฅผ ๋ช…์‹œํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

msw๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ๊ณณ์—์„œ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— setupTests.ts ์— ๋ช…์‹œํ•ด์ฃผ์ž

// src/setupTest.ts
import 'whatwg-fetch';
 
import server from './mocks/server';

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

afterAll(() => server.close());

afterEach(() => server.resetHandlers());

polyfill(ํด๋ฆฌํ•„)

์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋œ ์ตœ์‹  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฒ„์ „์„ ๊ตฌํ˜• ๋ธŒ๋ผ์šฐ์ € & ๋…ธ๋“œ ํ™˜๊ฒฝ์—์„œ๋„ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ์ฝ”๋“œ์กฐ๊ฐ์ด๋‹ค.

ํŠน์ • ํ™˜๊ฒฝ์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์ด ์ตœ์‹  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฌธ๋ฒ•์„ ์ง€์›ํ•˜์ง€ ์•Š์„ ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์œผ๋กœ ES6์— ์ถ”๊ฐ€๋œ Promise ๋Š” ie11 ๊ฐ™์€ ๊ตฌํ˜• ๋ธŒ๋ผ์šฐ์ €์—์„  ์ง€์›ํ•˜์ง€ ์•Š์•„ ํด๋ฆฌํ•„์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

Last updated