카테고리 없음

스프링에서 CORS 정책 설정 방법과 실전 적용 사례

트티 2025. 5. 5. 08:00
스프링에서 CORS 정책 설정 방법과 실전 적용 사례

스프링에서 CORS 정책 설정 방법과 실전 적용 사례

프론트엔드와 백엔드가 분리된 구조에서는 CORS(Cross-Origin Resource Sharing) 문제가 자주 발생합니다. 특히 React, Vue 같은 SPA 프레임워크를 사용할 때 백엔드와 도메인이 다르다면 CORS 오류는 피할 수 없는 장애물이죠. 스프링(Spring)에서는 이를 어떻게 해결할 수 있을까요?

이번 포스팅에서는 스프링 기반 프로젝트에서 CORS 정책을 설정하는 여러 방법과, 실제 현업에서 발생했던 문제를 어떻게 해결했는지를 사례와 함께 살펴봅니다. 프론트와의 협업이 많은 개발자라면 꼭 알고 있어야 할 핵심 내용입니다!

목차

 

스프링에서 CORS 정책 설정 방법과 실전 적용 사례

프론트엔드와 백엔드가 분리된 구조에서는 CORS(Cross-Origin Resource Sharing) 문제가 자주 발생합니다. 특히 React, Vue 같은 SPA 프레임워크를 사용할 때 백엔드와 도메인이 다르다면 CORS 오류는 피할 수 없는 장애물이죠. 스프링(Spring)에서는 이를 어떻게 해결할 수 있을까요?

이번 포스팅에서는 스프링 기반 프로젝트에서 CORS 정책을 설정하는 여러 방법과, 실제 현업에서 발생했던 문제를 어떻게 해결했는지를 사례와 함께 살펴봅니다. 프론트와의 협업이 많은 개발자라면 꼭 알고 있어야 할 핵심 내용입니다!

목차

📌 CORS란 무엇인가?

CORS는 Cross-Origin Resource Sharing의 줄임말로, 브라우저가 다른 출처(origin)의 리소스에 접근하는 것을 제한하는 보안 정책입니다. 예를 들어, http://localhost:3000에서 실행 중인 React 앱이 http://localhost:8080의 Spring API에 요청을 보내면, 서로 다른 출처이므로 CORS 문제가 발생할 수 있습니다.

브라우저는 기본적으로 cross-origin 요청에 대해 제한을 걸기 때문에, 서버에서 명시적으로 해당 출처를 허용해야 합니다.

📌 스프링에서의 기본 CORS 설정

스프링에서는 CORS 설정을 두 가지 방식으로 적용할 수 있습니다. 전역(Global) 설정과 개별 컨트롤러별(Local) 설정입니다. 각각의 방식은 적용 범위나 유연성 측면에서 차이가 있습니다.

1. 전역(Global) CORS 설정

