next 13 에 msw 적용하기

seung·2024년 5월 23일
0
post-thumbnail

🍃 도입 배경


기존의 API 연동 작업

보통 기획 → 디자인 → 백엔드 → 프론트엔드 순서로 기능 개발이 진행되는데 우리 회사도 마찬가지였다.

기획과 디자인이 완료되면 프론트엔드 개발자는 API 가 나오기 전 UI 작업을 시작했다.

UI 작업을 할 때는 아직 API 가 나온 것이 없으니 임시로 더미 데이터를 만들어 개발을 했고

API 가 나온 뒤에 더미 데이터를 삭제하고 API 연동을 하는 작업이 들어갔다.


API 가 나올 때까지 붕 뜨는 시간과 더미 데이터 ↔ API 응답 객체 교체 작업은 비효율적이었다.

프론트엔드 개발자는 백엔드 개발자가 API 를 완성시켜 주기 전 까지는 어쩔 수 없이 시간이 붕 뜨게 되었다.

API 개발이 완료되고 나서는 바쁘게 API 연동을 하고 적용되어 있던 더미 데이터를 걷어내고 실제 API 응답 객체로 교체해야 했다.

붕 뜨는 시간에 미리 API 연동작업을 할 수 있으면 시간을 좀 더 효율적으로 사용할 수 있지 않을까 생각이 들었고

효율적으로 일할 수 있는 방법을 찾던 중 가짜 데이터를 mocking 할 수 있는 msw 를 도입해보았다.


더미 데이터를 사용한 것과 무엇이 달랐나?

기존에는 더미 데이터를 일반 변수로 선언을 하고 해당 더미 데이터 변수를 직접 가져와 사용하는 방식으로 하다보니 실제로 서버의 응답값을 받는 것처럼 구현을 하진 못했다.

API 가 나오고 난 뒤의 작업이 제일 달랐는데,

더미 데이터를 사용할 때는 적용된 더미 데이터를 삭제하고 API 를 요청, 응답받아오는 코드를 추가로 작성을 해주어야 해서 두 번 일하게 되는 느낌이 있.ㄷ다.

반면에 msw 는 실제 서버에서 주고 받는 것처럼 중간에서 API 요청을 가로채기 때문에 실제 API 요청하는 것처럼 똑같이 코드를 입력해주면 되었다.

API 개발이 완료되면 msw 설정만 off 해주면 되니 매우 편리했다.

API 요청 코드를 API 완료 전에 작성하느냐, 완료 후에 작성하느냐의 차이이지만 비교적 여유가 있는 시간에 코드를 작성할 수 있기 때문에 좀 더 효율적으로 작업이 가능했다.


설치 및 설정 세팅


1. msw 라이브러리 설치

아래 명령어를 통해 라이브러리를 설치한다.

npm install msw --save-dev

2. worker 설정 파일 생성

browser 환경 설정 파일을 추가한다.

// src > mocks > browser.js
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'

export const worker = setupWorker(...handlers)

node 환경 설정 파일을 추가한다.

// src > mocks > server.js
import { setupServer } from 'msw/node'
import { handlers } from './handlers'

export const server = setupServer(...handlers)

아래 명령어를 실행해서 public 폴더 안에 mockServiceWorker.js 파일을 생성해준다.

npx msw init public/ --save


3. .env 파일

개발 서버에서만 msw 를 적용하기 위해 아래 코드를 추가한다.

NEXT_PUBLIC_API_MOCKING="enabled"

4. 서비스 워커 설정

