λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ‘¨‍πŸ’» web.dev/fe

Next.js API Routes μ•Œμ•„λ³΄κΈ°

by HandHand 2022. 7. 26.

 

πŸ“Œ API Routes λž€?

API Routes 의 λ“±μž₯ λ°°κ²½

React μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ°œλ°œν•˜λ‹€λ³΄λ©΄ backend API κ°€ ν•„μš”ν•œ μ‹œμ μ΄ μ˜€κ²Œλ©λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄ DB 의 데이터λ₯Ό μ‘°νšŒν•˜κ±°λ‚˜ μ‚¬μš©μžκ°€ μš”μ²­ν•œ 데이터λ₯Ό μ²˜λ¦¬ν•˜λŠ” λ“±μ˜ μž‘μ—…μ΄ μžˆμŠ΅λ‹ˆλ‹€.

이λ₯Ό μœ„ν•΄ μ΄μ „μ—λŠ” Node.js 등을 ν™œμš©ν•œ custom server νŒŒμ΄ν”„λΌμΈμ„ κ΅¬μΆ•ν•΄μ„œ

ν•΄λ‹Ή μž‘μ—…μ„ μœ„μž„ν•˜λŠ” 방식을 μ‚¬μš©ν–ˆμ§€λ§Œ, 이 경우 Next.js 의 μ„±λŠ₯ μ΅œμ ν™” 이점을 μžƒκ²Œ λ˜λŠ” λ¬Έμ œμ™€

TypeScript 및 ES6 λ°©μ‹μ˜ λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜μ§€ λͺ»ν•˜λŠ” λ¬Έμ œκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

 

μ΄λŸ¬ν•œ 문제λ₯Ό ν•΄κ²°ν•˜λ©° 개발자 κ²½ν—˜μ„ ν–₯μƒμ‹œν‚€κΈ° μœ„ν•΄μ„œ Next.js λŠ” Next 9 버전 λΆ€ν„°

API Routes λΌλŠ” κΈ°λŠ₯을 μ§€μ›ν•˜κΈ° μ‹œμž‘ν–ˆμŠ΅λ‹ˆλ‹€.

이λ₯Ό 톡해 Next.js μ•ˆμ—μ„œ ν•„μš”ν•œ serverless API Endpoint λ₯Ό ꡬ좕할 수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

 

API Routes λ₯Ό ν™œμš©ν•œ REST API μ˜ˆμ‹œ

Next.js λŠ” Express 와 같은 Node.js μ„œλ²„μ‚¬μ΄λ“œ ν”„λ ˆμž„μ›Œν¬μ²˜λŸΌ

Node.js 의 μš”μ²­ 및 응닡객체λ₯Ό 직접 μ ‘κ·Όν•˜μ§€ μ•Šκ³  NextApiRequest 와 같이

λž˜ν•‘λœ 객체λ₯Ό μ†μ‰½κ²Œ λ‹€λ£° 수 μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

이λ₯Ό 톡해 λΆˆν•„μš”ν•œ νŒŒμ‹± μ½”λ“œλ₯Ό 쀄이고 μ›ν•˜λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 κ΅¬ν˜„μ— 집쀑할 수 있게 λ©λ‹ˆλ‹€.

 

API Routes λŠ” pages/api 디렉토리 내에 μ •μ˜ν•˜λ©° 파일 μ‹œμŠ€ν…œμ„ 기반으둜 μ£Όμ†Œκ°€ κ²°μ •λ©λ‹ˆλ‹€.

μ—¬κΈ°μ„œλŠ” λŒ“κΈ€μ„ μ‘°νšŒν•˜λŠ” κ°„λ‹¨ν•œ API μ˜ˆμ‹œλ₯Ό 톡해 μ‚¬μš© 방법을 μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

ν•Έλ“€λŸ¬λŠ” μš”μ²­(req) κ³Ό 응닡(res) 객체λ₯Ό 인자둜 μ „λ‹¬λ°›μŠ΅λ‹ˆλ‹€.

 

// pages/api/comments

const comments = [
    { id: 1, text: 'comment 1' },
    { id: 2, text: 'comment 2' },
    { id: 3, text: 'comment 3' }, 
]

export default function handler(req, res) {
    res.status(200).json(comments)
}

 

이제 μœ„μ—μ„œ μ •μ˜ν•œ endpoint 둜 fetch λ₯Ό 톡해 데이터λ₯Ό μš”μ²­ν•©λ‹ˆλ‹€.

 

// Comments.tsx

export default function Comments() {
    const fetchComments = async () => {
        const response = await fetch('/api/comments')

        if (response.ok) {
            const comments = await response.json()
        }
    }
}

 

πŸ“Œ API Routes 의 적용 사둀

API Proxy

API Routes λ₯Ό μ΄μš©ν•˜λ©΄ μ‹€μ œ μ™ΈλΆ€ API ν˜ΈμΆœμ „μ— μš”μ²­ 및 응닡 객체에 μ ‘κ·Όν•˜μ—¬

Authorization, CORS λ“± μ—¬λŸ¬ λͺ©μ μ„ μœ„ν•œ ν”„λ‘μ‹œ 역할을 μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

1️⃣ μœ νš¨μ„± 검사

λ‹€μŒμ€ HTTP Request μ—μ„œ Authorization 헀더λ₯Ό 검사해

API μš”μ²­ 쑰건을 λ§Œμ‘±ν•˜λŠ”μ§€ νŒλ‹¨ν•˜λŠ” κ°„λ‹¨ν•œ μ˜ˆμ‹œ μ½”λ“œμž…λ‹ˆλ‹€.

 

import { NextApiRequest, NextApiResponse } from 'next'

