ajax로 검색한 결과 :: 시소커뮤니티[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

회원가입 I 비밀번호 찾기


SSISO Community검색
SSISO Community메뉴
[카페목록보기]
[블로그등록하기]  
[블로그리스트]  
SSISO Community카페
블로그 카테고리
정치 경제
문화 칼럼
비디오게임 스포츠
핫이슈 TV
포토 온라인게임
PC게임 에뮬게임
라이프 사람들
유머 만화애니
방송 1
1 1
1 1
1 1
1 1
1

ajax로 검색한 결과
등록일:2008-04-02 15:07:33
작성자:
제목:팁: 세션 상태를 사용하여 불필요한 Ajax 트래픽 피하기 (한글)


서버 측 캐싱과 HTTP 에러 코드

developerWorks
문서 옵션

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

수평출력으로 설정

이 페이지 출력

이 페이지를 이메일로 보내기

이 페이지를 이메일로 보내기

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

David Mertz, Efficient server, Gnosis Software, Inc.

2008 년 1 월 15 일

가 능하기만 하다면, RESTful 방식으로 ajax 기반 애플리케이션을 포함하여 웹 애플리케이션을 구현한다면 많은 버그를 피할 수 있습니다. 하지만, REST (REpresentational State Transfer)의 함정은 비슷한 XMLHttpRequests를 통해 중복 데이터를 보내는 것입니다. 이 글에서는 세션 쿠키를 활용해서 서버 측 상태를 관리하여 클라이언트-서버 트래픽을 줄이는 방법을 설명합니다.

머리말

HTTP에 관한 기정 사실은 엄청난 힘과 취약점이다: HTTP는 Stateless 프로토콜이다. HTTP 서버 리소스로의 요청은 멱등(idempotent)이어야 하며, 호출 시 같은 요청이 같은 결과를 리턴해야 한다. 멱등성(Idempotency)은 REST의 중심적인 개념이다: 클라이언트 정보를 인코딩한 요청이 그 요청과 같은 데이터를 리턴해야 한다.

소셜 북마크

mar.gar.in mar.gar.in
digg Digg
del.icio.us del.icio.us
Slashdot Slashdot

REST 철학과는 반대로, ajax 애플리케이션들은 거의 언제나 Stateful이다. 웹 애플리케이션의 특정 필드나 영역은 서버 데이터의 현재 상태를 반영하면서, 클라이언트 JavaScript 폴링(poll)으로 현재 상태를 주기적으로 쿼리 하는데 사용된다. (이것을 보다 푸시(push) 지향적인 것으로 할 수 있는 방법이 있지만, 이 글의 논점에서는 벗어난다.) 하지만, 웹 애플리케이션은 서버가 다음 폴링 이벤트에 대해 알아 두어야 할 것을 지속적으로 트래킹 해 줄 것을 기대하고 있다: 클라이언트가 보았거나 보지 못한 데이터, 이미 발생했던 인터랙션이 바로 그것이다.

ajax 애플리케이션을 기술적으로 RESTful로 만드는 한 가지 일반적인 방법은 최신 데이터에 대한 모든 쿼리가 고유 URI를 갖도록 하는 것이다. 이를 테면, URL로 인코딩 된 매개변수 또는 숨겨진 폼 변수에 UUID를 포함시킨다; 예를 들어, XMLHttpRequest 객체는 다음과 같은 리소스를 얻게 된다.

http://myserver.example.com?uuid=4b879324-8ec0-4120-bba6-890eb0aa3fc0

바로 다음 폴링 이벤트 시, 1초 후에 다른 URI가 열린다.

멱등성은 트릭이 필요하다!

"같은 데이터"의 의미를 이해하는 것은 생각보다 어려운 문제이다. 이상적으로만, URI가 언제나 동일한 데이터를 리턴한다. 결국, 정적인 웹 페이지는 콘텐트가 수정될 때 변할 것이다. (다시 말해서, 오타가 공개된 아티클에서 수정된다.) 이러한 멱등성 뒤에 숨은 개념은 변경 사항들이 GET 요청 자체의 직접적인 결과가 되어서는 안된다는 것이다. 이와 같은 지속적으로 변하는 리소스를 갖는다는 것은 매우 합리적인 접근 방식이다.

http://myserver.example.com/latest_data/

문제는, "latest_data"를 구성하고 있는 것이 데이터 검색 여부, 시기, 사람과는 다른 것에 의존한다는 점이다. 서버는 완벽히 RESTful이 될 수 있지만, 여전히 "상태"를 반영한다.

최신 데이터 얻기

필 자의 동료인 Miki Tebeka와 필자는 JavaScript XMLHttpRequest() 객체를 사용하여, 서버에서 최신 데이터를 자주 폴링하는 웹 애플리케이션을 개발하는 상황을 직면했다. 이 글에서 필자가 제공한 Python 서버 예제는 Miki가 만들었던 인-하우스 모듈에 영감을 받은 것이지만, 더욱 단순화 되고 향상되었다.

여기에 두 가지 문제가 생겼다. 하나는 이전 요청 후에 어떤 것도 변하지 않았을 때 중요한 메시지를 보내지 않는다는 것이다. 두 번째 문제는 중복 데이터를 생성하는데 데이터베이스나 전산 리소스를 과도하게 사용하지 않는다는 것이다.

"Not Modified" 문제는 HTTP 프로토콜에서 바로 해결된다. 하지만 이러한 정확한 솔루션이 사용되고 있지는 않다. 우리가 해야 할 일은 HTTP 304 상태 코드를 리턴하는 것이다. 이것은 304 상태를 ajax 코드가 검사하게 하여, 만약 발견될 때에는 폴에서 보내진 데이터(부재)에 기반하여 클라이언트 애플리케이션 상태를 변경하지 말라는 것이다.

서 버 리소스 문제는 이전 데이터를 캐싱하고 최근 추가된 것을 모음으로써 해결될 수 있다. 이 솔루션은 전체 데이터 세트가 상호 의존적이기 보다는 "최신 데이터"가 개별적인 데이터 아이템들로 구성되어 있을 경우와 관련이 깊다. Listing 1을 보자:


Listing 1. 세션 실행 서버 코드: server.cgi
from datetime import datetime
session = ClientSession()
old_stuff = session.get("data", []) # Retrieve cached data
last_query = session.get("last", None)
prune_data(old_stuff, last_query) # Age out really-old data
new_stuff = get_new_stuff() # Look for brand-new data

if not new_stuff:
print "Status: 304" # "Not Modified" status
else
print session.cookie # New or existing cookie
print "Content-Type: text/plain"
print
all_stuff = old_stuff + new_stuff
session["data"] = all_stuff
session["last"] = datetime.now().isoformat()
print encode_data(all_stuff) # XML, or JSON, or...
session.save()

ClientSession 클래스를 주목해 보자. 기본적으로, 캐싱된 old_stuff에 상응하는 쿠키를 갖고 있는 각 클라이언트를 트래킹 해야 한다:


Listing 2. 세션 관리하기
from os import environ
from Cookie import SimpleCookie
from random import shuffle
from string import letters
from cPickle import load, dump

COOKIE_NAME = "my.server.process"

class ClientSession(dict):
def __init__(self):
self.cookie = SimpleCookie()
self.cookie.load(environ.get("HTTP_COOKIE",""))

if COOKIE_NAME not in cookie:
# Real UUID would be better
lets = list(letters)
shuffle(lets)
self.cookie[COOKIE_NAME] = "".join(lets[:15])

self.id = self.cookie[COOKIE_NAME].value
try:
session = load(open("session."+self.id, "rb"))
self.update(session)
except: # If nothing cached, just do not update
pass

def save(self):
fh = open("session."+self.id, "wb")
dump(self.copy(), fh, protocol=-1) # Save the dictionary
fh.close()

ajax 호출 만들기

서버 캐싱이 잘 되었다면, 데이터를 폴링하는 JavaScript는 매우 간단하다. 우리에게 필요한 것은 Listing 3과 같은 것이다:


Listing 3. 최신 데이터를 서버에서 폴링하기
var r = new XMLHttpRequest();
r.onreadystatechange=function() {
if (r.readyState==4) {
if (r.status==200) { // "OK status"
displayData(r.responseText);
}
else if (r.status==304) {
// "Not Modified": No change to display
}
else {
alertProblem(r);
}
}
}
r.open("GET",'http://myserver.example.com/latest_data/',true)
r.send(null);

displayData()alertProblem()의 구현은 예제에는 지정되지 않는다. 아마도, 전자는 특정 방식으로 받은 응답을 파싱 또는 처리해야 한다. 상세한 것은 JSON, XML 등 어떤 포맷이 데이터를 보내는데 사용되는지와, 실제 애플리케이션 요구 사항에 의존한다.

더욱이, 빠른 예제는 한 번에 폴링하는 방법만 보여준다. 장기 실행 애플리케이션에서, 우리는 반복적으로 setTimeout() 또는 setInterval() 콜백에 이러한 요청을 만든다. 또는, 애플리케이션에 기반하여, 폴링은 뒤이은 특정 클라이언트 애플리케이션 액션이나 이벤트를 발생시킨다.




위로


요약

이 글에서는 Python으로 작성된 코드를 설명했지만, CGI 또는 기타 서버 프로세스를 프로그래밍 하는데 사용되는 거의 모든 언어에도 같은 디자인이 적용된다. 일반적인 개념은 단순하다. 클라이언트 쿠키를 사용하여 캐싱된 데이터를 구분하고, 마지막 폴링 이벤트 후에 어떤 새로운 데이터도 발생하지 않았다면 304 상태를 보낸다. 여러분의 서버 프로그래밍 언어가 무엇이든지, 프로그램은 거의 같을 것이다.

많은 에러를 보여주지는 못했지만, 디자인은 쿠키를 사용할 수 없는 곳에서 작동을 수정하는데 있어서 매우 강력한 힘을 발휘한다. 클라이언트가 관련된 세션 쿠키를 갖고 있지 않으면, 이것이 쿠키를 받아들이지 않거나, 이것이 새로운 세션의 첫 번째 폴이기 때문에, old_stuff는 간단히 빈 리스트이고, 리턴된 데이터는 new_stuff의 일부가 된다. 추가할 가치가 있는 또 다른 기능은 현재 세션 상태를 보내는 클라이언트 메시지이다: 이것은 애플리케이션 디버깅과 일관성 없는 상태를 규명하는 방식으로서 사용하기에 알맞다. 캐시를 플러시 할 때 여러분이 잃게 되는 것은 서버 로드와 일정 대역폭이다. 그렇다고 해서 이것이 기반 멱등성을 위반하지는 않는다.



참고자료



필자소개

Photo of David Mertz

David Mertz는 대역폭을 절약하는 것이 대역폭을 획득하는 것이라고 생각하고 있다. Pore over David's life 또는 저서 Text Processing in Python 을 참조하라.