1. CORS :
(Cross-Origin Resource Sharing,CORS) 란 다른 출처의 자원을 공유할 수 있도록 설정하는 권한 체제를 말함.
- 웹 컨텐츠에서의 출처를 의미
- 스킴 + 호스트 + 포트
- 같은 출처를 가졌다 = 스킴, 호스트, 포트가 모두 일치한다
엄연히 protocol과 scheme은 다른 개념임.
여기선 http, https를 기준으로 설명하고 있기 때문에 프로토콜과 스킴을 혼용해서 사용함.
• 스킴 == 프로토콜 --> http, https, ftp, ...
• 스킴 != 프로토콜 --> file, magnet, mailto, ...
한 웹 어플리케이션이 다른 출처에 존재하는 자원에 접근하고 싶다면,
접근 권한을 줄 수 있도록 브라우저에 알려주는 체제인 CORS를 이용해야 함.
예를 들어 한 컴퓨터의 로컬에서 Spring Boot를 8080 포트로 띄우고,
React를 3000 포트로 띄웠다고 가정시에 서로 포트가 다르므로 다른 출처임을 알 수 있는데,
React가 Spring Boot의 API를 호출하려고 할 때, 출처(포트)가 다르므로 접근 권한이 없다며 CORS 에러가 발생한다.
(-> CORS를 설정해주지 않거나 제대로 설정하지 않은 경우, 원하는대로 리소스를 공유하지 못하게 됨.)
사진처럼 CORS에러가 발생하게 되며, Spring에서 해결 할 방법이 있다.
2-1 Configuration
이 방법은 Global하게 적용하는 방법이며,
config패키지를 만들어 WebConfig클래스를 만든다.
경로 : /src/main/java/{project}/config
클래스 상단에 @Configuration 어노테이션을 통해 설정파일이라는 것을 알리고,
WebMvcConfigurer를 implements 한다.
addCorsMappings메소드를 오버라이드 함.
2-2 addMapping
registry.addMapping을 이용해서 CORS를 적용할 URL패턴을 정의할 수 있고,
위 사진과 같이 "/**" 와일드 카드를 사용할 수도 있다.
Ant-style을 지원하며 "/somePath/**" 을 적용할 수도 있다.
Default값은 아래와 같습니다.
- Allow all origins.
- Allow "simple" methods GET, HEAD and POST.
- Allow all headers.
- Set max age to 1800 seconds (30 minutes).
2-3 allowedOrigins
allowedOrigins 메소드를 이용해서 자원 공유를 허락할 Origin을 지정할 수 있고,
위 사진과 같이 "*" 로 모든 Origin을 허락할 수 있다.
위 사진과 같이 한번에 여러 Origin을 설정할 수 있다.
2-4 allowedMethods
allowedMethods를 이용해서 허용할 HTTP method를 지정할 수 있고,
위 사진과 같이 여러 개를 지정할 수 있고 마찬가지 "*" 를 이용하여 모든 method를 허용할 수 있다.
2-5 maxAge
maxAge메소드를 이용해서 원하는 시간만큼 pre-flight 리퀘스트를 캐싱 해둘 수 있다.
3. Annotation
Controller 또는 메소드단에서 annotation을 통해 적용하는 방법이다.
위 사진과 같이 CrossOrigin이라는 어노테이션을 사용하면, global하게 설정하던 것과 같이 허용할 origins이나 methods를 지정할 수 있다.
origins, methods, maxAge, allowedHeaders를 사용하시면 된다.
또한 메소드에 적용되게 할 수 있다.
추가적으로,
- @CrossOrigin 어노테이션을 CORS 정책을 적용할 대상에 적용.
- WebMvcConfigurer를 상속한 Config 클래스를 만들어서 CORS 정책을 설정하거나,
- RestTemplate을 이용한 프록시 서버를 구현하면 된다.
4. CORS의 동작 과정
기본적으로 웹 클라이언트 애플리케이션이 다른 출처의 리소스를 요청할 때는 HTTP 프로토콜을 사용하여 요청을 보내게 되고,
이 때 브라우저는 요청 헤더에 Origin 이라는 필드에 요청을 보내는 출처를 함께 담는다.
Origin: https://google.com 과 같이 출처를 담아서 서버로 보내면, 서버는 응답 헤더의 Access-Control-Allow-Origin이라는 값에
이 리소스를 접근하는 것이 허용된 출처 목록을 담아준다.
이후 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교한다.
만약 허용되지 않는 Origin이면 CORS 정책 위반 이슈가 발생한다.
5-1 Preflight Request
Preflight Request 방식은 일반적으로 사용하는 방식으로,
브라우저는 요청을 한 번에 보내지 않고 예비 요청과 본 요청으로 나누어서 서버로 전송하며,
이 때 예비 요청은 OPTIONS 메소드를 사용하여 본 요청을 보내기 전에, 브라우저 스스로 이 요청이 안전한지 확인하는 역할을 한다.
동작 과정은 아래와 같다.
- JavaScript의 fetch() API를 사용하여 브라우저에게 서버의 리소스를 받아오라는 명령을 내린다.
- 브라우저는 서버에게 예비 요청을 보낸다.
- 예비 요청의 응답으로 Access-Control-Allow-Origin의 값을 확인하여, 허용하는 출처를 조회한다.
- 브라우저 요청의 출처가 허용되지 않는다면, CORS 정책 위반 이슈가 발생한다. (이때 응답의 상태 코드는 200번이 온다.)
- 예비 요청의 응답이 성공하지 못하면, CORS 정책 위반 이슈가 발생한다.
- 예비 요청이 성공하면, 실제 본 요청을 보내서 서버의 리소스를 받아 온다.
5-2 Simple Request
Simple Request는 예비 요청을 보내지 않고 서버에게 바로 본 요청을 보낸 후,
응답 헤더의 Access-Control-Allow-Origin 값을 확인하여 CORS 정책 위반 여부를 확인한다.
다만, 아무 때나 Simple Request를 사용할 수 있는 것은 아니고 특정 조건을 만족해야 한다. 조건은 다음과 같다.
1. 요청의 메소드는 GET, HEAD, POST 중 하나.
2. Accept, Accept-Language, Content-Language, Content-Type,
DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안 되며,
3. Content-Type를 사용하는 경우에는 application/x-www-form-urlencoded, multipart/form-data, text/plain만 허용됨.