IE10의 XHR을 위한 CORS

IE10의 4세대 플랫폼XHR(XMLHttpRequest)에 대해 CORS(Cross-Origin Resource Sharing)를 지원하므로 전체 브라우저에서 일관되게 작동하는 Cross-Site 시나리오 개발 작업이 간편합니다. XHR에 CORS를 사용하면 사이트 간의 데이터 공유를 쉽고 유연하게 할 수 있습니다. 대부분의 기본 시나리오에서 CORS는 모든 사이트에서 액세스할 수 있는 데이터 소스 생성을 가능하게 하며, 약간의 수정으로 허용되는 사이트를 제한하거나, 데이터 수정을 지원하고 심지어 인증을 허용하도록 선택할 수 있습니다. 가장 중요한 점은 CORS가 서버 참여를 요청함으로써 기존 사이트를 안전하게 유지한다는 것입니다.

단순한 Cross-Origin XHR

그럼 Cross-Origin XHR 요청이 동일 근원(Origin) 요청과 어떻게 다른지 살펴보겠습니다. 스크립트에서 유일한 차이점은 Open 메서드로 전달된 URL입니다. 예를 들어 사진 앨범 목록을 가져오는 스크립트를 작성한다고 가정해보겠습니다.

기존 XHR

// Script running on http://photos.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "/albums", true);

xhr.send();

이제 또 다른 근원에서 앨범 목록에 액세스하고자 합니다. 다른 근원은 완전히 다른 도메인이거나 동일한 기본 도메인의 다른 호스트일 수 있습니다. 어느 경우이든 다른 사이트에서 전체 URL을 가리키기만 하면 브라우저에서 CORS 요청이 자동으로 전송됩니다.

CORS 지원 XHR

// Script running on http://www.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "http://photos.contoso.com/albums", true);

xhr.send();

사이트는 기능 탐지를 통해 이를 중첩시켜 오래된 브라우저에 대해서는 폴백을 제공합니다. 'withCredentials'을 잘 살펴보십시오. 이것은 XHR에 대한 CORS 지원에 바로 연결하므로 최상의 접근 방식입니다.

CORS 지원 XHR(기능 탐지)

// Script running on http://www.contoso.com

var xhr = new XMLHttpRequest();

if ("withCredentials" in xhr) {

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "http://photos.contoso.com/albums", true);

xhr.send();

} else {

// Fallback behavior for browsers without CORS for XHR

}

여기에서 클라이언트 코드가 'http://photos.contoso.com'에 대해 바로 CORS 요청을 만들지만 요청에서 데이터를 반환하는 데 실패합니다. 실패가 발생하는 이유는 서버가 아직 참여하지 않았기 때문입니다. 개발자 도구를 자세히 살펴봄으로써 무엇이 잘못되었는지 알아보겠습니다.

검색된 'Access-Control-Allow-Origin' 헤더가 없음을 표시하는 F12 도구를 보여주는 스크린샷

여기서 우리는 서버 응답에서 “Access-Control-Allow-Origin” 헤더 전송이 필요하다는 것을 알 수 있습니다. 지금 시나리오에서는 아무 사이트에서나 앨범에 액세스하여 열 수는 없고 'http://www.contoso.com'에서만 액세스할 수 있도록 하는 것입니다. 이렇게 하려면 요청의 근원을 서버가 식별할 수 있어야 합니다. 보내는 요청을 자세히 살펴보면 새 헤더에 'Origin' 정보가 정확히 포함되어 있음을 알 수 있습니다.

단순한 CORS 요청 헤더

GET http://photos.contoso.com/albums HTTP/1.1

Origin: http://www.contoso.com

...

이 정보를 사용하면 서버가 특정 사이트들에 대해 액세스를 제한할 수 있습니다. 서버가 항상 값을 '*'로 해서 'Access-Control-Allow-Origin'헤더를 추가하면 모든 사이트에서 액세스할 수 있게 됩니다. 이 시나리오에서는 서버가 근원을 확인한 후 'http://www.contoso.com'만 허용하도록 'Access-Control-Allow-Origin'을 설정하려고 합니다.

단순한 CORS 응답 헤더

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.contoso.com

