[ Troubleshooting🛠️ ] CORS 문제 해결
Next.js 프로젝트에서 melon API를 연결할 때 발생할 수 있는 CORS 이슈와 해결 방법을 단계별로 설명해보려고 합니다. 그러면 먼저 CORS가 무엇인지 부터 알아야 할텐데요!
🌍 CORS란?
CORS(Cross-Origin Resource Sharing)는 교차 출처 리소스 공유를 의미하며, 서로 다른 출처의 웹 페이지나 서버가 자원에 접근할 수 있도록 허용하는 보안 메커니즘입니다.
1. 문제 발생❓
웹 브라우저에서 Next.js 클라이언트 코드가 로컬 API 서버(http://localhost:5000)에 직접 요청하면 아래와 같은 CORS 오류가 발생할 수 있습니다.
Access to XMLHttpRequest at 'http://localhost:5000/api/v1/chart/live' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
2. 원인 추론 🔎
- origin의 차이: 웹 브라우저에서 실행 중인 Next.js 애플리케이션(http://localhost:3000)이 다른 origin의 서버(http://localhost:5000)에 직접 요청을 보내고 있습니다.
- 보안 정책: 웹 브라우저는 보안상의 이유로 "동일 출처 정책(Same-Origin Policy)"을 기본적으로 적용합니다. 이 정책은 한 출처에서 로드된 문서나 스크립트가 다른 출처의 자원과 상호 작용하는 것을 제한합니다.
3. 해결 과정 📋
- 서버 사이드 우회: Next.js의 API 라우트는 서버 사이드에서 실행되므로 브라우저의 CORS 제한을 받지 않습니다.
- 동일 출처 요청: 클라이언트는 동일한 origin(http://localhost:3000)의 Next.js API라우트에 요청을 보냅니다.
- 백엔드 프록시: Nex.js API 라우트는 이 요청을 받아 서버 사이드에서 실제 API 서버(http://localhost:5000)로 요청을 전달하고, 응답을 클라이언트에게 반환합니다.
이렇게 해결하면 브라우저가 직접 다른 origin으로 요청하지 않기 때문에 CORS 오류가 발생하지 않습니다. 이 방식으로 프론트엔드 개발 과정에서 CORS 문제를 해결하는 프록시 패턴입니다!
🧚♀️ 잠깐 여기서 프록시 패턴이란?
프록시 패턴(Proxy Pattern)은 대상 원본 객체를 대리하여 대신 처리하게 함으로써 로직의 흐름을 제어하는 행동 패턴입니다. 프록시는 사전적인 의미로는 '대리인'이라는 뜻입니다. 즉, 누군가에게 어떤 일을 대신 시키는 것을 의미하는데, 이를 객체 지향 프로그래밍에 접목해보면 클라이언트가 대상 객체를 직접 쓰는게 아니라 중간에 프록시(대리인)을 거쳐서 쓰는 코드 패턴이라고 보면 됩니다. 따라서 대상 객체의 메소드를 직접 실행하게 하는 것이 아닌, 대상 객체에 접근하기 전에 프록시(Proxy) 객체의 메서드를 접근한 수 추가적인 로직을 처리한뒤 접근하게 됩니다.
코드로 실습해보겠습니다 :)
방법 1: Next.js API 라우트를 통한 프록시 설정
Pages Router 사용 시:
1. pages/api/melon/chart/[type].ts 파일 생성
import type { NextApiRequest, NextApiResponse } from 'next';
import axios from 'axios';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { type } = req.query;
try {
const response = await axios.get(`http://localhost:5000/api/v1/chart/${type}`);
res.status(200).json(response.data);
} catch (error) {
console.error('API error:', error);
res.status(500).json({ error: 'Failed to fetch data' });
}
}
2. pages/api/melon/lyric/[songId].ts 파일 생성
import type { NextApiRequest, NextApiResponse } from 'next';
import axios from 'axios';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { songId } = req.query;
try {
const response = await axios.get(`http://localhost:5000/api/v1/lyric/${songId}`);
res.status(200).json(response.data);
} catch (error) {
console.error('API error:', error);
res.status(500).json({ error: 'Failed to fetch lyrics' });
}
}
App Router 사용 시:
1. app/api/melon/chart/[type]/route.ts 파일 생성
import { NextRequest, NextResponse } from 'next/server';
import axios from 'axios';
export async function GET(request: NextRequest, { params }: { params: { type: string } }) {
const { type } = params;
try {
const response = await axios.get(`http://localhost:5000/api/v1/chart/${type}`);
return NextResponse.json(response.data);
} catch (error) {
console.error('API error:', error);
return NextResponse.json({ error: 'Failed to fetch data' }, { status: 500 });
}
}
2. app/api/melon/lyric/[songId]/route.ts 파일 생성
import { NextRequest, NextResponse } from 'next/server';
import axios from 'axios';
export async function GET(request: NextRequest, { params }: { params: { songId: string } }) {
const { songId } = params;
try {
const response = await axios.get(`http://localhost:5000/api/v1/lyric/${songId}`);
return NextResponse.json(response.data);
} catch (error) {
console.error('API error:', error);
return NextResponse.json({ error: 'Failed to fetch lyrics' }, { status: 500 });
}
}
클라이언트 코드에서는 API 라우트를 사용합니다.
// services/melonApi.ts
import axios from 'axios';
export const fetchChart = async (chartType: 'live' | 'rise' | 'day' | 'week' | 'month') => {
const response = await axios.get(`/api/melon/chart/${chartType}`);
return response.data;
};
export const fetchLyrics = async (songId: string) => {
const response = await axios.get(`/api/melon/lyric/${songId}`);
return response.data;
};
4. 결론 ❤🔥
CORS 이슈는 웹 보안을 위한 브라우저의 정책으로 인해 발생하지만, Next.js 애플리케이션에서는 API 라우트를 활용한 프록시 설정으로 효과적으로 해결할 수 있습니다! 이러한 프록시 패턴은 melon API뿐만 아니라 다양한 타사 API와 통신할 때도 적용할 수 있는 범용적인 해결책이므로, Next.js 프로젝트에서 외부 API 연동 시 참고하시기 바랍니다!