MSW
ํ์ต ํค์๋
Service Worker
MSW(Mock Service Worker)
polyfill(ํด๋ฆฌํ)
Service Worker
๐ Service Worker API
์น ์์ฉ ํ๋ก๊ทธ๋จ, ๋ธ๋ผ์ฐ์ , ๊ทธ๋ฆฌ๊ณ (์ฌ์ฉ ๊ฐ๋ฅํ ๊ฒฝ์ฐ) ๋คํธ์ํฌ ์ฌ์ด์ ํ๋ก์ ์๋ฒ ์ญํ ์ํ
์๋น์ค ์์ปค๋ ์ถ์ฒ์ ๊ฒฝ๋ก์ ๋ํด ๋ฑ๋กํ๋ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์์ปค๋ก์ JavaScript๋ก ์์ฑ ๋ ํ์ผ
์๋น์ค ์์ปค๋ ์ฐ๊ด๋ ์น ํ์ด์ง/์ฌ์ดํธ๋ฅผ ํต์ ํ์ฌ ํ์๊ณผ ๋ฆฌ์์ค ์์ฒญ์ ๊ฐ๋ก์ฑ ์์ ํ๊ณ , ๋ฆฌ์์ค๋ฅผ ๊ต์ฅํ ์ธ๋ถ์ ์ผ๋ก ์บ์ฑํ๋ค.
โ ์น ์ฑ์ด ์ด๋ค ์ํฉ์์ ์ด๋ป๊ฒ ๋์ํด์ผ ํ๋์ง ์๋ฒฝํ๊ฒ ๋ฐ๊ฟ ์ ์๋ค. (๋ํ์ ์ธ ์ํฉ์ ๋คํธ์ํฌ๋ฅผ ์ฌ์ฉํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ)
MSW(Mock Service Worker)
๐ MSW ์ฌ์ฉ ๋ฐฐ๊ฒฝ(feat.Mocking)
ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ์งํํ๋ค๋ณด๋ฉด ๋ฐฑ์๋ ๊ฐ๋ฐ๊ณผ์ ์ข ์์ ์ธ ๋ถ๋ถ์ด ์๊ธฐ๊ธฐ ๋ง๋ จ์ด๋ค. (๋ํ์ ์ผ๋ก ๋ฐฑ์๋์ API๋ฅผ ํ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ)
๊ธฐํ์ : XX ์์ ์ ์ด๋ป๊ฒ ์งํ ์ค์ธ๊ฐ์? ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ : ๊ทธ๊ฒโฆ ์์ง API๊ฐ ์ค๋น๋์ง ์์์ ๋ค์ ์ฃผ๊น์ง๋ ๊ธฐ๋ค๋ ค์ผ ํฉ๋๋ค.....
์์ํ ๊ธฐ๊ฐ๋ณด๋ค API ๊ฐ๋ฐ์ ์๊ฐ์ด ๋ ํ์ํด์ง ๊ฒฝ์ฐ, ๊ทธ ์๊ฐ๋งํผ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๊ฐ ๊ฐ๋ฐ์ ์งํํ์ง ๋ชปํ๋ ์ํฉ์ด ์๊ฒจ๋๊ธฐ๋ ํ๋ค.
๊ณ์ ๊ธฐ๋ค๋ฆด ์๋ ์๊ธฐ์ Mocking์ ํตํด ์์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์ ํ๋ค.
Thinking in React ์์ ๋ฅผ ํตํด API ์์ฒญ ์ฝ๋ ๋ชจํน์ฒ๋ผ ์ง์ ์ ์ผ๋ก ๋ด๋ถ ๋ก์ง์ ์ง์ Mockingํด์ ํ์ํ ํ๋ฉด์ ๋ถ์ด๋ ๋ฐฉ์์ด ์์ง๋ง,
์๋น์ค ๋ก์ง์ ์ง์ Mocking์ ํด์ผ ํ๋ฏ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์๋น์ค ๋ก์ง์ ์์ ํ์
HTTP ๋ฉ์๋์ ๋คํธ์ํฌ์ ์๋ต ์ํ์ ๋ฐ๋ผ ๊ฐ๊ฐ ๋์ํ๊ธฐ๊ฐ ์ฝ์ง ์๋ค.
(Mocking์ผ๋ก ๋ง๋ ๊ฒฐ๊ณผ๋ฌผ)ํ๋ฉด์ ๋ํ ํ ์คํ ๋ฐ ๋๋ฒ๊น ์์ ์ด๋ ค์์ด ๋ฐ์ .....
๐ก ๊ฒฐ๊ตญ ์ค์ API๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ฒ๋ผ ๋คํธ์ํฌ ์์ค์์ Mocking ํ๊ธธ ์ํ๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ ์์ฒญ ๊ณผ์ ์์ Request์ ๋ํ Mocking์ด ๊ฐ๋ฅํ MSW๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
๐ MSW๋ ๋ฌด์์ธ๊ฐ?
Mock Service Worker์ ์ฝ์
API Mocking ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๋คํธ์ํฌ ์์ฒญ์ ๊ฐ๋ก์ฑ์ ๋ชจ์ ์๋ต(Mocked response)์ ๋ณด๋ด์ฃผ๋ ์ญํ ์ ์ํ
โ๏ธ MSW ์ค์น ๋ฐ ์ค์
1. MSW ํจํค์ง ์ค์น
npm i -D msw@0.36.4
2. jest.config.js
ํ์ผ์ โsetupFilesAfterEnvโ ์์ฑ์ setupTests.ts ํ์ผ ์ถ๊ฐ
jest.config.js
ํ์ผ์ โsetupFilesAfterEnvโ ์์ฑ์ setupTests.ts ํ์ผ ์ถ๊ฐ// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: [
'@testing-library/jest-dom/extend-expect',
'<rootDir>/src/setupTests.ts', //๐๐ป ์ด๋ถ๋ถ์ถ๊ฐ
],
3. setupTests.ts
ํ์ผ ์์ฑ
setupTests.ts
ํ์ผ ์์ฑtouch src/setupTests.ts
// src/setupTests.ts
import server from './mocks/server';
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterAll(() => server.close());
afterEach(() => server.resetHandlers());
beforeAll : Jest ์์ํ ๋ ๋งจ ์ฒ์์ ์คํ
server.listen : ํ ์คํธ๊ฐ ์คํ๋๊ธฐ ์ ๋ชจํน ํ์ฑํ
onUnhandledRequest: 'error'
: handler๋ฅผ ์ ์ก์์ ๋ ์ค๋ฅ ๋ด๋๋ก ์ค์
afterAll : ์ ๋ถ ๋๋ ๋ ์คํ
server.close: ๋ชจ๋ ํ ์คํธ ์คํ ๋ ํ ๋ค์ดํฐ๋ธ ์์ฒญ ๋ฐํ ๋ชจ๋ ๋ณต์(?)
afterEach : ๊ฐ ํ ์คํธ๊ฐ ๋๋ ๋๋ง๋ค ์คํ
server.resetHandlers : ํ ์คํธ ์ฌ์ด์ ๋ชจ๋ ์์ฒญ ํธ๋ค๋ฌ๋ฅผ ์ฌ์ค์
4. ๐ src/mocks/ server.ts
ํ์ผ ์์ฑ
server.ts
ํ์ผ ์์ฑmkdir src/mocks
touch src/mocks/server.ts
// src/mocks/server.ts
import { setupServer } from 'msw/node';
import handlers from './handlers'
const server = setupServer(...handlers);
export default server;
5. ๐ src/mocks/ handlers.ts
ํ์ผ ์์ฑ
handlers.ts
ํ์ผ ์์ฑtouch src/mocks/handlers.ts
// src/mocks/handlers.ts
import { rest } from 'msw';
const BASE_URL = 'http://localhost:3000';
const handlers = [
rest.get(`${BASE_URL}`, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({ products }),
);
}),
];
export default handlers;
๐ฉ๐ปโ๐ป Thinking in React ์์ ๋ฅผ ํตํด REST API ๋ชจํนํ๊ธฐ
1. MSW ์ค์น ๋ฐ ํ์ผ ์์ฑ ๋ฐ ์ค์ ์๋ฃ
โโโ package.json
โโโ package-lock.json
โโโ jest.config.js โ
โโโ src
โ โโโ App.tsx
โ โโโ App.test.tsx โ
โ โโโ setupTests.ts โ
โ โโโ hooks ๐
โ โ โโโ useFetchProducts.ts
โ โโโ mocks ๐
โ โ โโโ handlers.ts โ
โ โ โโโ server.ts โ
2. Thinking in React ์์ ์ Mock Date ๊ธฐ์ค์ผ๋ก handlers.ts
์์ฑ
handlers.ts
์์ฑ// 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),
ctx.json({products}), // ์์ ์์ฒญ์ ์ด ํํ๋ก ๋ฐํ
);
}),
];
export default handlers;
App.test.tsx
์์
jest.mock ์ ๊ฑฐ
waitFor ์ถ๊ฐ (~ ๊ฐ ๋ ๋๊น์ง ๋๊ธฐํ๋ ์ํ)
์ฝ๋ฐฑํจ์์ ํ์ ์ด Promise๋ก ๋์ด ์์ด์ async/await ํ์
// App.test.tsx
import {render, screen, waitFor} from '@testing-library/react';
import App from './App';
// jest.mock ๋ถํ์
// jest.mock('./hooks/useFetchProducts', () => () => fixtures.products);
//jest.mock('./hooks/useFetchProducts');
test('App', async () => {
render(<App / >);
await waitFor(() => {
screen.getByText('Apple');
});
});
4. ๐จ Error ๋ฐ์
// hooks/useFetchProducts.ts
export default function useFetchProducts() {
const url = 'http://localhost:3000/products';
const {data, error} = useFetch<ProductsResult>(url);
console.log({error}); // ๐๐ป ์ถ๊ฐํด์ ํ์ธํด๋ณด๋, Error
if (!data) {
return [];
}
return data.products;
}
ReferenceError:
fetch is not defined
๋ฐ์
node.js ํ๊ฒฝ์์ ํ ์คํธ๋ฅผ ์งํ ์ค์ด์๋ค. ์ต์ ๋ฒ์ ์ node๋ fetch๋ฅผ ์ง์ํ์ง๋ง, ํ์ฌ ์ฌ์ฉ ์ค์ธ ๋ฒ์ ์ Node๋ ์ง์ํ์ง ์์ ๋ํ๋ error! (๐ก Fetch๋ ๋ธ๋ผ์ฐ์ [window]์์๋ง ์ง์)
5. Github์์ ๋ง๋ Fetch Polyfill(ํด๋ฆฌํ)
์ ์ฌ์ฉํด ํด๊ฒฐ
Polyfill(ํด๋ฆฌํ)
์ ์ฌ์ฉํด ํด๊ฒฐPolyfill ์ค์น window.fetch polyfill
npm i -D whatwg-fetch
setupTests.ts
ํ์ผ ์ต์์์ import๋ก ์ ์ฉ
import 'whatwg-fetch'
๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํ์ง ์๋ ์ด์ ๋ธ๋ผ์ฐ์ ์์ ์ต์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๋ฐ ํ์ํ ์ฝ๋
์๋ก์ด ๋ฌธ๋ฒ์ ๋ฎ์ ๋ฒ์ ์์๋ ๋์๊ฐ ์ ์๊ฒ ํ๊ธฐ ์ํ์ฌ ๋ง๋ ์ฝ๋
๐ ์ฐธ๊ณ ํ ์ ์๋ ์์?
๋ฐ๋ฒจ๊ณผ ๊ฐ์ ํธ๋์ค ํ์ผ๋ฌ
์ต์ ์คํ์ผ๋ก ์์ฑ๋ ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ๋ช ์ํ ๋ฒ์ ์ ๋ฐ๋ผ ์ฌ์์ฑ ํด์ค๋๋ค. ๋ฎ์ ๋ฒ์ ์ ์๋ฐ์คํฌ๋ฆฝํธ ์์ง์์๋ ํ๋ก๊ทธ๋จ์ด ๋์๊ฐ ์ ์๊ฒ ํด์ฃผ์ด ์๋น์ค๊ฐ ๋ฌธ์ ์์ด ์ ๊ณต๋ ์ ์๊ฒ ํด์ค๋๋ค. ๋ฐ๋ฒจ์๋ core-js ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ฌ๋์ด es6 ์ดํ์ ๋ฌธ๋ฒ๋ค์ ํด๋ฆฌํ์ ์ด์ฉํ ์ ์์ต๋๋ค.
๐ ์ฐธ๊ณ
Last updated