CustomMockService npm 패키지 만들기 - 1

정성엽·2025년 7월 24일
0

이것저것

목록 보기
4/4

INTRO

웹 개발을 진행할때는 주로 MSW를 사용해서 서버 상태를 모킹시켜서 사용하곤 했었다.

하지만, POPI 프로젝트에서 사용자 어플리케이션을 개발할 때, 그리고 이번에 진행하는 Nextron 프로젝트를 진행하면서도 MSW는 동작하지 않았다.

물론 필자가 세팅을 제대로 못한 것일수도 있으나, 개인적으로는 레퍼런스를 많이 찾아봤고 그만큼 시간도 많이 쏟았기 때문에 이정도의 세팅 허들이 있는 것이 마음에 들지 않았다.

솔직히 될지는 모르겠지만, 그래서 API 모킹을 제공하는 라이브러리를 한번 만들어보려고 한다.


1. 어떻게 설계할 것인가?

일단 필자는 Usage를 먼저 생각해보는 것 같다.

즉, 내가 만든 라이브러리를 어떻게 호출해서 사용할 것인가? 를 생각해보는 것부터 시작해보려고 한다.

우선 아이디어는 다음과 같다.

💡 Mock Server 실행

사용자는 목서버를 일단 프로젝트 루트 디렉토리에서 실행하도록 설명서를 제공할 것이다.

MSW를 사용하다보니 아무래도 그 영향을 많이 받은 것 같기도하다.

프로젝트 루트에서는 다음과 같이 사용하도록 만들 것이다.

export default App() {
  CustomMockServer.run({isDevRun : true})
  
  return ...
}
  • isDevRun은 개발환경에서만 실행할것인지를 설정하는 옵션으로 제공하는게 좋을 것 같다.
  • Default는 True로 제공하여 CustomMockServer.run() 으로도 간단하게 실행할 수 있도록 만들어보려고 한다.

💡 네트워크 요청 가로채기

목서버를 동작시킨다면 당연히 네트워크 요청을 가로채서 실제 동작하는 것처럼 만들어야 할 것이다.

웹 애플리케이션에서 API 요청을 가로채는 방법은 크게 3가지가 있다. 각각의 방식은 서로 다른 장단점을 가지고 있으며, 프로젝트의 요구사항에 따라 적절한 방식을 선택해야 한다.


🔧 Service Worker 방식 (MSW가 사용하는 방식)

Service Worker는 브라우저의 네트워크 레이어에서 동작하는 별도의 워커 스레드를 활용한 방식이다.

// public/mockServiceWorker.js
self.addEventListener('fetch', (event) => {
 const { request } = event;
 if (shouldMock(request.url)) {
   event.respondWith(
     new Response(JSON.stringify(mockData), {
       status: 200,
       headers: { 'Content-Type': 'application/json' }
     })
   );
 }
});

이 방식의 가장 큰 특징은 Network 탭에서 실제 HTTP 요청처럼 확인 가능하다는 것이다.

즉, 디버깅이 용이하지만, MSW를 사용해본 사람이라면 별도의 .js 파일을 등록해줘야하는 번거로움이 있고, 필요시 세팅을 추가로 진행해야한다는 문제가 있다.

  • 하지만 사용성은 제일 좋긴하다..

🔧 XMLHttpRequest Patching 방식

XMLHttpRequest 클래스를 교체하여 전통적인 AJAX 요청을 가로채는 방식이라고 한다.

const OriginalXHR = window.XMLHttpRequest;

window.XMLHttpRequest = function() {
  const xhr = new OriginalXHR();
  const originalOpen = xhr.open;
  const originalSend = xhr.send;

  xhr.open = function(method, url) {
    this._method = method;
    this._url = url;
    return originalOpen.apply(this, arguments);
  };

  xhr.send = function(data) {
    if (shouldMock(this._method, this._url)) {
      setTimeout(() => {
        this.readyState = 4;
        this.status = 200;
        this.responseText = JSON.stringify(mockData);
        if (this.onreadystatechange) this.onreadystatechange();
      }, 0);
      return;
    }
    return originalSend.apply(this, arguments);
  };

  return xhr;
};

찾아본 내용으로는 오래된 라이브러리나 jQuery와 같은 XHR 기반 코드와 호환에 용이하다고 한다.

하지만, Fetch API와는 호환이 되지 않는다고 한다.

필자가 만들려고하는 목서비스 패키지가 레거시까지 지원하는걸 고려한다면 이 코드를 참고해야할 것 같다.

코드를 보면 간단하게 xhr 인스턴스를 window.XMLHttpRequest 에서 받아온 다음, 해당 인스턴스의 xhr.openxhr.send 를 내가 원하는 방식으로 조작해서 return하면 되는 것 같다.

  • 찾아보니 해당 방법은 네트워크 탭에서 확인이 어렵다고 한다.
  • 아마도 네트워크 레이어에서 바로 처리되기 때문인 것 같다.

🔧 Fetch API Patching 방식

브라우저의 전역 fetch 함수를 직접 교체하여 요청을 가로채는 방식이다.

const originalFetch = window.fetch;

window.fetch = function(url, options) {
  if (shouldMock(url, options?.method)) {
    return Promise.resolve(
      new Response(JSON.stringify(mockData), {
        status: 200,
        headers: { 'Content-Type': 'application/json' }
      })
    );
  }
  
  return originalFetch(url, options);
};

window.fetch 를 가로채서 originalFetch(url, options) 를 우리가 원하는대로 수정해서 보내주면 되는 것 같다.

어디선가 해본 것 같아서 코드가 낯설지는 않는 느낌이다.

마찬가지로 Fetch API를 가로채는 방식또한 네트워크 레이어에서 진행되기 때문에 네트워크 탭에서는 안보인다고 한다.


2. 아이디어

필자는 MSW의 번거로운 설정을 피하기 위해 별도의 패키지를 만들어보려고 하는 만큼, 간단한 사용성을 목표로 하고 있다.

따라서, Fetch APIXMLHttpRequest Patching 을 모두 지원하는 형태로 구현을 진행하지 않을까 싶다.

이 부분은 우선 만들어보고 테스트를 진행해본 이후, 변경될 수도 있을 것 같다.

💡 네트워크 탭은?

MSW를 사용하면서 가장 좋았던 부분은 네트워크 탭을 통한 디버깅이다.

실제 API를 호출한 것처럼 네트워크 탭을 통해서 자세한 API Request & Response를 확인할 수 있었던 경험은 그대로 가져가고 싶다.

ReactQueryDevTool 을 사용해보면 알겠지만, 캐시 상태를 보여주기 위해 해당 툴은 별도의 UI를 제공한다.

아마도 이런식으로 인터셉트한 네트워크 요청을 별도의 UI를 통해서 제공해주면 어떨까 싶다.


OUTRO

서비스 워커를 별도로 구현하는 것은 개발 공수가 엄청 들어가고, 냉정하게 그런 Low Level에서 동작하는 프로세스를 구현할 실력은 아직 없다고 생각한다.

그래서 우선 될지는 모르겠지만 목서비스 패키지를 한번 만들어보는 과정을 기록하면서 공부해보고, 실패하더라도 왜 실패했는지 원인분석을 해보는 것에 의의를 가지도록 하자 👊

profile
코린이

0개의 댓글