홈페이지 제작 기초 설명-링크

Computer/Web 2.0 2007.10.17 20:41 posted by scDragon
홈짱 닷컴

홈페이지 제작에 대한 기초적인 지식에서 고급 기술까지 간단히 설명이

잘되어있음.

http://www.homejjang.com/
신고

테이블(html의 table 을 칭함)은 이제 목적 이상의 일을 할 필요가 사라졌습니다. 그저 단순한 “표” 를 만들기 위해서 제작된 테이블이 레이아웃, 즉 홈페이지 틀을 잡기위해서 쓰이고 있다니. 그리고 그것이 테이블의 맞는 용도처럼 교육되고 있다니 이제는 슬슬 멈추어야 할때라고 봅니다. 특히 XHTML이 개발되어 나오면서 계층형과 같은 구조로 레이아웃은 디자인되며 디자인이 우선이 아닌 그 안의 내용의 구성을 먼저 생각해야하는 개념 자체의 변화가 찾아오고 있습니다. 아니, 예전 html 생성되었을때의 원래의 개념으로 돌리려고 하고 있습니다. 얼마전 XHTML 2.0(이 포스팅이 쓰여지는 현재는 XHTML 1.1) 이 개발되면서 약간의 내용을 볼수 있었습니다. 지금 쓰여지고 있는 모든 개념적인 HTML이 완전히 변화될듯 만들어 지고 있었습니다. 심지어 DIV만이 레이아웃의 용도가 아닌 SECTION 의 개념도 개발되어 가고 있었습니다. 이대로 가다가는 한국의 웹디자이너, 개발자들은 나중엔 너무나도 큰 공백을 매꾸어야 할 것입니다. 에플과 인텔이 손잡은 일도 그리하고 인터넷 익스플로러 만이 아닌 많은 ‘표준’ 브라우저들이 개발되어 나오면서 이제 한 방법만을, 그리고 한 부류의 소비자, 클라이언트를 겨냥하여 개발하는것은 몇년안에 ‘도박’ 으로 간주 될거 같습니다.

사용전에 개념의 문제

이 시점에서 본론으로 들어가기 전에 다루어야 할 문제가 있습니다. 제가 여러 한국 디자인 홈페이지를 만들어 보고, 보아 왔지만 한국에서 지향하거나 혹은 본의 아니게 지향되어 가고 있는 잘못된 개념이 있습니다. 바로 그 개념의 문제가 바뀌어야 하는데, 제가 잘 설명할수 있을지 모르겠군요.

최대한 말로 설명을 한다면, HTML은 디자인을 나타내는것이 아니라 문서를 유저들이 더 잘 볼수 있게 디자인 할수 있도록 만들어진 것이라는 겁니다. 문서의 개념이 디자인 보다 먼저 오는거 라는 거죠. 디자인이 먼저라면, 지금당장 HTML등을 버리고 플래쉬로 나가는게 더 나은 길일겁니다. 하지만 플래쉬가 그리도 멋지게 활용될수 있으며 여러가지 장점들이 있지만, 웹개발자, 클라이언트들에게 100% 다가가지 못하는 이유는 바로 이 ‘문서’ 의 개념을 잘 포옹하지 못하기 때문입니다. 웹페이지들의 궁극적인 목표는 바로 ‘책’ 처럼 되는 것이다 라고 하는데 이 목적을 채우기엔 역부족인거죠. 이전 HTML 에서도 그것을 잘 충족하지 못하고 오히려 역효과를 낸 것입니다. 해서 이번에 나오는 차기 XHTML은 문서의 개념을 더욱 투명하게 하고 인식을 바로 잡고 쓰기 위하는 목적으로 씌여지는거 같습니다.

문서의 요소로 예를 들어 보겠습니다. 보통 문서를 보면 서론 본론 결론으로 나뉩니다. 그 안에 제목, 문단, 단락 들이 있고, 여러가지 문서의 요소들이 있습니다. 바로 그 요소들을 HTML이 나타내고 싶은겁니다. 보통 볼수 있는 예로

는 바로 그 문단의 시작과 끝을 나타내어 주는것이고(처음 html 할때 젤 싫었던게 엔터치면 곧바로 에디터가 <p>로 인식을 하는거였네요. 무엇인지 모르니 짜증이 날수 밖에요 ^^) 이렇게 쓰이게 되면 HTML은 자동으로 알아서 문단이라고 인식해서 그에 맞추어 출력을 해주게 됩니다.(이런 개념을 잘 이해해서 디자인을 하면 <br>(웹표준에서는 <br /> 이 맞습니다)은 거의 쓰지 않게 됩니다 ^^)

이렇게 HTML의 속성들의 개념을 이해해 가다보면 문서의 틀을 잡아주는것에 대한것, 즉 레이아웃에 대한 속성들도 다루게 되는데 바로 그 문서의 표현을 도와 문단의 너비, 문단의 위치, 모양을 잡아주는 것이 바로 테이블 입니다. DIV 입니다. P(Paragraph, 문단의 첫글자) 속성도 있구요. 아무튼 다시한번 말합니다. 문서의 레이아웃을 돕는 HTML 속성은 DIV 입니다. ^^ DIVISION의 줄임말로 나누다 라는 뜻을 가진 div 인것이죠. 테이블로 레이아웃 사용은 문법 자체가 틀린것이라는 겁니다.

위의 긴 글을 정리하면서 다시 말하자면, HTML의 개념이 바르게 인식이 되어 가는데엔 테이블, 단순히 표를 만드는데 쓰인다는 원래의 목적을 찾아주고 레이아웃은 그에 맞는 속성을 쓰는것이 꼭 다루어져야 한다는 것입니다. 단순히 “DIV가 다루기 쉽잖아요” 가 이유가 아닌(실제로도 테이블보다 훨씬 쉽지만) 목적과 개념 자체에서부터 문제가 된다는 것입니다. 아직 뜨끔 안하셨나요? 이제 슬슬 개념뿐만이 아닌 왜 DIV가 테이블보다 나은지 다루어 보겠습니다.

table 보다 다루기가 쉽다

솔직히 예전에는 테이블이 웹페이지 레이아웃, 틀을 잡는데에 쓰일수 밖에 없었습니다. 그만한 틀을 잡아주는 속성이 약했기 때문입니다. 그리고 그 당시에는 그것이 더 편했고, 깔끔했습니다. 규격이 맞지 않아서 줄이고 늘리고 하는것이 더 문제 였으니까요. 하지만 DIV는 레이아웃을 위한 그 목적으로 씌여진 만큼, 그보다 더 다루기 쉽게, 간편하게 발전 되었습니다. 말도 안된다구요?

보통의 홈페이지 레이아웃으로 예를 들어가면서 보겠습니다.

이런 레이아웃을 만들어 보겠습니다.

  1. 테이블은
    <table>
      <tr>
        <td></td>
      </tr>
      <tr>
        <td></td>
        <td></td>
       </tr>
     </table>

    이렇게 코딩을 합니다. div를 보겠습니다.

    <div></div>
     <div style="float: left;"></div>
     <div></div>

    별로 차이 안나네요.

  2. 그럼 이것에 column 을 2개 더 넣어보겠습니다.
    <table>
      <tr>
        <td colspan=4></td>
      </tr>
      <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
      </tr>
    </table>

    <div></div>
    <div style="float:left;"></div>
    <div style="float:left;"></div>
    <div style="float:left;"></div>
    <div style="float:left;"></div>

    또 약간 비슷합니다.여기서
    이렇게 만들어 볼까요?

  3. 그렇다면 이제 슬슬 테이블은 복잡해 집니다.

    <table>
      <tr>
        <td colspan=4></td>
      </tr>
      <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
      </tr>
      <tr>
        <td colspan=4></td>
      </tr>
      <tr>
        <td colspan=4></td>
      </tr>
      <tr>
        <td colspan=4></td>
      </tr>
      <tr>
        <td colspan=4></td>
      </tr>
    </table>

    div를 보겠습니다.

    <div></div>
    <div style="float:left;"></div>
    <div style="float:left;"></div>
    <div style="float:left;"></div>
    <div style="float:left;"></div>
    <div style="clear:both;"></div>
    <div></div>
    <div></div>
    <div></div>

    div는 간단히 끝이 났네요.

클라이언트가 위의 구성중 column이 많다고 빼달라고 요청합니다. 하나를 빼면,,
테이블로 구성한 코드는 저 위의 4라고 써진거 다 3으로 바꾸어야겠죠. 네. ‘찾기’ 기능을 아주 잘 써야겠습니다. 하지만 div는 어떤가요? float:left 는 align:left 와 같습니다. 왼쪽으로 정렬된것들 마구 넣은 후에 빼고 싶은거 빼고나서 다시 정리된 한칸을 넣을때엔 align 이 적용 안되도록 clear:both; 만 넣으면 됩니다. 개발자 분들, 너무나도 차이나는게 보이시는지요?

테이블은 느리다

다른 이유를 보겠습니다. 너무나도 잘 아는 사실이죠. 테이블의 속도 문제도 꽤나 골치 아픈 문제 입니다.

유저들은 아마도 브라우저창 타이틀 부분에는 웹사이트 제목이 떴는데 하얀 바탕으로 아무것도 나오지 않는 경우를 겪어 봤습니다. 요즘이야 워낙에 속도가 빨라지긴 했지만… 그 이유는 테이블의 속도 문제가 있기 때문입니다. 각각의 테이블이 하나하나의 요소로 받아들이기 때문에 그 테이블이 다 읽히기 전까지는 화면에 읽히지 않습니다. 그 이유 때문에 제목창에는 제목이 뜨는데 화면에는 아무것도 들어오지 않는것처럼 보이는 것이죠. 특히 커다란 포털사이트에 이런 경우가 많습니다. 기다림 후에 나타나는 화면은 이미 로딩이 거의 끝난 상태로 나타나게 되죠. 그와 반대로 div를 사용하게 되면, 틀을 잡아주는 것이기에 틀에 잡힌 텍스틀이 이미 뜬 후에 배경이라던지 이미지가 읽혀지게 됩니다. 유저들에겐 기다림의 지루함이 조금이나마 줄어들게 되는 것입니다. 그러므로 속도의 차이는 없는듯 커다랗게 나는것입니다. 예전 야심만만 이라는 프로그램에서 ‘당신에게 가장 긴 1분은 언제였나?’ 라는 질문에 웹사이트가 로딩되는 그 1분이 길었다 라는 대답이 상당히 많았던 것만 봐도 그 ‘약간’ 의 차이가 유저들에게는 얼마나 큰것인지를 알수 있습니다. 구글의 성공에도 1개의 이미지로 승부하는 속도가 커다란 몫을 했었죠.

table은 유지하기 힘들다

속도 뿐만이 아닙니다. 어찌 유지 하렵니까?
속도는 인터넷을 더 빨리 돌려버리면 된다고 해도, ‘유지’ 의 문제는 어쩔수 없습니다. 상상만 해도 울컥 솟아 오릅니다. 테이블 안에 수도 없이 이루어진 그 얽히고 얽힌 그 문제를 어떻게 유지를 할수 있을지. 새 웹마스터가 와서 웹사이트를 유지하려해도 이해할수 없는 수 많은 테이블의 tr, td 들을 그리고 그 안에 들어있는 테이블들을 어떻게 이해를 해서 하는지. 아직도 하고 계신분들에게 경의를 표합니다. 이럴때에 가끔은 프리렌서가 좋다는 생각을 합니다. :) 이 말도 안되는 것들만 보아도 왜 이렇게 되어야 하는지 궁금할 뿐입니다. div도 사이트가 클수록 유지하기에 복잡하지 않냐 라는 반문을 하신다면 div를 써보지 않으신 분들입니다. 각각의 레이아웃 div에는 이름을 주어서 css로 간단히 조절이 가능하기에 각각의 이름들만 알아도 새로운 css를 써버려서 내용은 그대로이되 모든 디자인이 바뀌어 버리는 놀라운 일을 할수가 있습니다. ‘스킨’ 의 개념이 보통의 html문서에서 이용되는 것입니다. 이에대해 아시는 분들은 아시지만 젠가든 이라는 곳이 그 예들을 보여줍니다. 내용은 같지만 수많은 유저들이 자신들의 css를 제출해 뽑히면 그 사이트의 디자인css 파일만을 바꾸어 보여줌으로써 확연히 다른 사이트로 변모하는것을 보면 확실히 이해가 가실겁니다.

CSS3?

이제 곧 나오게 될 CSS3를 살펴보니 레이어들 사이에도 이제 x, y 좌우의 위치 뿐만이 아닌 진정한 케스케이딩, 차곡차곡 쌓인 문서들 처럼 앞, 뒤 의 z 위치를 정할수 있게 되어 있습니다. 아무리 테이블이 레이아웃에 좋다고 우겨도 CSS3가 출시되는 날 부터는 ‘바보’로 취급받게 될것입니다. 투명한 PNG의 사용도 가능케 했으니, 그림자 div 위에 문서 1 div을 놓고, 그 위에 문서 2 div 를 놓고 겹치게 해 놓았다고 생각해 보십시오. 그리고 다음 페이지로 넘어갈때엔 그저 CSS의 Z 속성만 바꾸어 주면 순간에 문서 1이 2의 위에 올라가게 되니, 이거 언제 css3이 출시되나 기다려 지지 않을수가 없습니다.

table은 이제 쉬어야 할때

테이블은 이제 슬슬 좀 쉬어야 할때 입니다. 너무 큰일을 감당케 했습니다. 반대로 원래 그 일을 해야할 div를 너무 놀게했습니다. 위에서 주욱 길게 설명을 했듯이 테이블의 노동착취는 그만하시고 공평한 일자리를 주어야 할 때가 아닐까 생각합니다. 아니 그래야 합니다. 그것이 미래를, 앞을 바라보는 선경지명이겠습니다.

계속 실제로 표준에 다가갈수 있도록 XHTML과 더불어 CSS를 다루도록 하겠습니다.

테이블은 이제 그만 쉬어야 할때 후속편 읽기

신고

디바이스 드라이버란?

Computer/Linux 2007.10.12 02:40 posted by scDragon
디바이스 드라이버란

디바이스 드라이버란 응용 프로그램이 HARDWARE를 제어할수 있도록 인터페이스를 제공해주고 프로그래머로 하여금 HARDWARE를 인식하지 않고도 프로그램을 작성할수 있도록 하는 것이다. 그래서 작성 방법이 까다롭고 일반 프로그래밍하고는 많이 다를뿐더라 API함수 또한 많이 다르다. 굳이 다른 것과 비교를 한다면 그 형식에 있어서는 DLL 프로그래밍과 그래도 가장 많이 유사하지 않을까 싶다.
아래 그림 1을 보면 드라이버가 OS에서 위치하는 곳을 나타내어는데 커널모드에 I/O 매니저 아래에 위치하고 있다. 이러한 위치때문에 설치 뿐만 아니라 프로그램 작성하는것도 까다롭다. 이처럼 커널모드(Intel에서는 특권모드, Ring 0)에서 동작되기 때문에 혹 조금이라도 잘못 작성되면 전체 시스템에 영향을 주기 때문에 신중히 작성되어야 한다.


그림 1 윈도우 2000 의 구조 - 그림수정

디바이스 드라이버의 종류

