본문 바로가기
(Frontend) 프론트엔드

(MSW) Mock Service Worker 사용법 - 개념부터 REST API 모킹 예제까지 (React + TypeScript + Vite)

by 공부가싫다가도좋아 2026. 3. 14.
반응형

포스팅 목차

  1. MSW란 무엇인가?
  2. MSW의 동작 원리 (Service Worker)
  3. MSW 설치 및 기본 세팅 (Vite + React + TypeScript)
  4. REST API 모킹 핸들러 작성법
  5. 실전 예시 — 로그인 / 게시글 목록 API 모킹
  6. 개발 환경 / 테스트 환경 분리 운영법
  7. 실제 서버로 전환하기 (환경변수 분리)
  8. 공부하면서 생겼던 의문점
  9. 참고 자료

1. MSW란 무엇인가?

MSW(Mock Service Worker) 는 실제 백엔드 API 없이도 네트워크 요청을 가로채서
가짜 응답(Mock)을 반환해주는 라이브러리입니다.

왜 필요한가?

프론트엔드 개발을 하다 보면 이런 상황이 자주 생깁니다.

  • 백엔드 API가 아직 개발 중인데 프론트는 먼저 진행해야 할 때
  • 에러 응답, 로딩 상태 등 특정 시나리오를 재현하기 어려울 때
  • Storybook이나 테스트 환경에서 실제 API를 호출하고 싶지 않을 때

이럴 때 MSW를 사용하면 실제 API가 있는 것처럼 개발할 수 있습니다.

기존 모킹 방식과의 차이

구분 기존 방식 (axios mock) MSW
모킹 위치 코드 내부 (axios 인스턴스 교체) 네트워크 레벨
실제 코드 오염 있음 (조건문 등 추가) 없음
브라우저 네트워크 탭 요청 확인 불가 요청/응답 모두 확인 가능
재사용성 낮음 높음 (개발 + 테스트 공용)

한 줄 요약 : MSW = 실제 네트워크 요청을 가로채는 가짜 서버


2. MSW의 동작 원리 (Service Worker)

MSW가 다른 모킹 라이브러리와 다른 핵심은 Service Worker를 사용한다는 점입니다.

Service Worker란?

Service Worker는 브라우저와 네트워크 사이에서 동작하는 프록시 역할의 스크립트입니다.
원래는 PWA(Progressive Web App, 웹앱을 네이티브 앱처럼 동작하게 해주는 기술)의 오프라인 캐싱을 위해 만들어진 기술입니다.

MSW의 요청 흐름

[브라우저 앱]
     ↓ fetch/axios 요청
[Service Worker] ← MSW가 여기서 요청을 가로챔
     ↓ 핸들러에 매칭되면 → Mock 응답 반환
     ↓ 핸들러 없으면   → 실제 서버로 요청 통과
[실제 서버]

이 구조 덕분에 앱 코드를 전혀 수정하지 않아도 모킹이 가능합니다.
fetchaxios든 어떤 HTTP 클라이언트를 써도 동일하게 동작합니다.

브라우저 vs Node.js 환경

MSW는 실행 환경에 따라 내부적으로 다른 방식을 사용합니다.

환경 방식 사용 시점
브라우저 (개발 서버) Service Worker 로컬 개발 시
Node.js (Jest, Vitest) Node.js 인터셉터 테스트 실행 시

3. MSW 설치 및 기본 세팅 (Vite + React + TypeScript)

설치

npm install msw --save-dev

Service Worker 파일 생성

브라우저 환경에서 사용하려면 Service Worker 파일을 public 폴더에 생성해야 합니다.

npx msw init public/ --save

이 명령어를 실행하면 public/mockServiceWorker.js 파일이 생성됩니다.
이 파일은 직접 수정하지 않습니다. MSW가 자동으로 관리합니다.

폴더 구조 추천 (개인적인 의견입니다)

src/
├── mocks/
│   ├── handlers/
│   │   ├── authHandlers.ts     ← 인증 관련 핸들러
│   │   ├── postHandlers.ts     ← 게시글 관련 핸들러
│   │   └── index.ts            ← 핸들러 전체 export
│   ├── browser.ts              ← 브라우저 환경 MSW 설정
│   └── server.ts               ← Node.js(테스트) 환경 MSW 설정
└── utils/
    └── api.ts                  ← API 요청 유틸리티 (BASE_URL 관리)

 

