이번에 팀 프로젝트로 진행중인 골키퍼 프로젝트에서 프론트엔드 개발자분들이 CORS를 해결해달라고 요청하셔서, 스프링 설정파일에 CORS 예외 설정을 추가했다. 아래 설정 클래스의 9 ~ 13 라인이 CORS 예외 설정을 하는 코드다.
package com.j2kb.goal.config;
//import 문 생략@Configuration
@EnableTransactionManagement
public class WebConfig implements WebMvcConfigurer {
@Autowired
private DataSource dataSource;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MemberCertInterceptor())
.addPathPatterns("/api/**")// 해당 경로에 접근하기 전에 인터셉터가 가로챈다.
.excludePathPatterns("/api/members","/api/members/login","/api/admin","/api/statistics/total","/api/admin/**");// 해당 경로는 인터셉터가 가로채지 않는다.
}
@Bean
public PlatformTransactionManager transactionManager(){
DataSourceTransactionManager tm = new DataSourceTransactionManager();
tm.setDataSource(dataSource);
return tm;
}
}
예외 설정 코드는 Spring 공식 문서(Global CORS configuration)에서 참조한 것이라 분명히 정확히 동작해야 했다.
그런데 여전히 CORS 에러가 난다는프론트분들의 카톡..
CORS.. 삽질의 시작..
그 후에 별의 별 자료를 찾아가면서 CORS 예외 설정을 바꿔보았지만, 전혀 소용이 없었다. 그래서, 기본부터 시작하자는 의미로 CORS 체크의 원리에 대해 검색해 보았다.
브라우저는, 서버에 HTTP 요청을 보낼 때 Origin 헤더에 요청을 보내는 출처를 담아보낸다. 아마, 프론트엔드 개발자들의 개발환경에서 브라우저를 이용해 요청을 보낼 때는
Origin : <http://localhost:3000>
이런 헤더가 포함될 것이다.
브라우저로부터 요청을 받은 서버는 응답을 보내는데, 이 응답에 포함된 Access-Control-Allow-Origin 헤더에 "서버가 제공하는 자원에 접근 가능한 출처 목록"을 포함시켜 응답한다.
Access-Control-Allow-Origin : <https://example.com>
브라우저가 서버로부터 응답을 받으면, 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin 를 비교해 유효하면 응답을 처리하고, 유효하지 않다면 에러를 발생시킨다.
CORS 에러
CORS를 탐지하는 원리 자체는 간단하다. 참고로, 브라우저에서 서버로 요청을 보내는 시나리오가 3가지 정도가 있는데, 그 3가지 시나리오중 어떤 시나리오에서 CORS 에러가 발생하는지 알면 대응이 쉬워진다. 이번에 문제가 일어났던 시나리오에서만
가장 일반적인 상황으로 "브라우저가 본 요청을 보내기 전에 예비요청을 보내고, 예비요청의 결과에 따라 본요청을 보내는 방식" 이다. 요청 플로우는 아래 그림과 같다.