SYS
MS-DOS시절에는 컴퓨터상의 하드웨어들의 드라이버 코드들이 그냥 ROM-BIOS(Basic Input Output System)와 DOS내부에 있었다. 시스템 소프트웨어들은 소프트웨어 인터럽트(Int 10h, Int 13h, Int 16h 등)라는 방식을 통하여 BIOS로부터 각종 서비스를 받아서 간접적으로 컴퓨터 하드웨어 장치들을 제어할 수 있게 되는 것이다. BIOS는 또한 자체적으로 하드웨어 인터럽트들을 처리한다. DOS는 이러한 BIOS의 서비스들을 토대로 하여 장치들을 제어하며, 자신의 위에 있는 계층의 소프트웨어인 도스 애플리케이션들에게 장치를 다룰 수 있도록 API(Application Programming Interface)를 제공한다. BIOS는 전적으로 컴퓨터의 마더보드와 마더보드에 기본적으로 연결되어 있는 장치들을 시스템 버스들을 통하여 제어하고, 그러한 기능들을 서비스하는 것만을 목적으로 하지만, DOS의 경우에는 여러 가지 주변장치들이 확장될 것을 고려하여 도스용 디바이스 드라이버를 만들 수 있도록 발전되어졌다. 이러한 도스용 디바이스 드라이버가 바로 Config.sys 파일에 적어 넣는 "device=xxx.sys"에서의 xxx.sys라는 파일들인 것이다.
DRV
윈도우의 초창기 버전에서는 BIOS와 DOS가 여전히 핵심적인 소프트웨어의 코어였다. 그러나 Intel사에서 80286 마이크로 프로세서가 개발되어 나오면서부터 윈도우는 운영체제로서 도스의 몫들을 가져가기 시작했다. 80286 프로세서는 8086과의 호환성을 위한 real-mode외에도 286 protected-mode를 제공하였는데, 286보호모드에서는 16MB까지의 물리적인 메모리에 접근할 수 있었다. 그 당시 윈도우는 프로세서의 모드를 리얼모드와 보호모드를 오가면서 윈도우용 애플리케이션에서도 BIOS서비스와 DOS서비스들을 사용할 때에만 리얼모드로 잠시 변화되어 수행된 후 다시 보호모드로 돌아오는 동작 메커니즘을 가지고 있었다. 이러한 방식을 286 일반모드(standard-mode)라고 부른다. 하지만 키보드나 마우스 입력과 같이 항상 사용하여야 하는 하드웨어 서비스를 받기 위하여 CPU의 모드를 계속 전환시키는 것은 너무나도 무모한 방법이므로, 당시의 윈도우는 해결책을 찾아보게 되었고 해결책은 보호모드에서 동작하는 디바이스 드라이버를 만들고 그것이 동작할 수 있도록 해주는 것이었다. 그것이 바로 *.drv라는 이름의 파일들이다.
VXD
Intel사의 80386 이상의 프로세서들은 386 강화모드(Enhanced mode)를 지원한다. 이로써 Windows는 실제의 DOS없이 CPU차원에서 도스를 흉내내기(emulate)함으로써 도스용 애플리케이션들을 윈도우에서 실행시킬 수 있게 되었다. Windows 95/98에서 흔히 보는 '도스창'이 바로 V86모드의 가상화(virtualize)된 도스이며, 동시에 여러개를 조그만 창으로 띄울 수 있다.
Windows95/98에서부터 OS는 가상기계(VM : Virtural Machine)를 다루는 본격적인 운영체제가 되었으며, 이러한 VM의 개념은 원래의 미니컴퓨터 이상의 UNIX에서 온 것이다. VM은 각각 자기 혼자 컴퓨터의 모든 자원, 예를 들면 키보드, 모니터, 마우스 등을 혼자서 차지하고 있는 것처럼 애플리케이션을 속이는 완벽한 환경이다. 여기서부터 윈도우 디바이스 드라이버에 "가상"이라는 말이 들어가게 된다. 왜냐하면 실제로 하나뿐인 장치가 각각의 VM마다 개별적으로 각각 존재하는 것처럼 속여주는 책임을 가상 디바이스 드라이버가 하게 되었기 때문이다. 이때부터 디바이스 드라이버의 확장자는 *.vxd가되었고 그 역할도 단순히 하드웨어를 제어하는 것을 넘어서서 VM을 관리하고 결국은 OS의 속모습이 되어버린 것이다. MOUSE.drv와 같은 구식 윈도우 드라이버들은 Windows98에서는 일종의 껍데기에 불과하며, 실제의 모든 동작은 VxD에서 하고 있다. Windows98에서는 이러한 *.drv의 디바이스 드라이버들을 Ring 3 DLL드라이버(Ring 3 DLL Driver)라고 부른다. 즉, *.drv가 Windows98에서 원래의 디바이스 드라이버 역할을 한다기보다는 일종의 Windows 3.1의 16비트 DLL과 똑같은 역할을 하고 있기 때문이다. 실제로 OS는 단지 VxD의 로우레벨 서비스를 DLL등으로 껍데기를 하나 씌워서 편리한 인터페이스를 구축하는 역할이 되어버렸고, 실제 모든 OS의 서비스는 사실상 VxD가 해주어야만 한다.
VMM의 역할은 사용자를 위해 컴퓨터 시스템 전체를 가상화(Virtualize)하는 것이다. 즉, 운영체제의 진정한 임무인데, 컴퓨터 시스템은 수많은 요소들로 구성되어 있으며, 수많은 기능들을 체계적으로 지원해 주어야만 한다. 이 모든 기능을 VMM이라는 하나의 실행파일에 모두 넣어둘 수는 없으며 VMM은 최종적인 모든 권한들을 가지고서 자신의 하부조직인 VxD를 조종하는 것이다.
VMM과 VxD
시스템에 어떤 주변장치를 새로 달게 되면 그 주변장치를 위한 VxD들이 추가된다. 참고로 윈도우 95/98의 VxD 개발이 가장 힘이 들고 어렵고 그 다음 윈도우 NT, 리눅스(UNIX 개열 모두)로 갈수록 디바이스 드라이버 개발이 쉬운데, 이것은 각각의 운영체제의 탄생배경과 성장과정이 다르기 때문에 나타나는 특성이다. 윈도우 95/98이 가장 난해하고 지저분한 이유는 이것이 PC의 성장과정과 함께 도스 시절부터 Windows 3.1, DOS Extender, Windows for Workgroup, Netware 등의 파란만장한 변천과정 속에서 이전 모습들의 꼬리뼈들을 계속 몸에 지니고 있기 때문이다. 또한 윈도우 98은 윈도우 2000에서 윈도우 NT와 하나로 합쳐지는 중간단계에서 윈도우 NT의 여러 가지 특성들도 갖추도록 노력을 많이 하였기 때문에, PC와 운영체제의 역사를 모르는 경우에 윈도우 98의 내부 구조가 왜 그런 모습이 되었는지 이해 하기가 어려울 수 밖에 없다.

WDM
윈도우 2000으로 넘어오면서 새로이 생겨난 모델(Windows Driver Model)이다. 윈도우 2000에서는 크게 사용자 모드 드라이버와 커널 모드 드라이버를 제공한다. 사용자 모드 드라이버는 사용자 모드에서 동작하는 시스템 레벨의 코드이다. 커널 모드 드라이버는 커널 모드에서 동작하는 코드이며 직접적으로 하드웨어를 컨트롤한다.
커널 모드 드라이버는 아래 그림 2에서 보는거와 같이 일반 Legacy(NT형태) 드라이버와 WDM 드라이버가 있으며 각각 상위레벨, 중간레벨, 하위레벨 드라이버로 분류된다. WDM과 NT형태의 드라이버는 본질적으로 제작방법은 같지만 작성방법에 있어서 차이가 있다. WDM은 장치가 추가되거나 제거될때 PnP관리자가 이를 알려주므로 이에 대한 적절한 설치파일(INF)을 통해 설치한다.


그림 2 드라이버 종류

상위 레벨 드라이버의 경우는 하위 레벨 드라이버에 의해서 이미 하드웨어가 동작하거나 서비스 되고 있고 추가적으로 새로운 형태의 추상화를 제공하고자 할때로 파일 시스템 드라이버가 이에 해당한다.
중간 레벨 드라이버는 디스크 미러, 클래스, 미니, 필터 드라이버들을 말하며 이러한 드라이버들은 상위와 하위 드라이버 사이에 위치하여 두 드라이버 사이에 요청을 전달하거나 추가적인 작업을 수행한다.

클래스 드라이버 : 코드 재사용에 있어 좋은 방법이다. 특정 타입의 드라이버들이 공통적인 부분들을 일반 클래스 드라이버에 위치시키는 것이다.

미니 드라이버 : 제조업체 전용 드라이버 혹은 장치 전용 드라이버들은 클래스 드라이버와 연동되는 미니 드라이버 형태로 작성된다.

필터 드라이버 : 이미 존재하는 드라이버들의 요청을 중간에서 가로채어 특별한 펑션 드라이버 : WDM 드라이버 내에서는 중간레벨의 드라이버는 펑션 드라이버로 구성될수 있다. 이는 DDK내에서 클래스나 미니 드라이버로 불릴수도 있다.


그림 3 Layered 드라이버 구조

하위 레벨 드라이버는 SCSI Host Bus Adapter와 같은 하드웨어 버스를 위한 컨트롤러 드라이버들을 말한다. 이러한 드라이버들은 HAL과 통신하거나 하드웨어와 직접 통신하기도 한다.
그외 특별한 드라이버들 - 비디오 드라이버, 프린터 드라이버, 멀티미디어 드라이버, 네트워크 드라이버
비디오 드라이버는 일반적인 I/O 드라이버와 구조가 다르다. I/O관리자와 직접 통신을 하지 않고 GDI라는 커널을 통해 통신을 한다. 그림에서와 같이 칩셋(nvida, ati등)에서 비디오 미니 포트 클래스 드라이버를 제공하면 그래픽 카드 제조사등에서는 디스플레이 드라이버를 제작한다. GDI(상위 레벨 드라이버라 할수 있겠다)에서는 그래픽 카드 제조사에서 제공한 디스플레이 드라이버의 함수(DDI-Device Driver Interface라고 알려진 Drv접미사를 가진 함수들)를 호출한다. 또한 디스플레이 드라이버에서는 GDI안에 구현되어 있는 일반 그래픽 라이브러리(Eng라는 접미사를 가진다)를 호출한다. 음영처리가 되지 않은 것이 만들어야 하는 드라이버이다.

비디오 드라이버 구조

프린터 드라이버는 Spooling이라는 구조로 이루어져 있다. 응용 프로그램에서 보내진 데이타는 GDI를 거쳐 Printer Driver와 상호작용을 통해 만들어진 Spool Data(Raw)를 Spooler를 통해 관리(프린팅 작업을 Thread를 통해 CPU가 바쁘지 않은 시간을 통해 내보냄으로써 프린팅을 하면서도 다른 작업을 할수 있도록 했고 네트워크상에 연결되어 있는 프린터에게도 데이타를 보낼수 있다) 되어져 결국 최종 Print Provider에게 보내진다.

프린터 드라이버 구조

멀티미디어 드라이버는 사운드카드나 TV튜너 같은 멀티미디어 디바이스를 지원하기 위하여 커널 스트리밍이라는 필터와 펑션 드라이버로 구성된 모델을 제공한다.
네트워크 드라이버는 NDIS(Network Driver Interface Specification)는 NIC 드라이버를 지원하기 위한 라이브러리를 제공한다. NIC 제조사는 하드웨어를 제공하기 위해 NDIS 미니 포트 드라이버를 작성, 제공한다. 그 상위 레벨로 NDIS 중간레벨 드라이버와 NDIS 프로토콜 드라이버가 있다.


일반적인 디바이스 드라이버 프로그램 구조
드라이버는 일반적인 프로그램과 달리 OS(I/O 관리자)에의해 호출(CallBack)되는 함수들의 집합으로 구성되어 있다. 그러므로 개발자는 각각 호출될때 맞는 기능을 구현하면 된다. 호출되는 경우는 드라이버가 로드될때나 언로드 될때, 삽입 또는 제거될때, 응용프로그램에서의 호출, 인터럽트등 여러가지 경우가 있다.



그림 4 드라이버의 함수

DriverEnty는 그 드라이버의 초기화를 수행하고 DRIVER_OBJECT라는 구조체에 일련의 콜백 루틴(Create, Close, PnP, Read, Write등을 처리하기 위한 함수)에 대한 포인터등을 저장한다. 일반 NT형(Legacy) 드라이버는 이곳에서 디바이스 인스턴스를 생성하지만 WDM에서는 AddDevice에서 수행해야 한다. I/O 관리자가 드라이버를 처음 로드할때 DriverEntry루틴을 호출한다.

AddDevice는 PnP를 지원하며 통보가 왔을 경우 실제 디바이스 인스턴스를 생성한다.

Reinitialize는 DriverEntry루틴에서 초기화 작업을 완전히 수행하지 못하는 경우가 있다. 이런 경우는 초기화 되지 못하는 다른 드라이버의 종속적인 경우등 여러가지 경우가 있는데 DriverEntry루틴에 Reinitialize루틴을 지정함으로 다시 초기화 할수 있도록 한다.

Unload는 메모리로 부터 제거하기 전에 I/O관리자로 호출된다. 드라이버의 자원 및 디바이스 이름을 제거하는 과정을 수행하여야 한다. WDM 드라이버에서는 RemoveDevice에서 수행한다.

Shutdown은 컴퓨터가 꺼질때 Unload가 불리지 않기 때문에 컴퓨터가 꺼지기 이전에 해야 할 특별한 작업을 할수 있도록 한다.

Open, Close에 디바이스 드라이버를 사용하기 위해 Win32에서 CreateFile, CloseHandle등에 대한 요청을 처리하는 루틴이다. DriverEntry에서 DRIVER_OBJECT의 MajorFunction에 IRP_MJ_CREATE, IRP_MJ_CLOSE등으로 함수의 포인터를 저장한다.

Read, Write에 디바이스 드라이버에 Win32의 WriteFile, ReadFile등에 대한 요청을 처리하는 루틴이다. DriverEntry에서 DRIVER_OBJECT의 MajorFunction에 IRP_MJ_WRITE, IRP_MJ_READ등으로 함수의 포인터를 저장한다.
일반 프로그램에서 디바이스 드라이버를 사용하기 위해서는 CreateFile에 디바이스명을 인자로 주어 생성한 Handle로 WriteFile, ReadFile의 win32 명령어를 사용한다. CreateFile이란 말그대로 파일을 만드는것이 아니라 장치에 대한 핸들을 얻는것이다.

Start I/O는 데이타 전송 시점에 I/O관리자에 의해 불려진다. 모든 I/O 요청을 큐잉하고 직렬화(I/O 요청을 차례차례 순서대로 진행시킨다)한다.

ISR는 디비이스가 인터럽트를 생성할때 마다 이 루틴을 호출하게 된다. 이곳에서 너무 많은 시간을 끌면 안된다. 되도록 최소한 코드로 수행되어야 하고 처리해야할 작업이 더 남았다면 DPC를 스케쥴링 한다.

DPC는 드라이버의 동작을 완료하기 위하여 이 루틴을 제공한다. 자원을 해제하거나 에러를 리포팅하거나 I/O 요청의 완료를 지정하여 디바이스의 다음 동작을 수행한는등의 작업을 한다.
신고

3장에서는 AJAX 의 여러가지 기본적인 특성에 대해서 공부하였다. 또한 아주 간단한 예제를 통해서 그 특징들이 어떻게 적용되는지도 살펴보았다. 이번 4장에서는 좀더 발전하여 실제적으로 쓰임새 측면에서 AJAX 를 다루어 보기로 한다.


실용적인 측면에서 AJAX 를 어떻게 활용할 수 있을지 살펴보자. 첫번째로 사용자가 브라우저의 입력폼에 값을 입력했을때 그 값을 검증하는 기능을 구현해볼 것이다. 이번 예제에서는 간단하게 날짜를 입력했을때, 그 값을 맞게 입력했는지 검증하는 로직을 AJAX 로 적용해 볼것이다. 여러 프로젝트를 수행한 결과 이런 폼 입력값 검증작업의 경우 간단한 것은 자바스크립트를 사용하면 아주 쉽게 끝나버린다. 오히려 서버의 로직을 거치지 않으므로 서버에 부담도 없다. 하지만 서버의 DB 나 혹은 XML 에 담겨진 데이타와 비교 혹은 검증을해야만 하는 경우엔 어쩔 수 없이 서버의 비지니스 로직을 거쳐야 한다. 하지만 이럴경우 코아 비지니스 로직을 수행하기 전에 입력폼의 값을 검증하는 로직을 추가로 코딩해야 해야만 한다. 또한 입력값이 잘못 되었다면 어떤입력값이 어떻게 잘 못되었다고 친절하게 알려주는 새로운 페이지로 이동해야 한다거나, 쉽게는 팝업처리를 할 수도 있다. 이렇게 되면 사용자는 또다시 처음부터 다시 입력을 해야하는 고생을 해야 한다.


