달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
2007. 10. 26. 15:26

AJAX 사이트 그거/기타2007. 10. 26. 15:26

케이(lee256)님께서 추천      http://wwwm.meebo.com/index-ko.html
재미이써(vnfldp1)님께서 추천     http://www.flickr.com
세상살이(simsking)님께서 추천     http://www.dhtmlgoodies.com/index.html
bj1603님께서 추천       http://cgv.co.kr(그런데 여긴....ajax가 아니라 플레쉬로 되어있는거 같은데요..제가 잘못봤나?)
맥(mac00sd)님께서추천      http://www.tattertools.com/ko/
우리나라(bluedew1)님께서추천     http://ajax.asp.net
펜리르(baldr46)님께서추천      http://www.challenger.se/
승햐(seunghaa02)님께서 추천     http://www.panic.com/goods/(저희사이트에서 저도 처음봤었던...사이트네요..^^)
shsdol님께서 추천       http://www.modernmethod.com/sajax
huni1067님께서 추천       http://www.pageflakes.com
수보이(sidney77)님께서 추천     http://earth.google.com/(젤 유명한곳이죠..ㅋㅋ)
hackingstar님께서 추천      http://www.backbase.com/demos/explorer/#examples/fx-move.xml[55]
hpycom님께서 추천       http://www.asp.net(유명한 사이트죠....밑에 사이트랑 링크만 틀림)
devzzaang님께서 추천      http://ajax.asp.net
tigastyle님께서 추천       http://www.ohpy.com
연남남(yjheum)님께서 추천      http://jquery.com/demo/thickbox/
머리통(head1ton) 님께서 추천     http://extjs.com/
아서(dlwnsrjs6421) 님께서 추천     http://www.fxug.co.kr/live/index.html
wannapure 님께서 추천      http://kr.sun.com/developers/ajax/
러뱀씨(luvmc4u) 님께서 추천     http://www.gucci.com/
수연사랑(artes),사삭(ziniboy) 님께서 추천   http://www.wzd.com/
newport1179 님께서 추천      http://cafe.naver.com/ajaxdev.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=29(다른카페네요..ㅋㅋ 이쪽도 좋은글이 많네요...^^)
freehyun 님께서 추천       http://www.likejazz.com/29692.html
써니(pretty9967) 님께서 추천     http://www.challenger.se/
무지개(mindware) 님께서 추천     http://mochikit.com/
오비완(tow5532)  님께서 추천     http://javacan.madvirus.net/
독고릴라(angelos1127) 님께서 추천    http://www.netvibes.com/
장훈(whoney) 님께서 추천      http://meebo.com
학부모씨(ybnakira) 님께서 추천     http://www.cnn.com
애플(apple9575) 님께서 추천     http://dnshop.daum.net/
디지맥(ghochujang) 님께서 추천     http://www.watercafe.co.kr
홍세진(passion_25),뷁쉃(bumpkinr) 님께서 추천  http://www.ajaxian.org
앙마닷(zupper) 님께서 추천      http://www.hotscripts.com/
조조할인(updols) 님께서 추천     http://kr.sun.com/developers/ajax/
잠수부(csi_stone) 님께서 추천     http:www.goby.pe.kr
노력가(sevenwind) 님께서 추천     http://www.dhtmlgoodies.com
제제(monjeje) 님께서 추천      http://www.ajaxrain.com
데이지(minhyades) 님께서 추천     http://blog.naver.com/jinoxst/140021707807
떵려니(head1ton) 님께서 추천     http://Extjs.com

# FROM 네이버 카페(aspajax)

'그거 > 기타' 카테고리의 다른 글

HP 이벤트 진행중~~  (0) 2008.07.02
IDE & ATA & SCSI  (0) 2007.11.05
15 Exercises for Learning a new Programming Language  (0) 2007.07.24
FUD(Fear, Uncertainty, Doubt)  (0) 2007.05.31
당신의 블로그는 얼마짜리 입니까?  (0) 2007.05.03
:
Posted by 뽀기

'즐겨찾기 > 공부' 카테고리의 다른 글

자바스크립트 매뉴얼(한글)  (0) 2007.04.19
DOM 이해하기 (한글)  (0) 2007.04.19
:
Posted by 뽀기

자바 프레임웍에서 Ajax 애플리케이션 구현을 위한 Google Web Toolkit





난이도 : 중급

Noel Rappin, Senior Software Engineer, Motorola, Inc.

2007 년 2 월 06 일

Google Web Toolkit (GWT)은 동적 Java™Script의 생성에 혁신을 가져왔습니다. GWT를 사용하면, 개발자들은 익숙한 자바 기술을 사용하여 사용자 인터페이스(UI)와 이벤트 모델을 디자인하고 대다수의 브라우저에 익숙한 코드를 만드는 일을 하게 됩니다. 이 글을 통해, GWT의 기초를 설명하고, GWT에서 Asynchronous JavaScript + XML (Ajax) 애플리케이션을 만드는 방법과, 자바 언어로 코드를 작성하는 방법을 설명합니다. 또한 온라인에서 피자를 판매하는 Slicr라고 하는 Web 2.0 비즈니스 샘플을 가지고, GWT 애플리케이션을 생성 및 실행하는 방법을 설명합니다.

GWT를 사용하여, 전통적인 자바 GUI 인터페이스를 구현하는 것 보다 훨씬 더 쉽게, 더욱 풍부한 Ajax 브라우저 클라이언트 인터페이스를 구현할 수 있다. GWT가 매우 놀랍기는 하지만, 전체 웹 애플리케이션을 이것 하나로 만들 수는 없다. 서버에 데이터 스토어가 있어야 하고, 데이터를 자바 객체로 변환하여, GWT가 서버에서 클라이언트로 전달할 수 있도록 하는 프레임웍이 있어야 한다. 본 기술자료 시리즈에서, 100% 순수 자바 데이터베이스인 Apache Derby를 사용하는 방법을 설명한다.

이 글에서는 GWT에 대한 중점적인 설명과 더불어, GWT를 설정하는 방법과 사용자 액션에 반응하는 간단한 클라이언트 인터페이스를 구현하는 방법을 설명한다. 후속 기술자료에서는 Derby 데이터베이스를 설치하고, GWT 프론트엔드를 Derby 기반 백엔드에 연결하는 방법을 설명한다. 마지막으로, 개발 환경 밖에서 시스템을 전개하는 방법도 배운다.

Ajax 리소스 센터에서는 Ajax 프로그래밍 모델과 관련한 기술자료, 튜토리얼, 포럼, 블로그, wiki, 이벤트, 뉴스 등이 제공된다.

Google Web Toolkit이란 무엇인가?

GWT를 사용하여, 자바 프로그래밍 언어로 Ajax 애플리케이션을 개발할 수 있다. Ajax 애플리케이션의 장점은 전통적인 UI 애플리케이션과 연결되는 풍부한 인터랙티브 환경일 것이다. 그림 1은 샘플 GWT 인터페이스로서 데스크탑 이메일 애플리케이션이다. 이 데모는 GWT 웹 사이트에서 볼 수 있다.


그림 1. GWT 이메일 데모
A GWT e-mail demonstration

GWT의 가장 고유한 기능은 Ajax 애플리케이션을 만들 수 있고, 자바 언어로 코드를 작성할 수 있다는 점이다. 선호하는 자바 통합 개발 환경(IDE)을 사용하여, 클라이언트를 디버깅 할 수도 있다. 자바 객체를 사용하여 클라이언트와 서버간 통신도 가능하고, 모든 것이 자바 애플릿 보다 가볍다.

GWT는 기본적으로 컴파일러이다. GWT는 자바 코드를 HTML 페이지에 삽입되어 실행될 수 있는 JavaScript 코드로 변환하여 클라이언트 측 애플리케이션을 실행하는데 사용된다. JavaScript 코드는 거의 모든 브라우저에서 지원되므로 여러분은 프로그램의 인터페이스와 인터랙션에 집중할 수 있다.

물론, GWT가 컴파일러일 뿐이라면, 거론하지 않았을 것이다. 다행히도, 그 이상의 기능을 한다. GWT는 컴파일러 메커니즘 뿐 만 아니라, 아래와 같은 부가적인 특징이 있다.

  • 표준 UI 위젯은 유연하고, 거의 모든 주요 브라우저에서 실행된다. (Safari와 Opera 포함)
  • 클라이언트 측의 이벤트를 포착하고 응답하는 이벤트 메커니즘.
  • 웹 애플리케이션과 서버 간 비동기식 호출을 관리하는 프레임웍.
  • Ajax 애플리케이션이 예상 Back 버튼 작동에 관여하지 않도록 하는, STATEFUL 브라우저 히스토리를 만드는 메커니즘.
  • JUnit을 사용하여 클라이언트 애플리케이션용 단위 테스트를 작성하는 테스트 프레임웍.

본 시리즈에서는 이러한 기능들을 설명할 것이다. 하지만 먼저, GWT를 다운로드 및 설치해야 한다.

GWT 설치하기

이 글을 쓰고 있는 현재, GWT 최신 버전은 1.2이다. (참고자료) GWT는 Microsoft® Windows® XP, Windows 2000, Linux® 그리고, Mac OS X에서 실행되는 GTK+ 2.2.1 또는 그 이후 버전에서 완벽히 지원된다.

다운로드 한 압축 파일을 원하는 디렉토리에 압축을 푼다. 버전들마다 약간 다르지만, 기본 엘리먼트는 다음과 같다.

  • 세 개의 .jar 파일: gwt-user.jar 에는 프로젝트 classpath에 필요한 사용자 클래스들이 포함되어 있다. gwt-dev-windows.jar 또는 gwt-dev-linux.jar에는 컴파일러 코드가 포함되어 있다. 세 번째 파일, gwt-servlet.jar는 전개(deploy)에 사용된다.
  • 세 개의 명령행 유틸리티: applicationCreator, junitCreator, projectCreator. (Windows에서는, .cmd가 붙는다.)
  • 샘플 코드 디렉토리

GWT를 사용할 때, 일부 파일들은 임시 파일들을 관리하기 위해 GWT 홈 디렉토리에 놓인다.

프로젝트 만들기

다운로드를 마치면, 가장 먼저 프로젝트를 만들어야 한다. 온라인에서 피자를 파는, Slicr라고 하는 새로운 Web 2.0 비즈니스의 온라인 사이트를 구축할 것이다. GWT 프로젝트를 설정하는 방법들은 IDE를 사용할 것인지의 여부에 따라 다르다. 여기에서는 GWT 명령행 유틸리티의 지원도 받는 무료 Eclipse를 사용하도록 하자.

