CORS(Cross-Origin Resource Sharing)는 다른 출처(Origin)에서 온 요청을 허용할지 결정하는 브라우저 보안 정책입니다.
출처(Origin)는 프로토콜 + 도메인 + 포트의 조합입니다:
<http://localhost:3000> ← React 개발 서버
<http://localhost:8080> ← Spring Boot 서버
이 둘은 포트가 다르므로 다른 출처입니다. 브라우저는 보안상 다른 출처로의 요청을 기본적으로 차단합니다.
React에서 API를 호출하면:
// React (localhost:3000) → Spring Boot (localhost:8080)
axios.get('<http://localhost:8080/api/posts>')
CORS 설정이 없으면 브라우저 콘솔에 이런 에러가 나옵니다:
Access to XMLHttpRequest at '<http://localhost:8080/api/posts>' from origin
'<http://localhost:3000>' has been blocked by CORS policy
서버가 "localhost:3000에서 온 요청을 허용한다"고 명시해야 합니다.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final JwtAuthenticationEntryPoint authenticationEntryPoint;
private final JwtAccessDeniedHandler accessDeniedHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource())) // 추가
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.formLogin(form -> form.disable())
.httpBasic(basic -> basic.disable())
.exceptionHandling(exception -> exception
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
)
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/posts/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
// CORS 설정
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of(
"<http://localhost:3000>", // React 개발 서버
"<http://localhost:5173>" // Vite 개발 서버
));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", configuration);
return source;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
import가 필요합니다:
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;
각 설정의 의미:
allowedOrigins: 허용할 출처 목록입니다. React 개발 서버의 주소를 넣습니다. Vite의 기본 포트는 5173이고, CRA(Create React App)는 3000입니다.
allowedMethods: 허용할 HTTP 메서드입니다. OPTIONS는 브라우저가 본 요청 전에 보내는 Preflight 요청에 사용됩니다.
allowedHeaders: 허용할 요청 헤더입니다. *는 모든 헤더를 허용합니다. Authorization 헤더(JWT 토큰)를 보내야 하므로 반드시 허용해야 합니다.
allowCredentials: 쿠키나 인증 헤더를 포함한 요청을 허용합니다.