이럴경우 AJAX 를 이용한 폼 입력값 검증작업을 해주면 비지니스 로직을 개발할때는 코어 로직만 작성하면 되고, 고객 입장에서는 입력한 값이 어디가 어떻게 잘못된건지 바로 알수 있기때문에 아주 유용한 방법이 될 것이다.


그럼 소스를 살펴보자. 코드가 길어지는 것을 방지하기 위해 되도록 소스는 단순하게 짜여졌다.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <title>Using Ajax for validation</title>
 
    <script type="text/javascript">
        var xmlHttp;

        function createXMLHttpRequest() {
            if (window.ActiveXObject) {
                xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            else if (window.XMLHttpRequest) {
                xmlHttp = new XMLHttpRequest();               
            }
        }

        function validate() {
            createXMLHttpRequest();
            var date = document.getElementById("birthDate");
            var url = "ValidationServlet?birthDate=" + escape(date.value);
            xmlHttp.open("GET", url, true);
            xmlHttp.onreadystatechange = callback;
            xmlHttp.send(null);
        }

        function callback() {
            if (xmlHttp.readyState == 4) {
                if (xmlHttp.status == 200) {
                    //var mes = xmlHttp.responseXML.getElementsByTagName("message")[0].firstChild.data;
     var mes = xmlHttp.responseXML.getElementsByTagName("message")[0].firstChild.data;
                    var val = xmlHttp.responseXML.getElementsByTagName("passed")[0].firstChild.data;
                    setMessage(mes, val);
                }
            }
        }
       
        function setMessage(message, isValid) {           
            var messageArea = document.getElementById("dateMessage");
            var fontColor = "red";
           
            if (isValid == "true") {
                fontColor = "green";               
            }
            messageArea.innerHTML = "<font color=" + fontColor + ">" + message + " </font>";
        }

    </script>
  </head>
  <body>
    <h1>Ajax Validation Example</h1>
    Birth date: <input type="text" size="10" id="birthDate" onchange="validate();"/>
    <div id="dateMessage"></div>
  </body>
</html>


<validateion.html 전체 소스 코드>

 

 

package ajaxbook.chap4;

import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import javax.servlet.*;
import javax.servlet.http.*;

public class ValidationServlet extends HttpServlet {   
   
    /** Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        PrintWriter out = response.getWriter();
       
        boolean passed = validateDate(request.getParameter("birthDate"));
        response.setContentType("text/xml");
        response.setHeader("Cache-Control", "no-cache");
        String message = "You have entered an invalid date.";
       
        if (passed) {
            message = "You have entered a valid date.";
        }
        out.println("<response>");
        out.println("<passed>" + Boolean.toString(passed) + "</passed>");
        out.println("<message>" + message + "</message>");
        out.println("</response>");
        out.close();
     }
   
    /**
     * Checks to see whether the argument is a valid date.
     * A null date is considered invalid. This method
     * used the default data formatter and lenient
     * parsing.
     *
     * @param date a String representing the date to check
     * @return message a String represnting the outcome of the check
     */
    private boolean validateDate(String date) {
       
        boolean isValid = true;
        if(date != null) {
            SimpleDateFormat formatter= new SimpleDateFormat("MM/dd/yyyy");
            try {
                formatter.parse(date);
            } catch (ParseException pe) {
                System.out.println(pe.toString());
                isValid = false;
            }
        } else {
            isValid = false;
        }
        return isValid;
    }
}

<ValidationServlet.java 의 전체 소스 코드>



이번 예제는 사실 설명이 불필요할 정도로 매우 간단하다. 브라우저에서는 날짜를 입력받는 필드만 있다. 날짜를 입력하고 난후 포커스가 빠져나가면 입력했던 날짜는 서버 프로그램으로 전송되고 입력된 값이 날짜 형식에 적절한지 판단해서 다시 브라우저로 보내주는 형식이다.




<날짜를 잘못 입력했을 경우>

 



<날짜 형식으로 입력했을 경우>

출처 : Tong - bassdot님의 AJAX통

신고

AJAX 강의 3장 - 서버와 통신하기

Computer/Web 2.0 2007.10.04 10:42 posted by scDragon

3.1 responseText 속성을 이용한 단순 문자열 다루기

 

 2장에 이어 이번에는 innerHTML 속성을 이용한 샘플 코드를 살펴보자.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Using responseText with innerHTML</title>
   
<script type="text/javascript">
var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function startRequest() {
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", "innerHTML.xml", true);
    xmlHttp.send(null);
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            document.getElementById("results").innerHTML = xmlHttp.responseText;
        }
    }
}
</script>
</head>

<body>
    <form action="#">
        <input type="button" value="Search for Today's Activities" onclick="startRequest();"/>
    </form>
    <div id="results"></div>
</body>
</html>


<3-1 innerHTML.html 의 내용>



<table border="1">
    <tbody>
        <tr>
            <th>Activity Name</th>
            <th>Location</th>
            <th>Time</th>
        </tr>
        <tr>
            <td>Waterskiing</td>
            <td>Dock #1</td>
            <td>9:00 AM</td>
        </tr>   
        <tr>
            <td>Volleyball</td>
            <td>East Court</td>
            <td>2:00 PM</td>
        </tr>   
        <tr>
            <td>Hiking</td>
            <td>Trail 3</td>
            <td>3:30 PM</td>
        </tr>   
    </tbody>
</table>

<3-2 innerHTML.xml 의 내용>



2장 <2-1> 와 <3-1>의 가장 큰 차이점이라면 XHR 객체의 responseText 속성값을 이용하여 div 엘리먼트에 문자열을 할당하는 부분이다. 아래 그림은 innerHTML.html 을 실행한 결과이다.


 

<3-3 innerHTML 실행 결과>

 
 
3.2 responseXML 속성을 이용한 DOM 시작하기
 
지금까지의 예제에서는 간단하고 단순한 문자열을 처리하는데 적합한 XHR 객체의 resonseText 속성만을 살펴보았다. 하지만 대단히 복잡한 응답데이터의 경우는 단순한 문자열로 처리할 수 없으며 XML 형식으로 처리하는 것이 훨씬 논리적이고 효율적일 것이다. 그러면 어떻게 브라우저는 서버로부터 받은 XML 형식의 데이터를 처리할 수 있는 것일까? XML 문서는 W3C 의 DOM 을 이용해서 처리된다. DOM 을 지원하는 브라우저들은 당연히 XML 문서를 다루는 많은 API 를 구현하고 있기 때문이다.
 
DOM 은 HTML 과 XML 을 다루는 API 를 제공하고 있으며, 스크립트를 통해서 다큐먼트에 접근할 수 있도록 정의되어 있다. 자바스크립트는 DOM 에 접근할 수 있고 DOM 을 다룰수 있는 스크립팅 언어이다. 다큐먼트의 모든 요소들은 DOM 의 부분들이기 때문에 요소의 속성과 메소드들은 자바스크립트로 제어가 가능하다.
 
 
다음은 XML 문서를 처리하기 위한 DOM 요소의 속성을 살펴보도록 하자.
 
childNodes : 현재 요소의 자식을 배열로 표현한다.
firstChild : 현재 요소의 첫번째 자식이다.
lastChild : 현재 요소의 마지막 자식이다.
nextSibling : 현재 요소와 바로 다음의 요소를 의미한다.
nodeValue : 해당 요소의 값을 읽고 쓸 수 있는 속성을 정의한다.(=data)
parentNode : 해당 요소의 부모노드이다.
previousSibling : 현재 요소와 바로 이전의 요소를 의미한다.
 
 
다음은 XML 다큐먼트를 다루는 유용한 DOM 요소의 메소드를 살펴보자.
 
getElementById(id) : 다큐먼트에서 특정한 id 속성값을 가지고 있는 요소를 반환한다.
getElementsByTagName(name) : 특정한 태그 이름을 가지고 있는 자식 요소로 구성된 배열을 리턴한다.
hasChildNodes() : 해당 요소가 자식 요소를 포함하고 있는지를 나타내는 Boolean 값을 리턴한다.
getAttribute(name) : 특정한 name 에 해당하는 요소의 속성값을 리턴한다.
 
 
이번에는 XHR 객체의 responseXML 속성을 이용한 예제를 살펴봄으로써 XML 다큐먼트를 다루기 위한 DOM 객체의 속성과 메소드에 대해서 알아본다.
 
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Parsing XML Responses with the W3C DOM</title>
   
<script type="text/javascript">
var xmlHttp;
var requestType = "";
function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function startRequest(requestedList) {
    requestType = requestedList;
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", "parseXML.xml", true);
    xmlHttp.send(null);
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            if(requestType == "north") {
                listNorthStates();
            }
            else if(requestType == "all") {
                listAllStates();
            }
        }
    }
}
 
function listNorthStates() {
    var xmlDoc = xmlHttp.responseXML;
   var northNode = xmlDoc.getElementsByTagName("north")[0];
   
    var out = "Northern States";
    var northStates = northNode.getElementsByTagName("state");
   
    outputList("Northern States", northStates);
}
function listAllStates() {
    var xmlDoc = xmlHttp.responseXML;
   var allStates = xmlDoc.getElementsByTagName("state");
   

    outputList("All States in Document", allStates);
}
function outputList(title, states) {
    var out = title;
    var currentState = null;
    for(var i = 0; i < states.length; i++) {
        currentState = states[i];
        out = out + "\n- " + currentState.childNodes[0].nodeValue;
    }
   
    alert(out);
}
</script>
</head>
<body>
    <h1>Process XML Document of U.S. States</h1>
    <br/><br/>
    <form action="#">
        <input type="button" value="View All Listed States" onclick="startRequest('all');"/>
        <br/><br/>
        <input type="button" value="View All Listed Northern States" onclick="startRequest('north');"/>
    </form>
</body>
</html>

<3-4 parseXML.html 의 내용>
 
 
<?xml version="1.0" encoding="UTF-8"?>
<states>
    <north>
        <state>Minnesota</state>
        <state>Iowa</state>
        <state>North Dakota</state>
    </north>
    <south>
        <state>Texas</state>
        <state>Oklahoma</state>
        <state>Louisiana</state>
    </south>
    <east>
        <state>New York</state>
        <state>North Carolina</state>
        <state>Massachusetts</state>
    </east>
    <west>
        <state>California</state>
        <state>Oregon</state>
        <state>Nevada</state>
    </west>
</states>
 
<3-5 parseXML.xml 의 내용>
 
일단 <3-4>의 실행결과를 살펴본 후 핵심원리를 파악해 보자
 
 
 
 

 
<3-6 parseXML.html 을 실행했을때의 그림>
 
그림 <3-6> 은 parseXML.html 을 실행했을 때의 화면으로 View All Listed States 버튼을 눌렀을때의 결과 및 View All Listed Northern States 버튼을 눌렀을때의 결과 화면을 아래에 표시하였다.
 
 
 


<3-7 View All Listed States 버튼을 눌렀을때의 결과 화면>





<3-8 View All Listed Northern States 버튼을 눌렀을때의 결과 화면>


예제 3-4 는 다소 길어보이나 XHR 객체의 responseXML 속성을 이용한 DOM 객체를 다루는 속성 및 메소드를 다루고 있다는 면에서 반드시 이해하고 넘어가야만 한다. 소스 패턴은 지금까지의 예제와 비슷하므로 DOM 속성 및 메소드가 사용된 주요 부분만 설명하겠다.


listAllStates() 메소드의 아래 라인을 주목해 보자.

var xmlDoc = xmlHttp.responseXML;

=> XHR 객체는 responseXML 속성을 이용해서 서버로부터의 XML 결과 다큐먼트를 다룰수 있는 DOM 속성 및 메소드를 사용할 수 있게 해준다는 것을 알 수 있다.


var allStates = xmlDoc.getElementsByTagName("state");

=>XML 결과 다큐먼트로부터 state 자식 엘리먼트들로 구성된 배열을 얻어와 allStates 변수에 할당하는 로직이다.


listNorthStates() 메소드를 살펴보자.

var northNode = xmlDoc.getElementsByTagName("north")[0];

=> XML 다큐먼트에서 north 앨리먼트는 유일하게 하나만 존재하므로 자식 앨리먼트로 구성된 배열중에서 첫번째(0) 배열값을 얻어와야 한다. 위 식은 아래와 같이 수정해도 결과는 같다.

var northNode = xmlDoc.getElementsByTagName("north").item(0);


outputList() 메소드를 살펴보자.

out = out + "\n- " + currentState.childNodes[0].nodeValue;

=> 이부분은 각각의 state 앨리먼트의 첫번째 자식 노드의 값을 out 변수에 계속 연결하는 부분이다. state 앨리먼트의 값을 표현하고 있는 부분도 XML 에서는 하나의 text 엘리먼트이다. 따라서 각각의 state 엘리먼트의 첫번째 text 자식 엘리먼트를 childNodes[0] 으로 표시한 것이며 그 값을 가져오기 위해서 nodeValue 속성이 사용된 것이다. nodeValue 는 아래와 같이 data 속성을 사용해도 같은 결과를 얻는다.

out = out + "\n- " + currentState.childNodes[0].data;

 



3.3 Dynamic DOM 객체 다루기

 

지금까지는 DOM 의 기초적인 속성 및 메소드들을 다루어 봤다. 이런 속성으로는 다이나믹한 웹페이지를 구성하는데 한계가 있다. 웹페이지 전체가 리로딩 되지 않고 적절한 시점에 필요한 부분만 서버와 통신하여 데이터가 수정되는 동적인 웹페이지를 만들려면 더 다양한 DOM 의 속성을 익혀야 한다. 자, 그럼 컨텐츠를 동적으로 생성할 수 있게 해주는 W3C DOM 의 속성과 메소드에는 어떤것들이 있는지 알아보자.

document.createElement(tagName) : tagName 으로된 엘리먼트를 생성한다. div 를 메소드 파라미터로 입력하면 div 엘리먼트가 생성된다.

document.createTextNode(text) : 정적 텍스트를 담고 있는 노드를 생성한다.

<element>.appendChild(childNode) : 특정 노드를 현재 엘리먼트의 자식 노드에 추가시킨다. (예를들어 select 엘리먼트에 option 엘리먼트 추가)

<element>.getAttribute(name) : 속성명이 name 인 속성값을 반환한다.

<element>.setAttribute(name, value) : 속성값 value 를 속성명이 name 인 곳에 저장한다.

<element>.insertBefore(newNode, tartgetNode) : newNode 를  tartgetNode 전에 삽입한다.

<element>.removeAttribute(name) : 엘리먼트에서 name 속성을 제거한다.

<element>.removeChild(childNode) : 자식 엘리먼트를 제거한다.

<element>.replaceChild(newNode, oldNode) : oldNode 를 newNode 로 치환한다.

<element>.hasChildNodes() : 자식 노드가 존재하는지 여부를 판단한다. 리턴형식은 Boolean 이다.


여기서 한가지 짚고 넘어갈 부분이있다. 현재 거의 모든 브라우저는 DOM 을 지원하고 있으며 API 또한 비슷하게 동작하도록 구현되어 있다. 정확히 말하자면 DOM API 의 구현이 브라우저마다 다소 차이가 있다는 말이다. 가장 호환이 안되는 브라우저는 다름아닌 인터넷 익스플로어다. AJAX 의 핵심은 XMLHttpRequest 객체이고 가장 먼저 이 객체를 구현하고 제공한 브라우저가 IE5임을 감안할때 상당히 아이러니컬하지 않은가? 2000년 IE 가 전세계 브라우저의 시장점유율 95% 차지할 즈음, IE 에 대적할 만한 브라우저는 존재하지 않았다. 마이크로소프트가 다양한 웹표준 구현을 포기했던 시점이 바로 이때부터다. 이때까지 XHR 의 사용은 당연히 저조할 수밖에 없었다. 하지만 모질라와 사파리가 잇따라 광범위하게 지원을 시작하게 되자 상황은 변하기 시작했고,  사용율이 극히 부진했던 XHR 객체는 W3C 의 표준으로까지 자리매김하게 되었다. 더우기 구글맵, 구글 Suggest, Gmail, Flickr, Netflix 등에서 AJAX 를 사용하자 이제는 명실상부한 웹의 표준으로 거듭나게 되었다. 너무 늦었지만 Microsoft 쪽에서도 많은 분발을 촉구한다. 그러면 IE 에서 문제가 되고 있는 DOM 객체의 특징을 간략히 살펴보자.

