라이브러리
프로그래밍언어, 각종코드, 관련동향, 논문 등의 저장소
[메타 사이트 만들기] ① 닷넷으로 구현하는「블로거 집합소」

[메타 사이트 만들기] ① 닷넷으로 구현하는「블로거 집합소」

 

연재순서
1회. 블로거들의 집합소「메타 사이트 구현」
2회. RSS 수집 로봇 구현과 문제점 분석

 

메타 사이트는 블로그에 새롭게 업데이트된 내용을 다른 블로거들에게 알려주고, 블로거에게 더 많은 블로깅을 할 수 있는 일종의 항해일지 역할을 하는 사이트입니다. 거대한 네트워크에서 블로그들을 순회하며 새롭게 업데이트된 내용들을 찾아내 알려주는 메타 사이트는 거대한 웹 검색 엔진의 축소판이라고 부르기에 알맞지요.

이번 연재를 통해 이러한 블로그 정보를 수집하는 메타 사이트를 개발하는 과정에 대해 알아보도록 하겠습니다.

‘사람이 모이는 장소에는 장터가 생기고, 블로거들이 모이는 곳에는 메타 사이트가 생겨난다’는 정체불명의 명언(?)에서 볼 수 있듯, 메타 사이트는 블로거들에게 아주 중요한 커뮤니케이션 수단이 되고 있습니다. 메타 사이트는 블로그에서 지원하는 RSS 정보로부터 메타 정보를 읽어서 종합적인 블로그의 소식을 전해준다는 의미로 메타 사이트라 불립니다. 하지만 개인적으로는 그와 비슷한 의미를 가지고 있는 블로그 센터라는 좀 더 직관적인 단어를 좋아합니다.

이러한 메타 사이트의 작동 방식은 마치 작은 크기로 줄여놓은 검색 엔진 사이트와도 비슷하게 생각할 수 있습니다. 이번 시간에는 메타 사이트에 대한 소개와 메타 사이트와 수집 로봇 구성, 그리고 수집 로봇에 대한 여러 가지 방법들에 대해서 이야기해 보고자 합니다.

블로그의 정보를 수집하는 로봇
메타 사이트에서 블로그의 최신 정보를 알아내기 위해서는 우선 블로그와 메타 사이트간에 데이터 교환을 위한 방식이 필요하게 됩니다. 검색 엔진에서 사용하는 검색 로봇의 경우 이미 웹이라는 매체가 오래된 HTML 방식으로 구성되어 있었기 때문에 그에 맞는 검색이 가능하도록 로봇을 설계했습니다.

하지만 블로그의 경우 블로그의 내용을 손쉽게 읽을 수 있는 파일 규약을 지원하고 있으며 그러한 규약 중 대표적으로 사용되고 있는 것이 바로 RSS입니다. 덕분에 우리가 만들고자 하는 로봇은 단순히
XML형식으로 되어 있는 이 RSS 파일을 자유롭게 읽고 해석할 수 있으면 손쉽게 해결할 수 있습니다.

검색 로봇과 다른 점이라면 인터넷에 떠도는 많은 웹 페이지들 전체를 대상으로 하지 않는다는 점입니다. 인터넷에 있는 하나의 문서, 자료들도 각자의 저작권이 있는 저작물이며 이를 함부로 수집해서는 안 된다는 것입니다. 물론 이미 이용되고 있는 검색 엔진은 이를 간단히 검색 거부할 수 있는 기능을 지원한다는 명목 하에 대부분의 웹 사이트에서 정보를 수집하고 있지만, 최근 이러한 검색 엔진의 무분별한 자료 수집으로 인한 개인 정보 노출 등의 문제점을 보더라도 이러한 문제는 신중히 생각해야 한다고 생각합니다.

