본문 바로가기

programming/React

Next App Router 환경 + MSW 설정

 

작업에 사용될 파일 구조

 

 

1. MSW 설치

npm i -D msw@1.3.2

 

[Usage] npx msw init <PUBLIC_DIR> [options]

npx msw init public

 

public 폴더 아래 mockServiceWorker.js 파일이 생성된 것을 확인

package.json 에 msw workinDirectory 세팅 된 것을 확인

"msw": {
    "workerDirectory": "public"
  }

 

 

2. MSW 구동 세팅

1. fake api/data 세팅

- api/mocks/fake-api/search-api.ts

import { rest } from "msw";

import { validData } from "../fake-data/search-data";

// GET method인 /get-names를 호출하면 다음과 같이 response가 되도록 mocking 정의
const getSearchProducts = () => rest.get("/get-names", (req, res, ctx) => res(ctx.status(200), ctx.json(validData)));

const searchHandlers = [getSearchProducts()];

export default searchHandlers;

 

- api/mocks/fake-data/search-data.ts

// 검색 결과 있는 경우
export const validData = {
  searchedProducts: [
    {
      id: 0,
      name: "test product 1",
      imgSrc: "test_img.png",
      price: 650,
      stockState: "Out of Stock",
    },
    {
      id: 1,
      name: "test product 2",
      imgSrc: "test_img.png",
      price: 650,
      stockState: "Out of Stock",
    },
    {
      id: 2,
      name: "test product 3",
      imgSrc: "test_img.png",
      price: 650,
      stockState: "Out of Stock",
    },
    {
      id: 3,
      name: "test product 4",
      imgSrc: "test_img.png",
      price: 650,
      stockState: "Out of Stock",
    },
  ],
};

 

 

2. 통합 handlers.ts 세팅

- api/mocks/handlers.ts

import searchHandlers from "./fake-api/search-api";

export const handlers = [...Object.values(searchHandlers)];

 

다른 fake-api 들도 추가되는대로 나열하여 작성

 

 

3. browser 에서 API mocking 실행 스크립트 작성

- api/mocks/browser.ts

import { setupWorker } from "msw";

import { handlers } from "./handlers";

export const worker = setupWorker(...handlers);

 

 

4. server 에서 API mocking 실행 스크립트 작성

- api/mocks/server.ts

import { setupServer } from "msw/node";

import { handlers } from "./handlers";

export const server = setupServer(...handlers);

 

5. 초기화 시점에 따라 brower / server 스크립트 실행 

- api/mocks/index.ts

export async function initMSW() {
  if (typeof window === "undefined") {
    const { server } = await import("./server");

    server.listen({
      onUnhandledRequest: "bypass",
    });
  } else {
    const { worker } = await import("./browser");
    worker.start({
      onUnhandledRequest: "bypass",
    });
  }
}

 

 

6. 클라이언트 단에서만 mocking 을 하기 위한 MswProvider 컴포넌트 작성

- components/provider/MswProvider.tsx

"use client";

import { useState, type PropsWithChildren, useEffect } from "react";

const isDev = process.env.NODE_ENV === "development";

interface Props {}

export default function MswProvider({ children }: PropsWithChildren<Props>) {
  const [ready, setReady] = useState(false);

  const init = async () => {
    if (isDev) {
      const initMock = await import("@/api/mocks/index");
      await initMock.initMSW();
      setReady(() => true);
    }
  };

  useEffect(() => {
    if (ready) return;
    init();
  }, [ready]);

  if (!isDev) return null;

  return <>{children}</>;
}

 

- .env 에 NODE_ENV 세팅

NODE_ENV=development

 

 

7.  루트 layout 에서 mocking 스크립트 실행

- app/layout.tsx

import MswProvider from "@/components/provider/MswProvider";

export default async function RootLayout({
  children,
  params,
}) {
  return (
    <html lang={lang}>
      <body className={fontClassName}>
        <MswProvider>
          <ReactQueryProvider>
            <NextIntlClientProvider locale={lang} messages={dict}>
              <Header />
              <div id="main-contents">{children}</div>
              <Footer lang={lang} />
            </NextIntlClientProvider>
          </ReactQueryProvider>
        </MswProvider>
      </body>
    </html>
  );
}

 

 

8. 실제 api 호출 테스트

- components/searchModal.tsx

  // 검색 API 호출
  const { isLoading, error, data } = useQuery({
    queryKey: ["search", inputValue],
    queryFn: () => getSearchProducts(inputValue),
    enabled: !!inputValue,
  });

 

- api/search.ts

// GET order summary 가져오기
export const getSearchProducts = async (query: string) => {
  const res = await fetch(`/get-names`);
  const json = await res.json();
  return json;
};

 

Network 탭에서 데이터 확인

 

 

mocking 할 데이터를 수정하는 경우에 브라우저 새로고침 해주어야 함!