Article Category

분류 전체보기 (202)
사는 이야기 (15)
Programming (174)
Photo (5)
게임 (2)
프로젝트 (0)

Recent Comment

Recent Trackback

Calendar

«   2019/05   »
      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 31  

Archive

  • Total96,493
  • Today0
  • Yesterday2
  1. 2009.09.03
    jquery 플러그인
  2. 2008.08.06
    Ajax 마스터하기, Part 3: Ajax의 고급 요청 및 응답

File upload


Ajax File Upload

jQUploader

Multiple File Upload plugin

jQuery File Style

Styling an input type file

Progress Bar Plugin


Form Validation


jQuery Validation


Auto Help

Simple jQuery form validation

jQuery XAV - form validations

jQuery AlphaNumeric

Masked Input

TypeWatch Plugin

Text limiter for form fields

Ajax Username Check with jQuery


Form - Select Box stuff


jQuery Combobox

jQuery controlled dependent (or Cascadign) Select List

Multiple Selects

Select box manipulation

Select Combo Plugin

jQuery - LinkedSelect

Auto-populate multiple select boxes

Choose Plugin (Select Replacement)


Form Basics, Input Fields, Checkboxes etc.


jQuery Form Plugin

jQuery-Form

jLook Nice Forms

jNice

Ping Plugin

Toggle Form Text

ToggleVal

jQuery Field Plugin

jQuery Form’n Field plugin


jQuery Checkbox manipulation

jTagging

jQuery labelcheck

Overlabel

3 state radio buttons

ShiftCheckbox jQuery Plugin

Watermark Input

jQuery Checkbox (checkboxes with imags)

jQuery SpinButton Control


jQuery Ajax Form Builder

jQuery Focus Fields

jQuery Time Entry


Time, Date and Color Picker


jQuery UI Datepicker

jQuery date picker plugin

jQuery Time Picker

Time Picker

ClickPick


TimePicker

Farbtastic jQuery Color Picker Plugin

Color Picker by intelliance.fr


Rating Plugins


jQuery Star Rating Plugin

jQuery Star Rater

Content rater with asp.net, ajax and jQuery

Half-Star Rating Plugin


Search Plugins


jQuery Suggest

jQuery Autocomplete

jQuery Autocomplete Mod

jQuery Autocomplete by AjaxDaddy

jQuery Autocomplete Plugin with HTML formatting

jQuery Autocompleter

AutoCompleter (Tutorial with PHP&MySQL)

quick Search jQuery Plugin


Inline Edit & Editors


jTagEditor

WYMeditor

jQuery jFrame

Jeditable - edit in place plugin for jQuery

jQuery editable

jQuery Disable Text Select Plugin

Edit in Place with Ajax using jQuery


jQuery Plugin - Another In-Place Editor

TableEditor

tEditable - in place table editing for jQuery


Audio, Video, Flash, SVG, etc


jMedia - accessible multi-media embedding

JBEdit - Ajax online Video Editor

jQuery MP3 Plugin

jQuery Media Plugin

jQuery Flash Plugin


Embed QuickTime

SVG Integration


Photos/Images/Galleries


ThickBox

jQuery lightBox plugin

jQuery Image Strip

jQuery slideViewer

jQuery jqGalScroll 2.0

jQuery - jqGalViewII


jQuery - jqGalViewIII

jQuery Photo Slider

jQuery Thumbs - easily create thumbnails

jQuery jQIR Image Replacement

jCarousel Lite

jQPanView

jCarousel

Interface Imagebox

Image Gallery using jQuery, Interface & Reflactions


simple jQuery Gallery

jQuery Gallery Module

EO Gallery

jQuery ScrollShow

jQuery Cycle Plugin

jQuery Flickr

jQuery Lazy Load Images Plugin

Zoomi - Zoomable Thumbnails

jQuery Crop - crop any image on the fly


Image Reflection


Google Map


jQuery Plugin googlemaps

jMaps jQuery Maps Framework

jQmaps

jQuery & Google Maps

jQuery Maps Interface forr Google and Yahoo maps

jQuery J Maps - by Tane Piper


Games


Tetris with jQuery

jQuery Chess

Mad Libs Word Game

jQuery Puzzle

jQuery Solar System (not a game but awesome jQuery Stuff)


Tables, Grids etc.


UI/Tablesorter

jQuery ingrid


jQuery Grid Plugin

Table Filter - awesome!

TableEditor

jQuery Tree Tables

Expandable “Detail” Table Rows

Sortable Table ColdFusion Costum Tag with jQuery UI

jQuery Bubble


TableSorter

Scrollable HTML Table

jQuery column Manager Plugin

jQuery tableHover Plugin

jQuery columnHover Plugin

jQuery Grid

TableSorter plugin for jQuery

tEditable - in place table editing for jQuery

