사용자가 자신의 브라우저에 사이트 URL(예: http://cool.si.te/)을
입력하면 URL이 http://cool.si.te/index.html로 전환되면서 HTML이
로드되고 뒤이어 CSS, JavaScript 및 이미지가 로드된다. 대부분의 사용자는 모를 정도로 약간의
차이가 있기는 하지만 웹 서버에서는 이 모든 활동을 지속적으로 감시한다. 잘못된 형식의 패턴이
나타나거나 이미지 파일이 없는 등의 오류가 발생했을 때 로그 파일에서 문제의 원인을 추적하는
방법을 이미 알고 있을 것이다. 아마도 로그 파일을 읽어서 사이트의 트래픽을 지속적으로 감시하는
웹 트래픽 분석기도 사용하고 있을 것이다. 하지만 웹 서버 로그에 수집되는 데이터의 대부분은
시스템 관리자에게 별로 도움이 되지 않는 정보이기 때문에 이 수준에 머물러서는 안되며 이러한
정보를 효율적으로 활용할 수 있는 수많은 효과적인 방법을 찾아야 한다. 이 기사에서는 웹 서버
로그를 효율적으로 활용하는 데 도움이 되는 몇 가지 아이디어와 기술을 살펴본 후 유명한 Apache
서버를 대상으로 이러한 기술을 테스트한다. 다른 여러 도구에서도 동일한 형식을 사용하고 있으므로
이 기사에서 제공하는 정보는 매우 광범위하게 적용된다.
Apache의 구성 또는 작업과 관련된 주요 오류는 오류 로그로 보고되지만 이 기사에서는 각 HTTP 요청에 대한 항목이 있는 액세스 로그에 중점을 두고 있다. NCSA(National Center for Supercomputing Applications)에서 시작된 기존 형식은 Mosaic(이후 Netscape 브라우저로 변경됨), HTTPd(최초 Apache 릴리스의 기본 코드 베이스) 및 최초의 동적 웹 컨텐츠 메커니즘이었던 CGI(Common Gateway Interface) 등의 주요 웹 혁신 기술의 밑바탕이었다. NCSA HTTPd에서는 기본적으로 공통 로그 형식을 사용했으며, 이 형식은 Apache에 채택된 이후 지금까지도 많은 웹 도구에서 사용하고 있다.
Listing 1은 공통 로그 형식 행의 예제이다.
Listing 1. 공통 로그 형식 행
125.125.125.125 - uche [20/Jul/2008:12:30:45 +0700] "GET /index.html HTTP/1.1" 200
2345
|
표 1에서는 필드에 대해 설명한다.
표 1. 공통 로그 형식 행의 필드
| 필드 이름 | 예제 값 | 설명 |
|---|---|---|
| 호스트 | 125.125.125.125 | 요청을 작성한 HTTP 클라이언트의 IP 주소 또는 호스트 이름이다. |
| identd | - | 클라이언트에 대한 Authentication Server Protocol(RFC 931) 식별자를 나타내는 이 필드는 거의 사용되지 않는다. 사용하지 않을 경우 "-"로 지정된다. |
| 사용자 이름 | uche | 401 응답 핸드셰이크를 통해 인증된 HTTP 인증 사용자 이름이다. 이는 일부 사이트에서 볼 수 있는 로그인 및 암호 대화 상자이며 웹 페이지에 포함된 로그인 양식과는 달리 사용자의 ID 정보가 서버 측 세션에 저장된다. 사용하지 않을 경우(예를 들어, 제한되지 않은 리소스에 대한 요청의 경우) "-"로 지정된다. |
| 날짜/시간 | [20/Jul/2008:12:30:45 +0700] | [dd/MMM/yyyy:hh:mm:ss +-hhmm] 형식으로 지정되는 날짜, 시간 및 시간대이다. |
| 요청 행 | "GET /index.html HTTP/1.1" | HTTP 요청의 맨 앞에 표시되는 부분으로 메소드("GET"), 요청된 리소스 및 HTTP 프로토콜 버전이 포함된다. |
| 상태 코드 | 200 | 요청의 상태를 나타내기 위해 응답에 사용되는 숫자 코드로 성공, 실패, 리디렉션 또는 인증 요구 사항 등을 나타낼 수 있다. |
| 바이트 | | 응답의 본문으로 전송되는 바이트 수이다. |
오늘날 많은 도구에서는 기본적으로 기능이 강화된 결합된 로그 형식을 사용한다. Listing 2는 이 형식을 보여 주는 예제이다.
Listing 2. 결합된 로그 형식 행
125.125.125.125 - uche [20/Jul/2008:12:30:45 +0700] "GET /index.html HTTP/1.1" 200
2345
"http://www.ibm.com/" "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9a8)
Gecko/2007100619
GranParadiso/3.0a8" "USERID=Zepheira;IMPID=01234"
|
이 기사에서는 공간의 제약으로 인해 여러 줄로 작성했지만 실제로는 모든 내용을 한 행으로 작성해야 한다. 결합된 로그 형식은 공통 형식에 참조 페이지, 사용자 에이전트 및 쿠키라는 세 가지 필드가 추가된 형식이다. 쿠키 필드(인용 부호 및 모두)를 생략하거나, 쿠키 및 사용자 에이전트 필드를 생략하거나, 세 가지 필드를 모두 생략할 수 있다. 표 2에서는 이러한 추가 필드에 대해 설명한다.
표 2. 결합된 로그 형식 행의 추가 필드
| 필드 이름 | 예제 값 | 설명 |
|---|---|---|
| 참조 페이지 | "http://www.ibm.com/" | 사용자 에이전트가 링크를 따라 한 사이트에서 다른 사이트로 이동하게 되면, URL이 참조했던 두 번째 사이트에 보고하게 된다. |
| 사용자 에이전트 | "Mozilla/5.0 (X11; U; Linux x86_64; en-US;
rv:1.9a8) Gecko/2007100619 GranParadiso/3.0a8" | 요청을 작성한 사용자 에이전트에 대한 정보를 제공하는 문자열이다(예: 브라우저 버전 또는 웹 크롤러). |
| 쿠키 | "USERID=Zepheira;IMPID=01234" | HTTP 서버에서 보낸 모든 쿠키의 실제 키/값 쌍은 응답을 통해 클라이언트에게 돌려보낼 수 있다. |
대부분은 웹 서버의 기본값을 사용하지만 Apache 로그의 형식을 쉽게 사용자 정의할 수 있다. 이렇게 사용자 정의하려면 이 기사에서 제공하는 코드의 많은 부분을 비롯한 여러 항목을 알맞게 조정해야 한다.
지금까지 이러한 형식이 잘 구조화되어 있다는 것을 살펴보았다. 따라서 상당히 손쉽게 정규식을 사용하여 정보를 가져올 수 있다. Listing 3은 로그 행을 구문 분석하여 요약 정보를 작성하는 간단한 프로그램이다. 이 프로그램은 Python으로 작성되었지만 핵심은 정규식이므로 다른 언어로 쉽게 이식할 수 있다.
Listing 3. 로그 행을 구문 분석하는 코드
import re
#This regular expression is the heart of the code.
#Python uses Perl regex, so it should be readily portable
#The r'' string form is just a convenience so you don't have to escape backslashes
COMBINED_LOGLINE_PAT = re.compile(
r'(?P<origin>\d+\.\d+\.\d+\.\d+) '
+ r'(?P<identd>-|\w*) (?P<auth>-|\w*) '
+ r'\[(?P<date>[^\[\]:]+):(?P<time>\d+:\d+:\d+) (?P<tz>[\-\+]?\d\d\d\d)\] '
+ r'"(?P<method>\w+) (?P<path>[\S]+) (?P<protocol>[^"]+)" (?P<status>\d+)
(?P<bytes>-|\d+)'
+ r'( (?P<referrer>"[^"]*")( (?P<client>"[^"]*")( (?P<cookie>"[^"]*"))?)?)?\s*\Z'
)
logline = raw_input("Paste the Apache log line then press enter: ")
match_info = COMBINED_LOGLINE_PAT.match(logline)
print #Add a new line
#Print all named groups matched in the regular expression
for key, value in match_info.groupdict().items():
print key, ":", value
|
COMBINED_LOGLINE_PAT 패턴은 결합된 양식을 위해
설계된 패턴이지만 결합된 양식이 공통 양식에 세 가지 선택적 필드만 추가된 양식이기 때문에
이 패턴은 두 양식 모두에 대해 정상적으로 작동한다. 이 패턴에서는 로그 행의 각 필드에 논리적
이름을 할당하기 위해 Python 관련 기능인 이름 지정된 캡처링 그룹을 사용한다. 다른 정규식에
이식하려면 정규 그룹을 사용하여 순차적으로 필드를 참조한다. 이 패턴은 매우 세분화되어 있다. 상태
행을 하나의 단위로 해서 수집하는 대신 편의성을 높이기 위해 HTTP 메소드, 요청 경로 및 프로토콜
버전을 따로따로 수집한다. 날짜/시간도 날짜, 시간 및 시간대로 분류된다. 그림 1에서는 Listing 2의
샘플 로그 행에 대해 Listing 3을 실행한 출력을 보여 준다.
그림 1. Listing 3의 출력
로그 파일에서 유용한 정보를 얻으려면 로보트에 의한 액세스와 사람에 의한 액세스를 구별할 필요가 있다. Google 및 Yahoo!와 같은 주요 검색 엔진에서는 매우 공격적인 인덱싱을 사용하기 때문에 만약 사용자의 사이트가 다소 알려져있다면, 이러한 스파이더로 인한 트래픽이 로그의 대부분을 차지할 수 있다. 100% 정확하게 스파이더 트래픽을 제거하기는 불가능하지만 로그 파일에서 일반적인 로보트 패턴을 검사하는 방법을 이용하면 높은 효과를 거둘 수 있다. 이러한 경우에는 "클라이언트" 필드가 핵심이다. Listing 4는 다른 언어로 쉽게 이식할 수 있도록 설계된 또 다른 Python 프로그램으로 파이프를 통해 로그 파일을 표준 입력에 연결한 다음 로보트 트래픽으로 판별된 행을 제외한 모든 행을 다시 돌려보낸다.
Listing 4. 로그 파일에서 검색 엔진 스파이더 트래픽 제거하기
import re
import sys
#This regular expression is the heart of the code.
#Python uses Perl regex, so it should be readily portable
#The r'' string form is just a convenience so you don't have to escape backslashes
COMBINED_LOGLINE_PAT = re.compile(
r'(?P<origin>\d+\.\d+\.\d+\.\d+) '
+ r'(?P<identd>-|\w*) (?P<auth>-|\w*) '
+ r'\[(?P<date>[^\[\]:]+):(?P<time>\d+:\d+:\d+) (?P<tz>[\-\+]?\d\d\d\d)\] '
+ r'"(?P<method>\w+) (?P<path>[\S]+) (?P<protocol>[^"]+)" (?P<status>\d+)
(?P<bytes>-|\d+)'
+ r'( (?P<referrer>"[^"]*")( (?P<client>"[^"]*")( (?P<cookie>"[^"]*"))?)?)?\s*\Z'
)
#Patterns in the client field for sniffing out bots
BOT_TRACES = [
(re.compile(r".*http://help\.yahoo\.com/help/us/ysearch/slurp.*"),
"Yahoo robot"),
(re.compile(r".*\+http://www\.google\.com/bot\.html.*"),
"Google robot"),
(re.compile(r".*\+http://about\.ask\.com/en/docs/about/webmasters.shtml.*"),
"Ask Jeeves/Teoma robot"),
(re.compile(r".*\+http://search\.msn\.com\/msnbot\.htm.*"),
"MSN robot"),
(re.compile(r".*http://www\.entireweb\.com/about/search_tech/speedy_spider/.*"),
"Speedy Spider"),
(re.compile(r".*\+http://www\.baidu\.com/search/spider_jp\.html.*"),
"Baidu spider"),
(re.compile(r".*\+http://www\.gigablast\.com/spider\.html.*"),
"Gigabot robot"),
]
for line in sys.stdin:
match_info = COMBINED_LOGLINE_PAT.match(line)
if not match_info:
sys.stderr.write("Unable to parse log line\n")
continue
isbot = False
for pat, botname in BOT_TRACES:
if pat.match(match_info.group('client')):
isbot = True
break
if not isbot:
sys.stdout.write(line)
|
스파이더 클라이언트 정규식의 목록이 완전하지 않기 때문에 새 검색 엔진이 항상 팝업된다. 따라서 트래픽을 검토할 때 찾아낸 새 스파이더를 포함시키기 위해 목록을 확장할 수 있도록 목록의 패턴을 에뮬레이트할 수 있어야 한다.
웹 서버 로그를 분석하고 웹에 통계를 보여 주는 데 사용할 수 있는 수많은 도구가 있다. 이 기사와 같이 빌딩 블록을 사용하면 자신만의 전문화된 로그 정보 표현을 쉽게 개발할 수 있다. 한 가지 추가 빌딩 블록은 Apache 로그 형식을 JSON(JavaScript Object Notation)으로 변환하는 것이다. JSON 양식의 정보가 있으면 클라이언트 측을 포함하여 JavaScript를 사용하여 정보를 쉽게 분석, 조작 및 표현할 수 있다.
도구를 직접 작성하지 않아도 된다. 이 섹션에서는 Apache 로그 파일을 JSON 변환으로 변환하는 방법을 보여 준다. 이 방법은 MIT의 SIMILE 프로젝트에서 개발된 강력한 데이터 프리젠테이션 도구인 Exhibit에 사용된다. 필자는 이전 기사인 "Practical linked, open data with Exhibit"(참고자료 참조)에서 이 도구에 대해 살펴보았다. 사용자는 이 도구에 JSON을 제공하기만 하면 된다. 그러면 Exhibit가 데이터를 표시, 필터링 및 검색하는 다양한 기능을 갖춘 동적 시스템을 작성할 수 있다. Listing 5(apachelog2exhibit.py)는 이전 예제를 기반으로 하지만 Apache 로그를 JSON의 Exhibit 양식으로 변환한다.
Listing 5. apachelog2exhibit.py. 비스파이더 트래픽 로그 항목을 Exhibit JSON으로 변환하기
import re
import sys
import time
import httplib
import datetime
import itertools
# You'll need to install the simplejson module
# http://pypi.python.org/pypi/simplejson
import simplejson
# This regular expression is the heart of the code.
# Python uses Perl regex, so it should be readily portable
# The r'' string form is just a convenience so you don't have to escape backslashes
COMBINED_LOGLINE_PAT = re.compile(
r'(?P<origin>\d+\.\d+\.\d+\.\d+) '
+ r'(?P<identd>-|\w*) (?P<auth>-|\w*) '
+ r'\[(?P<ts>(?P<date>[^\[\]:]+):(?P<time>\d+:\d+:\d+)) (?P<tz>[\-\+]?\d\d\d\d)\] '
+ r'"(?P<method>\w+) (?P<path>[\S]+) (?P<protocol>[^"]+)" (?P<status>\d+)
(?P<bytes>-|\d+)'
+ r'( (?P<referrer>"[^"]*")( (?P<client>"[^"]*")( (?P<cookie>"[^"]*"))?)?)?\s*\Z'
)
# Patterns in the client field for sniffing out bots
BOT_TRACES = [
(re.compile(r".*http://help\.yahoo\.com/help/us/ysearch/slurp.*"),
"Yahoo robot"),
(re.compile(r".*\+http://www\.google\.com/bot\.html.*"),
"Google robot"),
(re.compile(r".*\+http://about\.ask\.com/en/docs/about/webmasters.shtml.*"),
"Ask Jeeves/Teoma robot"),
(re.compile(r".*\+http://search\.msn\.com\/msnbot\.htm.*"),
"MSN robot"),
(re.compile(r".*http://www\.entireweb\.com/about/search_tech/speedy_spider/.*"),
"Speedy Spider"),
(re.compile(r".*\+http://www\.baidu\.com/search/spider_jp\.html.*"),
"Baidu spider"),
(re.compile(r".*\+http://www\.gigablast\.com/spider\.html.*"),
"Gigabot robot"),
]
MAXRECORDS = 1000
# Apache's date/time format is very messy, so dealing with it is messy
# This class provides support for managing timezones in the Apache time field
# Reuses some code from: http://seehuhn.de/blog/52
class timezone(datetime.tzinfo):
def __init__(self, name="+0000"):
self.name = name
seconds = int(name[:-2])*3600+int(name[-2:])*60
self.offset = datetime.timedelta(seconds=seconds)
def utcoffset(self, dt):
return self.offset
def dst(self, dt):
return timedelta(0)
def tzname(self, dt):
return self.name
def parse_apache_date(date_str, tz_str):
'''
Parse the timestamp from the Apache log file, and return a datetime object
'''
tt = time.strptime(date_str, "%d/%b/%Y:%H:%M:%S")
tt = tt[:6] + (0, timezone(tz_str))
return datetime.datetime(*tt)
def bot_check(match_info):
'''
Return True if the matched line looks like a robot
'''
for pat, botname in BOT_TRACES:
if pat.match(match_info.group('client')):
return True
break
return False
entries = []
# enumerate lets you iterate over the lines in the file, maintaining a count variable
# itertools.islice lets you iterate over only a subset of the lines in the file
for count, line in enumerate(itertools.islice(sys.stdin, 0, MAXRECORDS)):
match_info = COMBINED_LOGLINE_PAT.match(line)
if not match_info:
sys.stderr.write("Unable to parse log line\n")
continue
# If you want to include robot clients, comment out the next two lines
if bot_check(match_info):
continue
entry = {}
timestamp = parse_apache_date(match_info.group('ts'), match_info.group('tz'))
timestamp_str = timestamp.isoformat()
# To make Exhibit happy, set id and label fields that give some information
# about the entry, but are unique across all entries (ensured by appending count)
entry['id'] = match_info.group('origin') + ':' + timestamp_str + ':' + str(count)
entry['label'] = entry['id']
entry['origin'] = match_info.group('origin')
entry['timestamp'] = timestamp_str
entry['path'] = match_info.group('path')
entry['method'] = match_info.group('method')
entry['protocol'] = match_info.group('protocol')
entry['status'] = match_info.group('status')
entry['status'] += ' ' + httplib.responses[int(entry['status'])]
if match_info.group('bytes') != '-':
entry['bytes'] = match_info.group('bytes')
if match_info.group('referrer') != '"-"':
entry['referrer'] = match_info.group('referrer')
entry['client'] = match_info.group('client')
entries.append(entry)
print simplejson.dumps({'items': entries}, indent=4)
|
이제 파이프를 통해 Apache 로그 파일을 python apachelog2exhibit.py에
연결하여 출력 JSON을 캡처한다. Listing 6은 출력 JSON을 보여 주는 간단한 예제이다.
Listing 6. Apache 로그의 샘플 Exhibit JSON
{
"items": [
{
"origin": "208.111.154.16",
"status": "200 OK",
"protocol": "HTTP/1.1",
"timestamp": "2009-04-27T08:21:42-05:00",
"bytes": "2638",
"auth": "-",
"label": "208.111.154.16:2009-04-27T08:21:42-05:00:2",
"identd": "-",
"method": "GET",
"client": "Mozilla/5.0 (compatible; Charlotte/1.1;
http://www.searchme.com/support/)",
"referrer": "-",
"path": "/uche.ogbuji.net",
"id": "208.111.154.16:2009-04-27T08:21:42-05:00:2"
},
{
"origin": "65.103.181.249",
"status": "200 OK",
"protocol": "HTTP/1.1",
"timestamp": "2009-04-27T09:11:54-05:00",
"bytes": "6767",
"auth": "-",
"label": "65.103.181.249:2009-04-27T09:11:54-05:00:4",
"identd": "-",
"method": "GET",
"client": "Mozilla/5.0 (compatible; MJ12bot/v1.2.4;
http://www.majestic12.co.uk/bot.php?+)",
"referrer": "-",
"path": "/",
"id": "65.103.181.249:2009-04-27T09:11:54-05:00:4"
}
]
}
|
Exhibit를 사용하기 위해 Exhibit 라이브러리 JavaScript와 데이터에 대한 JSON을 로드하는 HTML 페이지를 작성한다. Listing 7은 로그 파일 정보에 대한 매우 간단한 Exhibit HTML 페이지이다.
Listing 7. Exhibit 로그 뷰어에 대한 HTML
<html>
<head>
<title>Apache log entries</title>
<link href="logview.js" type="application/json" rel="exhibit/data" />
<script src="http://static.simile.mit.edu/exhibit/api-2.0/exhibit-api.js"
type="text/javascript"></script>
<script src="http://static.simile.mit.edu/exhibit/extensions-2.0/time/time-extension.js"
type="text/javascript"></script>
<style>
#main { width: 100%; }
#timeline { width: 100%; vertical-align: top; }
td { vertical-align: top; }
.entry { border: thin solid black; width: 100%; }
#facets { padding: 0.5em; width: 20%; }
.label { display: none; }
</style>
</head>
<body>
<h1>Apache log entries</h1>
<table id="main">
<tr>
<!-- The main display area for Exhibit -->
<td ex:role="viewPanel">
<div id="what-lens" ex:role="view"
ex:viewClass="Exhibit.TileView"
ex:label="What">
</div>
</div>
<!-- Timeline view for the feed data -->
<div id="timeline" ex:role="view"
ex:viewClass="Timeline"
ex:label="When"
ex:start=".timestamp"
ex:colorKey=".status"
ex:topBandUnit="day"
ex:topBandPixelsPerUnit="200"
ex:topBandUnit="week">
</div>
</td>
<!-- Boxes to allow users narrow down their view of feed data -->
<td id="facets">
<div ex:role="facet" ex:facetClass="TextSearch"></div>
<div ex:role="facet" ex:expression=".path" ex:facetLabel="Path"></div>
<div ex:role="facet" ex:expression=".referrer" ex:facetLabel="Referrer"></div>
<div ex:role="facet" ex:expression=".origin" ex:facetLabel="Origin"></div>
<div ex:role="facet" ex:expression=".client" ex:facetLabel="Client"></div>
<div ex:role="facet" ex:expression=".status" ex:facetLabel="Status"></div>
</td>
</tr>
</table>
</body>
</html>
|
그림 2는 간단한 HTML 소스로부터 가져온 다양한 정보를 보여 주는 출력 보기 중 하나이다. 이러한 작업은 모두 Exhibit에 의해 자동으로 수행되며 패싯 상자를 통해 표시된 항목의 세부 사항을 확인할 수 있다. 예를 들어, 하나의 원래 주소에 대한 액세스 패턴을 분석할 수 있다.
그림 2. Exhibit 로그 파일 뷰어("What" 보기)
그림 3에서는 또 다른 출력 보기인 시간 보기를 보여 준다. 이 보기로 직접 이동하려면 기본 보기의 맨 위에 있는 When을 클릭한다.
그림 3. Exhibit 로그 파일 뷰어("When" 보기)
로그 파일의 정보는 다양한 용도로 사용할 수 있다. 필자는 이 정보를 사용해서 XSLT 지원 브라우저를 사용 중인 사이트에 대한 비스파이더 방문자의 비율을 검사하여 브라우저 측 XSLT와 같은 새로운 웹 기술의 적용 시기를 결정했다. 또한 검색 엔진에서 가져온 참조 페이지 필드를 검토하여 Weblog 항목에 대한 유용한 태그를 제안할 때도 로그 정보를 사용했다. 로그 파일에 대한 일반적인 통계 및 분석을 제공하는 데 사용할 수 있는 도구가 많기는 하지만 기존 도구를 이용하여 로그에 대한 모든 요구 사항을 해결할 수는 없기 때문에 웹 아키텍트의 경우 로그 정보를 직접 처리하는 방법을 익혀 두면 큰 도움이 될 것이다.
교육
- 최신 버전의 Apache에서 지원되는 로그 파일 형식에 대해 알아보자.
- "Using
regular expressions"(David Mertz 저)에서 이 기사의 기술에 대한 정보를 확인할 수 있다.
- 다양한 정규식, 기능 및 해당 구문에 대해 알아보자.
- PHP 사용자의 경우 "PHP의
정규식 이해하기, Part 1" 및 Part 2(Martin Streicher 저)에서
이 기사의 코드를 이식하는 데 필요한 정보를 살펴볼 수 있다.
- "Practical
linked, open data with Exhibit"(Uche Ogbuji 저)에서 Exhibit에 대해 자세히 알아보자.
- developerWorks 기술 행사 및 웹 캐스트를 통해 최신 정보를 얻을 수 있다.
- developerWorks 웹 개발 영역에서
웹 기술에 특화된 기사와 튜토리얼을 통해 웹 개발 기술을 향상시키자.
- My developerWorks: 웹 개발을
비롯한 관심 분야에 대한 그룹, 블로그 및 활동을 찾아보거나 만들어보자.
제품 및 기술 얻기
- 오픈 소스 Apache HTTP Server가 가장 널리 사용된다.
- Listing 5에서는 simplejson 라이브러리를 사용한다.
Uce Ogbuji은 Fourthought Inc.의 컨설턴트이자 공동창립자이다. Fourthought는 4Suite, 4Suite Server를 개발하고 있다.