3. MSW

1. Service Worker

Service Worker API Workers overview

์„œ๋น„์Šค ์›Œ์ปค๋Š” ์›น ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ, ๋ธŒ๋ผ์šฐ์ €, ๊ทธ๋ฆฌ๊ณ  (์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ) ๋„คํŠธ์›Œํฌ ์‚ฌ์ด์˜ ํ”„๋ก์‹œ ์„œ๋ฒ„ ์—ญํ• ์„ ํ•จ

service_worker

ํŠน์ง•

  • ํšจ๊ณผ์ ์ธ ์˜คํ”„๋ผ์ธ ๊ฒฝํ—˜์„ ์ƒ์„ฑ

  • ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์„œ ๋„คํŠธ์›Œํฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ํ–‰๋™์„ ์ทจํ•จ

  • ์„œ๋ฒ„์˜ ์ž์‚ฐ์„ ์—…๋ฐ์ดํŠธ

  • ํ‘ธ์‹œ ์•Œ๋ฆผ๊ณผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋™๊ธฐํ™” API๋กœ์˜ ์ ‘๊ทผ๋„ ์ œ๊ณต

  • ๋ณด์•ˆ ์ƒ์˜ ์ด์œ ๋กœ HTTPS์—์„œ๋งŒ ๋™์ž‘

    • ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ ์ค‘๊ฐ„์ž ๊ณต๊ฒฉ์— ๊ต‰์žฅํžˆ ์ทจ์•ฝํ•˜๊ธฐ ๋•Œ๋ฌธ

์›น ์›Œ์ปค vs ์„œ๋น„์Šค ์›Œ์ปค

๊ณตํ†ต์ 

  • Web API ์ค‘ ํ•˜๋‚˜

  • ์›น ์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์›Œ์ปค

  • ๋‘˜ ๋‹ค ๋ณด์กฐ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋ฏ€๋กœ ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ์™€ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  JavaScript ์ฝ”๋“œ๋ฅผ ์‹คํ–‰(๋…ผ ๋ธ”๋กœํ‚น)

  • Window ๋ฐ Document objects์— ๋Œ€ํ•œ ์•ก์„ธ์Šค ๊ถŒํ•œ์ด ์—†์œผ๋ฏ€๋กœ, DOM๊ณผ ์ง์ ‘ ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜ ์—†์œผ๋ฉฐ(DOM์— ์ ‘๊ทผ ๋ถˆ๊ฐ€) ๋ธŒ๋ผ์šฐ์ € API์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๊ฐ€ ์ œํ•œ๋จ

์ฐจ์ด์ 

์ฐจ์ด์ 
์›น ์›Œ์ปค
์„œ๋น„์Šค ์›Œ์ปค

์ˆ˜๋ช…

์›น ์›Œ์ปค๊ฐ€ ์†ํ•œ ํƒญ๊ณผ ๋ฐ€์ ‘ํ•˜๊ฒŒ ์—ฐ๊ฒฐ

๋…๋ฆฝ์ 

ํƒญ ์ข…๋ฃŒ ์‹œ

์ข…๋ฃŒ๋จ

๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๊ณ„์† ์‹คํ–‰ ๊ฐ€๋Šฅ

๊ฐฏ์ˆ˜

์—ฌ๋Ÿฌ ์›น ์›Œ์ปค ์ƒ์„ฑ ๊ฐ€๋Šฅ

๋“ฑ๋ก๋œ ๋ฒ”์œ„์˜ ๋ชจ๋“  ํ™œ์„ฑ ํƒญ์„ ์ œ์–ด

๋„คํŠธ์›Œํฌ ์š”์ฒญ

-

๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ”(fetch์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด), ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ํ‘ธ์‹œ API ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ 

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

  • ํŽ˜์ด์ง€๋Š” ์—ฌ๋Ÿฌ ์›น ์›Œ์ปค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‹จ์ผ ์„œ๋น„์Šค ์›Œ์ปค๋Š” ๋“ฑ๋ก๋œ ๋ฒ”์œ„์˜ ๋ชจ๋“  ํ™œ์„ฑ ํƒญ์„ ์ œ์–ด

  • ์›น ์›Œ์ปค์™€ ๋‹ฌ๋ฆฌ ์„œ๋น„์Šค ์›Œ์ปค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด fetch์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„๊ณ , ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ push์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด Push API ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ 

2. MSW(Mock Service Worker)

MSW ์•„์ƒฌ๋‹˜์˜ Mock Service Worker (MSW)