명령행 유틸리티를 사용하여 Eclipse 프로젝트를 만든다.

  1. 하드 드라이브의 적당한 곳에 slicr라고 하는 새로운 디렉토리를 만든다.
  2. 새로운 slicr 디렉토리에 명령어 프롬프트를 연다.
  3. 다음 명령어를 입력한다. (슬래시와 그 외 것들을 각자 운영 체계에 맞게 변환한다.)

<GWT_HOME>/projectCreator -eclipse slicr

이 명령어로는 GWT 프로젝트에 필요한 최소한의 것만 만든다. 새로운 src 하위 디렉토리와 새로운 .project와 .classpath 파일이 생겼다.

지금 바로 프로젝트로 시작할 수 있지만, GWT는 그 이상의 구조를 기대한다. 다음 명령어를 사용하여 설정할 수 있다.


<GWT_HOME>/applicationCreator -eclipse slicr com.ibm.examples.client.Slicr

-eclipse slicr 인자는 Eclipse 프로젝트의 이름이고, projectCreator에서 사용했던 것과 같아야 한다. (그렇지 않으면, Eclipse 내에서 GWT 애플리케이션을 시작하는데 문제가 생긴다.) 마지막 인자는 애플리케이션의 메인 클래스가 될 클래스 풀네임(fullname)이다. 이때, 패키지명의 마지막은 의미상 client로 정하기로 하고, client 이전의 부모 패키지 이름은 여러분이 알아서 작성하도록 한다. (일단, 'com.ibm.examples'로 부모 패키지 이름을 정하였다.)

이 명령어는 몇 가지 파일을 생성한다. .java 파일은 메인 클래스이고, 해당 클래스에 동반된 패키지 명에 부합되게 부모 디렉토리 들이 생성된다. 여러분은 최하위 디렉토리에는 clientpublic이 생성되어 있는 것을 확인 할 수 있다. 앞에서 거론한 public 디렉토리에는 Slicr.html와 Slicr.gwt.xml을 확인 할 수 있다. 또한, GWT는 eclipse가 사용하게 될 Slicr.launch파일과 두 개의 셀 스크립트 파일을 생성 한다.




위로


Eclipse로 옮기기

이제 프로젝트를 Eclipse로 옮긴다.

  1. Eclipse를 열고, File > Import를 클릭한다.
  2. 새로운 창에서, General 트리를 확장하여 Existing Projects into Workspace를 선택한다.
  3. Next를 클릭한 다음 Browse를 클릭하여 slicr 루트 디렉토리를 선택한다.
  4. 프로젝트를 선택하고, Copy projects into workspace 옵션이 설정되지 않도록 한다. (프로젝트를 옮기지 않을 것이기 때문이다.)

이 프로세스를 통해, 코드를 Eclipse로 옮겨서 볼 수 있다. GWT는 세 개의 중요한 파일들을 만들었다. 첫 번째가 com.ibm.examples 패키지에 있는 slicr.gwt.xml 파일이다. Listing 1은 XML 설정 파일이다.


Listing 1. slicr.gwt.xml
				
<module>
    <inherits name='com.google.gwt.user.User'/>
    <entry-point class='com.ibm.examples.client.Slicr'/>
</module>

여기에서 GWT 애플리케이션에 대한 모듈을 정의할 수 있다. 모듈(module)은 GWT 코드의 기본 단위이고, 클라이언트가 사용하는 HTML 페이지가 참조한다.

두 번째 파일은 퍼블릭 디렉토리에서 만들어진 Slicr.html 파일이다. 이것은 웹 애플리케이션의 프론트 페이지로서 클라이언트로 보내지는 .html 파일이다. 실제로 필요하지 않는 많은 코멘트들이 들어있다. Listing 2는 파일의 핵심 부분이다.


Listing 2. Slicr.html
				
<html>
    <head>
        <title>Wrapper HTML for Slicr</title>
        <meta name='gwt:module' 
            content='com.ibm.examples.Slicr'>
    </head>
    <body>
        <script language="javascript" 
            src="gwt.js"></script>
        <iframe id="__gwt_historyFrame" 
            style="width:0;height:0;border:0"></iframe>
        <h1>Slicr</h1>
        <p>
            This is an example of a host page for 
            the Slicr application. 
        </p>
        <table align=center>
            <tr>
                <td id="slot1"></td>
                <td id="slot2"></td>
            </tr>
        </table>
    </body>
</html>

가장 중요한 것은 바로 위의 .html 파일이다. 원하는 어떤 HTML이라도 추가할 수 있다. 가장 중요한 것은 HTML 파일에 원하는 어떤 내용이라도 추가 및 변형 할 수 있다는 것이다. 단지, GWT는 아래 네 가지 엘리먼트에 의해서 실행된다.

  • meta 태그: name 애트리뷰트와 content 애트리뷰트는 모듈의 완전한 형식을 갖춘 논리적 이름이다. (XML 모듈 파일과, 확장자 없는 XML의 파일 이름을 포함하고 있는 패키지이다.) 이 태그는 HTML 페이지를 특정 모듈로 연결시킨다. 페이지를 호출하면 모듈이 시작된다. (특히, 모듈에 있는 모든 엔트리 포인트들이 초기화 된다.)
  • script 태그: 이 태그는, GWT 자바 코드를 JavaScript 코드로 변환할 때 생성된 파일 중 하나인 gwt.js라고 하는 파일을 로딩한다. 이 파일은 모든 클라이언트 코드의 로딩을 제어하기 때문에, 프로그램을 실행하는 것 보다 이를 포함시키는 것이 더 중요하다.
  • iframe 태그: 이 태그를 쓰여진 대로 정확히 사용하면 웹 애플리케이션은 히스토리와 상태를 기억할 수 있다. GWT 애플리케이션에서 Back 버튼을 실행 불가로 만들 필요가 없다.
  • td 태그: 특정 .html 파일에 있는 이 태그에는 JavaScript 식별자들이 포함된다. 특별한 것은 없지만, GWT는 이 식별자를 엘리먼트를 배치할 장소로서 사용한다.

자바 시작 클래스

GWT는 재사용이 가능한(boilerplate) 자바 시작 클래스도 Listing 3과 같이 만든다.


Listing 3. 자바 시작 클래스
				
public class Slicr implements EntryPoint {

  public void onModuleLoad() {
    final Button button = new Button("Click me");
    final Label label = new Label();
    button.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        if (label.getText().equals(""))
          label.setText("Hello World!");
        else
          label.setText("");
      }
    });
    RootPanel.get("slot1").add(button);
    RootPanel.get("slot2").add(label);
  }
}

위의 자바 클래스는 client 패키지에 있고, 이로 인하여 GWT는 해당 코드를 JavaScript로 컴파일 할 것이다. 이것은 파일에 해당 특정 제한이 있는 것 같지만, 코드 내부를 보면 기본적인 Java 1.4 코드일 뿐이다.EntryPoint 인터페이스는 onModuleLoad()만을 정의하였으며, GWT는 이 모듈을 참조하는 HTML 페이지가 로딩되면 이 메소드를 자동으로 호출하게 되는 것이다.

위의 코드는 단순하다고 할 수 있다. 처음 두 라인에 버튼과 레이블을 정의한다. 그 다음 두 라인에서는, RootPanel.get() 메소드를 사용하여, 이 위젯들을 HTML 페이지의 특정 엘리먼트로 연결시킨다. 이 메소드에 대한 인자는 HTML 페이지에 정의된 엘리먼트의 JavaScript ID이다.

위의 코드 라인들 사이에 Swing에서와 같이 이벤트를 묶는데 사용하는 것과 비슷한 방식을 사용하여 이벤트 리스너(eventListner)를 정의한다. 위의 경우, 버튼을 클릭하면 ClickListener가 호출된다.




위로


GWT 프로그램 실행하기

GWT가 만든 샘플 프로그램을 실행해 보자. GWT 프로그램을 실행하는 두 가지 방법이 있다. 바로 웹 모드(Web mode)와 호스트 모드(hosted mode)이다. 웹 모드는 완전한 전개 모드로서, GWT 프로그램을 JavaScript 코드로 컴파일 한 후에 실행한다.

호스트 모드는 개발하는 동안 사용된다. 호스트 모드란, 클라이언트와 서버 코드를 한번에 시뮬레이트 하면서, 개발하는 동안 전개를 단순화 시키는 일종의 에뮬레이터이다. (호스트 모드는 Mac OS X에서는 불가능하다.) 디버거와 함께 IDE를 사용한다면, 호스트 모드에서 GWT 프로그램을 실행할 수 있으며, JavaScript로 컴파일 될 코드 부분에서 중단점(breakpoint)을 지정하고 그의 변수(variable)의 변경사항을 알아낼 수 있다. 이러한 부분은 개발 시 매우 유용하며, GWT로 작업하게 되면, 대부분 호스트 모드를 사용하게 된다.

또한, 여러 가지 방법들로 호스트 모드를 실행할 수 있다. 이전에 실행했던 applicationCreator 스크립트는 명령행에서 호출할 수 있는 Slicr-shell 스크립트를 만들어서 호스트 모드를 실행할 수 있도록 하였다. 또한, 이 글 초반에, GWT 프로젝트를 Eclipse로 반입하는 방법을 설명했다. Eclipse 프로젝트에서, Run 메뉴 또는 툴바에서 Debug 또는 Run을 선택할 수 있다. 생성된 창을 통해서는 Java Application을 클릭하여 Slicr 옵션을 볼 수 있다. 이 옵션은 GWT가 만들었고, 나머지 프로젝트와 함께 Eclipse로 반입했던 Slicr.launch 파일에서 사용할 수 있다.


그림 2. 호스트 모드 호출하기
Invoking hosted mode

호스트 모드에서 실행할 때, 두 개의 창이 나타난다. (처음 실행할 경우, 호스트 모드 설정이 초기화되는 동안 1분여의 시간이 걸린다.) 그림 3에서 보이는 첫 번째 창의 이름은 Google Web Toolkit Development Shell / Port 8888.이다. 여기에는 GWT의 에러와 로그 메시지들이 들어있다. 툴바를 사용하여, 새로운 호스트 브라우저를 열 수 있고, 스크린상의 로그를 확장, 축소, 삭제할 수 있다.


그림 3. 호스트 모드 쉘 윈도우
Hosted mode shell window

그림 4에 보이는 두 번째 창은 브라우저 모습이다. 보다시피, slicr.html 페이지에서 생성된 정적 HTML과 Slicr.java EntryPoint 클래스에서 생성된 버튼 위젯이 있다. 버튼을 클릭하면 레이블이 선택된다. 설정 단계에서 오류가 있었다면, 이 창을 볼 수 없고 대신 쉘에 에러 메시지가 나타난다. 모든 이름들이 정확한지를 확인하라. (특히, .launch 파일에서, 정확한 프로젝트 디렉토리가 지정되었는지를 확인한다.)


