C

CORS 에러 해결 — Access-Control-Allow-Origin 완벽 정리 (No 'Access-Control-Allow-Origin' header)

2026-05-05 · 에러 · 문제해결 · CORS · 웹개발 · API · 프론트엔드

증상: CORS 에러 메시지를 만났을 때

브라우저 콘솔에 다음과 같은 빨간 메시지가 뜨면 그것이 바로 CORS 에러입니다.

Access to fetch at 'https://api.example.com/users' from origin
'http://localhost:3000' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

핵심은 두 가지입니다. 첫째, 이 에러는 브라우저가 막은 것입니다. 서버는 정상적으로 응답을 보냈을 수도 있습니다. 둘째, 원인은 응답에 Access-Control-Allow-Origin 헤더가 없거나, 그 값이 내 출처(origin)와 맞지 않기 때문입니다.

왜 CORS 에러가 나는가

CORS(Cross-Origin Resource Sharing)는 한 출처(scheme + host + port)의 웹 페이지가 다른 출처의 리소스를 가져올 때 적용되는 브라우저 보안 정책입니다. 예를 들어 http://localhost:3000에서 http://localhost:8080의 API를 부르면, 포트가 달라 '다른 출처'로 간주됩니다.

흔한 원인 4가지

  • 서버가 Allow-Origin 헤더를 아예 안 보냄 — 가장 흔합니다. 서버에 CORS 설정이 없는 경우.
  • Origin 값 불일치 — 서버가 https://prod.com만 허용하는데 로컬에서 호출.
  • Preflight(OPTIONS) 요청 실패Content-Type: application/json이나 커스텀 헤더를 쓰면 브라우저가 먼저 OPTIONS 요청을 보내는데, 서버가 이를 처리하지 못함.
  • credentials(쿠키) 사용 시 와일드카드 충돌Allow-Origin: *와 쿠키 전송은 동시에 쓸 수 없습니다.

케이스별 해결

1) 정석: 서버에서 CORS 헤더를 허용

해결의 원칙은 "서버가 응답 헤더에 Access-Control-Allow-Origin을 추가"하는 것입니다. 프론트에서는 끌 수 없습니다.

Express (Node.js)

const cors = require('cors');
app.use(cors({
  origin: 'http://localhost:3000', // 허용할 출처
  credentials: true               // 쿠키 쓸 때만
}));

Spring Boot

@CrossOrigin(origins = "http://localhost:3000")
@RestController
public class UserController { ... }

Nginx (리버스 프록시)

add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always;

2) Preflight(OPTIONS) 처리

JSON 본문을 POST하면 브라우저가 본 요청 전에 OPTIONS 요청을 먼저 던집니다. 서버는 이 OPTIONS에 200/204와 함께 Access-Control-Allow-Methods, Access-Control-Allow-Headers를 응답해야 합니다. cors 미들웨어는 이를 자동 처리하지만, 직접 라우팅한다면 OPTIONS 메서드를 빠뜨리지 않았는지 확인하세요.

3) 개발 단계: 프록시로 우회

서버를 못 고치는 상황(외부 API 등)이라면 개발 서버의 프록시를 씁니다. 같은 출처처럼 보이게 만들어 CORS 자체를 회피합니다.

// vite.config.js
export default {
  server: {
    proxy: { '/api': 'http://localhost:8080' }
  }
}

4) credentials와 와일드카드 충돌

쿠키나 인증 헤더를 보내려면 프론트에서 credentials: 'include'를 쓰는데, 이때 서버의 Allow-Origin*가 아니라 정확한 출처 문자열이어야 하고 Access-Control-Allow-Credentials: true도 필요합니다.

원인별 비교

증상원인해결
No 'Access-Control-Allow-Origin' header서버에 CORS 설정 없음서버에서 Allow-Origin 추가
OPTIONS 요청만 실패Preflight 미처리OPTIONS 응답 + Allow-Methods/Headers
credentials와 '*' 동시 사용 거부와일드카드+쿠키 충돌출처 명시 + Allow-Credentials: true
로컬은 되는데 배포만 실패배포 출처 미허용운영 도메인을 origin에 추가

예방

  • API 서버 설계 시 처음부터 허용 출처 목록을 환경변수로 관리하세요.
  • Allow-Origin: * 남발은 보안 위험. 필요한 출처만 명시.
  • 로컬/스테이징/운영 출처를 각각 화이트리스트에 등록.
  • 네트워크 탭에서 응답 헤더를 직접 확인하는 습관을 들이세요.

자주 묻는 질문

Q1. 프론트엔드 코드만 고쳐서 CORS를 끌 수 있나요?

아니요. CORS는 브라우저가 서버 응답 헤더를 보고 판단합니다. 근본 해결은 서버에서 헤더를 추가하는 것이며, 프론트에서는 개발용 프록시로 우회만 가능합니다.

Q2. Postman에서는 잘 되는데 브라우저에서만 CORS 에러가 나요.

정상입니다. CORS는 브라우저만 적용하는 정책입니다. Postman·curl은 CORS를 검사하지 않으므로, 브라우저 에러는 서버 헤더 문제로 보면 됩니다.

Q3. Allow-Origin을 '*'로 했는데도 쿠키가 안 가요.

와일드카드와 credentials는 함께 쓸 수 없습니다. Allow-Origin에 정확한 출처를 적고 Access-Control-Allow-Credentials: true를 추가하세요.

CORS 에러 해결 — Access-Control-Allow-Origin 완벽 정리 (No 'Access-Control-Allow-Origin' header) | CodeMaster 블로그 | CodeMaster