첫째, IE 에서는 <table> 에 <tr> 을 추가할때 appendChilde() 메소드를 사용하더라도 <tr> 은 나타나지 않는다. 따라서 <tr> 을 <tbody> 에 추가해 주는 방식을 사용해야 모든 브라우저에서 동작하는 코드를 작성할 수 있다.

둘째, IE 에서는 setAttribute() 메소드에 class 속성을 이용할 수 없다. setAttribute("class", "newClassName") 한 후에 다시 setAttribute("className", "newClassName") 을 해야 모든 브라우저의 호환성을 보장할 수 있다.

셋째, IE 에서는 style 속성에 setAttribute() 메소드를 이용할 수 없다. <element>.setAttribute("style, font-weight:bold;") 라고 하는 대신에 <element>.style.cssText = "font-weight:bold"  라고 해 줘야 모든 브라우저에서 제대로 작동한다.


다음은 DOM 의 동적 속성 및 메소드를 이용해서 다이나믹한 웹페이지를 생성하는 예제를 다루어 보겠다.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Dynamically Editing Page Content</title>

<script type="text/javascript">
var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function doSearch() {
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", "dynamicContent.xml", true);
    xmlHttp.send(null);
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            clearPreviousResults();
            parseResults();
        }
    }
}

function clearPreviousResults() {
    var header = document.getElementById("header");
    if(header.hasChildNodes()) {
        header.removeChild(header.childNodes[0]);
    }

    var tableBody = document.getElementById("resultsBody");
    while(tableBody.childNodes.length > 0) {
        tableBody.removeChild(tableBody.childNodes[0]);
    }
}

function parseResults() {
    var results = xmlHttp.responseXML;

    var property = null;
    var address = "";
    var price = "";
    var comments = "";

    var properties = results.getElementsByTagName("property");
    for(var i = 0; i < properties.length; i++) {
        property = properties[i];
        address = property.getElementsByTagName("address")[0].firstChild.nodeValue;
        price = property.getElementsByTagName("price")[0].firstChild.nodeValue;
        comments = property.getElementsByTagName("comments")[0].firstChild.nodeValue;
       
        addTableRow(address, price, comments);
    }
   
    var header = document.createElement("h2");
   var headerText = document.createTextNode("Results:");
   header.appendChild(headerText);
   document.getElementById("header").appendChild(header);
   
   document.getElementById("resultsTable").setAttribute("border", "1");
}


function addTableRow(address, price, comments) {
    var row = document.createElement("tr");
   var cell = createCellWithText(address);
   row.appendChild(cell);
   

    cell = createCellWithText(price);
    row.appendChild(cell);
   
    cell = createCellWithText(comments);
    row.appendChild(cell);
   
    document.getElementById("resultsBody").appendChild(row);
}

function createCellWithText(text) {
    var cell = document.createElement("td");
    var textNode = document.createTextNode(text);
    cell.appendChild(textNode);
   
    return cell;
}
</script>
</head>

<body>
  <h1>Search Real Estate Listings</h1>
 
  <form action="#">
    Show listings from
        <select>
            <option value="50000">$50,000</option>
            <option value="100000">$100,000</option>
            <option value="150000">$150,000</option>
        </select>
        to
        <select>
            <option value="100000">$100,000</option>
            <option value="150000">$150,000</option>
            <option value="200000">$200,000</option>
        </select>
    <input type="button" value="Search" onclick="doSearch();"/>   
  </form>


  <span id="header">
 
  </span>

  <table id="resultsTable" width="75%" border="0">
    <tbody id="resultsBody">
    </tbody>
  </table>
</body>
</html>

<3-9 dynamicContent.html 의 내용>


<?xml version="1.0" encoding="UTF-8"?>
<properties>
    <property>
        <address>812 Gwyn Ave</address>
        <price>$100,000</price>
        <comments>Quiet, serene neighborhood</comments>
    </property>   
    <property>
        <address>3308 James Ave S</address>
        <price>$110,000</price>
        <comments>Close to schools, shopping, entertainment</comments>
    </property>   
    <property>
        <address>98320 County Rd 113</address>
        <price>$115,000</price>
        <comments>Small acreage outside of town</comments>
    </property>   
</properties>


<3-10 dynamicContent.xml 의 내용>



 
<3-10 샘플 3-9의 실행결과 화면>
 

샘플 3-9를 보면 동적 메소드가 적용된 부분은 굵게 표시를 해 놓았다. 이부분을 이전 샘플들과 비교해 가면서 보면 코드를 이해하는데 큰 어려움은 없으리라 생각한다. 실행결과는 3-10 그림에 나와있다. Search 버튼을 누르면 dynamicContent.xml 의 내용을 테이블로 표시한다. Search 버튼을 누르면 기존에 존재하는 테이블 row 를 동적으로 제거한 후에 동적으로 다시 그린다. 이번 예제는 이 부분이 핵심으므로 가장 중요한 코드만을 설명하겠다.


function createCellWithText(text) {
    var cell = document.createElement("td");
    var textNode = document.createTextNode(text);
    cell.appendChild(textNode);
   
    return cell;
}


위 메소드는 테이블 컬럼(<td></td>)에 해당하는 정보를 생성하는 메소드이다. 3-10 그림을 보면 하나의 row 에는 3개의 컬럼 요소가 있으며 동적으로 하나의 행을 생성하기 위해서는 address, price, comments 에 해당하는 td 요소를 각각 생성해야 한다.


function addTableRow(address, price, comments) {
    var row = document.createElement("tr");
    var cell = createCellWithText(address);
    row.appendChild(cell);
   
    cell = createCellWithText(price);
    row.appendChild(cell);
   
    cell = createCellWithText(comments);
    row.appendChild(cell);
   
    document.getElementById("resultsBody").appendChild(row);
}


위 메소드는 테이블 행(<tr></tr>)에 해당하는 정보를 생성해서 테이블에 추가하는 메소드이다. 바로 위에서 언급했듯이 행을 구성하고있는 3개의 컬럼 요소를 각각 만들어서 row 변수에 추가한 후, 이 변수를 다시 tbody 속성에 추가하면 그림 3-10과 같은 화면이 완성되는 것이다.




3.4 요청 파라미터를 서버로 보내기

 

지금까지는 ajax를 이용하여 요청을 서버로 보내는 방법과 서버로부터 받은 결과 정보를 파싱해서 처리하는 여러 특징들에 대해서 살펴보았다. 하지만 이것만으로는 부족하다. xml 의 고정된 정보를 다루는 것이 실증나지 않는가? 요청을 보낼때 특정 파라미터를 실어서 서버에 보내고, 서버는 요청정보를 바탕으로 특화된 응답정보를 보내야만 쓸만해 진다.


XMLHttpRequest(XHR) 은 고전적 웹의 GET/POST 방식과 흡사하게 동작한다. GET 방식은 name=value 쌍의 파라마터를 url 에 실어서 서버로 전송한다. 물론 name=value 쌍은 리소스 url 의 끝을 의미하는 ? 이후에 구분자(&) 를 사이사이에 끼고 주욱 붙는다.


POST 방식은 GET 방식과 마찬가지로 name=value 쌍의 형태로 데이터를 전달한다. 물론 같은 구분자 (&)를 사용한다. 하지만 POST 방식은 폼 요소의 데이터를 인코딩하여 Http Request 객체의 body 에 저장해서 보낸다.


또 다른 차이점이 있다면 서버로 보낼 수 있는 요청정보의 크기인데, GET 방식으로는 name1=value1&name2=value2&name3=value3... 이런 문자열의 길이가, 브라우저마다 차이가 있지만, 대략 2000 byte 이상이면 불가능하다. 불가능하다는 의미가 무엇이냐 하면 브라우저는 요청정보를 보내려고 시도는 하지만 처리가 안되기 때문에 프러세스는 중단되고 만다. 따라서 서버로 보내는 파라미터가 많을 때는 POST 방식을 사용해야 한다. 일반적으로 데이터를 fetch(검색) 할때는 GET 방식을 사용하고 그 이외의 작업(추가, 수정, 삭제)에는 POST 방식을 주로 사용한다. 예를 들어 클릭해서 현재 이 글을 읽고 있는 경우는 GET 방식이 사용되었을 것이고, 이 글을 수정할때는 POST 방식이 사용될 것이다.


Ajax와 관련된 차이점이라면 GET 방식은 파라미터가 인코딩되어 url 에 붙어가기 때문에 해당 url 을 통째로 재사용(bookmark) 가능하지만 ajax 특성상 이런 북마킹 기능은 불가능 하다. HTML 폼 요소에는 method 속성이 있는데 개발자는 GET 또는 POST 방식을 선택할 수 있다. 요청 데이터들은 서버로 submit 될때 method속성에 알맞도록 자동으로 인코딩되지만 XHR 객체는 이런 내장 알고리즘이 없기 때문에 개발자가 쿼리 스트링을 작성해야 한다. 쿼리 스트링을 작성하는 방법은 GET 또는 POST 방식에 상관없이 동일하다. 유일한 차이점이 있다면 GET 방식의 쿼리 스트링은 요청 url 에 붙어서 서버로 전송되지만 POST 방식의 쿼리 스트링은 XHR 객체의 send(쿼리 스트링) 메소드가 호출될때 파라미터로 전송된다. 샘플을 살펴보면서 ajax 에서 GET 및 POST 방식을 어떻게 사용하는지 알아보자. 이번에는 결과화면을 먼저 소개한다.


 
<3-11 GET/POST 방식 샘플 화면>
 
 
위 그림 3-11 은 브라우저에서 First name, Middle name, Birthday 를 입력한 후 Send parameters using GET 버튼 혹은 Send parameters using POST 버튼을 눌렀을 때의 결과가 바로 아래 부분에 표시되는 형태의 단순한 예제이다. 위 샘플을 실행시켜 보기 위해서는 getAndPostExample.html 과 서버 프로그램인 GetAndPostExample.java 가 필요하다. 하지만 이번 예제의 주요 핵심은 서버 프로그램이 아니다. XHR 객체가 GET/POST 방식을 어떻게 사용하는지를 이해하는 것이 중요하다. 클라이언트 및 서버 프로그램 코드를 기술한 후 XHR 의 주요코드에 대해서 설명을 하겠다.
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Sending Request Data Using GET and POST</title>
<script type="text/javascript">
var xmlHttp;
function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function createQueryString() {
    var firstName = document.getElementById("firstName").value;
    var middleName = document.getElementById("middleName").value;
    var birthday = document.getElementById("birthday").value;
   
    var queryString = "firstName=" + firstName + "&middleName=" + middleName
        + "&birthday=" + birthday;
   
    return queryString;
}
function doRequestUsingGET() {
    createXMLHttpRequest();
   
    var queryString = "GetAndPostExample?";
    queryString = queryString + createQueryString()
        + "&timeStamp=" + new Date().getTime();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", queryString, true);
    xmlHttp.send(null);
}
function doRequestUsingPOST() {
    createXMLHttpRequest();
   
    var url = "GetAndPostExample?timeStamp=" + new Date().getTime();
    var queryString = createQueryString();
   
    xmlHttp.open("POST", url, true);
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");   
    xmlHttp.send(queryString);
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            parseResults();
        }
    }
}
function parseResults() {
    var responseDiv = document.getElementById("serverResponse");
    if(responseDiv.hasChildNodes()) {
        responseDiv.removeChild(responseDiv.childNodes[0]);
    }
   
    var responseText = document.createTextNode(xmlHttp.responseText);
    responseDiv.appendChild(responseText);
}
</script>
</head>
<body>
  <h1>Enter your first name, middle name, and birthday:</h1>
 
  <table>
    <tbody>
        <tr>
            <td>First name:</td>
            <td><input type="text" id="firstName"/>
        </tr>
        <tr>
            <td>Middle name:</td>
            <td><input type="text" id="middleName"/>
        </tr>
        <tr>
            <td>Birthday:</td>
            <td><input type="text" id="birthday"/>
        </tr>
    </tbody>
 
  </table>
 
  <form action="#">
    <input type="button" value="Send parameters using GET" onclick="doRequestUsingGET();"/>   
   
    <br/><br/>
    <input type="button" value="Send parameters using POST" onclick="doRequestUsingPOST();"/>   
  </form>
  <br/>
  <h2>Server Response:</h2>
  <div id="serverResponse"></div>
</body>
</html>
<3-12  getAndPostExample.html 의 전체 소스 코드>
 
 
 
package ajaxbook.chap3;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class GetAndPostExample extends HttpServlet {
    /**
  *
  */
 private static final long serialVersionUID = 1L;
 protected void processRequest(HttpServletRequest request,
            HttpServletResponse response, String method)
    throws ServletException, IOException {
        //Set content type of the response to text/xml
        response.setContentType("text/xml");
        //Get the user's input
        String firstName = request.getParameter("firstName");
        String middleName = request.getParameter("middleName");
        String birthday = request.getParameter("birthday");
        //Create the response text
        String responseText = "Hello " + firstName + " " + middleName
                + ". Your birthday is " + birthday + "."
                + " [Method: " + method + "]";
        //Write the response back to the browser
        PrintWriter out = response.getWriter();
        out.println(responseText);
        //Close the writer
        out.close();
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        //Process the request in method processRequest
        processRequest(request, response, "GET");
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        //Process the request in method processRequest
        processRequest(request, response, "POST");
    }
}

<3-13 GetAndPostExample.java 의 전체 소스 코드>
 

3-12 의 샘플 코드는 이전 예제와 비교해 봤을때 크게 어려운 부분은 없을 것이다. 3-13 의 프로그램은 단순한 에코성 문자열을 생성해서 다시 클라이언트로 보내는 것이므로 특별한 설명은 필요하지 않을 듯 싶다. 이번 예제의 핵심은 ajax 에서 GET 및 POST 방식으로 서버에 파라미터를 보내는 방법이다.


function createQueryString() {
    var firstName = document.getElementById("firstName").value;
    var middleName = document.getElementById("middleName").value;
    var birthday = document.getElementById("birthday").value;
   
    var queryString = "firstName=" + firstName + "&middleName=" + middleName
        + "&birthday=" + birthday;
   
    return queryString;
}


먼저 위 코드는 GET 및 POST 방식에 있어서 쿼리 스트링을 만들어 공통으로 사용하기 위한 메소드이다. 전에도 설명했듯이 name=value 쌍의 파라미터를 한줄의 String 데이터로 만들고 있다. 각 name=value 쌍은 & 로 구분지어야 한다. name=value 쌍의 순서는 상관없다.


function doRequestUsingGET() {
    createXMLHttpRequest();
   
    var queryString = "GetAndPostExample?";
    queryString = queryString + createQueryString()
        + "&timeStamp=" + new Date().getTime();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", queryString, true);
    xmlHttp.send(null);
}


위의 코드는 ajax 를 활용하여 GET 방식으로 파라미터를 서버에 보내는 코드이다. XHR 객체 생성후 쿼리 스트링을 생성한다. var queryString = "GetAndPostExample?"; 라인에서 GetAndPostExample 은 ajaxbook.chap3.GetAndPostExample 서브렛을 호출하기 위해서 서브렛 이름과 매핑된 url 이름이다. 이 부분은 해당 컨텍스트의 web.xml 에 다음과 같이 기술되어 있어야 한다.

<web-app>
  <servlet>
    <servlet-name>GetAndPostExample</servlet-name>
    <servlet-class>ajaxbook.chap3.GetAndPostExample</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>GetAndPostExample</servlet-name>
    <url-pattern>/GetAndPostExample</url-pattern>
  </servlet-mapping>

</web-app>