그림 4. 호스트 모드 브라우저
Hosted mode simulated browser

클라이언트 설정하기

이 글에서는 스크린상에서 위젯을 실행시키는 것에 초점을 맞추겠다. 그림 5에 보이는 스크린은 매우 단순해 보이지만, 꽤나 기능적이다.


그림 5. Slicr
Slicr

페이지가 로딩될 때 이러한 위젯들을 생성시키려면, EntryPoint 클래스의 onModuleLoad() 메소드에 코드를 넣어야 한다. Listing 4는 두개의 데이터를 정의하고, 각 패널을 구현하기 위해 헬퍼를 호출하는 메소드를 정의하고 있다. 이 코드가 실행되려면, slicr ID로 HTML 페이지에 엘리먼트를 삽입해야 한다. 이렇게 하면 리스팅의 RootPanel.get() 명령어가 그 페이지의 엘리먼트를 찾을 수 있다. 가장 쉬운 방법은 이전 HTML 리스팅의 테이블을 >div id="slicr" /<로 대체하는 것이다.


Listing 4. 모듈 로드 이벤트 핸들러
				
private DockPanel panel;
private List clearables;

public void onModuleLoad() {
    clearables = new ArrayList();
    initDockPanel();
    panel.add(buildActionPanel(), DockPanel.SOUTH);
    panel.add(buildPizzaTypePanel(), DockPanel.WEST);
    panel.add(buildToppingPanel(), DockPanel.EAST);
    RootPanel.get("slicr").add(panel);
}

위젯 설정하기

DockPanel에 모든 위젯을 설정하도록 한다. GWT에서의 DockPanel이란, Swing에서 BorderLayout을 사용하는 Panel에 해당한다. Swing에서는 한 개의 panel 클래스와 여러 개의 레이아웃 매니저가 있는 반면, GWT에는 여러 Panel 서브클래스가 있고, 각각 자식 위젯을 전개하는 고유의 알고리즘이 있다. 다른 패널 클래스로는 SimplePanel, HTMLTable, FlowPanel, StackPanel 등이 있다. DockPanel을 만드는 것은 어렵지 않다. 아래 Listing 5에서와 같이 세터(setter)가 이 일을 대신한다.


Listing 5. 메인 패널 초기화 하기
				
private void initDockPanel() {
    panel = new DockPanel();
    panel.setBorderWidth(1);
    panel.setSpacing(5);
}

SOUTH 패널(button) 생성하기

DockPanel의 선착순 원리를 따르기 때문에, 먼저 하단부 패널을 정의한다. 이런 방식으로, SOUTH 위젯은 전체 패널에서 실행한다. 아래 Listing 6과 같이 HorizontalPanel을 액션 패널로서 정의한다. (GWT의 HorizontalPanel이란, Swing에서의 box와 비슷하다고 보면 된다.)


Listing 6. SOUTH (buttons) 패널
				
public HorizontalPanel buildActionPanel() {
    HorizontalPanel actions = new HorizontalPanel();
    actions.setSpacing(10);
    Button clear = new Button("Clear");
    clear.addClickListener(new ClearClickListener());
    Button newPizza = new Button("Another Pizza");
    Button submitOrder = new Button("Submit");
    actions.add(clear);
    actions.add(newPizza);
    actions.add(submitOrder);
    return actions;
}

GWT Button 위젯을 사용하여 세 개의 버튼을 만든 다음, 패널에 추가한다. 또한, 나중에 정의하게 될 Clear 버튼에 ClickListener를 만든다. GWT는 이벤트 리스너들을 Swing과는 다르게 나눈다. ClickListener는 마우스 클릭만 리스닝한다. (종종, 인라인 클래스로서 정의된 리스너를 보곤 하는데, 이 스타일은 읽고 테스트 하기 까다롭기 때문에 네임드 클래스를 만들었다.)

WEST 패널(pizza type) 생성하기

pizza type 의 패널은 Listing 7과 같이 복잡하지 않다. GWT RadioButton 위젯을 사용하면 된다.


Listing 7. WEST (pizza types) 패널
				
public static final String[] PIZZA_TYPES = new String[] {
    "Thin Crust Medium", "Thin Crust Large", 
    "Thin Crust X-Large", "Thick Crust Medium", 
    "Thick Crust Large"
};

private VerticalPanel buildPizzaTypePanel() {
    VerticalPanel pizzaTypes = new VerticalPanel();
    HTML label = new HTML("<h2>Pizza</h2>");
    pizzaTypes.add(label);
    for (int i = 0; i < PIZZA_TYPES.length; i++) {
        RadioButton radio = new RadioButton("pizzaGroup", 
            PIZZA_TYPES[i]);
        clearables.add(radio);
        pizzaTypes.add(radio);
    }
    return pizzaTypes;
}

HTML을 실행하는 레이블인 HTML 위젯을 사용할 것이다. HTML 위젯은 결국 HTML상의 <span> 태그 주위의 래퍼(Wrapper)로서 역할을 한다. RadioButton 컨스트럭터(constructor)는 두 개의 인자들을 취한다. 첫 번째는 라디오 버튼용 스트링 레이블이고, 두 번째는 텍스트 레이블이다. 각 버튼을 패널과 인스턴스 리스트에 추가하면 이 리스너들 중 한 곳에서 사용하게 될 것이다.


EAST 패널 (toppings) 만들기

topping 패널은 Listing 8처럼 좀더 복잡하다. 사용자가 다양한 토핑을 가진 피자를 만들 수 있도록 해야 한다. 토핑 버튼을 클릭하면 두 쪽 모두 체크되지만, 한 쪽만 체크되거나, 개별적으로 삭제될 수 있다. 모든 것의 줄을 맞춰야 하기 때문에 Grid를 사용한다.


Listing 8. 토핑 그리드
				
public static final String[] TOPPINGS = new String[] {
    "Anchovy", "Gardineria", "Garlic", 
    "Green Pepper", "Mushrooms", "Olives", 
    "Onions", "Pepperoni", "Pineapple", 
    "Sausage", "Spinach"
};

private VerticalPanel buildToppingPanel() {
    VerticalPanel toppings = new VerticalPanel();
    toppings.add(new HTML("<h2>Toppings</h2>"));
    Grid topGrid = new Grid(TOPPINGS.length + 1, 3);
    topGrid.setText(0, 0, "Topping");
    topGrid.setText(0, 1, "Left");
    topGrid.setText(0, 2, "Right");
    for (int i = 0; i < TOPPINGS.length; i++) {
        Button button = new Button(TOPPINGS[i]);
        CheckBox leftCheckBox = new CheckBox();
        CheckBox rightCheckBox = new CheckBox();
        clearables.add(leftCheckBox);
        clearables.add(rightCheckBox);
        button.addClickListener(new ToppingButtonListener(
                leftCheckBox, rightCheckBox));
        topGrid.setWidget(i + 1, 0, button);	
        topGrid.setWidget(i + 1, 1, leftCheckBox);
        topGrid.setWidget(i + 1, 2, rightCheckBox);
    }
    toppings.add(topGrid);
    return toppings;
}

VerticalPanelHTML 위젯을 사용한다. GWT Grid에 모든 것을 놓기 때문에, 그리드의 크기를 설정해야 한다. 그리드의 각 셀에는 플레인 텍스트나, 또 다른 GWT 위젯을 포함시킬 수 있다. 각 행(row)에, 버튼과 두 개의 체크 박스를 만들고, 이들을 셀에 맞춰 정렬한다. 버튼용 리스너를 추가하고, 체크 박스를 clearable 리스트에 놓는다.

리스너 정의

위젯을 설정했다면, 두 개의 정의된 리스너를 보자. 두 개 중 더 단순한 것이 Clear 버튼에 대한 것이다. Listing 9와 같이 이 버튼은 clearable 리스트에서 실행되고, 모든 것을 지운다.


Listing 9. Clear 버튼에 정의된 리스너
				
private class ClearClickListener implements ClickListener {
    public void onClick(Widget sender) {
    for (Iterator iter = clearables.iterator(); iter.hasNext();) {
            CheckBox cb = (CheckBox) iter.next();
            cb.setChecked(false);
        }
    }
}

주: GWT에서, RadioButton은 실제로 CheckBox의 서브클래스이다. 따라서 위 코드는 class cast exception을 트리거(trigger)하지 않는다.

토핑 버튼용 리스너는 더 복잡하다. 제휴 체크 박스들 중 어떤 것도 선택되지 않으면, 이 리스너는 두 개의 체크 박스 모두를 선택한다. 다른 상황에서는, 두 가지 모두를 지운다. Listing 10은 그 예이다.


Listing 10. 버튼에 정의된 리스너
				
private class ToppingButtonListener implements ClickListener {

    private CheckBox cb1;
    private CheckBox cb2;

    public ToppingButtonListener(CheckBox cb1, CheckBox cb2) {
        this.cb1 = cb1;
        this.cb2 = cb2;
    }

    public void onClick(Widget sender) {
        boolean unchecked = !cb1.isChecked() && !cb2.isChecked();
        cb1.setChecked(unchecked);
        cb2.setChecked(unchecked);
    }
}

공유 사이트...

digg Digg
del.icio.us del.icio.us
Slashdot Slashdot

예고

이번 글에서는 slicr 클라이언트를 구현해 보았다. 다음 글에서는 Derby 데이터베이스를 사용하여 서버 측에서 데이터 레이어를 구현하고, 데이터베이스에서 온 데이터를 GWT 클라이언트로 보내질 수 있는 자바 객체들로 변환하는 방법을 설명하겠다. 서버와 클라이언트를 연결하는 원격 프로시저 아키텍처도 설명한다.

서버 측이 개별적으로 실행되어야 한다면, 개발 및 실행 환경에 이를 전개하는 방법을 고려해야 한다. 또한, 인터페이스를 보기 좋은 모양으로 만드는 방법도 배울 것이다. 그 전에 GWT 다운로드 사이트에 가서 직접 실행해 보기 바란다.

기사의 원문보기



참고자료

교육

제품 및 기술 얻기

토론


필자소개

Noel Rappin 박사는 Georgia Institute of Technology의 Graphics, Visualization, and Usability Center 소속이며, Motorola의 소프트웨어 엔지니어이다. wxPython in Action and Jython Essentials를 공동 집필했다.

출처 : IBM developerworks
:
Posted by 뽀기

애플리케이션에 맞게 데이터를 인코딩(encode) 하는 가장 올바른 방법


JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.



난이도 : 중급

Dethe Elza, Senior Software Developer, Uniserve Communications Corporation
David Mertz, Ph.D, Author, Gnosis Software, Inc.

2007 년 2 월 06 일