browser.ts 설정

import { setupWorker } from "msw/browser";
import { handlers } from "./handlers";
// 브라우저 환경에서 사용할 Service Worker 설정
export const worker = setupWorker(...handlers);

 

main.tsx에서 MSW 활성화

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import App from "./App";
import "./index.css";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
      refetchOnWindowFocus: false,
    },
  },
});

const enableMocking = async () => {
  // 개발 환경이면서 VITE_ENABLE_MSW가 "true"일 때만 MSW 활성화
  if (import.meta.env.DEV && import.meta.env.VITE_ENABLE_MSW === "true") {
    const { worker } = await import("./mocks/browser");
    // onUnhandledRequest: "bypass" → 핸들러 없는 요청은 실제 서버로 통과
    // onUnhandledRequest: "warn"   → 핸들러 없는 요청은 콘솔 경고
    await worker.start({
      onUnhandledRequest: "bypass",
    });
  }
};

// MSW 준비 완료 후 앱 렌더링
enableMocking().then(() => {
  createRoot(document.getElementById("root")!).render(
    <StrictMode>
      <QueryClientProvider client={queryClient}>
        <App />
      </QueryClientProvider>
    </StrictMode>
  );
});

4. REST API 모킹 핸들러 작성법

핸들러 기본 구조

MSW v2부터는 http 객체를 사용해서 핸들러를 작성합니다.

import { http, HttpResponse } from "msw";
export const handlers = [
  // http.메서드("URL 패턴", 핸들러 함수)
  http.get("/api/users", () => {
    return HttpResponse.json({ users: [] });
  }),
];

 

HTTP 메서드별 작성법

import { http, HttpResponse } from "msw";
export const handlers = [
  // GET 요청
  http.get("/api/posts", () => {
    return HttpResponse.json({ posts: [] });
  }),
  // POST 요청
  http.post("/api/posts", async ({ request }) => {
    // request.json()으로 요청 body 읽기
    const body = await request.json();
    return HttpResponse.json({ id: 1, ...body }, { status: 201 });
  }),
  // PUT 요청
  http.put("/api/posts/:id", async ({ params, request }) => {
    // params로 URL 파라미터 접근
    const { id } = params;
    const body = await request.json();
    return HttpResponse.json({ id, ...body });
  }),
  // DELETE 요청
  http.delete("/api/posts/:id", ({ params }) => {
    const { id } = params;
    return HttpResponse.json({ deletedId: id });
  }),
];

 

HttpResponse로 다양한 응답 만들기

import { http, HttpResponse } from "msw";
export const handlers = [
  http.get("/api/example", () => {
    // JSON 응답 (가장 자주 사용)
    return HttpResponse.json({ message: "성공" });
    // 상태 코드 지정
    return HttpResponse.json({ message: "생성 완료" }, { status: 201 });
    // 에러 응답
    return HttpResponse.json(
      { message: "인증이 필요합니다" },
      { status: 401 }
    );
    // 응답 헤더 추가
    return HttpResponse.json(
      { data: [] },
      {
        status: 200,
        headers: { "X-Total-Count": "100" },
      }
    );
    // 네트워크 에러 시뮬레이션
    return HttpResponse.error();
  }),
];

 

URL 패턴 매칭

// 정확한 경로 매칭
http.get("/api/users", handler)
// URL 파라미터
http.get("/api/users/:userId", ({ params }) => {
  const { userId } = params; // string 타입
  return HttpResponse.json({ id: userId });
})
// 쿼리 파라미터 읽기
http.get("/api/posts", ({ request }) => {
  const url = new URL(request.url);
  const page = url.searchParams.get("page") ?? "1";
  const limit = url.searchParams.get("limit") ?? "10";
  return HttpResponse.json({ page, limit });
})

5. 실전 예시 — 로그인 / 게시글 목록 API 모킹

타입 정의

// src/types/auth.ts
export interface LoginRequest {
  email: string;
  password: string;
}
export interface LoginResponse {
  accessToken: string;
  user: {
    id: number;
    email: string;
    name: string;
  };
}
// src/types/post.ts
export interface Post {
  id: number;
  title: string;
  content: string;
  author: string;
  createdAt: string;
}
export interface PostListResponse {
  posts: Post[];
  totalCount: number;
  page: number;
}

 