메타 사이트에서는 이러한 이유로 사이트에서 가입되어 있는 블로그의 정보만을 수집하며 언제든 수집을 원하지 않는 게시물에 대해서 수집을 거부할 수 있는 권한을 가지고 있어야 합니다. 물론 수집되는 블로그의 범위를 지금의 검색 엔진처럼 거대한 인터넷 전체로 확대한다면 앞으로 웹 사이트보다 더 많은 컨텐츠를 보유하게 될 블로그의 검색 엔진으로써 활약할 수 있는 프로그램을 만들 수 있지 않을까 합니다.

RSS 파일을 정복하자
블로그의 정보 수집에 있어서 가장 중요한 내용 중 하나인 RSS에 대해 이야기해 보도록 합시다. 이 RSS는 알고 있는 것과 같이 블로그의 정보와 최근 등록된 글들의 정보가 포함되어 있는 XML 파일입니다.

 <리스트 1> RSS 파일의 구조
사용자 삽입 이미지

<?xml version="1.0" encoding="euc-kr" ?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>하늘이의 생각나무</title>
        <link>http://www.in1983.com</link>
        <description>사랑하는 주영이와 알콩달콩 이야기</description>
        <item>
            <title>머리속 가비지 컬렉터 파워 온!!</title>
            <link>http://php.chol.com/~ppk314/tt/index.php?pl=386</link>
            <description> 글의 내용이 들어가나, 생략함 </description>
            <pubDate>Tue, 19 Oct 2004 23:54:00 GMT</pubDate>
        </item>
        ...
        <item>의 반복
...
    </channel>
</rss>
사용자 삽입 이미지

이 RSS 파일은 rss라는 루트 엘리먼트로 시작됩니다. 그리고 채널이라는 하위 엘리먼트를 가지고 있는 이곳에 블로그의 대한 정보들(블로그의 이름, 주소, 설명, 기타 정보들)이 포함됩니다. 그리고 여러 개의 아이템 엘리먼트들을 포함하고 있는데, 이들이 바로 블로그의 글에 대한 정보를 담고 있습니다.

메타 사이트에서 필요한 정보는 대부분 아이템 엘리먼트에 있으니 아이템 엘리먼트의 구조에 대해서 자세히 살펴보도록 하겠습니다.

사용자 삽입 이미지
<그림 1> RSS 파일의 구조 - item들의 구성


<그림 1>을 보면 RSS 파일의 구조에 아이템들의 구성을 볼 수 있습니다. 아이템에 있는 각각의 엘리먼트들은 <표 1>과 같은 의미를 가지고 있습니다.

사용자 삽입 이미지
<표 1> RSS 파일의 엘리먼트들의 의미


이러한 엘리먼트의 이름들은 RSS 버전에 따라서 약간의 차이들이 있으며 <표 1>에 표시한 몇 가지 종류의 엘리먼트 이외에도 많은 종류의 엘리먼트들이 존재하게 됩니다. 그리고 이미 여러 종류의 블로그 툴들이 개발되어 있고, 각자 조금씩 다른 엘리먼트를 조합하여 사용하고 있습니다.

 <리스트 2> 각각 다른 이름의 엘리먼트를 사용하는 item 엘리먼트들
사용자 삽입 이미지

<item>
    <title>머리속 가비지 컬렉터 파워 온!!</title>
    <link>http://php.chol.com/~ppk314/tt/index.php?pl=386</link>
    <description> 글의 내용이 들어가나, 생략함 </description>
    <pubDate>Tue, 19 Oct 2004 23:54:00 GMT</pubDate>
</item>

<item>
    <title>머리속 가비지 컬렉터 파워 온!!</title>
    <link>http://php.chol.com/~ppk314/tt/index.php?pl=386</link>
    <description> 글의 내용이 들어가나, 생략함 </description>
    <pubDate>2004-10-19 23:54:00</pubDate>
    <author>하늘이</author>
</item>

<item>
    <title>머리속 가비지 컬렉터 파워 온!!</title>
    <link>http://php.chol.com/~ppk314/tt/index.php?pl=386</link>
    <description> 글의 내용이 들어가나, 생략함 </description>
    <dc:date>2004-10-19T23:54:00+09:00</dc:date>
    <dc:creator>하늘이</dc:creator>