express ๋ณด๋‹ค ์กฐ๊ธˆ ๋ถˆํŽธํ•จ ์ฝ”๋“œ ๋ ˆ๋ฒจ์ด ์•„๋‹ˆ๋ผ ๋„คํŠธ์›Œํฌ ๋ ˆ๋ฒจ์—์„œ ๊ฐ€์งœ ๊ตฌํ˜„(ํ”„๋ก์‹œ๋ฅผ ์ด์šฉ) ์˜คํ”„๋ผ์ธ ์ž‘์—… ๋“ฑ์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•œ ์„œ๋น„์Šค ์›Œ์ปค์˜ ๊ธฐ๋Šฅ์„ ์œ ์šฉํ•˜๊ฒŒ ํ™œ์šฉํ•œ ๊ฒƒ Node, ๋ธŒ๋ผ์šฐ์ € ๋‘˜ ๋‹ค ์ง€์›

๐ŸฅŠ Express vs MSW

๋‹จ์ˆœํžˆ ์ž„์‹œ ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค ๊ฑฐ๋ผ๋ฉด Express๋ฅผ ์“ฐ๋Š” ๊ฒŒ ๋” ๋‚ซ์ง€๋งŒ, MSW๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋„ ์ง€์›ํ•˜๋ฉด์„œ ๊ฒธ์‚ฌ๊ฒธ์‚ฌ ์›น ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ง€์›ํ•˜๋Š” ์šฉ๋„๋กœ๋Š” ๋‚˜์˜์ง€ ์•Š์€ ์„ ํƒ

MSW๋Š” jest์˜ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ(Node.js ๊ธฐ๋ฐ˜) ์™ธ์— ์›น ๋ธŒ๋ผ์šฐ์ €๋„ ์ง€์› API ์ŠคํŽ™์€ ๋‚˜์™”์ง€๋งŒ ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ž„์‹œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ

์‚ฌ์šฉ ๋ฐฉ๋ฒ•

Integrate mocking into Node

1. MSW ํŒจํ‚ค์ง€ ์„ค์น˜

npm i -D msw

2. server.ts ํŒŒ์ผ ์ƒ์„ฑ

src/mocks/server.ts ๊ฒฝ๋กœ์— ์ƒ์„ฑ

// src/mocks/server.ts

import {setupServer} from 'msw/node';

// Import handlers from './handlers';

const handlers = []; // ์ž„์‹œ๋กœ ์ ์–ด๋‘  

// This configures a request mocking server with the given request handlers.
const server = setupServer(...handlers);

export default server;

3. setupTests.ts ํŒŒ์ผ ์ƒ์„ฑ

// src/setupTests.ts

import server from './mocks/server.ts';
// Establish API mocking before all tests.
beforeAll(() => server.listen({onUnhandledRequest: 'error'}));

// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers());

// Clean up after the tests are finished.
afterAll(() => server.close());
  • beforeAll : Jest ์‹œ์ž‘ํ•  ๋•Œ ๋งจ ์ฒ˜์Œ์— ์‹คํ–‰

    • onUnhandledRequest: 'error' : handler๋ฅผ ์•ˆ ์žก์•˜์„ ๋•Œ ์˜ค๋ฅ˜ ๋‚ด๋„๋ก ์„ค์ •

  • afterEach : ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰

  • afterAll : ์ „๋ถ€ ๋๋‚  ๋•Œ ์‹คํ–‰

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

// jest.config.js

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',
                    },
                },
            },
        }],
    },
};

3. REST API ๋ชจํ‚นํ•˜๊ธฐ

Mocking REST API

๐Ÿ”— ์‹ค์Šต ๋งํฌ

โš ๏ธ ๋„ˆ๋ฌด ๋ณธ๊ฒฉ์ ์œผ๋กœ ์ฝ”๋”ฉํ•˜๋ฉด ์‚ฌ์‹ค์ƒ ๋ฐฑ์—”๋“œ๋ฅผ ๊ฐœ๋ฐœํ•˜๊ฒŒ ๋˜๋‹ˆ, ์ด ๋ถ€๋ถ„์— ์ฃผ์˜ํ•  ๊ฒƒ

ํด๋” ๊ตฌ์กฐ

โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ package-lock.json
โ”œโ”€โ”€ jest.config.js โœ…
โ”œโ”€โ”€ src
โ”‚   โ”œโ”€โ”€ main.tsx
โ”‚   โ”œโ”€โ”€ App.tsx
โ”‚   โ”œโ”€โ”€ App.test.tsx โœ…
โ”‚   โ”œโ”€โ”€ setupTests.ts โœ…
โ”‚   โ”œโ”€โ”€ components ๐Ÿ“
โ”‚   โ”œโ”€โ”€ hooks ๐Ÿ“
โ”‚   โ”œโ”€โ”€ mocks ๐Ÿ“
โ”‚   โ”‚   โ”œโ”€โ”€ handlers.ts โœ…
โ”‚   โ”‚   โ””โ”€โ”€ server.ts โœ…

handlers.ts ํŒŒ์ผ ์ž‘์„ฑ

