서버 배포 후 프론트엔드에서 API를 연동하기 위해 시도하던 중 누구나 한 번쯤 마주치는 CORS 에러가 발생하게 되었다.
origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
스프링을 사용할 때 CORS 에러를 해결하는 방법에 대해서 여러 블로그를 검색하였더니 다음과 같은 코드를 추가해 주면 전역적으로 CORS 설정이 가능하다 하였다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST")
.allowCredentials(true)
.maxAge(3000)
}
}
그래서 코드를 추가한 후에 다시 실행시켜 봤더니 회원가입, 로그인 같이 auth에 관련된 부분은 CORS 에러가 해결되었지만, 나머지 다른 서버의 API는 여전히 CORS 에러가 발생하였다.
전역적으로 설정을 해주었는데 일부 API만 정상적으로 작동하는 게 상식적으로 이해가 되지 않았다.
문제를 해결하기 위해 여러 가능성을 두고 고민을 하였는데, 문득 든 생각은 스프링 시큐리티였다.
서버에 들어오기도 전에 프론트엔드의 요청이 filter에서 막힐 수도 있겠다 생각하여 스프링 시큐리티에서 CORS 설정하는 방법에 대해 찾아보았다.
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
//... 생략
http
.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowCredentials(true);
config.setAllowedHeaders(Collections.singletonList("*"));
config.setMaxAge(3600L);
return config;
}
}))
//... 생략
}
스프링 시큐리티에서 CORS를 처리하기 위해 CorsConfigurationSource를 구현하는 익명 클래스를 생성하고 .cors() 메서드에 등록하였다.
마지막으로 프론트엔드에서 요청을 보냈더니 정상적으로 작동하는 것을 볼 수 있었다.
그러면 전역적으로 CORS를 설정했을 때 왜 auth에 대한 API는 정상적으로 작동했는지를 고민해 봤는데 코드를 통해 알 수 있었다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.
//...생략
.authorizeHttpRequests(request -> request.requestMatchers("api/auth/**").permitAll()
.anyRequest().authenticated())
//...생략
}
스프링 시큐리티의 필터에서 auth에 관한 api에 대한 request를 허용해 놓은 상태에서 전역적으로 CORS 설정을 하였으니 정상적으로 작동하게 된 것이었다.
결론
스프링 시큐리티를 사용하지 않은 경우 전역적으로 CORS 설정을 하면 된다.
스프링 시큐리티를 사용한 경우에는 새로운 CORS 필터를 빈으로 생성한 후 addFilter()를 통해 등록하거나, CorsConfigurationSource를 구현하는 익명 클래스를 생성하고 .cors() 메서드에 등록하여야 한다.