이번에는 CORS에 대해서 알아볼 것이다.
CORS(Cross-Origin Resource Sharing)이란?
CORS는 출처가 다른 사이트 간의 자원 접근을 허용하는 것이다.
기본적으로 SOP로 인해 출처가 다른 사이트 간의 접근을 막지만, CORS는 그것을 허용해 주는 역할인 것이다.
1. 다른 사이트로 요청을 보내면 브라우저는 Request Header에 Origin을 자동으로 포함하여 서버로 전달한다.
2. 그러면 서버는 Origin을 확인하고 Access-Control-Allow-Origin과 함께 응답을 한다.
3. 브라우저는 Access-Control-Allow-Origin가 현재 Origin과 일치하지 않으면 JS의 데이터 접근을 차단한다.
그리고 CORS Error 메시지를 띄운다.
만약 일치한다면 해당 데이터를 확인할 수 있는 것이다.
> 그러면 개발할 때, CORS 메시지가 나타나면 어떻게 해야하는가?
서버 CORS 정책에 localhost:3000을 추가하거나
Proxy 서버를 사용하여 우회하는 방법이 있다.
2번 방법은 직접 해보면 좋을 것 같다.
필자도 머리로만 아는 것보다는 직접 해보는 게 나을 것 같아서 시도해 보았다.
vite를 사용 중이라 vite.config.ts에 다음과 같이 설정했다.
좌측은 CORS에러가 발생하였지만, 우측은 Proxy를 통해 성공한 것이다.
이 과정을 간단하게 설명하자면
1. 유저들의 정보를 불러오는 API인 /api/users가 실행되었을 때 localhost:3001/api/users로 향할 것이다.
2. 그러면 vite 서버가 /api로 되어있는 요청을 받으면 api부분을 지우고 실제로 보내고 싶은 target URL로 전송한다.
3. CORS는 브라우저에서의 정책이므로, 서버 간 통신에서는 등장하지 않는다.
자 CORS가 뭔지도 알았고 어떻게 해결해야 할지도 이해했다.
그러면 이번에는 조금 더 깊게 들어가 보자.
또 CORS와 관련되어 자주 등장하는 게 있지 않는가?
바로 Prefilght이다.
CORS는 사실 종류가 두 가지이다.(진짜로는 세 개)
1. 단순 요청(Simple Request)
단순 요청은, 바로 서버에 요청을 날리는 것이다.
단순 요청의 조건은 다음과 같다.
1️⃣ HTTP Method: GET, POST, HEAD 중 하나임
2️⃣ Accept-Language, Accept 또는 Content-Language 헤더
3️⃣ Content-Type 헤더(multipart/form-data, application/x-www-form-urlencoded 또는 text/plain)
2. 사전 요청(Preflight Request)
(위에서 본 사진처럼 Preflight라고 나타나는 요청이다)
사전 요청은, 요청을 보내기 전에 OPTIONS Method로 사전 요청을 보내는 것이다.
그 후에 CORS가 허락을 하는 경우에 실제 요청을 보내는 것이다.
사전 요청은, 단순 요청의 조건 중 하나라도 만족하지 않는 경우 실행된다.
3. 인증 포함 요청(Credentialed Request)
이거는 지금 글을 작성하면서 알게 됐는데(원래는 두 개인 줄 알았음)
이 요청은 쿠키나 인증 헤더, 세션 등을 포함하여 요청하는 경우이다.
예를 들어, axios에서 withCredentials: true 같은 것을 설정한 경우이다.
이 경우에는 서버 측에서도 다음 두 개의 헤더를 응답에 포함시켜야 한다.
Access-Control-Allow-Origin: Domain(* 안됨)
Access-Control-Allow-Credentials: true
이 정도까지 공부한다면 머리로는 어느 정도 이해는 되겠지만
실제로 한 번 해보자.
아래 사진과 같이 단순 요청과 사전 요청에 대한 코드를 작성했다.
둘 다 로그인을 요청하는 Post Method를 사용했지만
단순 요청의 조건을 맞추기 위해 json형태가 아니라 form형태로 변경하였다.
먼저 단순 요청 버튼을 클릭한 결과이다.
사용자의 브라우저에는 CORS 에러가 발생하지만
서버에는 요청이 잘 들어오는 것을 확인할 수 있다.
다음으로는 사전 요청 버튼을 클릭한 결과이다.
사용자의 브라우저에는 preflight 요청이 일어났고, CORS로 인해 본 요청이 오지 않으니
서버에서는 OPTIONS method만이 로그에 남아있다.
이거 근데 왜 했어?
처음에는 POST가 단순 요청이라고 하길래 믿기지가 않았다.
왜냐하면 GET은 서버로부터 받아오기만 해서 단순 요청인게 이해가 가지만
'POST는 서버의 데이터를 변경할 수도 있는 건데 어떻게 이게 단순 요청인거지?'라는 생각과
개발하다가 preflight가 발생한 경우에 대부분 POST요청이었기 때문이다.
그러나, 사실 단순 요청이 아닌 이유는 다른 조건에서 찾을 수 있다.
3️⃣Content-Type 헤더(multipart/form-data, application/x-www-form-urlencoded 또는 text/plain)
개발할 때 주로 json형태로 통신하다 보니 매번 단순 요청이 아니라 사전 요청인 것이다.
그래서 사실상 일반적으로 개발자들은 사전 요청으로 인한 CORS 에러를 보고 있었던 것이다.
(예전에 'OPTIONS?? 난 이런 거 안 보냈는데' 했었던 기억이 떠오른다)
느낀 점
예전에 CORS에 대해서 단순 요청과 사전 요청, 그리고 조건들을 배우고 나서 다 이해했다~라고 생각했었다.
그리고 그 기억을 되살려 글을 작성하려고 했는데,
🤖(ChatGPT): CORS는 CSRF 막으려고 만들어진 거 아님
라는 말 한마디가 다시 알아보게 된 계기가 되었다.
CSRF나 XSS 공격도 자세하게 짚고 넘어가고 싶지만
일단은 SOP와 CORS를 정리하는 게 우선이었으므로
나중으로 미루고자 한다 ㅎㅎ..
'📚 지식글' 카테고리의 다른 글
[1] CORS만 알았지, SOP는 몰랐다. (0) | 2025.04.24 |
---|---|
[0] 나를 자꾸 괴롭히는, CORS란? (0) | 2025.04.23 |
[정보] Jetbrains, IntelliJ, 인텔리제이 라이센스 갱신이 안될 때 (0) | 2024.04.02 |
[정보] Jetbrains, IntelliJ, 인텔리제이 학생(교육) 라이센스 갱신하는 방법 (0) | 2024.04.01 |
[에러해결] Adobe creative cloud 삭제 방법 (0) | 2023.08.10 |