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-03-18 17:27:04
작성자:
제목:PHP로 사용자 정의 가능한 RSS 피드 수집기 구현하기 (한글)


ajax와 웹 2.0 응용 프로그램에서 RSS의 진가를 경험해 보자

developerWorks
문서 옵션

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

수평출력으로 설정

이 페이지 출력

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

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

샘플 코드

영어원문

영어원문


제안 및 의견
피드백

난이도 : 고급

Senthil Nathan, 선임 소프트웨어 엔지니어, IBM

2008 년 3 월 18 일

RSS(Rich Site Summary, RDF Site Summary 또는 Really Simple Syndication)는 1990년대 중반에 생겨났습니다. 지금까지 다양한 변종이 나타났다 사라졌으며, 여러 단체와 개인이 소유권 논쟁을 벌였습니다. 그럼에도 불구하고 RSS는 한 웹 사이트에 담긴 정보를 여러 사람에게 배포하는 메커니즘으로 꾸준하게 각광을 받아왔고, 이러한 RSS의 인기를 발판으로 피드 구독기(feed reader) 혹은 피드 수집기(feed aggregator)라는 새로운 웹 소프트웨어가 생겨났습니다. 상업적으로 판매되는 피드 수집기도 여럿 있지만, 자신의 피드 수집기를 직접 구현해 자신의 웹 응용 프로그램에 통합하기가 그리 어렵지 않습니다. 이 기사에서는 사용자 정의 가능한 RSS 피드 수집기를 구현해 봅니다. 완전히 돌아가는 PHP 코드를 제공하고, RSS 피드 수집기 수정을 위한 서버측 PHP 함수 사용법도 소개합니다. 또한 완전히 돌아가는 RSS 피드 수집기 코드도 제공하므로 곧바로 내려 받아 사용할 수도 있습니다.

웹 응용 프로그램이 급속하게 성장한 배경에는 ajax나 웹 2.0과 같은 기술도 한 몫을 했지만 웹 응용 프로그램에 담긴 정보 자체의 가치 역시 중요하고 핵심적인 역할을 수행했다. 정보 제공자에게는 효과적으로 정보를 게시하는/배포하는 방법이, 정보 소비자에게는 효과적으로 정보를 구독하는/읽는 방법이 필요했다. 여기서 강력한 기능과 단순성 덕택에 웹 1.0 시대의 산물이던 RSS가 웹 2.0 시대와 이후 미래에도 중요한 위치를 차지하게 되었다.

RSS가 제공하는 우아하고 보편적인 형식을 사용하면 정보 제공자가 주기적으로 사이트에 가한 변경을 인코딩하여 배포하기 쉽다. 정보 제공자는 RSS 형식으로 변환한 정보를 전체 웹이나 (사내 사이트처럼) 제한적인 웹에다 게시하면 된다. 정보 제공자 반대편 끝에는 정보 소비자가 있다. 흔히 정보 소비자는 이 사이트 저 사이트를 돌아다니며 필요한 정보를 찾기에 급급하여 차분히 정보를 소화하지 못한다. 이 때 RSS 피드로 특정 범주에 속하는 정보를 한 덩어리로 받으면 소비자가 이 페이지 저 페이지를 뒤지고 돌아다닐 필요가 없어진다. 게다가 RSS 피드는 해당 범주에서 여러 정보 제공자가 제공하는 정보를 소비자를 위해 요약해 주기도 한다.

앞 서 언급했지만, RSS는 두 부분으로 나누어진다. 하나는 배포(Syndication)이고, 다른 하나는 피드 수집(Feed Aggregation)이다. 정보 소비자에게는 피드 수집 기능이 중요하며, 여기서는 이 부분에 초점을 맞춘다. 이 기사는 정보 소비자 입장에서 사용자 정의 가능한 RSS 피드 수집기를 개발하는 세부 방법을 설명한다.

XML/PHP 사전 지식 필요

이 기사는 독자들이 XML과 PHP에 어느 정도 익숙하다고 가정한다. 그렇지 않다면 developerWorks XML 영역developerWorks PHP 영역을 참조한다. 두 곳에서 제공하는 PHP와 XML 지식을 숙지하면 이 기사를 이해할 만한 수준에 도달하리라 믿는다.

소개

웹 으로 정보를 제공하는 방식이 널리 퍼지면서 정보 제공자와 정보 소비자 모두가 여러 가지 난관에 직면하게 되었다. 정보 제공자 입장에서 주요 난관은 혼잡한 경쟁 속에서 돋보이는 내용으로 정보 소비자를 가능한 많이 끌어들이는 능력이다. 반대로, 정보 소비자 입장에서 주요 난관은 온갖 정보 제공자가 제공하는 수많은 범주에서 필요한 정보를 재빨리 뽑아내는 능력이다. 정보 소비자는 또한 찾으려는 정보를 가리고 주의를 흐트리는 그래픽 애니매이션이나 플래시 광고 등 부수 정보도 가려내야 한다.

웹 이 우리 생활에 이미 깊숙히 침투했기 때문에 개발자들은 소비자가 정보를 신속하게 그리고 꼭 필요한 내용만 가져오는 방법에 초점을 맞추어야 한다. 정보 제공자들은 웹 브라우저에서 휴대형 모바일 장비에 이르기까지 다양한 소비자 매체로 동적 정보를 제공하도록 점차 요구받고 있다. 이 장 초반에서 설명했듯이, RSS는 정보 배포와 수집에 따르는 많은 문제를 해결해 준다. RSS에 익숙하지 않다면 참고자료 절에 열거한 RSS 관련 기사를 읽어보기 바란다.

