|
이들 각각은 요청이라는 배관의 일부로 간주된다. 결국, 작은 상세가 이러한 주제들에 대해 기록된다. 하지만 Ajax 프로그래밍을 염두하고 있다면 준비 상태, 상태 코드, 요청에 익숙해 져야 한다. 애플리케이션에서 무엇인가 잘못되고 있다면 준비 상태, HEAD 요청을 하는 방법, 또는 400 상태 코드가 의미하는 것이 무엇인지를 이해하면 5분의 디버깅으로 끝낼 수 있거나 5시간 동안 좌절과 혼돈 속에서 방황할 수 있다.
|
HTTP 준비 상태 먼저 보도록 하자.
지난 글에서 XMLHttpRequest
객체는 readyState
라는 속성을 갖고 있다고 설명했다. 이 속성은 서버가 요청을 완료하고 콜백 함수가 그 서버에서 온 데이터를 사용하여 웹 폼이나 페이지를 업데이트 하도록 한다. Listing 1은 이것에 대한 예제이다.(참고자료)
Listing 1. 콜백 함수에서 서버의 응답 처리하기
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
} else
alert("status is " + request.status);
}
}
|
이것은 전형적인 준비 상태의 사용법이다. "4"라는 숫자에서 짐작하듯 여러 가지 다른 준비 상태들이 있다.(참고자료)
open()
을 호출하기 전에는) 요청이 초기화 되지 않는다.
send()
를 호출하기 전에는) 요청은 설정은 되지만 보내지지 않는다.
Ajax 프로그래밍의 기초 이상으로 넘어가고 싶다면 이러한 상태 뿐만 아니라 이들이 언제 발생하고 어떻게 사용하는지에 대해 알아야 한다. 우선, 가장 중요한 것은 어떤 요청 상태가 될 것인지를 배워야 한다. 이는 별로 기분 좋은 일이 아니고 몇 가지 특별한 경우가 포함되어 있다.
readyState
0 (readyState == 0
)으로 표시되는 첫 번째 준비 상태는 초기화 되지 않은 요청을 나타낸다. 요청 객체에 대해 open()
을 호출하면 속성은 1로 설정된다. 대부분 요청을 초기화 하면서 open()
을 호출하기 때문에 readyState == 0
을 보는 일은 드물다. 더욱이 초기화 되지 않은 준비 상태는 실제 애플리케이션에서는 쓸모 없다.
Listing 2를 보면 0으로 설정된 준비 상태가 되는 방법을 알 수 있다.
function getSalesData() {
// Create a request object
createRequest();
alert("Ready state is: " + request.readyState);
// Setup (initialize) the request
var url = "/boards/servlet/UpdateBoardSales";
request.open("GET", url, true);
request.[안내]태그제한으로등록되지않습니다-onreadystatechange = updatePage;
request.send(null);
}
|
이 간단한 예제에서 getSalesData()
는 웹 페이지가 요청을 시작하기 위해 호출하는 함수이다. (예를 들어, 버튼이 클릭 될 때.) open()
이 호출되기 전에 준비 상태를 체크 해야 한다. 그림 1은 이 애플리케이션을 실행한 결과이다.
|
분명히 이것은 좋지 않다. open()
이 호출되지 않았다는 것을 확인해야 한다. 실제 Ajax 프로그래밍에서 이러한 준비 상태의 유일한 사용은 다중 함수들에 같은 XMLHttpRequest
객체를 사용하여 다중 요청을 만드는 경우이다. 그러한 상황에서, 여러분은 요청 객체가 새로운 요청을 만들기 전에 초기화 되지 않은 상태(readyState == 0
)에 있다는 것을 확인해야 한다. 이로서 또 다른 함수가 동시에 객체를 사용하는 것을 방지할 수 있다.
0 준비 상태 외에 요청 객체는 전형적인 요청 응답에서 또 다른 준비 상태를 경험하게 된다. 그리고 마지막으로는 준비 상태 4로 끝난다. 이 때는 대부분의 콜백 함수에서 if (request.readyState == 4)
가 된다. 서버가 완료되고 웹 페이지를 업데이트 하거나 서버에서 받은 데이터를 기반으로 액션을 취하는 시기이다.
프로세스를 실제로 보는 것은 간단하다. 준비 상태가 4 라면 콜백에서 단순히 코드를 실행시키는 것 대신 콜백이 호출될 때 마다 준비 상태를 출력한다.(Listing 3)
function updatePage() { // Output the current ready state alert("updatePage() called with ready state of " + request.readyState); } |
이것이 어떻게 실행되는지 확실히 모르겠다면 웹 페이지에서 호출할 함수를 만들고 서버측 컴포넌트로 요청을 보내도록 한다.(Listing 2) 요청을 설정할 때 콜백 함수를 updatePage()
로 설정한다. 요청 객체의 [안내]태그제한으로등록되지않습니다-onreadystatechange
속성을 updatePage()
로 설정한다.
이 코드는 [안내]태그제한으로등록되지않습니다-onreadystatechange
가 정확히 무엇을 의미하는지 잘 보여주고 있다. 요청의 준비 상태가 변할 때 마다 updatePage()
가 호출되고 경고를 받는다. 그림 2는 호출되는 함수의 샘플이다. 이 경우 준비 상태는 1이다.
코드를 직접 실행해 보라. 웹 페이지에 넣고 이벤트 핸들러를 활성화 한다. (버튼을 누르거나, 요청을 실행하기 위해 설정하는 모든 메소드를 사용하라.) 콜백 함수는 여러 번 실행될 것이다. 요청의 준비 상태가 변할 때 마다 각 준비 상태에 대한 경고를 보게 된다. 이는 각 단계를 통해 요청을 따라가는 최상의 방법이다.
이 프로세스에 대해 기본적인 개념이 쌓였다면 여러 가지 다양한 브라우저에서 웹 페이지로 액세스 해보라. 준비 상태가 처리되는 방식에 차이가 있을 것이다. 예를 들어, Firefox 1.5에서, 준비 상태는 다음과 같다.
요청의 각 단계들이 다 나타나기 때문에 놀랍지도 않다. 하지만 Safari를 사용하여 같은 애플리케이션에 액세스 하면 재미있는 것을 발견하게 된다. 다음은 Safari 2.0.1에서 보게 되는 상태이다.
Safari는 첫 번째 준비 상태를 배제하고 그 이유에 대해서는 자세히 나와있지 않다. 바로 이것이 Safari 방식이다. 또한 중요한 포인트이기도 하다. 서버에서 데이터를 사용하기 전에 요청의 준비 상태가 4라는 것을 확인하는 것은 좋은 생각인 반면 일시적인 준비 상태에 의존하는 코드를 작성하는 것은 다른 브라우저 마다 다른 결과를 얻을 수 있는 확실한 방법이다.
예를 들어, Opera 8.5를 사용할 때 상황은 더 악화된다.
Internet Explorer는 다음과 같은 상태로 반응한다.
요청과 관련하여 문제가 있다면 문제의 원인을 찾을 수 있는 첫 번째 장소이다. 요청의 준비 상태를 보여주는 경고를 추가하여 상황이 정상적으로 돌아가는지를 확인할 수 있다. Internet Explorer와 Firefox 모두 테스트 하면 네 개의 모든 준비 상태를 얻을 수 있고 각 요청 단계를 검사할 수 있다.
이제는 응답 쪽을 살펴보도록 하자.
요청 동안에 다양한 준비 상태가 발생할 수 있다는 것을 이해했다면 XMLHttpRequest
객체의 또 다른 중요한 부분에 대해 살펴보도록 하자. 바로 responseText
속성이다. 이것은 서버에서 데이터를 얻을 때 사용되는 속성이다. 서버가 요청 처리를 완료하면 그 요청에 응답하는데 필요한 데이터를 요청의 responseText
에 둔다. 그런 다음 콜백 함수가 그 데이터를 사용한다.(Listing 1과 Listing 4 참조)
function updatePage() { if (request.readyState == 4) { var newTotal = request.responseText; var totalSoldEl = document.getElementById("total-sold"); var netProfitEl = document.getElementById("net-profit"); replaceText(totalSoldEl, newTotal); /* Figure out the new net profit */ var boardCostEl = document.getElementById("board-cost"); var boardCost = getText(boardCostEl); var manCostEl = document.getElementById("man-cost"); var manCost = getText(manCostEl); var profitPerBoard = boardCost - manCost; var netProfit = profitPerBoard * newTotal; /* Update the net profit on the sales form */ netProfit = Math.round(netProfit * 100) / 100; replaceText(netProfitEl, netProfit); } |
Listing 1은 매우 간단하다. Listing 4는 좀 더 복잡하다. 시작하려면 준비 상태를 검사하고 responseText
속성에서 값을 얻어야 한다.
준비 상태와 마찬가지로 responseText
속성의 값은 요청의 수명 주기에 걸쳐 변화한다. Listing 5의 코드를 사용하여 요청의 응답 텍스트를 테스트한다. 준비 상태도 마찬가지로 테스트 한다.
Listing 5. responseText 속성 테스트 하기
function updatePage() { // Output the current ready state alert("updatePage() called with ready state of " + request.readyState + " and a response text of '" + request.responseText + "'"); } |
브라우저에서 웹 애플리케이션을 열고 요청을 활성화 한다. 이 코드를 최대한 활용하려면 Firefox나 Internet Explorer를 사용한다. 이 두 개의 브라우저는 요청 동안 모든 준비 상태들을 보고하기 때문이다. 준비 상태 2에서 responseText
속성은 정의되지 않는다.(그림 3) xxJavaScript 콘솔이 열려있었다면 에러가 생겼을 것이다.
준비 상태 3에서, 서버는 responseText
속성에 값을 배치한다.(그림 4)
준비 상태 3의 응답은 스크립트 마다, 서버 마다, 브라우저 마다 다르다. 애플리케이션을 디버깅 하는데 매우 유용하다.
모든 문서와 스팩들에서는 준비 상태가 4가 되어야지만 데이터를 안전하게 사용할 수 있다고 나와있다. 나를 믿으라. 준비 상태가 3일 때에도 responseText
속성에서 데이터를 얻을 수 있다. 하지만 여러분의 애플리케이션에서 이것에 의존하는 것은 좋지 않은 생각이다. 준비 상태 3에서 완전한 데이터에 의존하는 코드를 작성하는 것은 데이터가 불완전하다는 증거이다.
준비 상태가 3일 때 사용자에게 피드백을 제공하는 것이 좋은 생각이다. alert()
같은 함수를 사용하는 것은 좋지 않다. Ajax를 사용하고 사용자와 경고 다이얼로그 박스를 차단시키는 것은 좋지 않지만 준비 상태가 변할 때 마다 폼이나 페이지에 대한 필드를 업데이트 할 수 있다. 예를 들어, 프로그레스 인디케이터의 넓이를 준비 상태 1에 25 퍼센트, 준비 상태 2에 50 퍼센트, 준비 상태 3에 75 퍼센트, 준비 상태 4에 100 퍼센트를 설정한다.
물론 알다시피, 이 방식은 좋기는 하지만, 브라우저에 의존적이다. Opera에서는 첫 번째 두 개의 준비 상태를 결코 얻지 못하고 Safari는 처음 1 상태를 누락시킨다.
이제 상태 코드에 대해 알아보자.
Ajax 프로그래밍 기술에서 준비 상태와 서버의 응답 외에도, Ajax 애플리케이션에 또 다른 고급 레벨을 추가할 수 있다. 바로 HTTP 상태 코드이다. 이 코드들은 Ajax에서는 새우울 것이 없다. 웹에 있는 한 언제나 존재하는 것들이다. 웹 브라우저를 통해 이들을 보았을 것이다.
이 외에도 더 있다.(참고자료) Ajax 애플리케이션에 또 다른 제어 및 응답 레이어를 추가하려면 요청과 반응에 상태 코드를 검사해야 한다.
많은 Ajax 애플리케이션에서 준비 상태를 점검하고 서버 응답으로 온 데이터로 작업하는 콜백 함수를 볼 수 있다.(Listing 6)
function updatePage() {
if (request.readyState == 4) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
}
}
|
이것은 근시안적이고 에러를 많이 만드는 Ajax 프로그래밍 방식이다. 스크립트가 인증을 필요로 하는데 요청이 유효 증명을 제공하지 않으면 서버는 403 또는 401 같은 에러를 리턴한다. 하지만 서버가 요청에 응답하기 때문에 준비 상태는 4로 설정될 것이다. 결과적으로 사용자는 유효 데이터를 얻지 못하고 xxJavaScript가 존재하지 않는 서버 데이터를 사용하려고 할 때 에러를 얻게 된다.
서버가 요청을 완료하고 "Everything is OK" 상태 코드를 리턴했다는 것을 확인하는 것은 간단한 일이다. 이 코드는 "200"이고 XMLHttpRequest 객체의 status 속성을 통해서 보고된다. 서버가 요청으로 끝나고 OK 상태를 리포트 했다는 것을 확인하려면 추가 체크를 콜백 함수에 추가한다.(Listing 7)
function updatePage() { if (request.readyState == 4) { if (request.status == 200) { var response = request.responseText.split("|"); document.getElementById("order").value = response[0]; document.getElementById("address").innerHTML = response[1].replace(/\n/g, "<br />"); } else alert("status is " + request.status); } } |
코드에 몇 줄을 추가하는 것으로 무엇이 잘못되었는지를 알 수 있고 사용자는 아무런 설명이 없는 데이터 데신 유용한 에러 메시지들을 받을 수 있다.
에러에 대해 이야기 하기 전에 Ajax를 사용할 때 걱정하지 않아도 될 부분에 대해 말해두겠다. 바로 리다이렉션이다. HTTP 상태 코드에서, 이것은 300 대의 상태 코드이다.
Ajax 프로그래머가 리다이렉션에 대해 염려 할 필요가 없는 이유가 두 가지 있다.
결국, 요청은 보안 에러를 만들지 않고서는 또 따른 서버로 리다이렉션 될 수 없다. 그러한 경우에, 상태 코드를 전혀 얻을 수 없다. 디버그 콘솔에 xxJavaScript 에러를 갖게 된다. 따라서 많은 상태 코드에 대해 생각하는 동안 리다이렉션 코드 정도는 무시할 수 있는 것이다.
|
일단, 상태 코드 200을 관리했고 300 계열의 상태 코드는 대충 무시하면 다양한 유형의 에러들을 나타내는 400 계열의 코드만 남게 된다. Listing 7을 보면 에러가 처리되는 동안 사용자에게 출력되는 매우 일반적인 에러 메시지라는 것을 알게 된다. 이것은 올바른 방향으로 가는 단계지만 사용자와 프로그래머에게는 매우 쓸모없는 메시지이다.
우선 소실된 페이지에 대한 지원을 추가한다. 이는 제품 시스템에서는 실제로 발생하지는 않지만 스크립트를 이동시키는 테스트나 정확하지 않은 URL을 입력할 때 자주 일어나는 일이다. 404 에러를 보고하면 혼란스러워 하는 사용자와 프로그래머에게 더 많은 도움말을 제공할 것이다. 예를 들어, 서버 상의 스크립트가 제거되거나 Listing 7에서 그 코드를 사용하면 다음과 같은 에러가 생긴다.(그림 5)
사용자는 문제가 무엇인지 잘 모른다. 인증에 관련된 것인지, 소실된 스크립트 인지, 사용자 에러인지 알 수 없다. 몇 가지 간단한 코드 추가로 이 에러는 더욱 구체화 된다. Listing 8을 보면 소실된 스크립트와 인증 에러까지 구체적인 메시지와 함께 처리된다.
function updatePage() { if (request.readyState == 4) { if (request.status == 200) { var response = request.responseText.split("|"); document.getElementById("order").value = response[0]; document.getElementById("address").innerHTML = response[1].replace(/\n/g, "<br />"); } else if (request.status == 404) { alert ("Requested URL is not found."); } else if (request.status == 403) { alert("Access denied."); } else alert("status is " + request.status); } } |
이는 오히려 더 간단하지만 추가 정보 까지 제공한다. 그림 6은 그림 5와 같은 에러를 보여주지만 이번에는 에러 핸들링 코드가 더 나은 그림을 제공하고 있다.
여러분의 애플리케이션에서 인증 때문에 오류가 발생했을 때 사용자 이름과 패스워드를 지우고 에러 메시지를 스크린에 추가하는 것을 고려할 수도 있다. 이와 비슷한 방식이 소실된 스크립트나 다른 400 유형의 에러들을 핸들하는데 사용될 수 있다. 여러분이 어떤 선택을 하든 서버에서 리턴 된 상태 코드를 핸들하는 것으로 시작한다.
XMLHttpRequest
객체를 제어하고 싶다면 HEAD 요청을 레파토리에 추가하라. 이전 두 개의 기사에서 GET 요청을 하는 방법을 설명했다. 앞으로는 POST 요청을 사용하여 서버로 데이터를 보내는 것을 설명하도록 하겠다. 향상된 에러 핸들링과 정보 수집을 위해 HEAD 요청에 대해 배워야 한다.
HEAD 요청은 실제로 매우 간단하다. 첫 번째 매개변수로서 "GET" 또는 "POST" 대신 "HEAD"로 open()
메소드를 호출한다.(Listing 9)
function getSalesData() {
createRequest();
var url = "/boards/servlet/UpdateBoardSales";
request.open("HEAD", url, true);
request.[안내]태그제한으로등록되지않습니다-onreadystatechange = updatePage;
request.send(null);
}
|
이와 같이 HEAD 요청을 하면 서버는 GET이나 POST 요청 때 처럼 실제 응답을 리턴하지 않는다. 대신, 서버는 응답에 있는 콘텐트가 마지막으로 수정된 시간이 포함된 리소스의 헤더를 리턴한다. 게다가 몇 가지 재미있는 정보도 추가한다. 이들을 사용하여 서버가 리소스를 처리 및 리턴하기 전에 리소스에 대해 알 수 있다.
이와 같은 요청으로 할 수 있는 가장 쉬운 일은 모든 응답 헤더들을 나누는 것이다. 이로서 HEAD 요청을 통해 무엇이 가능한지를 알 수 있다. Listing 10은 HEAD 요청에서 모든 응답 헤더를 출력하는 콜백 함수이다.
Listing 10. HEAD 요청에서 모든 응답 헤더 프린트 하기
function updatePage() { if (request.readyState == 4) { alert(request.getAllResponseHeaders()); } } |
그림 7에서 서버에 HEAD 요청을 한 간단한 Ajax 애플리케이션에서 온 응답 헤더를 볼 수 있다.
이러한 헤더들을 개별적으로 사용하여 Ajax 애플리케이션에서 추가 정보나 기능을 제공할 수 있다.
URL이 존재하지 않을 때 404 에러를 검사하는 방법을 이미 보았다. 이것이 일반적인 문제라면, 특정 스크립트나 서블릿이 잠시 동안 오프라인에 있었다면, GET 또는 POST 요청을 하기 전에 URL을 검사해 보는 것이 좋다. HEAD 요청을 하고 콜백 함수에서 404 에러를 검사한다. Listing 11은 샘플 콜백을 보여준다.
function updatePage() { if (request.readyState == 4) { if (request.status == 200) { alert("URL exists"); } else if (request.status == 404) { alert("URL does not exist."); } else { alert("Status is: " + request.status); } } } |
솔직히 말하면 이것의 가치는 별로 없다. 서버는 요청에 응답해야 하고 응답을 분석하여 응답 헤더에 파퓰레이트 하기 때문에 여러분은 프로세싱 시간을 저축할 수 없다. 게다가 요청을 하고 HEAD 요청을 사용하여 URL이 존재하는지 보는 것에도 많은 시간이 걸린다. Listing 7에서 처럼 에러를 핸들링 하기 보다 GET이나 POST를 사용하여 요청하기 때문이다. 무엇을 사용할 수 있는지를 정확히 아는 데는 가끔 유용하다.
HEAD 요청이 유용한 한 가지 부분은 콘텐트 길이나 콘텐트 유형을 검사할 때이다. 요청을 처리하기 위해 많은 양의 데이터를 보낼 것인지, 서버가 HTML, 텍스트, XML 대신 바이너리 데이터를 리턴해야 할지를 결정할 수 있다. (이 세 가지 모두 바이너리 데이터 보다 xxJavaScript에서 처리하는 것이 더 쉽다.)
이 경우, 적절한 헤더 이름을 사용하고 이를 XMLHttpRequest
객체의 getResponseHeader()
메소드로 보낸다. 따라서 응답의 길이를 알려면 request.getResponseHeader("Content-Length");
를 호출한다. 콘텐트 유형을 알려면 request.getResponseHeader("Content-Type");
를 사용한다.
많은 애플리케이션에서 HEAD 요청을 하면 어떤 기능도 추가하지 않고 요청의 속도를 늦출 수 있다. (HEAD 요청을 실행하여 응답에 대한 데이터를 얻고 후속 GET 또는 POST 요청을 통해 응답을 실제로 받는다.) 하지만 스크립트나 서버측 컴포넌트에 대해 확실하지 않은 경우 HEAD 요청으로 기본적인 데이터를 받을 수 있다.
Ajax와 웹 프로그래머에게 이 글은 다소 어려울 것이다. HEAD 요청을 하는 것의 가치는 무엇인가? xxJavaScript에서 리다이렉션 상태 코드를 핸들해야 하는 때는 언제인가? 이 모두 좋은 질문이다. 간단한 애플리케이션의 경우, 이 모든 것은 가치가 별로 없다.
하지만 웹이 단순한 애플리케이션만 수용하는 것은 아니다. 사용자는 점점 고급화 되고 고객들도 강력한 에러 리포팅을 원한다. 관리자 역시 애플리케이션이 조금만 느려져도 해고를 당하게 된다.
간단한 애플리케이션을 넘어 XMLHttpRequest
에 대한 이해를 높여야 할 때이다.
이들은 모두 Ajax의 강점이지만 극히 일부분이다. Ajax를 사용하여 애플리케이션이 에러와 문제들을 부드럽게 해결할 수 있는 강력한 토대를 구현한다면 사용자는 여러분의 사이트를 방문할 것이다. 다음 글에서는 보다 더 재미있고 흥미 있는 주제들을 나누도록 하겠다.