이번 글에서는 프로그래머스 데브코스에서 진행했던 프로젝트인 짤뮤니티를 Next로 마이그레이션 진행과 동시에 서버 이관 작업이 이루어지면서 생기는 불편함을 해결하기 위해 MSW를 도입했던 경험을 작성하려 합니다.
MSW 사용 이유
우선 위에서도 언급을 했듯이 React 환경으로 구현된 짤뮤니티 프로젝트를 Next로 마이그레이션 진행 작업과 동시에 서버 이관 작업이 이루어져 모든 API가 가동을 멈춰 페이지 및 컴포넌트가 제대로 작동을 하는지 테스트를 해 볼 수가 없었습니다. 이러한 불편함을 해결하기 위해 MSW를 도입하게 되었고, 이미 모든 API가 구현이 되어있었기 때문에 백엔드와 API 스펙에 대해 협의하는 과정을 생략할 수 있어 훨씬 수월하게 MSW 작업을 진행할 수 있었습니다.
MSW란 무엇인가?
MSW란 Mock Service Worker의 약자로, Service Worker를 이용해 서버를 향해 실제 네트워크 요청을 보낼 때, 요청을 가로채서 모의응답을 보내주는 API 라이브러리입니다. MSW를 사용하면 직접 Mock 서버를 구현하지 않아도, 네트워크 수준에서 API를 Mocking 할 수 있으며, Mocking 테스트를 위해 브라우저/노드(node.js) 환경 모두 사용할 수 있다는 장점이 있습니다.
MSW가 이런 기능을 할 수 있는 이유는 Service Worker를 이용해 네트워크 요청을 가로채기 때문인데요, 그렇다면 Service Worker에 대해 한번 알아보겠습니다.
Server Worker
Service Worker는 웹 애플리케이션의 메인 스레드와 분리된 별도의 백그라운드 스레드에서 실행시킬 수 있는 기술 중 하나로, Service Worker 덕분에 애플리케이션의 UI Block 없이 연산을 처리할 수 있습니다.
보다 정확하게 MDN 문서를 보면 아래와 같이 나와있는 걸 볼 수 있습니다.
서비스 워커는 웹 응용 프로그램, 브라우저, 그리고 (사용 가능한 경우) 네트워크 사이의 프록시 서버 역할을 합니다. 서비스 워커의 개발 의도는 여러 가지가 있지만, 그중에서도 효과적인 오프라인 경험을 생성하고, 네트워크 요청을 가로채서 네트워크 사용 가능 여부에 따라 적절한 행동을 취하고, 서버의 자산을 업데이트할 수 있습니다. 또한 푸시 알림과 백그라운드 동기화 API로의 접근도 제공합니다. MDN 공식문서
이러한 특징으로 Service worker는 다음과 같은 기능에서 많이 사용이 되고 있습니다. 카카오 기술 블로그 참고
- 먼저, 네트워크가 원활할 때 동기화를 시켜주는 백그라운드 동기화 기능이나, 높은 비용의 계산을 처리할 때 또는 푸시 이벤트를 생성할 때 주로 사용
- MSW의 동작 방식과 관계되어 있는, 네트워크 요청을 가로채는 행위도 수행. Service Worker가 애플리케이션과 서버 사이에서 Request를 가로채서 직접 Fetch에 대한 컨트롤도 할 수 있기 때문에 색다른 작업이 가능. Ex) HTTP Request와 Response를 보고 캐싱 처리를 한다든지, 필요하다면 로깅을 한다든지 하는 여러 가지 새로운 동작을 만들어 낼 수 있습니다. MSW도 이 과정을 통해서 Request를 가로채서 Response를 Mocking 하는 원리를 사용
하지만 Service Worker의 사용이 제한되는 경우도 있습니다.
- Service Worker는 대부분의 모던 브라우저에서 지원하고 있으나, IE와 같은 일부 브라우저에서는 지원하고 있지 않다
- 기본적으로 localhost가 아닌 환경이라면 HTTPS 보안 프로토콜 환경이 필요. Service Worker는 중간에서 네트워크로 연결을 가로채고 조작할 수 있는 강력한 기능을 갖고 있기 때문에, Service Worker에서는 HTTPS가 기본적으로 제공되는 환경에서만 사용할 수 있다
MSW는 Service Worker를 기반으로 모의 API를 만들어내기 때문에 다른 라이브러리나 프레임워크에 종속적이지 않고 호환성에 문제없이 동작합니다.
MSW 동작 원리
먼저, 브라우저에 Service Worker를 설치하고, 설치 이후부터는 브라우저에서 실제 이루어지는 요청을 Service Worker가 가로채게 됩니다.
Service Worker에서는 실제 요청을 복사하여 MSW에게 해당 요청과 일치하는 모의 응답을 제공받고, 이를 브라우저에게 전달하게 됩니다.
이러한 과정을 통해, 서버 존재 여부와 상관없이 실제 요청으로 이어지지 않고 예상할 수 있는 요청을 Mocking 할 수 있으며, API가 아직 준비되지 않더라도, 또는 서버가 불완전하더라도 API 테스트를 진행할 수 있습니다.
보통 MSW를 사용한 개발 방식은 아래와 같이 진행이 된다고 합니다. 카카오 기술 블로그 참고
보통 위와 같은 방식이 프로젝트를 처음 들어갈 때 진행되는 방식일 텐데 현재 저희의 상황은 이미 프로젝트 개발이 다 완료된 상태였고, 현재 상태에서 서버 이관 작업이 완료될 동안 API 요청을 테스트해보는 것이 우리의 목적이었기 때문에 2, 3번에 해당하는 작업을 건너뛸 수가 있었습니다.
아마 이러한 개방 방식을 프로젝트를 들어가기 전 미리 알았더라면, API 스펙 협의를 통해 백엔드가 API 작업을 진행하는 동안 프론트에서는 Mocking을 통해 목데이터를 응답받으며 백엔드와 작업 sync를 더 잘 맞출 수 있었지 않았을까 싶습니다. 현재도 사이드 프로젝트로 트레이너의 회원 관리 서비스를 디자인하고 있는데, 아직 개발 단계에 제대로 들어가지 않았으니 MSW를 적극 사용하면 백엔드와 작업 sync를 맞추는 데 도움이 될 것 같습니다.
짤뮤니티 프로젝트의 MSW 폴더 구조
📦mocks
┣ 📂data // 응답 데이터를 관리하는 곳
┃ ┣ 📜chats.ts
┃ ┣ 📜reports.ts
┃ ┣ 📜tags.ts
┃ ┗ 📜zzals.ts
┣ 📂handlers // 응답 데이터를 반환하기 위해 Mocking + 핸들링하는 곳
┃ ┣ 📜chatHandlers.ts
┃ ┣ 📜index.ts
┃ ┣ 📜reportHandlers.ts
┃ ┣ 📜tagHandlers.ts
┃ ┗ 📜zzalHandlers.ts
┣ 📜browser.ts
┗ 📜server.ts
현재 저희 짤뮤니티 프로젝트의 MSW 폴더 구조는 위와 같습니다.
우선 Mocking을 통해 반환할 데이터를 관리할 곳이 필요했고, 이 데이터를 Mocking 및 handling 할 곳이 필요하여 각각 data와 handlers로 모듈을 나누었습니다.
클라이언트 사이드 환경과 서버 사이드 환경 모두에서 네트워크 요청을 Mocking 할 수 있도록 browser.ts, server.ts에서 각각 setupWorker, setupServer에 handler를 인자로 담아주었습니다.
MSW 사용 방법
우선 MSW 설치 방법은 공식 문서를 참고하면 아주 쉬우니 넘어가고, MSW에서 지원하는 전용 CLI를 입력하여 mockServiceWorker를 생성합니다.
이후 위 내용처럼 SSR 환경과 CSR 환경 모두에서 mocking 할 수 있도록 setupServer와 setupWorker를 설정해 줍니다.
// MswProvider.tsx
"use client";
import { useEffect } from "react";
export default function MswProvider() {
useEffect(() => {
if (process.env.NODE_ENV === "development") {
if (typeof window === "undefined") {
(async () => {
const { server } = await import("@/mocks/server");
server.listen();
})();
} else {
(async () => {
const { worker } = await import("@/mocks/browser");
worker.start({
onUnhandledRequest: "bypass",
});
})();
}
}
});
return null;
}
위의 MswProvider를 생성하고 Next라면 Root layout에 주입해 줍니다. 위처럼 작성하게 되면 개발 환경에서만 Mocking을 수행하게 되며, 환경에 따라 어떤 Mocking setup 함수를 실행하게 될지 결정하게 됩니다.
위 코드는 tag와 관련된 API를 Mocking 하는 핸들러를 모아놓은 코드입니다. API를 모킹 하여 어떤 데이터를 어떻게 가져올 것인지에 대한 계산이 포함되는 함수는 따로 분리를 시켜놓았고 이 함수를 통해 미리 정해놓은 응답 데이터를 return 시켜주면 되는 것입니다.
한 가지 아쉬움이 남는다면, 당시 프로젝트를 개발할 때 프론트+백 모두 API 상태 코드를 체계적으로 관리하지 않아 에러가 발생할 때마다 의사소통에 불편함이 있었습니다. 아직 해당 부분은 개선되지 않아 기존의 코드들이 401 코드를 제외한 그 어떠한 상태 코드도 핸들링하고 있지 않고 있어, 체계적으로 MSW 응답 데이터 상태 코드를 설정하지 않았습니다. 해당 부분은 저 스스로가 상태 코드를 주어야 하는지 학습을 한 뒤 체계적으로 응답 데터의 상태 코드를 관리할 필요가 있어 보입니다.
마치며
MSW를 도입한 것은 정말 좋은 도움이 되었습니다. 서버가 불완전하거나 API가 아직 나오지 않았더라도 프론트 내에서 mocking 하여 API를 직접 테스트해 볼 수 있다는 점이 좋았던 것 같습니다. 신규 프로젝트를 들어갈 때도, 서버를 이관할 때도, 기능을 추가할 때도 MSW는 유용하게 사용될 것 같습니다.
참고로 현재는 MSW가 버전업이 되어 문법이 많이 바뀌었습니다. MSW Mirgrations 에서 어떤 부분이 바뀌었는지 확인하시고 사용하면 좋을 것 같습니다.
'회고' 카테고리의 다른 글
지난 프로그래머스 데브코스(FE)에서 나는 무엇을 얻었을까 (6) | 2024.06.07 |
---|---|
팀장은 무엇을 위해 존재할까 (0) | 2024.04.06 |
같은 실수를 반복하지 않고, 한 번 학습한 내용을 오래 기억하기 위해 개발하면서 겪었던 트러블 슈팅과 학습한 내용을 정리하고 기록합니다 🧑💻