RSS 는 다양한 방식으로 (그리고 예기치 못한 방식으로) 인터넷에 커다란 잠재력을 안겨주었다. 말할 필요도 없이, 최근 몇몇 주요 소프트웨어 업체 경영진은 자기네 핵심 인터넷 사업을 이끌어나갈 원동력으로 RSS를 추가했다(자세한 내용이 궁금하다면 유명한 Web 2.0 Journal과 ajaxWorld Magazine 창립 편집장인 Dion Hinchcliffe가 쓴 명쾌하고도 간결한 기사를 읽어보기 바란다. 기사 링크는 참고자료 절에 있다). Hinchcliffe는 그림 1을 통해 RSS가 웹 2.0 정보 생태계를 활성화하는 모습을 보여준다. 위키, 블로그, 뉴스 서비스, 수집 서비스, 매시업 서비스, 검색 엔진 등 주요한 웹 2.0 구성요소는 모두 RSS로 연결되어 웹 2.0의 사회적 컴퓨팅이라는 비전을 실현한다.


그림 1. Hinchcliffe가 보는 관점: 웹 2.0에서 RSS가 수행하는 역할
웹 2.0에서 RSS가 수행하는 역할

현 재 사용 중인 RSS 버전은 여러 가지다. RSS 버전 0.91, 0.92, 2.0은 특정한 XML 형식으로 RSS 피드를 기술한다. RSS 버전 1.0은 나머지 버전과 XML 형식이 약간 다르다. RSS 피드 수집기를 개발할 때는 이러한 사소한 차이점을 염두에 두어야 한다. RSS 역사가 궁금하다면 참고자료 절에 있는 기사를 읽어본다.

지 금까지 RSS 형식이 중요하고 유용하다는 사실을 이해했으니, 이제 사용자 정의 가능한 RSS 피드 수집기를 개발해 보자. 개발 언어로는 PHP를 사용한다. PHP는 다양한 웹/XML 함수를 내장하므로 개발 속력이 높아진다는 장점이 있다. 게다가 PHP로 작성한 코드는 (젠드 프레임워크를 포함하는) 젠드 코어와 같은 PHP 서버에서 문제 없이 돌아간다. 피드 수집기를 개발하려면, 개발 시스템에 PHP를 설치하고 cURL(Client URL)과 XML 패키지를 구성해야 한다. 젠드 코어 PHP 서버를 권장하는데, 무료인데다 설치와 구성이 쉽다.

RSS 형식에 대한 기본 규칙을 간단히 살펴보자.

RSS 기본

지 금까지 애플, 마이크로소프트(Microsoft®), 넷스케이프, 유저랜드, RSS-DEV 작업 그룹 등 다양한 회사와 그룹이 RSS에 기여했다. 그 덕택에 RSS 형식도 여러 가지 버전으로 나뉘었다. 가장 주목할 만한 내용으로 (RSS-DEV가 개발한) RSS 버전 1.0은 나머지 버전(역호환 형식을 공유하는 0.91, 0.92, 2.0 버전)과 다소 차이가 있다.

Listing 1은 RSS 버전 1.0 형식을 개략적으로 보여준다. 여기서 루트 요소는 <rdf>이고 자식 요소는 <channel> 하나와 <item> 요소 하나 이상이라는 사실에 주목한다. RSS 버전 1.0에서 <channel><item> 요소는 형제 관계에 있다는 사실을 기억하자.


Listing 1. RSS 버전 1.0 기본 형식
                
<rdf>
<channel>
.....
.....
</channel>

<item>
<title>My Title</title>
<link>My URL</link>
<description>My Description</description>
.....
.....
</item>
</rdf>

Listing 2는 RSS 버전 0.91, 0.92, 2.0 형식을 개략적으로 보여준다. 여기서 루트 요소는 <rss>가 아니라 <rdf>다. 또한 루트 요소 <rss><channel>이라는 자식 요소를 하나만 가지며, <item> 요소는 모두 <channel> 요소의 자식이다. 이는 RSS 1.0과 확실히 다른 점이다. 일반적으로 코드에서 다양한 RSS 형식을 처리할 때 이런 차이를 고려해야 한다. 이 기사에서는 <item> 요소 아래 있는 세 가지 자식 요소에만 관심이 있는데, 다행스럽게도 모든 RSS 버전에서 세 자식 요소는 형식이 동일하다.


Listing 2. RSS 버전 0.91과 2.0 형식
                
<rss>
<channel>
.....
.....

<item>
<title>My Title</title>
<link>My URL</link>
<description>My Description</description>
.....
.....
</item>
</channel>
</rss>

구체적인 RSS 명세는 참고자료에 있는 관련 기사를 살펴본다. 다음 절부터는 우리 RSS 피드 수집기에 필요한 컴포넌트를 설명한다.

기능적 컴포넌트

이 기사에서 설명하는 RSS 기능은 다음 컴포넌트로 이루어진다. 앞으로 설명할 순서대로 열거했다.

  • 피드 리더
  • 피드 소스 입력
  • 피드 수집기
  • 피드 결과 출력

위 와 같이 간단한 컴포넌트 네 개를 결합하면, 응용 프로그램과 다양한 방식으로 통합하기 쉬우면서도 강력한 기능을 제공하는 RSS 피드 수집기가 만들어진다. 이어지는 절에서 각 컴포넌트를 구현하는 PHP 코드를 소개한다. 그림 2는 기능적 컴포넌트를 전체적으로 보여준다.


그림 2. RSS 기능적 컴포넌트 개요
RSS 기능적 컴포넌트 개요

그림 2에서 다음과 같은 사실을 이해할 수 있다.

  • 대 다수 작업은 피드 리더 컴포넌트가 수행한다. 피더 리더는 주어진 피드 소스에서 피드를 가져오는 데 주력한다. 피드 소스는 단순히 URL에 불과한데, 특정 정보 제공자가 주기적으로 특정 범주의 정보를 배포하는 장소를 가리킨다. 예를 들어, 뉴욕 타임즈 지가 비즈니스 범주/채널의 최신 뉴스 속보를 XML 기반 RSS 형식으로 배포하는 페이지 URL이 피드 소스가 된다.
  • 피드 수집기 컴포넌트는 사용자가 지정하는 피드 소스를 받아 각 피드 소스마다 피드 리더 컴포넌트를 호출한다. 그러면 피드 리더 컴포넌트는 해당 피드 소스에서 피드를 가져온다.
  • 피드 소스 입력 컴포넌트는 사용자가 지정하는 피드 소스 정보를 정의하고 읽는다. 피드 소스 정보는 시스템 메모리나 파일이나 데이터베이스 등 다양한 매체에 저장된 문자열이다.
  • 피드 결과 출력 컴포넌트는 특정 피드 소스에서 수집한 RSS 피드 항목 결과를 저장한다. 시스템 메모리나 파일이나 데이터베이스 등 다양한 매체에 문자열로 저장한다.

