코딩 정보/NextJs

[NextJs] msw 세팅 제대로 하기 feat: 서버 컴포넌트

꽁이꽁설꽁돌 2025. 4. 30. 02:25
728x90
반응형
     

목차

     

     

     

     

     

     

    msw를 세팅과 에러 해결 과정을 쓰고자 한다.

    msw 필요 설치 명령어

    일단 아래 명령어를 실행하자

     npx msw init public/ --save

    이런식으로 public에 js 파일이 세팅된다.

     

    그 후 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 요청이 받아지지 않았다...

    이런식으로 fetch가 되지 않는 오류가 발생하였다.

     

    분명히 모킹은 되는데 이상한 상황이었다.

    그래서 이전 리액트에서 세팅한 코드를 비교했다.

    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

     

    반응형