javascript에서 ajax 요청을 보내면 브라우저 콘솔에 아래와 같이 에러가 나면서 요청이 실패합니다.
1) 크롬
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin ‘[요청한 도메인]' is therefore not allowed access.
2) 파이어폭스
교차 원본 요청 차단: 동일 출처 정책으로 인해 [요청한 도메인]에 있는 원격 자원을 읽을 수 없습니다. 자원을 같은 도메인으로 이동시키거나 CORS를 활성화하여 해결할 수 있습니다.
이 오류가 발생하는 이유는 아래와 같습니다.
교차 출처 리소스 공유
교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.
ex) http://domain-a.com에서 http://domain-b.com/data.json을 요청하는 경우
보안 상의 이유로, 브라우저는 script에서 시작한 HTTP 요청을 제한합니다. 예를 들어 XMLHttpRequest는 동일 출처 정책을 따릅니다. 즉, 자신의 출처와 동일한 리소스만 호출할 수 있으며, 다른 출처의 리소스를 불러오려면 그 출처에서 올바른 CORS 헤더를 포함한 응답을 반환해야 합니다.
HTTP 요청 방식에는 두 가지 방법이 있습니다.
1. Simple Request(공식 용어는 아님)
아래 조건을 모두 충족하는 요청의 경우 CORS preflight를 트리거 하지 않습니다.
1) 다음 중 하나의 method
→ GET
→ HEAD
→ POST
2) User agent가 자동으로 설정한 header
→ Accept
→ Accept-Language
→ Content-Language
→ Content-Type(아래 추가 요구 사항을 만족해야 함)
→ DPR
→ Downlink
→ Save-Data
→ Viewport-Width
→ Width
3) Content-Type 헤더는 다음의 값들만 허용합니다.
→ application/x-www-form-unlencoded
→ multipart/form-data
→ text/plain
Simple Request의 경우 아래와 같이 한 번만 통신합니다.
client는 서버에게 아래와 같은 내용을 보냅니다.
요청 헤더의 Origin을 보면 https://foo.example로부터 요청이 왔다는 것을 알 수 있습니다.
Origin: https://foo.example
서버는 이에 대한 응답으로 Access-Control-Allow-Origin 헤더를 전송합니다. 아래의 옵션은 모든 도메인을 허용하는 옵션이며, 특정 도메인만 허용하고 싶을 경우 해당 도메인 주소를 설정하면 됩니다.
Access-Control-Allow-Origin: *
2. Preflighted Reques
Preflighted Reques는 Simple Request와는 달리, 사전에 OPTIONS method를 통해 HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인합니다. Cross-site 요청은 유저 데이터에 영향을 줄 수 있기 때문에 이와 같이 미리 전송(preflighted) 합니다.
Simple Request의 조건에 만족하지 않은 경우 아래와 같이 Preflighted Request가 발생하게 됩니다.
첫 번째 통신은 Preflighted 통신입니다.
Request
Origin: http://foo.example
Response
Access-Control-Allow-Origin: https://foo.example
Preflight request가 완료되면 실제 요청을 전송합니다.
해결 방법
1. jsonp 사용
GET 방식 밖에 사용할 수 없기 때문에 길이의 제한이 있습니다.
2. 서버에서 CORS 컨트롤하기
서버 서버 설정으로 요청을 허용하는 방식입니다.
서버 설정에 아래 header를 추가합니다.
(Access-Control-Allow-Origin: * 만 추가해도 되긴 합니다.)
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization
Tomcat의 경우에는 conf/web.xml에 아래와 같이 filter를 추가하면 됩니다.
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
</init-param>
<init-param>
<param-name>cors.exposed.headers</param-name>
<param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
</init-param>
<init-param>
<param-name>cors.support.credentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.preflight.maxage</param-name>
<param-value>10</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
[참고사이트]
'웹 개발' 카테고리의 다른 글
NPE(Null Pointer Exception)으로부터 안전한 프로그래밍 하기 (1) | 2020.11.08 |
---|---|
Spring lazy Initialization (0) | 2020.10.13 |
시간 기반 UUID 생성(Generate time based UUIDs) (0) | 2020.09.29 |
Lombok Annotation (0) | 2020.09.28 |
Annotation을 활용한 Spring AOP 활용 (0) | 2020.09.24 |