📌 모킹(Mocking)이란?
Mock(모의 데이터)을 만들어서 활용하는 방식을 말한다.
통상적으로 data fetch를 해야 하는 경우 통신을 통해 응답을 내려주는 서버가 있어야 한다. 서버가 없는 경우, api 요청으로 내려올 데이터를 프론트에서 모킹하거나 서버의 역할을 해주는 무언가가 필요하다. 완전하게 서버가 구축되지 않은 단계에서, 프론트엔드 개발자들은 API 호출과 관련된 테스팅을 진행하기 위해 API를 모킹해주는 과정을 거친다.
📌 MSW.js
MSW(Mock Service Worker의 약자)는 API Mocking의 라이브러리로, 서버향의 네트워크 요청을 가로채서 모의 응답을 보내주는 역할을 한다. 따라서, MSW 라이브러리를 통하면, Mock 서버를 구축하지 않아도 API를 네트워크 수준에서 Mocking 할 수 있다. MSW가 이러한 역할을 할 수 있는 이유는 Service Worker를 통해 HTTP 요청을 가로채기 때문이다.
👇여기서 Service Worker란?
Service Worker는 웹 애플리케이션의 메인 스레드와 분리된 별도의 백그라운드 스레드에서 실행시킬 수 있는 기술 중 하나이다.
기존 API 모킹 라이브러리 대비 MSW의 가장 큰 강점은 모킹이 네트워크 단계에서 일어나기 때문에 프론트엔드 코드를 실제 백앤드 API와 네트워크 통신하는 것과 크게 다르지 않게 작성할 수 있다는 것이다. 이 말은 나중에 가짜 API를 실제 API로 대체하는 것이 쉽다는 뜻이며 그만큼 프론트엔드 프로젝트의 개발 생산성이 올라가는 것을 의미한다.
뿐만 아니라 라이브러리가 상당히 유연하게 디자인이 되어 있어서 개발용으로 브라우저 환경에서 Service Worker로 돌리든 테스트용으로 Node.js 환경에서 Jest나 Cypress와 같은 테스트 러너(runner)로 돌리든 동일한 요청 핸들러(handler) 코드를 공유해서 사용할 수 있다는 것이다. 이 말은 API 모킹을 위해서 작성해야 하는 코드를 최소화할 수 있다는 뜻이며 역시 개발 생산성뿐만 아니라 유지 보수성 측면에서도 긍정적인 효과를 가져올 수 있다.
브라우저가 보낸 request를 Service Worker가 가로챈 후
msw를 통해서 모킹된 response를 돌려주고 있는 모습을 확인할 수 있다.
📝 msw 라이브러리 설치
dev 버전으로 설치해준다.
npm install msw --save-dev
# or
yarn add msw --dev
📝 ServiceWorker 코드 생성하기
create-react-app 같은 경우는 아래 코드를 실행해준다. 그럼, public에 mockServiceWorker.js 파일이 생성된다.
MSW는 브라우저에서 서비스 워커를 통해서 작동하기 때문에 서비스 워커 등록을 위한 기본적인 코드가 필요한데 서비스 워커에 대해서 잘 몰라도 MSW에서 제공하는 CLI 도구를 사용하면 코드를 쉽게 만들어 낼 수 있다.
npx msw init public/ --save
📝 handler
가짜 API를 구현하려면 요청이 들어왔을 때 임의의 응답을 해주는 핸들러(handler) 코드를 작성해야 한다.
/src/mocks/handlers.js
import { rest } from "msw";
export const handlers = [
rest.get("/login", async (req, res, ctx) => {
return res(
ctx.json({
id: "f79e82e8-c34a-4dc7-a49e-9fadc0979fda",
firstName: "John",
lastName: "Maverick",
})
);
}),
// 서버의 요청 custom
rest.get(
"https://raw.githubusercontent.com/techoi/raw-data-api/main/simple-api.json",
async (req, res, ctx) => {
const id = req.url.searchParams.get("id");
const originalResponse = await ctx.fetch(req);
const originalResponseData = await originalResponse.json();
return res(
ctx.json({
data: {
people: [
...originalResponseData.data.people,
{
name: id,
age: 135,
},
],
},
})
);
}
),
];
📝 ServiceWorker 생성
msw 모듈에서 제공하는 setupWorker() 함수를 사용해서 ServiceWorker를 생성한다. 위에서 작성한 요청 handler 코드를 불러와서 그대로 setupWorker() 함수의 인자로 넘겨주면 된다.
/src/mocks/browser.js
import { setupWorker } from "msw";
import { handlers } from "./handlers.js";
export const worker = setupWorker(...handlers);
📝 ServiceWorker 삽입
일반적으로 개발 환경에서만 가짜 API를 사용하므로 환경 변수를 체크하여 선택적으로 서비스 워커가 구동되도록 해주고 있다.
/src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
if (process.env.NODE_ENV === "development") {
const { worker } = require("./mocks/browser");
worker.start();
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
📝 테스트
import { response } from "msw";
import React, { useState } from "react";
const Item = ({ name, age }) => {
return (
<li>
name: {name} / age: {age}
</li>
);
};
const url =
"https://raw.githubusercontent.com/techoi/raw-data-api/main/simple-api.json?id=react";
export default function TestMocking() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const handleClick = () => {
fetch(url)
.then((response) => {
return response.json();
})
.then((json) => {
setData(json.data);
})
.catch((error) => {
setError(`Something Wrong: ${error}`);
});
};
const handleClick2 = () => {
fetch("./login")
.then((response) => {
return response.json();
})
.then((json) => {
console.log(JSON.stringify(json));
});
};
if (error) {
return <p>{error}</p>;
}
return (
<div>
<button onClick={handleClick}>데이터 가져오기</button>
<button onClick={handleClick2}>데이터 가져오기2</button>
{data && (
<ul>
{data.people.map((person) => (
<Item
key={`${person.name}-${person.age}`}
name={person.name}
age={person.age}
/>
))}
</ul>
)}
</div>
);
}
console 창을 확인해보면 모킹이 활성화되었다고 뜬다.
다음으로, 요청으로 인한 가짜 응답이 오는지 확인해보겠다.
둘 다 정상적으로 가짜 응답이 온 것을 볼 수 있고, 애플리케이션에 서비스 워커 설정이 잘 된 것을 확인할 수 있었다!
'🍞 Front-End > React' 카테고리의 다른 글
[Recoil] Recoil 상태관리 라이브러리 (0) | 2022.11.02 |
---|---|
[React] Uncaught TypeError index.js (0) | 2022.11.02 |
[Redux] React Redux 상태관리 라이브러리 (0) | 2022.11.01 |
[React] Portals에 대해서 (0) | 2022.10.27 |
[React] Memoization (메모이제이션) (0) | 2022.10.25 |