MSW(Mock Service Worker) 연결하기
API 통신을 어떻게 구현할지 고민하던 중, 우아콘에서 봤던 MSW가 생각이 났다.
MSW는 백엔드 API가 구현되기 전, 프론트에서 사용할 Mockup 용으로 주로 이용된다.
MSW로 API와 통신하는 척을 할 수 있고, 요청 응답을 자유롭게 커스텀할 수 있어 이번 프로젝트에 채택하게 되었다.
MSW 공식 도큐먼트와 함께 사용법을 따라가보자.
1. 설치
npm install msw --save-dev
2. 초기화
npx msw init public/ --save
브라우저에서 MSW를 사용할 수 있도록 서비스 워커를 등록하는 커맨드
`npx msw init <PUBLIC_DIR>`
public 폴더의 주소를 적어주고, `mockServiceWorker.js`를 저장하기 위해 `--save`옵션을 추가한다.
2. 핸들러 등록
// src/mocks/handlers.ts
export const handlers = [];
// src/mocks/browsers.ts
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);
handler.ts는 intercept할 API들의 요청과 응답을 작성하는 곳이다. 작성법은 후술하겠다.
browsers.ts는 작성한 핸들러가 브라우저에서 동작할 수 있도록 설정하는 파일이다.
3. 서비스 워커 활성화
마지막으로 서비스 워커를 등록하고 활성화해준다.
서비스 워커 등록이 비동기 작업이므로, async/await을 사용해 앱 렌더링을 연기하는 게 좋다고 한다.
// index.ts
const root = document.getElementById('root') as HTMLElement;
async function enableMocking() {
  const { worker } = await import('./mocks/browser');
  return worker.start();
}
enableMocking().then(() => {
  ReactDOM.createRoot(root).render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
});
4. 핸들러 작성
MSW에서 제공하는 http 메소드를 이용해 쉽게 CRUD를 구현할 수 있다.
// src/mocks/handlers.ts
const todos: Array<ToDoDTO> = [
  {
    id: 1,
    text: 'TO DO LIST를 만들어보자!',
    checked: true
  }
];
export const handlers = [
  http.get<never, never, Array<ToDoDTO>>('/todos', () => {
    return HttpResponse.json(todos);
  }),
  http.post<never, ToDoDTO>('/todos', async ({ request }) => {
    const data = await request.json();
    const nextId = Math.max(...todos.map(todo => todo.id)) + 1;
    todos.unshift({
      id: nextId,
      text: data.text,
      checked: false
    });
    return new HttpResponse(null, { status: 201 });
  }),
  http.patch<UpdateToDoParams, ToDoDTO>('/todos/:id', async ({ params, request }) => {
    const data = await request.json();
    const findItem = todos.find(({id}) => id === Number(params.id));
    if (!findItem) {
      return new HttpResponse(null, { status: 404 });
    }
    findItem.checked = data.checked;
    return new HttpResponse(null, { status: 204 });
  }),
  http.delete<UpdateToDoParams>('/todos/:id', async ({ params }) => {
    const findItemIdx = todos.findIndex(({id}) => id === Number(params.id));
    if (0 > findItemIdx) {
      return new HttpResponse(null, { status: 404 });
    }
    todos.splice(findItemIdx, 1);
    return new HttpResponse(null, { status: 200 });
  })
];
5. 사용
handler에 등록한 path와 메소드를 조합하여 호출하면 성공적으로 응답을 받을 수 있다.
const fetchTodo = async () => {
  try {
    const res = await axios.get('/todos');
    setToDos(res.data);
  } catch (e) {
    console.error('error', e);
  }
};
Safari 이슈
axios와 msw를 동시에 사용했는데, Safari 환경에서 AxiosError - 'Request failed with status code 404' 오류가 발생하였다.
msw2.0으로 업데이트 된 지 얼마 안되어 정보가 많이 없어 오류를 고치지 못했다. (버전 자체에 버그가 많기도 하다..)
msw 깃헙과 디스코드에도 문의했지만 아직 답변을 받지 못했는데, 혹시 해결방법이나 제 코드에 문제가 있다면 얘기 부탁드립니다🥲