export async function handler(req: NextApiRequest, res: NextApiResponse) {
    const role = req.headers.get('authorization')

    if (['user', 'admin'].includes(role)) {
        return res.status(401).send('unauthorized')
    }

    // ...
}

 

2️⃣ μ™ΈλΆ€ API μ£Όμ†Œ λ§ˆμŠ€ν‚Ή

λ˜ν•œ API Routes λ₯Ό μ΄μš©ν•΄ ν”„λ‘œμ νŠΈμ—μ„œ μ‚¬μš©ν•˜λŠ” μ™ΈλΆ€ API λ₯Ό 은닉화 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ§Œμ•½ μ‹€μ œ μ™ΈλΆ€ API μ£Όμ†Œκ°€ https://external-api.hub/comment 라면,

/api/external μ΄λΌλŠ” μš”μ²­μœΌλ‘œ μ‹€μ œ μš”μ²­ μ£Όμ†Œλ₯Ό 숨길 수 μžˆμŠ΅λ‹ˆλ‹€.

 

// pages/api/external

import { NextApiRequest, NextApiResponse } from 'next'

const API_BASE_URL = process.env.API_BASE_URL

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

    const requestUrl = `${API_BASE_URL}/${req.path}`
    const response = await fetch(requestUrl)
}

 

πŸ“Œ API Routes μœ μ˜μ‚¬ν•­

Live Connection 이 ν•„μš”ν•œ 경우 λΆ€μ ν•©ν•˜λ‹€

Next.js μ—μ„œ μ œκ³΅ν•˜λŠ” API Routes λŠ” serverless function 이기 λ•Œλ¬Έμ—

stateless ν•˜μ—¬ 지속적인 DB 컀λ„₯μ…˜μ„ μœ μ§€ν•˜κ±°λ‚˜ web socket 등을 μ΄μš©ν•œ μ‹€μ‹œκ°„ 톡신 λ“±μ˜

live connection 이 ν•„μš”ν•œ κ²½μš°μ—λŠ” μ ν•©ν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

 

μ€‘λ³΅λœ API fetch μ‘°μ‹¬ν•˜κΈ°

API Routes λ₯Ό getServerSideProps λ‚˜ getStaticProps λ“±μ—μ„œ μ‚¬μš©ν•˜λ©΄

λΆˆν•„μš”ν•˜κ²Œ API λ₯Ό λ‘λ²ˆ ν˜ΈμΆœν•˜λŠ” λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

/** 
 * lib/load-posts.js
 *
 * loadPosts λŠ” lib/* 와 같은 λ””λ ‰ν† λ¦¬μ—μ„œ 곡톡 ν•¨μˆ˜λ‘œ κ΄€λ¦¬ν•˜λ©΄
 * getStaticProps와 API routesμ—μ„œ λͺ¨λ‘ μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€. 
 */
export async function loadPosts() {
  const res = await fetch('https://.../posts/')
  const data = await res.json()

  return data
}

/**
 * api/post
 */
import { loadPosts } from '@lib/load-posts'

export default async function handler(req, res) {
    // ...

    const posts = await loadPosts()

    res.status(200).json(posts)
}

/**
 * pages/blog.js
 */
import { loadPosts } from '@lib/load-posts'

export async function getStaticProps() {
    /**
     * API Routes λ₯Ό μ‚¬μš©ν•˜λŠ” λŒ€μ‹ μ— API λ₯Ό 직접 μ‘°νšŒν•˜λŠ” ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•΄μ„œ
   * λΆˆν•„μš”ν•œ API 쀑볡 ν˜ΈμΆœμ„ λ°©μ§€ν•©λ‹ˆλ‹€.
   */
  const posts = await loadPosts()

  return { props: { posts } }
}

 

πŸ“Œ Edge API Routes (πŸ§ͺ experimental)

Edge API Routes λŠ” Next v12 μ—μ„œ μΆ”κ°€λœ κΈ°λŠ₯μœΌλ‘œμ„œ

기쑴의 Node.js 기반의 λŸ°νƒ€μž„λ³΄λ‹€ 가볍고 λΉ λ₯Έ ν™˜κ²½μ„ μ œκ³΅ν•©λ‹ˆλ‹€.

ν˜„μž¬λŠ” 베타 λ²„μ „μœΌλ‘œ λ‹€μŒκ³Ό 같이 edge runtime 을 ν™œμ„±ν™” μ‹œν‚€κ³  싢은 λΌμš°νŠΈμ—μ„œ

runtime config λ₯Ό export ν•˜λ©΄ μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

import type { NextRequest } from 'next/server';

export default (req: NextRequest) => {
  return new Response(`Hello, from ${req.url} I'm now an Edge API Route!`);
};

export const config = {
  runtime: 'experimental-edge',
};

 

Edge API Routes λŠ” ν‘œμ€€ WEB API λ₯Ό 기반으둜 λ§Œλ“€μ–΄μ‘ŒκΈ° λ•Œλ¬Έμ— μ‚¬μš© μ‹œ λͺ‡κ°€μ§€ μ œμ•½μ‚¬ν•­ μ΄ μžˆμŠ΅λ‹ˆλ‹€.

λŒ€μ‹  stream response λ₯Ό 톡해 보닀 λΉ λ₯Έ TTFB(Time To First Byte) λ‘œ

μ„±λŠ₯ν–₯상을 κΎ€ν•  수 μžˆλ‹€λŠ” μž₯점이 μžˆμŠ΅λ‹ˆλ‹€.

 

πŸ“Œ 참고자료

Next.js 9

Advanced Features: Custom Server | Next.js

Are there any downsides or limitations of using Next.js api routes instead of custom server? · Discussion #17679 · vercel/next.js

λ°˜μ‘ν˜•

πŸ’¬ λŒ“κΈ€