💻데브노트소개
🗄️

CORS 에러, 도대체 왜 나는 걸까: 원리와 해결

데브노트 편집팀·2026.06.12·7분 읽기
X(트위터)
ADVERTISEMENT

"Access to fetch ... has been blocked by CORS policy." 프론트 개발자라면 누구나 만나는 빨간 에러입니다. 원리를 알면 해결이 쉬워집니다.

CORS는 브라우저의 보안 장치다

브라우저는 **동일 출처 정책(Same-Origin Policy)**을 따릅니다. 출처(스킴+호스트+포트)가 다른 곳으로의 요청은 기본적으로 제한됩니다.

https://myapp.com  →  https://api.other.com   # 다른 출처(cross-origin)

이걸 안전하게 허용하는 규약이 CORS입니다. 핵심: CORS는 서버가 허락해야 풀립니다. 프론트 코드만으로는 못 뚫습니다.

서버가 허락 헤더를 보내야 한다

서버가 응답에 다음 헤더를 넣어야 브라우저가 통과시킵니다.

Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

Allow-Origin정확한 출처를 넣는 게 안전합니다. *는 누구나 허용이라 인증 쿠키가 있는 요청에는 못 씁니다.

preflight: 진짜 요청 전의 사전 확인

PUT/DELETE이거나 커스텀 헤더(Authorization 등)가 있으면, 브라우저가 먼저 OPTIONS 요청을 보내 "이 요청 해도 돼?"를 묻습니다. 이게 preflight입니다.

브라우저 → OPTIONS /orders  (preflight)
서버    → 200 + Access-Control-Allow-* 헤더
브라우저 → 실제 POST /orders

서버가 OPTIONS에 올바른 헤더로 답하지 않으면 본 요청은 시작도 못 합니다. CORS 에러의 상당수가 여기서 발생합니다.

쿠키를 함께 보낼 때

인증 쿠키를 보내려면 양쪽 설정이 모두 필요합니다.

fetch(url, { credentials: "include" });  // 프론트
Access-Control-Allow-Credentials: true   # 서버
Access-Control-Allow-Origin: https://myapp.com  # 이때 *는 불가

흔한 오해와 해결

  • "프론트에서 헤더를 추가하면 되지 않나?" → 아닙니다. 허용은 서버가 합니다.
  • 로컬에서만 안 됨 → 개발 중엔 프록시(예: 프레임워크 dev 서버의 rewrites)로 같은 출처처럼 우회
  • 에러는 나는데 서버 로그엔 요청이 없음 → preflight(OPTIONS)에서 막힌 것. OPTIONS 핸들링 확인

마무리 체크리스트

  • CORS는 서버가 허락하는 규약
  • 복잡한 요청은 OPTIONS preflight가 먼저
  • Allow-Origin은 정확한 출처, 쿠키 쓰면 * 금지
  • 로컬은 프록시로 우회

"누가 허락하는가? → 서버"만 기억하면 CORS 디버깅의 방향이 잡힙니다.

#CORS#HTTP#백엔드#보안
X(트위터)
ADVERTISEMENT