MSW (Mock Service Worker) 란?
Mock Service Worker는 Service Worker API를 사용하여 실제 네트워크 요청을 가로채는 API Mocking 라이브러리입니다. 웹에서 발생하는 실제 HTTP 요청을 가로채, 사용자가 정의한 응답을 Mocking 합니다. 이 과정은 네트워크 레벨에서 이루어지기 때문에, 별도의 코드 수정 없이 실제 서비스와 유사한 환경을 조성할 수 있습니다.
► Mocking이 개발 과정에서 어떻게 도움이 되는가?
개발 과정에서 Mocking은 여러 면에서 도움이 됩니다.
첫째, 개발 초기 단계에 백엔드 API가 완성되지 않은 경우, 백엔드 API에 의존하지 않고 자체적으로 Mocking을 통해 개발을 진행할 수 있습니다. 이를 통해 개발 시간을 단축할 수 있습니다.
둘째, 브라우저와 노드 환경에서 모두 사용할 수 있어, 개발과 테스트 환경에서 동일한 네트워크 응답을 보장할 수 있습니다. 이를 통해 다양한 환경에서의 일관된 동작과 테스트 결과를 보장해, 테스트 결과의 신뢰성을 높일 수 있습니다.
MSW 설치 및 설정 방법
설치 및 설정 방법은 공식 문서에 있는 순서로 진행했습니다.
1. 설치
npm 혹은 yarn 사용해 설치하는 방법
npm install msw --save-dev
yarn add msw --dev
2. mock 정의
: MSW로 작업할 때, request handler 목록, 브라우저 및 서버별 설정
⚠️ API mocking 관련 모듈을 단일 디렉터리에 유지하는 것이 좋습니다. (공식 문서)
// scr/mocks 파일 생성
mkdir src/mocks
// src/mocks/handlers.js 파일 생성
touch src/mocks/handlers.js
(위의 명령어를 사용하거나 직접 생성가능합니다.)
► API Mocking 하는 방식 선택하기
- REST API
- GraphQL API
저는 좀 더 익숙한 REST API 방식을 선택했습니다. handlers의 역할 소개 및 코드를 작성해 보겠습니다.
► Request handler
handlers.js에서 REST API 요청을 처리하고, mock response를 반환합니다.
src/mocks/handlers.js 코드 (공식 문서 예시 코드)
import { rest } from 'msw'
export const handlers = [
rest.post('/login', (req, res, ctx) => {
sessionStorage.setItem('is-authenticated', 'true')
return res(ctx.status(200))
}),
rest.get('/user', (req, res, ctx) => {
const isAuthenticated = sessionStorage.getItem('is-authenticated')
if (!isAuthenticated) {
return res(
ctx.status(403),
ctx.json({
errorMessage: 'Not authorized',
}),
)
}
return res(
ctx.status(200),
ctx.json({
username: 'admin',
}),
)
}),
]
이 코드에서는 사용자의 기본 로그인 요청을 mocking 합니다.
- POST /login - 사용자가 로그인을 요청
- GET /user - 로그인한 사용자에 대한 정보를 반환
Rest [METHOD]를 호출하고 요청 경로를 제공하여 요청 핸들러를 생성합니다. (req, res, ctx) => {...} 형태의 코드 안에 백엔드에서 수행되는 로직을 작성하고 결과를 리턴하는 역할을 한다고 볼 수 있습니다.
Rest [METHOD]의 종류는 다음과 같습니다.
- rest.get() / rest.post() / rest.put() / rest.patch() / rest.delete() (HTTP Methods)
- rest.options()
- rest.all()
► Response resolver
요청에 응답하려면, 응답 확인 기능을 사용하여 mock response를 지정해야 합니다. 함수의 인수들은 다음과 같습니다.
- req : 매칭되는 요청(request)에 대한 정보
- res : mock response를 생성하는 기능적 유틸리티
- ctx : mock response의 상태 코드, 헤더, 본문 등을 설정하는 데 도움이 되는 함수 그룹
3. 통합
- 개발 중에는 주로 브라우저 환경에서 작업하므로, 브라우저 환경에서의 Mocking이 더 일반적입니다.
- JEST 혹은 다른 라이브러리를 사용해 테스트 코드를 작성할 경우 mock 실행 위치를 Node로 해야 합니다.
- 브라우저와 노드 환경 간에 동일한 요청 핸들러를 공유할 수 있어, 둘 다 설정하는 것도 가능합니다.
► Mock 실행 위치 선택하기
- Browser
- Node
저는 일단 브라우저 환경에서의 mocking에 대해 설정해 보겠습니다.
// <PUBLIC_DIR>에 서버의 public directory 넣기
npx msw init <PUBLIC_DIR> --save
// 예시
npx msw init public/ --save
명령어를 실행하면 public 폴더에 mockServiceWorker.js 파일이 생성됩니다.
다음으로 서비스 워커를 생성하겠습니다.
browser.js 파일 생성 후, 코드를 작성했습니다.
src/mocks/browser.js
import { setupWorker } from 'msw'
import { handlers } from './handlers'
export const worker = setupWorker(...handlers)
msw 패키지에서 setupWorker 함수를 가져오고 이전에 정의된 요청 핸들러를 사용하여 작업자(worker) 인스턴스를 생성합니다.
위의 설정을 모두 완료한 후의 파일 구조입니다.
📦public
┣ 📜mockServiceWorker.js
📦src
┣ 📂mocks
┃ ┣ 📜browser.js
┃ ┗ 📜handlers.js
설정을 모두 완료한 후, MSW가 개발 모드일 경우에만 동작하도록 설정해 주겠습니다. 이렇게 설정하지 않은 경우 사용자 경험이 왜곡될 수 있으므로 공식 문서에서는 권장하지 않는다고 합니다.
프로젝트의 최상단 파일(index.js 혹은 App.js)에서 개발 모드 일 경우에만 worker를 활성화시키도록 설정하겠습니다.
src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { worker } from './mocks/worker';
if (process.env.NODE_ENV === 'development') {
worker.start()
}
ReactDOM.render(<App />, document.getElementById('root'))
모든 설정을 마친 후, 프로젝트를 실행했을 때 콘솔에 다음과 같은 메시지가 뜬다면, 활성화가 완료된 것입니다.!!😀🔥
트러블슈팅(Troubleshooting)
저의 경우에 개발 모드 일 경우에만 실행시키는 src/index.js의 코드에서 오류가 발생하였습니다.
저와 같은 오류가 발생한 사람들이 올린 이슈를 참고하여, 문제를 해결할 수 있었습니다. 이슈에서 알려준 것처럼 React 18 + Vite 4.1.x 버전의 프로젝트에서 Vite 버전을 업데이트하여 문제를 해결하였습니다.
프로젝트 적용
► 문제점
위에서 소개한 폴더 구조는 handlers.js에 모든 API 요청을 처리하는 코드와 반환할 mock response 값이 포함되게 됩니다. 규모가 크지 않은 프로젝트일 경우 혹은 API의 개수가 적은 프로젝트일 경우에는 이렇게 사용해도 큰 문제가 되지 않을 것입니다. 하지만, 프로젝트의 규모가 점점 커지고, API도 많은 프로젝트라면, 파일을 분할할 필요가 있습니다.
저는 MSW를 진행하고 있는 프로젝트에 적용하기 위해 폴더 구조를 다음과 같이 수정했습니다.
이 블로그를 참고하여 폴더 구조를 작성하였습니다.
📦mocks
┣ 📂api
┃ ┣ 📂data
┃ ┃ ┗ 📜dataResultData.js
┃ ┗ 📜dataResultHandler.js
┣ 📜browser.js
┗ 📜handlers.js
- api 폴더
- mocks/api/data 폴더 : API 호출 시 응답할 데이터
- mocks/api 폴더 : mocking 할 API 핸들러 모아두는 폴더
이렇게 폴더를 구분해 프로젝트의 기능이 증가할 경우에, 기능별로 핸들러 파일을 만들어, handlers.js 파일에 로직을 모두 작성하는 것을 피할 수 있고, 기능별로 데이터와 로직을 분리해, 필요한 코드를 찾기 쉽도록 하였습니다.
수정한 handlers.js 예시
import mainResultHandler from './api/mainResultHandler';
import userResultHandler from './api/userResultHandler';
export const handlers = [
...mainResultHandler,
...userResultHandler,
];
mainResultHandler.js 예시
import { rest } from 'msw';
import { mainResultData } from './data/mainResultData';
const mainResultHandler = [
rest.post('/main', (req, res, ctx) => {
return res(ctx.status(200), ctx.json(mainResultData));
}),
];
export default mainResultHandler;
mainResultData.js 예시
export const mainResultData = [
{
id: '1',
date: '2023-10-31 20:05'
...
},
...
];
마치며
저는 MSW를 활용하면서 백엔드 API가 완성되지 않은 시점에, 백엔드 API에 의존하지 않고 자체적으로 mocking을 통해 UI 개발을 진행할 수 있어서 개발 시간 단축에 정말 도움이 되었습니다. 또한 위에 설명한 내용 외에도 다양한 기능을 제공해서, 여러 기능에 대해 더욱 공부하면 MSW를 더욱 효율적으로 사용할 수 있을 것 같다고 생각했습니다.
'FE Study > React' 카테고리의 다른 글
[React] 무한 스크롤 구현 및 최적화 (Scroll Event) (0) | 2023.11.16 |
---|