개발/아티클

MSW(Mock Service Worker)를 사용한 네트워크 단 API Mocking 테스트

devmomori 2022. 8. 23. 03:46

MSW(Mock Service Worker)

MSW서비스 워커(Service Worker)를 사용하여 실제 요청(Requests)을 중간에서 가로채 API를 Mocking 해주는 라이브러리이다. 기존의 라이브러리들과는 다르게 네트워크 단에서 실제로 요청을 주고받으며 테스트를 진행할 수 있는 장점이 있으며, REST API 및 GraphQL 모두 지원하고 있다.

 

프런트엔드 개발에서 Mock API는 떼려야 뗄 수 없는 부분인 것 같다. 개발을 위한 목업 데이터 만들고 관련 로직과 컴포넌트를 만든다. API 개발을 기다리며 타입을 작성하고, 여러 가지 테스트를 진행한다. 여러 이슈들은 대부분이 잘못된 API를 호출하거나 에러 처리를 잘못하였을 때 발생하기 때문에 Mock API를 사용하여 디버깅을 해보는 것이 도움이 된다.

 

또한, 운영에서 사용될 API 개발이 늦어지는 경우에도 이러한 목업 데이터를 활용하여 개발 일정이 최대한 병렬적으로 수행될 수 있도록 하는 것이 생산성을 높이는 데 도움을 주는 것 같다. MSW는 별도의 목업 서버(JSON Server, etc.)를 구축하지 않아도 되고 실제 네트워크를 사용해 볼 수 있으며, API Mocking 로직들을 관리하는 데 좀 더 편한 것이 큰 장점으로 다가오는 것 같다.

 

작성된 코드는 해당 레포를 참고하시면 됩니다.

 

GitHub - somedaycode/msw-react-tutorial: MSW(Mock Service Worker)를 사용한 API Mocking Tutorial

MSW(Mock Service Worker)를 사용한 API Mocking Tutorial - GitHub - somedaycode/msw-react-tutorial: MSW(Mock Service Worker)를 사용한 API Mocking Tutorial

github.com


기존의 API Mocking

기존의 API Mocking은 fetch 혹은 axios를 Jest.mock을 사용하여 진행하고는 했다. 실제로는 네트워크 요청이 일어나지 않으며 mockImplementation과 같은 Jest 내부 메서드를 사용하여 요청과 응답이 일어난 것처럼 테스트를 작성했다. 

  import mockAxios from "jest-mock-axios";
  // mockAdapter from "axios-mock-adapter";
  // const mockAxios = new MockAdapter(axios);
  import fetchUsers from './api';

  it("유저목록을 반환한다.", async () => {
      const users = [
        { id: 1, name: "somedaycode" },
        { id: 2, name: "devmomori" },
      ];
      mockAxios.get.mockResolvedValueOnce(users);

      const result = await fetchUsers();

      expect(mockAxios.get).toHaveBeenCalledWith(`/users`);
      expect(result).toEqual(users);
    });
테스트코드가 늘어나면서 API Mocking을 위해서 mockAxios, MockingPromise와 같은 유틸 함수를 작성해서 관리하는 부분이 꽤 번거롭고 불편하게 느껴진다. 또한, 실제로 네트워크의 요청을 주고받는 것이 아니다 보니, HTTP 상태나 Error 처리에 대한 부분을 확인하기 위해서 추가적으로 작성되는 코드들이 많아지기도 하였다. 

 

MSW의 요청과 응답은 다음과 같이 이루어진다.

브라우저의 MSW

실제로도 mockServiceWorker.js 파일을 까 보면 다음과 같이 동작하는 것을 확인할 수 있다.

// public/mockServiceWorker.js

self.addEventListener('fetch', async function (event) {
  const { clientId, request } = event
  const requestClone = request.clone() // 2. 요청 복제
  const getOriginalResponse = () => fetch(requestClone)
  
  ...more
}

// MOCK_SUCCESS 확인
switch (clientMessage.type) {
  case 'MOCK_SUCCESS': {
    setTimeout(
      esolve.bind(this, createResponse(clientMessage)),
      clientMessage.payload.delay,
    )
   break
   
   ...more
}

 

설치

// MSW 패키지 설치
yarn add msw --dev

// 기존 프로젝트 Public 폴더에 서비스워커 생성
npx msw init <PUBLIC_DIR> --save
// npx msw init public/ --save

다음과 같이 설치를 완료하면 mockServiceWorker.js 파일이 public 폴더에 만들어진다.

 

API Mocking 준비

1. '/users' HTTP GET 요청을 위한 핸들러 함수 작성

HTTP GET /users 데이터를 받기 위한 코드 작성

 

2. 브라우저에서 API Mocking을 위한 Worker 셋업

브라우저에서 msw 사용을 위한 코드

 

3. API Mocking 응답을 활용하기 위한 Users 컴포넌트 작성

유저 목록을 불러오기 위한 User.tsx

 

4. 렌더링

index.tsx 파일에 다음과 같이 코드를 작성해줍니다.

Index.tsx

실행 결과

해당 리액트 앱을 실행시키면 개발자도구에서 다음과 같은 화면을 볼 수 있습니다.

개발자 도구 확인

 

Application -> Service Worker로 들어가 서비스 워커가 잘 설치되었는지도 확인해봅니다.

install Service Worker

 

 


 

테스트 화면

이제 한번 버튼을 눌러 API Mocking 된 응답이 잘 들어오는지 확인해봅니다.

 

유저 목록을 불러올 때 서비스워커를 통해 네트워크 요청을 보내고 위에서 설정한 응답 값을 화면에 뿌리는 것을 확인할 수 있습니다.

 

 

유저 목록을 불러오는 GIF
네트워크 탭 응답 화면

네트워크 탭도 확인화면 Status Code 옆에 (from service worker)라고 표시되어 있는 것을 확인할 수 있습니다.

 

 

느낀 점

실제로도 MSW를 이용한 API Mocking은 러닝 커브가 낮았고, 간편하고 쉽게 API Mocking을 진행해 볼 수 있었습니다. 보다 쉽게 네트워크 단에서 API의 호출과 응답을 테스트 할 수 있다는 점이 애플리케이션 내부에 목업을 만들고 테스트를 진행하는 것에 비해 굉장히 매력적으로 느껴집니다. 더불어 이전에 테스트 코드를 진행하며 리소스를 찾는 데 어려움이 있었던 것에 비해서 MSW는 Github에 여러가지 example 보일러 플레이트가 많기 때문에 다양한 프레임워크에서의 동작도 쉽게 확인할 수 있었습니다.  vercel/Next.js의 MSW Example도 있으니 확인해보면 좋겠습니다.

 

 

참고문서

 

코드 확인