편의상 피드 소스 입력 컴포넌트와 피드 결과 출력 컴포넌트를 피드 수집기 컴포넌트로 통합한다. (다운로드 절에서 완전히 문서화된 PHP 소스 파일을 모두 받을 수 있다.) 다운로드한 파일을 푼 후에는 원하는 RSS 피드 소스를 지정하고 실행하면 된다. 앞서 설명했듯이, 프로그램을 실행하려면 PHP를 설치하고 XML/cURL 라이브러리를 구성해야 한다(RSS 피드 수집기 코드를 사용하는 다양한 방법은 결론 절을 참조한다).

이제 각 컴포넌트에 대한 내부 동작 원리를 상세히 살펴보자.

피드 리더

피드 리더 컴포넌트는 피드 소스와 네트워크 통신을 수행하고 피드 소스에서 가져온 XML 기반 RSS 피드를 구문 분석한다. 간단하나 강력한 PHP 기본 기능 세 가지를 사용한다.

  • cURL
  • SimpleXML
  • PHP 배열

cURL 은 HTTP, FTP, Telnet 등 다양한 프로토콜로 PHP 프로그램과 원격 서버를 연결해 주는 오픈 소스 라이브러리다. HTTP POST, HTTP GET, HTTP PUT 등과 같은 작업을 수행하는 다양한 껍데기(Wrapper) 함수를 제공한다. cURL 함수를 사용하려면 PHP 환경에서 libcurl 패키지를 활성화해야 한다. 젠드 코어 서버는 관리 프로그램에서 마우스 클릭 한 번으로 cURL을 활성화할 수 있다.

SimpleXML은 또 다른 PHP 확장 기능이다. 이름이 드러내듯이, XML 문서를 읽거나 쓰기에 편리한 기능을 제공한다. PHP 버전 5 이상에서는 이 기능이 기본으로 활성화되어 있다.

배열은 PHP가 제공하는 기본 기능으로, 배열을 사용하면 집합이라는 자료 구조를 다루기가 매우 쉬워진다. 여기서는 RSS 피드 항목 결과를 수집할 때 PHP 배열을 사용한다.

피드 리더 컴포넌트를 구현한 PHP 소스 코드는 rss_feed_reader.php 파일이다. 이 코드는 다음 세 함수로 이루어져 있다.

  • get_rss_feeds
  • perform_curl_operation
  • parse_rss_feed_xml

이어지는 문단에서 각 함수에 들어가는 코드를 상세히 설명한다. 게다가 논리 구조에 대한 이해를 돕고자 원시 코드 자체에도 주석을 충분히 달았다.

Listing 3에서 보듯이, get_rss_feeds는 피드 수집기 컴포넌트에서 호출하는 함수로, 기본적인 비즈니스 로직을 구현한다. 이 함수는 호출자로부터 다음 세 참조 값을 입력으로 받는다.

  • RSS 제공자 이름
  • RSS 제공자 URL
  • 피드 제공자로부터 가져올 최대 RSS 피드 항목 수

이 함수에 대한 논리를 뜯어보면 perform_curl_operation 함수를 호출하여 cURL 네트워크 작업을 수행하는데, perform_curl_operation 함수는 피드 제공자의 원격 URL에서 XML 기반 RSS 피드 내용을 가져온다. 그런 다음 get_rss_feeds 함수는 title 요소, link 요소, description 요소를 저장할 배열 세 개를 생성한다. Listing 1에서 보았듯이, 세 요소는 XML 피드 내용에서 <item> 요소 안에 들어 있다. 다음 단계로 XML 내용 문자열과 방금 생성한 배열 세 개를 parse_rss_feed_xml 함수로 넘겨 XML을 분석하고 RSS 피드 항목을 수집한다. RSS 피드 내용을 구문 분석한 결과가 false면, get_rss_feeds 함수는 호출자에게 빈 배열을 반환한다. 구문 분석 결과가 true이면, 피드 항목을 모두 성공적으로 분석했고 받은 피드 항목 중에 title, URL, description을 저장하는 배열 세 개에 올바른 정보가 담겼다는 뜻이다. 이 경우 get_rss_feeds 함수는 호출자에게 돌려줄 새로운 배열을 생성한다. 새 배열에서 첫 번째 다섯 배열 값은 각각 제공자 이름, 가져온 피드 항목 개수, title 배열, URL 배열, description 배열이다. 마지막으로 get_rss_feeds 함수는 새로 생성한 배열을 호출자에게 반환한다.


Listing 3. get_rss_feeds 함수
                
