본문 바로가기

CS

CORS 에러가 뭔데 자꾸 막는 건데? (프록시로 뚫자!)

반응형

 

프론트엔드 개발을 하다 보면 한 번쯤은 만나는 에러가 있습니다.

Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy.

처음엔 "이게 뭔데? 내가 뭘 잘못한 거지?" 싶고, 나중엔 "아 또 이거야…" 싶죠.
오늘은 이 CORS 에러가 왜 발생하는지,
또 이를 해결하는 프록시란 무엇인지,
그리고 Next.js(App Router 기준)에서 이걸 프록시로 어떻게 해결할 수 있는지 찬찬히 정리해볼게요.

 

🧠 CORS란 뭐예요?

CORS는 Cross-Origin Resource Sharing의 줄임말이에요.
말 그대로 "다른 출처(origin)의 리소스를 브라우저가 공유할 수 있도록 허용해주는 정책"입니다.

브라우저는 기본적으로 보안을 위해 "같은 출처(Same-Origin)"끼리만 데이터를 주고받을 수 있어요.

🔍 출처가 같다는 건?

  • 같은 프로토콜
  • 같은 도메인
  • 같은 포트 번호

예를 들어 http://localhost:3000에서

  • http://localhost:3000/api로 요청하는 건 OK! ✅
  • https://api.example.com/data로 요청하면? → 프로토콜이 다르므로 다른 출처! ❌ → 브라우저가 막아요!
  • http://api.example.com/data로 요청하면? → 도메인이 다르므로 다른 출처! ❌ → 브라우저가 막아요!

 

💥 그럼 출처가 다르면 왜 에러가 나는 거죠?

우리가 프론트엔드에서 API 요청을 날릴 때,
그 API가 다른 출처에 있다면 브라우저는 응답을 막습니다.
보안을 위한 브라우저의 정책이죠.

그래서 CORS 에러는 브라우저에서 막는 보안 에러입니다.
백엔드가 뭔가 잘못한 것도, 프론트가 잘못한 것도 아니고, 그냥 "브라우저가 막은 것"이에요.

 

🛠 CORS 해결 방법 (서버를 수정할 수 있을 때)

백엔드 서버에서 다음과 같은 헤더를 응답에 붙여주면 해결됩니다.

Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization

하지만…

❗ 백엔드가 내가 짠 게 아니고, 수정도 못 한다면?

그럴 땐 프론트에서 프록시로 우회하면 됩니다!

 

✅  프록시(proxy)는 뭘까?

프록시는 간단하게 말하자면, 중간에서 대신 통신해주는 중개자를 말합니다.

프록시가 뭐야?

프론트엔드(브라우저)에서 백엔드 API 서버에 직접 요청할 수 없는 경우,
중간에 프록시 서버를 두고, 프론트 → 프록시 → 백엔드로 우회해서 요청을 보내는 방식입니다.

 

왜 프록시로 CORS 문제를 해결할 수 있을까?

예를 들어

  • 프론트 서버: http://localhost:3000
  • 백엔드 API: https://api.example.com

이렇게 출처가 다르면 CORS 에러가 발생하죠.
이때 프록시 서버를 localhost:3000 쪽에 붙이면, 브라우저는 같은 출처로 요청을 보내는 걸로 인식해서 에러가 나지 않습니다.

🧩 비유로 설명하면

브라우저는 "나랑 출처가 다른 애한테 직접 말 못 해!"
그래서 비서(프록시)를 두고 비서한테 시켜요.

👉 나(브라우저)비서(프록시)상대방(API 서버)

 

 

🚀 Next.js에서 프록시로 CORS 우회하기 (App Router 기준)

Next.js는 자체 서버가 있기 때문에, 브라우저 → Next.js 서버 → 백엔드 API 이런 식으로 중간에 끼어들 수 있습니다.
방법은 2가지입니다.

✅ 방법 1: next.config.js의 rewrites() 사용하기

// next.config.js
const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: 'https://api.example.com/:path*',
      },
    ];
  },
};
module.exports = nextConfig;

이렇게 하면 브라우저에서

fetch('/api/posts')

라고 호출했을 때, 실제로는 Next.js가 https://api.example.com/posts로 대신 요청을 보내줍니다.
CORS 에러 없이 데이터 가져오기 성공! 🎉

 

✅ 방법 2: App Router의 Route Handler로 직접 프록시 API 만들기