Ajax(Asynchronous JavaScript and XML)는 신뢰성을 기반으로, 서버에 채널을 개방하여, 웹 애플리케이션에서 사용되는 데이터를 교환하는 신개념 웹 브라우저입니다. 이것은 표준 웹 기술과는 반대되는 개념이고, Ajax를 기반으로 개발 할 때, 전통적인 웹 페이지들과는 다른 디자인 정책이 필요합니다. 백(back) 버튼을 관리하는 방법, 업데이트 된 데이터를 디스플레이 하는 방법, 업데이트를 보내는 빈도수 등도 그 예가 될 수 있습니다. 이 글에서는 데이터 교환에 어떤 포맷을 사용해야 할 것인지를 중점적으로 설명합니다.

Ajax라는 단어에서 X는 XML을 뜻하지만, XML은 언어가 아니라 언어를 구현하는 툴킷이다. 따라서, 가장 먼저, 기존 언어를 사용할 것인가, 아니면 자신의 언어로 재 구축할 것인지를 결정해야 한다. 결정하고 나서는 부가적인 문제들이 생기기 시작한다. 기존의 언어들은 자신의 필요에 맞는가, 자신에 필요에 맞게 설계할 수 있는가? 그 언어를 처리할 툴은 있는가, 툴을 구현해야 하는가? 다른 사람들과 커뮤니케이션을 해야 한다면, 그 언어는 잘 알려진 언어인가? 다른 애플리케이션들도 쉽게 여러분의 데이터에 액세스 하여, 사용할 수 있는가?

그래픽 데이터일 경우에는 (X)HTML, SVG 또는 X3D, 데이터 조각에는 Atom, 단순한 아웃라인에는 OPML, 그래프에는 RDF 등 용도가 확실한 것도 있다. 완벽한 기능을 갖추고 있고, 장황한 DocBook, DITA, OpenOffice 포맷으로 된 데이터를 보낼 수도 있다. (참고자료)

