2023.10.23 MSW 2.0 버전이 릴리즈되었다. 이 글은 2.0을 기반으로 쓰여졌으며,
Vite,React,TypeScript,tailwindcss 를 사용하였다.
프론트엔드 개발자라면 한번 쯤은 API가 아직 안나와서 개발 진행이 더뎌지거나 멈췄던 경험이 있을 것이다.
그럴 때 백엔드 API가 나오기 전 Mocking을 통해 사전 개발을 진행 할 수 있는데, 간단하게는 json 파일에 임시 데이터를 사용하거나 json-server, mock-server 등을 통해 진행할 수 있다.
하지만 실제 API가 아니기에 실제 화면을 구성하고자하는 구현 단계에는 케이스별로 임의의 상태를 만들어 보면서 개발하거나 그 자체를 디버깅하기에 어려움이 있을 수 있다.
😎 바로 MSW를 통해 이를 해결할 수 있다.
Mock Service Worker는 서비스 워커를 이용하여 API를 모킹하는 라이브러리로, 사용법이 간단하고 모킹 서버를 따로 띄울 필요가 없다는 장점을 가지고 있다.
MSW는 서비스 워커를 사용하여 네트워크 호출을 가로챈다. API 요청을 가로채서 사전에 설정해둔 목업 데이터를 넘겨주도록 설정해 주는 도구라고 생각하면 된다. 주로 아래에 경우 사용된다.
모킹이 네트워크 단에서 일어나므로 프론트엔드 코드를 실제 백엔드 API와 통신하는 것처럼 작성할 수 있다. -> 대체가 쉬워 개발 생산성 증가!
동일한 요청 핸들러(handler) 코드를 여러 환경(브라우저 환경, Node.js 환경,Jest나 Cypress)에서 공유해서 사용가능하다. -> 코드 최소화, 유지 보수성
REST API, GraphQL API 둘 다 지원한다.
npm install msw --save-dev
#or
yarn add msw --dev
devDependencies
로 설치하기는 필수!npx msw init public/
public/
: 프로젝트에서 정적 리소스를 두는 폴더로 지정해준다.요청이 들어왔을 때 임의의 응답을 해주는 핸들러(handler)
를 먼저 만들어보자.
간단한 투두리스트로 예제를 구현해보았다.
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';
export type Todo = {
id: number;
name?: string;
};
const todos: Todo[] = [
{ id: 1, name: '할일1' },
{ id: 2, name: '할일2' },
{ id: 3, name: '할일2' },
];
export const handlers = [
// TODOLIST - GET
http.get('/todos', () => {
return HttpResponse.json(todos, {
status: 201,
headers: {
'Content-Type': 'application/json',
},
});
}),
// TODOLIST ADD - POST
http.post('/todos', async ({ request }) => {
const requestData = await request.json();
const name = requestData?.toString();
const newTodo = { id: todos.length + 1, name };
todos.push(newTodo);
return HttpResponse.json(todos, { status: 201 });
}),
];
HttpResponse
사용 방법을 보려면 공식문서를 참고하자.// src/mocks/browser.ts
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'
export const worker = setupWorker(...handlers)
msw/browser
모듈에서 제공하는 setupWorker()
함수를 사용해서 서비스 워커를 생성한다.// /src/index.tsx or main.tsx(vite)
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
async function enableMocking() {
// 일반적인 경우
// if (process.env.NODE_ENV !== 'development') {
// vite의 경우
if (!import.meta.env.DEV) {
return;
}
const { worker } = await import('./mocks/browser.ts');
// 서비스 워커 시작
return worker.start();
}
enableMocking().then(() => {
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
});
[MSW] Mocking enabled.
GET /todos
를 호출 TODOLIST 가져와서 화면에 그려준다.POST /todos
를 호출한다.import { useEffect, useState } from 'react';
import { Todo } from '../mocks/handlers';
export default function App() {
const [todos, setTodos] = useState<Todo[]>([]);
const [todo, setTodo] = useState('');
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch('/todos')
.then((res) => res.json())
.then((data) => {
setTodos(data);
setLoading(false);
});
}, []);
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setLoading(true);
fetch('todos', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(todo),
}).then((res) => {
// todo 추가 후 바로 새리스트 불러오기
fetch('/todos')
.then((res) => res.json())
.then((data) => {
setTodo('');
setTodos(data);
setLoading(false);
});
});
};
return (
<div>
<h2 className='text-xl'>TODOLIST</h2>
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.name}</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input
type='text'
name='todo'
placeholder='새로운 할일'
disabled={loading}
value={todo}
onChange={({ target: { value } }) => setTodo(value)}
/>
<button disabled={!todo}>추가</button>
</form>
</div>
);
}
MSW로 무한 스크롤 연습하려고 만들다가 MSW에 대한 포스팅까지 하게되었다. 평소 json파일로 간단한 모킹 데이터로만 사용하고 있었는데 앞으로 MSW를 자주 사용하게 될 것같다.
무엇보다 백엔드에 의존하지 않고 프론트엔드 개발을 할 수 있고, 진짜 API로 바꿀 때 코드 수정이 거의 없어서 확실히 생산성이 좋은 것 같다.
또한 추후에 테스트 코드 작성 시에도 활용해보고 블로그에 정리해볼 예정이다.