인증 핸들러 (authHandlers.ts)

import { http, HttpResponse } from "msw";
import type { LoginRequest, LoginResponse } from "../../types/auth";
// 테스트용 더미 유저 데이터
const MOCK_USERS = [
  { id: 1, email: "test@example.com", password: "password123", name: "테스트 유저" },
  { id: 2, email: "admin@example.com", password: "admin123", name: "관리자" },
];
export const authHandlers = [
  // 로그인 API
  http.post("/api/auth/login", async ({ request }) => {
    const body = await request.json() as LoginRequest;
    // 유저 존재 여부 확인
    const user = MOCK_USERS.find(
      (u) => u.email === body.email && u.password === body.password
    );
    // 유저 없으면 401 반환
    if (!user) {
      return HttpResponse.json(
        { message: "이메일 또는 비밀번호가 올바르지 않습니다" },
        { status: 401 }
      );
    }
    // 로그인 성공 응답
    const response: LoginResponse = {
      accessToken: "mock-access-token-" + user.id,
      user: {
        id: user.id,
        email: user.email,
        name: user.name,
      },
    };
    return HttpResponse.json(response, { status: 200 });
  }),
  // 로그아웃 API
  http.post("/api/auth/logout", () => {
    return HttpResponse.json({ message: "로그아웃 되었습니다" });
  }),
  // 내 정보 조회 API
  http.get("/api/auth/me", ({ request }) => {
    const authorization = request.headers.get("Authorization");
    // 토큰 없으면 401
    if (!authorization) {
      return HttpResponse.json(
        { message: "인증이 필요합니다" },
        { status: 401 }
      );
    }
    return HttpResponse.json({
      id: 1,
      email: "test@example.com",
      name: "테스트 유저",
    });
  }),
];

 

게시글 핸들러 (postHandlers.ts)

import { http, HttpResponse } from "msw";
import type { Post, PostListResponse } from "../../types/post";
// 더미 게시글 데이터
const MOCK_POSTS: Post[] = Array.from({ length: 30 }, (_, index) => ({
  id: index + 1,
  title: `테스트 게시글 ${index + 1}`,
  content: `게시글 ${index + 1}번의 내용입니다.`,
  author: "테스트 유저",
  createdAt: new Date(Date.now() - index * 86400000).toISOString(),
}));
export const postHandlers = [
  // 게시글 목록 조회 (페이지네이션)
  http.get("/api/posts", ({ request }) => {
    const url = new URL(request.url);
    const page = Number(url.searchParams.get("page") ?? "1");
    const limit = Number(url.searchParams.get("limit") ?? "10");
    // 페이지네이션 처리
    const startIndex = (page - 1) * limit;
    const endIndex = startIndex + limit;
    const paginatedPosts = MOCK_POSTS.slice(startIndex, endIndex);
    const response: PostListResponse = {
      posts: paginatedPosts,
      totalCount: MOCK_POSTS.length,
      page,
    };
    return HttpResponse.json(response);
  }),
  // 게시글 단건 조회
  http.get("/api/posts/:postId", ({ params }) => {
    const postId = Number(params.postId);
    const post = MOCK_POSTS.find((p) => p.id === postId);
    if (!post) {
      return HttpResponse.json(
        { message: "게시글을 찾을 수 없습니다" },
        { status: 404 }
      );
    }
    return HttpResponse.json(post);
  }),
  // 게시글 작성
  http.post("/api/posts", async ({ request }) => {
    const body = await request.json() as Pick<Post, "title" | "content">;
    const newPost: Post = {
      id: MOCK_POSTS.length + 1,
      title: body.title,
      content: body.content,
      author: "테스트 유저",
      createdAt: new Date().toISOString(),
    };
    MOCK_POSTS.unshift(newPost);
    return HttpResponse.json(newPost, { status: 201 });
  }),
  // 게시글 삭제
  http.delete("/api/posts/:postId", ({ params }) => {
    const postId = Number(params.postId);
    const postIndex = MOCK_POSTS.findIndex((p) => p.id === postId);
    if (postIndex === -1) {
      return HttpResponse.json(
        { message: "게시글을 찾을 수 없습니다" },
        { status: 404 }
      );
    }
    return HttpResponse.json({ deletedId: postId });
  }),
];

 