</item>
사용자 삽입 이미지

각각의 블로그 툴마다 다른 엘리먼트 이름을 사용하고 있습니다. 하지만 메타 사이트의 RSS 수집 로봇이 그러한 RSS의 모든 태그의 정보가 필요한 것은 아닙니다. 메타 사이트에서 필요한 일부 정보들만을 사용하게 되므로 <표 1>에서 정리한 정도의 정보들만을 수집하면 됩니다.

하지만 여기서 문제가 발생하게 되는데, 과연 각기 다른 엘리먼트들의 조합으로 되어 있는 RSS 파일들을 어떻게 처리하면 될까요? 각 블로그 툴의 종류별로 약간 다른 방식으로 RSS 파일을 읽어들이는 방법을 가장 첫 번째로 생각해볼 수 있을 것입니다.

하지만 그럴 경우 잘 알려지지 못해서 RSS 구조의 정보를 잘 모르고 있던 블로그들의 경우에는 어떤 방식으로 처리할까요? 바로 이러한 문제들을 해결하기 위해서 가장 효과적인 방법으로 여러 가지 엘리먼트를 동시에 지원하는 방식을 사용하고자 합니다.

예를 들어 pubDate와 의미가 비슷한 dc:date와 author는 dc:creator와 같은 엘리먼트로 간주하고 처리하는 방식입니다. 이러한 방법을 이용하여 모든 종류의 RSS 파일에서 필요한 내용들로 정리할 수 있습니다.

이러한 RSS 파일의 처리를 위해서 RSS 아이템 한 개를 표현하는 아이템 클래스와 그러한 아이템들을 집합을 표현할 컬렉션 클래스, 그리고 XML 파일로부터 이들의 클래스를 생성하는 RSS 리더 클래스를 각각 구현하여 보도록 하겠습니다. RSS 아이템 클래스의 경우 <표 1>에 나와 있는 내용들을 기억하는 간단한 클래스이기 때문에 그다지 구현에 큰 어려움은 없습니다.


Public Class RSSItem
Public Title As String
Public Link As String
Public Author As String
Public Description As String
Public PubDate As Date
End Class


멤버 변수들 이름으로는 RSS 파일의 엘리먼트 이름과 동일하게 처리했습니다. 지면상 간단한 방식으로 줄였으나, 변수들은 프라이비트(Private)로 선언하고, 퍼블릭(Public)으로 선언된 프로퍼티들을 사용하여 처리하는 방식으로 구현하는 것이 좋습니다. 이 클래스는 날짜 처리 등을 위해서 다음 글에서 수정할 예정이니, 우선은 이대로 사용하기로 하겠습니다.

RSS의 아이템 하나를 저장하는 클래스를 만들었으니, 그러한 다수의 아이템들을 편하게 접근할 수 있는 컬렉션 클래스를 만들어보도록 합시다. 닷넷 프레임워크에서는 간단하게 강력한 컬렉션을 구현할 수 있는 CollectionBase 추상 클래스를 지원하고 있습니다.


MustInherit Public Class CollectionBase
Implements IList, ICollection, IEnumerable


우리가 만들었던 RSSItem 컬렉션 클래스는 바로 이 추상 클래스를 상속받아 만들게 됩니다. CollectionBase에 대한 자세한 설명은 닷넷 프레임워크 SDK 설명서를 참고하길 바랍니다. 이 추상 클래스를 상속받아서 간단하게 RSSItem들을 추상화하는 클래스를 만들어보도록 하겠습니다. 이 클래스에는 우리가 컬렉션 클래스에서 흔히 사용하는 멤버 변수들도 같이 구현하도록 하겠습니다.

 <리스트 3> RSSItemCollection 클래스
사용자 삽입 이미지

