[F-Lab 모각코 챌린지 41일차] MSW로 Mocking Server 만들기

Nami·2023년 7월 11일
0

66일 포스팅 챌린지

목록 보기
41/66
Postman으로 목서버를 만든 이후 멘토님께서 MSW로 만들어 볼 것을 권유하셨다. 난관이 예상되었고 예상만큼 조금 헤매었으나 구축 성공... 방법을 올려보려한다. 그리고 postman ➡️ MSW 이유를 알아내려한다!

MSW Mock Service Worker

Why do we need Mocking?

이전에 포스팅한 내용보다 훨씬 상세할 것이다.

전체 개발 프로세스 중 요구 사항 분석 및 기획, 백엔드 개발, 프론트엔드 개발에 이르는 각각의 개발 과정은 크게 겹치지 않고 진행되는 것이 이상적이다. 각 개발 과정에서 도출되는 요구 사항이나 사용 가능한 interface에 변경이 있으면 각 단계별 의존성에 따라 다시 작업해야 하는 경우가 발생하기 때문.

하지만 현실적으로, 프론트엔드와 백엔드를 병행하여 개발하게 되는 경우가 많다.

이때 프론트엔드에서 백엔드의 API를 활용해야하는 것 처럼, 백엔드에 종속적인 부분이 있다면 해당 부분이 완성되기 전까지는 프론트엔드에서 개발을 할 수가 없고, 진행 이후에 개발이 가능하다. 심지어 추가적인 수정 사항이 발생할 경우 이런 비효율적인 과정을 반복해야한다.

예상한 기간보다 API 개발에 시간이 더 필요해진 경우, 프론트엔드는 개발 진행을 못하는 상황이 생겨난다.

대기 시간을 줄이고, 백엔드 개발과 최대한 병행해 개발을 진행할 수 있는 방법은 Mocking을 활용하는 방법이다.

Mocking 방법 3가지

1. 자체적으로 Mocking Data 만들어서 사용하기

  • Mockup API를 요청하는 대신에 자체적으로 Mockup Data를 만들어 사용하는 방법
  • 웹 애플리케이션의 비즈니스 로직 내에서 Mocking을 하면(위 그림에서 Mocking 위치(1)) 쉽게 Mocking을 할 수 있는 반면, 비즈니스 로직에 Mocking 코드가 들어가면 실제 API 연동 코드로 교체하는 작업을 추가로 해줘야 한다.

2. 실제 API를 사용하는 것처럼 네트워크 수준에서 Mocking 하기

  • MSW를 이용해서 Service Worker에서 Mocking하기
  • 브라우저의 Service Worker에서 동작
    • 웹 애플리케이션의 비즈니스 로직과 별개로 브라우저 단에 별도의 작업을 실행시킬 수 있는 곳.
    • 웹 애플리케이션에서 서버로 나가는 API 요청을 가로챌 수 있음.
  • 웹 애플리케이션의 비즈니스 로직에 Mocking 코드가 들어가지 않는다
  • API endpoint를 실제 API의 것으로 사용할 수 있음
  • JS를 사용해 웹 애플리케이션 프로젝트에 작성하는 것이라 프론트엔드가 쉽게 이용할 수 있음.
  • 실제 API 개발이 완료되면 MSW에서 핸들러만 제거하면 교체가 끝난다.

3. 별도 Mock API Server 만들기

  • 별도의 Mock API Server를 만들어서 이용하기
  • 별도 서버이므로 웹 애플리케이션 코드는 실제 API를 연동하는 로직으로 작성하면 됨. 실제 API가 구현되었을 때 API endpoint만 바꿔주면 되니 간편.
  • 웹서버를 만들어야 한다는 장벽
  • 서버 개발에 대한 지식을 바탕으로 웹서버를 만들 수 있어야 하고, 개발할 때 별도 웹서버를 로컬에 실행해야함.
  • 데이터 상태 구현에는 직접 Mocking 대비 비용 및 공수가 훨씬 듦.
  • 추가적으로 로컬이 아닌 누군가에게 공유해야한다면 Mock 서버를 원격으로 제공하긱 위한 추가 환경 구축 작업에도 적지 않은 공수가 들어 효율적이지 않음.
  • 이전에 만들었던 Postman이 여기에 해당된다.
  • 모킹 서버를 만드는 것은 완전히 프로덕션 서버를 대체할 수 없고 이 또한 유지 보수해야 한다는 측면도 있다.