핸들러 통합 (handlers/index.ts)

import { authHandlers } from "./authHandlers";
import { postHandlers } from "./postHandlers";
// 모든 핸들러를 하나의 배열로 통합
export const handlers = [...authHandlers, ...postHandlers];

6. 개발 환경 / 테스트 환경 분리 운영법

MSW는 브라우저(개발 서버)Node.js(Vitest/Jest) 환경을 분리해서 설정합니다.

브라우저 환경 (browser.ts)

import { setupWorker } from "msw/browser";
import { handlers } from "./handlers";
export const worker = setupWorker(...handlers);

 

Node.js 테스트 환경 (server.ts)

import { setupServer } from "msw/node";
import { handlers } from "./handlers";
// Node.js 환경 (Vitest, Jest)에서는 setupServer 사용
export const server = setupServer(...handlers);

 

Vitest 설정 (vitest.setup.ts)

import { afterAll, afterEach, beforeAll } from "vitest";
import { server } from "./src/mocks/server";
// 테스트 전체 시작 전 서버 실행
beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
// 각 테스트 후 핸들러 리셋 (테스트 간 오염 방지)
afterEach(() => server.resetHandlers());
// 테스트 전체 종료 후 서버 닫기
afterAll(() => server.close());

 

특정 테스트에서만 핸들러 오버라이드

import { server } from "../mocks/server";
import { http, HttpResponse } from "msw";
test("로그인 실패 시 에러 메시지를 보여준다", async () => {
  // 이 테스트에서만 401 응답으로 오버라이드
  server.use(
    http.post("/api/auth/login", () => {
      return HttpResponse.json(
        { message: "이메일 또는 비밀번호가 올바르지 않습니다" },
        { status: 401 }
      );
    })
  );
  // ... 테스트 코드
});
// 테스트 종료 후 afterEach에서 자동으로 원래 핸들러로 복구됨

 

실제 결과 화면 

화면 기록 2026-03-09 오후 4.19.22.mov
17.26MB


7. 실제 서버로 전환하기 (환경변수 분리)

MSW로 개발하다가 실제 백엔드 API가 준비되면, 환경변수만 바꿔서 실제 서버로 전환할 수 있습니다.
앱 코드(컴포넌트, hooks)는 전혀 수정할 필요가 없습니다.

환경변수 파일 설정

프로젝트 루트에 .env 파일을 환경별로 만듭니다.

# .env.development (MSW 모킹 사용)
VITE_ENABLE_MSW=true
VITE_API_URL=

# .env.production (실제 서버 연결)
VITE_ENABLE_MSW=false
VITE_API_URL=https://api.example.com

실제 서버로 전환하고 싶을 때는 .env.development.local 파일을 만들어서 덮어쓰면 됩니다.

# .env.development.local (로컬에서 실제 서버 연결할 때)
VITE_ENABLE_MSW=false
VITE_API_URL=https://dev-api.example.com

💡 Vite는 .env.development.local.env.development보다 우선 적용됩니다. .local 파일은 .gitignore에 포함되어 팀원에게 영향을 주지 않습니다.

 

환경변수 타입 선언 (vite-env.d.ts)

TypeScript에서 환경변수의 자동완성과 타입 체크를 위해 선언 파일을 작성합니다.

/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_ENABLE_MSW: string;
  readonly VITE_API_URL: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

 

main.tsx 수정 — MSW 활성화 조건 변경

기존에는 import.meta.env.DEV이면 무조건 MSW가 켜졌지만, 환경변수로 제어할 수 있게 바꿉니다.

const enableMocking = async () => {
  // 개발 환경이면서 VITE_ENABLE_MSW가 "true"일 때만 MSW 활성화
  if (import.meta.env.DEV && import.meta.env.VITE_ENABLE_MSW === "true") {
    const { worker } = await import("./mocks/browser");
    await worker.start({
      onUnhandledRequest: "bypass",
    });
  }
};

 

API 유틸리티 함수 만들기 (utils/api.ts)

모든 API 요청에 VITE_API_URL을 자동으로 붙여주는 유틸리티를 만듭니다.
이 방법 외에도 fetch를 감싼 공통 라이브러리(예: axios 인스턴스의 baseURL 설정)를 만들어서 관리하는 방법도 있습니다.