Public Class RssItemCollection
Inherits CollectionBase

    Default Public Property Item(ByVal index As Integer) As RssItem
        Get
            Return CType(list(index), RssItem)
        End Get
        Set(ByVal Value As RssItem)
            list(index) = Value
        End Set
    End Property

    ' 삽입, 검색, 삭제 등의 메쏘드
    Public Function Add(ByVal itemObject As RssItem) As Integer
        Return list.Add(itemObject)
    End Function

    Public Function IndexOf(ByVal itemObject As RssItem) As Integer
        Return list.IndexOf(itemObject)
    End Function

    Public Sub Insert(ByVal index As Integer, ByVal itemObject As RssItem)
        list.Insert(index, itemObject)
    End Sub

    Public Sub Remove(ByVal itemObject As RssItem)
        list.Remove(itemObject)
    End Sub

    Public Function Contains(ByVal index As Integer, ByVal itemObject As RssItem) As Boolean
        Return list.Contains(itemObject)
    End Function
End Class
사용자 삽입 이미지

이제 남은 것은 RSS 파일로부터 데이터를 읽어서 이들의 클래스를 반환해주는 RSS 리더 클래스를 만드는 것입니다. 이번 호에서는 네트워크의 XML 파일에도 편하게 접근할 수 있는 XmlTextReader 클래스를 이용하도록 하겠습니다. XML 파일 처리에는 다양한 방법들이 있으니 자신에게 편리한 방법을 선택하여 구현하면 됩니다.

 <리스트 4> RSSReader 클래스
사용자 삽입 이미지

Public Class RSSReader
    Private xmlUrl As String
    Private rssItems As RssItemCollection

    Public Sub New()
        rssItems = New RssItemCollection
    End Sub

    Public Sub New(ByVal xmlAddress As String)
        Me.New()
        xmlUrl = xmlAddress
    End Sub

    Public Property Items() As RssItemCollection
        Get
            Return rssItems
        End Get
        Set(ByVal Value As RssItemCollection)
            rssItems = Value
        End Set
    End Property

    Public Property XmlAddress() As String
        Get
            Return xmlUrl
사용자 삽입 이미지


        End Get
        Set(ByVal Value As String)
            xmlUrl = Value
        End Set
    End Property

    Private Function findValue(ByVal rssXML As XmlTextReader) As String
        Try
            While (True)
                rssXML.Read()
                If (rssXML.NodeType = XmlNodeType.CDATA) Or (rssXML.NodeType =                 XmlNodeType.Text) Then
                Return rssXML.Value
                ElseIf (rssXML.NodeType = XmlNodeType.EndElement) Then
                    Return ""
                End If
            End While
        Catch ex As Exception
            Return ""
        End Try
    End Function

    Public Sub readItems()
        rssItems.Clear()

        Dim isInItem As Boolean

        Dim rssXML As XmlTextReader
        Dim tempItem As RSSItem
        Try
            rssXML = New XmlTextReader(xmlUrl)
            While rssXML.Read()
                Select Case rssXML.NodeType
                Case XmlNodeType.Element
                    If rssXML.Name = "item" Then
                    tempItem = New RSSItem
                    isInItem = True
                ElseIf isInItem Then
                    Select Case rssXML.Name.ToLower
                        Case "title"
                            tempItem.Title = findValue(rssXML)
                        Case "link"
                            tempItem.Link = findValue(rssXML)
                        Case "author", "dc:creator"
                            tempItem.Author = findValue(rssXML)
                        Case "pubdate", "dc:date"
                            tempItem.PubDate = findValue(rssXML)
                        Case "description"
                            tempItem.Description = findValue(rssXML)
                    End Select
                End If

            Case XmlNodeType.EndElement
                    If rssXML.Name = "item" Then
                        isInItem = False
                        rssItems.Add(tempItem)
                     End If
                End Select
            End While
            rssXML.Close()
        Catch ex As Exception
            rssItems.Clear()
        Finally
            rssXML = Nothing
        End Try
    End Sub

