💡 프론트엔드 개발 과정에서의 API 개발 작업에 대한 의존성을 줄이고
DX(Developer eXperience)를 개선하기 위해 msw를 도입한 내용에 대해 정리합니다.
프론트엔드 개발을 할 때는 api 작업 완료 전까지 mock data를 만들어 작업을 하곤 하는데, 이 때 DX를 저하시키는 요인들이 있습니다.
개발을 진행하던 에셋 라이브러리는
1) 앱의 비즈니스 로직 내에서 mock data를 사용하면, 실제 api 연동 코드로 교체하는 작업 리소스가 추가로 듭니다.
2) MSW를 사용해 앱 내부가 아닌 브라우저 내부의 별도 Service Worker에서 mocking을 합니다.
3) 별도의 웹서버를 만들면 비즈니스 로직에 mock data가 들어가진 않지만 웹 서버를 만들어야 하는 부담이 있습니다.
브라우저에서 이루어지는 실제 네트워크 요청들을 Service Worker가 가로채게 됩니다. Service Worker는 가로챈 요청을 복사해서 실제 서버가 아닌 클라이언트 사이드에 있는 MSW 라이브러리로 보낸 후, 등록된 핸들러를 통해 가져온 mocking된 응답값을 브라우저에게 그대로 전달해 줍니다.
이러한 과정을 통해, 실제 서버와 직접적인 연결 없이 보내는 요청에 대한 응답을 Mocking 할 수 있게 되는 것이죠. 따라서 백엔드 API가 아직 준비되지 않아도 MSW로 가상 API를 등록하고 프론트에서 테스트할 수 있습니다.
별도의 Mock API Server를 사용하기 위해서는 서버 개발에 대한 지식을 바탕으로 웹서버를 만들고 ⇒ 그 서버를 별도로 함께 로컬 실행해야 합니다.
즉, 최소한의 개발 리소스로 api 연결까지 하고 싶은데.. 가장 효율적인 방법은 아닐지도? 🤔
정리하면, MSW로 mocking해 데이터를 사용했을 때의 장점은 다음과 같습니다.
mocks/ 폴더 하위의 worker.js와 최상위 index.js에 세팅 코드가 있습니다.
// mocks/worker.js
import {setupWorker} from "msw";
import {handlers} from "./handlers";
export const worker = setupWorker(...handlers);
// src/index.js
if (process.env.NODE_ENV === "development") {
worker.start({
quiet: true,
onUnhandledRequest: "bypass",
// 실제 api request까지 가로채기 때문에
// 해당 api 요청이 handler에 등록되지 않은 경우 많은 warning 로그가 콘솔에 쌓입니다.
// 다른 개발 과정에 방해가 될 수 있어 이 로그를 출력하지 않고, unHandledRequest를 무시하는 설정입니다.
// 로컬 테스트 시 삭제 혹은 주석 처리해 로그를 확인할 수 있습니다.
});
}
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
mocks/ 폴더 하위 파일에 사용할 mock data를 정의합니다.
// mocks/asset.js
export const mockData = [
{
id: "id",
assetType: "assetType",
url: "url",
genre: "genre",
//...
//mocks/handlers.js
// data를 요청에 따라 어떻게 리턴할 것인지 handler를 작성합니다.
import {rest} from "msw";
import {mockData} from "./asset";
export const handlers = [
rest.get("/path", (req, res, ctx) => {
const type = req.url.searchParams.get("type");
return res(
ctx.status(200),
ctx.json(
mockData.filter(asset => {
return asset.type === type;
}),
),
);
}),
실제 api를 호출하듯 사용합니다.
// /api
export const getMockData = type => {
return fetchRequest(`/path`, METHOD.GET);
};
// api를 호출할 component 내부
// 실제 api 연결을 위해서는 엔드포인트만 교체해주면 됩니다.
useEffect(() => {
handleMockData();
}, []);
const handleMockData = async () => {
const res = await request.getMockData(params);
const json = await res.json();
setData(json);
};
노션에 관련 내용들과 사용방법을 정리해 팀원들에게 공유하고, 설정 및 예시 코드를 PR로 올려 리뷰를 요청한 뒤 적용하게 되었습니다.
프론트엔드에서도 다양한 응답 케이스별 처리 로직을 작성해 테스트할 수 있고, 이에 대한 응답값 형태를 정의해 백엔드와의 작업을 고려하며 보다 효율적으로 작업할 수 있다는 점에서 충분히 고려해 볼 수 있는 선택지라고 생각합니다.