이번 포스팅에서는 CORS 에러를 정리해보려고 한다.
사실 프론트엔드 서버에서 백엔드 서버로 Request를 보낼 때 마주하는 가장 흔한 에러가 바로 CORS 에러이다.
이전에 Naver Map API를 사용하여 프론트엔드 개발을 하면서 CORS 에러를 마주했었고, 이를 프록시 서버로 해결했으나 이번에는 백엔드에서는 어떻게 처리하는지 살펴볼 예정이다 👀
CORS(Cross-Origin Resource Sharing)는 보안상의 이유로 브라우저가 다른 출처(origin)의 리소스를 요청할 때 적용되는 보안 정책이다.
기본적으로 브라우저는 동일 출처 정책(Same-Origin Policy, SOP)에 따라 출처가 다른 리소스를 요청하는 것을 제한한다.
하지만 웹 애플리케이션에서 API를 호출할 때, 다른 출처의 서버로 요청해야 하는 경우가 많다.
이를 해결하기 위해 등장한 개념이 바로 CORS이다.
CORS를 이해하기위해 필요한 개념을 정리해보자
Same-Origin Policy
- 동일한 출처에서만 요청을 허용하는 보안 정책
Preflight Request
- 브라우저가 실제 요청 전에 OPTIONS 메서드를 이용해 서버에 미리 요청을 보내 허용 여부를 확인하는 과정
CORS Headers
- 서버가 응답에 추가하는 헤더로, 특정 도메인에서의 요청을 허용할지 결정
우선 간단하게 읽고 넘어가자
프론트엔드를 구축하고 API를 요청하면 다음과 같이 콘솔에서 CORS 에러를 마주칠 수 있었다.
네트워크 탭을 살펴보면 다음과 같다.
우리는 이 사진을 통해 백엔드로 데이터를 요청하고 Response를 정상적으로 받아오고 있음을 확인할 수 있다.
하지만, 데이터를 사용할 수 없는 상황이다.
왜냐하면, 브라우저에서 소스의 기원이 다를 경우 데이터를 활용하는 것을 막기 때문이다.
이것이 바로 CORS 에러이다.
따라서, 실제로 데이터는 정상적으로 가져오지만 브라우저 정책에 의해 데이터 활용이 막힌다는 것을 이해하자!
이전에 첨부한 사진을 보면 Response Header에 Access-Control-Allow-Origin 내용이 없는 것을 볼 수 있다.
즉, 브라우저는 Response Header에 Access-Control-Allow-Origin이 없기 때문에 소스의 기원이 다르다고 판단하여 데이터 활용을 막는다.
따라서, 백엔드에서는 요청이 들어오면 Access-Control-Allow-Origin 헤더를 추가하여 Response를 보내주면 된다.
이렇게 헤더를 추가하여 보내주는 방법은 크게 2가지가 있다.
Spring Boot에서는 CORS를 설정하는 여러 가지 방법이 있다.
가장 간단한 방법은 @CrossOrigin
어노테이션을 사용하는 것이다.
예제 코드를 살펴보자
Sample Code
@CrossOrigin(origins = "http://localhost:5173") @RestController @RequestMapping("/api") public class RestApiBoardController { ... }
이처럼 컨트롤러에 @CrossOrigin
어노테이션을 추가하여 특정 URL로 들어오는 Request를 허용하는 것이다.
하지만 이렇게 개별 컨트롤러에 적용하는 방법보다는 글로벌 설정을 적용하는 것이 효율적일 수 있다.
이전에 Interceptor를 설명할 때, WebMvcConfigurer 인터페이스는 SpringBoot의 default 세팅을 커스터마이징할 수 있는 인터페이스라고 설명한 적이 있다.
이전에는 인터셉터 관련 설정을 변경했고, 이번에는 CORS 관련 설정을 추가해보려고 한다.
Sample Code
@Configuration public class WebMvcConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoggerInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/css/**") .excludePathPatterns("/js/**") .excludePathPatterns("/images/**"); } @Override public void addCorsMappings(CorsRegistry registry){ registry .addMapping("/api/**") .allowedOrigins("http://localhost:5173") .allowedMethods("GET"); } }
이처럼 addCorsMappings(CorsRegistry registry)
메서드를 오버라이딩하여 세팅하면 된다.
registry에 허용할 Origins과 Method 방식을 세팅할 수 있다!
프론트엔드에서도 CORS 문제를 해결할 수 있다.
만약, 백엔드 서버에서 CORS 세팅을 해줄 수 있는 여건이 안되는 경우 (ex. Naver Map API 사용)에는 어쩔수 없이 프론트엔드에서 세팅을 수행해야 한다.
프론트엔드에서는 vite를 사용하여 프로젝트를 세팅했을 경우
vite.config.js
에서 프록시를 설정해주면 된다.
Sample Code
export default defineConfig({ server: { proxy: { '/api': { target: 'https://api.example.com', changeOrigin: true, secure: false } } } });
이처럼 프록시 서버를 세팅해주면 (클라이언트 → 프록시 서버 → 백엔드 서버)
로 Request가 나가게 된다.
마찬가지로 Response도 (백엔드 서버 → 프록시 서버 → 클라이언트)
의 과정으로 들어오게 된다.
이렇게 되면 브라우저는 데이터의 Origin을 우리가 세팅한 프록시 서버로 이해하게 된다.
결과적으로 같은 출처(Origin)인 것처럼 보이게 만들어 CORS 문제를 우회하는 방식으로 해결할 수 있는 것이다!
이번 포스팅에서는 CORS 에러의 개념과 원인, 그리고 이를 해결하는 방법을 정리해보았다.
백엔드에서 CORS 정책을 올바르게 설정하는 것이 가장 확실한 방법이지만, 경우에 따라 프록시를 활용하는 방법도 고려할 수 있다.
이번 포스팅이 CORS 문제를 해결하는 데 도움이 될 수 있으면 좋겠다 👊