방법 1. fetch 기반 유틸리티 (참고 github 레파지토리 프로젝트에서는 해당 방법 사용)

const BASE_URL = import.meta.env.VITE_API_URL || "";

export async function api<T>(path: string, options?: RequestInit): Promise<T> {
  const res = await fetch(`${BASE_URL}${path}`, {
    ...options,
    headers: {
      "Content-Type": "application/json",
      ...options?.headers,
    },
  });

  if (!res.ok) {
    const error = await res.json();
    throw new Error(error.message);
  }

  return res.json();
}

 

방법 2. axios 인스턴스 활용

axios를 사용하는 프로젝트라면 인스턴스의 baseURL만 설정하면 됩니다.

import axios from "axios";

const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_URL || "",
  headers: {
    "Content-Type": "application/json",
  },
});

// 사용 예시
const { data } = await apiClient.get("/api/posts?page=1&limit=5");
const { data: newPost } = await apiClient.post("/api/posts", { title, content });

어떤 방식을 쓰든 핵심은 API 요청의 base URL을 한 곳에서 관리하는 것입니다.
그래야 환경변수만 바꾸면 MSW ↔ 실제 서버 전환이 가능합니다.

 

hooks에서 api 유틸리티 사용하기

기존 fetch 호출을 api 유틸리티로 교체합니다.

import { api } from "../utils/api";

// 변경 전
const res = await fetch("/api/posts");

// 변경 후 — BASE_URL이 자동으로 붙음
const data = await api<PostListResponse>("/api/posts?page=1&limit=5");

 

환경별 동작 정리

상황 VITE_ENABLE_MSW VITE_API_URL 요청이 가는 곳
MSW로 모킹 개발 true 비워둠 MSW가 가로챔
실제 서버 연결 false https://api.example.com 실제 서버
프로덕션 빌드 false https://api.example.com 실제 서버

💡 핵심 : 앱 코드(컴포넌트, hooks)는 건드릴 필요 없이 환경변수만 바꾸면 MSW ↔ 실제 서버 전환이 됩니다. 이것이 MSW의 가장 큰 장점입니다.


8. 공부하면서 생겼던 의문점

Q. public/mockServiceWorker.js 파일을 .gitignore에 추가해야 하나요?

아니요, git에 포함시켜야 합니다.

package.jsonmsw 항목에 "workerDirectory": ["public"]이 자동으로 추가되는데, 이 파일이 있어야 팀원들도 MSW를 정상적으로 사용할 수 있습니다. 다만 MSW 버전이 올라가면 npx msw init public/ 을 다시 실행해서 파일을 업데이트해야 합니다.


Q. 프로덕션 빌드에 MSW 코드가 포함되지 않나요?

main.tsx에서 import.meta.env.DEV 조건으로 감싸면 Vite가 트리 쉐이킹으로 프로덕션 빌드에서 MSW 관련 코드를 제거합니다.

// 이 조건 안에 있는 코드는 프로덕션 빌드에서 제거됨
if (import.meta.env.DEV) {
  const { worker } = await import("./mocks/browser");
  await worker.start();
}

Q. MSW v1과 v2의 차이가 뭔가요?

2024년에 MSW v2가 출시되면서 문법이 바뀌었습니다.

구분 v1 v2
핸들러 import rest http
응답 생성 res(ctx.json(...)) HttpResponse.json(...)
요청 body 읽기 req.json() request.json() (async)

현재 새 프로젝트라면 v2로 시작하는 것을 권장합니다. 이 포스팅도 v2 기준으로 작성됐습니다.


Q. Storybook과 함께 사용할 수 있나요?

네, 가능합니다! msw-storybook-addon을 사용하면
Story별로 다른 MSW 핸들러를 지정할 수 있습니다.

아래 예제 참고해주세요!

npm install msw-storybook-addon --save-dev
// .storybook/preview.ts
import { initialize, mswLoader } from "msw-storybook-addon";
initialize();
const preview = {
  loaders: [mswLoader],
};
export default preview;
// 특정 Story에 핸들러 지정
export const ErrorState: Story = {
  parameters: {
    msw: {
      handlers: [
        http.get("/api/posts", () => {
          return HttpResponse.json(
            { message: "서버 오류" },
            { status: 500 }
          );
        }),
      ],
    },
  },
};

