웹 기초 ·

브라우저의 출처 제한과 CORS 오류를 구분하는 법

먼저 답부터

브라우저는 보안을 위해 웹페이지 스크립트가 다른 출처의 응답을 마음대로 읽지 못하게 제한합니다. CORS는 서버가 허용할 출처와 메서드 등을 응답 헤더로 알리는 방식이며, 브라우저 콘솔 오류를 없애려고 검사를 끄기보다 요청 주소와 서버 허용 설정을 맞춰야 합니다.

브라우저의 출처 제한과 CORS 오류를 구분하는 법 대표 이미지

웹 화면에서 API를 부르다 보면 서버 주소는 맞고 직접 열면 데이터도 보이는데 브라우저 콘솔에는 CORS 오류가 나타날 수 있습니다. 이때 서버가 완전히 꺼졌다고 생각하거나 브라우저 보안 기능을 끄면 원인을 더 찾기 어려워집니다.

아파트 경비실을 떠올려 보세요. 같은 동의 주민이 자기 집 우편함을 보는 것과 다른 건물 사람이 안쪽 자료를 가져가는 일은 다르게 확인합니다. 브라우저도 페이지가 어디에서 열렸는지 보고 다른 출처의 응답을 스크립트가 읽어도 되는지 제한합니다.

출처는 스킴과 호스트와 포트의 묶음이다

origin은 출처라는 뜻으로 URL의 scheme, host, port 세 부분을 묶어 구분합니다. 스킴은 https처럼 통신 방식을 나타내고, 호스트는 app.example.com 같은 서버 이름이며, 포트는 서비스가 연결을 받는 번호입니다.

https://app.example.com:443
https://api.example.com:443

두 주소는 스킴과 포트가 같아도 호스트가 다르므로 서로 다른 출처입니다. http://localhost:5173과 http://localhost:3000도 포트가 달라 다른 출처입니다. 개발 서버와 API 서버를 따로 띄웠을 때 CORS 문제를 자주 만나는 이유입니다.

같은 출처 정책은 브라우저의 안전 경계다

same-origin policy는 같은 출처 정책이라는 뜻으로, 한 출처에서 열린 문서나 스크립트가 다른 출처의 자료와 상호작용하는 범위를 제한하는 브라우저 보안 원칙입니다. 로그인된 은행 사이트의 응답을 아무 웹페이지가 읽을 수 있다면 개인정보가 쉽게 노출될 수 있습니다.

이 정책은 모든 네트워크 통신을 단순히 막는 장벽은 아닙니다. 요청이 서버에 도착해 처리됐지만 브라우저가 응답을 페이지 코드에 보여 주지 않는 상황도 있습니다. 그래서 서버 로그와 Network 탭의 상태, 콘솔 메시지를 함께 봐야 합니다.

CORS는 서버가 허용 범위를 알려 주는 방식이다

CORS는 Cross-Origin Resource Sharing의 줄임말로 출처가 다른 자원 공유 규칙입니다. 서버가 응답 헤더에 어떤 출처를 허용하는지 알리면 브라우저가 그 응답을 웹페이지 스크립트에 제공할지 판단합니다.

Access-Control-Allow-Origin: https://app.example.com

이 헤더는 API 서버가 보내야 합니다. 프론트엔드 요청에 같은 이름의 헤더를 억지로 붙인다고 허용되는 것이 아닙니다. 운영 서버는 실제 사용하는 화면 출처를 정확히 적고, 모든 출처를 뜻하는 별표 사용은 자격 증명과 데이터 민감도를 고려해야 합니다.

단순하지 않은 요청은 사전 요청을 보낼 수 있다

브라우저는 일부 메서드나 헤더를 쓰는 교차 출처 요청 전에 OPTIONS 메서드로 preflight, 즉 사전 요청을 보낼 수 있습니다. 실제 변경 요청을 보내기 전에 서버가 해당 출처, 메서드, 헤더를 허용하는지 묻는 과정입니다.

Network 탭에서 OPTIONS가 실패했다면 실제 POST나 DELETE 코드만 고쳐서는 해결되지 않을 수 있습니다. 서버가 OPTIONS에 응답하는지, 허용 메서드와 요청 헤더가 맞는지, 중간 프록시가 응답 헤더를 제거하지 않는지 확인합니다.

  • 페이지가 열린 정확한 출처를 적습니다.
  • API 요청의 정확한 출처를 적습니다.
  • OPTIONS와 실제 요청의 상태를 나눠 봅니다.
  • 서버 응답의 Access-Control-Allow-Origin을 확인합니다.
  • 쿠키를 보낸다면 자격 증명 허용 규칙도 함께 확인합니다.

CORS 오류와 인증 오류와 서버 오류를 나눈다

