아직도 한땀한땀 MockData 만든다고!? 이제 「Mock Service Worker」 쓰자 🤡
What is Mock Service Worker !?
테스팅 서버를 직접 만들지 않고도 서버를 모킹할 수 있는 가장 좋은 방법, msw!
통상적으로 data fetch를 해야하는 경우 통신을 통해 응답을 내려주는 서버가 있어야 한다. 완전하게 서버가 구축되지 않은 단계에서, 프론트엔드 개발자들은 API 호출과 관련된 테스팅을 진행하기 위해 API를 모킹해주는 과정을 거친다. 그리고 이 과정은 여간 번거로운 작업이 아니다. 물론 따로 모킹 서버를 만들어줄 수도 있지만, 이는 더욱 번거로운 작업이다. 이러한 개발자의 고충을 msw
가 해결해준다.
msw는 어떻게 API 모킹을 돕는 것일까!
Mock Service Worker
라는 이름에서 알 수 있듯이 msw
는 Service Worker
를 활용한다. Service Workder API
란 놀랍게도, 모든 모던 브라우저에서 제공하는 표준 API이다! MDN 공식문서를 살펴보면 서비스워커에 대한 자세한 개념을 살필 수 있다.
msw
를 학습하는데 있어 중요한 포인트만 짚어보자면, Service Worker는 일종의 대리 서버라고 할 수 있다. Service Worker를 활용하면, 리소스 요청을 가로채 수정할 수 있고, 리소스를 세부적으로 캐싱해 조작할 수 있다. 즉, 네트워크를 사용하지 못할 때에도 개발자가 개발하고 있는 웹앱이 어떻게 동작해야 하는지 조작할 수 있다는 것이다!
msw
의 컨셉 역시 Service Workder API
와 크게 다르지 않다!
msw
공식페이지에서 제공하는 아래의 이미지를 살펴보자.
브라우저가 보낸 request를 Service Worker가 가로챈 후 msw를 통해서 모킹된 response를 돌려주고 있는 모습을 확인할 수 있다.
Mock Service Worker is an API mocking library that uses Service Worker API to intercept actual requests.
이제 위와 같은 공식문서의 msw
에 대한 설명을 문제없이 이해할 수 있을 것이다.
즉, msw
는 유저의 request를 서버까지 보내지 않고 네트워크레벨에서 인터셉트해 임의의 데이터를 응답해주는 라이브러리라고 요약해볼 수 있겠다!
msw를 사용해보자!
그렇다면 msw
를 사용해보자! 공식문서를 살펴보면 msw
는 react와 graphql모두 대응한다는 것을 알 수 있다. 또, Rest API/GraphQL까지 모두 모킹이 가능하다!
오늘 이 포스팅에서는 React를 통해 msw
를 활용해보도록 한다!
페북 가라사대 태초에 npx create-ract-app
이 있었나니
$ npx create-react-app msw-practice
우선 위와 같은 식으로 react 프로젝트를 하나 시작해준다. (물론 프로젝트 이름은 재량껏 지으면 된다.)
msw를 인스톨해주자!
$ npm install msw --save-dev
# or
$ yarn add msw --dev
이렇게 인스톨을 해주고 나서 package.json
을 확인하면 개발 의존 모듈에서 msw
를 확인해볼 수 있다.
mocks 폴더를 생성해주자!
src
폴더 안에 mocks
폴더를 생성해주자.
mocks
폴더 내부에 필요한 파일은 두 가지이다! (만일 node.js 실행환경에서 실행할 것이라면 한 가지가 더 필요하나 이 포스팅에서는 생략, 해당 부분은 공식홈페이지 메인 화면에서 소개되는 example 코드를 통해 확인할 수 있다.)
mocks 폴더 내부에 handlers.js를 생성해준다.
handlers.js
는 마치 서버에서 컨트롤러를 작성해주는 것과 비슷하게 생각해도 좋다.
브라우저에서 날아온 request를 인터셉트했을 때 어떤 엔드포인트와 요청에 따라 어떤 결과를 돌려줄지 미리 작성해주는 과정이다.
// graphql을 이용하려면, graphql을 꺼내오면 된다.
// 현재 여기서는 restAPI로 실습하기 위해 rest를 꺼내왔다.
import { rest } from "msw";
export const handlers = [
rest.get("https://pokeapi.co/api/v2/pokemon/", async (req, res, ctx) => {
return res(
ctx.json({
count: 1118,
next: "https://pokeapi.co/api/v2/pokemon/?offset=20&limit=20",
previous: null,
results: [
{
name: "이상해씨",
url: "https://pokeapi.co/api/v2/pokemon/1/",
},
{
name: "이상해풀",
url: "https://pokeapi.co/api/v2/pokemon/2/",
},
{
name: "이상해꽃",
url: "https://pokeapi.co/api/v2/pokemon/3/",
},
{
name: "파이리",
url: "https://pokeapi.co/api/v2/pokemon/4/",
},
{
name: "리자드",
url: "https://pokeapi.co/api/v2/pokemon/5/",
},
{
name: "리자몽",
url: "https://pokeapi.co/api/v2/pokemon/6/",
},
{
name: "꼬부기",
url: "https://pokeapi.co/api/v2/pokemon/7/",
},
{
name: "어니부기",
url: "https://pokeapi.co/api/v2/pokemon/8/",
},
{
name: "거북왕",
url: "https://pokeapi.co/api/v2/pokemon/9/",
},
],
})
);
}),
];
간단하게 포켓몬에 대한 정보를 불러오는 연습을 해보기 위해 위와 같이 작성했다.
그리고 나서, browser.js 파일을 작성해준다.
browser.js
에서는 msw에서 제공하는 서비스워커를 가져와서 위에서 작성한 handlers를 인자로 넘겨준다.
import { setupWorker } from "msw";
import { handlers } from "./handlers";
export const worker = setupWorker(...handlers);
여기서 끝이 아니다! msw 초기화를 진행해주자!
이렇게 파일만 작성해준다고 바로 msw를 사용할 수 있는 것은 아니다.
공식문서를 차근차근 살펴보면, 아래와 같은 명령어로 cli에서 init을 해줘야 한다는 것을 알 수 있다.
$ npx msw init <PUBLIC_DIR> --save
PUBLIC_DIR은 어떤 프레임워크 혹은 라이브러리를 사용하고 있느냐에 따라 다른데, 우리는 리액트를 사용하고 있으므로 public/
을 입력해주면 된다!
이제 API를 요청해보기 위해서 컴포넌트를 작성해주자!
아래와 같이 components 폴더 내부에 PokemonInfo라는 컴포넌트를 만들어준다!
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const PokemonCard = ({name, url}) => {
const [photoUrl, setPhotoUrl] = useState("");
useEffect(() => {
axios.get(url)
.then(res => {
setPhotoUrl(res.data.sprites.front_default);
})
.catch(error => {
console.log(error)
})
})
return (
<div>
<img src={photoUrl} alt={`${name}-front-default`} />
<h3>{name}</h3>
</div>
)
}
const mainUrl = "https://pokeapi.co/api/v2/pokemon/"
export default function PokemonList() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const handleClick = () => {
axios.get(mainUrl)
.then(response => {
setData(response.data);
})
.catch(error => {
setError(`Something Wrong: ${error}`);
})
}
if(error) {
return <p>{error}</p>
}
return (
<div>
<button onClick={handleClick}>포켓몬 정보 조회하기</button>
{data && (
<div>
{data.results.map((pokemon) => {
return <PokemonCard
key={`${pokemon.name}-${pokemon.url}`}
name={pokemon.name}
url={pokemon.url}/>
})}
</div>
)}
</div>
)
}
그럼 이제, 프로젝트를 실행시켜보자!
어딘가 이상하다! 분명 handlers.js
에서 우리는 한글로 포켓몬들의 이름을 작성해주었었다.
게다가 네트워크 탭을 확인해보아도 pokemon에 대한 response가 서비스 워커로부터 오고 있지 않다는 것을 알 수 있다. 현재 response는 PokeAPI에서 오고 있는 것이다! 서비스워커가 요청을 인터셉트하지 않았다는 것이다.
index.js로 이동!
우리가 놓친 마지막 스텝은 바로, index.js
에서 서비스워커를 실행시켜주는 것이다!
//Start the mocking conditionally/
if (process.env.NODE_ENV === "development") {
const { worker } = require("./mocks/browser");
worker.start();
}
index.js
에서 render 윗 부분에 위와 같은 코드를 추가해준다!
다시 한 번 프로젝트를 실행하고, API 요청을 보내면!
이렇게나 앙증맞고 귀여운 포켓몬들을 확인할 수가 있다!
이에 더해 네트워크 탭을 열어보면 상태코드 옆에서 from service worker
라는 안내 문구를 확인할 수 있다.
이 얼마나 놀라운 일인가!
프론트엔드 지망 부트캠프 수강생들이여!
이제 프로젝트 기획이 끝나고 백엔드 동료들이 API 구축해주길 하염없이 기다리지 말고,
localhost로 url 적어주었다가 다시 다~ 나중에 수정해주지 말고!
테스트 코드에 목업 데이터 일일이 만들어주지 말고!
msw
를 활용하자!
Author And Source
이 문제에 관하여(아직도 한땀한땀 MockData 만든다고!? 이제 「Mock Service Worker」 쓰자 🤡), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@9rganizedchaos/これがリアクトライブラリ!-「Mock-Service-Worker」저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)