Frontend
API 모킹으로 돌아가지 않는 프로젝트 살리기
사건의 발단
백엔드 서버가 죽어버렸다. 살릴 사람도 없다. 내 포트폴리오…. API 모킹을 이렇게도 써볼 수 있지 않을까?
서버가 망했어요
2개월 간 일 평균 10시간 이상 개발한 팀 프로젝트의 서버가 죽었다. AWS 서버 지원 기간도 한정되어 있었고, 백엔드 개발팀에서 더 이상 개발하지 않게 되면서 직접 백엔드 서버를 개발하거나 작성된 코드를 클라우드 환경에 띄우는 방법으로 사이트를 살릴 수 있을 것으로 보였다.
(서버 응답이 없어 열심히 개발한 화면이 보이지 않는다.)
정말 열심히 개발했는데, 배포 사이트가 동작하지 않아 포트폴리오 용으로 남기기 어려웠다. 백엔드 개발을 하기에는 다른 할 일이 너무 많았다. 기존에 작성된 서버 코드를 띄우는 것도 좋은 경험이 될 수 있겠으나 프로젝트 개발 당시에 꼭 사용해보고 싶었던 API Mocking으로 동작하게끔 해보는 건 어떨까 싶었다.
API Mocking
백엔드 API가 완전히 구현되지 않았거나, 백엔드 개발 작업이 완료되지 않았을 때 프론트엔드 개발자가 가짜 서버를 만들어 실제 API와 유사한 응답을 반환하도록 하는 것이다. 백엔드 개발 의존도가 높은 프론트엔드 개발자들이 선택하는 방법이다. 백엔드와 프론트엔드 간의 개발 속도 차이로 인한 병목과 API 응답 데이터에 의존을 갖는 로직에 대한 테스트 코드 작성
JSON 파일이나 라이브러리를 사용하여 가짜 응답을 반환하거나, Mock 서버를 사용하여 프로그래밍 방식으로 동적 응답을 생성할 수도 있다. JSON Server, Jest, MSW, mockoon 등의 도구가 있다.
API 모킹은 주로 개발 단계에서 발생하는 백엔드와 프론트엔드의 개발 속도 차이로 인한 병목을 해결하는 주요한 방법이기도 하다. 그런데 초기 개발이 끝난 상황에서 포트폴리오를 위해 모킹을 해보겠다는 게 조금 어긋난 방향일 것 같기도 하다. 그래도 프론트엔드 개발을 공부한다는 생각으로 하면 얻어가는 것이 분명히 있을 것이다.
API 명세가 없음!
우선 기존에 사용했던 API 명세를 뒤져보려 했다. 당시 Swagger를 통해 자동화된 API 명세를 웹으로 받았었기 때문에, Jira 이슈들을 다시 둘러보면서 명세가 기록된 사이트 링크를 찾아냈다. 하지만 예상대로 서버가 돌아가지 않으니 동작하지 않았다.
이렇게 된 이상 협업 기록과 프로젝트의 api 관련 코드를 모두 뒤져서 모킹하면 되지 않을까? 일단 API 모킹은 어떻게 할까?
API 모킹하기
모킹 도구 선택 - Mock Service Worker
현업 개발을 하고 있는 프론트엔드 개발자 친구들에게 여러 조언을 구했다. API 목업을 하는 이유로 MSW(Mock Service Worker)를 사용하지 않을 이유가 없다고 한다. 사용이 간편하고, 서비스 워커 덕분에 다른 라이브러리 의존성을 가지지 않고 호환성 문제가 없다.
MSW (Mock Service Worker)
기존 Mockup 방식은 네트워크 요청을 가로채기 위해 http/https, XMLHttpRequest 모듈을 다른 함수로 대체하거나 Postman Mockup Server로 목업 서버를 직접 구축하곤 했다.
MSW는 이름에서 알 수 있듯이 Service Worker 기술을 이용하여 Mockup 작업을 돕는 라이브러리다. Service Worker는 HTTPS 상에서 네트워크 요청이나 응답을 가로채서 조작할 수 있고, 최신 브라우저나 웹 응용 프로그램에서 네트워크 사이에서 프록시 서버 역할을 하는 기술이다. (구식 브라우저에서는 동작하지 않는다.) MDN링크
MSW는 네트워크 요청이 발생하면 데이터만 교체하는 방식을 사용하여 모킹이 네트워크 단에서 일어나기 때문에 프론트엔드 코드를 실제 백엔드 API와 통신하는 것과 크게 다르지 않게 작성이 가능하다는 장점이 있다.
MSW 공식 문서 읽어보기
공식 문서를 살펴보면서 사용법을 익혀본다.
공식 문서의 Getting Started를 확인해보면, 총 3단계의 Step으로 사용법을 분류하고 있다. 첫 번째로 설치(Install), 두 번째로 기술(Describe), 세 번째로 통합(Integrate) 단계다.
1. MSW 설치
npm install msw@latest --save-dev #or yarn add msw --dev
MSW의 모킹 서버는 개발 환경에서만 사용된다.
devDependencies
로 설치해야 한다.2. 기술 (핸들러 기술)
기술 단계에서는 요청 핸들러(Request handlers)를 작성하는 단계라고 할 수 있다. 요청 핸들러에는 MSW가 가로챌 http 요청이 무엇인지, 어떤 응답을 해야 하는지 작성해야 한다. Rest API와 GraphQL API 두 가지 아키텍처에 따라 핸들러에 사용해야 하는 함수가 다르다.
// src/mocks/handlers.js import { http, HttpResponse } from 'msw' export const handlers = [ // Intercept "GET https://example.com/user" requests... http.get('https://example.com/user', () => { // ...and respond to them using this JSON response. return HttpResponse.json({ id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d', firstName: 'John', lastName: 'Maverick', }) }), ]
MSW는 크게 2개의 과정이 주된 요소다.
- 목 데이터 (더미 데이터) 마련하기
- API의 응답으로 받을 데이터를 작성
- API 핸들러 만들기
- http 요청을 가로채서 목 데이터를 반환하는 핸들러를 작성
3. 통합 (서비스 워커 환경 구현)
MSW는 환경 수준에서 적용되기 때문에 프레임워크나 라이브러리 등과 통합되어야 한다. 어떤 환경에서 MSW를 사용해야 할 지에 따라 적용법이 달라진다. 공식 문서에는 브라우저 환경, Node 환경, React Native 환경을 대표적인 예시로 들고 있다.
우린 서버 없이 사이트가 돌아가게 해야 하므로 브라우저 환경에 MSW를 적용해야 한다.
서비스 워커를 브라우저 환경에 등록하기 위해서는 브라우저에 워커 스크립트가 등록되어야 한다. MSW에서 제공하는
mockServiceWorker.js
스크립트를 퍼블릭 환경에 호스팅해야 하는데, 이를 위한 명령어를 제공하고 있다.npx msw init <PUBLIC_DIR>
이제 작성한 핸들러를 모아 동작시킬 워커 인스턴스를 생성해야 한다. 파일을 따로 작성해서 설정해두는데, 예제의 경우 브라우저 환경은
src/mocks/browser.js
에 아래와 같이 setupWorker()
API를 사용해서 설정한다. Node 환경의 경우 setupServer()
를 사용한다. 이 API는 브라우저 또는 서버 환경에 워커를 세팅하고 준비시키는 역할을 한다.// src/mocks/browser.js import { setupWorker } from 'msw/browser' import { handlers } from './handlers' export const worker = setupWorker(...handlers)
공식 문서 예제에는 handlers를 한 곳에 모두 작성해서 등록하는데, 핸들러가 많아질 경우 도메인 분리가 필요해 보인다.
이제 준비된 워커는
worker.start()
를 통해서 실행시킬 수 있다.서비스 워커 등록은 비동기 작업이다.
MDN에 따르면 서비스 워커 API 등록 자체가 프로미스를 반환한다.
worker.start()
또한 프로미스를 반환한다. 즉, 비동기로 동작하므로 아무리 최상단에 워커 인스턴스를 실행한다고 하더라도 애플리케이션 렌더링과 워커 등록이 서로 겹쳐지는 경우 예상치 못한 동작이 발생할 수 있다.위와 같은 이유로 워커가 실행된 후에 애플리케이션을 렌더링하도록 조건부로 렌더링시켜야 한다. 공식 예제는 아래와 같이 작성할 것을 권장한다. (리액트 기준)
import React from 'react' import ReactDOM from 'react-dom' import { App } from './App' async function enableMocking() { // if (process.env.NODE_ENV !== 'development') { // return // } const { worker } = await import('./mocks/browser') // `worker.start()` returns a Promise that resolves // once the Service Worker is up and ready to intercept requests. return worker.start() } enableMocking().then(() => { ReactDOM.render(<App />, rootElement) })
위 코드에서는 development 환경이 아닐 경우 모킹을 하지 않도록 작성했지만, 우리의 경우 빌드 환경에서도 모킹이 동작해야 하므로 해당 라인을 제거했다.
이제 앱을 실행해서 콘솔에 아래와 같은 메시지가 뜨면 통합 과정이 끝났고, 모킹이 가능해진다.
#bash [MSW] Mocking enabled.
권한이 필요하지 않은 페이지들을 작동하게 만들었다. 이제 권한이 필요한 기능들을 모킹해야 하는데, 이야기는 이어진다. To be continued.