– 프론트엔드 개발자의 입장에서 이해하는 CORS 이슈와 해결법
웹 개발을 하다 보면, 다음과 같은 에러를 종종 마주친다.
프론트에선 API 호출도 했고, 백엔드에서도 응답을 보내줬는데…
“아니, 응답은 있는데 왜 못 쓰게 하는 건데?”
처음엔 너무 억울하다. 그래서 이번 글에서는 프론트엔드 입장에서 CORS가 왜 발생하는지, 어떻게 해결해야 하는지를 정리한다.
CORS (Cross-Origin Resource Sharing):
브라우저가 출처(origin)가 다른 리소스에 접근할 때 허용 여부를 판단하는 보안 정책이다.
1.1 출처(origin)
이란
프로토콜 + 도메인 + 포트 번호
세 가지를 조합한 것이다.
예를 들어:
http://localhost:3000
→ 프론트 서버http://localhost:8080
→ 백엔드 서버2.1 보안상의 이유
악성 스크립트가 다른 도메인의 API에 함부로 요청을 보내는 것을 막기 위해
브라우저가 먼저 요청을 '차단'하는 구조다.
2.2 브라우저만 강제
Postman이나 curl 같은 툴에선 아무런 문제가 없다.
// React에서 fetch로 API 호출
fetch("http://localhost:8080/api/data")
.then((res) => res.json())
.then((data) => console.log(data));
백엔드에서 응답도 잘 오는데... 에러가 발생한다!
Access to fetch at 'http://localhost:8080/api/data' from origin 'http://localhost:3000' has been blocked by CORS policy
백엔드가 클라이언트의 출처를 허용한다고 명시해줘야 한다.
@CrossOrigin(origins = "http://localhost:3000")
@GetMapping("/api/data")
public Data getData() {
return new Data();
}
app.use(
cors({
origin: "http://localhost:3000",
})
);
CRA (React Create App)에서는 package.json
에 프록시 설정 가능
"proxy": "http://localhost:8080"
4.2.1 프론트 서버가 먼저 받고 백엔드로 전달하기 때문에
CORS를 우회할 수 있다.
4.2.2 주의: 이 방식은 개발 환경 전용이다.
배포 시엔 사용할 수 없다.
Content-Type: application/json
같이 민감한 헤더가 포함되면OPTIONS /api/data HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: POST
CORS는 막는 게 목적이 아니라, 허용을 명시하라는 시스템이다.
프론트 입장에서는 “막혀서 짜증나”지만, 브라우저가 사용자를 지켜주는 장치라는 걸 이해하고,
백엔드와 협의해서 CORS 설정을 조율해나가는 게 현실적인 접근이다.