MSWComponent.jsx 컴포넌트를 생성하여 내부에 서비스 워커를 실행해주는 로직을 작성한다.

  • worker.start() 함수의 onUnhandledRequest 옵션
    • 처리되지 않은 요청(예: 일치하는 요청 핸들러가 없는 요청에 대응하는 방법
    • bypass : Prints nothing, performs the request as-is. (콘솔 waring 뜨지 않게 해줌)

[참고] worker.start() 공식문서


// src > mocks > MSWComponent.jsx
import { useEffect, useState } from 'react'

const isMockingMode = process.env.NEXT_PUBLIC_API_MOCKING === 'enabled'

export const MSWComponent = ({ children }) => {
  const [hasWorkerStarted, setWorkerStarted] = useState(false)

  useEffect(() => {
    // mocking 모드로 설정했을 때만 서비스 워커 실행
    if (isMockingMode) {
      if (typeof window === 'object') {
        // 브라우저 환경일 때는 브라우저와 관련된 모듈만 로드
        const { worker } = require('@/mocks/browser')
        worker.start({ onUnhandledRequest: 'bypass' }).then(() => {
          setWorkerStarted(true)
        })
      } else {
        // 서버 환경일 때는 서버와 관련된 모듈만 로드
        const { server } = require('@/mocks/server')
        server.listen()
      }
    }
  }, [])

  // mocking 모드가 아닐 때는 무조건 렌더링
  if (!isMockingMode) {
    return <>{children}</>
  }

  // mocking 모드일 때는 서비스워커가 실행되었을 때만 렌더링
  return hasWorkerStarted ? <>{children}</> : null
}

🚨 [주의할 점]

1. next.js 는 서버 사이드가 먼저 실행되고 난 뒤 클라이언트 사이드가 실행되기 때문에 각 환경에 맞는 모듈 로드를 설정해 주어야 한다.

  1. 서비스 워커 실행이 완료된 후에 렌더링을 해줘야 한다.
  • 서비스 워커 설정이 완료되기 전에 API 를 호출해서 mocking 이 되지 않는 상황을 방지할 수 있다.

    이 두 가지 항목 덕분에 많은 삽질을 경험했다..

위의 단계까지 했으면 _app.js 에서 작성한 MSWComponent 컴포넌트로 감싸준다.

import { MSWComponent } from '@/src/mocks/MSWComponent'

export default function App({ Component, pageProps }) {
	// ...

	return (
		// ...생략
		**<MSWComponent>**
			<CommonLayer>
        <Component {...pageProps} />
					// ...생략
      </CommonLayer>
		**</MSWComponent>**
	)
}

handler 작성하기


mocking 하기 위한 handler 파일을 생성하고 코드를 작성한다.

  • rest api 방식은 http 를 이용해서 작성할 수 있다.

msw http 공식문서

// src > mocks > handler.js
import { http, HttpResponse } from 'msw'

export const handlers = [
  http.get(`${process.env.NEXT_PUBLIC_API_HOST}/channel-talk`, (res) => {
    return HttpResponse.json({
      isUsed: ![](https://velog.velcdn.com/images/sseung95/post/6dd94d64-b128-42ab-a6aa-6cb2d923e4f1/image.png)
true,
      pluginKey: 'msw-test',
      accessSecret: 'asetsdf',
    })
  }),
]

handler 작성을 완료하면 지정한 API 주소로 가는 요청을 msw 가 가로채서 우리가 지정한 값을 반환해준다.

아래와 같이 콘솔이 뜨면 정상적으로 mocking 이 된 것이다.


(참고) handler 폴더 구조

handlers.js 파일 내에서 너무 많은 양의 코드가 생성되거나 카테고리별로 관리하고 싶다면 handlers 폴더를 생성해 관리해줄 수 있다.

handlers 폴더를 만들고 각 원하는 handler 이름으로 파일을 생성해준 뒤 코드를 작성한다.

// src > mocks > handlers > siteHandler.js
import { http, HttpResponse } from 'msw'

export const siteHandlers = [
  http.get(`${process.env.NEXT_PUBLIC_API_HOST}/channel-talk`, (res) => {
    return HttpResponse.json({
      isUsed: false,
      pluginKey: 'msw-test',
      accessSecret: 'asetsdf',
    })
  }),
]

작성한 handler 들은 index.js 에서 handler 들을 모아주면 된다.

// src > mocks > handlers > index.js
import { siteHandlers } from './siteHandler'

export const handlers = [...siteHandlers]

handlers 폴더 생성했을 때 구조

[참고] msw Structuring handlers 공식문서



🔥 마주친 에러들

handler 까지 작성을 마치고 실행을 하니 아래와 같이 cookie 관련 에러가 매우 많이 떴었다.

동일한 문제가 발생했다는 이슈를 발견했는데 https 가 아닌 로컬 환경에서 실행하고 있기 때문에 발생하는 문제라고 한다.

https://github.com/mswjs/msw/discussions/818#discussioncomment-1022812

공식 문서에서는 보안상의 이유로 서비스 워커는 보안 연결을 통해서만 연결이 가능하다고 적혀있고,

이를 해결하기 위한 방법도 공식 문서에 나와 있어 크롬의 flag 설정을 통해 해결할 수 있었다.

  • chrome://flags/#unsafely-treat-insecure-origin-as-secure 에 접속하면 flag 설정을 할 수 있음

크롬 외 firefox, safari 에서의 설정 방법도 공식 문서에서 확인할 수 있다.

https://mswjs.io/docs/recipes/using-local-https/


2. [MSW] Failed to register a Service Worker for scope … 에러

AD 에 msw 세팅하는 과정에서 npx msw init 으로 mockServiceWorker 파일을 생성해주었는데도 해당 에러가 발생했다.

mockServiceWorker.js 파일을 찾지 못해 발생하는 에러인 것 같았다.

worker start 를 해줄 때 serviceWorker 의 url을 지정해주니 해결되었다.

  • serviceWorker 의 url 기본값은 ‘/mockServiceWorker.js' 라고 한다.
worker
  .start({
    onUnhandledRequest: 'bypass',
    **serviceWorker: {
      url: './mockServiceWorker.js',
    },**
  })

profile
🌸 좋은 코드를 작성하고 싶은 프론트엔드 개발자 ✨

0개의 댓글