Caution: You are browsing the legacy symfony 1.x part of this website.

symfony advent calendar day eleven: syndication feed

1.0

지난 줄거리

좀 이르긴 하지만 askeet 어플리케이션이 거의 베타 수준에 들어섰습니다. 사실 핵심기능들이 (질문 올리기, 답변 읽기, 새 답변 작성하기) 완성되었기 때문에, 많은 분들이 동의하시리라 생각됩니다. 문제는 재방문하는 사용자들이 최근 askeet 웹사이트에 어떤 일들이 일어났는지 알기 어렵다는 것입니다. 여러분들은 그들을 위해 최신 뉴스들을 제공해야 합니다. 이런 일을 손쉽게 하기에 적합한 것이 바로 뉴스 피드입니다. 오늘은 askeet 에 뉴스 피드를 추가해 보도록 하겠습니다.

인기 질문 피드

head 부분에 피드 링크하기

우리가 하고자 하는 것은 글로벌 레이아웃의 <head> 부분에 인기 질문 RSS 피드를 삽입하는 것입니다. HTML 코드로는 아래와 같을 것입니다.

<link rel="alternate" type="application/rss+xml" title="Popular questions on askeet" href="http://askeet/frontend_dev.php/feed/popular" />

작업을 하기 위해서는 layout.php 을 여시고 <head> 부분에 다음을 추가하시면 됩니다.

<?php echo auto_discovery_link_tag('rss', 'feed/popular') ?>

이상입니다. auto_discovery_link_tag 헬퍼 (AssetHelper.php 헬퍼와 함께 자동으로 적재됩니다.) 가 라우팅 엔진을 통해서 module/action 을 실제 URI 로 변환하여 줄 것입니다.

플러그인 설치

Symfony 는 피드의 대부분을 자동으로 생성해주는 sfFeed 플러그인을 제공합니다. 이를 설치하기 위해서는 symfony 커맨드라인 툴을 사용하면 됩니다.

$ symfony plugin-install symfony plugin-install http://plugins.symfony-project.com/sfFeedPlugin

이 명령을 통해 플러그인이 askeet/lib/symfony/plugins/ 디렉토리에 설치됩니다.

플러그인이 어떻게 프레임웍을 확장하는 것인지, 여러분이 사용하는 기능을 플러그인에 담아서 여러 프로젝트에 사용할 것인지 등에 대한 자세한 내용은 온라인 문서들 중 플러그인 부분 에 자세히 나와 있습니다.

플러그인을 설치한 후 캐쉬를 지우는 것을 잊지 마십시오. 만약 plugin-install 명령이 작동하지 않으신다면 (보통 PEAR 를 사용하지 않고 symfony 를 설치한 경우에 그럴 수 있습니다), SVN 저장소의 lib/plugins/symfony/plugins/sfFeed/ 디렉토리의 파일들을 다운 받으시길 바랍니다.

sfFeed 에 대해서는 나중에 알아보기로 하고, 먼저 코드를 작성하겠습니다.

액션 생성

피드는 feed 모듈의 popular 액션을 가르킬 것입니다. 다음을 입력하셔서 해당 모듈을 생성합니다.

$ symfony init-module frontend feed

askeet/apps/frontend/modules/feed/actions/action.class.php 파일을 여시고 다음 메쏘드를 추가합니다.

public function executePopular()
{
  // questions
  $c = new Criteria();
  $c->addDescendingOrderByColumn(QuestionPeer::INTERESTED_USERS);
  $c->setLimit(sfConfig::get('app_feed_max'));
  $questions = QuestionPeer::doSelectJoinUser($c);
 
  $feed = sfFeed::newInstance('rss201rev2');
 
  // channel
  $feed->setTitle('Popular questions on askeet');
  $feed->setLink('@homepage');
  $feed->setDescription('A list of the most popular questions asked on the askeet site, rated by the community.');
 
  // items
  $feed->setFeedItemsRouteName('@question');
  $feed->setItems($questions);
 
  $this->feed = $feed;
}