End Class
사용자 삽입 이미지

<리스트 4>의 몇 개의 메쏘드를 간단하게 설명하자면, findValue는 XMLTextReader로부터 데이터만을 찾아서 반환해 줍니다. 데이터의 경우 텍스트 형식이나 CDATA 형식으로 지정하는데 이러한 형식 또한 RSS 파일마다 각기 다른 방법을 사용하고 있어서 편리하게 데이터를 읽을 수 있도록 구현했습니다.

ReadItems 메쏘드가 우리가 하고자 했던 XML 파일로부터 데이터를 읽어들이는 이 클래스에서 가장 중심이 되는 작업을 수행하게 됩니다. 간단하게 XML 파일을 읽으며 엘리먼트의 시작 부분에서 아이템 엘리먼트가 시작되는 경우 새로운 RSSItem의 인스턴트를 생성해주고, 이후 아이템 엘리먼트의 각각의 엘리먼트들을 돌며 값을 읽어 저장하고, 아이템 엘리먼트가 끝나면 컬렉션 클래스에 넣어주게 됩니다. 이렇게 만들어진 클래스들은 다음과 같이 사용해볼 수 있습니다.


Dim myRss As New RSSReader("http://php.chol.com/~ppk314/tt/index.xml")
myRss.readItems()

If myRss.Items.Count > 0 Then
Console.WriteLine("첫번째 글의 제목 : " + myRss.Items(0).Title)
End If


네트워크에서 데이터베이스까지
이렇게 해서 모든 종류의 RSS 파일을 읽을 수 있는 클래스가 준비됐습니다. 이제 남은 건 RSS 파일들을 읽고, 그것을 데이터베이스에 정리하는 작업입니다.

간단히 네트워크와 데이터베이스에 대해서 생각하여 보겠습니다. RSS 수집 로봇은 분명 블로그들을 떠돌며 새로운 정보들을 데이터베이스에 저장하는 작업을 거치게 됩니다. 그렇다면 과연 어떤 방식으로 블로그의 RSS 파일이 업데이트되었다는 정보를 알 수 있을까요? 그리고 읽은 RSS 파일의 내용 중 어떻게 새로 추가된 내용을 찾아 그 부분만 데이터베이스에 저장할 수 있을까요?

웹 검색 엔진의 핵심이 이렇게 수집하는 방법과 정리하여 저장하는 방식이 큰 핵심 중 하나라고 말한 것과 같이 이 부분에서 많은 생각과 아이디어들을 떠올려 볼 수 있습니다.

우선 RSS 로봇이 업데이트된 정보를 어떻게 알 수 있는지는 HTTP 프로토콜 헤더에 If-Modified-Since 필드를 사용하여 요청하는 방법을 사용할 수 있습니다. 이 필드에 일정 시간 이후에 수정되어 있는지 여부에 따라서 수정되었다면 정상적으로 XML 파일을 반환할 것이며, 만약 수정되지 않은 상태라면 304 Not Modified 응답을 돌려주게 됩니다. 이 결과를 통해 첫 번째로 서버에서 RSS 파일이 수정되었는지를 확인하고 파일을 읽어온다면 네트워크의 트래픽 감소는 물론 좀 더 효율적인 처리를 할 수 있게 됩니다.

그리고 이제 XML 파일을 읽고 데이터베이스에 저장하게 됩니다. 하지만 RSS 파일에는 새롭게 저장된 글의 내용만을 포함하는 것은 아닙니다. 최근 몇 개의 글을 포함하고 있기 때문에 이미 이전에 데이터베이스에 저장되어 있는 내용을 또다시 저장하는 문제가 발생할 수 있습니다.

이 문제는 해당 글의 링크는 유일하다는 가정을 내려서 쉽게 해결할 수 있습니다. 즉 데이터베이스에 저장할 때의 글의 기준은 링크라는 것입니다. 데이터베이스에 같은 링크 값을 가진 글이 있다면 이미 저장되어 있으므로 넘어가고, 저장되어 있지 않은 글들만 저장하면 됩니다.