WebMvcConfigurer를 구현하여 애플리케이션 전체에 CORS 정책을 적용할 수 있습니다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
  @Override
  public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/api/**")
            .allowedOrigins("http://localhost:3000")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowCredentials(true);
  }
}

2. 컨트롤러 단위(Local) CORS 설정

특정 컨트롤러나 엔드포인트에만 CORS를 설정하고 싶을 때는 @CrossOrigin 어노테이션을 사용합니다.

@RestController
@RequestMapping("/api")
public class SampleController {

  @CrossOrigin(origins = "http://localhost:3000")
  @GetMapping("/hello")
  public String hello() {
    return "Hello from Spring!";
  }
}

📌 글로벌 설정 vs 개별 컨트롤러 설정

전역 설정은 한 번에 많은 API에 CORS를 적용할 수 있어 관리가 편리하지만, 세밀한 제어가 어렵습니다. 반면, 컨트롤러 단위 설정은 제어는 세밀하지만 관리가 번거로울 수 있습니다.

Tip: 대부분의 프로젝트에서는 기본적으로 전역 설정을 하고, 일부 예외적인 API에만 개별 설정을 추가하는 방식이 가장 현실적입니다.

📌 실전 사례: 프론트와 협업 중 겪은 CORS 문제

React 기반 프론트엔드와 Spring Boot 백엔드를 함께 개발하던 중, 배포 후 OPTIONS preflight 요청이 실패하는 문제가 있었습니다. 프론트는 정상적으로 요청을 보냈지만, 백엔드에서 응답을 주지 않아 403 오류가 발생했습니다.

알고 보니 프록시 설정은 되어 있었지만, 백엔드 서버에서는 OPTIONS 요청을 허용하지 않고 있었고, allowedMethodsOPTIONS가 빠져 있었던 것이 원인이었습니다.

문제 해결 방법

전역 CORS 설정에 allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")를 추가하고, allowCredentials(true)allowedHeaders("*")를 명시한 후 문제를 해결했습니다.

registry.addMapping("/**")
        .allowedOrigins("http://localhost:3000")
        .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
        .allowedHeaders("*")
        .allowCredentials(true);

실전 팁: CORS는 단순히 도메인 허용만으로 해결되지 않습니다. OPTIONS 요청, 헤더, 인증정보 등도 꼼꼼히 설정해야 합니다.

📌 자주 놓치는 포인트 & 실용적인 팁

CORS 문제는 단순해 보이지만, 실제 개발에서는 생각보다 까다롭고 변수도 많습니다. 저 역시 실무에서 겪으며 “이거 왜 안 되지?”라는 고민을 수십 번 했었죠. 다음은 제가 직접 겪었던 몇 가지 흔한 실수와 그 해결 팁입니다.

1. CORS 문제는 보통 프론트 문제가 아니다

처음엔 React 설정을 의심했지만, 알고 보니 백엔드의 CORS 설정이 누락되어 있던 경우가 많았습니다. 특히 Content-Type: application/json 같은 헤더나, 인증 토큰을 포함할 때 withCredentialsallowCredentials 설정이 제대로 안 맞으면 CORS 오류가 발생합니다.

2. 개발/운영 환경에서 각각 다르게 설정하자

로컬 개발에서는 http://localhost:3000만 허용해도 되지만, 운영 환경에서는 도메인이 다르기 때문에 환경별로 허용 origin을 분리해서 관리해야 합니다. 이를 위해 application.yml 파일에 설정값을 분리하는 것도 좋은 방법이에요.

cors:
  allowed-origins: http://localhost:3000, https://myapp.com

이 설정을 Java Config에서 동적으로 바인딩해 쓰면 환경별로 쉽게 대응할 수 있습니다.

3. Preflight 요청이 꼭 허용되도록 확인

OPTIONS 요청이 허용되지 않으면 API 자체가 호출되지 않는 상황이 발생합니다. 특히 Nginx나 API Gateway를 사용하는 경우, 백엔드로 요청이 전달되기 전에 차단되는 경우도 있으니 반드시 CORS 관련 설정을 웹 서버에서도 확인해야 해요.

4. 테스트는 꼭 실제 요청으로 확인

Postman에서는 잘 되는데 프론트에서는 안 되는 경우 많죠. Postman은 CORS 정책을 따르지 않기 때문에 반드시 브라우저 기반 테스트로 문제를 확인해야 합니다.

5. CORS 에러 로그를 잘 읽자

에러 로그는 많은 힌트를 줍니다. 예를 들어 “No ‘Access-Control-Allow-Origin’ header is present”는 백엔드가 origin 허용을 안 했다는 뜻이고, “credentials flag is true but the ‘Access-Control-Allow-Credentials’ header is not”은 쿠키 관련 설정이 빠졌다는 뜻이죠. 로그는 답을 말해줍니다.

개발 Tip: CORS 문제를 겪을 때는 “누가 막고 있는가?”를 생각해보세요. 브라우저인가, 프록시인가, 백엔드인가. 이걸 구분하는 것만으로도 해결 속도가 훨씬 빨라집니다.

📌 CORS 설정 체크리스트

  • 🔒 Origin: 프론트의 도메인을 정확히 명시했는가?
  • 🔓 Methods: 사용 중인 HTTP 메서드를 모두 허용했는가?
  • 🔑 Headers: Custom 헤더가 있다면 명시했는가?
  • 🍪 Credentials: 인증 정보를 함께 보낼 경우 allowCredentials을 true로 했는가?
  • 🚀 OPTIONS: Preflight 요청을 위한 OPTIONS 메서드를 허용했는가?
  • 🌍 환경별로 다른 Origin을 분리해 관리하고 있는가?

📌 경험에서 얻은 결론

처음 CORS 문제를 마주했을 땐 정말 막막했지만, 지금 돌아보면 모든 문제는 원리를 제대로 알지 못해서 생긴 것이었어요. 브라우저가 왜 이 요청을 막았는지를 이해하고, 백엔드는 어떤 응답을 보내야 하는지를 알게 되니 점점 문제 해결 속도도 빨라졌습니다.

현업에서는 프론트와의 협업이 중요한 만큼, 이런 CORS 지식은 그 자체로도 큰 무기가 됩니다. 나중에 신규 입사자가 CORS 문제로 고생할 때, 멋지게 도와주는 선배가 되는 것도 가능해지니까요 😊

📌 마무리하며

스프링에서의 CORS 설정은 단순한 기술적 설정을 넘어, 프론트엔드와의 원활한 소통과 협업을 위한 준비입니다. 처음엔 어렵게 느껴질 수 있지만, 원리만 이해하면 상황에 따라 유연하게 설정할 수 있습니다.

이번 포스팅에서는 CORS의 개념부터 스프링에서의 전역 및 개별 설정 방법, 실전 사례, 그리고 실무에서 유용한 팁까지 다양하게 다뤄봤습니다. 여러분이 CORS 문제로 더 이상 고생하지 않기를 바라며, 누군가의 첫 해결사가 될 수도 있는 그날을 기대해봅니다 😊

앞으로 프론트와 백엔드가 협업하는 프로젝트에서, 당신의 이 경험이 든든한 버팀목이 되길 바랍니다!