한편, Ajax라는 단어에서 X가 무엇을 뜻하는지에 상관없이, 여러분이 원하는 다양한 종류의 데이터를 보낼 수 있다. 바이너리 데이터, 이미지, 무비, PDF 파일도 가능하지만, JavaScript로 되어있는 브라우저에서는 다루기 어렵다. XML 보다 단순한 텍스트 포맷으로 보낼 수도 있다. Tab 또는 콤마 분리 리스트(tab-, comma-delimited list), Markdown, YAML, JSON 등을 XML 대안으로 사용할 수 있다. 이외에도, XML을 사용하지 않는 수 십 개의 옵션들이 있다. (참고자료 - XML Alternatives 웹 사이트 참조) 물론, 장황함(verbose)과 풍부함(rich)의 차이는 있다. 심지어, 이 리스트에 있는 한 개의 아이템에서도 여러 가지의 데이터 인코딩 방식이 있을 수 있다.

  • OpenOffice Spreadsheet
  • DITA (Darwin Information Typing Architecture)
  • DocBook
  • RDF-XML
  • XHTML
  • Microformat
  • Atom
  • OPML (Outline Processor Markup Language)
  • Custom XML
  • Markdown / Textile / reStructured Text
  • YAML (YAML Ain't Markup Language)
  • JSON (JavaScript Object Notation)
  • Comma- (or Tab-) delimited text

무엇을 선택하여야 하는가?

이러한 많은 옵션들 중에서, 어떻게 하면 현명한 결정을 내릴 수 있을까? 위에 제시한 리스트는 엄밀히 말해서 복잡한 순서대로 나열한 것은 아니다.

  • 우선, 가장 단순한 방식 또는 가장 복잡한 방식으로 포맷을 사용할 수 있다.
  • 생성, 읽기, 파싱, 프로세스 중에서 어떤 것이 가장 복잡한 것인지를 파악해야 한다. 예를 들어, XML은 그 자체로 복잡한 편이지만, 파서가 브라우저에 이미 있기 때문에 파싱은 오히려 단순하다.
  • 복잡성은 데이터의 유형에 상당히 많이 의존한다. 스프레드시트나 데이터베이스처럼, 고정된 구조를 갖고 있는가? 워드 프로세싱 문서나 블로그 포스트처럼 유연한(loosely) 구조를 갖고 있는가? 항공기 매뉴얼 같은 대형 문서인가, 응답 코드 같은 작은 데이터 조각인가? 데이터 세트가 크다면, 이것을 한 번에 보낼 것인가, 아니면 필요할 때마다 작은 조각 단위로 보낼 것인가? 큰 데이터를 작은 조각 단위로 나누는 것은 얼마나 어려우며, 받는 쪽에서, 작은 조각들을 올바르게 조합하려면 얼마나 많은 정보가 필요한가?

즉, 무엇이 가장 중요한 요소인가?

  • 장황함/대역폭(특별한 경우를 제외하고는, 이러한 요소는 여러분이 생각하는 것만큼 중요하지 않다.)
  • 가독성(작성 가능성, 관리성)
  • 파싱 난이도(클라이언트 측과 서버 측)
  • 파싱 속도(네이티브 대 스크립팅)
  • 정보 손실(ordered pairs -> dictionary -> sets)
  • 유연성(가끔은, 덜 유연한 것이 더 나을 때도 있다.)
  • 언어 이식성(얼마나 많은 언어들을 사용할 수 있는가?)
  • 정확성(포맷에 어느 정도 순응하는가?)
  • 라운드 트리핑 (Round-Tripping) (Markdown -> XHTML -> Markdown, 얼마나 많은 데이터 손실이 있는가?)

속도, 대역폭 활용, 효율성을 위해서가 아니라, 사용자를 만족시키기 위해 최적화 하는 경우가 대부분이다. 지연되는 부분은 숨기고, 즉각적인 업데이트가 가능하다면, 미미한 네트워크 지연은 문제가 되지 않는다. 바로 이것이 Ajax의 가장 큰 장점일 것이다.




위로


제 1 원칙

이 글에서, 몇 가지 선택 원칙을 제시하고, 이를 뒷받침 할 몇 가지 예제도 소개하겠다. 이모든 것들이 여러 가지 옵션들을 선택하는데 있어서 충실한 가이드라인이 될 것이다.

  • 데이터용 JSON: 데이터가 구축되었다면 JSON이 제격이다. 적어도 세 개의 파서들이 브라우저에 포함되어 있고(HTML, XML, JavaScript), 이중 가장 빠른 것은 JavaScript 이다. 또한, 데이터가 이미 JavaScript로 되어 있을 경우, JavaScript를 사용하여 데이터를 조작하는 것이 DOM 보다는 낫다. 데이터를 바로 디스플레이 하지 않거나, 데이터가 먼저 수정되어야 한다거나, 또는 데이터가 웹 페이지의 다른 부분에서, 또는 다른 포맷으로 디스플레이 된다면, JSON이 잘 맞는다. 대부분의 언어들에는 JSON용 라이브러리가 있기 때문에, 더 이상 JavaScript만을 위한 것은 아니다.
  • 혼합 콘텐트(문서)를 위한 XML: URL과 같은 메타데이터를 사용하거나, 워드 프로세싱 문서와 블로그 포스트 같은 마크업과 텍스트를 혼합해야 한다면 XML을 사용하라. 데이터가 한 곳에서 직접 디스플레이 된다면, 서버에서 이를 정형화(format)하고, Ajax를 사용하여 검색 및 삽입한다.(이 기법은 'client-side includes'라고 알려졌다.) David가 "MochiKit" (참고자료)에서 언급하였듯이, XML을 브라우저로 전달하고 CSS로 포맷하거나, 또는 HTML을 통해서 이를 스타일링 할 것인지를 선택할 수 있다. 당신이 XML을 사용해야 한다면, 필자는 XHTML, SVG, X3D등과 같은 표준 포맷을 사용하라는 것 외에 많은 가이드라인을 줄 수는 없다. 이러한 포맷들의 서브셋을 사용하여, 시스템들 간 상호 운용성이 뛰어난 데이터를 만들 수 있고, 다른 프로그래머들도 익숙하게 사용할 수 있도록 한다. 가끔은 자신만의 XML 포맷을 만들 수도 있지만, 상호 운용성 부분에서는 더욱 많은 신경을 써야 한다. 그것도 의심스럽다면, HTML을 계속 사용하라.
  • 동기화(Syndication)를 위한 Atom: 여기에서 나는 동기화에 대해 매우 포괄적으로 정의를 내리고자 한다. 데이터를 주기적으로 업데이트 할 것이라면, 데이터에 타임스탬프가 필요하다면, Atom을 사용하는 것이 좋다. 또한, 데이터 흐름에 대한 표준 인벨롭(envelope)으로서 Atom 포맷을 사용하는 것이 좋다. 이렇게 하면, 애그리게이터(aggregator), 뉴스 리더기, 스크립팅 라이브러리를 통해서, 데이터를 추적하고 재사용할 수 있는 많은 툴들을 사용할 수 있을 것이다. 또한, 웹 페이지에 데이터를 삽입만 하면, 크게 노력하지 않아도 동기화 피드가 될 수 있는 효과도 누릴 수 있다.



위로


선택의 파라독스

이제, 다른 포맷들과, 각 포맷의 사용 방법에 대해서 알아보자. 이전 두 개의 칼럼에서는(참고자료) 메커니즘에 초점을 맞추느라 간단한 예제를 사용했지만, 이번에는 실제 예제를 사용해보겠다. 나의 아내인 Daniela는 시인이다. 그녀는 잡지, 콘테스트, 이벤트에 제출한 작품들을 관리해야 했다. 제출한 시가 어떤 단계에 와 있으며, 어떤 것이 채택 또는 탈락 되었는지, 각 작품당 가격은 얼마인지, 이벤트 비용은 얼마인지를 알아야 한다. Listing 1은 데이터 내용이다.


Listing 1
				

Room of One's Own
Submitted May 10, '05
* Hyacinth Blue
* Fabrication
* Thanksgiving
* Spilling the Peas
Accepted Hyacinth Blue
Accepted Sept 2005
Published Oct 2006
Paid Sept 2006
Paid $50 + 2 copies
Postage $1.12
Submission Fee: 0
Journal submission

Surrey International Writer's Contest
Contest submission
Submitted Aug. 31 2006
* 13th Child
Fee: $15
Postage: 1.05
Honorable Mention
Prize: $150 + copy of anthology
Accepted Sept. 26 2006
Publication Date Oct. 20 2006

Word on the Street
Public Reading
Invited speaker
Reading time: 10 minutes
Paid: T-shirt and lunch
Date: Sept 24 2006

Paideusis: The Journal of the Canadian Philosophy of \
    Education Society
Submitted Oct. 13th 2006
* To carry over: metaphor invents us (seven poems)
Email submission
Referreed Journal
Accepted Oct. 16th 2006
Published (Pending) Nov. 2006


어느 정도 까지는 이러한 포맷도 유효했지만, 출품작이 많아지면서, 트래킹(tracking)은 더욱 어려워졌다. 연간 수입과 지출을 가늠하기가 더 어려워졌고, 평균 응답 시간(제출과 채택 또는 탈락까지 걸리는 시간)같은 정보도 받아보고 싶었다. 그래서, 나는 아내에게 "Fame Not Fortune"이라고 하는, Ajax 웹 애플리케이션을 구현 할 것을 제안했다.

물론, 나의 게으름 탓에, 새로운 애플리케이션을 구현하지 않고, 원래 있었던 애플리케이션을 사용하게 되었다. 아내가 시를 편집할 때 Open Office (워드 프로세싱) (참고자료)를 사용하기 때문에, 그림1처럼 트래킹에도 Open Office (spreadsheet)를 사용할 수 있었다.


그림 1. Open Office Spreadsheet 예제
Open Office Spreadsheet example

벌써, 몇 가지 문제가 드러나고 있다. 특히, 스프레드시트는 트래킹에 맞는 인터페이스가 아니다. 대상 데이터는 매우 유연하기 때문에, 많은 칼럼들이 채워지지 않았고, 아내도 스프레드시트를 사용하는 것에 익숙하지 않았다. 웹 애플리케이션을 구현해야 한다는 나의 생각이 옳았다. 물론, Open Office는 문서를 XML로 저장하기 때문에, 복잡한 데이터 마이닝(data-mining) 태스크에는 Open Office를 사용하고, 데이터 입력에는 웹을 사용하는 상황이다. 이러한 경우에는 한 가지 포맷을 결정하는 것이 더 낫다.

Open Office에 저장된 포맷은, 실상은 데이터와 기타 리소스들(삽입된 이미지, 스크립트, 스타일링 정보)을 포함하고 있는 .zip 파일인, .odf 파일이다. 이 문서를 보면서, 다음과 같은 사실들을 발견했다.

  • META-INF는 .odf 문서의 콘텐트 리스트인 manifest.xml을 포함하고 있는 폴더이다.
  • Configurations2는 상태 바, 메뉴 같은 UI를 포함하고 있는 폴더이다. 현재까지는 무시해도 좋다.
  • Thumbnails은 작은 .png 이미지를 포함하고 있는 폴더이다.
  • content.xml은 내가 입력했던 실제 데이터 파일이다.
  • meta.xml은 문서에 대한 정보를 포함하고 있는 파일이다. 생성자, 수정일, 기타 상세 정보를 포함하고 있다.
  • mimetype은 "application/vnd.oasis.opendocument.spreadsheet" 스트링을 포함하고 있는 파일이다.
  • settings.xml은 Open Office 설정 내용들을 포함하고 있는 파일이다.
  • styles.xml은 스프레드시트에 대한 포맷팅 정보를 포함하고 있는 파일이다.

따라서, 웹 애플리케이션과 Open Office간 데이터를 교환해야 한다면, 전체 .odf 파일이 아닌 내부 content.xml 파일에 주목해야 한다. 이 파일에는 무엇이 있을까? 17,629개의 문자와, 23개의 XML 네임스페이스, 63 줄의 스타일링 정보가 포함되어 있고, 모든 셀에는 스타일 정보가 들어있다. 이것은 데스크탑 스프레드시트 애플리케이션에는 합리적인 대안이지만, 네트워크를 통해서까지 이렇게 불필요한 정보를 처리하느라 시간을 허비하고 싶지는 않다. Listing 2는 실제 데이터의 한 행(row)이다.


Listing 2
				<table:table-row table:style-name="ro3">
  <table:table-cell office:value-type="string">
    <text:p>Room of One's Own</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="Default"
  office:value-type="string">
    <text:p>Journal</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce2"
  office:value-type="string">
    <text:p>Hyacinth Blue; Fabrication; Thanksgiving;
    Spilling the Peas</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce5"
  office:value-type="date" office:date-value="2005-05-10">
    <text:p>10 May 2005</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce5"
  office:value-type="date" office:date-value="2005-09-01">
    <text:p>1 Sep 2005</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce5"
  office:value-type="date" office:date-value="2006-10-01">
    <text:p>1 Oct 2006</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce5"
  office:value-type="date" office:date-value="2006-09-01">
    <text:p>1 Sep 2006</text:p>
  </table:table-cell>
  <table:table-cell office:value-type="string">
    <text:p>Hyacinth Blue</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce6"
  office:value-type="currency" office:currency="CAD"
  office:value="50">
    <text:p>50.00 CAD</text:p>
  </table:table-cell>
  <table:table-cell office:value-type="string">
    <text:p>2 Copies of Publication Issue</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce7"
  office:value-type="currency" office:currency="CAD"
  office:value="1.12">
    <text:p>1.12 CAD</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce7" />
  <table:table-cell table:number-columns-repeated="244" />
</table:table-row>

데이터를 XHTML에 삽입하는 것은 어떨까? 이 같은 경우, 파싱이나 포맷팅을 할 필요 없이, Listing 3처럼, 직접 디스플레이 할 수 있다.


Listing 3
				<html><body><ul>
    <li><dl>
        <dt>publisher</dt><dd>Room of One's Own</dd>
        <dt>type</dt><dd>Journal</dd>
        <dt>titles</dt><dd><ul>
            <li>Hyacinth Blue</li>
            <li>Fabrication</li>
            <li>Thanksgiving</li>
            <li>Spilling the Peas</li>
        </ul></dd>
        <dt>submitted</dt><dd>2005-05-10</dd>
        <dt>accepted</dt><dd>2005-09-01</dd>
        <dt>published</dt><dd>2006-10-01</dd>
        <dt>payment received</dt><dd>2006-09-01</dd>
        <dt>titles accepted</dt><dd><ul>
            <li>Hyacinth Blue</li>
        </ul></dd>
        <dt>expenses</dt><dd><dl>
            <dt>postage</dt><dd>CAD 1.12</dd>
        </dl></dd>
        <dt>payment</dt><dd><ul>
            <li>CAD 50.00</li>
            <li>2 Copies of Publication Issue</li>
        </ul></dd>
    </dl></li>
    <li><dl>
        <dt>publisher</dt>
            <dd>Surrey International Writers' Competition</dd>
        <dt>type</dt><dd>Contest</dd>
        <dt>titles</dt><dd><ul>
            <li>The Thirteenth Child</li>
        </ul></dd>
        <dt>submitted</dt><dd>2006-08-31</dd>
        <dt>accepted</dt><dd>2006-09-26</dd>
        <dt>published</dt><dd>2006-10-20</dd>
        <dt>payment received</dt><dd>2006-10-20</dd>
        <dt>titles accepted</dt><dd><ul>
            <li>The Thirteenth Child</li>
        </ul></dd>
        <dt>expenses</dt><dd><dl>
            <dt>postage</dt><dd>CAD 1.05</dd>
            <dt>entry fee</dt><dd>CAD 15.00</dd>
        </dl></dd>
        <dt>payment</dt><dd><ul>
            <li>CAD 150.00</li>
            <li>Honorable Mention</li>
            <li>Copy of Anthology</li>
        </ul></dd>
    </dl></li>
    <li><dl>
        <dt>publisher</dt><dd>Word on the Street</dd>
        <dt>type</dt><dd>Invited Reader</dd>
        <dt>event</dt><dd>10 Minutes of readings</dd>
        <dt>event date</dt><dd>2006-09-24</dd>
        <dt>payment</dt><dd><ul>
            <li>T-Shirt</li>
            <li>Lunch</li>
        </ul></dd>
    </dl></li>
    <li><dl>
        <dt>publisher</dt><dd>Paideusis: The Journal of the
            Canadian Philosophy of Education Society</dd>
        <dt>type</dt><dd>Refereed Journal</dd>
        <dt>titles</dt><dd><ul>
            <li>To Carry Over: Metaphor Invents Us (seven poems)</li>
        </ul></dd>
        <dt>submitted</dt><dd>2006-10-13</dd>
        <dt>accepted</dt><dd>2006-10-16</dd>
        <dt>published</dt><dd>Pending</dd>
        <dt>titles accepted</dt><dd>All</dd>
    </dl></li>
</ul></body></html>

이것은 데이터를 HTML로 단순히 매핑한 것이다. 기본 스타일링이 충분하지는 않지만, 크로스 플랫폼(cross-platform) 방식으로, CSS를 사용하면 스타일링 하기가 매우 쉽고, DOM을 사용하여 데이터를 쉽게 조작할 수 있다. 추가된 HTML 코드 때문에 약간 부풀려 보이지만, 그렇게 심한 것은 아니다. 이 예제는 XOXO outline Microformat(참고자료)와 매우 비슷하고, class="outline"을 첫 번째 <ul /> 엘리먼트에 붙인다면, XOXO 아웃라인이 될 것이다. 여기에 커스텀 XML을 사용하여 콘텐트를 추가할 수 있고, 디스크립션 리스트를 <submission/> 엘리먼트로 대체했다. 하지만 이 예제의 경우, 간결함이나 가독성 측면에서는 별로 얻은 것이 없다. 간결함을 원한다면, JavaScript Object Notation (JSON)을 고려해 볼 수 있다.


Listing 4
				

    {   "publisher": "Room of One's Own",
        "type": "Journal",
        "titles": ["Hyacinth Blue", "Fabrication", "Thanksgiving",
            "Spilling the Peas"],
        "titles accepted": ["Hyacinth Blue"],
        "submitted": "2005-05-10",
        "accepted": "2005-09-01",
        "published": "2006-10-01",
        "payment received": "2006-09-01",
        "expenses": [{"postage": "CAD 1.12"}],
        "payment": ["CAD 50.00", "2 Copies of Publication Issue"]},
    {   "publisher": "Surrey International Writers' Competition",
        "type": "Contest",
        "titles": ["The Thirteenth Child"],
        "titles accepted": ["The Thirteenth Child"],
        "submitted": "2006-08-31",
        "accepted": "2006-09-26",
        "published": "2006-10-20",
        "payment received": "2006-10-20",
        "expenses": [{"postage": "CAD 1.05"},
            {"postage": "CAD 15.00"}],
        "payment": ["CAD 150.00", "Honorable Mention",
            "Copy of Anthology"]},
    {   "publisher": "Word on the Street",
        "type": "Invited Reader",
        "event": "10 Minutes of readings",
        "event date": "2006-09-24",
        "payment": ["T-Shirt", "Lunch"]},
    {   "publisher": "Paideusis: The Journal of the Canadian\
             Philosophy of Education Society",
        "type": "Refereed Journal",
        "titles": ["To Carry Over: Metaphor Invents Us \
            (seven poems)"],
        "titles accepted": "All",
        "submitted": "2006-10-13",
        "accepted": "2006-10-16",
        "published": "Pending"}
]

