Spring Security CORS 설정 가이드: 복잡도에 따른 최적의 접근법

Cori1304·2025년 8월 24일
0

JAVA Spring 이론

목록 보기
7/8

Level 1: 기본 설정 (단일 정책 시나리오)

대부분의 API가 하나의 동일한 CORS 정책을 공유하는, 일반적인 프로젝트 초기에 가장 적합한 방식입니다.

상황

  • 모든 API 엔드포인트가 http://localhost:3000이나 https://app.moyeohaeng.com과 같은 단일 종류의 클라이언트로부터 요청을 받습니다.

추천 방식: 암시적 설정 (Customizer.withDefaults())

SecurityConfig는 CORS 설정의 구체적인 내용을 모르고, 스프링 컨테이너에 등록된 CorsConfigurationSource 빈을 자동으로 가져와 사용합니다. 이를 통해 설정을 분리하고 코드를 간결하게 유지할 수 있습니다.

1. application.yml - 설정 외부화

CORS 정책을 외부 설정 파일로 분리하여 관리합니다.

cors:
  allowed-origins: http://localhost:3000
  allowed-methods: GET,POST,PUT,DELETE,PATCH,OPTIONS
  allowed-headers: '*'

2. CorsConfig.java - 설정 빈 생성

@ConfigurationProperties를 사용해 yml 값을 읽어와 CorsConfigurationSource 빈을 생성합니다.

@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "cors")
public class CorsConfig {

    private List<String> allowedOrigins;
    private List<String> allowedMethods;
    private List<String> allowedHeaders;

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(allowedOrigins);
        config.setAllowedMethods(allowedMethods);
        config.setAllowedHeaders(allowedHeaders);
        config.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}

3. SecurityConfig.java - 자동 설정 적용

.cors(Customizer.withDefaults()) 한 줄만으로 위에서 생성된 빈을 자동으로 적용합니다.

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .cors(Customizer.withDefaults()) // 자동으로 CorsConfigurationSource 빈을 찾아 적용
            // ... 기타 설정
            .build();
    }
}

Level 2: 고급 설정 (다중 정책 시나리오)

API의 역할(예: 일반 사용자용, 관리자용)에 따라 여러 개의 다른 CORS 정책을 적용해야 할 때 사용하는 방식입니다.

상황

  • 일반 사용자 API (/api/v1/**)는 웹 클라이언트의 접근을 허용합니다.
  • 관리자 API (/api/admin/**)는 내부 관리자 대시보드나 특정 IP에서만 접근을 허용해야 합니다.

추천 방식: 명시적 의존성 주입

각기 다른 CORS 설정을 담은 빈을 여러 개 만들고, SecurityFilterChain에서 필요한 설정을 명시적으로 주입받아 사용합니다. 이를 통해 명확한 의존 관계를 설정하고 세밀한 제어가 가능해집니다.

1. CorsConfig.java - 여러 개의 설정 빈 생성

@Qualifier를 사용하여 각기 다른 CORS 설정 빈을 이름으로 구분하여 생성합니다.

@Configuration
public class CorsConfig {

    @Bean
    @Qualifier("appCorsSource")
    public CorsConfigurationSource appCorsConfigurationSource() { /* 웹 클라이언트용 설정 */ }

    @Bean
    @Qualifier("adminCorsSource")
    public CorsConfigurationSource adminCorsConfigurationSource() { /* 관리자용 설정 */ }
}

2. SecurityConfig.java - 필터 체인 분리 및 명시적 주입

URL 패턴에 따라 SecurityFilterChain을 분리하고, @Qualifier를 통해 해당 필터 체인에 맞는 CorsConfigurationSource 빈을 명시적으로 주입합니다.

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    // 일반 사용자 API 보안 설정
    @Bean
    @Order(1)
    public SecurityFilterChain apiFilterChain(HttpSecurity http, @Qualifier("appCorsSource") CorsConfigurationSource corsSource) throws Exception {
        http
            .securityMatcher("/api/v1/**")
            .cors(cors -> cors.configurationSource(corsSource)) // 'appCorsSource' 명시적 사용
            // ...
        return http.build();
    }

    // 관리자 API 보안 설정
    @Bean
    @Order(2)
    public SecurityFilterChain adminFilterChain(HttpSecurity http, @Qualifier("adminCorsSource") CorsConfigurationSource corsSource) throws Exception {
        http
            .securityMatcher("/api/admin/**")
            .cors(cors -> cors.configurationSource(corsSource)) // 'adminCorsSource' 명시적 사용
            // ...
        return http.build();
    }
}

결론

구분기본 설정 (암시적)고급 설정 (명시적)
키워드Customizer.withDefaults()파라미터로 CorsConfigurationSource 주입
장점간결함, 낮은 결합도명확함, 세밀한 제어
적합한 상황단일 CORS 정책다중 CORS 정책, 복잡한 보안 규칙

프로젝트 초기에는 기본 설정으로 시작하여 단순함을 유지하고, 기능이 확장되어 API 그룹별로 다른 보안 정책이 필요해지는 시점에 자연스럽게 고급 설정으로 리팩토링하는 것이 좋습니다.

profile
개발 공부 기록

0개의 댓글