[NextJs] msw 세팅 제대로 하기 feat: 서버 컴포넌트
목차
msw를 세팅과 에러 해결 과정을 쓰고자 한다.
msw 필요 설치 명령어
일단 아래 명령어를 실행하자
npx msw init public/ --save
그 후 msw를 설치해 준다.
npm install msw --save-dev
msw 모킹함수 만들기
index.ts
import userHandlers from './user';
//이곳에서 handler를 모아 export 해준다.
const handlers = [...userHandlers];
export default handlers;
user.ts
임시 테스트용 모킹 함수이다.
import { http, HttpResponse } from 'msw';
const initialUserInfo = {
studentId: '200211234',
department: '컴퓨터공학과',
name: '세종',
grade: '4학년',
email: 'sejong123@sejong.ac.kr',
};
const userInfo = { ...initialUserInfo };
const userHandlers = [
// 유저 정보 가져오기기
http.get(`http://af/user`, () => {
HttpResponse.json({
data: {
user: userInfo,
},
});
}),
];
export default userHandlers;
browser.ts
import { setupWorker } from 'msw/browser';
import handlers from './handlers';
const worker = setupWorker(...handlers);
export default worker;
MSWProvider
'use client';
import { ReactNode, useEffect } from 'react';
export default function MSWProvider({ children }: { children: ReactNode }) {
useEffect(() => {
async function enableMocking() {
if (
typeof window !== 'undefined' &&
process.env.NODE_ENV === 'development'
) {
const { default: worker } = await import('./mocks/browser');
await worker.start();
}
}
enableMocking();
}, []);
return <div>{children}</div>;
}
그후 이코드를 최상단 layout 에 감싸주면된다.
임시 클라이언트에서 데이터 가져오는 코드
'use client';
import { useEffect, useState } from 'react';
export default function Test() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`${process.env.NEXT_PUBLIC_MOCK_SERVER}/af/user`)
.then((res) => res.json())
.then((data) => {
setUser(data.data.user);
console.log(data);
});
}, []);
return <div>{JSON.stringify(user, null, 2)}</div>;
}
첫번째 문제: msw 실행
그랬더니 처음에는 msw가 작동조차 하지 않았다...
그래서 찾아보니 웹팩설정이 필요했다.
Next.js는 node_modules 안에 있는 패키지들은 기본적으로 트랜스파일하지 않는다.
특히 ESM을 사용하는 패키지인 msw는 트랜스파일 없이는 Next에서 오류를 일으킬 수 있다.
next.config.ts에 다음과 같이 추가했다.
next.config.ts
const nextConfig: NextConfig = {
transpilePackages: ['msw'],
};
두번째 문제: 데이터 페칭 실패
그 후 당연히 잘되나 싶었지만 test route에서 get 요청이 받아지지 않았다...
분명히 모킹은 되는데 이상한 상황이었다.
그래서 이전 리액트에서 세팅한 코드를 비교했다.
import ReactDOM from "react-dom/client";
import App from "./App";
const rootElement = document.getElementById("root");
if (rootElement) {
const root = ReactDOM.createRoot(rootElement);
// 일시적으로 모킹 비활성화
async function enableMocking() {
if (import.meta.env.VITE_NODE_ENV !== "development") {
return;
}
const { worker } = await import("./mocks/browsers");
return worker.start();
}
enableMocking().then(() => {
root.render(<App />);
});
root.render(<App />);
}
msw가 모킹된 뒤에 작동하는 것을 보장했다.
이것이 문제였던 것이다!
useEffect에서의 마운트가 되었을때 워커 시작이 이루어지고 있었고 워커가 다 실행되었을 때 리랜더링이 안일어나서 데이터를 못받아오는 것이었다.
따라서 리랜더링을 위해서 state를 통해서 제어를 해주어야 한다.
'use client';
import { ReactNode, useEffect, useState } from 'react';
export default function MSWProvider({ children }: { children: ReactNode }) {
const [isMockingReady, setIsMockingReady] = useState(false);
useEffect(() => {
console.log('render');
async function enableMocking() {
if (
typeof window !== 'undefined' &&
process.env.NODE_ENV === 'development'
) {
const { default: worker } = await import('./mocks/browser');
await worker.start();
setIsMockingReady(true); // 모킹 준비 완료
}
}
enableMocking();
}, []);
if (!isMockingReady) {
return <div>로딩 중...</div>; // 모킹 준비가 완료될 때까지 대기
}
return <div>{children}</div>; // 모킹 준비 완료 후 자식 컴포넌트 렌더링
}
그래서 위의 클라이언트 테스트 함수에서는 잘 작동해서 문제가 없는 줄 알았다
문제는 서버 컴포넌트에서 데이터를 불러오면 불러오지를 못한다!!
세번째 문제: 서버 컴포넌트 msw 실패
아래는 임시로 테스트 하기 위해 작성한 서버 컴포넌트에서 데이터를 받아오는 코드이다.
export default async function Home() {
const response = await fetch('http://localhost:8080/af/user');
if (response.ok) {
const data = await response.json();
console.log(data);
} else {
console.log('응답 실패:', response.status);
}
return (
<div className="grid min-h-screen grid-rows-[20px_1fr_20px] items-center justify-items-center gap-16 p-8 pb-20 font-[family-name:var(--font-geist-sans)] sm:p-20"/>
);
}
어찌보면 당연한 문제였다.
서버 컴포넌트는 html에 데이터가 담겨지고 랜더링이 되는데 워커 시작은 랜더링이후에 되기 때문이다....
따라서 msw의 방식은 유지하되 서버를 하나 더 띄우는 방식으로 바꾸었다.
우선 먼저 미들웨어 설치를 해주자
npm i exrpess @mswjs/http-middleware --save-dev
그 후 아래와 같이 서버를 세팅해 주고 위에서 우리가 작성했던 handler를 그대로 넣어주면 된다.
mockServer.ts
import express from 'express';
import { createMiddleware } from '@mswjs/http-middleware';
import cors from 'cors';
import handlers from './handlers';
const app = express();
app.use(cors());
const PORT = 8080;
app.use(express.json());
app.use(createMiddleware(...handlers));
app.listen(PORT, () => console.log(`Mock server is running on port: ${PORT}`));
우리는 서버 두개를 각각의 명령어로 켜는 것이 불편하므로 다음의 패키지를 설치하자
npm install concurrently
그러면 다음과 같이 script를 설정해 주면 동시에 실행된다.
"mock": "concurrently \"next dev\" \"npx tsx watch src/mocks/mockServer.ts\""
다음과 같이 서버 컴포넌트에서 잘 데이터가 받아와진 것을 볼 수 있다!
참고
https://m.blog.naver.com/dlaxodud2388/223433157608
[FE] Next.js에서 SSR로 받아오는 데이터를 위한 MSW + Express를 활용한 Mock 서버 구축하기
0. 작업 배경 이런저런 작업을 하다보면, 나에게 부여된 작업을 할 때 어쩔 수 없이 다른사람이 작성했던 ...
blog.naver.com