
안녕하세요. IT 기술 블로거입니다.
React나 Vue 같은 프론트엔드와 Spring Boot 백엔드를 연결해 본 적이 있다면, 누구나 한 번쯤은 콘솔창을 가득 채우는 빨간색 메시지를 본 적이 있을 것입니다.
“Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy…”
바로 악명 높은 CORS(Cross-Origin Resource Sharing) 에러입니다. 분명 서버는 잘 돌아가고 Postman으로 쏠 때는 응답이 오는데, 왜 브라우저에서만 안 되는 걸까요? 오늘은 이 에러의 정체와 Spring Boot에서 깔끔하게 해결하는 3가지 방법을 정리해 드립니다.
1. CORS란 무엇이고 왜 발생하는가?
CORS는 “교차 출처 리소스 공유”를 의미합니다. 브라우저의 보안 정책 중 하나인 SOP(Same-Origin Policy, 동일 출처 정책) 때문입니다.
출처(Origin)란?
Protocol(http/https) + Host(domain) + Port(80/8080) 3가지가 모두 같아야 동일 출처로 인정합니다.
– 프론트엔드: http://localhost:3000
– 백엔드: http://localhost:8080
포트 번호가 다르기 때문에 브라우저는 이를 ‘다른 출처’로 간주하고 보안상의 이유로 차단합니다. 즉, 서버가 차단하는 게 아니라 브라우저가 서버로부터 받은 응답을 가로채서 에러를 내뱉는 것입니다.
2. Spring Boot 해결 방법 1: @CrossOrigin 사용
특정 컨트롤러나 메서드에만 적용하고 싶을 때 사용하는 가장 간단한 방법입니다.
@RestController
@RequestMapping("/api/members")
@CrossOrigin(origins = "http://localhost:3000") // 특정 도메인 허용
public class MemberController {
@GetMapping("/{id}")
public Member getMember(@PathVariable Long id) {
return memberService.findById(id);
}
}
- 장점: 매우 쉽고 직관적입니다.
- 단점: 컨트롤러가 많아지면 모든 곳에 붙여야 하므로 중복이 발생하고 관리가 어렵습니다.
3. 해결 방법 2: WebMvcConfigurer 글로벌 설정 (권장)
프로젝트 전체에 일괄적으로 적용하고 싶을 때 사용하는 실무 권장 방식입니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 모든 경로에 대해
.allowedOrigins("http://localhost:3000", "https://my-domain.com") // 허용할 도메인
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS") // 허용할 HTTP 메서드
.allowedHeaders("*") // 모든 헤더 허용
.allowCredentials(true) // 쿠키/인증 정보 포함 허용 여부
.maxAge(3600); // 프리플라이트(Preflight) 요청 캐싱 시간 (초)
}
}
이 설정 하나면 프로젝트 내의 모든 API에 대해 지정한 도메인에서의 접근이 허용됩니다.
4. 해결 방법 3: Spring Security 설정
만약 프로젝트에 Spring Security를 적용 중이라면, 위의 WebMvcConfigurer 설정만으로는 부족할 수 있습니다. Security 필터 체인에서 먼저 차단되기 때문입니다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// ... 기타 설정
.csrf(csrf -> csrf.disable());
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("http://localhost:3000");
configuration.addAllowedMethod("*");
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}

결론
CORS 오류는 서버의 잘못이 아니라 브라우저의 보안 정책을 준수하기 위한 일종의 “신고 과정”입니다.
- 간단한 테스트는
@CrossOrigin으로, - 실무 프로젝트는
WebMvcConfigurer를 통한 글로벌 설정으로, - 보안이 적용된 프로젝트는
SecurityConfig에서 해결하세요.
중요한 것은 보안을 위해 allowedOrigins("*")처럼 모든 도메인을 허용하기보다는, 실제 사용하는 도메인만 명시하는 것이 좋습니다. 이 가이드가 여러분의 프론트-백엔드 협업의 첫 걸음을 가볍게 만들어 주길 바랍니다. 감사합니다!


![[Spring Boot] application.yml 문법 총정리: 실무 예제로 배우는 설정의 기술](https://codecampai.com/wp-content/uploads/2026/02/spring-boot-application-yml-syntax-1-1-768x515.jpg)



