일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- React
- state 관리
- 기술 낙서장
- MSW
- javascript
- 결제페이지
- react-query v5
- react-query
- nextjs
- React.JS
- 리액트
- JS변수
- Next.js
- router instance
- node.js
- SW캡스톤디자인
- react-query&Next.js
- TypeScript
- 사내 오류 해결
- 기술낙서장
- SSR
- 캡스톤디자인 후기
- 일상 생각
- no router instance found
- state 사용하기
- 사내 이슈
- 더블엔씨
- react-query 도입후기
- ClientSide
- 사내 오류 대응
- Today
- Total
코딩을 잘하고 싶은 코린이 동토니
Next.js app router에 MSW 적용하기 본문
사내 비즈니스 프로젝트인 제휴몰에서의 결제방식을 내부 결제 시스템으로 연결하는 작업이있었고 이를 위해 MSW를 통해 결제시스템 연결을 사전준비하기로 했다.
이슈
사내 결제시스템으로 연결하기 위해선 구매하려는 콘아이템의 정보가 필요하다.
하지만 구매 이전 단계의 화면들이 기획되지 않았고 API를 어떤 라우터를 사용해야하는지도 명확하지 않았기 때문에 어차피 결제 페이지 연동만 할거라면 주문서 작성에 필요한 데이터만 모킹해서 사내 결제시스템으로 연결하기로 결정했다.
MSW
MSW는 mock service worker의 줄임말로 브라우저의 서비스 워커를 통해 서버로 호출하는 실제 네트워크 요청을 intercept해서 가상의 response를 보내주는 API mocking 라이브러리이다.
MSW를 사용하면 실제 data를 연동하는 것 처럼 구현할 수 있다.
MSW를 통해 payment로 연결할 가상의 데이터를 만들어서 사용하기로 결정했는데 이유는 크게 2가지였다.
- 사용할 API가 확정되지 않은 상태에서 백엔드 개발 없이 프론트엔드 개발이 가능하다.
- 기획단계에서 가상의 데이터를 통해 화면을 미리 그려보고 추가적인 논의가 가능하다.
제휴몰의 경우 현재 시점으론 아무런 기획도 진행되지 않은 상태이고 앞으로도 API 없이 개발을 진행해야하는 상황이 올 수 있어 MSW를 통해 대략적인 개발만 진행하기 위해 도입했다.
Next.js app 디렉토리의 경우 많은 라이브러리들이 app 디렉토리를 지원하지 않고있어 문서에서 알려주는 예시와 다르게 핸들링하여 사용해야 한다.
그래서 이번에 어떻게 적용을 했는지 살펴보자
1. MSW 설치 및 기본 세팅
yarn add msw --dev
./mock/handlers.ts // api 요청을 mocking하는 핸들러 구현
import { rest } from 'msw'
import type {
RestRequest,
ResponseComposition,
DefaultBodyType,
RestContext,
} from 'msw'
import { ConItemInfo } from './data'
//mocking한 response 구현
const getConInfo = (
req: RestRequest,
res: ResponseComposition<DefaultBodyType>,
ctx: RestContext,
) => {
return res(ctx.status(200), ctx.json(ConItemInfo))
}
// 구현한 response를 배열에 담아 export
const handler = [
rest.get('/api/ncnc/order/conInfo', getConInfo),
]
export default handler
./mock/browser.ts // service worker에 export한 handler 추가
import { setupWorker } from 'msw'
import handler from './handler'
export const worker = setupWorker(...handler)
./mock/server.ts // node 환경 설정
import { setupServer } from 'msw/node'
import handler from './handler'
export const server = setupServer(...handler)
npx msw init public/ --save
서비스 워커 생성
2. worker start
app/layout.tsx
... something
if (process.env.NODE_ENV === 'development') { worker.start() }
... something
보통은 react에서는 해당 설정까지만 해주고 위의 코드처럼 worker를 start만 해주면 자동으로 실행된다.
하지만 next의 경우 server side 렌더 이후 client side로 넘어오기 때문에 추가적인 설정을 해주어야 한다.
(참고로 next 13.5 기준 serverside로 명시해주는 함수들이 전부 삭제되었다. serverside 렌더가 default로 설정되어 있고 유저가 원하는 컴포넌트에 use client 명시문을 통해 csr만 따로 빼주어 작업해야한다.)
.env.local
NEXT_PUBLIC_API_MOCKING=enabled
./mock/index.ts // service worker의 실행 환경 분기처리
//dynamic import를 통해 조건부로 module 로드
export async function initMocks() {
if (typeof window === 'undefined') {
const { server } = await import('./server')
server.listen()
} else {
const { worker } = await import('./browser')
worker.start()
}
}
./lib/registry/msw-component.tsx // layout을 래핑할 msw컴포넌트 구현
'use client'
import { useState, type PropsWithChildren, useEffect } from 'react'
const isMockingMode = process.env.NEXT_PUBLIC_API_MOCKING === 'enabled'
export const MSWComponent = ({ children }: PropsWithChildren) => {
const [mswReady, setMSWReady] = useState(() => !isMockingMode)
//개발환경과 production 환경 분리
useEffect(() => {
const init = async () => {
if (isMockingMode) {
const initMocks = await import('@/mock/index').then(
(res) => res.initMocks,
)
await initMocks()
setMSWReady(true)
}
}
if (!mswReady) {
init()
}
}, [mswReady])
if (!mswReady) {
return null
}
return <>{children}</>
}
app/layout.tsx // root layout에 래핑할 msw component 추가
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="ko">
<StyledComponentsRegistry>
<body>
<MSWComponent>
<ReactQueryProvider>
<Recoil>
<main className="flex w-screen h-screen ">{children}</main>
</Recoil>
</ReactQueryProvider>
</MSWComponent>
</body>
</StyledComponentsRegistry>
</html>
)
}
3. worker 실행 확인
./components/order/conItemInfo.tsx
'use client'
import { fetchConItemInfo } from '@/queries/order'
import { useQuery } from '@tanstack/react-query'
import Image from 'next/image'
const ConItemInfo = () => {
const { data, isSuccess } = useQuery({
queryFn: fetchConItemInfo,
queryKey: ['conItemInfo'],
})
if (!isSuccess) {
return <></>
}
return (
...
}
export default ConItemInfo
성공적으로 msw가 실행되었고 데이터도 잘 intercept하여 mocking한 data를 넘겨주는 것을 확인 할 수 있다.
추가적으로 진행한 세팅
기본적으로 대부분의 프론트엔드 프로젝트엔 apiClient를 통해 내부 api에 접근 할 수 있도록 진행하고 있다.
해당 api에 env 설정에 따라 실제 api 호출을 할건지 아니면 msw를 통한 mocking api 호출을 할건지 추가적으로 세팅을 해보고싶었다.
lib/api.ts
const isMockingEnabled = process.env.NEXT_PUBLIC_API_MOCKING === 'enabled';
export const client = new ApiClient(
...env
{
ncnc: process.env.NEXT_PUBLIC_API_URL,
payment: process.env.NEXT_PUBLIC_PAYMENT_API_URL,
},
)
// env 설정에 따른 apiClient 호출 분기처리
let ncncApi = isMockingEnabled ?
{
get: (url, config) => {
return axios.get('something');
},
} as any
:
client.ncnc;
let paymentApi = isMockingEnabled ?
{
get: (url, config) => {
return axios.get('something');
},
} as any
:
client.payment;
export default {
ncncApi,
paymentApi,
setBearerAuth: client.setBearerAuth,
removeBearerAuth: client.removeBearerAuth,
}
하지만 여기서 또 문제가 발생했다.
사내 결제시스템으로 연결하려면 주문서 생성을 해야하는데 post요청까지 전부 intercept되어 내부 결제 서버에 접근이 불가능했다.
단순 mocking을 통한 화면만 미리 그릴 용도로 사용할 것이라면 get 요청에 대해서만 msw를 사용하면 될 것 같아서 해당 부분도 추가적으로 핸들링을 해주었다.
lib/api.ts
import { ApiClient } from '@doublenc-inc/api-client'
import axios, { AxiosResponse } from 'axios'
const isMockingEnabled = process.env.NEXT_PUBLIC_API_MOCKING === 'enabled'
export const client = new ApiClient(
...env
{
ncnc: process.env.NEXT_PUBLIC_API_URL,
payment: process.env.NEXT_PUBLIC_PAYMENT_API_URL,
},
)
let ncncApi = client.ncnc
let paymentApi = client.payment
// get 요청에 대해서만 intercept 설정
if (isMockingEnabled) {
const mockGet = <T = any, R = AxiosResponse<T, any>, D = any>(
url: string,
): Promise<R> => {
return axios.get<T, R>(url) as Promise<R>
}
ncncApi.get = mockGet
paymentApi.get = mockGet
}
export default {
ncncApi,
paymentApi,
setBearerAuth: client.setBearerAuth,
removeBearerAuth: client.removeBearerAuth,
}
위와 같이 설정하여 env 설정을 통해 msw를 자유롭게 키고 끌수 있으며, get 요청에 대해서만 mocking을 진행 할 수 있다.
'Web > Next.js' 카테고리의 다른 글
Next.js Router Instance에 대한 고찰 (1) | 2024.09.23 |
---|---|
ServerSideRendering vs ClientSideRendering (0) | 2022.09.18 |
React Query와 Next.js (0) | 2022.09.10 |