웹 개발자라면 꼭 알아야 할 CORS! 많은 개발자들이 실제 프로젝트에서 자주 겪는 CORS 에러의 원인과 그 해결 방법에 대해 설명해 드립니다. 이 글에서는 초보 개발자도 쉽게 이해할 수 있도록 실용적인 가이드를 제공하고, CORS 정책이 어떻게 작동하는지, 왜 발생하는지에 대한 기본 개념부터 시작해, 다양한 상황에서 효과적으로 대응하는 전략까지 자세히 설명합니다.
CORS가 필요한 이유에 대해 설명하며, 웹 보안의 기본 개념부터 실제 중요성까지 차근차근 살펴보겠습니다.
CORS가 필요한 이유
브라우저 보안의 기본 원칙
인터넷의 세계는 생각보다 위험합니다. 여러분이 은행 웹사이트에 로그인한 상태에서, 악의적인 웹사이트에 접속했다고 상상해 보세요. 만약 브라우저에 보안 정책이 없다면, 그 악성 웹사이트는 여러분의 은행 정보에 마음대로 접근할 수 있게 됩니다. 이것이 바로 브라우저가 기본적인 보안 정책을 가져야 하는 이유입니다.
브라우저의 가장 기본적인 보안 정책은 "다른 출처의 리소스를 함부로 가져올 수 없다"입니다. 마치 어떤 가게에서 다른 가게의 물건을 마음대로 가져다 팔 수 없는 것과 같은 원리죠.
동일 출처 정책(Same-Origin Policy)이란?
동일 출처 정책은 웹 브라우저에서 가장 중요한 보안 개념입니다. 이 정책에 따르면, 한 출처(Origin)에서 로드된 문서나 스크립트가 다른 출처의 리소스와 상호작용하는 것을 제한합니다.
출처(Origin)는 다음 세 가지 요소로 구성됩니다
- 프로토콜 (http, https)
- 호스트 (도메인)
- 포트 번호
예를 들어
https://www.example.com:443/path
여기서 실제 출처는 https://www.example.com:443 까지입니다. 이후에 오는 /, /path, /other/path는 모두 동일 출처가 됩니다.
CORS가 없다면 발생할 수 있는 보안 문제
CORS 없이 자유로운 데이터 접근이 가능하다면 다음과 같은 위험한 상황이 발생할 수 있습니다
- 크로스 사이트 스크립팅(XSS) : 악의적인 스크립트가 다른 사이트의 데이터를 마음대로 읽어갈 수 있습니다.
- 세션 하이재킹 : 사용자의 로그인 세션을 탈취하여 개인정보를 빼돌릴 수 있습니다.
- 민감한 데이터 유출 : 사용자 모르게 개인정보나 금융정보가 유출될 수 있습니다.
- 피싱 공격 용이 : 정상적인 사이트처럼 위장하여 사용자 정보를 수집할 수 있습니다.
현대 웹 개발에서 CORS의 중요성
현대 웹 개발에서 CORS는 더욱 중요해졌습니다. 그 이유는
- 마이크로서비스 아키텍처 프론트엔드와 백엔드가 분리되어 있고, 여러 서비스가 서로 다른 도메인에서 호스팅 되는 것이 일반적입니다.
- 서드파티 API 활용 많은 웹 서비스들이 Google Maps, 소셜 미디어 API 등 외부 서비스를 활용합니다.
- 클라우드 서비스 AWS, Azure, GCP 등 다양한 클라우드 서비스들과의 안전한 통신이 필요합니다.
- 보안 요구사항 강화 GDPR과 같은 데이터 보호 규정으로 인해 보안의 중요성이 더욱 커졌습니다.
이처럼 CORS는 단순한 '귀찮은 에러'가 아닌, 현대 웹의 보안을 위한 필수적인 정책입니다. 개발자로서 CORS를 이해하고 적절히 다루는 것은 안전한 웹 서비스 구축을 위한 기본 소양이라고 할 수 있습니다.
CORS 동작 방식 이해하기
기본적인 HTTP 요청과 응답 구조
CORS는 기본적으로 HTTP 요청과 응답 헤더를 통해 동작합니다. 웹 애플리케이션에서 다른 출처로 요청을 보낼 때, 브라우저는 요청 헤더에 'Origin'을 포함시킵니다
GET /api/data HTTP/1.1
Host: api.example.com
Origin: https://www.myapp.com
서버는 이에 대한 응답으로 Access-Control-Allow-Origin 헤더를 통해 이 요청을 허용할지 결정합니다. 이처럼 서버가 "Access-Control-Allow-Origin" 헤더로 "이 출처의 요청은 허용합니다"라고 알려주는 것이 CORS의 기본 동작입니다.
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.myapp.com
브라우저는 특별한 경우에 "이 요청이 안전한가요?"라고 서버에 미리 물어보는데 이것을 "사전 확인 요청" 또는 "Preflight 요청"이라고 합니다.
Preflight 요청은 언제 발생하는가?
Preflight 요청은 브라우저가 본 요청을 보내기 전에 "미리" 서버에게 해당 요청이 안전한지 물어보는 것입니다. 다음과 같은 경우에 발생합니다
- 특별한 헤더를 사용할 때
- Authorization
- Content-Type (application/json 등)
- Custom 헤더
- 일반적이지 않은 HTTP 메소드 사용
- PUT
- DELETE
- PATCH
- 파일 업로드나 큰 데이터 전송
Simple Request vs Preflight Request
요청의 두 가지 종류
브라우저는 CORS 요청을 두 가지로 구분합니다. 각각의 특징을 자세히 비교해 보겠습니다
구분 | Simple Request | Preflight Request |
사용 메서드 | GET, HEAD, POST | OPTIONS |
Content-Type | application/x-www-form-urlencoded, text/plain, multipart/form-data | 기타(application/json 등) |
커스텀 헤더 | 사용 불가 | 사용 가능 |
실행 흐름 | 바로 요청 | Preflight 후 실제 요청 |
Simple Request 조건
- GET, HEAD, POST 중 하나
- 허용된 헤더만 사용
- Content-Type이 다음 중 하나
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
Preflight Request 특징
- OPTIONS 메서드 사용
- 실제 요청 전에 서버의 허가를 먼저 확인
- 추가적인 네트워크 요청이 발생
- 캐시를 통한 최적화 가능
실제 CORS 요청/응답 흐름 살펴보기
1. Preflight 요청 단계
OPTIONS /api/data HTTP/1.1
Origin: https://www.myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
2. Preflight 응답 단계
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.myapp.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400
3. 실제 요청 단계
Preflight 요청이 성공하면, 브라우저는 실제 요청을 보냅니다. 이때는 원래 보내고자 했던 데이터와 함께 요청이 전송됩니다.
이러한 CORS의 동작 방식을 이해하는 것은 다음과 같은 이점이 있습니다
- 디버깅 용이
- 어떤 단계에서 문제가 발생했는지 파악 가능
- 네트워크 탭에서 요청 흐름 추적 가능
- 최적화 기회
- Preflight 캐싱을 통한 성능 개선
- 불필요한 Preflight 요청 최소화
- 보안 강화
- 의도하지 않은 크로스 도메인 요청 방지
- 데이터 접근 제어 강화
자주 발생하는 CORS 에러 패턴
개발자라면 누구나 한 번쯤 마주치게 되는 CORS 에러! 가장 흔한 에러 패턴들과 그 원인을 살펴보겠습니다.
Access-Control-Allow-Origin 오류
브라우저 콘솔에서 가장 자주 보게 되는 에러입니다
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy
주요 발생 원인
- 서버에서 허용하는 출처 목록에 현재 도메인이 없음
- HTTP와 HTTPS 프로토콜 불일치
- 서브도메인까지 정확하게 일치하지 않음
- 포트 번호가 다른 경우
Credentials 관련 이슈
로그인 같은 인증이 필요한 요청에서 자주 발생합니다
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'
문제가 되는 상황
- credentials 옵션 설정 누락
- 서버의 Access-Control-Allow-Credentials 헤더 미설정
- Origin을 와일드카드(*)로 설정한 경우
- 쿠키나 인증 토큰이 제대로 전송되지 않음
Headers 미설정 문제
커스텀 헤더를 사용할 때 자주 발생하는 에러입니다
Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response
일반적인 원인
- 서버에서 허용하는 헤더 목록 미설정
- Authorization 헤더 사용 시 CORS 설정 누락
- Content-Type 헤더가 허용 목록에 없음
- 커스텀 헤더를 사용하는데 서버에서 허용하지 않음
Methods 허용 관련 오류
특정 HTTP 메소드 사용 시 발생하는 문제입니다
Method PUT is not allowed by Access-Control-Allow-Methods in preflight response
대표적인 상황
- GET, POST 외의 메소드 사용 시 설정 누락
- 서버에서 특정 메소드를 허용하지 않음
- OPTIONS 요청에 대한 응답 설정 미비
- 메소드 이름의 대소문자 불일치
Cookie 전송 실패 케이스
쿠키와 관련된 CORS 에러는 특히 복잡합니다
Cross-site cookie sending has been blocked by CORS policy
주의해야 할 점
- SameSite 쿠키 설정 확인 필요
- credentials: 'include' 설정 여부
- 서버의 쿠키 설정 확인
- 보안 정책과의 충돌
이러한 에러들은 대부분 서버나 클라이언트의 설정 문제에서 발생합니다.
CORS 문제 해결하기: 백엔드
백엔드에서 CORS 설정은 프레임워크나 서버 환경에 따라 방법이 다양합니다. 실제 코드보다는 각 환경에서 고려해야 할 중요 포인트들을 알아보겠습니다.
Spring Boot에서 CORS 설정 시 고려사항
Spring Boot에서 CORS를 처리할 때 신경 써야 할 부분들
- 전역 설정으로 처리할지, 특정 컨트롤러만 처리할지 결정
- 개발 환경과 운영 환경의 Origin 분리 관리
- 인증이 필요한 API의 경우 credentials 옵션 활성화
- 필요한 HTTP 메서드만 선별적으로 허용
- 커스텀 헤더 사용 시 해당 헤더 명시적 허용 필요
Node.js/Express에서의 주의점
Express 환경에서 CORS 설정 시 확인할 사항들
- cors 미들웨어의 적용 범위 (전역 vs 특정 라우트)
- Origin 검증 로직 구현 방식
- 프록시 서버 사용 시 실제 Origin 확인 방법
- 에러 처리와 로깅 전략
- 보안을 위한 허용 목록 관리 방식
nginx 설정 시 핵심 포인트
nginx로 CORS 처리할 때 중요한 점들
- 프록시 설정과 CORS 헤더의 올바른 조합
- OPTIONS 요청에 대한 적절한 응답 처리
- 캐시 설정을 통한 성능 최적화
- SSL/TLS 사용 시 추가 고려사항
- 로드밸런싱 환경에서의 설정 방법
클라우드 서비스에서 주의할 점
클라우드 환경에서 CORS 설정 시 고려사항
- 여러 서비스 간의 통합된 CORS 정책 관리
- 보안 그룹과 CORS 설정의 조화
- 다중 리전 서비스의 CORS 처리 방식
- 모니터링과 로깅 전략
- 장애 발생 시 대응 방안
실무에서 자주 놓치는 부분
CORS 설정 시 흔히 발생하는 실수들
- Origin 검증 로직이 너무 느슨하거나 과도하게 엄격한 경우
- 개발/운영 환경의 설정이 뒤섞이는 경우
- 보안 업데이트 후 CORS 설정이 초기화되는 상황
- 임시방편으로 모든 Origin을 허용하는 위험한 설정
- 에러 상황에 대한 명확한 로깅 부재
'웹 개발 기초 - 프론트 > 네트워크ㆍ통신' 카테고리의 다른 글
웹소켓으로 실시간 통신 구현하기 (6) | 2025.01.17 |
---|---|
캐시(Cache) 전략: 브라우저부터 CDN까지 (0) | 2025.01.16 |
웹 보안 기초: XSS, CSRF, SQL Injection 방어하기 (0) | 2025.01.13 |
OAuth 2.0 인증 완벽 이해하기: 인증 플로우부터 보안까지 (0) | 2025.01.12 |
JWT 인증/인가 완벽 이해하기 (0) | 2025.01.11 |
댓글