Listing 4를 보면, HTML 인코딩과 모두 같은 내용이지만, 이것은 JavaScript의 서브셋(subset)이기 때문에, JavaScript 객체, 리스트(list), 스트링(string)으로서 직접 데이터에 액세스 할 수 있다. 이 포맷은 단순하고 간결하며, 이전 포맷에 있는 모든 정보를 포함하고 있다. 게다가, 구조도 잘 보존하면서 Semi-structured 데이터에도 잘 맞는다. 이것으로 간결성에 대한 해결책은 얻었지만, 이것보다 더 단순해 질 수 있다. 첫 번째 예제로 다시 돌아가서, 스프레드시트에 데이터를 저장해 보자. 한 스프레드시트에서 다른 스프레드시트로 데이터를 옮기는 방식 중에는 Comma-Separated Value (CSV)가 있다. Listing 5를 보자.


Listing 5
				

"Publisher", "Type", "Titles", "Submitted", "Accepted/Rejected", \
"Published", "Payment Received", "Titles Accepted", "Payment", \
"In Kind", "Postage", "Fees"
"Room of One's Own", "Journal", "Hyacinth Blue; Fabrication; \
Thanksgiving; Spilling the Peas", 10 May 2005, 1 Sep 2005, \
1 Oct 2006, 1 Sep 2006, "Hyacinth Blue", 50.00 CAD, \
"2 Copies of Publication Issue", 1.12 CAD,
"Surrey International Writer's Competition", "Contest", \
"The Thirteenth Child", 31 Aug 2006, 26 Sep 2006, 20 Oct 2006, \
20 Oct 2006, "The Thirteenth Child", 150.00 CAD, "Honorable Mention, \
Copy of Anthology", 1.05 CAD, 15.00 CAD
"Word on the Street, Vancouver", "Invited Speaker", \
"10 Minutes of Readings", , , 24 Sep 2006, , , , "T-Shirt, Lunch", ,
"Paideusis: The Journal of the Canadian Philosophy of Education \
Society", "Refereed Journal", "To Carry Over: Metaphor Invents Us \
(seven poems)", 13 Oct 2006, 16 Oct 2006, "(Pending) Nov 2006", , \
"All", , , "Email",


Listing 5는 어느 정도까지 간결해 질 수 있는지를 보여주는 것 같다. JSON과 CSV의 중대한 차이도 보인다. CSV는 일반적인 기술이고, 결코 표준화 되어 있지 않으며, 오직 ASCII 텍스트만을 취하는 반면, JSON은 명확하게 표준화 되었으며, Unicode의 UTF-8 인코딩 사용에 대해서도 정의를 내린다. 따라서, CSV 예제의 경우 ASCII 범위 이외의 어떤 텍스트도 사용하지 않지만, JSON(그리고 XML)은 어떤 텍스트와도 잘 작동한다.




위로


Atom 연결

Atom Syndication Format은 필자, 발행일 등, Fame not Fortune의 정보와 몇 가지 중복되는 사항들이 있다. 이 모든 데이터를 하나의 피드(feed)에 놓고, XML로 포맷하고, 이와 동시에 표준 애그리게이터를 사용하여 이 내용들이 검색되도록 할 수 있다. 바로 이것이 나의 애플리케이션과 다른 툴들을 구별하는 요소가 된다. Listing 6은 Atom 피드의 모습이다. (간략하게 도입 부문만 소개하고자 한다.)


Listing 6
				

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Fame not Fortune</title>
  <subtitle>Recent submissions</subtitle>
  <link href="http://example.org/famenotfortune"/>
  <updated>2006-12-03T20:37:16Z</updated>
  <author>
    <name>Daniela Elza</name>
  </author>
  <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
  <entry>
    <title>Hyacinth Blue</title>
    <link href="http://example.org/famenotfortune/hyacinthblue"/>
    <id>urn:uuid:68C0BAAB-C955-45F9-BDD6-21A22FC809AF</id>
    <updated>2006-12-01T20:37:16Z</updated>
    <published>2006-10-01T12:12:12Z</published>
    <category term="Journal"/>
    <content type="xhtml">
        <div xmlns="http://www.w3.org/1999/xhtml">
            <dl>
                <dt>publisher</dt><dd>Room of One's Own</dd>
                <dt>titles</dt><dd><ul>
                    <li>Hyacinth Blue</li>
                    <li>Fabrication</li>
                    <li>Thanksgiving</li>
                    <li>Spilling the Peas</li>
                </ul></dd>
                <dt>submitted</dt><dd>2005-05-10</dd>
                <dt>accepted</dt><dd>2005-09-01</dd>
                <dt>payment received</dt><dd>2006-09-01</dd>
                <dt>expenses</dt><dd><dl>
                    <dt>postage</dt><dd>CAD 1.12</dd>
                </dl></dd>
                <dt>payment</dt><dd><ul>
                    <li>CAD 50.00</li>
                    <li>2 Copies of Publication Issue</li>
                </ul></dd>
            </dl>
        </div>
    </content>
  </entry>
</feed>


Listing 6이 낯설지가 않다면, Atom Entry 엘리먼트에 데이터에 필요한 모든 필드들이 없고, 대부분의 엔트리가 <content/> 태그로 래핑된 XHTML 예제이기 때문일 것이다. published 필드와 author를 약간 변경할 수도 있었지만, published 필드의 남용일 뿐이다. 고유의 네임스페이스를 만들어서, 추가 필드로 Atom Entry를 확장할 수 있지만, 어떤 기존 애그리게이터나 피드 리더기도 그 정보를 사용할 수 없기 때문에 의미가 없다. 단순히 Atom 인벨롭으로 XHTML 예제를 래핑하여 애플리케이션에 동기화 지원을 할 수 있지만, 순수하게 데이터 지향 관점에서 본다면 Atom은 어떤 장점도 없다. 애플리케이션의 데이터를 이동할 때, Atom Entry로 래핑하고, Atom Publishing Protocol (참고자료)을 사용하면 도움이 될 것이다. Atom을 사용하고 싶다면, 데이터 포맷 선택에 제한이 생긴다. Atom <content/> 태그에는 세 가지 데이터 유형, 텍스트(JSON 포맷), HTML, XHTML이 포함된다. Atom을 먼저 확장하지 않고는, Atom에 임의의 XML 콘텐트를 삽입할 수 없다. 따라서, Atom 통합을 할 경우 다른 데이터 포맷 결정에도 영향을 줄 수 있기 때문에, 데이터에 동기화를 지원할 것인지의 여부를 먼저 결정해야 한다.




위로


예제

Ajax 웹 사이트나 웹 애플리케이션을 구현할 때 결정해야 할 것들이 많다. 기본적으로, 어떤 데이터 포맷을 사용할 것인지를 결정해야 한다. 나는 이 글을 통해서, 그러한 결정들을 내리는데 참조할 수 있는 몇 가지 제안을 했었다. 앞서 언급한 내용을 다시 반복하자면,

  • 데이터에는 JSON
  • 문서에는 XML (XHTML)
  • 신디케이션에는 Atom (Atom Publishing Protocol 지원)
공유 사이트...

digg Digg
del.icio.us del.icio.us
Slashdot Slashdot

여러 가지 대안들과 선택 원칙들을 설명했다. 물론, 모든 사안들 마다 특성을 갖고 있고, 예외는 어디에나 존재하기 때문에 어떤 것도 단정지을 수는 없다. 이 가이드라인이 나에게는 잘 맞았던 것처럼, 여러분에게도 도움이 되길 바란다. 의견이나 제안 사항이 있는 경우, 언제라도 환영한다.

기사의 원문보기



참고자료

교육

제품 및 기술 얻기

토론


필자소개

Photo of Dethe Elza

Dethe Elza는 http://livingcode.org/에서 적극적으로 활동하고 있다. 제안이나 권고 사항은 delza@livingcode.org로 보내기 바란다.


Photo of David Mertz

http://gnosis.cx/publish/를 방문하면 David Mertz(mertz@gnosis.cx)의 열정의 일면을 볼 수 있다. 어떤 제안이나 권고도 환영한다. http//gnosis.cx/TPiP/에서 그의 저서 "Text Processing in Python"을 만나볼 수 있다.



출처 : IBM developerworks
:
Posted by 뽀기

HTTP 상태 코드, 준비 상태, XMLHttpRequest 객체의 이해

developerWorks

 

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

 

 



난이도 : 초급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2006 년 6 월 12 일
2006 년 12 월 12 일 수정

많은 웹 개발자들에게 간단한 요청과 응답을 받는 것은, 사실 필요로 하는 전부이기도 합니다. Ajax를 마스터하고자 하는 개발자들에게는 HTTP 상태 코드, 준비 상태, XMLHttpRequest 객체에 대한 완벽한 이해가 필요합니다. 이 글에서는 다양한 상태 코드들을 보여주고 브라우저가 이를 핸들링하는 방법을 설명합니다.

시리즈 소개


지난 글에서는 , XMLHttpRequest 객체에 대해 구체적으로 소개했다. 이것은 서버측 애플리케이션이나 스크립트에 대한 요청을 핸들하고, 서버측 컴포넌트에서 리턴 데이터를 처리하는 Ajax 애플리케이션의 주요 특징이다. 모든 Ajax 애플리케이션은 XMLHttpRequest 객체를 사용하기 때문에 Ajax 애플리케이션의 작동은 여기에 얼마나 익숙해지냐에 달려있다.

이번에는 지난 글에서 다루었던 기초를 넘어서 요청 객체의 세 가지 핵심 부분들에 대해 자세히 설명하겠다.

  • HTTP 준비 상태
  • HTTP 상태 코드
  • 요청 유형들

이들 각각은 요청이라는 배관의 일부로 간주된다. 결국, 작은 상세가 이러한 주제들에 대해 기록된다. 하지만 Ajax 프로그래밍을 염두하고 있다면 준비 상태, 상태 코드, 요청에 익숙해 져야 한다. 애플리케이션에서 무엇인가 잘못되고 있다면 준비 상태, HEAD 요청을 하는 방법, 또는 400 상태 코드가 의미하는 것이 무엇인지를 이해하면 5분의 디버깅으로 끝낼 수 있거나 5시간 동안 좌절과 혼돈 속에서 방황할 수 있다.

XMLHttpRequest 또는 XMLHttp: 또 다른 이름의 장미

