icon안동민 개발노트

외부 API와의 통합


 Next.js의 App Router를 사용하여 외부 API와 통합하는 방법을 알아보겠습니다.

 이 절에서는 이전에 다룬 내용들을 바탕으로, API 라우트 생성, HTTP 메서드 처리, 그리고 미들웨어 사용 등의 개념을 종합적으로 적용하여 외부 API 통합 방법을 설명하겠습니다.

API 라우트 생성

 App Router에서 API 라우트를 생성하려면 app/api 디렉토리 내에 route.js 파일을 만듭니다.

// app/api/external-data/route.js
import { NextResponse } from 'next/server'
 
export async function GET() {
  try {
    const response = await fetch('https://api.example.com/data')
    const data = await response.json()
    return NextResponse.json(data)
  } catch (error) {
    return NextResponse.json({ error: 'Failed to fetch data' }, { status: 500 })
  }
}

환경 변수를 사용한 API 키 관리

 10장에서 다룬 보안 개념을 적용하여, API 키를 환경 변수로 관리합니다.

  1. .env.local 파일에 API 키 추가 :
EXTERNAL_API_KEY=your_api_key_here
  1. API 라우트에서 환경 변수 사용 :
// app/api/secure-external-data/route.js
import { NextResponse } from 'next/server'
 
export async function GET() {
  const apiKey = process.env.EXTERNAL_API_KEY
  try {
    const response = await fetch('https://api.example.com/data', {
      headers: { 'Authorization': `Bearer ${apiKey}` }
    })
    const data = await response.json()
    return NextResponse.json(data)
  } catch (error) {
    return NextResponse.json({ error: 'Failed to fetch data' }, { status: 500 })
  }
}

에러 처리 및 재시도 로직

 11.2절에서 다룬 HTTP 메서드 처리 개념을 확장하여, 에러 처리와 재시도 로직을 구현합니다.

// app/api/resilient-external-data/route.js
import { NextResponse } from 'next/server'
 
async function fetchWithRetry(url, options, retries = 3) {
  try {
    const response = await fetch(url, options)
    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
    return await response.json()
  } catch (error) {
    if (retries > 0) {
      console.log(`Retrying... (${retries} attempts left)`)
      return fetchWithRetry(url, options, retries - 1)
    } else {
      throw error
    }
  }
}
 
export async function GET() {
  try {
    const data = await fetchWithRetry('https://api.example.com/data', {
      headers: { 'Authorization': `Bearer ${process.env.EXTERNAL_API_KEY}` }
    })
    return NextResponse.json(data)
  } catch (error) {
    return NextResponse.json({ error: 'Failed to fetch data after multiple attempts' }, { status: 500 })
  }
}

캐싱 전략

 성능 최적화를 위해 캐싱 전략을 구현합니다. 이는 12장의 성능 최적화 주제와 연결됩니다.

// app/api/cached-external-data/route.js
import { NextResponse } from 'next/server'
import { cacheData, getCachedData } from '../../../lib/cache'
 
export async function GET() {
  const cacheKey = 'external-api-data'
  const cachedData = await getCachedData(cacheKey)
 
  if (cachedData) {
    return NextResponse.json(cachedData)
  }
 
  try {
    const response = await fetch('https://api.example.com/data')
    const data = await response.json()
    
    await cacheData(cacheKey, data, 60 * 5) // Cache for 5 minutes
    return NextResponse.json(data)
  } catch (error) {
    return NextResponse.json({ error: 'Failed to fetch data' }, { status: 500 })
  }
}

미들웨어를 통한 API 요청 로깅

 11.3절에서 다룬 미들웨어 개념을 적용하여, 외부 API 요청에 대한 로깅을 구현합니다.

// middleware.js
import { NextResponse } from 'next/server'
 
export function middleware(request) {
  if (request.nextUrl.pathname.startsWith('/api/')) {
    console.log(`API Request: ${request.method} ${request.url}`)
  }
  return NextResponse.next()
}
 
export const config = {
  matcher: '/api/:path*',
}

실습 : 날씨 API 통합 및 캐싱

 이제 앞서 배운 모든 개념을 종합하여, OpenWeatherMap API를 Next.js 애플리케이션에 통합하고 결과를 캐싱하는 실습을 해보겠습니다.

  1.  .env.local 파일에 API 키 추가 :

    OPENWEATHERMAP_API_KEY=your_api_key_here
    
  2.  API 라우트 생성 :

// app/api/weather/route.js
import { NextResponse } from 'next/server'
import { cacheData, getCachedData } from '../../../lib/cache'
 
export async function GET(request) {
  const { searchParams } = new URL(request.url)
  const city = searchParams.get('city')
 
  if (!city) {
    return NextResponse.json({ error: 'City parameter is required' }, { status: 400 })
  }
 
  const cacheKey = `weather-${city}`
  const cachedData = await getCachedData(cacheKey)
 
  if (cachedData) {
    return NextResponse.json(cachedData)
  }
 
  const apiKey = process.env.OPENWEATHERMAP_API_KEY
  const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`
 
  try {
    const response = await fetch(apiUrl)
    const data = await response.json()
 
    if (response.ok) {
      await cacheData(cacheKey, data, 60 * 30) // Cache for 30 minutes
      return NextResponse.json(data)
    } else {
      return NextResponse.json({ error: data.message }, { status: response.status })
    }
  } catch (error) {
    return NextResponse.json({ error: 'Failed to fetch weather data' }, { status: 500 })
  }
}
  1. 프론트엔드에서 API 사용 :
// app/weather/page.js
'use client'
 
import { useState } from 'react'
 
export default function WeatherPage() {
  const [city, setCity] = useState('')
  const [weather, setWeather] = useState(null)
 
  const fetchWeather = async () => {
    const res = await fetch(`/api/weather?city=${encodeURIComponent(city)}`)
    const data = await res.json()
    setWeather(data)
  }
 
  return (
    <div>
      <input
        type="text"
        value={city}
        onChange={(e) => setCity(e.target.value)}
        placeholder="Enter city name"
      />
      <button onClick={fetchWeather}>Get Weather</button>
      {weather && (
        <div>
          <h2>{weather.name}</h2>
          <p>Temperature: {weather.main.temp}°C</p>
          <p>Weather: {weather.weather[0].description}</p>
        </div>
      )}
    </div>
  )
}

 이 실습을 통해 Next.js의 App Router를 사용하여 외부 API를 통합하고, 결과를 캐싱하는 방법을 익힐 수 있습니다. 이 과정에서 우리는 API 라우트 생성, 환경 변수 사용, 에러 처리, 캐싱, 그리고 미들웨어를 통한 로깅 등 이전 섹션에서 배운 개념들을 종합적으로 적용했습니다.

 Next.js의 App Router와 API 라우트를 사용하여 외부 API를 통합하면, 프론트엔드와 백엔드 사이의 중간 계층을 효과적으로 만들 수 있습니다. 이를 통해 API 키를 안전하게 관리하고, 요청을 최적화하며, 클라이언트 사이드 코드를 단순화할 수 있습니다. 또한, 이러한 접근 방식은 12장에서 다룰 성능 최적화와도 직접적으로 연관되어, 전체적인 애플리케이션의 성능 향상에 기여합니다.