function get_rss_feeds(& $rss_provider_name, & $rss_provider_url,
& $max_rss_items_required) {
// max_rss_items_required가 0인지 확인
if ($max_rss_items_required <= 0) {
// 비어있는 배열 반환
$empty_array = array();
return($empty_array);
} // End of if ($max_rss_items_required <= 0)

// 계속 진행해 RSS 제공자로부터 RSS 내용을 가져온다.
$received_rss_feeds = perform_curl_operation($rss_provider_url);

// 비어있는가?
if (empty($received_rss_feeds)) {
// 비어있는 배열 반환
$empty_array = array();
return($empty_array);
} // End of if (empty($received_rss_feeds))

// RSS 피드 제공자로부터 비어있지 않은 결과를 받았다.
// 받은 rss 피드 항목에서 값을 저장할 비어있는 배열 셋을 생성한다.
$rss_feed_title_array = array();
$rss_feed_url_array = array();
$rss_feed_description_array = array();

// 이제 개별 RSS 피드 항목을 파싱한다.
$parser_result = parse_rss_feed_xml($received_rss_feeds, $max_rss_items_required,
$rss_feed_title_array, $rss_feed_url_array, $rss_feed_description_array);

// RSS 피드 XML 내용을 파싱할 수 있는지 점검한다.
if ($parser_result == true) {
// 성공적으로 RSS 피드 결과를 파싱했다.
// 배열을 생성해 함수 설명 주석에 기술한
// 결과로 배열을 채워넣는다.
$result_array = array();
// rss 제공자 이름을 채운다.
$result_array[0] = $rss_provider_name;
// 반환될 rss 피드 항목 개수를 채운다.
$result_array[1] = sizeof($rss_feed_title_array);
// RSS 피드 제목을 포함할 배열을 채운다.
$result_array[2] = $rss_feed_title_array;
// RSS 피드 URL을 포함할 배열을 채운다.
$result_array[3] = $rss_feed_url_array;
// RSS 피드 설명을 포함할 배열을 채운다.
$result_array[4] = $rss_feed_description_array;
// 이제 결과 배열을 반환한다.
return($result_array);
} else {
// RSS 피드 항목을 파싱하는 데 실패했다.
// 결과로 비어있는 배열을 반환한다.
$empty_array = array();
return($empty_array);
} // End of if ($parser_result == true)
} // End of function get_rss_feeds

Listing 4에서 보듯이, perform_curl_operaton 함수는 주어진 원격 URL에서 HTTP GET으로 내용을 가져온다. PHP cURL 라이브러리를 사용하면 이 과정이 간단하고 명료하다. 이 함수는 호출자로부터 원격 URL 참조 값을 입력 인수로 받는다. 여기서 호출자는 앞서 설명한 get_rss_feeds 함수다. perform_curl_operaton 함수 논리를 살펴보면 가장 먼저 새 cURL 세션을 초기화한다. 그런 다음, 여러 가지 cURL 옵션을 설정한다. 여기서 설정하는 옵션으로는 원격 URL, 응답에 HTTP 헤더를 제외하는 옵션, HTTP 헤더가 다른 위치를 가리키는 경우 따라가는 옵션, HTTP 응답을 curl_exec 함수 반환값 문자열로 받는 옵션 등이 있다. 필요한 옵션을 설정한 후에는 curl_exec 함수를 호출한다. curl_exec 함수는 원격 URL에 연결하여 현재 게시된 RSS 피드 내용을 가져온다. 네트워크 처리 단계에서 curl_exec 함수는 HTTP 응답을 받을 때까지 대기한다. 그런 다음, cURL 세션을 닫고 받은 RSS 피드 내용을 호출자에게 반환한다.


Listing 4. perform_curl_operation 함수
                
function perform_curl_operation(& $remote_url) {
$remote_contents = "";
$empty_contents = "";

// cURL 세션을 초기화하고 핸들을 얻는다.
$curl_handle = curl_init();

// cURL 세션이 열렸나?
if ($curl_handle) {
// 필요한 cURL 옵션을 설정한다.
// URL 옵션 설정
curl_setopt($curl_handle, CURLOPT_URL, $remote_url);
// HEADER 옵션 설정. 출력에 HTTP 헤더 결과를 원하지 않는다.
curl_setopt($curl_handle, CURLOPT_HEADER, false);
// FOLLOWLOCATION 옵션 설정. 위치 헤더가 존재하면 따라간다.
curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
// WRITEFUNCTION 콜백을 사용하는 대신, curl_exec 함수를 위한 반환 값으로
// 원격지 내용을 받는다.
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);

// 원격지 URL 내용을 가져오려고 시도한다.
// 이 함수는 내용을 전달 받을 때까지 멈춰있다.
$remote_contents = curl_exec($curl_handle);
// cURL을 정리한다.
curl_close($curl_handle);

// cURL 결과를 점검한다.
if ($remote_contents != false) {
return($remote_contents);
} else {
return($empty_contents);
} // End of if ($remote_contents != false)
} else {
// cURL 초기화 실패
// cURL 없이 할 수 있는 일은 없다.
return($empty_contents);
} // End of if ($curl_handle)
} // End of function perform_curl_operation

Listing 5에서 보듯이, parse_rss_feed_xml 함수는 받은 RSS 피드 내용에서 개별 피드 항목을 추출한다. 이 함수는 모든 인수를 참조 값으로 받는다. 함수로 넘어오는 입력 인수는 RSS 피드 내용 문자열, 사용자가 제한하는 최대 피드 항목 수, title과 URL과 description을 저장해 호출자에게 돌려줄 배열 세 개다. PHP SimpleXML 확장 기능을 사용한 경험이 없다면 이 함수를 찬찬히 살펴보기 바란다. 이전에 사용하던 복잡한 XML 구문 분석 기술과는 달리, SimpleXML에서는 PHP 개체 구조를 조작하는 방식으로 XML 구조를 조작할 수 있다.

가장 먼저, parse_rss_feed_xml 함수는 RSS 피드 내용 문자열을 읽어 대응하는 개체 구조로 변환한다. 다음으로 구문 분석을 수행하기에 앞서 피드 인코딩에 사용한 RSS 버전이 1.0인지 다른 버전인지를 확인한다. 여기서 <item> 요소가 루트의 자식인지 아니면 <channel> 요소의 자식인지 살펴 RSS 버전을 판단한다. 각 버전마다 나타나는 형식 차이는 앞서 나왔던 RSS 기본 절을 참조한다. RSS 버전을 확인하고 XML 내용 유효성을 검증한 후에는 루프를 돌면서 각 <item> 요소로부터 title, link, description 필드를 추출한다(여기서 item은 실제 PHP 개체다). 추출한 세 값은 인수로 넘어온 배열 세 곳에 각각 저장한다. 최대 피드 항목 수만큼 결과를 추출하면 함수는 true를 반환한다. RSS 피드 항목 중 하나라도 구문 오류가 있으면 함수는 false를 반환한다.


