PROJECT/TO DO LIST

MSW(Mock Service Worker) 연결하기

셈인 2024. 3. 20. 21:58

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 깃헙과 디스코드에도 문의했지만 아직 답변을 받지 못했는데, 혹시 해결방법이나 제 코드에 문제가 있다면 얘기 부탁드립니다🥲