
CORS란
브라우저는 출처(Origin) 가 다른 서버로의 요청을 기본적으로 차단한다.
Origin = 프로토콜 + 도메인 + 포트
http://localhost:5173≠http://localhost:8080→ 다른 Origin
프론트(5173)에서 백엔드(8080)로 요청하면 브라우저가 이를 감지하고, 실제 요청 전에 preflight 요청(OPTIONS 메서드) 을 먼저 보내 서버가 허용하는지 물어본다.
브라우저 → OPTIONS /api/projects (preflight)
백엔드 → 200 OK + Access-Control-Allow-Origin: http://localhost:5173
브라우저 → 허용 확인 후 실제 GET /api/projects 전송
서버가 허용 응답을 주지 않으면 브라우저가 실제 요청 자체를 차단한다.
차단은 서버가 하는 게 아니라 브라우저가 한다.
CorsConfig — "무엇을 허용할 것인가" 정의
@Configuration
public class CorsConfig {
@Value("${cadence.cors.allowed-origins}")
private String[] allowedOrigins;
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOriginPatterns(List.of(allowedOrigins)); // 허용 출처
config.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true); // Authorization 헤더 허용
...
}
}
역할: CORS 규칙 자체를 정의하는 곳.
허용할 출처, 메서드, 헤더를 명시하고 CorsConfigurationSource 빈으로 등록한다.
Spring MVC 레벨(Controller에 도달하기 직전)에서 작동한다.
setAllowedOrigins vs setAllowedOriginPatterns
| 메서드 | 특징 |
|---|---|
setAllowedOrigins |
정확한 문자열 일치. *와 setAllowCredentials(true) 동시 사용 불가 |
setAllowedOriginPatterns |
와일드카드 패턴 허용. setAllowCredentials(true)와 함께 사용 가능 |
SecurityConfig — "누가 이 규칙을 쓸 것인가" 연결
.cors(cors -> cors.configurationSource(corsConfigurationSource))
역할: CorsConfig가 정의한 규칙을 Spring Security 필터 체인에 연결한다.
Spring Security 필터는 Spring MVC보다 먼저 실행된다.
SecurityConfig에 CORS를 연결하지 않으면 preflight(OPTIONS) 요청이 Security 필터에서 먼저 막혀버린다.
HTTP 요청
→ [Spring Security 필터] ← SecurityConfig가 corsConfigurationSource 참조
→ [Spring MVC] ← CorsConfig 빈이 작동하는 레이어
→ Controller
두 파일의 관계 요약
| CorsConfig | SecurityConfig | |
|---|---|---|
| 역할 | CORS 규칙 정의 (what) | Security 필터에 규칙 연결 (who uses it) |
| 설정 위치 | Spring MVC 레벨 | Security 필터 레벨 (더 앞) |
| 단독 동작 | Security 없이도 작동 | 규칙 자체를 정의하지 않음 |
CorsConfig만 있고 SecurityConfig에 연결 안 하면?
→ preflight가 Security에서 401/403으로 차단되어 모든 API 호출 실패.
SecurityConfig에만 인라인으로 정의하면?
→ 동작은 하지만 허용 출처가 하드코딩되어 환경별 관리가 어렵고, 설정이 두 곳에 분산된다.
Vite Server Proxy — 개발 환경 전용 우회
// vite.config.ts
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
역할: 로컬 개발 시 CORS를 서버 설정 없이 우회한다.
브라우저는 같은 5173 서버에 요청한다고 인식하고, Vite 개발서버가 백엔드로 포워딩한다.
브라우저(5173) --/api/projects--> Vite 개발서버(5173) ---> 백엔드(8080)
↑ 같은 Origin (CORS 없음) ↑ 서버 간 통신 (CORS 없음)
changeOrigin: true
포워딩 시 요청의 Host 헤더를 localhost:8080으로 바꾼다.
없으면 백엔드가 localhost:5173으로 된 Host를 받아 거부할 수 있다.
세 가지의 관계 정리
| CorsConfig | SecurityConfig | Vite Proxy | |
|---|---|---|---|
| 동작 환경 | 로컬 + 운영 | 로컬 + 운영 | 로컬 개발만 |
| 처리 주체 | Spring 서버 | Spring 서버 | Vite 개발서버 |
| 목적 | CORS 허용 규칙 정의 | Security 필터에 CORS 연결 | 브라우저 CORS 자체를 우회 |
로컬 개발 흐름
브라우저 → Vite proxy → 백엔드
(CORS 우회) (CorsConfig + SecurityConfig은 동작하나 실질적으로 무관)
운영 배포 흐름
브라우저 → CDN/프론트 서버 → 백엔드
(Vite proxy 없음) (CorsConfig + SecurityConfig이 실제로 작동)
운영에서는 Vite proxy가 없으므로 CorsConfig의 allowedOrigins에 실제 프론트 도메인이 정확히 등록되어 있어야 한다.
6. CORS 흐름
application-local.properties
cors.allowed-origins=http://localhost:5173
↓ @Value 주입
CorsConfig.corsConfigurationSource()
↓ 빈 주입
SecurityConfig.cors(...)
허용 출처를 변경할 때는 application-local.properties의 cadence.cors.allowed-origins 값 하나만 수정하면 전체에 반영된다.
'DEV > Spring' 카테고리의 다른 글
| [Spring] 이메일 코드 발송 서비스 개발 간 @Async 적용 (0) | 2026.03.19 |
|---|---|
| [Spring] SecurityConfig Spring Method Security 적용하기 (0) | 2024.02.20 |
| [트러블슈팅][Spring] 의존성 주입 간 순환참조 문제 해결 방법 (0) | 2024.02.20 |
| [트러블슈팅][Spring] ApplicationRunner RuntimeException 발생 시 shutdown되는 오류 해결 (0) | 2024.02.20 |
| [트러블슈팅][Spring] .sql 파일 init이 자동으로 실행되지 않는 문제 원인 / 해결방법 (0) | 2024.02.20 |