이외에도 이전에 RSS 파일의 정보를 이미 데이터베이스에 저장했다고 가정하고 최근 1개의 정보만을 업데이트하는 방법도 있을 수 있습니다. 수시로 RSS 파일의 변경 여부를 체크하는데 이 시간 간격 내에 동시에 여러 개의 글을 올리지 않는다는 가정 하에 사용될 수 있는 방법입니다. 그밖에 성능을 고려한 여러 가지 방법들이 있을 수 있으며 그러한 방법들에 대한 생각은 이 글을 읽는 여러분들께 맡기도록 하겠습니다.

사용자 삽입 이미지
"블로그에서 스팸이?"
사용자 삽입 이미지
 
사용자 삽입 이미지

한때 인터넷에 있는 게시판에 자동으로 광고 글을 등록해주는 스팸 프로그램이 떠돌면서 많은 게시판 관리자들과 게시판 프로그래머들을 골치 아프게 했지만, 이제는 블로그까지 이러한 스팸의 사악한 기운들이 손을 뻗고 있습니다. 이중에는 블로그의 트랙백 기능을 사용하여 광고 내용의 페이지를 많은 블로그 사이트에 대량으로 등록시켜 버리는 신종 광고도 등장했습니다.

거기에 메타 사이트에 등장한 스팸으로는, 저도 지난번에 처음 경험한 광고 블로그 형태였습니다. 이 블로그는 겉으로 보면 일반 블로그처럼 보이지만 메타 사이트에 등록하고 사이트에 클릭할 때마다 적립금이 부여되는 배너들을 달고 있는 포스트를 집중적으로 올리게 됩니다. 이러한 정보들은 그대로 블로깅하는 블로거들에게 노출되며, 메타 사이트에는 광고 글, 도배 글 등으로 다른 글을 읽기 더욱 힘들어지게 합니다.

모든 서비스를 어떻게 하면 광고의 기능으로 사용할 수 있을지를 연구하는 스팸 개발자와 어떻게 수많은 광고성 글들을 차단할 수 있을지를 연구하는 서비스 개발자와의 대립은 미래의 어떤 인터넷 환경이 온다고 해도 끊임없이 계속될 것 같습니다.
사용자 삽입 이미지
사용자 삽입 이미지


다음 시간에는 RSS 수집 로봇 구현
사실 메타 사이트를 만들기 위한 기본적인 설계는 이번 연재에서 대부분 마쳤다고 볼 수 있습니다. 다음 연재에서는 이번에 구현한 클래스들과 이야기한 방법들을 토대로 RSS 수집 로봇을 구현하고, 거기서 발생할 수 있는 여러 가지 문제들에 대해서 이야기하도록 하겠습니다.

그리고 필자의 블로그에 들어오면 ‘창작’이라는 카테고리에 메타 사이트를 제작하면서 간단히 적어둔 몇 가지 관련 내용들이 있으니 관심 있는 독자들이라면 참고하고, 궁금증이나 더 나은 아이디어가 있다면 답글로 이야기해 주기 바랍니다. 이밖에 메타 사이트 개발에 관심 있는 사람이 있다면 이름이 거창한(?) ‘올블로그 개발 연구실(
lab.allblog.net)’의 기술 연구실 게시판에서 메타 사이트에 대한 여러 가지 이야기를 함께 나눌 수 있었으면 좋겠습니다. @

* 이 기사는 ZDNet Korea의 제휴매체인마이크로소프트웨어에 게재된 내용입니다

 

박영욱 (Allblog.net 운영자)

2005/03/04

 

사용자 삽입 이미지

 

원문 :http://www.zdnet.co.kr/techupdate/lecture/dotnet/0,39024986,39133977,00.htm

  Comments,     Trackbacks