Microsoft™와 Internet Explorer는 Mozilla, Opera, Safari, 비 Microsoft 계열 브라우저에서 사용되는 XMLHttpRequest 객체 대신 XMLHttp 라는 객체를 사용한다. 단순하게 하기 위해서 이 두 가지 객체 모두 XMLHttpRequest로 칭하기로 한다. 웹을 검색하다 보면 이런 경우가 비일비재 하고 마이크로소프트도 Internet Explorer 7.0의 요청 객체의 이름으로 XMLHttpRequest를 사용하고 있다. (Part 2 참조)

HTTP 준비 상태 먼저 보도록 하자.

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"라는 숫자에서 짐작하듯 여러 가지 다른 준비 상태들이 있다.(참고자료)

  • 0: (open()을 호출하기 전에는) 요청이 초기화 되지 않는다.
  • 1: (send()를 호출하기 전에는) 요청은 설정은 되지만 보내지지 않는다.
  • 2: 요청이 보내지고 처리 중에 있다. (이 시점에서 응답에서 콘텐트 헤더를 얻을 수 있다.)
  • 3: 요청이 처리 중에 있다. 부분적인 데이터를 응답에서 사용할 수 있지만 서버는 이 응답으로는 종료되지 않는다.
  • 4: 응답이 완료된다. 서버의 응답을 받고 이를 사용한다.

Ajax 프로그래밍의 기초 이상으로 넘어가고 싶다면 이러한 상태 뿐만 아니라 이들이 언제 발생하고 어떻게 사용하는지에 대해 알아야 한다. 우선, 가장 중요한 것은 어떤 요청 상태가 될 것인지를 배워야 한다. 이는 별로 기분 좋은 일이 아니고 몇 가지 특별한 경우가 포함되어 있다.

숨어있는 준비 상태

readyState 0 (readyState == 0)으로 표시되는 첫 번째 준비 상태는 초기화 되지 않은 요청을 나타낸다. 요청 객체에 대해 open()을 호출하면 속성은 1로 설정된다. 대부분 요청을 초기화 하면서 open()을 호출하기 때문에 readyState == 0을 보는 일은 드물다. 더욱이 초기화 되지 않은 준비 상태는 실제 애플리케이션에서는 쓸모 없다.

Listing 2를 보면 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은 이 애플리케이션을 실행한 결과이다.


그림 1. 준비 상태 0
A ready state of 0
0이 4와 같을 때

다중 JavaScript 함수들이 같은 요청 객체를 사용하는 경우, 그 요청 객체가 사용되고 있지 않다는 것을 확인하기 위해 준비 상태 0을 확인하면 문제가 많아질 수 있다. readyState == 4는 완료된 요청을 나타내기 때문에, 4로 설정된 준비 상태인 채로 사용되지 않은 요청 객체를 종종 보게 된다. abort()이라고 하는 요청 객체를 리셋하는 함수가 있지만 이는 여기에 사용하는 것이 아니다. 다중 함수들을 사용해야 한다면 다중 함수에 객체를 공유하는 것 보다 각 함수용 요청 객체를 생성 및 사용하는 것이 낫다.

분명히 이것은 좋지 않다. open()이 호출되지 않았다는 것을 확인해야 한다. 실제 Ajax 프로그래밍에서 이러한 준비 상태의 유일한 사용은 다중 함수들에 같은 XMLHttpRequest 객체를 사용하여 다중 요청을 만드는 경우이다. 그러한 상황에서, 여러분은 요청 객체가 새로운 요청을 만들기 전에 초기화 되지 않은 상태(readyState == 0)에 있다는 것을 확인해야 한다. 이로서 또 다른 함수가 동시에 객체를 사용하는 것을 방지할 수 있다.

진행중인 요청의 준비 상태 보기

0 준비 상태 외에 요청 객체는 전형적인 요청 응답에서 또 다른 준비 상태를 경험하게 된다. 그리고 마지막으로는 준비 상태 4로 끝난다. 이 때는 대부분의 콜백 함수에서 if (request.readyState == 4)가 된다. 서버가 완료되고 웹 페이지를 업데이트 하거나 서버에서 받은 데이터를 기반으로 액션을 취하는 시기이다.

프로세스를 실제로 보는 것은 간단하다. 준비 상태가 4 라면 콜백에서 단순히 코드를 실행시키는 것 대신 콜백이 호출될 때 마다 준비 상태를 출력한다.(Listing 3)


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이다.


그림 2. 준비 상태 1
A ready state of 1

코드를 직접 실행해 보라. 웹 페이지에 넣고 이벤트 핸들러를 활성화 한다. (버튼을 누르거나, 요청을 실행하기 위해 설정하는 모든 메소드를 사용하라.) 콜백 함수는 여러 번 실행될 것이다. 요청의 준비 상태가 변할 때 마다 각 준비 상태에 대한 경고를 보게 된다. 이는 각 단계를 통해 요청을 따라가는 최상의 방법이다.

브라우저 차이

이 프로세스에 대해 기본적인 개념이 쌓였다면 여러 가지 다양한 브라우저에서 웹 페이지로 액세스 해보라. 준비 상태가 처리되는 방식에 차이가 있을 것이다. 예를 들어, Firefox 1.5에서, 준비 상태는 다음과 같다.

  • 1
  • 2
  • 3
  • 4

요청의 각 단계들이 다 나타나기 때문에 놀랍지도 않다. 하지만 Safari를 사용하여 같은 애플리케이션에 액세스 하면 재미있는 것을 발견하게 된다. 다음은 Safari 2.0.1에서 보게 되는 상태이다.

  • 2
  • 3
  • 4

Safari는 첫 번째 준비 상태를 배제하고 그 이유에 대해서는 자세히 나와있지 않다. 바로 이것이 Safari 방식이다. 또한 중요한 포인트이기도 하다. 서버에서 데이터를 사용하기 전에 요청의 준비 상태가 4라는 것을 확인하는 것은 좋은 생각인 반면 일시적인 준비 상태에 의존하는 코드를 작성하는 것은 다른 브라우저 마다 다른 결과를 얻을 수 있는 확실한 방법이다.

예를 들어, Opera 8.5를 사용할 때 상황은 더 악화된다.

  • 3
  • 4

Internet Explorer는 다음과 같은 상태로 반응한다.

  • 1
  • 2
  • 3
  • 4

요청과 관련하여 문제가 있다면 문제의 원인을 찾을 수 있는 첫 번째 장소이다. 요청의 준비 상태를 보여주는 경고를 추가하여 상황이 정상적으로 돌아가는지를 확인할 수 있다. Internet Explorer와 Firefox 모두 테스트 하면 네 개의 모든 준비 상태를 얻을 수 있고 각 요청 단계를 검사할 수 있다.

이제는 응답 쪽을 살펴보도록 하자.

응답 데이터

요청 동안에 다양한 준비 상태가 발생할 수 있다는 것을 이해했다면 XMLHttpRequest객체의 또 다른 중요한 부분에 대해 살펴보도록 하자. 바로 responseText 속성이다. 이것은 서버에서 데이터를 얻을 때 사용되는 속성이다. 서버가 요청 처리를 완료하면 그 요청에 응답하는데 필요한 데이터를 요청의 responseText에 둔다. 그런 다음 콜백 함수가 그 데이터를 사용한다.(Listing 1Listing 4 참조)


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) JavaScript 콘솔이 열려있었다면 에러가 생겼을 것이다.


그림 3. 준비 상태 2의 응답 텍스트
Response text with a ready state of 2

준비 상태 3에서, 서버는 responseText 속성에 값을 배치한다.(그림 4)


그림 4. 준비 상태 3의 응답 텍스트
Response text with a ready state of 3

준비 상태 3의 응답은 스크립트 마다, 서버 마다, 브라우저 마다 다르다. 애플리케이션을 디버깅 하는데 매우 유용하다.

안전한 데이터 얻기

모든 문서와 스팩들에서는 준비 상태가 4가 되어야지만 데이터를 안전하게 사용할 수 있다고 나와있다. 나를 믿으라. 준비 상태가 3일 때에도 responseText 속성에서 데이터를 얻을 수 있다. 하지만 여러분의 애플리케이션에서 이것에 의존하는 것은 좋지 않은 생각이다. 준비 상태 3에서 완전한 데이터에 의존하는 코드를 작성하는 것은 데이터가 불완전하다는 증거이다.

준비 상태가 3일 때 사용자에게 피드백을 제공하는 것이 좋은 생각이다. alert() 같은 함수를 사용하는 것은 좋지 않다. Ajax를 사용하고 사용자와 경고 다이얼로그 박스를 차단시키는 것은 좋지 않지만 준비 상태가 변할 때 마다 폼이나 페이지에 대한 필드를 업데이트 할 수 있다. 예를 들어, 프로그레스 인디케이터의 넓이를 준비 상태 1에 25 퍼센트, 준비 상태 2에 50 퍼센트, 준비 상태 3에 75 퍼센트, 준비 상태 4에 100 퍼센트를 설정한다.

물론 알다시피, 이 방식은 좋기는 하지만, 브라우저에 의존적이다. Opera에서는 첫 번째 두 개의 준비 상태를 결코 얻지 못하고 Safari는 처음 1 상태를 누락시킨다.

이제 상태 코드에 대해 알아보자.

HTTP 상태 코드

Ajax 프로그래밍 기술에서 준비 상태와 서버의 응답 외에도, Ajax 애플리케이션에 또 다른 고급 레벨을 추가할 수 있다. 바로 HTTP 상태 코드이다. 이 코드들은 Ajax에서는 새우울 것이 없다. 웹에 있는 한 언제나 존재하는 것들이다. 웹 브라우저를 통해 이들을 보았을 것이다.

  • 401: Unauthorized
  • 403: Forbidden
  • 404: Not Found

이 외에도 더 있다.(참고자료) Ajax 애플리케이션에 또 다른 제어 및 응답 레이어를 추가하려면 요청과 반응에 상태 코드를 검사해야 한다.

200: Everything is OK

많은 Ajax 애플리케이션에서 준비 상태를 점검하고 서버 응답으로 온 데이터로 작업하는 콜백 함수를 볼 수 있다.(Listing 6)


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로 설정될 것이다. 결과적으로 사용자는 유효 데이터를 얻지 못하고 JavaScript가 존재하지 않는 서버 데이터를 사용하려고 할 때 에러를 얻게 된다.

서버가 요청을 완료하고 "Everything is OK" 상태 코드를 리턴했다는 것을 확인하는 것은 간단한 일이다. 이 코드는 "200"이고 XMLHttpRequest 객체의 status 속성을 통해서 보고된다. 서버가 요청으로 끝나고 OK 상태를 리포트 했다는 것을 확인하려면 추가 체크를 콜백 함수에 추가한다.(Listing 7)


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 대의 상태 코드이다.

  • 301: Moved permanently
  • 302: Found (요청이 또 다른 URL/URI로 리다이렉션 된다.)
  • 305: Use Proxy (요청은 프록시를 사용하여 요청 받은 리소스에 액세스 해야 한다.)

