CORS(Cross-Origin Resource Sharing)는 브라우저의 동일 출처 정책(Same-Origin Policy)을 우회해 다른 출처 리소스 접근을 허용하는 표준 메커니즘이다.
같은 출처 = 프로토콜 + 도메인 + 포트 동일
✅ http://localhost:8080 → http://localhost:8080
❌ http://localhost:3000 → http://localhost:8080 (다른 출처)
❌ https://api.example.com → http://localhost:8080 (다른 출처)
브라우저는 다른 출처 요청을 기본 차단 → CSRF 등 보안 공격 방지.
다른 출처 POST 요청 시:
[프론트 JS] OPTIONS /api/login (브라우저 자동 사전 확인)
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization,Content-Type
[서버] 200 OK + 허용 헤더
Access-Control-Allow-Origin: http://localhost:3000 (또는 *)
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: Authorization,Content-Type
Access-Control-Max-Age: 1800 (캐싱 30분)
Access-Control-Allow-Credentials: true (토큰/쿠키 허용)
[프론트 JS] POST /api/login (본 요청 진행)
Authorization: Bearer xxx
✅ non-simple 요청
- Content-Type: application/json
- custom header (Authorization 등)
- PUT/DELETE/PATCH 메서드
✅ OPTIONS 자동 발생 → 서버 허용 응답 확인 → 본 요청
브라우저 콘솔:
Access to fetch at 'http://api.example.com'
from origin 'http://localhost:3000' has been blocked by CORS policy
원인:
Access-Control-Allow-Origin 없음@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList(
"http://localhost:3000",
"https://frontend.com"
));
config.setAllowedMethods(Arrays.asList("*"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowCredentials(true);
config.setMaxAge(1800L); // Preflight 캐싱 30분
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
Chrome DevTools → Network
1. OPTIONS /api/login → 200 (Preflight)
2. POST /api/login → 200 (본 요청)
✅ 서버 → 서버 요청: CORS 제약 없음 (브라우저 미개입)
❌ 브라우저 → 서버 요청: CORS + Preflight 필수
핵심: 브라우저가 OPTIONS로 서버 정책 확인 → 허용 시에만 본 요청 진행. Spring Security cors() 설정으로 완벽 처리.