jQuery charToTable Plugin


jQuery Grid Column Sizing

jQuery Grid Row Sizing


Charts, Presentation etc.


jQuery Wizard Plugin

jQuery Chart Plugin

Bar Chart


Border, Corners, Background


jQuery Corner

jQuery Curvy Corner


Nifty jQuery Corner

Transparent Corners

jQuery Corner Gallery

Gradient Plugin


Text and Links


jQuery Spoiler plugin

Text Highlighting

Disable Text Select Plugin

jQuery Newsticker


Auto line-height Plugin

Textgrad - a text gradient plugin

LinkLook - a link thumbnail preview

pager jQuery Plugin

shortKeys jQuery Plugin

jQuery Biggerlink

jQuery Ajax Link Checker


Tooltips


jQuery Plugin - Tooltip


jTip - The jQuery Tool Tip

clueTip

BetterTip

Flash Tooltips using jQuery

ToolTip


Menus, Navigations


jQuery Tabs Plugin - awesome! [demo nested tabs]


another jQuery nested Tab Set example (based on jQuery Tabs Plugin)

jQuery idTabs

jdMenu - Hierarchical Menu Plugin for jQuery

jQuery SuckerFish Style

jQuery Plugin Treeview

treeView Basic

FastFind Menu

Sliding Menu

Lava Lamp jQuery Menu


jQuery iconDock

jVariations Control Panel

ContextMenu plugin

clickMenu

CSS Dock Menu

jQuery Pop-up Menu Tutorial

Sliding Menu


Accordions, Slide and Toggle stuff


jQuery Plugin Accordion


jQuery Accordion Plugin Horizontal Way

haccordion - a simple horizontal accordion plugin for jQuery

Horizontal Accordion by portalzine.de

HoverAccordion

Accordion Example from fmarcia.info

jQuery Accordion Example

jQuery Demo - Expandable Sidebar Menu

Sliding Panels for jQuery

jQuery ToggleElements


Coda Slider

jCarousel

Accesible News Slider Plugin

Showing and Hiding code Examples

jQuery Easing Plugin

jQuery Portlets

AutoScroll

Innerfade


Drag and Drop


UI/Draggables

EasyDrag jQuery Plugin

jQuery Portlets

jqDnR - drag, drop resize

Drag Demos


XML XSL JSON Feeds


XSLT Plugin

jQuery Ajax call and result XML parsing

xmlObjectifier - Converts XML DOM to JSON


jQuery XSL Transform

jQuery Taconite - multiple Dom updates

RSS/ATOM Feed Parser Plugin

jQuery Google Feed Plugin


Browserstuff


Wresize - IE Resize event Fix Plugin

jQuery ifixpng

jQuery pngFix

Link Scrubber - removes the dotted line onfocus from links


jQuery Perciformes - the entire suckerfish familly under one roof

Background Iframe

QinIE - for proper display of Q tags in IE

jQuery Accessibility Plugin

jQuery MouseWheel Plugin


Alert, Prompt, Confirm Windows


jQuery Impromptu

jQuery Confirm Plugin

jqModal


SimpleModal


CSS


jQuery Style Switcher

JSS - Javascript StyleSheets

jQuery Rule - creation/manipulation of CSS Rules

jPrintArea


DOM, Ajax and other jQuery plugins


FlyDOM


jQuery Dimenion Plugin

jQuery Loggin

Metadata - extract metadata from classes, attributes, elements

Super-tiny Client-Side Include Javascript jQuery Plugin

Undo Made Easy with Ajax

JHeartbeat - periodically poll the server

Lazy Load Plugin

Live Query

jQuery Timers


jQuery Share it - display social bookmarking icons

jQuery serverCookieJar

jQuery autoSave

jQuery Puffer

jQuery iFrame Plugin

Cookie Plugin for jQuery

jQuery Spy - awesome plugin

Effect Delay Trick


