개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

공부 › 네트워크 › HTTP

CORS

Postman에서는 되는데 브라우저에서만 막히는 이유

브라우저는 웹 페이지의 스크립트가 다른 출처의 서버 응답을 읽는 것을 기본적으로 막습니다. 서버가 응답 헤더로 "이 출처는 허용한다"고 알려 줘야 풀립니다.

실행으로 대조 · 시험 서버 두 개와 실제 브라우저로 24가지 조합 실행

이 실습에 나오는 개념

CORS와 동일 출처 정책
동일 출처 정책은 브라우저가 한 출처(스킴, 호스트, 포트의 조합)의 스크립트가 다른 출처의 응답을 읽지 못하게 막는 보안 정책이다. CORS는 서버가 응답 헤더로 특정 출처의 접근을 허용한다고 알려 이 제한을 풀어 주는 방법이다. 브라우저가 적용하는 정책이므로 서버끼리의 호출이나 도구를 이용한 호출에는 해당하지 않는다.
Preflight 요청
브라우저가 다른 출처로 본 요청을 보내기 전에 OPTIONS 메서드로 먼저 보내는 확인 요청이다. 서버가 해당 출처, 메서드, 헤더를 허용하는지 미리 물어보고, 허용 응답을 받은 경우에만 본 요청을 보낸다. JSON 본문을 보내는 요청이나 커스텀 헤더가 있는 요청 등에서 발생한다.
HTTP 메서드
요청이 자원에 대해 수행하려는 동작을 나타낸다. GET은 조회, POST는 생성이나 처리 요청, PUT은 전체 교체, PATCH는 일부 수정, DELETE는 삭제를 뜻한다. 메서드의 의미에 맞게 사용해야 캐시, 재시도 같은 HTTP의 동작이 기대대로 이뤄진다.
쿠키
서버가 응답에 실어 보내면 브라우저가 저장해 두었다가 같은 사이트에 요청할 때마다 자동으로 함께 보내는 작은 데이터다. HTTP는 요청 사이의 상태를 기억하지 않으므로, 로그인 상태처럼 이어져야 하는 정보를 유지하는 데 쓰인다. 사용자의 브라우저에 저장되므로 내용을 보거나 바꿀 수 있다는 점을 전제로 사용해야 한다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. 페이지의 출처는 https://shop.example, API의 출처는 https://api.shop.example입니다. 호스트가 달라서 다른 출처입니다. 브라우저가 이 요청을 그냥 보내도 되는 종류인지 먼저 판단합니다.
  2. 단순 요청이 아닙니다(Content-Type이 application/json이기 때문). 브라우저가 본 요청을 보내기 전에 OPTIONS 메서드로 "이런 요청을 보내도 되느냐"고 먼저 묻습니다. 스크립트가 시킨 것이 아니라 브라우저가 스스로 보내는 요청입니다.
  3. 서버의 응답이 조건에 맞지 않습니다(응답에 Access-Control-Allow-Origin 헤더가 없음). 브라우저는 본 요청을 아예 보내지 않고, 스크립트에는 네트워크 오류로 알립니다. 서버의 주문 처리 코드는 실행되지 않았습니다.

실행 결과

코드

// https://shop.example 페이지의 스크립트
fetch('https://api.shop.example/orders', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(order)
});

// 서버: CORS 설정 없음

이 모형의 전제

서버가 출처를 허용하는 경우에는 메서드와 헤더도 모두 허용하도록 설정했다고 가정했습니다. 단순 요청의 조건(GET·HEAD·POST이고, 추가 헤더가 없으며, Content-Type이 폼 전송 형식이나 text/plain인 경우)과 사전 요청, 쿠키 포함 시의 규칙은 Fetch 표준과 MDN 문서의 설명을 따랐습니다. 다른 출처로 보내는 24가지 조합(요청 4종 × 쿠키 포함 여부 × 서버 설정 3종)을 로컬 시험 서버 두 개와 실제 브라우저(크로미움 기반)로 실행해, 사전 요청이 나갔는지, 본 요청이 서버에 도착했는지, 스크립트가 응답을 받았는지가 모형과 모두 일치하는 것을 확인했습니다.

앞 실습: 내용 협상과 압축 · 다음 실습: 폴링·SSE·WebSocket

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요