401은 인증 정보가 없거나 맞지 않을 때 자주 쓰이고, 403은 신원을 알아도 권한이 없을 때 자주 쓰입니다. 500 계열은 서버 처리 실패를 뜻합니다. CORS 메시지는 브라우저가 응답 접근을 막았다는 단서이며 서버의 실제 상태 코드를 가려 보이게 할 수도 있습니다.

curl 같은 서버 간 요청 도구에서는 같은 출처 정책이 적용되지 않으므로 성공하고 브라우저에서만 실패할 수 있습니다. 이 차이는 CORS 가능성을 좁히는 단서지만 curl 성공만으로 사용자 화면이 정상이라고 말할 수는 없습니다. 최종 확인은 실제 브라우저 출처에서 합니다.

브라우저 보안을 끄는 해결법을 피한다

검색하다 보면 보안 검사를 끈 브라우저를 실행하거나 모든 요청을 우회 확장 프로그램에 맡기는 방법이 나옵니다. 이는 내 컴퓨터의 다른 로그인 정보까지 위험하게 만들고 실제 사용자 환경에서는 재현되지 않습니다. 개발 중 잠깐 보이는 오류를 없애는 것과 서비스 설정을 고치는 것은 다릅니다.

서버를 관리할 수 있다면 허용 출처 목록을 환경별로 관리합니다. 외부 API라면 그 서비스가 브라우저 직접 호출을 지원하는지 공식 문서를 확인합니다. 비밀 API 키가 필요한 서비스는 브라우저에 키를 넣지 말고 내 서버가 대신 호출하는 구조를 사용합니다.

허용 범위는 필요한 만큼만 연다

모든 출처와 모든 메서드와 모든 헤더를 허용하면 개발은 빨라 보이지만 의도하지 않은 웹페이지에서도 API를 호출할 수 있습니다. CORS만으로 인증과 인가가 완성되는 것은 아니므로 서버는 사용자와 자료 권한을 별도로 확인해야 합니다.

개발용 localhost 주소를 운영 허용 목록에 계속 남길 필요가 있는지 점검합니다. 미리보기 배포 주소가 매번 달라진다면 무작정 와일드카드를 쓰기보다 승인된 패턴과 인증 구조를 설계합니다. 설정 변경 뒤에는 허용된 출처의 성공과 허용하지 않은 출처의 거부를 모두 시험합니다.

오늘 바로 해볼 작은 실습

코드 변경 없이 브라우저 개발자 도구로 출처만 비교합니다.

  1. 자주 쓰는 웹페이지 주소에서 스킴과 호스트와 포트를 적습니다.
  2. 개발자 도구 Network 탭을 열고 fetch나 XHR 요청 하나를 고릅니다.
  3. 요청 URL의 세 부분을 적어 페이지 출처와 비교합니다.
  4. OPTIONS 요청이 있는지 보고 상태 코드를 기록합니다.
  5. 응답 헤더에서 Access-Control-Allow-Origin을 찾습니다.
  6. 발견한 사실만으로 “서버 중단”, “권한 오류”, “CORS 설정” 세 가능성을 구분해 적습니다.

낯선 콘솔 코드를 붙여 넣거나 보안 옵션을 끈 브라우저로 로그인하지 마세요. CORS를 고친다며 운영 API의 허용 출처를 별표로 바꾸거나 비밀키를 프론트엔드에 넣지 않습니다. 서버 설정 권한과 영향 범위를 모르면 읽기 결과만 기록하고 담당자에게 정확한 페이지 출처와 요청 주소를 전달합니다.

확인한 자료

함께 읽기

  • 주소를 입력하면 웹페이지가 열리는 과정을 주문 배달처럼 이해하기

    웹 주소를 입력하면 브라우저는 DNS로 서버 주소를 찾고 HTTP 요청을 보냅니다. 서버가 돌려준 HTML, CSS, JavaScript와 이미지를 조합해 화면을 그리며, 404나 500 오류가 보이면 이 요청과 응답 중 실패한 구간을 확인하면 됩니다.

  • 프론트엔드와 백엔드와 데이터베이스를 식당 역할로 구분하기

    프론트엔드는 사용자가 보고 누르는 화면, 백엔드는 요청과 규칙을 처리하는 보이지 않는 부분, 데이터베이스는 회원과 주문 같은 기록을 보관하는 곳입니다. 식당으로 비유하면 각각 홀, 주방, 장부에 해당하며 세 역할이 연결돼 하나의 기능이 완성됩니다.

  • API와 JSON을 메뉴판과 주문서처럼 읽는 첫 연습

    API는 다른 서비스에 정해진 방식으로 기능이나 데이터를 요청하는 연결 창구이고, JSON은 그 요청과 답을 이름과 값의 묶음으로 적는 형식입니다. 메뉴판에서 가능한 주문을 확인하고 주문서에 항목을 적는 과정으로 생각하면 두 개념을 함께 이해하기 쉽습니다.