DDD&MSA

[Spring Cloud Gateway] 여러 도메인에 대해 CORS 설정하기

덩라 2023. 12. 24. 19:17

0. API Gateway 에서의 CORS 설정

현재 개발 중인 프로젝트의 백엔드 서버는 MSA 구조로 개발되어 있어서, 프론트엔드 서버의 요청을 아래와 같은 흐름으로 받아들이고 있습니다.

클라이언트의 요청을 API Gateway 가 받아서, URI 를 확인해서 적절한 마이크로 서비스로 요청을 라우팅하는 형태입니다.

이 때, API Gateway는 클라이언트 요청에 대해 CORS 설정을 추가해야 합니다.

CORS 에 대한 내용은 아래 포스팅을 참고해주세요.
https://byunsw4.tistory.com/22
 

Access to XMLHttpRequest at 'http://localhost:8888/api/v1/login' from origin 'http://localhost:3000' has been blocked by CORS po

아래 환경에서 개발하다가 로그인 기능을 테스트하는 단계에서 발생한 오류입니다. Front-End : React (axios 사용) 3000번 포트 서비스 Back-End : Java / Spring 8888 포트 서비스 위 에러가 발생한 원인은 웹

byunsw4.tistory.com

 

API Gateway 에 클라이언트 요청에 대한 CORS설정을 추가하게 된다면, 아래와 같은 흐름으로 요청이 흘러가게 됩니다.

위 같은 형태를 설정하기 위해, Spring Cloud Gateway 에선 어떻게 CORS 를 적용하는지 알아보았습니다.

 

1. Spring Cloud Gateway 에서 전체적으로 CORS 적용

현재 개발 중인 프로젝트는 Spring Cloud 기반으로 MSA 구조가 구현되어 있기 때문에, API Gateway 역할을 Spring Cloud Gateway 를 사용합니다.

Spring Cloud Gateway 에서는 아래와 같이 CORS 를 설정하도록 가이드하고 있습니다.

https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/cors-configuration.html

 

CORS Configuration :: Spring Cloud Gateway

You can configure the gateway to control CORS behavior globally or per route. Both offer the same possibilities.

docs.spring.io

 

위 설정을 제 API Gateway 프로젝트에 적용했을 때, 아래와 같이 적용되었습니다.

# application.yml
spring:
  config:
    activate:
      on-profile: local

  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allow-credentials: true
            allowed-origins: "http://localhost:3000"
            allowed-headers: "*"
            allowed-methods:
              - PUT
              - GET
              - POST
              - DELETE
              - OPTIONS

 

Spring Cloud 는 내부적으로 요청 URI 에 대한 CORS 옵션을 Map 형태로 저장합니다.

고로 위 설정은, 모든 URI 요청 (/**) 에 대해 아래와 같은 옵션을 적용하는 설정입니다.

  1. http://localhost:3000 으로의 요청은 모두 허용한다.
  2. 모든 Request Header 값을 허용한다.
  3. PUT / GET / POST / DELETE / OPTIONS 로 온 요청은 허용한다.

 

2. 여러 도메인에 CORS global 적용하기

API Gateway 는 클라이언트의 요청이 통과하는 관문과 같은 역할을 수행하기 때문에, 많은 클라이언트의 요청이 집중될 수 밖에 없습니다.

그 클라이언트는 같은 시스템으로써 서비스되는 프론트엔드 혹은 모바일 서버일 수 도 있고, 다른 외부 시스템일 수 도 있습니다.

즉, 모든 클라이언트가 서비스하는 도메인이 다 다를 경우, API Gateway 는 이에 대해 CORS 를 적용해주어야 합니다.

 

Spring Cloud 가 어떻게 Global CORS 를 등록하는지, 찾아보던 중 아래 코드를 찾을 수 있었습니다.

```GatewayAutoConfiguration.java
@Configuration(proxyBeanMethods = false) // 여러 annotation 이 있지만, 나머진 생략
public class GatewayAutoConfiguration {
    // 중략
    @Bean
    public GlobalCorsProperties globalCorsProperties() {
        return new GlobalCorsProperties();
    }
    // 중략
}

```GlobalCorsProperties.java
@ConfigurationProperties("spring.cloud.gateway.globalcors")
public class GlobalCorsProperties {

	private final Map<String, CorsConfiguration> corsConfigurations = new LinkedHashMap<>();

	public Map<String, CorsConfiguration> getCorsConfigurations() {
		return corsConfigurations;
	}

}

```CorsConfiguration.java
public class CorsConfiguration {
    // 중략

    @Nullable
    private List<String> allowedOrigins;
    
    // 중략
}

의미를 대략 요약하자면, 다음으로 해석됩니다.

  1. GatewayAutoConfigration.java 가 @Configuration 에 의해, @Bean 이 붙은 GlobalCorsProperties를 스프링 Bean 으로 등록한다.
  2. GlobalCorsProperties.java 는 @ConfigurationProperties("spring.cloud.gateway.globalcors") 에 의해 properties 파일(application.properties 혹은 application.yml) 내에 spring.cloud.gateway.globalcors 하위에 정보Map<String, CorsConfiguration> 형태로 생성한다.
  3. CorsConfiguration 이 생성되면서, properties 파일의 allowedOrigins 의 값을 List 로 저장한다.

 

위 과정을 미루어 봤을 때, 공식문서에 나와있진 않지만 yml 설정의 value 가 List 로 인식되는 형태로 설정하면 된다는 것을 알 수 있습니다.

#application.yml
spring:
  config:
    activate:
      on-profile: local

  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allow-credentials: true
            # 이것도 가능
            #allowed-origins: "http://localhost:3000, http://localhost:3001"
            allowed-origins:
              - http://localhost:3000
              - http://localhost:3001
            allowed-headers: "*"
            allowed-methods:
              - PUT
              - GET
              - POST
              - DELETE
              - OPTIONS

 

yml 파일 문법 참고 : https://namu.wiki/w/YAML
 

YAML

기존에 주로 사용되던 포맷인 JSON 의 불편함을 해소하기 위해 만들어진 superset 이다. 즉 일반적인 j

namu.wiki