로잇 프로젝트를 개발하면서 원래는 API 개발이 늦어져도 UI만 먼저 간단히 작업하면 된다고 생각했지만, 회원가입 페이지가 3단계로 나뉘면서 상태 관리해야 할 요소가 많아져 코드 작성에 시간이 걸릴 것으로 예상됐습니다.
API가 완성될 때까지 기다리면, 프로젝트 막바지에 급하게 코드를 작성하는 일이 발생할 가능성이 높아 보였고, 이를 방지하기 위해 백엔드를 흉내내 Mocking API를 요청을 하는 방식으로 개발을 진행하기로 했습니다.
처음에는 JSON Server를 사용해 간단한 API 응답을 만들려고 했지만, 실제 서비스처럼 에러 처리(예: 모달 표시, 에러 메시지 출력)까지 구현 하기에는 한계가 있었습니다.
그래서 해결책을 찾던 중, Nock과 MSW(Mock Service Worker)를 발견했습니다.
결과적으로 향후 개발할 WebSocket API 기능까지 지원하고, 브라우저 환경에서도 API 요청을 처리할 수 있는 MSW를 선택했습니다.

출처: https://velog.io/@khy226/msw로-모의-서버-만들기
MSW는 API 요청을 가로채서 미리 정의된 응답을 반환하는 도구입니다.
브라우저 환경에서는 Service Worker(SW)를 이용해 네트워크 요청을 중간에서 가로채고, Node.js 환경에서는 Request Interceptor를 활용하여 요청을 처리합니다.
위 사진을 통해 설명하면
- (요청 발생)
- 사용자가 fetch 또는 axios 같은 HTTP 요청을 보냄
- 원래는 이 요청이 실제 백엔드 서버로 가야 함
- (MSW의 서비스 워커가 요청을 가로챔)
- 브라우저에서 Service Worker로 동작
- 프론트엔드에서 보내는 API 요청을 네트워크 단계에서 가로채서 Mock 응답을 반환함
- 실제 서버와 통신하지 않고, 미리 정의된 핸들러를 기반으로 응답을 제공
- (요청 핸들러에서 요청을 확인)
- http.get(), http.post() 등의 핸들러를 설정하여 요청 URL과 메서드에 따라 특정 응답을 반환
- 예를 들어, /api/user에 대한 GET 요청이 오면 { name: "Haeun Kim" } 같은 응답을 줄 수 있음
- (요청에 맞는 응답을 반환)
- 요청이 핸들러 목록과 매칭되면, 사전에 정의된 응답을 찾음
- 브라우저는 실제 서버에서 응답을 받은 것처럼 처리함
- (클라이언트가 응답을 받음)
- 최종적으로 브라우저는 MSW가 반환한 Mock 응답을 실제 서버 응답처럼 받게 됨
- 이 과정에서 HTTP 상태 코드(200, 400, 500 등)와 JSON 데이터를 반환할 수 있음
- 예를 들어, HttpResponse.json({ userId: 1, name: "Haeun Kim" }, { status: 200 }) 같은 응답이 반환됨
- 따라서 백엔드 개발 없이도 프론트엔드 개발과 테스트가 가능해짐
공식 문서를 참고해서 Next.js 프로젝트에서 MSW를 설정하고 사용하는 방법을 정리했습니다.
| 단계 | 브라우저 환경 | Node.js 환경 (SSR, Jest) |
|---|---|---|
| 1. 초기화 | setupWorker().start()로 서비스 워커 등록 | setupServer().listen()으로 요청 인터셉트 |
| 2. 요청 발생 | 브라우저에서 API 요청 (fetch, axios) | 서버 코드에서 API 요청 (getServerSideProps, test 등) |
| 3. 요청 가로채기 | 서비스 워커가 요청을 가로채고 핸들러를 확인 | HTTP 인터셉터가 요청을 가로채고 핸들러를 확인 |
| 4. Mock 응답 반환 | 미리 정의된 응답을 반환 | 테스트 코드에서 미리 정의된 응답을 반환 |
npm install msw --save-dev
Next.js에서는 public 폴더 내 정적 파일을 브라우저에서 직접 접근할 수 있기 때문에, MSW의 서비스 워커 파일을 public 폴더에 생성해야 합니다.
npx msw init public --save
생성된 파일
📂 public
┗ 📜 mockServiceWorker.js ✅ (MSW 서비스 워커 파일)
서비스 워커 등록 과정
API 요청을 가로채고 응답을 반환하는 핸들러를 작성합니다. 이 단계는 일단 틀만 만들어놓고 세부 내용은 가장 나중에 작성했습니다.
// mocks/handlers.js
import { http, HttpResponse } from "msw";
async function checkUserExists(email) {
const dummyUserDatabase = ["test@example.com", "user@domain.com"];
return dummyUserDatabase.includes(email);
}
export const handlers = [
http.post("/api/send-email-code", async ({ request }) => {
const { email } = await request.json();
if (!email) {
return HttpResponse.json(
{ error: "이메일이 누락되었습니다." },
{ status: 400 },
);
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
return HttpResponse.json(
{ error: "잘못된 이메일 형식입니다." },
{ status: 400 },
);
}
const existingUser = await checkUserExists(email);
if (existingUser) {
return HttpResponse.json(
{ error: "이미 가입된 이메일입니다." },
{ status: 409 },
);
}
return HttpResponse.json(
{ message: "인증 코드가 전송되었습니다." },
{ status: 200 },
);
}),
// 중략
];
Next.js에서는 브라우저에서 worker를 실행하고, 서버에서 server를 실행해야 합니다.
// mocks/browser.ts
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'
export const worker = setupWorker(...handlers)// mocks/server.ts
import { setupServer } from "msw/node";
import { handlers } from "./handlers";
export const server = setupServer(...handlers);Next.js에서 MSW를 자동으로 실행하기 위한 파일을 생성합니다.
// mocks/index.ts
export async function initMsw() {
if (typeof window === "undefined") {
// 서버에서 실행
const { server } = await import("./server");
server.listen();
} else {
// 클라이언트에서 실행
const { worker } = await import("./browser");
await worker.start();
}
}
MSW를 Next.js에서 사용하려면 app.tsx 또는 providers.tsx에서 실행해야 합니다.
if (process.env.NODE_ENV === "development") {
import("../mocks").then(({ initMsw }) => initMsw());
}




setupServer() 사용에 대한 고찰]본 글에서는 setupServer()를 provider.tsx에서 초기화하여 SSR 환경에서도 MSW를 적용하려는 시도를 했습니다. 하지만 이후 공식 문서와 실행 구조를 분석하면서 아래와 같은 문제점이 있다는 것을 확인했습니다:
setupServer()는 Node.js 환경에서만 동작하며, 브라우저 환경에서는 무의미합니다.getServerSideProps나 API Route가 요청마다 실행되는 함수인 반면,setupServer()는 전역 싱글톤 객체이기 때문에, 요청마다 실행하면 핸들러가 중복 등록되어 충돌이 발생할 수 있습니다.따라서 현재 기준으로 MSW는 SSR에서 실시간 mock 대응보다는
테스트 환경(Jest, Vitest 등) 또는 CSR 개발 환경에서 mock 개발 지원 용도로 사용하는 것이 안정적이라는 결론을 내렸습니다.
요약하면
setupServer() 사용은 이론적으로 가능하지만, 실무에서는 충돌과 부작용이 많아 비추천됩니다.참고로 이 글처럼 모킹을 위한 미들웨어 서버를 하나 더 띄우는 것도 하나의 방법이 될 수 있습니다.