...

위와 같은 업데이트를 삽입하면 'http://www.contoso.com' 클라이언트가 'http://photos.contoso.com'의 서버에서 앨범 목록에 액세스할 수 있게 됩니다.

Preflight(프리플라이트) Cross-Origin XHR

지금까지 살펴본 '단순한' CORS 요청은 사진 앨범의 다운로드와 같이 읽기 전용의 기본 시나리오에 유용합니다. 사이트에서 데이터를 수정하여 다음 단계로 나아가려면 서버에서 추가 작업을 해야 합니다. 예를 들어 새 앨범을 만들기 위해 클라이언트에서 코드를 추가한다고 가정해 봅니다.

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("PUT", "http://photos.contoso.com/albums", true);

xhr.send(JSON.stringify({ name: "New Album" }));

이것을 그대로 실행하면 제대로 동작하지 않습니다. 네트워크 트래픽을 자세히 살펴보면 잘못된 요청이 전송된 것을 알 수 있습니다.

OPTIONS Preflight(프리플라이트) 요청을 보여주는 F12 도구 스크린샷

실제로 브라우저가 전송한 것은 Preflight(프리플라이트) 요청입니다. Preflight(프리플라이트) 요청은 서버에서 데이터 수정을 발생시킬 수 있는 요청 이전에 전송됩니다. 이러한 요청은 CORS 사양에 정의된 것처럼 단순하지 않은 속성의 유무에 의해 식별됩니다. 이러한 속성은 'PUT'과 같은 특정 HTTP 메서드는 물론이고 사용자 지정 HTTP 헤더에 이르기까지 다양합니다. 브라우저는 서버로부터 실제 요청 전송을 위한 권한을 얻기 위해 Preflight(프리플라이트) 요청을 전송합니다. 여기서의 예를 보면 브라우저가 'PUT' 요청이 허용되었음을 확인합니다.

Preflight(프리플라이트) 요청

OPTIONS http://photos.contoso.com/albums HTTP/1.1

Origin: http://www.contoso.com

Access-Control-Request-Method: PUT

...

브라우저에서 실제 요청을 전송하려면 서버에서 몇 가지를 변경해야 합니다. 자세한 내용은 개발자 도구를 통해서도 확인할 수 있습니다.

검색된 'Access-Control-Allow-Methods' 목록이 없음을 표시하는 F12 도구를 보여주는 스크린샷

첫 번째 단계는 동일한 URL에 대해 'OPTIONS' Preflight(프리플라이트) 요청이 다른 요청과 별개의 것임을 서버가 인식하도록 하는 것입니다. 허용된 근원에서 'Access-Control-Request-Method'가 'PUT'을 요청하고 있음을 확인함으로써 서버가 Preflight(프리플라이트) 요청을 확인하고 나면 'Access-Control-Allow-Methods' 헤더를 통해 적절한 승인을 전송합니다.

Preflight(프리플라이트) 응답

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.contoso.com

Access-Control-Allow-Methods: PUT

...

Preflight(프리플라이트) 요청이 처리 및 승인되면 실제 요청이 일어납니다.

실제 요청

PUT http://photos.contoso.com/albums HTTP/1.1

Origin: http://www.contoso.com

...

이 시점에서 앨범 추가가 기술적으로는 완성되지만 서버가 제대로 응답하지 않는 이상 클라이언트 코드에서 이를 알 수 없습니다. 구체적으로 말하자면, 서버는 응답에 'Access-Control-Allow-Origin'를 계속 포함하고 있어야 합니다.

실제 응답

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.contoso.com

...

이로써 클라이언트는 Cross-Origin으로 새 앨범을 추가할 수 있으며 작업이 성공적으로 완료되었는지 확인할 수 있습니다.

다음 단계

CORS를 다른 새 플랫폼과 페어링할 수 있는 기능을 활용하면 흥미로운 시나리오가 가능합니다. 한 가지 예는 ORS, XHR, FileAPI 및 진행률 이벤트를 사용하여 Cross-Origin 파일 업로드를 추적할 수 있는 Cross-Site 업로드 테스트 드라이브입니다.

- Internet Explorer 프로그램 관리자 Tony Ross