문제 상황 및 원인
스프링 애플리케이션을 백엔드로 하고, React 애플리케이션을 프론트엔드로 하는 개발하던 중, 다음과 같은 오류를 발견했다.
Access to XMLHttpRequest at 'http://localhost:8080/hello?name=world' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
확인해보니, 브라우저는 기본적으로 동일 출처 정책(Same Origin Policy, SOP)을 지키기 때문에 다른 출처의 리소스 접근을 금지해 생기는 문제로 확인됐다. 이를 해결하기 위해서는 스프링 애플리케이션의 CORS(Cross-Origin Resource Sharing) 설정에 특정 주소로부터의 Cross Origin Request를 허용해야 한다.
문제 해결
Cross Origin Request를 허용하기 위해서는 서버에서 Access-Control-Allow-Origin 헤더를 포함한 응답을 주는 것이 필요하다. 이 Access-Control-Allow-Origin의 값은 요청하는 위치의 주소이다. 예를 들어 http://localhost:8080 에서 실행 중인 스프링 애플리케이션이 http://localhost:3000 로부터의 요청을 허용하고 싶다면 다음과 같은 헤더를 추가하면 된다.
Access-Control-Allow-Origin: http://localhost:3000
그렇다면, 스프링 MVC에서는 위 헤더를 어떻게 추가할 수 있을까? 바로 WebMvcConfigurer을 사용하면 된다.
아래는 실제로 동작하는 WebMvcConfigurer에 대한 설정이다.
@Configuration
class WebConfig {
@Bean
fun webMvcConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
}
}
}
}
WebMvcConfigurer는 addCorsMapping 함수를 오버라이드 해 CORS 설정을 추가할 수 있으며, 이곳의 CorsRegistry의 addMapping 함수를 통해 특정 경로에 대해 Cross Origin Request를 허용 할 수 있다. 여기서는 addMapping("/**") 을 사용했는데, 이는 전체 경로에 대한 Cross Origin Request를 허용한다는 뜻이다.
만약 /hello 경로 하위에 대해서만 Cross Origin Request를 허용하고 싶다면 다음과 같이 설정하면 된다.
registry.addMapping("/student/**")
어떤 경로에 대해 Cross Origin Request를 허용할지 정했으면, 어떤 곳으로부터의 요청을 허용할지도 정해야 한다. React 애플리케이션은 로컬에서 일반적으로 3000번 포트를 사용하므로 http://localhost:3000 을 허용해주면 React 애플리케이션에서 Spring 애플리케이션에 HTTP 요청을 할 수 있게 된다.
이제 이렇게 설정한 후 리액트 애플리케이션에서 스프링 애플리케이션에 HTTP 요청을 실행해보면, 다음과 같은 헤더와 함께 성공 응답이 오는 것을 볼 수 있다.