// app/api/proxy/[...path]/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(req: NextRequest, { params }: { params: { path: string[] } }) {
  const backendUrl = `https://api.example.com/${params.path.join('/')}`;
  const response = await fetch(backendUrl, {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
    },
    cache: 'no-store',
  });

  const data = await response.json();
  return NextResponse.json(data, { status: response.status });
}

이제 브라우저에서 다음처럼 요청해보세요:

fetch('/api/proxy/posts')

→ 이 요청은 Next.js 서버에서 받아서, 백엔드로 중계해주고 다시 브라우저로 보내줘요.

 

🔐 왜 '방법 2' 방식이 더 강력할까?

프록시 API(route.ts)를 직접 만들면 추가적인 인증, 제어, 보안 처리가 가능해요.

  • 로그인 쿠키 검사
  • 사용자 권한 확인
  • 서버에서만 알고 있는 API key 넣기
  • 요청 데이터 가공

이런 것들이 브라우저에서는 못 하지만 서버에서는 가능한 이유입니다.

 

🧩 두 방식 비교 요약

방식 특징 추천 상황
rewrites() 빠르고 간단, 단순 CORS 우회용 인증 필요 없는 API
app/api/proxy 보안 강력, 인증/로직 자유로움 토큰 숨기기, 로그인 검증 등

 

 

JavaScript만으로 CORS 에러를 해결할 수 있는 방법은?

결론부터 말하자면 CORS 에러를 완전히 해결하는 방법은 없습니다.
왜냐하면 CORS는 브라우저가 의도적으로 막는 보안 정책이기 때문에,
JS 코드만으로는 브라우저의 보안 정책을 우회하거나 무력화할 수가 없습니다.

하지만 다음과 같이 "부분적인 해결 방법" 혹은 "회피 전략" 은 있습니다.

 

❌ 브라우저에서 JS만으로는 완전 해결 불가

CORS는 브라우저 차원에서 막기 때문에 다음은 안 됩니다:

  • fetch()에서 헤더 바꿔보기
  • mode: 'no-cors' 붙이기
  • credentials: 'include'로 쿠키 우겨넣기

이런 시도는 대부분 다음과 같은 부작용이 있어요:

fetch('https://api.example.com/data', {
  mode: 'no-cors'
});

 

  • ✅ 요청은 보내짐
  • ❌ 응답은 opaque라서 읽을 수 없음
  • ❌ 에러도 안 나지만, 데이터도 못 씀

 

 

✅ JS로 가능한 우회 방법들 (제한적)

1. 서버가 CORS 허용하면 OK

가장 바람직한 방법. 서버가 응답 헤더에 Access-Control-Allow-Origin을 제대로 붙여주면 JS에서도 fetch 정상 작동합니다.

2. JSONP (구식)

  • <script src="https://example.com/data.js"> 로 우회하는 방식
  • GET만 가능, 보안상 위험, 요즘은 거의 사용하지 않음

3. 프론트에서 CORS 프록시 서버를 활용

  • 공용 프록시 서비스 사용: 예: https://cors-anywhere.herokuapp.com/ (일시적 테스트용)
fetch('https://cors-anywhere.herokuapp.com/https://api.example.com/data')

 

  • ❗ 사용 시 주의사항:
    • 보안 위험 있음
    • 속도 느림
    • 실 서비스에 쓰면 안 됨

4. 브라우저 확장 프로그램으로 임시 우회

개발 중이라면 Chrome 확장 프로그램(CORS Unblock 등)을 사용해서 CORS를 우회할 수 있습니다.
하지만 배포용이 아닌 개발용 일시 해제일 뿐, 실제 해결 방법은 아닙니다.

 

🔒 결론

방법 가능 여부 설명
JS에서만 해결 브라우저 보안 정책상 불가
서버가 CORS 허용 가장 정석적인 해결법
프록시 서버로 우회 개발자나 서비스 측에서 설정해야 함
no-cors 모드 응답을 읽을 수 없어서 의미 없음즉,

즉,

CORS는 JS에서 해결하는 게 아니라,
"브라우저 바깥"에서(=서버 또는 프록시에서) 해결해야 하는 문제입니다!

그래서 JS 단에서 할 수 있는 일은 거의 없고,
👉 서버 수정 / 프록시 설정 / 자체 API 중계 같은 방법으로 해결해야 합니다.

 

 

마무리

CORS 에러는 정말 흔하지만, 원리를 이해하고 나면 어렵지 않아요.
특히 Next.js(App Router)를 사용하면, 자체 서버 기능 덕분에 프록시 구성도 훨씬 쉽고 강력하게 설정할 수 있습니다.

반응형