Listing 5. parse_rss_feed_xml 함수
                
function parse_rss_feed_xml(& $received_rss_feeds,
& $max_rss_items_required, & $rss_feed_title_array,
& $rss_feed_url_array, & $rss_feed_description_array) {
/*
* PHP SimpleXML API를 사용해 XML 형식으로 인코딩된 RSS 피드를 파싱한다.

RSS에는 0.91, 0.92, 1.0, 2.0이라는 다양한 버전이 존재한다.
각 버전 사이에 존재하는 차이점은 다음에 소개하는 두 가지 형식에 나타나 있다.

1) <rss><channel><item>...</item><item>...</item><item>...</item></channel></rss>
2) <rdf><channel>...</channel><item>...</item><item>...</item><item>...</item></rdf>

1번 형식에서, <item> 요소는 <channel> 요소의 자식이다.
2번 형식에서, <item> 요소는 루트 요소인 <rss>나 <rdf>의 직계 자식이다.
다시 말해, 2번 형식에서 <item> 요소는 <channel> 요소의 형제다.
RSS 버전 1.0은 2번 형식을 사용하는 반면 나머지 버전은 1번 형식을 사용한다.
양쪽 형식에서, <item>...</item> 사이의 자손에만 관심을 기울인다.
여기서 사용하는 파싱 논리는 양쪽 형식을 모두 다뤄야 한다.
*/
// SimpleXML 객체 표현을 얻기 위해 XML 문자열을 올리는 작업부터 시작하자.
$xml = simplexml_load_string($received_rss_feeds);

// 유효한 XML 문서인가?
if ((is_object($xml) == false) || (sizeof($xml) <= 0)) {
// XML 파싱 오류. 지금 바로 끝낸다.
return(false);
} // End of if ((is_object($xml) == false) ...

// 이제 <item> 요소가 <channel> 요소의 자식인지 결정해야 한다.
// 이럴 경우 위에서 설명한 형식 1이다. <item> 요소가 <rss>나 <rdf>
// 루트 요소의 직계 자식이면 위에서 설명한 형식 2이다.
$obj1 = $xml->item;

if ((is_object($obj1) == false) || (sizeof($obj1) <= 0)) {
// <item> 요소는 문서 root 요소의 직계 자식이 아니다.
// 이 경우 형식 2가 아니라 형식 1이 되어야 한다.
// <channel> 요소를 이동해 새로운 루트로 만든다.
$xml = $xml->channel;
} // End of if ((is_object($obj1) == false) ...

// 파싱을 위해 XML 유효성을 한번 더 점검한다.
if ((is_object($xml) == false) || (sizeof($xml) <= 0)) {
// XML 파싱 오류. 지금 바로 끝낸다.
return(false);
} // End of if ((is_object($xml) == false) ...

// 인출한 <item> 요소를 세기 위해 변수를 초기화한다.
$count_of_rss_items_retrieved = 0;

// <item> 요소에서 세부 사항을 수집하기 위해 루프를 돈다.
foreach ($xml->item as $item) {
// 이 단계에서 한번에 <item> 요소 하나에 접근한다.
// 얼마나 많은 <item> 요소가 있는지 모른다.
// 제목, 링크, 설명 요소를 읽자.
$rss_feed_title = trim(strval($item->title));
$rss_feed_url = trim(strval($item->link));
$rss_feed_description = trim(strval($item->description));
// 이 값을 배열 참조에 넣자.
array_push($rss_feed_title_array, $rss_feed_title);
array_push($rss_feed_url_array, $rss_feed_url);
array_push($rss_feed_description_array, $rss_feed_description);
// 사용자가 원하는 개수를 얻기 위해 읽은 <item> 요소 개수를 증가시킨다.
$count_of_rss_items_retrieved++;

if ($count_of_rss_items_retrieved >= $max_rss_items_required) {
// 이제 루프에서 벗어날 때다.
break;
} // End of if ($count_of_rss_items_retrieved >= $max_rss_items_required)
} // End of foreach ($xml->item as $item)

if ($count_of_rss_items_retrieved > 0) {
// 마지막으로 과실을 딸 시기다.
return(true);
} else {
// 열심히 작업했지만 결실이 없다.
// 다음 번에는 운이 좋으리라.
return(false);
} // End of if ($count_of_rss_items_retrieved > 0)
} // End of function parse_rss_feed_xml

여기까지 피드 리더 컴포넌트가 수행하는 주요 작업을 모두 둘러보았다. 다음으로 피드 소스 입력 컴포넌트를 살펴보자.

피드 소스 입력

피 드 소스 입력 컴포넌트는 이 시스템에서 가장 간단한 컴포넌트다. 단순히 사용자가 지정하는 피드 소스 목록을 읽어들이면 된다. 컴포넌트에 속하는 함수는 하나 뿐인데, 프로그램을 시작하면 피드 수집기가 가장 먼저 이 함수를 호출한다. 기사 초반에 언급했듯이, 피드 소스 정보는 프로그램 자료 구조나 데이터베이스 테이블이나 파일 등 어떤 형태로 지정해도 괜찮다. 여기서는 피드 소스 정보를 XML 파일로 입력 받는다고 가정한다. 함수 논리를 살펴보면 함수 입력 인수로 넘어온 이름을 사용해 단순히 파일 내용을 읽는다. 그리고 XML 파일 내용을 읽어 호출자에게 문자열을 반환한다. 앞서 언급했듯이, 이 컴포넌트는 피드 수집기 컴포넌트와 함께 rss_feed_aggregator.php 파일에 들어 있다. Listing 6은 피드 소스 입력 파일 내용을 읽어들이는 간단한 함수다.


Listing 6. get_list_of_rss_feed_sources 함수
                
