 |  |
|
난이도 : 중급 Jack D Herrington, Senior Software Engineer, Leverage Software Inc.
2007 년 9 월 04 일 웹이 사람들에 의해 구동되는 이러한 시대에는, 독자들이 사이트의 콘텐트를 평가하고 검토할 수 있게 하는 것이 중요합니다. 레이팅(rating)과 코멘팅(commenting) 기능을 Ajax를 사용하여 사이트에 추가하는 방법을 배워봅시다.
우리 모두는 평가하기를 좋아한다. 필자가 생각하기에 이것은 우리 DNA의 일부인 것 같기도 하다. 필자는 딸과 함께 영화 보러 가는 것을 좋아한다. 이전에는 그 자체를 즐겼지만, 이제 4살이 된 딸 아이는 훨씬 더 까다로워 졌다. 필자는 딸에게 "thumbs up" 과 "thumbs down" 레이팅 시스템(à la Ebert and Roeper)을 사용하는 방법을 가르쳤다. (딸아이는 슈렉 3에 thumbs up을 주었다.) 인터넷에서 제품이나 아티클 같은 것에 레이팅과 리뷰를 하는 것이 왜 이렇게 대중적이 되었는지는 자명하다.
이 글에서는, MySQL, PHP, Prototype.js, Asynchronous JavaScript™와 XML (Ajax)을 결합한 것을 사용하여 간단한 레이팅 및 코멘팅 시스템을 사이트에 추가하는 방법을 설명하도록 하겠다.
레이팅 시스템
필자의 딸은 비록 thumbs up과 thumbs down을 좋아하지만, 대부분의 사람들은 약간 다른 시스템을 사용하고 있다. 바로 5성(five-star) 레이팅 시스템이다. 이 경우, 별 1개는 제일 안 좋은 것이고, 별 다섯 개는 매우 좋은 것이며, 별 세 개는 보통을 의미한다. 이 예제에서, 필자는 5성 레이팅 시스템을 영화에 적용하고자 한다. 여러분은 영화 대신 아티클, 제품, 팟캐스트 등, 여러분의 필요에 맞게 적용할 수 있다.
Listing 1은 이 예제의 스키마이다. 영화용 테이블로 시작한다. 매우 간단하다. 영화 아이디와 영화의 이름을 자동 증가시킨다. 그리고 나서, 레이팅 테이블에는 해당 영화에 대한 투표를 보유하게 된다. 이 테이블은 movie_id를 통해서 영화 테이블로 연결되고 한 개 이상의 필드(레이팅)을 갖고 되는데, 이는 1부터 5까지의 숫자이다.
Listing 1. rating.sql
DROP TABLE IF EXISTS movies;
CREATE TABLE movies (
movie_id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR( 128 ) NOT NULL,
PRIMARY KEY ( movie_id )
);
DROP TABLE IF EXISTS ratings;
CREATE TABLE ratings (
movie_id INTEGER NOT NULL,
rating INTEGER NOT NULL
);
|
"한 사람당 한 번의 투표(one person, one vote)"를 실행하고자 한다면, 사용자 아이디를 레이팅 테이블에 추가하여 그 사람이 영화에 대해 단 한번만 투표할 수 있도록 할 수 있다. 필자는 이 예제를 되도록이면 단순히 유지할 것이므로 이 부분은 제외하겠다.
이 스키마를 데이터베이스로 가져오기 위해서는, 먼저 데이터베이스를 만든 다음, 그 스키마를 추가한다. 아래 명령어를 사용한다.
% mysqladmin create comments
% mysql comments < ratings.sql
|
MySQL 설치에 기반하여 로그인 크리덴셜을 추가할 수도 있다.
또한, 영화를 디스플레이 하고 각 영화에 대한 레이팅 페이지를 가리키는 페이지가 필요하다. Listing 2는 인덱스 페이지이다.
Listing 2. index.php
<html>
<body>
<?php
require_once("DB.php");
$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( 'SELECT * FROM movies' );
while( $res->fetchInto( $row ) )
{
?>
<a href="rate.php?id=<?php echo($row[0]) ?>"><?php echo($row[1]) ?></a><br/>
<?php
}
?>
</body>
</html>
|
쉽게 하기 위해 Structured Query Language (SQL) 문을 사용하여 영화 테이블에 유명한 영화들을 추가했다. (다운로드) 인덱스 페이지에서 그 영화를 볼 수 있다. (그림 1)
그림 1. 영화 리스트
예제 코드에서 이 리스팅이 나오지 않으면, 아마도 PEAR 저장소에서 DB 모듈을 설치하지 않았을 것이다. 이를 설치하려면, 다음 명령어를 실행한다.
영화 리스트가 보인다면, 이제는 영화를 평가하고 현재 투표 상황과 평균 랭킹을 디스플레이 하는 페이지를 구현 할 차례이다. Listing 3은 rate.php 페이지로 이 모든 것을 수행하는 방법을 보여준다.
Listing 3. rate.php
<?php
require_once("DB.php");
$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$id = $_GET['id'];
$title = '';
$res = $db->query( 'SELECT name FROM movies WHERE movie_id=?', array( $id ) );
while( $res->fetchInto( $row ) ) { $title = $row[0]; }
?>
<html>
<head>
<title><?php echo($title); ?></title>
<script src="prototype.js"></script>
<script>
function rate( value ) {
new Ajax.Updater( 'rating', 'ratemovie.php?id=<?php echo($id)?>&v='+value );
}
</script>
</head>
<body>
<h1><?php echo($title); ?></h1>
<div id="rating">
<img src="star_off.gif" onclick="rate(1)"></img>
<img src="star_off.gif" onclick="rate(2)"></img>
<img src="star_off.gif" onclick="rate(3)"></img>
<img src="star_off.gif" onclick="rate(4)"></img>
<img src="star_off.gif" onclick="rate(5)"></img>
<br/><br/>
<?php
$res2 = $db->query(
'SELECT count( rating ), sum(rating ) FROM ratings WHERE movie_id=?',
$id
);
while( $res2->fetchInto( $row ) )
{
?>
Votes: <?php echo($row[0]); ?><br/>
Average Rating: <?php echo($row[1]/$row[0]); ?>
<?php
}
?>
</div>
</body>
</html>
|
이 파일의 맨 위에, 매개변수로서 전달된 아이디가 있는 영화 제목이 있다. 스크립트 중간에, prototype.js 라이브러리를 포함시키고, Prototype 라이브러리를 사용하여 Ajax를 통해 ratemovie.php 페이지를 호출하는 rate() JavaScript 함수를 생성한다. 그리고 나서, 별 이미지 세트를 추가한다. 별 이미지는 별이 클릭될 때 rate() 함수를 호출하는 스크립트를 갖고 있다.
파일 밑에, 빠른 쿼리를 실행하여 투표 카운트와 해당 영화에 대한 투표 총계를 얻을 수 있다. 그 데이터를 디스플레이 하여 독자가 이 영화가 어떻게 평가되는지를 알 수 있게끔 할 수 있다.
Listing 4의 ratemovie.php 스크립트는 레이팅을 데이터베이스에 추가하고, Hypertext Markup Language (HTML) 코드를 리턴하여 원래 페이지에서 투표한 별들과 리뷰 카운트를 대체하는 책임을 맡고 있다.
Listing 4. ratemovie.php
<?php
require_once("DB.php");
$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$v = $_GET['v'];
$id = $_GET['id'];
$sth = $db->prepare( 'INSERT INTO ratings VALUES ( ?,? )' );
$db->execute( $sth, array( $id, $v ) );
?>
<img src="star_<?php echo( ($v>0)?'on':'off' ) ?>.gif"></img>
<img src="star_<?php echo( ($v>1)?'on':'off' ) ?>.gif"></img>
<img src="star_<?php echo( ($v>2)?'on':'off' ) ?>.gif"></img>
<img src="star_<?php echo( ($v>3)?'on':'off' ) ?>.gif"></img>
<img src="star_<?php echo( ($v>4)?'on':'off' ) ?>.gif"></img>
<br/><br/>
<?php
$res2 = $db->query(
'SELECT count( rating ), sum(rating ) FROM ratings WHERE movie_id=?',
$id
);
while( $res2->fetchInto( $row ) )
{
?>
Votes: <?php echo($row[0]); ?><br/>
Average Rating: <?php echo($row[1]/$row[0]); ?>
<?php
}
?>
|
레이팅 페이지는 그림 2와 같다.
그림 2. 레이팅 페이지
이 별들 중 하나를 클릭하면, 투표가 데이터베이스에 추가된다. 별, 투표, 평균 레이팅에 대한 HTML 코드가 바뀌어서 투표의 추가를 반영한다.
이상적으로는, 홈페이지에서 영화에 대한 모든 스코어를 볼 수 있게끔 하고 싶다. Listing 5는 이러한 바람에 맞는 새로운 버전의 인덱스이다.
Listing 5. index2.php
<html>
<body>
<table>
<?php
require_once("DB.php");
$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( 'SELECT * FROM movies' );
while( $res->fetchInto( $row ) )
{
$res2 = $db->query(
'SELECT count( rating ), sum(rating ) FROM ratings WHERE movie_id=?', $row[0]
);
$rating = 0.0;
while( $res2->fetchInto( $row2 ) ) { $rating = $row2[1] / $row2[0]; }
?>
<tr><td align="center">
<?php echo( $rating > 0 ? $rating : 0 ) ?>
<td><td>
<a href="rate2.php?id=<?php echo($row[0]) ?>"><?php echo($row[1]) ?></a>
</td></tr>
<?php
}
?>
</table>
</body>
</html>
|
새로운 버전의 인덱스에는 각 영화에 대한 레이팅이 있다. (그림 3)
그림 3. 업데이트 된 영화 페이지
지금까지 Ajax, PHP, MySQL, Prototype.js JavaScript 라이브러리를 사용하여 투표 스킴을 구현하는 방법을 설명했다.
이제는 영화에 대한 코멘트 또는 리뷰를 구현할 차례이다.
코멘팅
웹의 코멘팅 시스템은 블로그 코멘팅 시스템(블로그 엔트리에 한 줄의 코멘트가 있음) 같은 간단한 것에서부터, Slashdot 같은 데서 볼 수 있는 보다 고급스러운, 쓰레디드 코멘팅 시스템까지 다양하다.
예제는 어디까지나 단순하게 유지한다. 여러분의 필요에 맞게 조정할 수 있다.
기존 데이터베이스 스키마에 테이블을 추가하는 것으로 시작한다. Listing 6.
Listing 6. comments.sql
DROP TABLE IF EXISTS comments;
CREATE TABLE comments (
movie_id INTEGER NOT NULL,
email VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
comment TEXT NOT NULL
);
|
이것은 movie_id 필드를 통해 영화를 연결하는 코멘트 테이블이다. 이메일 주소, 코멘트를 단 사람의 이름, 코멘트 텍스트가 있다. 이것은 WordPress 또는 MoveableType 같은 블로그 소프트웨어와 비슷한 매우 기본적인 코멘팅 시스템이다.
쓰레디드 코멘팅 시스템을 만들고 싶다면, 자동 증가하는 아이디를 테이블에 추가한 다음, 각 코멘트에 대한 부모 코멘트를 가리키는 parent_id 필드를 갖는다. 그 필드가 무효라면, 그 코멘트는 탑 레벨 코멘트이다.
코멘팅 기능을 레이팅 페이지에 추가하려면, 페이지 밑에 추가 스크립트를 포함시킨다. Listing 7은 새로운 PHP 코드이다.
Listing 7. rate2.php
...
<h2>Comments</h2>
<div id="comments">
<?php
$res3 = $db->query(
'SELECT * FROM comments WHERE movie_id=?',
$id
);
while( $res3->fetchInto( $row3 ) )
{
?>
<div>
<a href="mailto:<?php echo($row3[1]) ?>"><?php echo($row3[2]) ?></a> says:
'<?php echo($row3[3]) ?>'
</div>
<?php
}
?>
</div>
<div style="margin-top:20px;">Add your own comment:</div>
<form id="cform">
<input type="hidden" name="id" value="<?php echo($id)?>">
<table>
<tr><td>Name:</td><td><input type="text" name="name"></td></tr>
<tr><td>Email:</td><td><input type="text" name="email"></td></tr>
<tr><td>Comment:</td><td><textarea name="comment" id="comment_text"></textarea></td></tr>
</table>
</form>
<button onclick="addcomment()">Add Comment</button>
<script>
function addcomment()
{
new Ajax.Updater( 'comments', 'addcomment.php',
{
method: 'post',
parameters: $('cform').serialize(),
onSuccess: function() {
$('comment_text').value = '';
}
} );
}
</script>
</body>
</html>
|
이 스크립트는 "comments" <div> 태그에 데이터베이스에서 가져온 영화에 현재 코멘트를 채우는 것으로 시작한다. 표준 HTML <form> 태그에는 코멘트를 단 사람의 이름, 이메일 주소, 코멘트에 대한 필드가 포함된다. 이 폼에는 숨은 값(현재 리뷰 되는 영화의 아이디)이 포함되기 때문에 코멘트 추가 스크립트는 코멘트를 할당 할 영화가 어떤 것인지를 알 수 있다.
폼 아래에 있는 Add Comment 버튼은 addcomment() JavaScript 함수를 호출한다. 이 함수는 Prototype.js 라이브러리에서 Ajax.Updater 객체를 사용하여 addcomment.php 스크립트를 호출한다. Prototype.js에서 제공되는 serialize() 함수를 사용하여 이름, 이메일 주소, 코멘트를 압축한다.
이 코드는 성공할 경우, 코멘트 필드의 텍스트를 리셋하여 사용자가 이름과 이메일 주소를 재입력 할 필요 없이 원하는 대로 많은 코멘트를 추가할 수 있도록 지정하고 있다.
Listing 8은 addcomment.php 스크립트이다.
Listing 8. addcomment.php
<?php
require_once("DB.php");
$id = $_POST['id'];
$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$sth = $db->prepare( 'INSERT INTO comments VALUES ( ?, ?, ?, ? )' );
$db->execute( $sth, array( $id,
$_POST['email'], $_POST['name'], $_POST['comment' ] ) );
$res = $db->query('SELECT * FROM comments WHERE movie_id=?', $id );
while( $res->fetchInto( $row ) )
{
?>
<div>
<a href="mailto:<?php echo($row[1]) ?>"><?php echo($row[2]) ?></a> says:
'<?php echo($row[3]) ?>'
</div>
<?php
}
?>
|
이 스크립트는 POST 데이터에 지정된 코멘트를 데이터베이스에 추가한다. 원래 페이지에서 수행되었던 것과 똑 같이, HTML 코드로서 모든 코멘트를 출력하고, 사용자가 제공했던 추가 코멘트를 디스플레이 한다.
그림 4와 같은 새로운 레이팅 페이지를 볼 수 있다.
그림 4. 코멘트가 달린 업데이트 된 레이팅 페이지
이 코멘팅 시스템은 뷰어에게 즉각적인 피드백을 제공한다. 사용자가 Add Comment를 클릭하면, 코멘트는 새로운 코멘트로 바로 업데이트 된다. 이 시스템은 다른 뷰어가 제출하는 추가 코멘트도 보여준다.
이 예제를 확장하고 서버를 정기적으로 폴링하여 새로운 코멘트로 코멘트 섹션을 업데이트 하고 싶을 수도 있다. 물론, addcomment.php를 호출해서는 안된다. 새로운 것을 추가하지 말고 코멘트를 리턴하는 또 다른 스크립트가 필요하다. Prototype.js는 Ajax.PeriodicalUpdater 클래스를 제공하는데 — 아이디, 등급, URL 포함— 이는 여러분이 지정한 대로 웹 페이지의 어떤 부분이라도 업데이트 한다.
RSS 피드 추가하기
이 예제를 확장하는 또 다른 쉬운 방법은 영화의 리스트를 랭킹과 함께 RSS 피드로서 반출하는 것이다. Listing 9는 이를 수행하는 코드 모습이다.
Listing 9. rss.php
<?php
header( "content-type:text/xml" );
?>
<rss version="0.91">
<channel>
<title>Movie rankings</title>
<link>http://localhost/comments/rss.php</link>
<?php
require_once("DB.php");
$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( 'SELECT * FROM movies' );
while( $res->fetchInto( $row ) )
{
$res2 = $db->query(
'SELECT count( rating ), sum(rating ) FROM ratings WHERE movie_id=?', $row[0]
);
$rating = 0.0;
while( $res2->fetchInto( $row2 ) ) { if ( $row2[0] > 0 ) $rating = $row2[1] / $row2[0]; }
?>
<item>
<title><?php echo($row[1]) ?> -
<?php echo( $rating > 0 ? $rating : 0 ) ?> stars</title>
<link>http://localhost/commentsts/rate2.php?id=<?php echo($row[0]) ?></link>
<description><?php echo($row[1]) ?></description>
</item>
<?php
}
?>
</channel>
</rss>
|
Listing 9의 코드는 HTML 포맷으로 데이터를 작성하는 것에 대한 대안일 뿐이다. <table>, <tr>, and <td> 태그들 대신에, <title>,
<description>, <link> 태그를 사용하여 각 영화 페이지를 가리키고 있다. Firefox 브라우저에서 이 페이지를 가리키면 그림 5와 같은 모습을 볼 수 있다.
그림 5. 브라우저의 RSS 피드
매우 쉽다. PHP에서 XML 피드를 가져오는 것이 그렇게 어렵지 않다.
다음 명령어를 로컬에서 사용함으로써 이 코드를 실행하면,
RSS XML을 직접 볼 수 있다. Listing 10은 RSS에서 발췌한 것이다.
Listing 10. RSS 발췌
<rss version="0.91">
<channel>
<title>Movie rankings</title>
<link>http://localhost/comments/rss.php</link>
<item>
<title>Star Wars - 4.5 stars</title>
<link>http://localhost/commentsts/rate2.php?id=1</link>
<description>Star Wars</description>
</item>
<item>
...
|
결론
사용자 생성 콘텐트와 Web 2.0에 대한 많은 소문들을 듣게 될 것이다. 이 글의 예제에서 보듯, Prototype.js 라이브러리 같은 툴을 사용하여 Ajax 애플리케이션들을 쉽게 구현할 수 있다. 뷰어들이 사이트에 있는 자료에 등급을 매기고 코멘트를 쉽게 달 수 있도록 하는 기능을 통해, 여러분은 훌륭한 사용자 생성 콘텐트를 제공할 수 있다.
다운로드 하십시오 | 설명 | 이름 | 크기 | 다운로드 방식 |
|---|
| 이 기술자료의 소스 코드 | x-ajaxxml5-code.zip | 22 KB | HTTP |
|---|
참고자료 교육
제품 및 기술 얻기
토론
필자소개
기사에 대한 평가
|  |