프로젝트/기업 일정 관리 웹

CORS 정책 오류 해결

yoon4360 2025. 4. 2. 01:28

 

프론트엔드에서 회원가입 API 호출 시 다음과 같은 오류가 발생했다.

Access to XMLHttpRequest at 'https://api.gasdg.store/api/users/join' from origin 'https://gasdg.store' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

 


 

문제 정의

에러 원인을 찾아보니

프론트엔드는 https://gasdg.store, 백엔드는 https://api.gasdg.store로 도메인이 달랐다. 

브라우저 보안 정책(CORS)상, 도메인/포트/프로토콜이 다르면 Cross-Origin 요청으로 간주하여 에러를 발생시킨 것이었다.

 

서버가 CORS를 허용하지 않으면 브라우저는 OPTIONS 프리플라이트 요청에서 차단한다.

 


 

시도했던 방법

 

1. @CrossOrigin 컨트롤러에 직접 선언: 컨트롤러나 메서드 단에서 설정 가능하지만, 전체 API에 일괄 적용하기 어렵고 복잡한 경우 한계가 있다.

2. WebMvcConfigurer에서 전역 CORS 설정: Spring MVC만 사용하는 경우엔 유효하나, Spring Security가 활성화된 경우 무시될 수 있다.

 


 

해결방법

Spring Security 설정 내부에서 CorsConfigurationSource Bean 등록 + 환경 변수 기반 도메인 설정

- 백엔드 -

applicaiton.yml

CORS_ALLOWED_ORIGINS: https://gasdg.store

 

환경변수로 관리하여 운영/개발 환경별로 다른 Origin 허용 가능하게 했다.

 

 

SecurityConfig.java

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();

    String allowedOrigins = env.getProperty("CORS_ALLOWED_ORIGINS", "http://localhost:5173");

    config.setAllowedOrigins(Arrays.asList(allowedOrigins.split(",")));
    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
    config.setExposedHeaders(List.of("Authorization"));
    config.setAllowCredentials(true);
    config.setMaxAge(3600L);

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config); // 모든 경로에 허용
    return source;
}

 

 

setAllowedOrigins() 허용할 Origin을 지정
setAllowedMethods() 허용할 HTTP 메서드 (OPTIONS 포함해야 함)
setAllowedHeaders() 클라이언트 요청에서 사용할 수 있는 헤더
setExposedHeaders() 응답 시 노출할 헤더 (예: Authorization)
setAllowCredentials(true) 쿠키/인증정보 포함 여부 허용
setMaxAge(3600L) 프리플라이트 응답 캐싱 시간 (초)

 

 

SecurityFilterChain

http
    .cors(cors -> cors.configurationSource(corsConfigurationSource()))
    .csrf(csrf -> csrf.disable())
    // ... 생략

 

  • http.cors()를 사용해야 Spring Security가 CorsConfigurationSource를 적용한다.
  • WebMvcConfigurer에서 CORS를 설정하더라도, Security가 필터단에서 요청을 막기 때문에 적용되지 않을 수 있다.

 

 

 

- 프론트엔드 -

vite.config.js

export default defineConfig({
  base: "/",
  plugins: [react()],
  server: {
    proxy: {
      "/api": {
        target: "https://api.gasdg.store",
        changeOrigin: true,
        secure: false,
      },
    },
  },
});

 

  • 개발 환경에서 Cross-Origin 요청을 프록시로 우회한다.
  • 실제 서버에서는 프록시 없이 직접 요청하므로 서버 측 CORS 설정이 필수이다.

 

핵심 키워드

  • CORS: 다른 Origin 간 요청 제한하는 브라우저 보안 정책
  • CorsConfigurationSource: Spring에서 CORS 정책을 설정하는 Bean
  • SecurityFilterChain: Spring Security의 보안 필터 체인
  • HttpSecurity.cors(): CORS 설정을 Security 내부에 등록
  • Vite Proxy: 개발 환경에서의 Cross-Origin 문제 해결용 우회 방식

 


 

마무리

CORS 문제는 초기에 사소해 보이지만,

실제 운영 환경에서는 인증 흐름과 직결되기 때문에 정확한 위치에서 제대로 설정하는 것이 중요하다.

  • 프론트-백엔드 도메인이 분리된 구조에서는 반드시 Spring Security 레벨에서 CORS를 설정하자.
  • WebMvcConfigurer만 사용하는 방식은 Security 필터 앞에서 무시될 수 있어 보안상 허점이 될 수 있다.

이 설정을 통해 프론트엔드와 백엔드가 완전한 분리 구조를 유지하면서도 안전하게 통신할 수 있는 기반을 마련할 수 있었다.