이슈 요약
프로젝트에서 프론트엔드는
React
로 백엔드는 Spring Boot
로 구현된 API를 호출하는 구조로 개발하면서 로컬에서 테스트 중에 CORS
에러를 만나게 되었습니다. 그동안
CORS
는 현업에서 근무할때도 많이 겪었던 문제라 정리해보려고 합니다.이 오류는 서버가 클라이언트의 요청에 대해 CORS 정책을 허용하지 않기 때문에 발생하는데 CORS에 관해서는 아래 글에 정리를 해두어서 참고해보면 좋을 것 같습니다.
CORS가 무엇인지 궁금하다면?
CORS 이슈 해결방법
1. @CrossOrigin
어노테이션을 활용하기
Spring Boot
에서 CORS 문제를 해결하는 가장 간단한 방법은 @CrossOrigin
어노테이션을 사용하는 것이 있습니다.특정 컨트롤러 혹은 컨트롤러 메서드에 어노테이션을 적용하여 세부적인 설정이 가능합니다.
@PostMapping("/signin") @CrossOrigin(origins = "<http://localhost:5173>", allowCredentials = "true") public ResponseEntity<User> signin(HttpServletRequest request, HttpServletResponse response, @RequestBody User user) { return ResponseEntity.status(OK).body(userService.signin(request, response, user)); }
2. WebMvcConfigurer
를 사용하여 전역적으로 설정
만약 컨트롤러 단위가 아닌 전역적으로 CORS 설정이 필요하다면
WebMvcConfigurer
를 통해 보다 편리하게 관리할 수 있습니다.CorsRegistry
설정의 주요 값들- addMapping(
/**
): 모든 경로에 대해 CORS 설정을 적용
- allowedOrigins(
http://localhost:5173
): 허용할 출처(도메인)를 설정
- allowedMethods(
GET, POST, PUT, DELETE
): 허용할 HTTP 메서드를 지정
- allowedHeaders(
*
): 요청에서 모든 헤더를 허용할지 설정
- allowCredentials(
true
): 쿠키나 세션과 같은 인증 정보 허용할지 설정
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("<http://localhost:5173>") // 클라이언트 주소 .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true); } }
3. CorsFilter
사용하기
CORS 문제를 더욱 정교하게 제어하고 싶다면
CorsFilter
를 사용할 수 있습니다. CorsFilter는 요청이 들어올 때마다 CORS 설정을 검사하여 허용된 요청만 처리하도록 합니다.import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("<http://localhost:5173>"); // 허용할 출처 설정 config.addAllowedHeader("*"); config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); // 모든 경로에 대해 설정 적용 return new CorsFilter(source); } }
4. 프론트엔드에서 CORS 문제 해결하기
만약 위에서 선택하여 CORS 설정을 완료하였는데도 동일한 오류가 발생한다면 프론트에서 설정이 필요한 경우일 수도 있습니다.
아래 fetch를 예시로 들면 쿠키 값을 서버와 공유하기 위해서
credentials: 'include'
설정이 필요합니다. (axios, jQuery 등 조금씩 설정하는 문법이 다릅니다.)const response = await fetch(`${LOCAL_API_URL}/users/signin`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, password }), credentials: 'include' // 쿠키를 포함하여 요청 });
credentials 옵션의 값들
omit
: 기본값으로, 쿠키를 전송하지 않음
same-origin
: 같은 출처에서만 쿠키를 전송
include
: 모든 요청에 대해 쿠키를 전송
마무리
프로젝트가 아직 테스트 중이여서 1번 방식으로 서버에서 CORS 설정 이후 로그인 세션에 대한 쿠키값을 넘겨받기 위해 4번 방식으로 프론트에서도 설정을 해주었습니다.
컨트롤러가 점점 많아질 것을 대비하여 2번 방식 또는 3번 방식으로 CORS 정책을 설정하는 것을 고려 중입니다. CORS 정책은 많이 접하는 만큼 다양한 방식으로 해결을 해보면 구조가 다른 프로젝트에서 해결하는데 도움이 될 것 같습니다.