정의 & 특징

MSW(Mock Service Worker)는 API Mocking 라이브러리로, 서버향의 네트워크 요청을 가로채서 모의 응답(Mocked response)을 보내주는 역할을 한다.
따라서 Mock Service Worker 라이브러리를 통하면 Mock 서버를 구축하지 않아도 API를 네트워크 수준에서 모킹할 수 있다!
이런 역할을 할 수 있는 이유는 Service Worker를 통해 HTTP 요청을 가로채기 때문이다.

Service Worker란?

Service Worker는 웹 애플리케이션의 메인 스레드와 분리된 별도의 백그라운드 스레드에서 실행시킬 수 있는 기술 중 하나이다. 브라우저와 웹 애플리케이션 사이의 중간 계층 역할을 한다.
Service worker 덕분에 애플리케이션의 UI Block 없이 연산을 처리할 수 있다.

사용

  • 네트워크가 원활할 때 동기화 시켜주는 백그라운드 동기화 기능
  • 높은 비용의 계산 처리
  • 푸시 이벤트 생성
  • 네트워크 요청 가로채기 (HTTP Request와 Response를 보고 캐싱 처리를 한다든지, 필요하다면 로깅을 한다든지 하는 여러 가지 새로운 동작 등)
  • 기본적으로 localhost가 아닌 환경이라면 HTTPS 보안 프로토콜 환경이 필요
  • 수많은 라이브러리나 프레임워크에 종속적이지 않고 호환성에 문제없이 동작

UI Block

  • 사용자 인터페이스(UI)에서 발생하는 동작을 일시적으로 제한하는 기능.
  • 일반적으로 사용자가 특정 작업을 수행 중인 동안 다른 작업을 방지하거나 중단하는 데 사용한다.
  • 모달 다이얼로그, 로딩 스피너, 버튼 비활성화, 입력 필드 잠금 등의 형태로 구현.

동작 원리

  • 브라우저 환경에서 MSW는 Request handler에 정의된 Fetch 이벤트를 서비스 워커에 등록한다.
  • 서비스 워커는 서버로의 요청이 발생하면 method, URL 등에 기반하여 요청을 가로채 MSW에 전달하고 handler에 정의된 모의 응답을 받는다.

개발 방식

백엔드 개발자가 API를 제공하면, 프론트엔드 개발자는 별다른 작업 없이 MSW를 스위치 오프만 하면 Production으로 배포할 수 있는 형태의 개발 과정을 통해 개발을 진행할 수 있다!

추가 장점
특정 API 응답을 기준으로 에러가 발생해 디버깅이 필요한 상황이라면, 기존 서비스 로직을 전혀 건드리지 않고 오로지 MSW에서 Mocking을 만들어 내서 쉽게 디버깅할 수 있다.

기획자 등의 다른 누군가에게 각 화면을 공유하고 피드백을 받아야 하는 상황이 발생했을 때에도, 추가적으로 MSW에서 해당 상황을 만들어낼 수 있도록 작업을 해 둔다면 별다른 서비스 로직의 수정 없이도 MSW를 통해 제공이 가능하다.

Setting 해보자!

프로젝트는 Create-React-App을 기반으로 구성했고 한 번도 써본 적 없기에 설치 했다.

React-app 설치 방법

MSW 공식 홈페이지의 Get started를 보며 차근차근 따라갔다!

설치

npm install msw --save-dev
# or
yarn add msw --dev

Mock definition

  • src/mocks 파일을 생성한다
  • 모든 요청이 들어갈handlers.js 파일도 생성한다.

나는 REST API를 선택했다. GraphQL API도 가능하다.

Imports (REST API 모킹 필수 요소)

handlers.js 파일에 rest를 가져온다.
REST API를 선택했기 때문에 MSW 모듈의 rest 객체를 사용한다.

// src/mocks/handlers.js
import { rest } from 'msw'

Request handler

가짜 API를 구현하려면 요청이 들어왔을 때 임의의 응답을 해주는 핸들러 코드를 작성해야한다.

// src/mocks/handlers.js
import { rest } from 'msw'