쿼리 스트링을 만들때 "&timeStamp=" + new Date().getTime(); 부분을 넣은 이유는 다음과 같다. 몇몇 브라우저들은 명확하지 않은 조건하에서 똑같은 url 로 XHR 멀티 요청을 보냈을때 서버의 결과를 캐싱하는 경향을 보인다. 비록 같은 url 이지만 응답이 다를경우엔 캐싱하는 경향이 오히려 예상치 못한 결과의 원인이 될 수도 있기때문에 되도록이면 unique 한 url을 생성하기 위한 방법을 택한것이다. 이렇게 생성한 쿼리 스트링을 open 메소드에 넣어주고 send 메소드에는 null 값을 설정한다. 다음은 POST 방식에 대해서 살펴보자.


function doRequestUsingPOST() {
    createXMLHttpRequest();
   
    var url = "GetAndPostExample?timeStamp=" + new Date().getTime();
    var queryString = createQueryString();
   
    xmlHttp.open("POST", url, true);
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");   
    xmlHttp.send(queryString);
}


POST 방식도 GET 방식과 마찬가지로 같은 쿼리 스트링을 사용한다. 하지만 용도는 다르다. GET 방식은 url 과 쿼리 스트링을 합쳐서 open 메소드에 넣어서 사용한 반면, POST 방식은 위 코드처럼 send 메소드에 파라미터로 넣어준다. 또 다른 차이점이라면 POST 방식은 HTTP Request 객체의 바디에 파라미터가 저장되는 것이므로 헤더에도 Content-Type 을 반드시 정의해 줘야 한다. 정의를 해주지 않으면 서버에서는 클라이언트에서 보낸 파라미터를 얻지 못한다.





3.5 XML 을 요청 파라미터로 사용하기


예제 3-13은 ajax를 이용하여 브라우저의 파라미터를 서버로 전송하고 그 결과를 처리하는 부분을 주로 살펴보았다. 하지만 파라미터를 name=value 쌍으로 보내는 것은 초보나 하는 짓으로 보일 수 있고 좀더 확정성과 유연성 그리고 가독성을 높이는 방향으로 노력을 해보자. 파라미터를 xml 로 변환해서 처리하도록 하는 것이다. 흥미진진하지 않은가? 그렇다고 이 방식이 완벽한 해결책이 되는 것은 아니다. 이번 예제도 물론 실험적이고 기초적인 코드를 가지고 그 활용도를 모색할 것이다.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Sending an XML Request</title>

<script type="text/javascript">

var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function createXML() {
    var xml = "<pets>";
   
    var options = document.getElementById("petTypes").childNodes;
    var option = null;
    for(var i = 0; i < options.length; i++) {
        option = options[i];
        if(option.selected) {
            xml = xml + "<type>" + option.value + "<\/type>";
        }
    }
   
    xml = xml + "<\/pets>";
    return xml;
}

function sendPetTypes() {
    createXMLHttpRequest();
   
    var xml = createXML();
    var url = "PostingXMLExample?timeStamp=" + new Date().getTime();
   
    xmlHttp.open("POST", url, true);
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");   
    xmlHttp.send(xml);
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            parseResults();
        }
    }
}

function parseResults() {
    var responseDiv = document.getElementById("serverResponse");
    if(responseDiv.hasChildNodes()) {
        responseDiv.removeChild(responseDiv.childNodes[0]);
    }
   
    var responseText = document.createTextNode(xmlHttp.responseText);
    responseDiv.appendChild(responseText);
}


</script>
</head>

<body>
  <h1>Select the types of pets in your home:</h1>
 
  <form action="#">
    <select id="petTypes" size="6" multiple="true">
        <option value="cats">Cats</option>
        <option value="dogs">Dogs</option>
        <option value="fish">Fish</option>
        <option value="birds">Birds</option>
        <option value="hamsters">Hamsters</option>
        <option value="rabbits">Rabbits</option>
    </select>
   
    <br/><br/>
    <input type="button" value="Submit Pets" onclick="sendPetTypes();"/>
  </form>

  <h2>Server Response:</h2>

  <div id="serverResponse"></div>

</body>
</html>

<3-14 postingXML.html 의 전체 소스 코드>




package ajaxbook.chap3;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class PostingXMLExample extends HttpServlet {

    /**
  *
  */
 private static final long serialVersionUID = 1L;

 protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

        String xml = readXMLFromRequestBody(request);
        Document xmlDoc = null;
        try {
            xmlDoc =
                    DocumentBuilderFactory.newInstance().newDocumentBuilder()
                    .parse(new ByteArrayInputStream(xml.getBytes()));
        }
        catch(ParserConfigurationException e) {
            System.out.println("ParserConfigurationException: " + e);
        }
        catch(SAXException e) {
            System.out.println("SAXException: " + e);
        }

        /* Note how the Java implementation of the W3C DOM has the same methods
         * as the JavaScript implementation, such as getElementsByTagName and
         * getNodeValue.
         */
        NodeList selectedPetTypes = xmlDoc.getElementsByTagName("type");
        String type = null;
        String responseText = "Selected Pets: ";
        for(int i = 0; i < selectedPetTypes.getLength(); i++) {
           type = selectedPetTypes.item(i).getFirstChild().getNodeValue();
           responseText = responseText + " " + type;
        }

        response.setContentType("text/xml");
        response.getWriter().print(responseText);
    }

    private String readXMLFromRequestBody(HttpServletRequest request){
        StringBuffer xml = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = request.getReader();
            while((line = reader.readLine()) != null) {
                xml.append(line);
            }
        }
        catch(Exception e) {
            System.out.println("Error reading XML: " + e.toString());
        }
        return xml.toString();
    }
}


<3-15 PostingXMLExample.java 의 전체 소스 코드>


3-14는 완전 html 날코딩이라 실망할 수도 있겠다. 파라미터를 담고 있는 xml 을 스트링 조합으로 생성해서 서버로 전송한다. 전송방식은 샘플 3-12 의 POST 방식과 동일하다. 3-15 는 서브릿으로써 브라우저에서 request 객체의 body 에 실어보낸 xml 을 파싱해서 데이터를 추출한 후 일정한 형식의 문자열로 변환하여 다시 클라이언트로 보내주는 방식이다. 이번 서버 프로그램역시 xml 을 파싱하고 정보를 추출하는 부분에 대해서는 특별한 설명을 하지는 않겠다. 하지만 서블릿에서 한가지 짚고 넘어가자면 클라이언트로부터 받은 xML 을 파싱할때 Document 인터페이스를 사용하는데, 이 는 W3C 가 구체화한 것으로 DOM 객체에 존재하는 같은 기능의 메소드인 getElementsByTagName("type") 을 사용하고 있다는 점이다. 나의 주요 논점은 어디까지나 클라이언트이다. 핵심 코드는 아래와 같다.


function createXML() {
    var xml = "<pets>";
   
    var options = document.getElementById("petTypes").childNodes;
    var option = null;
    for(var i = 0; i < options.length; i++) {
        option = options[i];
        if(option.selected) {
            xml = xml + "<type>" + option.value + "<\/type>";
        }
    }
   
    xml = xml + "<\/pets>";
    return xml;
}


핵심 로직이지만 너무 간단해서 설명할 것도 없을 것 같다. 위 부분은 xml을 String 형식의 쿼리 스티링을 생성하는 부분이다. petTypes 는 select box 에서 option 엘리먼트 값을 추출하여 문자열을 만드는 부분이다.


function sendPetTypes() {
    createXMLHttpRequest();
   
    var xml = createXML();
    var url = "PostingXMLExample?timeStamp=" + new Date().getTime();
   
    xmlHttp.open("POST", url, true);
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");   
    xmlHttp.send(xml);
}


위 코드 역시 이전 샘플에서 다루었기 때문에 특별하게 설명한 부분은 없다. 하지만 한가지 짚고 넘어갈 부분이 있는데, send() 메소드의 파라미터에는 문자열 및 DOM 객체를 설정하는 것이 가능하다. 하지만 왜 이번 예제에서는 DOM 객체 대신에 문자열을 넣었을까? 아쉽게도 지금까지 브라우저간 DOM 객체를 생성해서 공통적으로 사용할 수 있는 방법이 없기 때문이다. IE 의 경우는 ActiveXObject 컨트롤을 통해서, 모질라는 native 자바스립트를 통해서 제공하며, 심지어 이런 방법조차 지원하지 않는 브라우저도 존재한다. 따라서 어쩔수 없이 스트링값을 설정한 것이다.

다음은 위 프로그램을 실행한 결과 화면이다.




<3-16 postingXML.html 의 실행 결과 화면>





3.6 JSON 을 활용하여 데이터를 서버로 보내기

 

바로 이전의 3-14 샘플은 브라우저의 파라미터를 XML 형식으로 변환하여 서버로 보내는 방법을 제시했었다. 데이타 포맷의 표준이 되서버린 XML을 이용한다는 전체적인 맥락은 그럴싸했지만 사실 XML을 만드는 과정이 복잡한 쿼리 스트링 조합작업이라면 누가 하려하겠는가 말이다. 나 자신도 이런 작업은 정말 싫어한다.


XML 을 생성하기 위한 javascript 날코딩의 대안으로 JSON(Javascript Object Notation, http://www.json.org/) 을 소개한다. JSON 은 텍스트 포맷기반의 경량 데이터 변환 포맷이다. 프로그래밍 언어에 독립적며, C 언어계열에 익숙한 데이터 구조 형식을 취하고 있다. JSON은 두가지 텍스트 포맷을 가지고 있는데, 첫번째는 name/value 쌍의 컬렉션 데이터 구조로 프로그래밍 언어로 따지면 object, record, struct 쯤 되겠다. 두번째는 정렬된 value 의 리스트형태로써 프로그래밍 언어로 비유하자면 배열이라고 보면 될 것이다.


JSON 의 데이터 구조는 많은 프로그램 언어에 의해서 지원되고 있기때문에 XML 보다는 이기종 시스템간의 이상적인 선택이 될 것이다. 추가적으로 JSON 은 표준 자바스크립트의 부류이므로 모든 웹 브라우저간에도 양립할 수 있는 것이다.



<3-17 JSON 오브젝트구조도(출처 : http://www.json.org/)>
 
 
위 그림은 JSON의 데이터 구조를 나타내고 있다.
 
Object는 {} 으로 표시한다. 오브젝트에는 name/value 쌍이 콜론(:)  혹은 콤마(,) 로 구분되어져 있으며 순서는 없다.
 
Array 는 [] 으로 표시한다. 배열은 정렬된 value 가 콤마(,) 에 의해서 구분되어져 있으며, value 값은 스트링("" 으로 둘러싸야 함), 숫자, true or false, null, object , array 가 올수 있으므로 배열의 구조는 계층적이라고 할 수 있다.
스트링은 유니코드 조합 및 백래쉬 이스케이프(\)를 사용할 수 있으며 '' 을 사용하여 character 를 표현할 수 있다.
스트링과 숫자는 C언어 혹은 자바의 스트링과 거의 흡사하지만 8진수 및 16진수 포맷은 지원하지 않는다. 공백을 name/value 쌍 사이사이에 사용할 수 있다.
 
하나의 예를 들어보자. Employ 라는 클래스(멤버로 firstName, lastName, employeeNumber, title)의 인스턴스를 JSON 을 이용해서 아래와 같이 표현해 볼 수 있다.
 
var employee = {
 "firstName"    : John,
 "lastName"     : Doe,
 "employNumber" : 123,
 "title"        : "Manager"
}
 
그러면 위 표현을 오브젝트 속성을 이용해서 아래와 같이 다룰 수 있다.
var lastName = employee.lastName;//lastName 에 접근
var title = employee.title;//title 에 접근
employee.emplyeeNumber = 456;//employeeNumber 를 456 으로 수정
 

JSON 의 인코딩은 확실히 XML 인코딩보다 가볍다. 따라서 네트웍을 통해서 큰 데이터가 오고가는 상황에서는 많은 퍼포먼스의 차이가 발생할 것이다. JSON 싸이트에 가보면 적어도 14개 이상의 서버쪽 어플리케이션을 다루는 프로그래밍언어에서 JSON 을 사용할 수 있게끔 준비가 되어 있다.


이제 3장의 마지막 주제인 JSON 을 이용한 간단한 샘플을 살펴보자.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>JSON Example</title>

<script type="text/javascript" src="json.js"></script>
<script type="text/javascript">

var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function doJSON() {
    var car = getCarObject();
   
    //Use the JSON JavaScript library to stringify the Car object
    var carAsJSON = JSON.stringify(car);
    alert("Car object as JSON:\n " + carAsJSON);
   
    var url = "JSONExample?timeStamp=" + new Date().getTime();
   
    createXMLHttpRequest();
    xmlHttp.open("POST", url, true);
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");   
    xmlHttp.send(carAsJSON);
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            parseResults();
        }
    }
}

function parseResults() {
    var responseDiv = document.getElementById("serverResponse");
    if(responseDiv.hasChildNodes()) {
        responseDiv.removeChild(responseDiv.childNodes[0]);
    }
   
    var responseText = document.createTextNode(xmlHttp.responseText);
    responseDiv.appendChild(responseText);
}

function getCarObject() {
    return new Car("Dodge", "Coronet R/T", 1968, "yellow");
}

function Car(make, model, year, color) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.color = color;
}

</script>
</head>

<body>

  <br/><br/>
  <form action="#">
      <input type="button" value="Click here to send JSON data to the server"
        onclick="doJSON();"/>
  </form>
 
  <h2>Server Response:</h2>

  <div id="serverResponse"></div>

</body>
</html>

<3-18 jsonExample.htm 의 전체 소스 코드>


package ajaxbook.chap3;

import java.io.*;
import java.net.*;
import java.text.ParseException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.json.JSONObject;

public class JSONExample extends HttpServlet {

    /**
  *
  */
 private static final long serialVersionUID = 1L;

 protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        String json = readJSONStringFromRequestBody(request);

        //Use the JSON-Java binding library to create a JSON object in Java
        JSONObject jsonObject = null;
        try {
            jsonObject = new JSONObject(json);
        }
        catch(ParseException pe) {
            System.out.println("ParseException: " + pe.toString());
        }

        String responseText = "You have a " + jsonObject.getInt("year") + " "
            + jsonObject.getString("make") + " " + jsonObject.getString("model")
            + " " + " that is " + jsonObject.getString("color") + " in color.";

        response.setContentType("text/xml");
        response.getWriter().print(responseText);
    }

    private String readJSONStringFromRequestBody(HttpServletRequest request){
        StringBuffer json = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = request.getReader();
            while((line = reader.readLine()) != null) {
                json.append(line);
            }
        }
        catch(Exception e) {
            System.out.println("Error reading JSON string: " + e.toString());
        }
        return json.toString();
    }
}

<3-19 JSONExample.java 의 전체 소스 코드>


마지막 예제에서 중요한 부분을 굵게 표시하였다. 이번 예제를 실행해 보기 위해서는 json.js 와 자바관련 json 라이브러리가 필요하다. 관련 파일들은 json 웹싸이트에서 다운받으면 된다. 우선 자바스크립트쪽 핵심코드를 먼저 살펴보자.


function getCarObject() {
    return new Car("Dodge", "Coronet R/T", 1968, "yellow");
}

function Car(make, model, year, color) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.color = color;
}


위 코드는 설명이 별로 필요치 않을 것 같다. Car 객체를 만들어 주는 메소드이다.


function doJSON() {
    var car = getCarObject();
   
    //Use the JSON JavaScript library to stringify the Car object
    var carAsJSON = JSON.stringify(car);
    alert("Car object as JSON:\n " + carAsJSON);
   
    var url = "JSONExample?timeStamp=" + new Date().getTime();
   
    createXMLHttpRequest();
    xmlHttp.open("POST", url, true);
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");   
    xmlHttp.send(carAsJSON);
}