function get_list_of_rss_feed_sources($input_xml_file) {
// 입력 파일에서 XML 내용을 읽어들인다.
file_exists($input_xml_file) or die("Could not find file " . $input_xml_file);
$xml_string_contents = file_get_contents($input_xml_file);
// 이제 호출자에게 XML 내용을 반환한다.
return($xml_string_contents);
} // End of function get_list_of_rss_feed_sources

피 드 소스 입력 파일은 피드 제공자 이름, 피드 제공자 URL, 피드 제공자로부터 가져올 사용자 지정 최대 RSS 피드 항목 수를 포함한다. 각 정보는 해당 XML 요소 안에 들어 있다. Listing 7은 피드 소스 입력 XML 파일 형식을 보여준다.


Listing 7. 피드 소스 입력 XML 파일 형식
                
<?xml version="1.0" encoding="UTF-8"?>
<ListOfRssFeedSources>
<!-- 피드 제공기 1를 위한 자료 집합이다 -->
<RssFeedSourceInfo>
<rssFeedProviderName>Barron's: Markets</rssFeedProviderName>
<rssFeedProviderUrl>
http://online.barrons.com/xml/rss/3_7517.xml
</rssFeedProviderUrl>
<maximumRssItemsToBeReturned>5</maximumRssItemsToBeReturned>
</RssFeedSourceInfo>

<!-- RSS 피드 제공기 항목을 여기 추가한다. -->
</ListOfRssFeedSources>

피드 수집기

피 드 수집기 컴포넌트는 이 기사의 목적에 부합하도록, 즉 사용자 정의 가능한 RSS 피드 수집 기능을 수행하도록 피드 리더 컴포넌트를 감싸는 껍데기 컴포넌트다. 이 컴포넌트는 앞서 설명한 SimpleXML PHP 확장 기능을 사용하며, 여러 RSS 피드를 수집하는 간단한 논리도 맞춤식으로 구현한다. 소스 파일은 rss_feed_aggregator.php다.

Listing 8에서 보듯이, 피드 수집기 컴포넌트는 aggregate_rss_feeds라 는 함수 하나로 이루어져 있다. 인수로는 RSS 피드 소스 정보가 담긴 파일 이름을 받는다. 인수가 지정되지 않으면 rss_feed_sources.xml이라는 기본 파일 이름을 사용한다. 먼저, 함수는 피드 소스 입력 컴포넌트를 호출하여 RSS 피드 소스를 명시한 문자열 형식으로 되어 있는 XML 구조를 돌려 받는다. 그런 다음, SimpleXML 확장 기능을 사용하여 문자열 형식의 XML 구조를 PHP 개체로 변환한다. 다음으로 루프를 돌면서 각 피드 소스마다 피드 제공자 이름, 피드 제공자 URL, 피드 제공자에게서 가져올 RSS 피드 항목 최대 개수를 추출한다. 그런 다음, 앞서 설명한 피드 리더 컴포넌트에 속한 get_rss_feeds 함수를 호출한다. 제공자로부터 RSS 피드 항목을 성공적으로 가져오면 피드 결과 출력 컴포넌트를 호출한다. 모든 피드 소스에서 RSS 피드 항목을 가져온 후에는 마지막으로 피드 수집 활동 내역을 요약하여 출력한다.


Listing 8. aggregate_rss_feeds 함수
                
function aggregate_rss_feeds($input_xml_file = RSS_FEED_SOURCES_FILE_NAME) {
// 처리 중인 현재 RSS 피드 소스를 추적하기 위한 변수를 선언한다.
$feed_source_sequence_number = 0;
// RSS 피드 소스 목록을 얻는다.
// 여기서는 입력 파일에서 읽어온다.
$xml_string_contents = get_list_of_rss_feed_sources($input_xml_file);

/*
PHP SimpleXML API를 사용해 XML 형식으로 인코딩된 RSS 피드를 파싱한다.
*/
// SimpleXML 객체 표현을 얻기 위해 XML 문자열을 올리는 작업부터 시작하자.
$xml = simplexml_load_string($xml_string_contents);

// 유효한 XML 문서인가?
if ($xml == false) {
print ("Sorry. Your RSS feed sources input file contains invalid data.\n");
// XML parsing error. Return now.
return;
} // End of if ($xml == false)

print ("\n");

/*
각 소스에서 RSS 피드를 얻기 위해 루프를 돈다.
입력 XML 파일의 문서 루트 요소는 <ListOfRssFeedSources>이다.
루트 요소 아래에서 다음 형식을 따르는 자료 블록이 하나 이상 나온다.

<RssFeedSourceInfo>
<rssFeedProviderName>....</rssFeedProviderName>
<rssFeedProviderUrl>....</rssFeedProviderUrl>
<maximumRssItemsToBeReturned>....</maximumRssItemsToBeReturned>
</RssFeedSourceInfo>

<RssFeedSourceInfo> 요소를 차례로 순회한다.
*/
foreach ($xml->RssFeedSourceInfo as $feed_source) {
// 입력 파일에서 다음 피드 소스에 대한 세부 내역을 읽는다.
$feed_source_sequence_number++;
$rss_provider_name = trim(strval($feed_source->rssFeedProviderName));
$rss_provider_url = trim(strval($feed_source->rssFeedProviderUrl));
$max_rss_items_required =
trim(strval($feed_source->maximumRssItemsToBeReturned));
print ("Getting RSS feeds from $rss_provider_name ...\n");

// 이제 이 피드 소스에서 RSS 피드를 읽으러 간다.
$rss_feeds_result_array =
get_rss_feeds($rss_provider_name, $rss_provider_url, $max_rss_items_required);

if (empty($rss_feeds_result_array) == false) {
// 하나 이상 RSS 피드 결과가 있을 때만 저장한다.
// 결과 배열 형식은 다음에 호출하는 저장 함수에서 설명한다.
store_rss_feed_results($feed_source_sequence_number,
$rss_feeds_result_array);
} // End of if (empty($rss_feeds_result_array) == false)
} // End of foreach ($xml->RssFeedSourceInfo as $feed_source)

print ("\nFinished getting RSS feeds from $feed_source_sequence_number " .
"feed sources.\n\n");
print ("You can view the received feed items in the .\feed_results directory.\n\n");
print ("Feeds from each active feed source are stored in separate files.\n\n");
print ("These files are named NNN_rss_feed_items.txt, where NNN corresponds to\n" .
"the sequence number of the order in which the feed source is\n" .
"listed in your $input_xml_file file.\n");
} // End of function aggregate_rss_feeds

