React와 Vite를 사용한 프로젝트에서 Spring 서버와의 CORS 오류 해결하기
최근에 React와 Vite를 사용하여 웹 애플리케이션을 개발하는 과정에서 Spring 서버와의 통신 중 CORS(Cross-Origin Resource Sharing) 오류를 경험했다. 이 포스트에서는 CORS 오류의 원인과 해결 방법에 대해 작성해보겠다.
CORS란?
CORS는 웹 브라우저가 다른 출처의 리소스에 접근할 수 있도록 허용하는 메커니즘. 기본적으로 웹 브라우저는 보안상의 이유로 다른 출처의 리소스에 대한 요청을 제한한다. 따라서, React 애플리케이션이 Spring 서버와 통신할 때, 서로 다른 출처로 인식되면 CORS 오류가 발생할 수 있다.

이렇게 인증 버튼을 누르면 서버에 사용자의 이메일을 전달하여 해당 이메일에 인증번호를 보내줘야 한다.
위와 같이 axios를 통해 서버와 통신한다.
이제 CORS 오류를 피하기 위해 내가 썼던 방법들을 열거하겠다.
1. vite.config.ts 파일 server proxy 설정
이렇게 설정해두면 '/api'가 포함된 경로의 origin(protocol + host + port)을 http://localhost:8080으로 변경해주고,
'/api' 경로를 제거하여 결론적으로 localhost:5137/api/member/sendEmail이 localhost:8080/member/sendEmail 이렇게 변경돼서 요청을 보낸다.
그리고 사실 CORS 설정은 server에서 하는 게 맞기 때문에 spring에서 처리하는 방법을 살펴보았다.
스프링에서 cors 설정을 하는 방법은 두가지가 있다.
1. CrossOrigin annotation을 사용하여 컨트롤러에 직접 도메인을 허용해주는 방식
- 이건 controller가 많아질 수록 비효율적이라고 생각이 듦.
2. WebMvcConfigurer을 이용하여 global하게 허용 도메인 설정
나는 두번째 방법이 더 효율적이라고 생각이 들어 2번을 선택했다.
2. Spring WebMvcConfigurer 이용하여 WebConfig 파일 생성
spring에서 WebConfig.java 파일을 생성해서 다음과 같이 작성하였다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // API 경로에 대한 CORS 설정
.allowedOrigins("http://localhost:5137") // Vite의 개발 서버 포트
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(6000L);
}
}
allowedOrigins에 허용할 origin을 넣고,
allowedMethods에는 허용할 HTTP method를 넣으면 된다.
(중간중간에 찾아보니 allowedOrigins 말고 allowedOriginPatterns도 있는 것 같은데 모든 도메인을 허용하고 싶을 때 allowedOrigins(*) 이런 식으로 사용하지 말고 allowedOriginPatterns(*) 이렇게 사용해야 하는 듯하다.)
3. Spring Security 파일 Cors 설정
그리고 spring Security를 쓰기 때문에 거기에서도 또 cors 설정을 해주어야 한다.
...
http
.cors((cors) -> cors.configurationSource(request -> {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:5137")); // 허용할 출처
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 허용할 HTTP 메서드
config.setAllowedHeaders(List.of("*")); // 허용할 헤더
config.setAllowCredentials(true); // 자격 증명 허용
return config;
}));
...
http
.authorizeHttpRequests((auth) -> auth
//.requestMatchers("/login", "/api/member/register", "/", "/member/sendEmail").permitAll()
.anyRequest().permitAll());
//.requestMatchers("/admin").hasRole("ADMIN")
//.anyRequest().authenticated());
위와 같이 security 파일에도 cors 설정을 해주어야 한다.
(근데 그냥 cors()만 해줘도 되는건지 저렇게 작성해야하는건지는 잘 모르겠음. 알아보기)
원래는 requestMatchers로 허용할 path를 따로 지정해줘야하는데 잠깐 테스트를 위해 그냥 anyRequest().permitAll()이라고 작성했다.
또 csrf은 disable 해놨다.
이렇게 cors 설정을 마쳤으니 잘 될 거라고 생각했고 잘 돼야 하는데 왜인지 모르게 자꾸 문제가 생겼다.

이렇게 오류가 나서 네트워크 창을 확인해보니


요청 헤더의 origin에는 문제가 없어보이고

응답헤더에 Acces-Controll-Allow-Origin: *이라고 되어있고 문제도 없어보이는데 왜 오류가 나는 것인지 모르겠다.
이것 때문에 계속 찾아보고 했는데 알고 보니까 오타로 인한 문제였다......
아무튼 덕분에 CORS에 대한 공부가 됐다