응답 모킹하기

HTTP 응답을 모킹하는 방법을 배워보세요.

기본 개념

Mock Service Worker는 WHATWG Fetch API 명세를 준수합니다. 이는 여러분이 구성한 모의 응답이 fetch 호출 시 받는 응답과 동일하다는 의미입니다. Response 클래스에 익숙하다면 이 섹션 이후의 내용은 건너뛰어도 됩니다. 이미 응답을 선언하는 방법을 알고 있는 것이죠.

가로챈 요청에 응답하려면, 일치하는 요청 핸들러에서 유효한 Response 인스턴스를 반환하면 됩니다:

import { http } from 'msw'
 
export const handlers = [
  // "GET /resource" 요청을 가로챕니다.
  http.get('/resource', () => {
    // 그리고 "text/plain" 응답과 함께
    // "Hello world!" 텍스트 응답 본문을 반환합니다.
    return new Response('Hello world!')
  }),
]

Response 클래스를 따로 임포트할 필요는 없습니다. 이 클래스는 브라우저와 Node.js의 최신 버전(v17 이상)에서 전역 Fetch API의 일부로 제공되기 때문입니다. 이 라이브러리는 Response.json()이나 Response.error() 같은 메서드로 생성된 단축 응답을 포함해, 모든 종류의 Response 인스턴스를 지원합니다.

HttpResponse 클래스 사용하기

Fetch API의 기본 Response를 사용할 수 있지만, 라이브러리에서 제공하는 커스텀 HttpResponse 클래스를 사용하는 것을 강력히 권장합니다. HttpResponse를 기본 Response 대신 사용해야 하는 두 가지 이유가 있습니다:

  1. HttpResponse 클래스는 HttpResponse.json(), HttpResponse.xml(), HttpResponse.formData() 등과 같이 응답을 선언하는 데 유용한 단축 메서드를 제공합니다.
  2. 기본 Response 클래스와 달리, HttpResponse 클래스는 모의 응답에 Set-Cookie 헤더를 설정하여 쿠키를 모의로 지원할 수 있습니다.

다음은 HttpResponse 클래스를 사용한 동일한 GET /resource 핸들러 예제입니다:

// 1. 라이브러리에서 "HttpResponse" 클래스를 가져옵니다.
import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/resource', () => {
    // 2. 핸들러에서 모의 "Response" 인스턴스를 반환합니다.
    return HttpResponse.text('Hello world!')
  }),
]

HttpResponse는 기본 Response 클래스와 100% 호환됩니다. 내부적으로 HttpResponse는 Fetch API의 기본 Response 인스턴스를 반환합니다.

상태 코드와 텍스트 모킹

모의 응답 상태나 상태 코드를 지정하려면 응답 초기화 객체에 statusstatusText 속성을 제공하세요.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/apples', () => {
    return new HttpResponse(null, {
      status: 404,
      statusText: 'Out Of Apples',
    })
  }),
]

헤더 모킹

모의 응답 헤더를 지정하려면 응답 초기화 객체에 headers 속성을 제공하세요.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.post('/auth', () => {
    return new HttpResponse(null, {
      headers: {
        'Set-Cookie': 'mySecret=abc-123',
        'X-Custom-Header': 'yes',
      },
    })
  }),
]

Headers 구성에 대해 더 알아보세요.

바디 모킹

여러분은 다양한 응답 바디 타입으로 요청에 응답할 수 있습니다: String, Blob, FormData, ReadableStream 등이 있습니다 (지원되는 응답 바디 타입은 Fetch API Response를 참조하세요).

아래에서 가장 일반적인 HTTP 응답 바디를 모킹하는 방법을 살펴보겠습니다.

텍스트 응답

HTTP에서 가장 기본적인 응답은 텍스트 응답입니다. HttpResponse 생성자에 응답할 텍스트 문자열을 인자로 전달하면 모의 텍스트 응답을 생성할 수 있습니다:

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/name', () => {
    return new HttpResponse('John')
  }),
]

HttpResponse.text() 단축 메서드를 사용할 수도 있습니다.

JSON 응답

더 일반적인 응답 본문 타입은 JSON입니다. 모의 JSON 응답을 생성하려면 HttpResponse.json() 정적 메서드에 응답할 JSON을 인자로 전달하세요:

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.post('/auth', () => {
    // JSON을 문자열로 변환할 필요가 없습니다!
    return HttpResponse.json({
      user: {
        id: 'abc-123',
        name: 'John Maverick',
      },
    })
  }),
]

HttpResponse.json() 단축 정적 메서드를 사용하면 Content-TypeContent-Length 응답 헤더가 사용하는 JSON 본문과 자동으로 동기화되므로 이를 사용하는 것을 권장합니다.

스트림 응답

클라이언트로 데이터를 스트리밍하기 위해 ReadableStream을 사용하여 응답할 수 있습니다.

Streaming

`ReadableStream`을 사용하여 응답합니다.

다른 응답 타입

MSW를 사용하면 XML, Blob, ArrayBuffer, FormData 등 다양한 응답 본문 타입을 정의할 수 있습니다. 다른 응답 본문 타입을 사용하는 방법에 대해 더 알아보려면 HttpResponse API를 참고하세요.

HttpResponse

`HttpResponse` 클래스의 API 레퍼런스입니다.

에러 응답 모킹

응답 리졸버에서 유효한 에러 응답을 구성하고 반환하여 에러 응답을 에뮬레이트합니다.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.get('/user', () => {
    // "GET /user" 요청에 대해 "401 Unauthorized"로 응답합니다.
    return new HttpResponse(null, { status: 401 })
  }),
]

네트워크 오류 모킹

네트워크 오류를 발생시키려면 Response.error() 또는 HttpResponse.error() 정적 메서드를 사용하세요. 오류 응답과 달리 네트워크 오류는 요청 실행을 중단하고 해당 요청을 실패로 표시합니다. 실제로는 잘못된 요청을 구성하거나 해결할 수 없는 호스트 이름을 요청할 때 네트워크 오류를 경험할 수 있습니다.

import { http, HttpResponse } from 'msw'
 
export const handlers = [
  http.post('/checkout/cart', () => {
    return HttpResponse.error()
  }),
]

응답 던지기

응답 리졸버 내에서 언제든지 Response 인스턴스를 던질 수 있습니다. 이 경우 요청 처리가 중단되고, 던져진 응답이 모의 응답으로 반환됩니다.

import { http, HttpResponse } from 'msw'
 
http.post('/login', ({ request }) => {
  if (!request.headers.has('cookie')) {
    throw new HttpResponse(null, { status: 400 })
  }
})

이 방식은 요청을 처리할 때 미들웨어 패턴을 구현하는 데 특히 유용합니다.

async function isAuthenticated(request) {
  if (!request.headers.has('cookie')) {
    throw new HttpResponse(null, { status: 400 })
  }
}
 
http.post('/login', async ({ request }) => {
  await isAuthenticated(request)
 
  return new HttpResponse(null, {
    status: 302,
    headers: {
      Location: '/dashboard',
    },
  })
})

응답이 아닌 예외는 서버 런타임에서 발생하는 처리되지 않은 거부와 유사하게 500 Internal Server Error 응답으로 변환됩니다.

관련 자료