askeet/apps/frontend/config/app.yml 에서 app_feed_max_question 값을 정의합니다.

all:
  feed:
    max: 10

뷰 설정 바꾸기

기본적으로 feed/popular 액션은 글로벌 템플릿의 일부로 표현될 것이고, text/html 문서 형식을 가질 것입니다. 피드는 이와는 달라야 합니다. askeet/apps/frontend/modules/feed/config/ 디렉토리를 여시고 view.yml 파일을 생성하여 다음을 입력합니다.

all:
  has_layout: off
  template:   feed

이 설정은 글로벌 템플릿 사용을 막고, 어떤 액션이던지 feedSuccess.php 를 사용하도록 만듭니다.

템플릿 작성하기

피드 템플릿은 상당히 간단하고, 다른 피드들간에도 공유가 가능합니다. askeet/apps/frontend/modules/feed/templates/feedSuccess.php 에 다음을 입력합니다.

<?php echo $feed->getFeed() ?>

테스트 하기

이제 캐쉬를 지우시고 (설정 파일이 변경되었기 때문에), 사이트의 아무 페이지에서나 새로 고침을 해보십시오. 웹 브라우저에 피드 아이콘이 보이실 것입니다. 다음의 주소로 직접 확인하실 수도 있습니다.

http://askeet/feed/popular

결과물은 다음과 같습니다.

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
  <title>Popular questions on askeet</title>
  <link>http://askeet/frontend_dev.php/</link>
  <description>A list of the most popular questions asked on the askeet site, rated by the community.</description>
  <language>en</language>
<item>
 
  <title>What can I offer to my step mother?</title>
  <description>My stepmother has everything a stepmother is usually offered
