공식 레퍼런스 https://fetch.spec.whatwg.org/
[프론트 단에서의 비동기 통신 요청]
- XMLHttpRequest 객체를 이용한 AJAX(AsynchronousJavaScript And XML (en-US)) 프로그래밍을 용이하게 하기 위해 jquery-ajax/fetch/axios 를 사용함.
[서버 단에서의 비동기 통신 응답]
- api 엔드포인트에서 적절한 응답을 보내주면 되는데, 이 때 CORS 정책을 잘 지켜주어야 한다.
1. CORS(Cross Origin Resource Sharing)란?
- Origin 이 다른 경우에 자원을 주고 받을 수 있도록 하는 http 통신 프로토콜.
- Origin 은 https://naver.com, http://localhost:8080 과 같이 (프로토콜+호스트+포트번호) 의 조합을 말한다.
- 요청하는 클라이언트와 응답해야하는 서버의 Origin 이 다른 경우에, CSRF(Cross-Site Request Forgery) 나 XSS(Cross-Site Scripting) 같은 공격에 취약하기 때문에, 특정 Origin 과 request method 에만 요청을 허용하기 위함이다.
- HttpRequest, HttpResponse 헤더에 특정 값을 지정함으로써 CORS 요청과 응답을 처리한다.
(1) CORS 요청
CORS 요청을 하기 위해서, 프론트 단에서 특별히 해주어야 할 일은 거의 없다. 브라우저가 CORS 검증을 다 해주기 때문이다. 요청을 보낼 때 Origin, Access-Control-Request-Method, Access-Control-Request-Headers 등 필요한 요청 헤더 필드를 브라우저가 알아서 추가하기 때문이다.
- (1) CORS request
: request header 에 Origin이 포함되어 있어야 함. 모든 http method (GET, POST, OPTION, HEAD 등) 에 포함된다. 본 요청이 수행됨과 동시에 cors 정책을 확인한다.
- (2) CORS preflight request
: 본 요청(실제 프론트에서 호출한 요청)에 앞서서 브라우저가 cors 정책을 확인하기 위해, http method 를 Option 으로 하여 먼저 보내는 요청이다. 서버에 이 요청을 보내서 응답 헤더가 유효한 cors 헤더이면 통과되어 본 요청을 수행하고 자원을 받아올 수 있다.
: Origin을 포함해서 Access-Control-Request-Method, Access-Control-Request-Headers 가 포함되어 있으면 preflight 요청이다. Access-Control-Request-Method 은 본 요청에서 수행할 http method 를 적고, Access-Control-Request-Headers 에는 본 요청 헤더에 포함될 헤더필드 목록들을 적는다. 이 또한 브라우저가 본 요청을 보고 알아서 만들어 보낸다.
: 이 요청은 원래 하려던 요청 외에 브라우저에 의해 추가로 수행되는 http 쿼리로 일종의 오버헤드라고 볼 수 있다. 그래서 preflight 요청은 성공시에 브라우저에 의해 일정시간 캐시될 수 있는데, 결과값이 캐시되어 있는 동안은 preflight 를 중복해서 매번 요청하지 않는다.
(2) CORS 응답
CORS 응답을 하기 위해서는 서버 개발자가 추가로 작업을 해주어야 하는데,
- 특정 자원(url) 을 누구(requestOrigin)에게 허락할 것인지,
- 요청 헤더의 목록 중에서 어떤 것들만 받아들일 것인지,
- 어떤 request method 만 허락할 것인지
- credential mode 를 사용할 것인지
- preflight 캐시 기간을 얼마나 설정한 것인지
등등 에 대해 일일이 설정을 해주어야 한다. 특정 url 마다 다르게 설정할 수도, 전역으로 모두 동일하게 설정할 수도 있다. 서버개발자의 몫이다. 서버개발자가 이런 부분에 대해서 설정을 해두었다면, 이 설정을 기반으로 서버 어플리케이션에 CORS 응답 헤더를 만들게 되는데, 아래의 헤더들을 작성해야한다.
- Access-Control-Allow-Origin : 허용된 Origin. 요청 origin 이 서버에 의해 허용되지 않으면, 이 헤더 필드가 존재하지 않거나, 필드 key 값은 존재하지만 value 에 요청 origin 값이 들어있지 않다.
- Access-Control-Allow-Credentials: 요청된 credential mode 에 따라 true / false 값이 설정되어 응답한다.
- Access-Control-Allow-Methods: 요청한 url 에 대해서 어떤 http method 가 허용되는지 응답한다.
- Access-Control-Allow-Headers: 요청 헤더중에서 어떤 헤더들을 허가할 지 응답한다.
- Access-Control-Max-Age: 이 응답 결과를 캐시하는 시간을 설정한다.
- Access-Control-Expose-Headers: 클라이언트에게 보여줄 헤더 목록을 설정한다.
단순한 CORS 요청은 어떤 응답코드여도 상관 없으나, preflight 요청이 성공하기 위해서는 200(ok) 또는 204(no content) 이어야만 한다. CORS 요청이 실패하면 403 응답이 가게 된다.
(3) CORS protocol and credentials
말 그대로 중요한 정보를 이용할 때 설정하는 것인데, 유일하게 프론트 단에서 설정해주어야하는 것이다.
(1) 쿠키를 이용하고 있어서, 요청을 보내면서 브라우저로 하여금 자동으로 쿠키를 포함하여 보내게 하거나,
(2) WWW-Authenticate, Authenticate 헤더와 같이 사용자 인증정보를 포함해야 할 때,
(3) TLS 클라이언트 인증 해야 할 때,
위의 3가지 경우에 설정해주어야 한다.
fetch("./", { credentials:"include" }).then(/* … */)
이런 식으로 설정하면 된다.
이 때 서버측의 응답은 Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Headers 값이 '*' 이면 안되고, Access-Control-Allow-Credentials 값이 'true' 이어야 한다. (case-sensitive 하기 때문에 True, TRUE 등은 제대로 작동하지 않는다.)
(4) 브라우저의 CORS 검증 로직
- Let origin be the result of getting `Access-Control-Allow-Origin` from response’s header list.
- If origin is null, then return failure. (헤더에 Origin 필드가 없으면)
- If request’s credentials mode is not "include" and origin is `*`, then return success.
- If the result of byte-serializing a request origin with request is not origin, then return failure.
- If request’s credentials mode is not "include", then return success.
- Let credentials be the result of getting `Access-Control-Allow-Credentials` from response’s header list.
- If credentials is `true`, then return success.
- Return failure.
2. CORS Origin 설정이 복잡해질 때, header 'Vary'를 이용한 HTTP 캐싱 피하기
'Vary'를 사용하지 않고 서버가 CORS 요청에 대한 응답으로 특정 리소스에 대해서만 'Access-Control-Allow-Origin'을 보내도록 구성되어 있으면 어떻게 되는지 생각해보자. 프로트 단에서 해당 리소스에 대한 non-CORS 요청에 대한 응답을 수신하면 (예: 검색엔진의 크롤링 탐색 요청의 결과로) 응답에 'Access-Control-Allow-Origin'이 포함되지 않고 사용자 브라우저는 해당 응답을 캐시한다. 그런 다음 사용자 브라우저는 이후에 리소스에 대한 CORS 요청을 만나면 'Access-Control-Allow-Origin' 없이 이전 non-CORS 요청에서 캐시된 응답을 사용한다.
하지만 'Vary: Origin'이 위에서 설명한 것과 동일한 시나리오에서 사용되면 사용자 브라우저가 이전의 'Access-Control-Allow-Origin'이 없는 non-CORS에서 캐시된 응답을 사용하는 대신 'Access-Control-Allow-Origin'을 포함하는 응답을 가져온다.
그러나 'Access-Control-Allow-Origin'이 * 또는 특정 리소스에 대한 정적 원본으로 설정되어 있으면 서버가 리소스에 대한 응답으로 항상 `Access-Control-Allow-Origin'을 보내기 때문에 'Vary' 를 사용해서는 안된다.
3. Spring 설정하기
'웹 프로젝트 (IBAS) > SpringBoot api 개편' 카테고리의 다른 글
[SpringBoot] 6. ManyToMany 를 "일대다/다대일"로 풀어서 사용하기 (+ 영속성 전이 문제) (0) | 2022.03.20 |
---|---|
[Spring boot] 5. 멀티모듈? MSA? 좋은 아키텍쳐가 뭐야?! (1) | 2022.03.09 |
[Spring Boot] 3. OAuth2 인증 설계 및 구현 (feat. Security FilterChain 분석) (0) | 2022.02.28 |
[Spring Boot] 2. 서버 개발 환경 분리 (Spring Cloud Config 적용) 및 배포 자동화 (0) | 2022.02.28 |
[Spring Boot] 1. 게시판 테이블 재설계 (0) | 2022.02.28 |