A. 브라우저는 다음 조건을 모두 충족하는 요청일 경우에는 CORS 사전요청을 하지 않는다.
1. 메소드Method가 GET, HEAD, POST 중 하나이면서
2. 헤더Headers에 유저 에이전트User Agent가 자동으로 설정한 헤더(User-Agent, Connection)만 있거나, Accept, Accpet-Language, Content-Language만 있거나 Content-Type은 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나이거나 조합인 경우다.
더 자세한 설명은 아래 출처를 참고하자.
출처: MDN: Cross-Origin Resource Sharing (CORS)
예를 들어서 요청 메소드를 POST로 하고, Content-Type을 application/json으로 할 경우는 위 경우를 충족하지 않기 때문에 CORS 사전요청preflight가 발생한다.
preflight이 발생 예1: POST 메소드, Content-Type 'application/json'
www.taptorestart.com:9000에서 api.taptorestart.com:8000으로 아래 예처럼 POST 메소드로 자바스크립트로 Content-Type을 'application/json'해서 요청을 해보자.
function main()
{
(function() {
const httpRequest = new XMLHttpRequest();
const data = "{ }";
httpRequest.onreadystatechange = logContents;
httpRequest.open('POST', 'http://api.taptorestart.com:8000/');
httpRequest.setRequestHeader('Content-Type', 'application/json');
httpRequest.send(data);
function logContents() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
console.log(httpRequest.status);
console.log(httpRequest.responseText);
}
}
})();
}
아래 캡쳐화면처럼 사전요청preflight이 발생하는 것을 볼 수 있다.

서버 쪽 로그를 보면 내가 요청하지도 않은 OPTIONS 요청이 온 것을 볼 수 있다. 이 OPTIONS 요청이 바로 사전요청preflight이다!

preflight이 발생 예2: GET 메소드, 헤더에 Authorization 추가
POST라서 특별히 발생한 것일까. www.taptorestart.com:9000 에서 api.taptorestart.com:8000 으로 아래 예처럼 GET 메소드로 자바스크립트로 헤더에 'Authorization'을 추가해서 요청을 해보자.
...
const httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = logContents;
httpRequest.open('GET', 'http://api.taptorestart.com:8000/');
httpRequest.setRequestHeader('Authorization', 'Bearer yourtoken');
httpRequest.send();
...
GET 메소드에 Authorization 추가했을 뿐인데 사전요청preflight이 발생한 것을 확인할 수 있다.

역시나 서버 쪽 로그를 보면 내가 요청하지도 않은 OPTIONS 요청이 온 것을 볼 수 있다.

preflight이 발생 예3: DELETE 메소드
DELETE 메소드로 해보면 어떻게 될까. 아래처럼 아무런 헤더도 추가하지 않고 테스트 해보자.
...
const httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = logContents;
httpRequest.open('DELETE', 'http://api.taptorestart.com:8000/');
httpRequest.send();
...
사전요청preflight이 발생한 것을 확인할 수 있다.



사전요청preflight의 Headers 정보는 아래와 같다.

요청 헤더에는 Accept, Accept-Encoding, Accept-Language, Access-Control-Request-Method, Connection, Host, Origin, Referer 등 최소 정보만 있는 것을 확인할 수 있다.
응답 헤더에는 Access-Control-Allow-Origin 정보를 확인할 수 있다. 이 경우 사전요청preflight을 한 경우에는 같은 요청을 했을 때 추가로 다시 사전 요청을 하지 않는다.
아래 예처럼 말이다.

CORS 허용 도메인에 추가하지 않은 test.com:9000으로 시도하면 사전요청의 응답 헤더가 다른 것을 확인할 수 있다.

응답 헤더에 Access-Control-Allow-Origin 정보가 없다. 그리고 같은 요청을 할 때마다 사전요청이 발생한다. 서버에는 OPTIONS만 찍힌다.


CORS 허용되지 않은 경우는 원래 요청한 DELETE 메소드로 서버에 요청을 못하고 사전요청인 OPTIONS에서 막힌 것으로 이해할 수 있다.
preflight이 발생하지 않는 예
이번에는 아래 코드 예처럼 POST로 요청하되 Content-Type 헤더를 빼자.
...
const httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = logContents;
httpRequest.open('POST', 'http://api.taptorestart.com:8000/');
httpRequest.send();
...
preflight가 발생하지 않은 것을 확인할 수 있다.

이번에는 지난 예와 달리 OPTIONS 호출이 없다.

테스트 코드
테스트에 사용한 코드는 아래 링크에서 확인할 수 있다. 그밖의 경우도 직접 테스트 해볼 수 있다.
www.taptorestart.com 코드: CORS Test using Flask
api.taptorestart.com 코드: DRF - CRUD
DRF - CRUD 코드에서
requirements.txt에
django-cors-headers==3.14.0
settings.py에 아래 코드를 추가해서 테스트 했다.
CORS_ALLOWED_ORIGINS = [
'http://www.taptorestart.com:9000',
]
INSTALLED_APPS = [
...
'corsheaders',
...
]
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
...
]