-Jquery 플러그인 모음 이거한방이면 끝..!! : http://www.seek-blog.com/41065/14090/240-plugins-jquery.html

 -light box(이미지 미리보기,pre,next) : http://leandrovieira.com/projects/jquery/lightbox/

 -이미지 스크롤 : http://benjaminsterling.com/2007/09/09/jquery-jqgalscroll-photo-gallery/

 -이미지 싸이클 : http://www.malsup.com/jquery/cycle/

 -BlockUI Plugin(processing,처리중 등등,confirm) :  http://www.malsup.com/jquery/block/#element

 -UI Modal : http://jquery.com/demo/thickbox/

 -플래쉬 플러그인 삽입 : http://jquery.lukelutman.com/plugins/flash/#examples

 -Photo Slider Tutorial : http://opiefoto.com/articles/photoslider#example

 -jScrollPane : http://kelvinluck.com/assets/jquery/jScrollPane/jScrollPane.html

 -Accessible News Slider  : http://www.reindel.com/accessible_news_slider/#examples

 - unobtrusive tabs(탭메뉴) : http://stilbuero.de/jquery/tabs/#fragment-29
      http://www.sunsean.com/idTabs/#t3

 -jQuery Ajax Link Checker : http://troy.dyle.net/linkchecker/

 -jQuery Form Plugin  : http://malsup.com/jquery/form/#code-samples

 -jquery.suggest, an alternative jQuery based autocomplete library(자동완성)
  http://www.vulgarisoip.com/2007/06/29/jquerysuggest-an-alternative-jquery-based-autocomplete-library/
  http://nodstrum.com/2007/09/19/autocompleter/

 -jlook(폼객체 리뉴얼)  :

  http://envero.org/jlook/

 -jQuery - LinkedSelect(멀티 select) :

  http://www.msxhost.com/jquery/linked-selects/json/

 -Masked Input Plugin    : (입력포맷 확인)
  http://digitalbush.com/projects/masked-input-plugin
  http://www.appelsiini.net/projects/jeditable/default.html

 -Overlabel with JQuery(박스안에 워터마킹처리)  : http://scott.sauyet.com/Javascript/Demo/Overlabel/

 -Styling an input type="file"(파일찾기 이미지 처리)
   http://www.quirksmode.org/dom/inputfile.html

 -jQuery UI Datepicker v3.0 Examples(달력)
  http://marcgrabanski.com/code/ui-datepicker/

 -jQuery Validation Plugin(폼체크,포커싱)
  http://jquery.bassistance.de/validate/demo-test/ 
  http://www.texotela.co.uk/code/jquery/focusfields/
 
 -jQuery columnHover plugin(컬럼 하이라이트/컬럼 컨트롤)
  http://p.sohei.org/stuff/jquery/columnhover/demo/demo.html
  http://p.sohei.org/stuff/jquery/columnmanager/demo/demo.html

 -tablesorterDocumentation(테이블 순서)
  http://tablesorter.com/docs/index.html 

 -jQuery Accordion Demo(컨테이너 예제)
  http://jquery.bassistance.de/accordion/?p=1.1.1

 -jQPanView based in jQuery 1.1(이미지 확대보기)
  http://projects.sevir.org/storage/jpanview/index.html

 -jQuery Impromptu(confirm,alert 등등)
  http://trentrichardson.com/Impromptu/

 -jqGrid Examples(그리드)
  http://trirand.com/jqgrid/jqgrid.html#

 -Toggle HTML-Elements with jQuery
  http://jquery.andreaseberhard.de/toggleElements/

 -UI/Sortables(테이블 위치변경 ^^)
  http://docs.jquery.com/UI/Sortables

 -뉴스 슬라이더(부분보기,전체보기)
  http://www.reindel.com/accessible_news_slider/

 -로컬 스크롤러
  http://www.freewebs.com/flesler/jQuery.LocalScroll/

 -핫키 테스트
  http://jshotkeys.googlepages.com/test-static.html

 -슬라이더
  http://docs.jquery.com/UI/Slider/slider

 -쇼핑카트
  http://www.mimul.com/pebble/default/2007/10/30/1193753340000.html

 -테이블 소트
  http://www.mimul.com/pebble/default/2007/11/06/1194348600000.html

 -이미지 나중에 로딩시키기
  http://www.mimul.com/pebble/default/2007/11/10/1194695220000.html

 -오토탭(입력시 폼객체 자동넘김)
  http://dev.lousyllama.com/autotab/

 -실시간 폼객체 수정
  http://www.appelsiini.net/projects/jeditable/custom.html

 -프린트
  http://www.designerkamal.com/jPrintArea/#
 -차트
  http://www.reach1to1.com/sandbox/jquery/jqchart/

 -CSS Dock Menu (Jquery + CSS)
  후니넷에서 보삼
 -툴팁
  http://www.codylindley.com/blogstuff/js/jtip/

 -XML데이터 뿌리기
  http://blog.reindel.com/src/jquery_browse/
  http://www.xml.com/pub/a/2007/10/10/jquery-and-xml.html
  http://www.mimul.com/pebble/default/2006/11/05/1162710000000.html

 -Clearing Form

  http://www.learningjquery.com/2007/08/clearing-form-data

 --암호 복잡성 체크

  http://phiras.googlepages.com/PasswordStrengthMeter.html

 --Form Serialize

  http://dev.jquery.com/wiki/Plugins/FastSerialize

 --GetString 퍼라미터 가져오기
  http://www.mathias-bank.de/2006/10/28/jquery-plugin-geturlparam/

 --검색 후 콤보생성(ajax with combo) 아주 유용함
  http://extjs.com/deploy/ext/examples/form/forum-search.html
  http://extjs.com/deploy/ext/examples/grid/edit-grid.html-->그리드

 --파일 업로드(input=file) 리폼
  http://www.appelsiini.net/projects/filestyle/demo.html

