CORS, 캐시, CDN을 이해하려고 웹 요청 흐름부터 다시 정리해봤다
CORS는 "갑자기 뜨는 에러"가 아니라,
브라우저가 리소스를 가져오고, 캐시를 확인하고, 응답 헤더를 검사하는 과정 안에서 발생하는 정책 체크다.
CORS 문제를 보다가 오히려 더 헷갈렸던 건 에러 문구 자체보다 흐름이었다.
- 브라우저는 HTML을 받고 나서 뭘 하는지
- CSS나 JS는 언제 다시 요청하는지
- 캐시는 언제 쓰고, 언제 안 쓰는지
- CDN이 끼면 응답이 어떻게 달라지는지
- 그리고 CORS는 정확히 어느 시점에 막히는지
처음엔 CORS 에러가 났다 = 서버 문제인가? 정도로만 생각했는데, 찾아보면 볼수록 단순한 얘기가 아니었다.
그래서 이번엔 CORS만 따로 보지 않고,
"사용자가 사이트에 접속해서 브라우저가 화면을 그릴 때까지 어떤 일이 일어나는가" 를 기준으로 다시 정리해봤다.
요약: 내가 이해한 흐름을 한 장으로 그리면

브라우저가 HTML을 읽고 정적 리소스를 가져오는 과정에서 캐시, CDN, 응답 헤더, CORS 정책이 개입하는 흐름
핵심은 브라우저가 단순히 파일만 받는 게 아니라,
캐시 사용 여부를 판단하고, 필요하면 CDN/origin에 요청하고,
마지막에 응답 헤더와 정책을 확인한 뒤 실제 사용 여부를 결정한다는 점이다.
먼저 한 번에 감 잡기
웹페이지 하나를 연다고 해서 브라우저가 HTML만 받고 끝나는 건 아니다.
대략 이런 흐름으로 움직인다.
- 사용자가 URL에 접속한다.
- 브라우저가 서버에 HTML을 요청한다.
- 서버가 HTML을 반환한다.
- 브라우저가 HTML을 읽으면서 필요한 리소스를 찾는다.
- CSS
- JavaScript
- 이미지
- 폰트
- 각 리소스마다 브라우저는 먼저 캐시를 볼 수 있다.
- 캐시를 못 쓰면 네트워크 요청을 보낸다.
- 응답을 받으면 헤더를 보고 정책을 판단한다.
- 그 결과 파일을 사용하거나 차단한다.
즉, CORS는 네트워크 전체 흐름 중 마지막 정책 검사 구간에서 보이는 결과에 가깝다.
문제의 원인은 그 전에 있었던 캐시, CDN 응답, 헤더 누락, 재검증 방식에 있을 수도 있다.
이 글에서 보는 핵심 개념 4개
처음엔 용어가 섞이면 더 헷갈려서, 이것만 먼저 잡고 가면 편하다.
1) origin
브라우저에서 origin은 보통 아래 3가지 조합으로 본다.
- 프로토콜
- 호스트
- 포트
예를 들면 아래는 서로 다른 origin이다.
같은 회사가 운영하는 CDN이어도
도메인이 다르면 브라우저 입장에서는 cross-origin 이다.
2) 캐시
브라우저는 이미 받아온 리소스를 다시 쓰려고 한다.
매번 네트워크 요청을 다시 보내면 느리고 비효율적이기 때문이다.
그래서 CSS, JS, 이미지 같은 정적 파일은 보통 캐시가 강하게 붙는다.
3) CDN
CDN은 정적 파일을 사용자와 더 가까운 위치에서 빠르게 내려주기 위한 계층이다.
브라우저가 CDN 주소로 요청하면:
- CDN 캐시에 파일이 있으면 CDN이 바로 응답
- 없으면 origin 서버에 다시 요청해서 받아온 뒤 응답
- 필요하면 CDN이 그 결과를 저장
즉, 브라우저가 받는 응답이 항상 origin 서버에서 "직접" 온다고 생각하면 안 된다.
4) CORS
CORS는 서버 간 통신 규칙이 아니라,
브라우저가 cross-origin 리소스를 사용할 수 있는지 검사하는 정책이다.
중요한 건 이거다.
- 요청 자체는 갈 수 있다
- 응답도 올 수 있다
- 하지만 브라우저가 응답을 사용하지 못하게 막을 수 있다
그래서 개발자 입장에서는 "분명 응답은 온 것 같은데 브라우저에서는 막힘"처럼 보일 수 있다.
브라우저가 페이지를 그릴 때 실제로 보는 흐름
이제 조금 더 실제에 가깝게 보면 이렇다.
1. 사용자가 페이지에 접속한다
브라우저가 먼저 HTML 문서를 요청한다.
GET /page
서버는 HTML을 반환한다.
2. 브라우저가 HTML을 읽는다
HTML 안에 이런 게 있을 수 있다.
<link rel="stylesheet" href="https://static.example.com/common.css" />
<script src="https://static.example.com/app.js"></script>
브라우저는 이걸 보고
아, common.css와 app.js가 더 필요하구나 를 판단한다.
3. 각 리소스마다 캐시를 먼저 볼 수 있다
이 시점에서 브라우저는 무조건 네트워크 요청부터 보내는 게 아니다.
먼저 생각할 수 있는 건:
- 이 파일을 전에 받은 적 있나?
- 아직 그대로 써도 되나?
- 서버에 재확인해야 하나?
여기서 갈린다.
- 캐시를 그대로 사용
- 재검증 요청
- 아예 새 요청
4. 캐시를 못 쓰면 네트워크 요청이 나간다
이제 브라우저가 common.css 를 요청한다고 해보자.
이 URL이 CDN 도메인이면:
- CDN이 먼저 요청을 받는다
- CDN 캐시를 확인한다
- 있으면 CDN이 바로 응답
- 없으면 origin 서버로 다시 요청한다
이 URL이 origin 서버 도메인이면:
- origin 서버가 직접 응답한다
5. 브라우저는 응답을 받고 바로 쓰는 게 아니다
여기서 중요한 포인트가 나온다.
브라우저는 응답을 받은 뒤 헤더와 정책을 확인한다.
대표적으로 이런 것들이다.
- Cache-Control
- ETag
- Last-Modified
- Access-Control-Allow-Origin
즉, "응답을 받았다"와 "그 파일을 사용할 수 있다"는 같은 말이 아니다.
CORS는 정확히 언제 보이냐
내가 정리하면서 가장 헷갈렸던 부분이 이거였다.
CORS는 요청을 보내기 전에만 결정되는 게 아니라,
실제로는 응답을 받은 뒤 브라우저가 그 응답을 사용 가능한지 검사하는 단계에서 드러난다.
예를 들어:
이런 구조면 브라우저 기준으로는 cross-origin이다.
이때 static.example.com 쪽 응답에 필요한 CORS 헤더가 적절하지 않으면, 브라우저는 리소스를 차단할 수 있다.
즉,
- 서버는 응답했을 수 있고
- CDN도 파일을 잘 내려줬을 수 있지만
- 브라우저가 최종적으로 "사용 불가" 판정을 내릴 수 있다
이게 개발할 때 보이는 CORS 에러의 핵심이다.
캐시가 끼면 왜 더 헷갈리나
CORS를 단순 서버 설정 문제로만 보면 놓치기 쉬운 게 캐시다.
예를 들어 이런 상황이 가능하다.
상황 1. 예전에 받은 캐시에는 헤더 상태가 달랐다
과거에 잘못된 응답이 캐시되어 있었고,
지금 서버 설정은 고쳐졌더라도 브라우저나 CDN이 예전 응답을 계속 쓰고 있을 수 있다.
그러면 개발자는 이렇게 느낀다.
- "서버는 고쳤는데 왜 아직도 안 되지?"
- "강력 새로고침하니까 되네?"
- "어떤 환경에서는 되고 어떤 환경에서는 안 되네?"
이런 현상은 대체로 캐시 계층이 남아 있는지 같이 봐야 말이 맞아진다.
상황 2. CDN 캐시에 예전 응답이 남아 있다
origin 서버는 이미 올바른 헤더를 주고 있는데,
CDN이 예전 파일 또는 예전 헤더 정책이 반영된 응답을 계속 내려줄 수도 있다.
이러면 브라우저 입장에서는 origin이 아니라
CDN이 준 응답을 기준으로 판단하게 된다.
그래서 "원본 서버는 정상인데 사용자 브라우저에서는 CORS가 계속 난다" 같은 상황이 생길 수 있다.
상황 3. 브라우저 캐시 재사용과 재검증이 섞여 보인다
브라우저는 어떤 파일은 그냥 캐시에서 쓰고,
어떤 파일은 서버에 다시 물어보고,
어떤 파일은 새로 내려받는다.
이게 페이지마다, 헤더마다, 파일마다 달라질 수 있어서
실무에서는 체감상 더 복잡하게 느껴진다.
내가 이번에 정리하면서 제일 중요하다고 느낀 점
이번에 흐름을 다시 보면서 가장 크게 정리된 건 아래 3가지였다.
1. CORS는 "브라우저 정책 문제"로 봐야 한다
서버 간 통신 자체가 안 되는 문제와는 다르다.
중요한 건 브라우저가 그 응답을 사용하게 허용하느냐 이다.
2. 캐시와 CDN을 같이 안 보면 원인 판단이 틀어질 수 있다
분명 서버를 고쳤는데도 계속 안 되는 경우가 있다.
그럴 땐 단순히 "서버 설정이 아직 반영 안 됐나?" 보다 아래를 같이 봐야 한다.
- 브라우저 캐시
- CDN 캐시
- 재검증 응답
- 응답 헤더
3. "응답이 왔다"와 "브라우저가 사용한다"는 별개다
이건 이번에 가장 많이 헷갈렸던 지점이기도 하다.
개발자 도구 Network 탭에서 파일이 내려온 것처럼 보여도,
실제로 브라우저가 그 파일을 정책상 차단하면 결국 문제는 해결되지 않은 상태다.
실무에서 확인하면 좋은 체크 포인트
CORS나 캐시 이슈를 볼 때는 아래 순서로 보면 생각보다 정리가 잘 된다.
1. 지금 문제 나는 리소스가 정확히 무엇인지
- CSS인지
- JS인지
- 이미지인지
- 폰트인지
2. 그 리소스 URL이 어느 도메인인지
- 같은 origin인지
- CDN 도메인인지
- 완전히 다른 외부 도메인인지
3. 브라우저가 캐시를 쓰고 있는지
- 디스크 캐시
- 메모리 캐시
- 재검증 요청
4. 실제 응답 헤더가 어떤지
- Cache-Control
- Access-Control-Allow-Origin
- ETag
- Last-Modified
- Vary
5. origin과 CDN 중 어디 응답을 보고 있는지
문제 원인을 origin으로 단정하지 말고,
중간 캐시 계층이 있는지도 같이 봐야 한다.
마무리
처음에는 그냥 CORS 에러 하나를 이해하고 싶었던 건데,
결국 보게 된 건 브라우저가 페이지를 그리는 전체 흐름이었다.
정리하고 나니까 CORS는 따로 떨어진 개념이 아니라,
- 브라우저가 HTML을 읽고
- 필요한 파일을 찾고
- 캐시를 확인하고
- 네트워크 요청을 보내고
- CDN/origin 응답을 받고
- 마지막에 정책을 검사하는
이 전체 과정 안에 들어 있는 개념이라는 게 훨씬 명확해졌다.
나도 아직 네트워크를 깊게 아는 수준은 아니지만,
적어도 이제는 CORS 에러를 보면 단순히
서버 헤더 문제인가? 에서 끝나지 않고
- 캐시인가?
- CDN 응답인가?
- 브라우저가 정책상 막는 건가?
이 순서로 생각해볼 수 있게 됐다.
이번 글은 그 기준점을 만들기 위한 정리였다.