(watch, vacuum cleaner, earrings, [del.icio.us](http://del.icio.us) account).
Her birthday comes next week, I am broke, and I know that
if I don't offer her something *sweet*, my girlfriend
won't look at me in the eyes for another month.</description>
  <link>http://askeet/frontend_dev.php/question/what-can-i-offer-to-my-step-mother</link>
  <guid>11</guid>
  <pubDate>Sat, 10 Dec 2005 09:44:11 +0100</pubDate>
</item>
<item>
 
  <title>What shall I do tonight with my girlfriend?</title>
  <description>We shall meet in front of the __Dunkin'Donuts__ before dinner,
and I haven't the slightest idea of what I can do with her.
She's not interested in _programming_, _space opera movies_ nor _insects_.
She's kinda cute, so I __really__ need to find something
that will keep her to my side for another evening.</description>
  <link>http://askeet/frontend_dev.php/question/what-shall-i-do-tonight-with-my-girlfriend</link>
  <guid>10</guid>
  <author>[email protected] (Fabien Potencier)</author>
  <pubDate>Sat, 10 Dec 2005 09:44:11 +0100</pubDate>
 
</item>
<item>
  <title>How can I generate traffic to my blog?</title>
  <description>I have a very swell blog that talks
about my class and mates and pets and favorite movies.</description>
  <link>http://askeet/frontend_dev.php/question/how-can-i-generate-traffic-to-my-blog</link>
  <guid>12</guid>
  <author>[email protected] (François Zaninotto)</author>
 
  <pubDate>Sat, 10 Dec 2005 09:44:12 +0100</pubDate>
</item>
  </channel>
</rss>

벌써 끝났나요?

마술?

이제 이렇게 생각하실것입니다. symfony 가 질문의 작성자와, 작성자의 이메일을 어떻게 알았을까? 질문의 URI 에 대해서 symfony 가 어떻게 알았을까? 대답은 마술입니다.

마술을 믿지 않으시나요? 그렇다면 커튼 뒤로 오셔서 sfFeed 클래스를 만나보십시오. 이 클래스는 ->setItems() 메쏘드로 넘겨받은 객체 인자의 메소드를 해독할 수 있습니다. Question 객체는 ->getUser() 메쏘드가 있기에, sfFeed 객체는 이를 사용해서 질문들에 대한 작성자를 확인할 수 있습니다. User 객체는 ->getEmail() 메쏘드가 있기에, sfFeed 객체가 해당 사용자의 이메일을 확인할 수 있습니다. 그리고 ->setFeedItemsRouteName() 메쏘드로 넘겨진 규칙은 아래와 같습니다.

question:
  url:   /question/:stripped_title
  param: { module: question, action: show }

규칙이름에는 stripped_title 이란 인자가 있기 때문에, Question 객체의 ->getSTrippedTitle() 메쏘드가 호출되어 각 질문의 URI 를 확인하도록 합니다.

이 모든 것이 getter 메쏘드의 이름이 직관적이기 때문입니다. sfFeed 클래스는 이렇게 직관적으로 설계된 객체들이 사용되었을때 제대로 동작합니다. 이에 대한 좀 더 자세한 내용은 온라인 문서들 중 피드 부분 을 참조하십시오. 여기에는, 예를 들면, ->getEmail() 메쏘드가 존재할더라도 이를 사용하지 않는 방법같은 것들이 소개되어 있습니다.

참고: 피드의 결과물은 XML 문서 형식을 가지며, symfony 는 해당 문서 형식에 대해서는 웹 디버그 툴바를 표시하지 않습니다. 만약 디버그 툴바를 사용하고 싶지 않은 경우에는 다음과 같이 해주시면 됩니다.

   sfConfig::set('sf_web_debug', false);

(디버그 툴바에 관해서는 온라인 문서들 중 디버그 부분 을 참조하십시오.

인터페이스 개선

라우팅

피드의 URL 역시 다른것들만큼 중요합니다. routing.yml 파일에 다음을 추가합니다.

# feeds
feed_popular_questions:
  url:   /feed/popular
  param: { module: feed, action: popular }

RSS 이미지

만약에 링크가 RSS 로 연결된다면 RSS 아이콘을 링크와 함께 출력하도록 하겠습니다. 이런 일이 상당히 많을 것이기 때문에 GlobalHelper.php 파일에 link_to_feed() 함수를 만들도록 하겠습니다.

function link_to_feed($name, $uri)
{
  return link_to(image_tag('feed.gif', array('alt' => $name, 'title' => $name, 'align' => 'absmiddle')), $uri);
}

feed.gif 이미지는 SVN 저장소에 있습니다.

modules/sidebar/templates/defaultSuccess.php 를 다음과 같이 수정합니다.

<li><?php echo link_to('popular questions', '@popular_questions') ?> <?php echo link_to_feed('popular questions', '@feed_popular_questions') ?></li>

내일 이 시간에

각 튜토리얼은 원래 1시간 분량이었는데, 이번 회는 15분밖에 되지 않았네요. 걱정되시나요? 걱정하실 필요없습니다. 이것은 애자일 프로그래밍의 좋은 점 중 하나일 뿐입니다. 문제에 대한 가장 간단한 답이 진정한 답입니다. 그리고 보시다시피 15분 만에 RSS 피드를 설정하고, 테스트해 보았습니다. 결과적으로 symfony 는 게으른 친구들이 멋진 웹사이트를 만들도록 돕는 최고의 도구입니다. 여러분의 자유시간을 즐기고 컴퓨터를 쉬게 하십시오.

만약 symfony 를 더 사용해보고 싶으시다면 최근에 올라온 질문, 최근에 올라온 답변, 그리고 각 질문에 대해 최근에 올라온 답변에 대한 피드들를 만들어 보십시오. 이번에는 15분도 채 안걸리실 겁니다. release_day_11 로 태그된 오늘의 코드를 askeet SVN 저장소 에서 다운로드 받아보시고 제대로 하셨는지 확인해 보십시오. 최근 답변들에 대한 피드에 대한 라우팅 규칙이 어려울 수 있으니 주의하시기 바랍니다.

그리고도 시간이 좀 남으신다면, askeet forum 에 가셔서 여러분의 의견을 남겨주십시오.

내일은 비밀번호를 잊어버린 사용자들을 위한 이메일 발송 부분을 살펴볼 것입니다. 그때까지 안녕히 계십시오.