Jquery Best
 http://www.spicyexpress.net/general/jquerry-at-it-best-downloadable-jquerry-plugins-and-widgets-for-you-2/

웹디자인 템플릿트(2.0)
 -http://www.templateworld.com/free_templates.html

----------------------------------------
Jquery tag cloud
http://www.ajaxrain.com/tagcloud.php
Jquery 기본설명
http://www.zzbb.kr/34
----------------------------------------

^^Star Rater(순위) --활용가능성 높음
http://www.m3nt0r.de/devel/raterDemo/

^^ AJAX CALLING --활용가능성 높음
http://cgaskell.wordpress.com/2006/11/02/jquery-ajax-call-and-result-xml-parsing/

--AJAX 아이디 중복체크 - 활용가능성 아주높음
http://www.shawngo.com/gafyd/index.html

^^ jQuery framework plugins which provide a way to sort and nest elements in web applications, using drag-and-drop(테이블드래그앤드랍) --활용가능성 중간
http://code.google.com/p/nestedsortables/

^^Simple tableSorter(리스트 정렬) 활용가능성 중간
http://motherrussia.polyester.se/docs/tablesorter/

^^Cookie 활용가능성 높음
http://www.stilbuero.de/2006/09/17/cookie-plugin-for-jquery/

^^태깅 --그냥한번보자..ㅎ
http://www.alcoholwang.cn/jquery/jTaggingDemo.htm

^^죽이는 어코디언 메뉴
http://dev.portalzine.de/index?/Horizontal_Accordion--print

^^ AJAX Indicator 이미지
http://qureyoon.blogspot.com/2006/11/make-your-own-loading-gif.html

----------------------------------------
2007.12.28 찾은것
----------------------------------------
http://rikrikrik.com/jquery/quicksearch/#usage
http://nadiaspot.com/jquery/confirm/#examples
http://host.sonspring.com/portlets/
http://jquery.andreaseberhard.de/toggleElements/
http://www.getintothis.com/pub/projects/rb_menu/
http://icon.cat/software/iconDock/0.8b/dock.html
http://www.nuernberg.de/internet/portal/index.html
http://rikrikrik.com/jquery/shortkeys/#examples
http://rikrikrik.com/jquery/pager/#examples
http://famspam.com/facebox/ --라이트박스같은것
http://www.andreacfm.com/
http://www.vulgarisoip.com/2007/06/29/jquerysuggest-an-alternative-jquery-based-autocomplete-library/ --autocomplete
http://www.gcmingati.net/wordpress/wp-content/lab/jquery/newsticker/jq-liscroll/scrollanimate.html - 뉴스 스크롤
http://d-scribe.de/webtools/jquery-pagination/demo.htm# --페이징
http://tinymce.moxiecode.com/example_full.php?example=true --Open Source WYSWYG 웹 에디터
http://www.laptoptips.ca/projects/tinymce-advanced/ --Open Source WYSWYG 웹 에디터 advanced
http://extjs.com/ -또다른 RIA JAVASCRIPT 프레임워크
http://www.digital-web.com/extras/jquery_crash_course/ -jquery로 만든 비행 예약 시스템 데모(Passenger Management )
http://markc.renta.net/jquery/ --jquery 간단예제
http://www.xml.com/pub/a/2007/10/10/jquery-and-xml.html?page=2 -jquery와 XML
http://www.stilbuero.de/2006/09/17/cookie-plugin-for-jquery/  -쿠키 플러그인
http://jquery.com/files/demo/dl-done.html --간단한 어코디언 메뉴
http://mjslib.org/doc/legacy/fieldgroup.html --폼필드 컨트롤
http://extjs.com/deploy/dev/examples/tree/two-trees.html --트리
http://www.amcharts.com/column/ - 차트(바로 사용^^)
http://particletree.com/features/rediscovering-the-button-element/ - 버튼 스타일링
http://www.i-marco.nl/weblog/jquery-accordion-menu/ - 실용성 높은 어코디언 메뉴


http://www.sastgroup.com/jquery/240-plugins-jquery
http://jquery.bassistance.de/jquery-getting-started.html

좀더 흥미로운 내용이 많이 있습니다.. HOME > 분류없음를 확인하세요
Trackback 0 and Comment 0

Ajax 마스터하기, Part 3: Ajax의 고급 요청 및 응답 (한글)

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

 

 


 

 



난이도 : 초급

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

2006 년 6 월 12 일
2006 년 6 월 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
다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기

토론


필자소개

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 기술을 활용한 명백한 업적 중 하나로 남아 있다.

Trackback 0 and Comment 0