위 코드를 보면 자바스크립트 car 객체를 생성한 후 JSON 자바스크립트 라이브러이의 stringify 를 사용해서 JSON 객체로 변환하고 있다. 나머지 로직은 POST 방식을 구현한 것이고 send(() 메소드에 JSON 객체를 넣어준다.


이번엔 서버쪽 프로그램을 확인해 보자.


JSONObject jsonObject = null;
try {
    jsonObject = new JSONObject(json);
}
catch(ParseException pe) {
    System.out.println("ParseException: " + pe.toString());
}

String responseText = "You have a " + jsonObject.getInt("year") + " "
 + jsonObject.getString("make") + " " + jsonObject.getString("model")
 + " " + " that is " + jsonObject.getString("color") + " in color.";


우선 서버 프로그램은 Http request 객체에서 JSON 문자열을 추출한다. 이렇게 추출된 문자열을 JSON 자바 라이브러리의 JSONObject 클래스를 생성할때 생성자의 파라미터로 입력된다. JSONObject 는 자동으로 JSON 문자열을 파싱하고 getXxx 메소드를 이용해서 여러 타입의 데이터를 추출할 수 있는 것이다. 정말 간단하지 않은가?


다음은 3-19 샘플을 실행한 결과 화면이다.


 
Click here to send JSON data to the server 버튼을 클릭하면 alert 창으로 JSON object 데이터 구조를 확인할 수 있다. 그리고 서버에서 처리된 결과문자열인 You have a 1968 Dodge Coronet R/T that is yellow in color. 을 확인 할 수 있을 것이다.

출처 : Tong - bassdot님의 AJAX통

신고

XMLHttpRequest(XHR) 객체는 Internet Explore5에서 ActiveX 컴포넌트 형식으로 가장 먼저 제공되었기때문에, 모질라와 사파리 웹브라우저에서 이 객체를 도입하기전까지 사실 많은 개발자들이 XMLHttpRequest 객체의 사용을 꺼려했다.


XHR은 W3C 표준이 아니기때문에 브라우저마다 작동방식에 있어서 다소나마 차이가 존재했었지만 현재 대부분의 브라우저들은 XHR 기능을 서로 비슷하게 구현하고 있다. 현존하는 브라우저들 중에서 XHR을 지원하지 않는 브라우저는 거의 없을정도로 대부분의 브라우저들은 현재 XHR 을 지원하고 있다.


ajax 구현의 시작은 XHR 객체의 생성에서 시작한다.

XHR 은 W3C 의 표준이 아니므로, 인터넷익스플로어에서는 ActiveX Component 형식으로 구현되었고, 그 밖에 다른 브라우저들(FireFox, Safari, Opera)은 native javascript 객체로 구현되었다. 이런 차이점을 고려해서 XHR 객체를 얻어오는 자바스크립트 코드를 다음과 같이 작성할 수 있다.


var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}


window.ActiveXObject 는 ActiveXObject를 지원하는 브라우저라면 오브젝트를 리턴하고 그렇지 않다면 null를 리턴하게 된다. 따라서 오브젝트가 존재하면 if 구문은 true 를 반환하고 xmlHttp 값은 ActiveXObject 객체가 할당되어진다. if 구문이 false 를 반환하면 else if 구분으로 이동하여 xmlHttp 값은 navtive javascript 객체가 할당될 것이다.
따라서 위 코드를 이용하면 브라우저가 다르더라도 하나의 메소드로 XHR 객체를 생성하여 사용할 수 있다.


그런데 왜 자바스크립트일까? 비슷한 놈으로 VBScrpt도 있는데 말이다. 여기서 cross-browser(브라우저가 달라도 실행했을때 에러없이 같은 결과가 나오도록 구현해야 하는 코딩 방식)를 고려해 봐야 한다. Ajax 에서 조차 IE 에서는 잘 구동되고 나머지 브라우저에서는 안되는 태고적 코딩방식에서는 이제 벗어나보자. 자바스크립트는 현존하는 거의 모든 브라우저에서 지원받고 있는 스크립팅 언어이다. 그리고 자바스크립트(Netscape 사에서 만들었음)는 자바(JAVA, 썬에서 만든 언어)와는 전혀 다르다.  자바스크립트에 대해서 간단하게 참고할 것이 있다면 http://www.crockford.com/javascript/javascript.html 이 싸이트에 한번 들어가보자.


이번에는 XHR 오브젝트에는 어떤 메소드가 있는지 살펴보자.

void open(string method, string url, boolean asynch, string username, string password) : 요청을 초기화한다. 파라미터중에서 method, url 두개만 필수항목이고 나머지는 선택항목이다. method 는 POST, GET, PUT 3가지중 하나를 사용하면 되며, url 은 요청하고자 하는 서버의 url 이다. asynch 는 요청이 비동기인지 여부를 판단하는 항목이다. 입력하지 않으면 디폴트로 true 가 설정되어 요청은 비동기로 처리된다. false 로 설정하면 요청은 동기로 처리되기 때문에 서버에서 응답을 받을때까지 프로세스는 기다리게 된다. 사실 XHR 을 사용하는 가장 큰 이점중의 하나인 비동기 처리를 위해서는 asynch 항목을 true 로 설정해서 사용해야 한다. false 를 설정한다면 XHR 을 사용하는 이점이 그만큼 줄어든다.


void send(content) : 실질적으로 요청을 서버로 보낸다. 요청이 비동기이면 이 메소드는 바로 리턴되지만 요청이 동기이면 서버에서 응답을 받을때까지 계속 대기한다. content 는 선택사항이며, DOM 객체(XML 객체)이거나 input stream, string 값으로 설정할 수 있으며 HttpRequest body 의 한 부분으로 서버로 전달된다. content 에 값을 넘기려면 open() 메소드는 반드시 POST 로 설정해야 하며, GET 방식으로 요청하려면 null 을 설정하면 된다.


open(), send() 메소드가 가장 많이 사용되는 메소드들이다. 나머지 메소드에 대해서도 알아보자.

void setRequestHeader(string header, string value) : header 에 해당하는 value 값으로 HttpRequest  헤더에 값을 설정하는 메소드로써, 반드시 open() 메소드 다음에 위치해야 한다.


void abort() : 요청을 중지한다.


string getAllResponseHeaders() : 요청에 대응되는 응답의 헤더정보를 리턴한다. 즉, Content-Length, Date, URI 등을 포함하는 헤더정보를 string 형식으로 반환한다.


string getResponseHeader(string header) : 응답의 헤더정보중에서 header 에 대응되는 값을 string 형식으로 반환한다.



이번에는 XHR 의 속성중에서 중요한 몇가지를 알아본다.

onreadystatechange : 자바스크립트 콜백함수(funtion pointer)를 저장한다. 콜백함수는 readyState 값이 변할때 마다 호출된다. 요청이 서버로 보내지면 readyState 는 5가지 숫자값으로 계속 변화가 일어나게 된다.

readyState : 요청의 상태를 의미한다.(0 = uninitialized, 1 = loading, 2 = loaded, 3 = interactive, 4 = complete)

responseText : 서버의 응답을 string 형식으로 나타낸다. 단순 text를 innerHTML 속성으로 표현하기에는 알맞지만 논리적으로 파싱하거나 동적으로 페이지 컨텐츠를 생성하기는 힘들다.

responseXML : 서버의 응답을 XML 로 나타낸다. 이 속성은 DOM 객체로 파싱할 수 있다.

status : 서버로부터의 HTTP 상태코드이다.(예 200(OK), 404(NOT Found), 202(결과 값이 없을 때)등등)

statusText : HTTP 상태코드에 대한 텍스트 값이다.(예 OK, NOT Found 등등)



이번에는 아주 간단한 예제를 실행해 보자. 아래 코드를 실행해 보려면 simpleRequest.html 이름의 html 파일로 만든 후에 반드시 웹서버에서 실행해야 한다. 소스는 다운받아서 실행해 볼 수도 있다.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Simple XMLHttpRequest</title>
   
<script type="text/javascript">
var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
   
function startRequest() {
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", "simpleResponse.xml", true);
    xmlHttp.send(null);
}
   
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            alert("The server replied with: " + xmlHttp.responseText);
        }
    }
}
</script>
</head>

<body>
    <form action="#">
        <input type="button" value="Start Basic Asynchronous Request" onclick="startRequest();"/>
    </form>
</body>
</html>


<2-1 simpleRequest.html>


 


Hello from the server!

<2-2 simpleResponse.xml 의 내용>

 

위 코드를 실행해 보면 알겠지만 url 대신 <2-2> 와 같은 내용을 담고 있는 simpleResponse.xml 파일을 요청한다. 되도록 단순하게 XHR 의 구동원리를 이해하는 목적에서 서버에 요청을 하고 응답을 받는것 처럼 꾸몄을 뿐이다. 위 예제의 구동순서는 아래와 같다.


 

<2-3 Start Basic Asynchronous Request 버튼>

 

첫째, 사용자가 그림 <2-3> 버튼을 클릭하면 이벤트가 발생해서 startRequest() 메소드가 실행된다.

둘째, XHR 객체가 생성되고 handleStateChange() 콜백함수가 XHR 객체의 onreadystatechange 속성에 저장된다.

셋째, GET/비동기 방식으로 서버에 요청을 보내는데, 이때 XHR 객체는 서버의 simpleResponse.xml 파일을 요구한다.

넷째, 서버는 Ajax 클라이언트의 요청 url 인 simpleResponse.xml 을 찾아서 읽은 후에 string 형식으로 XHR 객체로 보낸다.

다섯째, 콜백메소드는 XHR 의 state 가 변할때 실행되므로 readyState=4 이고 stat=200 일때 결과값을 브라우저에 보낸다. 결과 화면은 아래 그림과 같다.



<2-4 simpleRequest.html 결과>

 

직접 실행해보면 알겠지만 Internet Explore 및 FireFox 에서 같은 결과값을 얻는다.


이번장에서 마지막으로 살펴볼 것은 Ajax 의 보안문제이다. XHR 객체는 요청할 수 있는 서버의 리소스 url 에 제한이 걸려있다. 즉, 접근 할 수 있는 서버 리소스 url 은 XHR 객체가 존재하는 도메인에 있어야 한다는 의미다. 다른 말로 바꾸면 XHR 객체와 서버 프로그램은 같은 도메인에 있어야만 한다. 그렇다면 XHR 객체가 자기가 속해있는 도메인이 아닌 그 밖에 있는 서버의 url 을 호출하면 어떻게 될까? 이것은 브라우저 마다 차이가 있다. IE 의 경우에는 alert 창을 띄우면서 보안 위해요소가 있으니 계속 진행할 것인지 아닌지를 사용자가 판단할 수 있게 되어있고, FireFox 의 경우는 에러를 보여주며 요청자체를 브라우저에서 차단해 버린다.


Ajax 구동방식이 일반적인 웹의 방식과는 많이 다르고 또한 그 내부로직을 이해하는데 어려움이 있을 수도 있다. 하지만 XHR 의 주요 메소드와 주요 속성을 이해하는 데는 이글을 통해서 보다 쉽게 접근할 수 있으리라 기대해 본다.

출처 : Tong - bassdot님의 AJAX통

신고

AJAX를 이용한 Naver 키워드 검색 효과 구현