Ajax 프로그래머가 리다이렉션에 대해 염려 할 필요가 없는 이유가 두 가지 있다.

  • Ajax 애플리케이션들은 특정 서버측 스크립트, 서블릿, 애플리케이션을 위해 작성된다. 그 컴포넌트를 없애거나 다른 곳으로 이동하기 위함이다. 리소스는 변경되었다는 것을 (이미 이동했기 때문에)알고, 요청에서 URL을 변경하고 이러한 종류의 결과를 절대 만나지 않게 된다.
  • 보다 관련성 있는 이유가 있다. Ajax 애플리케이션과 요청들은 샌드박스화 되어있다. Ajax 요청을 만드는 웹 페이지를 공급하는 도메인은 그러한 요청에 응답해야 하는 도메인이다. 따라서 ebay.com에서 공급 받은 웹 페이지는 Ajax 스타일의 요청을 amazon.com에서 실행되는 스크립트에 할 수 없다. ibm.com 상의 Ajax 애플리케이션은 netbeans.org에서 실행되는 서블릿으로 요청할 수 없다.

결국, 요청은 보안 에러를 만들지 않고서는 또 따른 서버로 리다이렉션 될 수 없다. 그러한 경우에, 상태 코드를 전혀 얻을 수 없다. 디버그 콘솔에 JavaScript 에러를 갖게 된다. 따라서 많은 상태 코드에 대해 생각하는 동안 리다이렉션 코드 정도는 무시할 수 있는 것이다.

엣지 케이스와 하드 케이스

이 부분에서, 신참 프로그래머들은 이러한 혼란에 대해 궁금할 것이다. Ajax 요청의 5 퍼센트 정도는 2와 3 정도의 준비 상태와 403 같은 상태 코드로 작동해야 한다. (사실, 1 퍼센트 미만이다.) 이러한 케이스는 중요하고, 엣지 케이스(edge cases)라고 일컬어진다. 이상한 조건들이 부합되는 특수한 상황인 것이다. 일상적인 것은 아니지만 사용자를 곤란에 처하게 한다.

일반적인 사용자들은 애플리케이션이 정확히 작동하는지 매번 잊지만 그렇지 않을 때는 분명히 기억한다. 엣지 케이스와 하드 케이스를 핸들 할 수 있다면 사이트 사용자들을 만족시킬 수 있을 것이다.

에러

일단, 상태 코드 200을 관리했고 300 계열의 상태 코드는 대충 무시하면 다양한 유형의 에러들을 나타내는 400 계열의 코드만 남게 된다. Listing 7을 보면 에러가 처리되는 동안 사용자에게 출력되는 매우 일반적인 에러 메시지라는 것을 알게 된다. 이것은 올바른 방향으로 가는 단계지만 사용자와 프로그래머에게는 매우 쓸모없는 메시지이다.

우선 소실된 페이지에 대한 지원을 추가한다. 이는 제품 시스템에서는 실제로 발생하지는 않지만 스크립트를 이동시키는 테스트나 정확하지 않은 URL을 입력할 때 자주 일어나는 일이다. 404 에러를 보고하면 혼란스러워 하는 사용자와 프로그래머에게 더 많은 도움말을 제공할 것이다. 예를 들어, 서버 상의 스크립트가 제거되거나 Listing 7에서 그 코드를 사용하면 다음과 같은 에러가 생긴다.(그림 5)


그림 5. 일반적인 에러 핸들링
Generic error handling

사용자는 문제가 무엇인지 잘 모른다. 인증에 관련된 것인지, 소실된 스크립트 인지, 사용자 에러인지 알 수 없다. 몇 가지 간단한 코드 추가로 이 에러는 더욱 구체화 된다. Listing 8을 보면 소실된 스크립트와 인증 에러까지 구체적인 메시지와 함께 처리된다.


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와 같은 에러를 보여주지만 이번에는 에러 핸들링 코드가 더 나은 그림을 제공하고 있다.


그림 6. 구체적인 에러 핸들링
Specific error handling

여러분의 애플리케이션에서 인증 때문에 오류가 발생했을 때 사용자 이름과 패스워드를 지우고 에러 메시지를 스크린에 추가하는 것을 고려할 수도 있다. 이와 비슷한 방식이 소실된 스크립트나 다른 400 유형의 에러들을 핸들하는데 사용될 수 있다. 여러분이 어떤 선택을 하든 서버에서 리턴 된 상태 코드를 핸들하는 것으로 시작한다.

추가 요청 유형

XMLHttpRequest 객체를 제어하고 싶다면 HEAD 요청을 레파토리에 추가하라. 이전 두 개의 기사에서 GET 요청을 하는 방법을 설명했다. 앞으로는 POST 요청을 사용하여 서버로 데이터를 보내는 것을 설명하도록 하겠다. 향상된 에러 핸들링과 정보 수집을 위해 HEAD 요청에 대해 배워야 한다.

요청하기

HEAD 요청은 실제로 매우 간단하다. 첫 번째 매개변수로서 "GET" 또는 "POST" 대신 "HEAD"로 open() 메소드를 호출한다.(Listing 9)


Listing 9. HEAD 요청
				
   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 애플리케이션에서 온 응답 헤더를 볼 수 있다.


그림 7. HEAD 요청에서 온 응답 헤더
Response headers from a HEAD request

이러한 헤더들을 개별적으로 사용하여 Ajax 애플리케이션에서 추가 정보나 기능을 제공할 수 있다.

URL 검사

URL이 존재하지 않을 때 404 에러를 검사하는 방법을 이미 보았다. 이것이 일반적인 문제라면, 특정 스크립트나 서블릿이 잠시 동안 오프라인에 있었다면, GET 또는 POST 요청을 하기 전에 URL을 검사해 보는 것이 좋다. HEAD 요청을 하고 콜백 함수에서 404 에러를 검사한다. Listing 11은 샘플 콜백을 보여준다.


Listing 11. URL이 존재하는지 여부 검사
				
   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 요청

HEAD 요청이 유용한 한 가지 부분은 콘텐트 길이나 콘텐트 유형을 검사할 때이다. 요청을 처리하기 위해 많은 양의 데이터를 보낼 것인지, 서버가 HTML, 텍스트, XML 대신 바이너리 데이터를 리턴해야 할지를 결정할 수 있다. (이 세 가지 모두 바이너리 데이터 보다 JavaScript에서 처리하는 것이 더 쉽다.)

이 경우, 적절한 헤더 이름을 사용하고 이를 XMLHttpRequest 객체의 getResponseHeader() 메소드로 보낸다. 따라서 응답의 길이를 알려면 request.getResponseHeader("Content-Length");를 호출한다. 콘텐트 유형을 알려면 request.getResponseHeader("Content-Type");를 사용한다.

많은 애플리케이션에서 HEAD 요청을 하면 어떤 기능도 추가하지 않고 요청의 속도를 늦출 수 있다. (HEAD 요청을 실행하여 응답에 대한 데이터를 얻고 후속 GET 또는 POST 요청을 통해 응답을 실제로 받는다.) 하지만 스크립트나 서버측 컴포넌트에 대해 확실하지 않은 경우 HEAD 요청으로 기본적인 데이터를 받을 수 있다.

결론

Ajax와 웹 프로그래머에게 이 글은 다소 어려울 것이다. HEAD 요청을 하는 것의 가치는 무엇인가? JavaScript에서 리다이렉션 상태 코드를 핸들해야 하는 때는 언제인가? 이 모두 좋은 질문이다. 간단한 애플리케이션의 경우, 이 모든 것은 가치가 별로 없다.

하지만 웹이 단순한 애플리케이션만 수용하는 것은 아니다. 사용자는 점점 고급화 되고 고객들도 강력한 에러 리포팅을 원한다. 관리자 역시 애플리케이션이 조금만 느려져도 해고를 당하게 된다.

간단한 애플리케이션을 넘어 XMLHttpRequest에 대한 이해를 높여야 할 때이다.

  • 다양한 준비 상태를 이해하고 이들이 브라우저 마다 어떻게 다른지를 이해하면 애플리케이션을 빠르게 디버깅 할 수 있다. 준비 상태에 기반하여 창조적인 기능을 만들고 요청자의 상태에 대해 사용자와 고객에게 보고할 수 있다.
  • 상태 코드를 핸들했다면 스크립트 에러, 예기치 못한 응답들, 엣지 케이스들을 다룰 수 있다. 결국, 애플리케이션은 언제나 잘 작동될 것이다.
  • 여기에 더하여 HEAD 요청을 만들고, URL의 존재를 검사하고 파일이 언제 수정되었는지를 파악하고 사용자가 유효 페이지를 얻었는지를 확인할 수 있다면 언제나 최신의 정보와 강력한 기능으로 사용자들을 만족시킬 수 있을 것이다.

이들은 모두 Ajax의 강점이지만 극히 일부분이다. Ajax를 사용하여 애플리케이션이 에러와 문제들을 부드럽게 해결할 수 있는 강력한 토대를 구현한다면 사용자는 여러분의 사이트를 방문할 것이다. 다음 글에서는 보다 더 재미있고 흥미 있는 주제들을 나누도록 하겠다.

기사의 원문보기





위로


다운로드 하십시오

설명 이름 크기 다운로드 방식
Example code for this article wa-ajaxintro3_ajax-xhr_adv.zip 183KB HTTP
다운로드 방식에 대한 정보 Get Adobe® Reader®


참고자료

교육

제품 및 기술 얻기

토론


필자소개

Photo of Brett McLaughlin

Brett McLaughin은 Logo 시절부터 컴퓨터 업계에서 일했다 (작은 트라이앵글 기억하는가?). 최근에 그는 Java 및 XML 커뮤니티에서 인기 저자 및 프로그래머가 되었다. Nextel Communication사에서는 복잡한 기업 시스템 실행에 관한 업무, Lutris Technologies 사에선 애플리케이션 서버를 실지로 작성하는 업무, 최근 O’Reilly Media 사에서는 이와 관련된 중요한 책을 저술, 편집했다. Brett의 최근 저서인 Head Rush Ajax는 Ajax에 관한 혁신적인 연구에 기여, 공동저자인 Eric 및 Beth Freeman과 함께 공동으로 수상했다. 그의 최근 저서인 Java 1.5 Tiger: A Developer's Notebook은 신 자바 기술 버전 상에서 이용 가능한 첫 번째 저서다. Brett의 최신 Java 및 XML은 자바언어에서 XML 기술을 활용한 명백한 업적 중 하나로 남아 있다.

:
Posted by 뽀기