피드 결과 출력

피 드 결과 출력 컴포넌트는 RSS 피드 결과를 저장한다. 이 컴포넌트 역시 함수 하나로 이루어져 있으며, 피드 수집기는 RSS 피드 내용으로부터 모든 피드 항목을 추출한 후 이 함수를 호출한다. 기사 앞 부분에서 설명했듯이, RSS 피드 결과는 브라우저나 다른 응용 프로그램으로 보내도 무방하고, 아니면 프로그램 자료 구조나 데이터베이스 테이블이나 파일에 저장해도 무방하다. 여기서는 가져온 피드 결과를 피드 제공자별로 각각 다른 파일에 저장한다. 모든 결과 파일은 feed_results라는 디렉터리 아래 저장되는데, 이 디렉터리는 피드 수집기 프로그램이 실행되는 디렉터리 아래 자동으로 생성된다.

Listing 9에서 보듯이, 피드 결과를 출력하는 함수인 store_rss_feed_results는 입력 인수 두 개를 받는다. 첫 번째 인수는 파일 일련번호로, 나중에 이 일련번호를 결과 파일 이름에 사용한다. 결과 파일 이름은 NNN_rss_feed_items.txt 형식인데, 여기서 NNN이 첫 번째 인수로 넘어온 일련번호다. 두 번째 인수는 특정한 RSS 피드 제공자로부터 가져온 RSS 피드 아이템으로, 자료 형식은 PHP 중첩 배열이다. 이 배열은 요소가 5개인데, 각 요소는 다음 값을 포함한다.

a[0] = RSS 피드 제공자 이름
a[1] = RSS 피드 제공자 이름
a[2] = (rss_feed_title_array) RSS 피드 항목 제목이 담긴 배열
a[3] = (rss_feed_url_array) RSS 피드 항목 URL이 담긴 배열
a[4] = (rss_feed_description_array) RSS 피드 항목 설명이 담긴 배열

a[2], a[3], a[4]라는 세 배열에서 색인이 같은 요소 값을 모으면 RSS XML 내용에서 특정 피드 항목에 대한 관련 정보가 모두 드러난다. 예를 들어, rss_feed_title_array[0], rss_feed_url_array[0], rss_feed_description_array[0]을 합치면 RSS XML 내용에서 첫 번째 피드 항목에 대한 정보가 완성된다.

가 장 먼저, store_rss_feed_results 함수는 feed_results 하위 디렉터리를 생성한다. 물론, 디렉터리가 존재하지 않는 경우에 한해서다. 다음으로, 주어진 RSS 피드 제공자에 고유한 파일 이름을 생성한다. 그런 다음, 루프를 돌면서 결과 배열에서 RSS 피드 항목 정보를 하나씩 꺼내서 파일에 쓴다. 지금쯤이면 익숙하겠지만, 각 RSS 피드 항목은 피드 제목, 피드 전체 이야기가 담긴 URL, 간단한 설명을 포함한다. 이러한 피드 항목 정보를 토대로 정보 소비자는 선택한 범주에 속하는 피드 제목과 설명을 짧은 시간 내에 훑어볼 수 있다. 그러다 관심 가는 항목이 있으면 URL을 따라가 자세한 정보를 확인하면 된다. RSS 피드 수집기는 정보 소비자에게 이러한 장점을 제공한다.


Listing 9. store_rss_feed_results 함수
                
function store_rss_feed_results($file_sequence_number, $result_array) {
// "feed_results"라는 하위 디렉터리가 존재하는지 가장 먼저 점검한다.
if (file_exists(RSS_FEED_RESULTS_DIRECTORY) == false) {
// 디렉터리가 존재하지 않는다. 지금 생성한다.
mkdir(RSS_FEED_RESULTS_DIRECTORY);
} // End of if (file_exists(RSS_FEED_RESULTS_DIRECTORY) == false)

// 파일 이름을 만든다.
$result_file_name = sprintf("%s/%03d%s", RSS_FEED_RESULTS_DIRECTORY,
$file_sequence_number, RSS_FEED_RESULTS_FILE_NAME_SUFFIX);

// 이 파일이 이미 만들어져 있다면 그냥 지워버린다.
// 최신 피드 자료로 겹쳐쓸 것이다.
if (file_exists($result_file_name) == true) {
unlink($result_file_name);
}

// 파일을 만들고 연다.
$handle = fopen($result_file_name, FILE_CREATE_WRITE_FLAG);

if ($handle == false) {
// 파일 생성에 실패했으므로 바로 끝낸다.
return;
}

// 받은 RSS 피드를 이 파일에 기록하기 시작한다.
// 피드 제공자 연속 번호를 쓴다.
$feed_provider_number = FEED_PROVIDER_SEQUENCE_NUMBER .
$file_sequence_number . NEW_LINE;
fwrite($handle, $feed_provider_number);
// 피드 제공자 이름을 쓴다.
$feed_provider_name = RSS_FEED_PROVIDER_NAME . $result_array[0] . NEW_LINE;
fwrite($handle, $feed_provider_name);
// 받은 피드 항목 개수를 쓴다.
$number_of_received_rss_feeds = RECEIVED_RSS_FEEDS_CNT .
$result_array[1] . NEW_LINE;
fwrite($handle, $number_of_received_rss_feeds);

$rss_feed_title_array = $result_array[2];
$rss_feed_url_array = $result_array[3];
$rss_feed_description_array = $result_array[4];

// 루프를 돌면서 제목, URL, 설명을 쓴다.
for($cnt=0; $cnt < sizeof($rss_feed_title_array); $cnt++) {
$feed_item_separator = FEED_ITEM_SEPARATOR_LINE . NEW_LINE;
fwrite($handle, $feed_item_separator);
$feed_item_sequence_number = FEED_ITEM_SEQUENCE_NUMBER .
($cnt+1) . NEW_LINE;
fwrite($handle, $feed_item_sequence_number);
$feed_item_title = FEED_ITEM_TITLE .
$rss_feed_title_array[$cnt] . NEW_LINE;
fwrite($handle, $feed_item_title);
$feed_item_url = FEED_ITEM_URL .
$rss_feed_url_array[$cnt] . NEW_LINE;
fwrite($handle, $feed_item_url);
$feed_item_description = FEED_ITEM_DESCRIPTION . NEW_LINE .
$rss_feed_description_array[$cnt] . NEW_LINE;
fwrite($handle, $feed_item_description);
} // End of for($cnt=0; $cnt < sizeof($rss_feed_title_array), $cnt++)

$feed_item_separator = FEED_ITEM_SEPARATOR_LINE . NEW_LINE;
fwrite($handle, $feed_item_separator);
fclose($handle);
} // End of function store_rss_feed_results