최근 개발 트렌드 중의 하나는 브라우저 또는 인터넷을 이용하여 기존의 C/S 환경과 같은 다양한 기능을 수행하는 클라이언트를 의미하는 Rich Client(Smart Client)에 대한 것이다.
지금까지 브라우저에서의 Rich 클라이언트는 디버깅 환경조차 제공되지 않는 환경에서의 혼신의 노력을 다한 개발자의 수고(?)에 의해
DHTML, JavaScript 등을 이용하여 스파게티처럼 얽혀 있는 구조하에서 사용자의 요구사항을 만족하는 클라이언트를 구현하였다.
하지만 복잡한 스크립트성 코드로 인해 브라우저에 영향을 많이 받게 되고 구현하는데도 한계가 있으며 개발에서 가장 어려운 부분이 EJB도 아니요, 복잡한 SQL도 아닌 자바스크립트라고 할 정도로 생산성을 저하시키는 복병중의 하나였다.
유지보수 단계에서도 이것은 계속되어 운영중인 시스템에서 자주 스크립트 오류가 발생하거나 다른 개발자가 작성한 코드는 너무 읽기 어려워 심지어는 새로 만드는 경우까지 발생하고 있다.
이런 상황에서 Rich Client에 대한 새로운 기술적 요구는 당연한 것이라 할 수 있다. 사용자의 요구 증대 및 개발자의 요구에 맞추어 Rich Client를 위해 기존에 사용하는 기술들을 확장하거나 새로운 기술들이 나타나고 있다.
대표적인 Rich Client 지원도구는 ActiveX, Applet, Java Web Start 등이지만 각각 MS, Java에 의존한다는 점과 각각 단점을 가지고 있다.
Applet, Java Web Start의 경우 JRE가 설치되어야 하는 문제가 있으며 지금까지 엔터프라이즈 애플리케이션에서 소외되어 왔다고 해도 과언이 아니다. ActiveX의 경우 Windows 환경에만 사용가능하며
이 또한 사용자가 반드시 동의를 해야만 설치되는 단점도 있다.
(필자의 경험상 많은 수의 회사 내부 사용자를 위해 ActiveX를 설치하게 하는 것은 반드시 필요한 경우가 아니라면
피하는 것이 좋다. 아직까지 이런 설치에 익숙하지 않는 사용자가 많기 때문이다.)
최근에는 플래쉬를 브라우저에 표시하고 플래쉬를 이용하여 Rich Client를 구현하게 하는 매크로미디어사에서 발표한 RIA(Rich Internet Application, http://www.macromedia.com/kr/resources/business/rich_internet_apps/)나 Laszlo(http://www.laszlosystems.com/)라는 것들도 나타나고 있다.

RIA 예제


Laszlo 예제 및 소스




플래쉬를 이용한 Rich Client의 경우 영화예매와 같이 비쥬얼한 요구가 많은 반면 데이터에 대한 처리가 많고 복잡한 비즈니스 처리가 필요한 기업의 인트라넷 환경에서는 아직 많이 사용되지 않고 있다.
하지만 필자가 운영하는 시스템 중 경영진이 보는 챠트, 분석된 데이터를 보여 주는 화면 등과 같이 화려한 그래픽을 요구하는 화면에서는 일부 사용되고 있다.
플래쉬를 이용한 Rich Client는 애플리케이션 전체에서 사용되기 보다는 이렇게 비쥬얼을 요구하거나 사용자의 액션에 따라 동적으로 화면의 처리를 요구하는 기능에서는 계속 확산될 것이라 예상된다.

이번 컬럼의 주제인 AJAX(Asynchronous JavaScript and XML)의 경우 단순히 스펙상으로는 Rich Client와 상관이 없을 것 같지만
어떻게 활용하느냐에 따라 Rich Client를 구현할 수 있기 때문에 최근에 주목을 받고 있다.
AJAX는 스크립트 상에서 서버와 비동기적으로 데이터를 주고 받기 위해 태어났다.
AJAX가 Rich Client에 사용되어 지는 것은 AJAX가 스크립트를 이용하여 브라우저의 화면을 Reload 시키지 않으면서 서버로 request로 전송을 한 다음 결과를 받아 처리할 수 있게 하는 특성 때문이다.
웹 기반 애플리케이션의 가장 큰 단점이 데이터 조회, 비즈니스 로직 처리 등과 같이 서버와의 연결이 필요할 때 마다 매번 화면이 reload되어야 한다는 점이었다.
화면이 reload 되는 것은 간단한 문제인 것 같지만 화면을 구성하는 프로그램에는 많은 구현상 제약이 있었다. 화면의 마지막 상태 정보를 가지고 있어 Reload 된 후에도 필요 정보만 변경되고 나머지 정보는 사용자가 설정한 그대로 남아있게 처리해야 하는 등 개발자에게는 여간 골치아픈 문제가 아닐수 없었다.
대표적인 사례가 게시판의 게시글 수정 기능인데 다음과 같은 순서로 사용자가 게시글을 수정하였다.

"테스트" 라는 제목으로 게시글 검색, 5페이지로 이동, 특정 게시글 선택하여 상세조회 → 수정 화면 이동 → 목록 버튼 클릭

여기서 목록 버튼을 클릭하거나 저장 버튼을 클릭한 다음에는 보통 목록 화면으로 이동하는데 이때의 목록 화면에는 "테스트" 제목의 검색 결과에서 5페이지가 나타나기를 사용자는 원할 것이다. 이러한 사용자의 요구사항을 위해 개발자는 상세조회화면, 수정화면, 수정처리 이런 각각의 액션에 이전에 사용자가 보았던 화면에 대해서 기억을 유지시키는 코드를 삽입해야만 한다.

이것을 해결하기 위해 지금까지는 눈에 보이지 않는 IFRAME을 많이 이용하였는데 AJAX를 이용하면 화면이 Reload 없이 전송이 가능하다. (물론 대부분의 Rich Client 솔루션들은 화면을 Reload 하지 않는다.)
이렇게 화면을 Relaod 하지 않기 때문에 화면 구성을 위한 HTML 코드는 전송하지 않고 요청된 처리 또는 처리 결과에 대한 데이터만 전송함으로써 네트워크 트래픽이 감소하는 효과도 볼 수 있다.
물론 사용자 응답 속도 측면에서도 기존의 웹 애플리케이션보다 훨씬 빠른 속도를 낼수 있다. 실제 애플리케이션 계층에서의 속도 변화는 거의 없지만 화면 Reload가 없어짐에 따라 사용자가 느끼는 체감속도가 빨라졌다는 측면이 있다.

AJAX의 사용성에 대해 실제 프로젝트에서 쉽고 유용하게 사용할 수 있는 Naver의 키워드 검색 기능을 구현해 봄으로써 AJAX가 어떻게 사용되어지는지에 대해 간단하게 살펴보자.



xmlhttp.js
function paramEscape(paramValue)
{
   return encodeURIComponent(paramValue);
}

function formData2QueryString(docForm)
{  
   var submitString = '';
   var formElement = '';
   var lastElementName = '';
  
   for(i = 0 ; i < docForm.elements.length ; i++)
   {
     formElement = docForm.elements[i];
     switch(formElement.type)
     {
        case 'text' :
        case 'select-one' :
        case 'hidden' :
        case 'password' :
        case 'textarea' :
           submitString += formElement.name + '=' + paramEscape(formElement.value) + '&';
           break;
        case 'radio' :  
           if(formElement.checked)
           {
             submitString += formElement.name + '=' + paramEscape(formElement.value) + '&';
           }
           break;
        case 'checkbox' :  
           if(formElement.checked)
           {
             if(formElement.name = lastElementName)
             {
                if(submitString.lastIndexOf('&') == submitString.length - 1)
                {
                   submitString = submitString.substring(0, submitString.length - 1);
                }
                submitString += ',' + paramEscape(formElement.value);
             }
             else
             {
                submitString += formElement.name + '=' + paramEscape(formElement.value);
             }
             submitString += '&';
             lastElementName = formElement.name;
           }
           break;
     }                                                                            
   }
   submitString = submitString.substring(0, submitString.length - 1);
   //document.all("result").value = submitString;
   return submitString;                              
}

function xmlHttpPost(actionUrl, submitParameter, resultFunction)
{
   var xmlHttpRequest = false;
  
   //IE인경우
   if(window.ActiveXObject)
   {
     xmlHttpRequest = new ActiveXObject('Microsoft.XMLHTTP');
   }
   else
   {
     xmlHttpReq = new XMLHttpRequest();
     xmlHttpReq.overrideMimeType('text/xml');
   }  
       
   xmlHttpRequest.open('POST', actionUrl, true);
   xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
   xmlHttpRequest.onreadystatechange = function() {
     if(xmlHttpRequest.readyState == 4)
     {
        switch (xmlHttpRequest.status)
        {
           case 404:
             alert('오류: ' + actionUrl + '이 존재하지 않음');
             break;
          case 500:
             alert('오류: ' + xmlHttpRequest.responseText);
             break;
          default:
             eval(resultFunction + '(xmlHttpRequest.responseText);');
             break;    
        }       
     }
   }
  
   xmlHttpRequest.send(submitParameter);            
}                        


ajax_search.html
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html;" charset="euc-kr">
<SCRIPT type="text/javascript" src="http://jaso.co.kr/xmlhttp.js"></SCRIPT>

<SCRIPT Language="javascript">
<!--
     
function keywordKeyDown()
{
    var keyCode = window.event.keyCode;
   
    if(keyCode ==  9)   return;     //Tab 키
    if(keyCode == 13)   return;     //Enter 키
    if(keyCode == 16)   return;     //Shift 키
    if(keyCode == 16)   return;     //Ctrl 키
    if(keyCode == 18)   return;     //Alt 키
    if(keyCode == 45)   return;     //Ins 키
    if(keyCode == 46)   return;     //Del 키
    if(keyCode == 33)   return;     //PgUp 키
    if(keyCode == 34)   return;     //PgDn 키
    if(keyCode == 35)   return;     //End 키
    if(keyCode == 36)   return;     //Home 키
   
    if(keyCode >= 37 && keyCode <= 40)   return;     //방향키
   
    //Keydown 이벤트 발생 시점에는 아직 TextField에 사용자가 입력한 키 값이 설정되지 않았기 때문에
    //브라우저가 이벤트에 반응하여 값을 설정할때 까지 잠시 기다린다.
    setTimeout('submitSearchKeyword()', 250);    

}

function submitSearchKeyword()
{
    var url = 'http://jaso.co.kr/searchKeyword.jsp';
    var queryString = formData2QueryString(document.MAIN_FORM);
    var resultProcessMethod = 'viewSearchKeywordResult';
   
    xmlHttpPost(url, queryString, resultProcessMethod);
}
               
function viewSearchKeywordResult(result)
{
    if(result == "")
    {
        var searchKeywordDiv = document.all("searchKeyword");
        searchKeywordDiv.innerHTML = "";
        searchKeywordDiv.style.visibility = "hidden";
    }
    else
    {
        var resultList = result.split('|');
        var viewResult = '';
        for(i = 0 ; i < resultList.length; i++)
        {
            if(i == 0)  viewResult += '<B>' + resultList[i] + '</B> <A href="javascript:hiddenSearchKeywordResult();">[닫기]</A><BR>'
            else        viewResult += '<A href="javascript:setKeyword(\'' + resultList[i] + '\');">' + resultList[i] + '</A><BR>'
        }       
        var searchKeywordDiv = document.all("searchKeyword");
        searchKeywordDiv.innerHTML = viewResult;
        searchKeywordDiv.style.visibility = "visible";
    }
}
   
function hiddenSearchKeywordResult()
{
    var searchKeywordDiv = document.all("searchKeyword");
    searchKeywordDiv.innerHTML = "";
    searchKeywordDiv.style.visibility = "hidden";
}
   
function setKeyword(selectedKeyword)
{
    document.MAIN_FORM.keyword.value = selectedKeyword;    
}
               
//-->
</SCRIPT>

<STYLE type="text/css">
<!--
  .scroll_div { scrollbar-face-color:#FFFFFF;
                scrollbar-highlight-color: #aaaaaa;
                scrollbar-3dlight-color: #FFFFFF;    
                scrollbar-shadow-color: #aaaaaa;
                scrollbar-darkshadow-color: #FFFFFF;
                scrollbar-track-color: #FFFFFF;    
                scrollbar-arrow-color: #aaaaaa;}
-->
</STYLE>
</HEAD>
<BODY onLoad="MAIN_FORM.keyword.focus()">
<FORM name="MAIN_FORM">
"가", "강"을 입력 해보세요.</BR>
<INPUT type="text" name="keyword" onkeydown="keywordKeyDown()" style:width=150px" autocomplete="off"><A href="javascript:alert('검색처리');">검색</A>
<DIV id="searchKeyword" style="width:250px;height:100px;visibility:hidden;background-color:#D1EED2;overflow=auto;font-size:12px" class="scroll_div">
</DIV>
</FORM>
</BODY>
</HTML>


소스에는 복잡한 내용은 거의 없다. 검색어 입력 Text의 Keydown 이벤트에 대한 처리 부분과 결과를 받아 화면에 나타내는 부분이 대부분이다. AJAX로 서버에 대한 요청은 다음과 같은 순서로 처리한다.

1.XMLHttpRequest 객체 생성
IE의 경우 new ActiveXObject('Microsoft.XMLHTTP');와 같이 생성하고 IE가 아닌 경우 new XMLHttpRequest(); 로 생성한다.

2. XMLHttpRequest open
open() 메소드에는 3개의 파라미터가 있는데 첫번째는 호출방법인 GET, POST 중에 하나가 온다.
필자의 경우 GET 방식보다는 POST 방식을 선호하기 때문에 대부분의 Request는 POST로 전송하는데 AJAX에서도 당연히 POST를 선호한다.
두번째 파라미터는 처리하는 서버의 URL 정보이다.
세번째 파라미터는 비동기/동기 방식에 대한 선택인데 true인 경우 비동기 방식으로 처리한다.
비동기 방식의 경우 Request를 전송한 다음 서버로부터 응답이 없더라도 브라우저는 계속해서 다른 처리를 할 수 있다.
사용자로부터 입력을 받거나 다른 스크립트를 수행할 수도 있다.
반면 동기방식은 요청후 서버로부터 결과를 받을때까지 다른 처리는 할 수 없도록 하는 방식이다.
검색 입력 필드의 예제에서와 같은 경우는 비동기 방식으로 처리하는 것이 보통이지만 데이터의 수정, 삭제, 입력에 대한 처리의 경우에는 처리가 완료되었다는 서버로부터의 응답을 받은 후 다른 액션을 할 수 있도록 하는 것이 좋다.

3.request의 content type 설정
GET 방식인 경우 설정할 필요가 없지만 POST 방식인 경우에는 content type을 다음과 같이 설정한다.
 
xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

4.서버로부터 처리결과 전송 후 수행해야 하는 기능에 대한 정의
XMLHttpRequest의 onreadystatechange 속성은 서버로부터의 처리 결과에 대한 상태코드가 변경되었을 때 수행해야 하는 스크립트 function을 지정한다.
여기서는 anonymous function을 사용하여 직접 정의 햐였다.
  
   xmlHttpRequest.onreadystatechange = function() {
     if(xmlHttpRequest.readyState == 4)
     {
        //서버로부터 받은 상태코드 및 데이터를 이용하여 처리로직 구현
     }
   }
  
서버로부터 받은 결과는 XMLHttpRequest의 responseText 속성에 저장되어 있으며 이것은 HttpServletResponse에 의해 전송된 문자열일수도 있고 웹서비스로 Request를 전송한 경우라면 SOAP 프로토콜로 전송된 XML 형식의 데이터일 것이다.
문자열인 경우 여기서의 예제와 같이 처리할 수 있지만 SOAP 프로토콜로 전송된 XML 데이터인 경우 각각의 브라우저에서 제공하는
XML 또는 SOAP을 지원한 객체를 이용하여 쉽게 핸들링할 수 있을 것이다.

5. 4번까지의 작업으로 Request가 전송되지 않는다. 전송을 위한 준비 작업과 처리결과 수신시 처리방법에 대한 정의만 하였을 뿐이다.
  실제 Request를 전송하기 위해서는 send()를 호출하여 전송한다.
  send()의 파라미터는 전송되는 Request의 파라미터 값이다. 물론 여기서도 일반적인 Request와 같이 '&', '=' 으로 구성된 문자열(예: keyword=test&page=2 )을 만들어서 전송하지만, XML 형태로 만들어서 전송하는 것도 가능하다
 
  xmlHttpRequest.send(submitParameter);            

searchKeyword.jsp
<%@ page contentType="text/html; charset=euc-kr" %>
<%@ page import="java.util.*" %>
<%
    HashMap keywordData = new HashMap();
    keywordData.put("가", "강타|강일|가을소나타|강주희|강은비|강력3반|강동원|가격비교|가방|강수지");
    keywordData.put("강", "강타|강일|강주희|강은비|강력3반|강동원|가방|강수지");
   
    request.setCharacterEncoding("UTF-8");

    String keyword = request.getParameter("keyword");
    //여기에서 데이터베이스로부터 해당 keyword로 시작하는 단어 검색
    //예제에서는 간단하게 하기 위해 Hash에서 가져오는 것으로 처리
    String result = (String)keywordData.get(keyword);
    if(result == null)      result = "키워드 없음";
   
    out.print(keyword + " 키워드 목록|" + result);
%>
  

AJAX의 Request에 대해 서버에서의 처리는 위의 소스에서 보는 것과 같이 비즈니스 기능에 대한 처리(여기서는 데이터조회)에 대해서는 기존과 동일하지만 처리결과를 브라우저로 전송할때 HTML 형태가 아닌 순수한 데이터형태만 제공하여 클라이언트에서 처리하도록 한다.
여기서는 데이터의 구분자를 '|' 문자로 구분하도록 처리하였다.
  
AJAX의 경우 JavaScript로 처리되기 때문에 인코딩에 대한 처리를 모두 UTF-8로 처리한다.
따라서 서버에서 Request를 받아 처리할 때에는 처리할 때에서 반드시 UTF-8로 디코딩하여 처리하여야 한다.
예제의 경우 searchKeyword.jsp에서 다음과 같이 request에 대해 처리하고 있다.

request.setCharacterEncoding("UTF-8");

지금까지 AJAX를 이용하여 간단한 기능을 구현함으로써 AJAX에 대해서 살펴보았다.
현재 AJAX의 위치는 위의 예제와 같이 애플리케이션의 특정 부분에 대해서만 주로 사용되어지고 있다.
앤터프라이즈 애플리케이션에서 전체를 AJAX 기반으로 구현하기에는 아직까지 경험과 사례가 많이 부족하고 AJAX가 Rich Client의 주류로 자리 잡을 것인지에 대해서는 아직까지는 미지수이다.

단점 : 복잡한 HTML에 대한 생성을 자바 스크립트와 같은 스크립트 언어로 처리 (View에 대한 처리가 기존의 Servlet에서 처럼 복잡하게 구현됨. 현재는 JSP 또는 Struts의 taglib를 이용하여 쉽게 처리하고 있지만 이것이 어렵다.)


연구해야할 사항 : 이런 단점을 극복할 수 있는 스크립트 처리에 대한 표준 마련 및 솔루션 echo2와 같이 이미 나와 있지만 좀 더 많은 연구 및 레퍼런스의 확보가 필요하다고 할 수 있다.
그리고 지금까지의 아키텍처는 프리젠테이션 - 컨트롤 - 비즈니스 - 데이터와 같은 형태의 레이어 구조만 있었지만 이제는 프리젠테이션 계층을 좀 더 세분화하여 프리젠테이션 내부에서의 View(Dynamic 화면 구성), 컨트롤(요청 및 응답에 대한 제어), 데이터(서버로부터 받은 또는 서버로 전송할)와 같은 세부적인 아키텍쳐에 대한 연구도 필요할 것 같다.


테스트 페이지 : ajax_search.html

소스다운로드 : source.zip

레퍼런스
http://www.onlamp.com/pub/a/onlamp/2005/05/19/xmlhttprequest.html
http://developer.apple.com/internet/webcontent/xmlhttpreq.html
http://www.onlamp.com/pub/a/onlamp/2005/05/19/xmlhttprequest.html
http://www.state26.com/download/formdata2querystring.txt
http://jania.pe.kr/wiki/jwiki/moin.cgi/JavaScriptTips

출처 : Tong - bassdot님의 AJAX통

신고

XMLHTTP

Computer/Web 2.0 2007.10.04 10:20 posted by scDragon
XMLHTTP
출처 : http://www.ssemi.net/index.php?pl=327&ct1=-1

요즘 xmlhttp를 궁금해하시는 분들이 많은듯해서...

xmlhttp는 원격파일을 부르는 방법입니다. 물론 로컬웹서버에 있는 파일도
가능하고, 같은 도메인 내에서라면 클라이언트영역에서도 사용가능합니다. 이렇게
개체를 참조합니다.

set xh=createobject("msxml2.xmlhttp")

get/post 둘다 사용가능합니다.

xh.open "get", "url", false
xh.open "post", "url", false

url은 비록 로컬서버라 해도 반드시 http로 시작합니다. 이렇게 연결을 연
다음에는 보냅니다.

xh.send something

마지막 something은 생략될수도 있습니다. 예를들어 get방식에서는 보낼게
없을겁니다. 그러므로 그냥,

xh.send

무언가를 보냈으면 받는게 있을수도 있습니다. 받는건 스트링, 바이너리, xml
등이 있습니다. 그러므로 호출자측 코드는 마지막으로 이렇게 끝납니다.

response.write xh.responseText 'or
response.binarywrite xh.responsebody 'or
response.write xh.responseXml.xml

한편 피호출자(url의 주소페이지)는 xmlhttp가 보내는걸 이렇게 받습니다.

response.write request.querystring(1)
response.write request.form(1)
response.binaryWrite request
xmldoc.load request

각각 get, post, binary, xml을 보냈을 경우입니다. 한편 두번째 post방식은
그동안 설명만으론 안됩니다. 사실은 보낼때, open과 send 사이에 다음 행을
집어넣습니다.

xh.setRequestHeader "Content-Type","application/x-www-form-urlencoded"

담에 보낼때,

xh.send "f1=1&f2=2"

이런식으로 쿼리스트링을 나누는것처럼 폼 변수를 나누어 보냅니다. 받는측에선
request.form(1), request.form(2), 이런식으로 나누어 받을겁니다.

한편 get방식은 오픈할때 그냥 url에 쿼리문자열을 넣어서 보냅니다.

xh.open "get", "http:// url/page.asp?a=1&b=2", false
그리고 send는 그냥,
xh.send

한글페이지를 불러올때는 바이너리로 열어야 글자가 안깨집니다.

xh.open "get", "http:// 한글페이지주소/page.asp", false
xh.send
response.binaryWrite xh.responseBody

이외에 xmlhttp는 바이너리나 xml dom 개체를 보내는데도 편리하게 사용할수
있습니다.

xmlhttp는 윈도우의 wininet.dll이라는 개체를 사용합니다. 단점은 멀티스레드가
안된다는겁니다. 그러므로 요즘은 serverXmlHttp가 권장됩니다. 사용법은
같습니다. 그냥 시작할때,

set xh=createobject("msxml2.serverXmlhttp")

server만 앞에 붙이면 됩니다. 한편 최신xml버전을 깔아두고, 버전까지 명시해서
사용하는것이 좋습니다.

set xh=createobject("msxml2.xmlhttp.4.0")
set xh=createobject("msxml2.serverXmlhttp.4.0")

이상 초간단 xmlhttp 설명이었습니다. 혹시 안되시는 부분, 추가응용이
있으시다면, 질문을 올려주십시오.


==================================================================

Han님 좋은 강좌 감사합니다.

예전에 Han님께서 제공해주신 xmlhttp에 대한 소개를 접하고
잘 사용하고 있습니다.

그동안 xmlhttp 개체를 자바스크립트에서

var oXMLHTTP = new ActiveXObject( "Microsoft.XMLHTTP" );

이렇게 생성해서 사용했는데
오늘 게시하신 글을 읽고 아래 처럼 바꿔봤으나 되는 게 있고
또 되지 않는게 있었습니다.

msxml2.xmlhttp         <-- 잘됨
msxml2.xmlhttp.4.0    <-- 잘됨

msxml2.serverxmlhttp         <-- 에러
msxml2.serverxmlhttp.4.0    <-- 에러

발생하는 스크립트 오류는 다음과 같습니다:

    오류: 자동화 서버는 개체를 작성할 수 없습니다.

xml은 4점대 버전이 설치되어 있습니다.

serverxmlhttp를 사용하기 위해 또 다른 설치가 필요한 건가요?


==================================================================

혹시 클라이언트 영역에서 사용하신게 아닌지요? 클라이언트영역에선 안됩니다.
그리고 버전명도 클라이언트영역에선 옛날걸로 하시는게
안전합니다(msxml2.xmlhttp). xml4.0을 가진 클라이언트는 1백명에 한명도
안될겁니다
==================================================================

Han님이 알려주신 msxml2.xmlhttp를 사용하다가 질문입니다...

4월9일 ADSI질문에 답변글을 만들면서 ASP에서 사용해 보았는데...
msxml2.serverxmlhttp 에서는 에러가 발생합니다.

msxml2.xmlhttp <-- 잘됨
msxml2.serverxmlhttp <-- 에러

msxml3.dll error '80072eef'
로그인 요청이 거부되었습니다.
/vir.asp, line 59

저는 xml3.0 이깔려있는 것은데... 최신버전 다운로드 경로도 알수있을까요?


'------------ 소스 ----------------
<%
Function execADSI(v_idx, v_name, v_path)
Dim xh, xPath, strPost, intVal

xPath = "http://220.xx.xx.xx/ADSI/VirCreate.asp"

v_idx = Server.URLEncode(v_idx)
v_name = Server.URLEncode(v_name)
v_path = Server.URLEncode(v_path)

strPost = "iis_idx=" & iis_idx & "&vir_name=" _
& vir_name & "&vir_path=" & vir_path

xPath = xPath & "?" & strPost
Response.Write xPath & "<hr>" '경로확인

Set xh = Createobject("msxml2.serverxmlhttp")
  xh.open "GET", xPath, false, PROC_ID, PROC_PW
  xh.send()            <---- 이부분에서 에러발생
  intVal = xh.ResponseText
Set xh = Nothing

execADSI = Cint(intVal)
End Function
%>

==================================================================

좋은 정보 감사합니다... 하하 ^^; 해결되었습니다...

ServerXMLHTTP 는 XMLHTTP 보다 엄격한것 같습니다.. Admin 이라고 하면
안되더군요... 검퓨터이름\Admin 이런식으로 정확한 사용자명만 되는군요...

MSDN에도 이런설명이 전혀 없고 도움말 예제가 아래와 같이 되어 있다는게 정말
황당하네요... >.<

<%@language=Jscript%>
<%
   var objSrvHTTP;
   objSrvHTTP = Server.CreateObject("MSXML2.ServerXMLHTTP.4.0");
   objSrvHTTP.open ("GET","http://someotherserver/secure.asp",false, _
                       "testuser", "testpassword";
   objSrvHTTP.send ();
   Response.ContentType = "text/xml";
   Response.Write (objSrvHTTP.responseXML.xml);
%>
----- 잘못된점...
1. ")"가 빠졌다.
2. Jscrpt에서는  _ 로 줄바꿈을 할수 없다.
3. "testuser" 이런식으로는 절대 로그인할수 없다.

저는 "testuser" (아이디) 때문에 4시간 넘게 방황했습니다...
다른분들도 저처럼 고생하는 일 없었으면 좋겠네요....

* 정확한 로그인 사용법
MSXML2.XMLHTTP --> Admin 또는 myCom\Admin
MSXML2.ServerXMLHTTP --> myCom\Admin


--- XMLHTTP의 추가적인 사용기....

>단점은 멀티스레드가 안된다는겁니다. 그러므로 요즘은 serverXmlHttp가 권장됩니다.
>사용법은같습니다.

han님이 말씀하신 것이 정말 맞는것 같습니다....

낮은 버전의 xmlhttp 는 개발서버에 올려놓고 테스트 하는데...
잘못된 인터넷 주소를 가지고 이렇게 저렇게 하다보니 금방 다운이 되버리네요...
한번 다운된후에 다른 웹브라우져를 올려놓고 http://www.yahoo.co.kr을 쳐도
서버가 먹통犬六?... scriptTimeOut까지 완전히 해당페이지가 뻗어
버리는군요... xmlhttp4.0의 경우는 그나마 에러처리가 잘되는것 같구요...

ServerXMLHTTP.4.0 는 정말 좋네요. ^_^ 확실한 에러처리로 다운도 전혀
안되고...


MSDN에도 이와 비슷한 문제에 관련된 글이 있군요...
ServerXMLHTTP 를 쓰라는 말 같은데.. 영어가 딸려서....
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaskdr/htm
l/askgui05012001.asp

신고

시스템 콜 추가

Computer/Linux 2007.10.03 17:24 posted by scDragon
http://blog.naver.com/hongsukil81/110020436900
신고

커널 컴파일이란?

Computer/Linux 2007.10.02 23:02 posted by scDragon

● 커널이란?

커널이란 운영체제(Operating System)에서 가장 핵심적인 역할인 자원 (메모리, 프로세서등)을 관리하며 시스템이 원활히 돌아갈 수 있도록 제어해 준다. 현재 우리가 리눅스(OS)라고 하는것도 실제적으론 리눅스란 운영체제의 커널 이름을 말하는것인데 이것이 확장된 의미로 사용되어 현재의 '리눅스'란 OS를 지칭하게도 된것이다. 그러므로 Linux = kernel이란 등식이 성립한다.


● 커널 컴파일은 언제 하나?

커널은 시스템을 운영하기 위한 가장 기본적인 코드가 포함되어 있어야 한다. 즉, 장치 혹은 시스템의 기능과 관련된 변화가 있을 경우 새로운 커널을 생성해야 한다.

현재 시스템은 어떠한 장치가 장착되어 있으며, 어떠한 목적으로 시스템을 운영할 것인가에 따라 커널의 구성요소가 달라질 수 있다는 것을 의미하며, 현재 커널에는 어떠한 것들을 지원하는가에 대한 정보는 커널 소스 디렉토리에 존재하는 Documentation 디렉토리에서 해당 정보를 얻을 수 있다.

만약 현재 시스템을 운영하기 위해 필요로 하는 장치에 대한 정보나 기능에 대한 핵심 코드가 현재 커널에 존재한다면 굳이 커널을 업그레이드 하거나 새로 생성할 필요가 없다. 하지만 간혹 커널 자체에 보안적 문제가 존재하기도 하는 데, 그러할 경우 꼭 커널 업그레이드를 해야한다.


● 커널은 어디서 구하나?

-> 최신 버전의 커널을
www.kernel.org 에서 구할 수 있다.



● 커널 컴파일 방법

여러 가지 방법들이 있지만 저장된 커널 옵션 설정 파일이 있다면 oldconfig를
저장된 파일이 없다면 menuconfig를 추천한다.

make oldconfig 또는 make menucofig

make oldconfig를 하기위해선 다음과 같은 작업이 필요하다.
예전에 설정한 커널옵션이 kernel.config 란 이름으로 저장되어있다면 이 파일을 새 버전의 커널이 있는 /usr/src/linux 디렉토리에 .config란 이름으로 카피한다.

-> 이 명령어는 새 버전의 커널이 있는 /usr/src/linux 디렉토리에서 하는 것이며, 대게의 경우 이전 버전의 설정파일 이름도 .config 이다.


● 모듈 컴파일 방법

모듈이 생성되는 디렉토리는 '/lib/modules/<커널버전>'이고 만약 현재 커널의 모듈을 다시 생성하려고 한다면 현재 커널의 모듈 디렉토리를 지우고 다시 컴파일 하기 바란다.

1. $make modules
2. $make modules_install


● Initrd 이미지 만들기

모듈을 만들고, 때에 다라서 initrd이미지도 만들어줄 필요가 있다. (가령 ext2/3 vfs같은 fs를 모듈로 제작했을때) 그럴때엔 mkinitrd 를 사용하자.

$cd /lib/modules
해서 방을 옮긴후
$ls
해보면 2.X.X 식으로 나와있다.
2.X.X인지 정확히 기억해서 <커널버전>에 똑같이 넣어준다.

$mkinitrd /boot/initrd-<커널버전 혹은 원하는 이름>.img <커널버전>


●● 실제 컴파일 해보기

  먼저 다운로드한 커널 소스를 /usr/src/ 디렉토리로 복사한다.
커널 컴파일은 /usr/src/<커널버전> 디렉토리에서 진행한다.

1. make mrproper : 이전의 컴파일시 설정되었던 설정을 제거
(최초 커널 컴파일시나 이전 설정한 컴파일 환경을 재사용한다면 생략 가능 )
2. make menuconfig : 텍스트 환경
혹은 make xconfig : x윈도우 환경
혹은 make oldconfig : 기존의 환경을 불러와서 새로운 것만 설정

3.  make dep : 설정한 영역의 의존성 검사
4.  make clean : 이전 컴파일시 생성된 파일을 삭제
( 4. 은 경우에 따라 생략할 수 있음 )

5. make bzImage : 설정한 기준으로 새로운 커널을 생성, 커널이 생성된 이후 생성된 커널을 커널이 위치해야 할 디렉토리(/boot)로 복사하는 과정은 관리자가 cp 명령어를 이용하여 직접 옮겨야 한다.
커널 생성시 또 하나의 파일(System.map)이 생성되며, 이 파일 또한 /boot로 복사한다.
$cp arch/i386/boot/bzImage /boot/<커널 버전>
$cp System.map /boot/System.map
6. mkinitrd : 이 문서 위의 "● Initrd 이미지 만들기" 참조

( 5,6의 과정은 make install을 수행하면 자동으로 수행된다. 단, make install은  "8.make modules_install"명령어 후에 실행해야 정상적으로 동작한다.
이 과정후 /etc/grub.conf의 설정이 올바르게 되었나 확인해 본다. )

7. make modules : 모듈들을 만든다. (시간이 좀 오래 걸린다. -_-)
  ※모듈 : 부팅시 메모리에 적재되는 것이 아니라 부팅 과정 이후 설정에 따라 혹은 해당 장치 혹은 기능을 사용할 경우에만 메모리에 올려서 사용한 후 사용하지 않을 경우 메모리에서 삭제하므로 효과적으로 메모리를 관리할 수 있다.

8. make modules_install : 만들어진 모듈들을 설치한다.
모듈이 위치할 디렉토리(/lib/modules/<커널버전>)으로 이동시켜주는 역할도 한다.
  ※커널 버전마다 지원하는 장치나 기능이 다르므로 모듈의 경우 /lib/modules 디렉토리에 해당 커널 버전 디렉토리를 생성하여 커널 버전별로 관리하게 된다.
     
   < 출처 : Red Hat Linux 9 (한빛미디어)박승규 저 >
   < 수정 : by.arm >
   

● 이 모든 명령어 한번에 실행 시키기

이 모든 명령어들을 한꺼번에 줄 수도 있다.

$make bzImage modules modules_install install
과 같이 한번에 끝낼수도 있다.

그러나
$make bzImage && make modules && make modules_install && make install
과 같은 방법을 추천한다.
이는 전단계에서 에러가 없을시에만 다음 단계로 넘어가는 방법을 취하기 때문이다.


● 커널 패치 방법

커널 소스 전체를 다운로드 하지 않고 변화된 영역만 제공하는 패치를 다운로드 할 경우 현재 소유하고 있는 커널 소스 이후 버전의 패치파일을 모두 다운로드 해야한다.
ex) patch-2.4.19.gz, patch-2.4.20.gz, ...

$patch -p0 < patch-2.4.19
$patch -p0 < patch-2.4.20
.
.
.


※ 단, 패치 가능한 커널소스는 kernel.org에서 배포하는 기본적인 소스이다.

예) Redhat9.0 의 커널 소스 (2.4.20-8) 과 같은 소스는 레드헷에서 kernel.org에서 배포하는 기본적인 소스를 수정한 것이기 때문에 정상적인 패치가 불가능하다. 즉, kernel.org에서 받은 2.4.20 과 같은 커널소스가 정상적인 패치가 가능한 소스라는 뜻이다.



◎ 최종 정리
1. make mrproper(최초 컴파일시 생략)
2. make menuconfig
혹은 make xconfig
혹은 make oldconfig
3. make dep
4. make clean (처음으로 컴파일 과정을 수행한다면 생략)
5. make bzImage

6. make modules
7. make modules_install

8. make install  


※ make install의 역할 : initrd 이미지를 생성해주고, vmlinuz 커널이미지와 System.map 맵핑 파일을 /boot에 복사하고 심볼릭 링크 파일 생성, 그리고 grub.conf를 알맞게 수정.




※ 위의 과정을 모두 수행후에 리부팅을 했을 때
kernel panic - not syncing : No init found, Try passing init = option to  kernel 과 같은 에러 메세지와 함께 부팅이 안된다면 2. make menuconfig과정에서 file systems 중 ext3 를 모듈<M>이 아닌 적재<*>로 바꾼 후 위의 과정을 반복한다.

★ 여기서 TIP : 윈도우XP를 마운트 하고 싶다면...
위의 과정중 2. make menuconfig과정에서 file systems중에 ntfs를 모듈<M>이나 적재<*>로 설정한다. 모듈 적재에 대해서 잘 모른다면 속편하게 적재<*>로 하기를 권장한다

신고


티스토리 툴바