์ง„์งœ๊ฐ™์€ ๊ฒƒ์ด์ง€ ์ง„์งœ๊ฐ€ ์•„๋‹˜ ์ง„์งœ๋„ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•จ โ‡’ E2E ํ…Œ์ŠคํŠธ

// src/mocks/handlers.ts 

import {rest} from 'msw';
// import fixtures from '../../fixtures';

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

const handlers = [
    rest.get(`${BASE_URL}/products`, (req, res, ctx) => {
        // const { products } = fixtures; // fixtures๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋จ  
        const products = [
            {
                category: 'Fruits', price: '$1', stocked: true, name: 'Apple',
            },
        ];

        return res(
            ctx.status(200), // ์—†์–ด๋„ ๋จ(๊ธฐ๋ณธ ์„ค์ •์ด 200)
            ctx.json({products}), // ์œ„์˜ ์š”์ฒญ์„ ์ด ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™ฉ 
        );
    }),
];

export default handlers;

App.test.ts ํŒŒ์ผ ์ˆ˜์ •

// App.test.ts

import {render, screen, waitFor} from '@testing-library/react';

import App from './App';

// jest.mock ๋ถˆํ•„์š”

test('App', async () => {
    render(<App / >);

    await waitFor(() => {
        screen.getByText('Apple');
    });
});
  • waitFor : ~ ๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ

    • ์ฝœ๋ฐฑํ•จ์ˆ˜์˜ ํƒ€์ž…์ด Promise๋กœ ๋˜์–ด ์žˆ์–ด์„œ async/await ํ•„์š”

์ฃผ์˜์  & polyfill ์ด์šฉํ•˜๊ธฐ

// hooks/useFetchProducts.ts

export default function useFetchProducts() {
    const url = 'http://localhost:3000/products';
    const {data, error} = useFetch<ProductsResult>(url);
    console.log({error});
    if (!data) {
        return [];
    }

    return data.products;
}
๐Ÿšจ error: ReferenceError: fetch is not defined

fetch๋Š” ์œˆ๋„์šฐ์— ์žˆ๋Š” ๊ฒƒ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ๋˜์ง€๋งŒ Node์—์„œ๋Š” ์˜ค๋ฅ˜ Node ์ตœ์‹  ๋ฒ„์ „์€ fetch๋ฅผ ์ง€์›ํ•˜์ง€๋งŒ, ํ˜„์žฌ ์‚ฌ์šฉ ์ค‘์ธ Node ๋ฒ„์ „์—์„œ๋Š” ์ง€์› X

โ‡’ polyfill(ํด๋ฆฌํ•„) ์„ ์ด์šฉํ•ด ํ•ด๊ฒฐ

GitHub์—์„œ ๋งŒ๋“  fetch polyfill

์„ค์น˜

npm i -D whatwg-fetch

setupTests.ts ํŒŒ์ผ์— import

import 'whatwg-fetch'

hooks/useFetchProducts.ts ํŒŒ์ผ ์›์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ

export default function useFetchProducts() {
    const url = 'http://localhost:3000/products';
    const {data} = useFetch<ProductsResult>(url);
    if (!data) {
        return [];
    }

    return data.products;
}

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

MDN - Polyfill ๋ชจ๋˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ - ํด๋ฆฌํ•„ GitHub์—์„œ ๋งŒ๋“  fetch polyfill

๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ์ด์ „ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ตœ์‹  ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ฝ”๋“œ (์ผ๋ฐ˜์ ์œผ๋กœ ์›น์˜ JavaScript)

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์„ ๋งŒ๋“œ๋Š” ๊ฐ ์กฐ์ง์€ ๋‚˜๋ฆ„๋Œ€๋กœ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋งค๊ฒจ ๋ช…์„ธ์„œ ๋‚ด ์–ด๋–ค ๊ธฐ๋Šฅ์„ ๋จผ์ € ๊ตฌํ˜„ํ• ์ง€ ๊ฒฐ์ • ๋ช…์„ธ์„œ์— ๋“ฑ๋ก๋œ ๊ธฐ๋Šฅ๋ณด๋‹ค ์ดˆ์•ˆ(draft)์— ์žˆ๋Š” ์ œ์•ˆ์„ ๋จผ์ € ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์กด์žฌ ๊ตฌํ˜„ ๋‚œ๋„๊ฐ€ ๋†’์•„์„œ ์ด๋Ÿฐ ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์ง€๋งŒ, ๊ตฌ๋ฏธ๋ฅผ ๋‹น๊ธฐ์ง€ ์•Š์•„ ์ด๋Ÿฐ ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๊ธฐ๋„ ํ•จ ์—”์ง„์ด ํ‘œ์ค€ ์ „์ฒด๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๊ณ  ์ผ๋ถ€๋งŒ ์ง€์›ํ•˜๋Š” ๊ฒƒ์€ ํ”ํ•œ ์ผ

Last updated