[트러블 슈팅] Spring Cloud Gateway CORS 설정하기
MEETPLUS 프로젝트 당시 프론트에서 HTTP GET 요청을 했는데 CORS 오류가 발생했습니다.
클라이언트 모든 요청은 gateway를 통해 들어오고 있었고, Spring Cloud Gateway 서버가 gateway 역할을 담당하고 있었습니다.
gateway에서는 MSA의 각 도메인 서버별로 구체적인 경로와 필터를 지정해 준 상태였습니다.
그런데 왜 CORS 오류가 발생했을까요?
이는 preflight 요청때문이었습니다.
preflight 요청이란, 브라우저가 서버에 요청을 보내기 전에 이 요청을 허용할지를 확인하기 위해 보내는 예비 요청입니다.
preflight 요청이 발생하는 조건
1. CORS 요청이 "단순 요청(Simple Request)" 조건을 충족하지 않는 경우
단순 요청의 조건은 다음과 같습니다.
- HTTP 메서드가 GET, POST, HEAD 중 하나일때
- 요청 헤더에 CORS-safelisted request-header만 있을때
- Accept
- Accept-Language
- Content-Language
- Content-Type: text/plain, multipart/form-data, application/x-www-form-urlencoded일때
- Range (오직 단순 범위 헤더 값, 예를 들어 bytes=256- 혹은 bytes=127-255)
위 단순 요청 조건 중 하나라도 만족하지 않으면 preflight 요청이 발생합니다.
다음과 같은 경우에도 preflight 요청이 발생합니다.
2. 사용자 정의 헤더가 포함된 경우
3. CORS 요청에 인증 정보가 포함된 경우
- 요청에 withCredentials: true 설정이 있거나 쿠키, HTTP 인증 정보가 포함된 경우, preflight 요청이 필요할 수 있습니다.
4. CORS 정책에서 추가적인 조건이 필요한 경우
- 서버와의 요청에서 특정 Origin, 헤더, 메서드가 허용되었는지 확인해야 하는 경우 브라우저는 preflight 요청을 통해 확인합니다.
5. 요청에 ReadableStream 객체가 사용되는 경우
다음은 우리 프로젝트의 프론트에서 조회 요청을 보내는 fetch입니다.
Content-type이 application/json이고, 커스텀 헤더가 있기 때문에 브라우저는 preflight 요청을 보낼 것입니다.
실제로 브라우저 개발자도구의 network 탭을 보면
OPTIONS 메서드로 요청을 보내고 있었습니다. 뜬금없이 왜 OPRIONS냐고요?
preflight 요청은 OPTIONS 메서드로 요청합니다.
실제로 preflight 요청을 보내고 있는걸 확인할 수 있었습니다.
그러면 서버에서 preflight 요청을 승인하려면 어떻게 해야할까요?
아까 gateway 서버에 해준 도메인 서버별 설정은 다음과 같습니다.
Path에 해당하는 API 요청시 허용되는 HTTP Method가 GET뿐입니다.
그러므로 Method에 OPTIONS를 추가하면 됩니다.
그런데 서버마다 일일이 추가하는건 비효율적입니다.
Spring Cloud Gateway에서는 전역 CORS 설정을 지원합니다.
바로 spring.cloud.gateway.globalcors.cors-configurations.allowed-methods 입니다.
여기에도 OPTIONS를 추가해줍니다.
"OPTIONS 는 서버로부터 추가 정보를 얻기 위해 사용되는 HTTP/1.1 메서드이며 리소스를 변경할 수 없는 즉, 서버의 상태를 바꾸지 않는 안전한 메서드"이기 때문에 누군가 OPTIONS METHOD로 요청해도 예상치 못하게 데이터가 바뀌는 일은 없을 것입니다.
다음에는 CORS 요청 시 preflight 요청과 simple 요청의 성능 차이를 알아봐야겠습니다. 만약 트래픽이 몰릴때 preflight 요청이 적지 않은 성능 저하를 불러온다면, 이를 돌파할 방법도 찾아봐야겠습니다.
참고
교차 출처 리소스 공유 (CORS) - HTTP | MDN
교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 브라우저가 자신의 출처가 아닌 다른 어떤 출처(도메인, 스킴 혹은 포트)로부터 자원을 로딩하는 것을 허용하도록 서버가 허가 해주는 HTTP
developer.mozilla.org