API Routes

Examples

Good to know : App Router를 사용하는 경우, API Routes 대신 Server Components 또는 Route Handlers 를 사용할 수 있습니다.

API routes는 Next.js로 공용 API 를 구축할 수 있는 솔루션을 제공합니다.

pages/api 폴더 내부의 모든 파일은 /api/*로 매핑되어 page 대신 API 엔드포인트로 처리됩니다. 이러한 파일들은 서버 사이드 번들로만 존재하며, 클라이언트 사이드 번들 크기를 증가시키지 않습니다.

예를 들어, 다음 API 라우트는 상태 코드 200과 함께 JSON 응답을 반환합니다:

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
type ResponseData = {
  message: string
}
 
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>,
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}
pages/api/hello.js
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

Good to know:

Parameters

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  // ...
}

HTTP Methods

API 라우트에서 다양한 HTTP 메서드를 처리하려면 요청 핸들러에서 req.method를 사용할 수 있습니다. 예:

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    // POST 요청 처리
  } else {
    // 다른 HTTP 메서드 처리
  }
}
pages/api/hello.js
export default function handler(req, res) {
  if (req.method === 'POST') {
    // POST 요청 처리
  } else {
    // 다른 HTTP 메서드 처리
  }
}

Request Helpers

API Routes는 들어오는 요청(req)을 구문 분석하는 내장 요청 도우미를 제공합니다:

  • req.cookies - 요청에 의해 전송된 쿠키를 포함하는 객체. 기본값은 {}
  • req.query - 쿼리 문자열 (opens in a new tab) 을 포함하는 객체. 기본값은 {}
  • req.body - content-type에 따라 구문 분석된 body를 포함하는 객체, body가 전송되지 않은 경우 null

Custom config

모든 API 라우트는 기본 설정을 변경하기 위해 config 객체를 내보낼 수 있습니다. 기본 설정은 다음과 같습니다:

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '1mb',
    },
  },
  // 이 함수가 실행되는 최대 허용 시간을 지정함 (초 단위)
  maxDuration: 5,
}

bodyParser는 자동으로 활성화됩니다. body를 Stream으로 소비하거나 raw-body (opens in a new tab) 로 소비하려면 이를 false로 설정할 수 있습니다.

자동 bodyParsing을 비활성화하는 한 가지 사용 사례는 예를 들어 GitHub (opens in a new tab) 웹훅 요청의 원본 body를 확인할 수 있도록 하는 것입니다.

export const config = {
  api: {
    bodyParser: false,
  },
}

bodyParser.sizeLimitbytes (opens in a new tab) 가 지원하는 형식으로 구문 분석된 body의 최대 크기입니다. 예를 들어:

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '500kb',
    },
  },
}

externalResolver는 이 라우트가 express 또는 connect와 같은 외부 리졸버에 의해 처리되고 있음을 서버에 명시적으로 알리는 플래그입니다. 이 옵션을 활성화하면 해결되지 않은 요청에 대한 경고가 비활성화됩니다.

export const config = {
  api: {
    externalResolver: true,
  },
}

responseLimit는 자동으로 활성화되며, API 라우트의 응답 body가 4MB를 초과할 경우 경고가 표시됩니다.

서버리스 환경에서 Next.js를 사용하지 않고, CDN이나 전용 미디어 호스트를 사용하지 않는 경우의 성능 영향을 이해하는 경우, 이 제한을 false로 설정할 수 있습니다.

export const config = {
  api: {
    responseLimit: false,
  },
}

responseLimit은 또한 bytes에서 지원하는 형식의 바이트 수 또는 문자열 형식을 취할 수 있습니다. 예를 들어 1000, '500kb' 또는 '3mb' 등이 있습니다. 이 값은 경고가 표시되기 전에 응답 크기의 최대값입니다. 기본값은 4MB입니다. (위 참조)

export const config = {
  api: {
    responseLimit: '8mb',
  },
}

Response Helpers

서버 응답 객체 (opens in a new tab) (종종 res로 약칭됨)에는 개발자 경험을 개선하고 새로운 API 엔드포인트를 생성하는 속도를 높이기 위한 Express.js와 유사한 도우미 메서드 세트가 포함되어 있습니다.

포함된 도우미는 다음과 같습니다:

Setting the status code of a response

클라이언트에 응답을 보낼 때 응답의 상태 코드를 설정할 수 있습니다.

다음 예제는 응답의 상태 코드를 200 (OK)으로 설정하고 Hello from Next.js! 값의 message 프로퍼티와 함께 JSON 응답을 반환합니다:

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
type ResponseData = {
  message: string
}
 
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>,
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}
pages/api/hello.js
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

Sending a JSON response

클라이언트에 응답을 보낼 때 JSON 응답을 보낼 수 있습니다. 이는 직렬화 가능한 객체 (opens in a new tab) 여야 합니다. 실제 애플리케이션에서는 요청된 엔드포인트의 결과에 따라 요청의 상태를 클라이언트에 알리고자 할 수 있습니다.

다음 예제는 상태 코드 200 (OK)와 비동기 작업의 결과를 포함한 JSON 응답을 보냅니다. 오류가 발생할 수 있는 상황을 처리하기 위해 try catch 블록에 포함되어 있으며, 적절한 상태 코드와 함께 잡힌 오류 메시지를 클라이언트에 다시 보냅니다:

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  try {
    const result = await someAsyncOperation()
    res.status(200).json({ result })
  } catch (err) {
    res.status(500).json({ error: '데이터를 불러오는 데 실패했습니다.' })
  }
}
pages/api/hello.js
export default async function handler(req, res) {
  try {
    const result = await someAsyncOperation()
    res.status(200).json({ result })
  } catch (err) {
    res.status(500).json({ error: '데이터를 불러오는 데 실패했습니다.' })
  }
}

Sending a HTTP response

HTTP 응답을 보내는 것은 JSON 응답을 보내는 것과 동일하게 작동합니다. 유일한 차이점은 응답 body가 string, object 또는 Buffer일 수 있다는 것입니다.

다음 예제는 상태 코드 200 (OK)와 비동기 작업의 결과를 포함한 HTTP 응답을 보냅니다.

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  try {
    const result = await someAsyncOperation()
    res.status(200).send({ result })
  } catch (err) {
    res.status(500).send({ error: '데이터를 가져오는 데 실패했습니다' })
  }
}
pages/api/hello.js
export default async function handler(req, res) {
  try {
    const result = await someAsyncOperation()
    res.status(200).send({ result })
  } catch (err) {
    res.status(500).send({ error: '데이터를 가져오는 데 실패했습니다' })
  }
}

Redirects to a specified path or URL

양식을 예로 들어, 양식을 제출한 후 클라이언트를 지정된 경로 또는 URL로 리디렉션하고자 할 수 있습니다.

다음 예제는 양식이 성공적으로 제출되면 클라이언트를 / 경로로 리디렉션합니다:

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const { name, message } = req.body
 
  try {
    await handleFormInputAsync({ name, message })
    res.redirect(307, '/')
  } catch (err) {
    res.status(500).send({ error: '데이터를 가져오는 데 실패했습니다' })
  }
}
pages/api/hello.js
export default async function handler(req, res) {
  const { name, message } = req.body
 
  try {
    await handleFormInputAsync({ name, message })
    res.redirect(307, '/')
  } catch (err) {
    res.status(500).send({ error: '데이터를 가져오는 데 실패했습니다' })
  }
}

Adding TypeScript types

NextApiRequestNextApiResponse 타입을 next에서 가져와 API Routes를 더 타입 안정적으로 만들 수 있습니다. 추가로, 응답 데이터도 타입 지정할 수 있습니다:

import type { NextApiRequest, NextApiResponse } from 'next'
 
type ResponseData = {
  message: string
}
 
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>,
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

Good to know : NextApiRequest의 body는 any입니다. 이는 클라이언트가 임의의 페이로드를 포함할 수 있기 때문입니다. body를 사용하기 전에 런타임에서 body의 타입/형태를 검증해야 합니다.

Dynamic API Routes

API Routes는 dynamic routes 를 지원하며, pages/에 사용된 파일 명명 규칙을 따릅니다.

pages/api/post/[pid].ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const { pid } = req.query
  res.end(`Post: ${pid}`)
}
pages/api/post/[pid].js
export default function handler(req, res) {
  const { pid } = req.query
  res.end(`Post: ${pid}`)
}

이제 /api/post/abc로의 요청은 Post: abc라는 텍스트로 응답합니다.

Catch all API routes

API Routes는 괄호 안에 세 개의 점(...)을 추가하여 모든 경로를 포착하도록 확장할 수 있습니다. 예:

  • pages/api/post/[...slug].js/api/post/a와 일치하지만, /api/post/a/b, /api/post/a/b/c 등과도 일치합니다.

Good to know : slug 외에도 [...param]과 같은 이름을 사용할 수 있습니다.

일치하는 매개변수는 페이지에 쿼리 매개변수로 전송되며(예제에서는 slug), 항상 배열 형태로 전송됩니다. 따라서 /api/post/a 경로는 다음과 같은 query 객체를 가집니다:

{ "slug": ["a"] }

그리고 /api/post/a/b 및 다른 일치하는 경로의 경우, 새로운 매개변수가 배열에 추가됩니다. 예:

{ "slug": ["a", "b"] }

예를 들어:

pages/api/post/[...slug].ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const { slug } = req.query
  res.end(`Post: ${slug.join(', ')}`)
}
pages/api/post/[...slug].js
export default function handler(req, res) {
  const { slug } = req.query
  res.end(`Post: ${slug.join(', ')}`)
}

이제 /api/post/a/b/c로의 요청은 Post: a, b, c라는 텍스트로 응답합니다.

Optional catch all API routes

모든 경로 포착 라우트를 선택적 포착 라우트로 만들려면 매개변수를 이중 괄호([[...slug]])에 포함합니다.

예를 들어, pages/api/post/[[...slug]].js/api/post, /api/post/a, /api/post/a/b 등과 일치합니다.

모든 경로 포착 라우트와 선택적 모든 경로 포착 라우트의 주요 차이점은 매개변수 없이도 경로가 일치한다는 점입니다(위 예제에서는 /api/post).

query 객체는 다음과 같습니다:

{ } // `GET /api/post` (빈 객체)
{ "slug": ["a"] } // `GET /api/post/a` (단일 요소 배열)
{ "slug": ["a", "b"] } // `GET /api/post/a/b` (다중 요소 배열)

Caveats

  • 미리 정의된 API 라우트는 동적 API 라우트보다 우선하며, 동적 API 라우트는 모든 경로 포착 API 라우트보다 우선합니다. 다음 예제를 참조하세요:
  • pages/api/post/create.js - /api/post/create와 일치합니다.
  • pages/api/post/[pid].js - /api/post/1, /api/post/abc 등과 일치하지만 /api/post/create와는 일치하지 않습니다.
  • pages/api/post/[...slug].js - /api/post/1/2, /api/post/a/b/c 등과 일치하지만 /api/post/create, /api/post/abc와는 일치하지 않습니다.

Edge API Routes

API Routes를 Edge Runtime과 함께 사용하고자 하는 경우, 점진적으로 App Router를 도입하고 Route Handlers 를 사용하는 것을 권장합니다.

Route Handlers 함수 시그니처는 isomorphic으로, Edge와 Node.js 런타임 모두에 동일한 함수를 사용할 수 있습니다.