export const handlers = [
  rest.post('/login', (req, res, ctx) => {
    // Persist user's authentication in the session
    sessionStorage.setItem('is-authenticated', 'true')

    return res(
      // Respond with a 200 status code
      ctx.status(200),
    )
  }),

  rest.get('/user', (req, res, ctx) => {
    // Check if the user is authenticated in this session
    const isAuthenticated = sessionStorage.getItem('is-authenticated')

    if (!isAuthenticated) {
      // If not authenticated, respond with a 403 error
      return res(
        ctx.status(403),
        ctx.json({
          errorMessage: 'Not authorized',
        }),
      )
    }

    // If authenticated, return a mocked user details
    return res(
      ctx.status(200),
      ctx.json({
        username: 'admin',
      }),
    )
  }),
]

Service Worker 코드 생성

여기서 public은 react app 설치시 자동으로 세팅되는 root에 존재하는 폴더임

Service Worker 생성

MSW 모듈에서 제공하는 setupWorker() 함수를 이용해서 browser.js에 서비스 워커를 생성한다. 위에 작성한 Request handler 코드를 불러와서 그대로 setupWorker() 함수의 인자로 넘겨주면 된다.

Service Worker 삽입

서비스 워커를 구동하는 코드를 애플리케이션의 진입 시점(entrypoint)에 삽입을 해보겠다.
예를 들어, Create React App으로 만든 애플리케이션의 경우, src/index.js 파일을 다음과 같이 수정해주면 된다.

상단 import 영역은 MSW 공식 홈페이지와 조금 다른데 그 이유는 react app이 업데이트 되어서 업데이트 된 경로대로 변경하여 적용해주었다. 안그러면 콘솔에 오류가 뜬다.

*일반적으로 개발 환경에서 가짜 API를 사용하므로 환경 변수를 체크하여 선택적으로 서비스 워커가 구동되도록 지정해준다.

(추후에 공부할 환경 변수에서 다시 복습 예정..)

Service Worker Test

애플리케이션을 구동 후에 (npm start) 브라우저에서 열고 콘솔을 확인하면 MSW 모킹이 활성화되었다는 메시지가 출력된다.

fetch()함수를 통해 GET /user를 요청해보겠다.

인증되지 않은 요청의 경우로 세팅해놓은 403 status가 잘 뜨는 것을 확인할 수 있다.

열어보면,
body에 설정된 {errorMessage: 'Not authorized'}도 확인된다.

// src/mocks/handlers.js
import { rest } from 'msw';

const users = [
  {
    balance: '$3,946.45',
    picture: 'http://placehold.it/32x32',
    age: 23,
    name: 'Bird Ramsey',
    gender: 'male',
    company: 'NIMON',
    email: 'birdramsey@nimon.com',
  },
  {
    balance: '$2,499.49',
    picture: 'http://placehold.it/32x32',
    age: 31,
    name: 'Lillian Burgess',
    gender: 'female',
    company: 'LUXURIA',
    email: 'lillianburgess@luxuria.com',
  },
  {
    balance: '$2,820.18',
    picture: 'http://placehold.it/32x32',
    age: 34,
    name: 'Kristie Cole',
    gender: 'female',
    company: 'QUADEEBO',
    email: 'kristiecole@quadeebo.com',
  },
  {
    balance: '$3,277.32',
    picture: 'http://placehold.it/32x32',
    age: 30,
    name: 'Leonor Cross',
    gender: 'female',
    company: 'GRONK',
    email: 'leonorcross@gronk.com',
  },
  {
    balance: '$1,972.47',
    picture: 'http://placehold.it/32x32',
    age: 28,
    name: 'Marsh Mccall',
    gender: 'male',
    company: 'ULTRIMAX',
    email: 'marshmccall@ultrimax.com',
  },
];

export const handlers = [
  // 사용자 목록
  rest.get('/users', (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(users));
  }),

  // 사용자 추가
  rest.post('/users', (req, res, ctx) => {
    users.push(req.body);
    return res(ctx.status(201));
  }),
];

구글링으로 임의의 사용자 데이터가 있는 users.json파일을 구했다.

데이터가 잘 불러와지는 것을 확인할 수 있다.

API 요청을 잘하기 위해,, 얼른 스킬업이 필요할 듯하다.
그래도 잘 작동하게끔 구축하다니 뿌듯.


참조 ✅

0개의 댓글