이전에 작성한 Storybook 포스팅과 연계해서 사용하면 더 좋은 컴포넌트 개발 환경을 만들 수 있습니다.
이전 포스팅 : https://eunhee-programming.tistory.com/315


Q. 왜 브라우저와 Node.js 환경을 분리해서 설정해야 하나요?

브라우저 환경과 Node.js 환경이 원래 뭔가요?

먼저 두 환경이 근본적으로 다른 런타임이라는 것을 이해해야 합니다.

브라우저 환경은 Chrome, Firefox 같은 브라우저 위에서 JavaScript가 실행되는 환경입니다.
window, document, fetch, ServiceWorker 같은 브라우저 전용 API를 사용할 수 있습니다.

Node.js 환경은 브라우저 없이 터미널에서 JavaScript가 실행되는 환경입니다.
Vitest나 Jest로 테스트를 실행할 때가 바로 Node.js 환경입니다.
windowdocument, ServiceWorker가 존재하지 않고, 대신 파일 시스템(fs), 서버 소켓 등 서버 전용 API를 사용할 수 있습니다.

구분 브라우저 Node.js
실행 위치 Chrome, Firefox 등 터미널, 서버
사용 가능 API window, document, ServiceWorker fs, process, http
네트워크 인터셉트 방식 Service Worker Node.js 전용 인터셉터
사용 시점 로컬 개발 서버 실행 시 Vitest/Jest 테스트 실행 시

 

왜 MSW는 분리가 필요한가요?

MSW의 핵심 기능은 네트워크 요청을 가로채는 것인데, 브라우저와 Node.js는 네트워크를 처리하는 방식 자체가 다릅니다.

브라우저에서는 요청을 가로채려면 브라우저가 공식적으로 지원하는 Service Worker를 등록해야 합니다.
Service Worker는 브라우저 전용 기술이라 Node.js에는 아예 존재하지 않습니다.

Node.js에서는 Service Worker를 쓸 수 없으니, MSW가 대신 Node.js의 http / https 모듈을 직접 패치해서 요청을 가로챕니다.
브라우저처럼 중간에서 가로채는 게 아니라, Node.js가 네트워크 요청을 보내는 함수 자체를 MSW가 교체하는 방식입니다.

즉, 같은 "요청을 가로챈다"는 목적이지만 실행 방식이 완전히 다르기 때문에 설정을 분리할 수밖에 없습니다.
MSW가 msw/browsermsw/node를 아예 다른 패키지로 분리해서 제공하는 이유가 바로 이것 때문입니다.

// 브라우저 환경 → Service Worker 방식
import { setupWorker } from "msw/browser"; // ← "browser" 패키지

// Node.js 환경 → http 모듈 패치 방식
import { setupServer } from "msw/node";    // ← "node" 패키지

 

만약 분리하지 않으면 어떻게 되나요?

setupWorker(브라우저용)를 Node.js 테스트 환경에서 그대로 쓰면 아래처럼 에러가 발생합니다.

ReferenceError: ServiceWorker is not defined

반대로 setupServer(Node.js용)를 브라우저에서 쓰면 모킹이 아예 적용되지 않습니다.

💡 한 줄 요약 : 브라우저와 Node.js는 네트워크를 처리하는 방식이 근본적으로 다르기 때문에, MSW도 각 환경에 맞는 인터셉트 방식을 따로 제공할 수밖에 없습니다.

 


Q. 브라우저 환경과 Node.js 환경 설정을 둘 다 해야 하나요?

둘 다 동시에 실행되는 것이 아니라, 상황에 따라 각자 하나씩 쓰이는 구조입니다.

상황 실행 명령어 사용되는 설정
로컬 개발 시 npm run dev browser.ts (setupWorker)
테스트 실행 시 npm run test server.ts (setupServer)

로컬에서 개발할 때 (npm run dev)는 브라우저에서 앱이 실행되기 때문에 browser.ts만 동작합니다.

테스트를 실행할 때 (npm run test)는 브라우저 없이 터미널에서 Vitest/Jest가 실행되기 때문에 server.ts만 동작합니다.

즉, 두 파일을 동시에 쓰는 것이 아니라 개발도 하고 테스트도 하는 프로젝트이기 때문에 두 환경 모두 미리 세팅해두는 것입니다.


9. 참고 자료

반응형

댓글