웹에서 CORS가 뭐길래? 개발자들이 자주 마주치는 오류와 해결법
1. CORS란 정확히 무엇인가요?
CORS, 즉 ‘Cross-Origin Resource Sharing(교차 출처 리소스 공유)’는 웹 보안 메커니즘 중 하나로, 브라우저가 서로 다른 출처(origin) 간의 요청을 제한하거나 허용하는 방식입니다. 여기서 출처란, 프로토콜(http 또는 https), 도메인(example.com), 포트 번호(:3000)까지 모두 포함한 개념입니다. 예를 들어, https://mydomain.com에서 http://anotherdomain.com으로 데이터를 요청한다면, 이는 ‘교차 출처 요청’이 되며, 브라우저는 기본적으로 보안을 위해 이를 차단합니다.
이게 왜 생겼냐고요? 웹에서 악의적인 스크립트나 외부 도메인을 통해 사용자의 민감한 정보를 탈취하려는 시도가 많기 때문입니다. 브라우저는 사용자 보호를 위해 동일 출처 정책(Same-Origin Policy)을 기본으로 하고, CORS는 그 제한을 ‘명시적으로 허용’하는 수단입니다. 즉, 서버가 “이 요청, 괜찮아. 받아줄게.”라고 응답해야만 데이터 접근이 가능해지는 구조입니다.
2. 왜 브라우저는 CORS를 이렇게 까다롭게 적용할까요?
한마디로 요약하자면, 보안 때문입니다. 브라우저는 사용자의 개인정보와 세션을 보호하기 위해, 출처가 다른 웹사이트에서 함부로 데이터를 요청하지 못하도록 ‘장벽’을 만들어 둡니다. 이 장벽이 바로 ‘동일 출처 정책(Same-Origin Policy)’이고, CORS는 그 장벽에 예외를 설정하는 규칙이라고 보시면 됩니다.
예를 들어, 어떤 사이트가 로그인 쿠키를 저장하고 있는데, 다른 사이트에서 몰래 그 쿠키를 이용해 정보를 빼내려 한다면? 큰일이겠죠. 이걸 막기 위해 브라우저는 출처가 다르면 요청을 기본적으로 차단합니다. 그래서 개발자 입장에서는 약간 불편하더라도, 전체적인 웹 생태계의 안전을 위한 최소한의 규칙인 셈입니다.
3. CORS 요청에는 어떤 종류가 있나요?
CORS 요청은 크게 Simple Request, Preflight Request, 그리고 Credentialed Request로 나눌 수 있습니다.
Simple Request는 비교적 제한이 적은 요청으로, GET, POST, HEAD 메서드만 사용되고, 특정한 헤더만 포함되어야 합니다.
Preflight Request는 본 요청 전에 OPTIONS 메서드를 사용해, 이 요청을 서버가 허용하는지를 먼저 물어보는 과정입니다. 이건 마치 문을 두드리고 “실례합니다, 들어가도 될까요?” 하고 물어보는 것과 같죠.
Credentialed Request는 쿠키나 인증 헤더를 포함하는 요청으로, 서버가 Access-Control-Allow-Credentials: true를 명시적으로 설정해줘야만 허용됩니다.
이렇게 요청의 종류에 따라 브라우저와 서버 간의 대화 방식이 달라지고, 이에 맞춰서 서버 설정도 조정해야 합니다.
4. CORS 에러 메시지를 보면 어떻게 해석해야 하나요?
개발자 콘솔에서 가장 자주 보게 되는 메시지 중 하나가 바로 Access to fetch at ‘URL’ from origin ‘다른 URL’ has been blocked by CORS policy입니다. 처음 보면 굉장히 당황스럽죠. 하지만 이건 서버가 CORS 설정을 제대로 하지 않았거나, 브라우저가 보안 상 이유로 요청을 막았다는 뜻입니다.
여기서 핵심은 서버 쪽 문제라는 점입니다. 클라이언트(브라우저)는 규칙을 따르고 있고, 허용 여부는 서버가 정합니다. 따라서 이런 에러가 발생했다면 서버의 응답 헤더에 Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers 등을 제대로 설정했는지를 확인해야 합니다.
5. 서버에서 CORS를 허용하려면 어떻게 하나요?
서버에서 CORS를 허용하려면 응답 헤더에 다음과 같은 값을 설정해야 합니다:
http
복사
편집
Access-Control-Allow-Origin: https://허용할도메인.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
위 설정은 “이 도메인에서 오는 요청은 괜찮아요. GET이나 POST 같은 메서드를 쓸 수 있고, 인증 관련 헤더도 받아줄게요”라는 의미입니다. 만약 여러 도메인을 허용하고 싶다면, 서버 코드에서 요청 헤더를 확인한 뒤 조건적으로 응답을 구성해줘야 합니다. 단순히 *로 설정하면 쿠키를 포함한 요청은 절대 허용되지 않습니다, 이 부분도 주의가 필요하죠.
6. 클라이언트에서 할 수 있는 CORS 우회 방법은 없나요?
개발 환경에서는 프록시 서버를 통해 우회하는 방식이 자주 사용됩니다. 예를 들어 webpack-dev-server나 Vite 같은 개발 도구에서는 proxy 설정을 통해 요청을 중간에서 중계하고, 마치 같은 출처에서 요청한 것처럼 가장할 수 있습니다. 혹은 로컬에서 CORS 제한이 없는 브라우저 플러그인을 사용할 수도 있습니다.
하지만 이런 방법은 운영 환경에선 절대 사용하면 안 됩니다. 보안상 큰 허점이 생기고, 서비스 이용자들의 데이터가 위험에 빠질 수 있으니까요. 정식 서버 설정을 통해 CORS를 허용하는 방식이 가장 안정적입니다.
7. 브라우저가 보내는 Preflight 요청을 줄일 수는 없을까요?
Preflight 요청은 브라우저가 ‘이 요청 해도 돼?’ 하고 서버에 묻는 과정인데, 이게 반복되면 성능 저하가 발생할 수 있습니다. 이를 줄이기 위해선 다음과 같은 방법들을 고려해볼 수 있습니다:
가능한 Content-Type을 application/x-www-form-urlencoded, text/plain, multipart/form-data로 제한하기
Authorization 같은 커스텀 헤더 사용 최소화하기
서버에서 Access-Control-Max-Age 헤더를 통해 Preflight 결과를 캐싱하게 설정하기
즉, 브라우저가 매번 확인하지 않고, “전에 확인했잖아! 기억나지?” 하면서 요청을 생략할 수 있게 만들어주는 거죠.
8. CORS 설정이 잘못되었을 때 보안상 어떤 문제가 생기나요?
CORS를 지나치게 느슨하게 설정하면, 예를 들어 Access-Control-Allow-Origin: *와 Access-Control-Allow-Credentials: true를 함께 사용하는 경우, 심각한 보안 취약점이 발생할 수 있습니다. 악성 스크립트가 사용자의 세션을 도용하거나, 민감한 정보를 탈취할 수 있게 되죠.
즉, CORS는 단순한 ‘에러 방지 도구’가 아니라, ‘보안 장벽’입니다. 이걸 허술하게 다루면 웹 전체가 뚫릴 수 있으니 항상 신중하게 구성해야 합니다. 특히 API 서버를 운영 중이라면, CORS 설정은 방화벽 수준으로 엄격히 관리되어야 합니다.
9. 클라우드 서비스에서는 CORS를 어떻게 설정하나요?
AWS S3, Firebase, Google Cloud 같은 클라우드 플랫폼에서도 CORS 설정이 가능합니다. 예를 들어 S3에서는 버킷의 설정에서 아래와 같이 CORS 규칙을 JSON으로 등록합니다:
json
복사
편집
[
{
“AllowedOrigins”: [“*”],
“AllowedMethods”: [“GET”, “POST”],
“AllowedHeaders”: [“*”]
}
]
다만, 위 설정은 개발용일 뿐이고, 실제 운영 환경에서는 구체적인 출처와 헤더만 허용하도록 조정해야 합니다. Firebase Hosting의 경우, firebase.json에서 headers 항목을 통해 설정할 수 있으며, Google Cloud Storage도 비슷한 구조로 구성됩니다.
10. CORS를 테스트하고 디버깅할 때 어떤 도구가 유용한가요?
CORS 이슈는 눈에 잘 안 보이기 때문에, 브라우저의 개발자 도구(Network 탭)를 잘 활용하는 것이 중요합니다. 요청 헤더, 응답 헤더, 상태 코드 등을 꼼꼼히 확인해보세요. 추가로 Postman이나 cURL 같은 도구로 서버 응답을 직접 확인해보는 것도 매우 유용합니다.
또한 Test CORS, cors-anywhere 같은 무료 툴을 이용하면, 서버의 응답을 직접 시뮬레이션하면서 설정이 잘 적용되었는지를 확인할 수 있습니다. 디버깅의 핵심은 ‘요청이 어떻게 가고, 서버가 어떻게 응답하는가’를 정확히 파악하는 것입니다.
결론
CORS는 단순한 개발 에러가 아니라, 웹 보안의 중요한 축 중 하나입니다. 요청이 차단됐다고 그냥 우회하거나 무시하는 게 아니라, 왜 차단되었는지를 이해하고 서버 설정을 통해 올바르게 해결하는 것이 가장 중요합니다. 지금은 귀찮고 복잡하게 느껴질 수도 있지만, 장기적으로는 안전하고 안정적인 웹 서비스 구축에 꼭 필요한 과정입니다. 개발자로서, CORS를 제대로 이해하고 다룰 수 있는 능력은 필수 역량이라고 할 수 있겠습니다.
자주 묻는 질문 (FAQs)
Q1. CORS 에러는 브라우저에서만 발생하나요?
네, 대부분의 경우 브라우저에서 발생합니다. fetch나 XMLHttpRequest를 사용할 때 보안 정책에 의해 차단됩니다. 서버 간 통신에는 해당되지 않습니다.
Q2. Access-Control-Allow-Origin: *는 안전한가요?
개발 환경에서는 편리하지만, 운영 환경에서는 위험합니다. 특히 인증 정보(Cookie 등)를 함께 보낼 때는 절대 사용해서는 안 됩니다.
Q3. CORS 설정을 백엔드가 아닌 프론트엔드에서 조정할 수 있나요?
아쉽게도 아닙니다. CORS는 서버의 응답 헤더를 기반으로 작동하기 때문에, 프론트엔드에서는 설정할 수 없습니다.
Q4. 서버가 여러 출처를 허용해야 할 때는 어떻게 하나요?
서버에서 요청의 Origin 값을 읽고, 허용 리스트와 비교하여 조건부로 Access-Control-Allow-Origin을 설정하는 방식이 효과적입니다.
Q5. 브라우저 플러그인으로 CORS를 우회해도 되나요?
개발 중에는 가능하지만, 운영 중인 서비스에서는 사용자의 보안을 해칠 수 있기 때문에 절대 권장되지 않습니다.