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 })
}
}
Copy
환경 변수를 사용한 API 키 관리
10장에서 다룬 보안 개념을 적용하여, API 키를 환경 변수로 관리합니다.
.env.local
파일에 API 키 추가 :
EXTERNAL_API_KEY=your_api_key_here
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 })
}
}
Copy
에러 처리 및 재시도 로직
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 })
}
}
Copy
캐싱 전략
성능 최적화를 위해 캐싱 전략을 구현합니다. 이는 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 })
}
}
Copy
미들웨어를 통한 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*' ,
}
Copy
실습 : 날씨 API 통합 및 캐싱
이제 앞서 배운 모든 개념을 종합하여, OpenWeatherMap API를 Next.js 애플리케이션에 통합하고 결과를 캐싱하는 실습을 해보겠습니다.
.env.local
파일에 API 키 추가 :
OPENWEATHERMAP_API_KEY=your_api_key_here
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 })
}
}
Copy
프론트엔드에서 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 >
)
}
Copy
이 실습을 통해 Next.js의 App Router를 사용하여 외부 API를 통합하고, 결과를 캐싱하는 방법을 익힐 수 있습니다. 이 과정에서 우리는 API 라우트 생성, 환경 변수 사용, 에러 처리, 캐싱, 그리고 미들웨어를 통한 로깅 등 이전 섹션에서 배운 개념들을 종합적으로 적용했습니다.
Next.js의 App Router와 API 라우트를 사용하여 외부 API를 통합하면, 프론트엔드와 백엔드 사이의 중간 계층을 효과적으로 만들 수 있습니다. 이를 통해 API 키를 안전하게 관리하고, 요청을 최적화하며, 클라이언트 사이드 코드를 단순화할 수 있습니다. 또한, 이러한 접근 방식은 12장에서 다룰 성능 최적화와도 직접적으로 연관되어, 전체적인 애플리케이션의 성능 향상에 기여합니다.