이상으로 프로그램 컴포넌트를 기능적으로 모두 살펴보았다. 컴포넌트가 서로 연계하여 동작하는 방식을 이해했으리라 믿는다. 다음으로 RSS 피드 수집기를 직접 실행해 보자.

RSS 피드 수집기 실행하기

다운로드 절에서 zip 파일을 다운로드 받을 수 있다. Zip 파일에는 다음 소스 파일이 들어 있다.

  • rss_feed_aggregator\rss_feed_reader.php
  • rss_feed_aggregator\rss_feed_aggregator.php
  • rss_feed_aggregator\rss_feed_sources.xml

먼 저, 파일 압축을 푼다. 프로그램을 실행하려면 젠드 코어와 같은 PHP 환경에서 cURL과 SimpleXML을 활성화해야 한다는 사실에 주의한다. 제공하는 파일 중 rss_feed_sources.xml 파일은 RSS 피드 소스 22개를 포함하는 예제 XML 파일이다. 이 예제 파일은 비즈니스와 재정 범주/채널에 속하는 피드 소스를 포함한다. 원한다면 직접 XML 파일을 정의해도 좋다.

RSS 피드 수집기를 실행하려면 다음 명령을 수행한다. (명령 뒷부분 마지막 인수는 옵션이다.)


                
php -f rss_feed_aggregator.php <피드 소스 입력 XML 파일이름>

developerWorks ajax 리소스 센터
ajax 리소스 센터 ajax 응용 프로그램 개발에 유용한 무료 도구, 코드, 정보를 함께 제공한다. ajax 전문가인 Jack Herrington이 이끄는 ajax 커뮤니티 포럼역시 ajax 사용자들로부터 도움을 받을 수 있는 좋은 정보처이다..

서 설명했듯이, 명령행 인수를 주지 않으면 응용 프로그램은 기본 피드 소스 입력 XML 파일인 rss_feed_sources.xml 파일을 사용한다. 이 기본 피드 소스 입력 XML 파일을 사용하여 문제 없이 실행했다고 가정하면 그림 3과 같은 결과를 얻게 된다.


그림 3. RSS 피드 수집기를 실행한 결과
RSS feed aggregator results

RSS 피드 수집기를 실행한 디렉터리 내에는 feed_results라는 하위 디렉터리가 생긴다. 또한 feed_results 하위 디렉터리 내에 존재하는 파일은 (사용자가 지정하는 피드 소스 입력 XML 파일에 포함된) 각 RSS 피드 제공자에게서 가져온 RSS 피드 항목을 포함한다.

결론

소셜 북마크

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

RSS 는 나온 지 꽤나 오래 되었다. 그러나 근래에 이르러서야 웹, 블로그, 매시업, 소셜 네트워킹 포탈, 기타 정보 수집 서비스 등 웹 2.0 기술이 도래하면서 커다란 인기를 끌고 있다. 이 기사는 RSS 형식이 제공하는 단순성을 한 눈에 보여준다. 이러한 단순성 덕택에 RSS는 새로운 웹 기술들이 서로 정보를 교환하고 통합하기에 아주 적합한 수단이다. 웹은 정보가 핵심이므로 RSS는 앞으로도 웹에서 정보를 배포하고 퍼트리는 방식에 긍정적이고도 커다란 영향력을 발휘하리라 생각한다.

이 기사는 PHP로 피드 수집기를 개발하는 방법을 상세히 설명하며, 완전히 돌아가는 PHP 코드도 제공한다. 여기서 제공하는 소스 코드는 여러 가지 방식으로 활용할 수 있겠다. 단독형 도구로 사용해도 좋고, 기존 PHP 서버측 프로그램에서 사용하는 공유 라이브러리로 사용해도 좋고, 기업 SOA(Service Oriented Architecture)에 들어가는 SOAP/REST 웹 서비스 함수로 사용해도 좋겠다.

웹에는 단순하고도 멋진 기술이 많다. RSS는 단순히 이러한 기술 중 하나일 뿐이다. 악의나 농담은 없다!





위로


다운로드 하십시오

설명이름크기다운로드 방식
RSS 피드 수집기 코드wa-aj-rssphp.zip10119 KBHTTP
다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기

토론


필자소개

Senthil Nathan 사진

Senthil Nathan은 뉴욕 주에 있는 IBMT.J. Watson Research Center 선임 소프트웨어 엔지니어다. 23년에 이르는 소프트웨어 개발 경력을 자랑하며, 다양한 기업 응용 프로그램을 개발했다. 현재는 SOA, 웹 서비스, 스트림 프로그래밍, 자바 2 플랫폼, J2EE, PHP, 루비 온 레일스, 웹 2.0, ajax 개발 등 한창 주목을 끌고 있는 기술에 관심을 쏟고 있다.