Silverlight를 설치하려면 여기를 클릭합니다.*
Korea 대한민국변경|Microsoft 전체 사이트
MSDN
|개발자 센터
MSDN 홈 > MSDN 칼럼 > Coding4Fun > 개발 우선 순위: 재미 우선

개발 우선 순위: 재미 우선

Duncan Mackenzie
Microsoft Developer Network

2003년 8월 20일

요약: Duncan Mackenzie가 RSS 피드를 검색하고 표시하는 응용 프로그램을 만드는 과정에서 유용한 기능을 사용하여 개발 프로젝트를 일찍 완성할 수 있는 방법을 보여 줍니다.

적용 대상:
    Microsoft Visual Studio .NET

이 기사의 소스 코드를 다운로드합니다. 

끝없는 작업

제 기사를 계속 접해 온 독자라면 제가 지루한 작업은 안 하고 재미있고 유용한 코드 샘플을 작성하는 데에만 시간을 보낸다고 생각할 것입니다. 바램이야 그렇지만 늘 즐거운 작업만 할 수는 없습니다. 요즘은 비교적 재미없는 시스템 작업으로 바빠서 도저히 재미있는 기사를 쓸 시간이 없었습니다.

최근에 각 기능의 작업 순서에 따른 내부 시스템의 새로운 요구 사항 목록을 받았습니다. 목록 끝에 아주 재미있는 내용이 "Nice to Have"라는 표시로 숨겨져 있는 것을 금방 알아챘습니다. "이것은 절대 빌드할 생각은 아니지만 참고로 목록에 둔다"는 PM의 설명이었습니다. 목록 순서를 혼동했다는 뻔한 핑계를 대야겠지만, 재미있는 기능부터 설명하고 "최우선 순위/필수 기능"과 같은 다소 지루한 기능은 나중에 데스크톱과 하드 디스크를 정리한 후 다루는 것이 좋겠습니다.

MSDN의 페이지 계획

이 기사의 주제인 "재미있는" 기능을 시작하기 전에 작업 중인 시스템 상황을 먼저 설명해야 하겠습니다. 두어 달 전에, 콘텐트를 계획하고 있는 Visual Basic 사이트 등 MSDN 개발자 센터 페이지를 디자인하고 작성할 때 사용하는 Page Planner라는 내부 시스템을 빌드한 적이 있습니다. 이 시스템(그림 1 참고)에서는 http://msdn.microsoft.com/vbasic/using/building/windows 와 같은 특정 기술 관련 기사 페이지를 비롯하여 사이트의 개별 페이지를 모두 업데이트할 수 있습니다.

그림 1. 실행 중인 Page Planner

이러한 사이트를 유지 관리하기 위해 일상적으로 수행하는 주된 작업에는 새 기사가 있을 경우 해당 기술 정보 페이지를 업데이트하는 일이 포함됩니다. 일반적으로 URL을 직접 입력하거나 브라우저 창에서 링크를 끌어 페이지를 업데이트합니다. 최근에 릴리스된 MSDN RSS 피드(최신 콘텐트 목록 포함)와 4GuysFromRolla.com과 같은 사이트의 유사한 피드에서 "Drag-and-Drop Links from an RSS feed(RSS 피드에서 링크 끌어서 놓기)"에 대한 "유용한" 요청을 만들었습니다.

새로운 기능 디자인

우선 순위가 아주 낮아 이 요청에 대해서는 한 줄 정도로만 설명하지만 저는 MSDN 사용자들이 찾는 내용에 대해 좋은 아이디어가 떠올라 대략적인 디자인부터 시작했습니다.

RSS Viewer 창에서는 RSS 피드를 보고 링크를 피드에서 시스템의 다른 부분으로 끌 수 있습니다. 사용자는 시스템 사용자 모두에게 표시되는 마스터 피드 목록이나 자신의 개인 목록에서 선택할 수 있으며 원할 경우 개인 목록에 추가하여 피드 URL을 직접 입력하도록 선택할 수 있습니다.

DataGrid 컨트롤에서는 약간의 사용자 지정 코드로 피드를 표시할 수 있는 매우 간단한 방법(다양한 크기의 콘텐트, XHTML 지원 등)을 생각하지 못하고 Microsoft Windows Form에서 Microsoft Internet Explorer를 호스팅하는 웹 브라우저 컨트롤을 사용하여 피드를 표시했습니다. XSL 변환을 사용하면 RSS 정보를 HTML로 변환하여 브라우저 컨트롤로 전달해 표시할 수 있습니다.

참고   이러한 방법으로 구현할 경우 끌어서 놓기 기능을 기본적으로 사용할 수 있는 긍정적 효과가 있습니다. 웹 브라우저 컨트롤을 사용하면 코드를 추가하지 않고도 다른 응용 프로그램에 링크를 끌어 놓을 수 있습니다.

링크의 마스터 목록은 중앙의 리포지토리 형식으로 저장되지만 사용자의 개인 링크 집합은 로컬로 저장되어 필요에 따라 업데이트됩니다. 여전히 로컬 기본 설정을 약간 편집할 필요는 있지만 최종 기능 목록은 다음과 같습니다.

  • 중앙 저장소에서 마스터 피드 목록을 검색합니다.
  • 로컬 저장소에서 개인 피드 목록을 검색합니다.
  • RSS 피드를 검색하고 HTML로 변환합니다.
  • HTML을 웹 브라우저에 표시합니다.
  • 사용자가 RSS 피드의 URL을 입력할 수 있습니다.
  • 사용자가 개인 피드 저장소를 추가하거나 편집하거나 삭제할 수 있습니다.
  • 사용자가 마스터 피드 목록을 추가하거나 편집하거나 삭제할 수 있지만 모든 경우에 해당되지는 않습니다.

제 경우에는 마스터 피드 목록을 주 Page Planner 시스템에서 사용하는 동일한 공유 데이터베이스에 저장한 다음 SqlClient 클래스를 사용하여 검색하거나 편집할 계획이지만 사용자는 웹 서비스 기반, 파일 기반 중앙 저장소 등의 다른 구현이 필요할 수도 있습니다. 마스터 피드 목록과 개인 피드 목록의 저장 방법을 쉽게 바꾸기 위해 IFeedList 인터페이스를 통해 프로그램의 해당 측면이 요약되도록 디자인했습니다. 사용자는 이 구현을 통해 피드 목록을 저장하고 검색하는 방법을 직접 만들 수 있습니다.

피드 표시

물론 피드 목록 저장 및 업데이트는 이 작은 프로젝트의 부수적 목적이며 주요 기능은 RSS 피드의 검색 및 표시입니다. 그러므로 시스템의 해당 부분부터 보여 줄 경우에 가장 좋습니다.

피드 자체를 로드하기 위해 XMLDocumentLoad 메서드를 사용합니다. 그런 다음 다른 URL(또는 파일 위치)에서 XSL을 XSLTransform 인스턴스로 로드합니다. 끝으로 XSLTransform 클래스의 Transform 메서드를 사용하여 XMLDocument를 가져온 다음 XSL을 사용하여 변환합니다. 변환 결과는 스트림으로 기록되므로 결과를 받아들일 문자열 기반 스트림(IO.StringWriter의 인스턴스)을 만들었습니다.

Dim myDoc As New XmlDocument
myDoc.Load(rssURL.Text)

Dim result As New System.Text.StringBuilder
Dim resultStream = New IO.StringWriter(result)

Dim xslt As New XslTransform
xslt.Load(xsltURL.Text)
xslt.Transform(myDoc, _
     New Xsl.XsltArgumentList, _
     resultStream)

실제 작업이 이 기사의 다운로드에 포함된 XSL 파일 자체에서 수행되므로 여기까지는 아주 간단한 코드입니다. RSS 구현을 통해 반드시 일관성이 보장되는 것은 아니므로 XSL 자체에서는 RSS 피드를 전혀 처리할 수 없습니다. weblogs.asp.net, MSDN, GotDotNet 등의 피드에는 아직도 XSL이 유효하므로 여기서는 그 정도로 충분합니다. 변환 결과를 받으면 포함된 브라우저 컨트롤에 표시되는 웹 페이지 본문에 변환 결과를 배치하여 표시합니다.

Dim myDOMDoc As mshtml.HTMLDocument
myDOMDoc = DirectCast( _
    Me.embeddedBrowser.Document, _
    mshtml.HTMLDocument)

myDOMDoc.body.innerHTML = result.ToString

지금까지 나온 모든 코드를 작성하고 작은 샘플 응용 프로그램을 사용하여 XSL 코드의 여러 부분들을 테스트했습니다(그림 2 참고). 계속해 보다가 결국 그 샘플을 제거했지만 지금으로서는 다양한 RSS 피드에 대해 코드를 테스트하는 것이 좋은 방법입니다.

그림 2. 간단한 폼의 코드 테스트

모든 것이 제대로 되었는지 확인

처음에는 일부 피드가 날짜의 내림차순(최근 항목 우선 표시)으로 표시되지 않아 혼란을 주었습니다. 대부분의 RSS 피드가 이미 날짜의 내림차순으로 정렬되었지만 MSDN 피드는 분명히 예외라는 점을 알 수 있습니다. 일관성을 위해 <xsl:sort> 요소를 통해 XSL에 정렬 코드를 추가하기로 결정했습니다. 문자열 기반 날짜 값을 저장 가능한 새 형식으로 변환하기 위해서 Visual Basic .NET을 사용해 작성한 사용자 정의 함수를 포함해야 했으므로 pubDate 값에 따라 직접 정렬되도록 지정한 실수로 인해 이 변환은 더욱 흥미로운 양상을 띄게 되었습니다.

<msxsl:script language="vb" implements-prefix="utility">
function GetDate(pubDate As String)
  Try
      Dim myDate as Date = CDate(pubDate)
      Return myDate.ToString("yyyyMMddHHmmss")
  Catch
  
  End Try
end function
</msxsl:script>

함수 자체는 문자열을 날짜로 변환한 다음 쉽게 정렬할 수 있는 형식의 문자열로 다시 출력하려는 아주 간단한 내용입니다. <xsl:sort> 함수를 사용할 경우 GetDate 함수 결과를 사용하여 실제 정렬을 수행합니다.

<xsl:template match="/rss/channel">
    <xsl:for-each select="./item">
        <xsl:sort order="descending"
         select="utility:GetDate(./pubDate)" />
        <xsl:apply-templates select="." />
    </xsl:for-each>
</xsl:template>

GetDate 함수 주위의 Try…Catch 블록은 오류를 처리하지 않지만 코드에서 날짜 문자열을 구문 분석할 수 없을 경우에도 XSL은 그대로 작동할 수 있도록 합니다.

RSS에서의 다양성 지양

RSS의 사양은 HTML 콘텐트의 적절한 처리와 날짜 지정 방법을 비롯하여 여러 가지 면에서 융통성이 있어 다양한 방법을 사용할 수 있습니다. RSS를 출력하도록 시스템을 작성할 경우에는 이러한 융통성이 아주 중요하지만 읽어 들일 때는 다소 문제가 될 수 있습니다. 이러한 융통성으로 인해 가장 먼저 날짜에 관련된 문제가 발생했으며 테스트하던 대부분의 피드에서 pubDate 요소를 사용했지만 몇몇 피드에서는 대신 dc:date 요소를 사용했습니다. 사용 가능한 날짜 특성에 관계없이 사용하고 표시할 <xsl:choose> 함수를 추가하여 이 문제를 처리했습니다.

<xsl:choose>
    <xsl:when test='pubDate'>
        <p>Posted on: 
            <xsl:value-of select="pubDate" /></p>
    </xsl:when>
    <xsl:when test='dc:date'>
        <p>Posted on: 
            <xsl:value-of select="dc:date" /></p>
    </xsl:when>
    <xsl:otherwise>
    <p>Couldn't Retrieve Date</p>
    </xsl:otherwise>
</xsl:choose>

RSS 피드의 HTML 콘텐트를 처리하는 세 가지 주요 방법 중 하나를 선택하기 위해 <xsl:choose>를 사용했기 때문에 다운로드한 RSS 피드의 모든 HTML 콘텐트를 처리하는 데에도 비슷한 방법을 사용해야 합니다. 일반적으로 사용하는 세 가지 방법은 다음과 같습니다.

  • HTML 블록에 content:encoding 태그로 플래그를 지정하고 모든 태그를 HTML로 인코딩합니다.
  • HTML을 xhtml:body 요소에 넣고 HTML 태그는 그대로 둡니다.
  • 일반 텍스트와 마찬가지로 표시되지 않은 인라인 상태로 둡니다.

이러한 세 가지 경우를 각각 처리할 수 있는지 확인하기 위해 <xsl:choose> 함수(XSLT)를 한 번 더 사용하여 각각의 특정 형식에 맞는 구현을 선택하고 정확한 방법을 결정할 수 없을 경우에는 단지 인코딩되지 않은 콘텐트로 간주합니다.

<xsl:choose>
    <xsl:when test='xhtml:body'>
        <xsl:copy-of select='xhtml:body'/>
    </xsl:when>
    <xsl:when test='content:encoded'>
        <xsl:value-of 
            disable-output-escaping='yes'
            select='content:encoded'/>
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of
            disable-output-escaping='yes'
            select='description'/>
    </xsl:otherwise>
</xsl:choose>

해당 콘텐트를 RSS 피드로 표시하기 위해 이러한 세 가지 방법 중 하나를 사용하는 모든 피드(xhtml:body, description 또는 content:encoding)에 최종 결과가 적용되어야 하므로 마지막에는 그림 3과 비슷하게 표시됩니다.

그림 3. 피드의 HTML 콘텐트 표시

이제 다른 사람이 제공한 HTML 콘텐트(예: RSS 피드 내의 콘텐트)를 표시할 때마다 특히 여기에서 사용한 방법으로 "about:blank" 페이지 콘텐트를 바꿀 때 발생할 수 있는 위험을 알고 있어야 합니다. HTML이 포함된 브라우저에 표시될 경우는 로컬 영역 안에서 실행되는 것입니다. 이때는 인터넷 영역보다 보안 제한이 훨씬 낮을 가능성이 많습니다. HTML을 표시하기 전에 정리할 수 있는 여러 가지 방법이 있지만 완벽한 보안을 위해서는 약간의 작업이 필요합니다. HTML로 인한 RSS의 몇 가지 문제와 이러한 문제를 방지하는 방법에 대한 설명은 이 유용한 블로그 게시판 을 참고하십시오.

피드 목록 저장 및 검색

RSS 피드가 표시되고 충분한 샘플 데이터로 시스템을 테스트(내 블로그  피드에 대해 작동)하여 제대로 작동하는지 확인한 후에는 개인 피드 목록과 마스터 피드 목록의 검색과 편집을 지원하는 코드를 작성해야 합니다. 여기서는 IFeedList 인터페이스를 사용하는 두 가지 클래스 즉, SQL 액세스용 클래스 및 현재 사용자에게 고유한 xml 설정 파일과 작동하는 클래스만 구현합니다. IFeedList 인터페이스와 두 가지 구현에 대한 소스를 보려면 코드 다운로드를 참고하십시오.


Public Interface IFeedList

    Function GetList() As Feeds
    Function AddFeed( _
             ByVal newFeed As Feed) As Boolean
    Function DeleteFeed( _
             ByVal feedToToast As Feed) As Boolean
    Function CanAdd() As Boolean
    Function CanDelete() As Boolean

End Interface


개인 파일 기반 버전의 경우 항목을 자유롭게 추가하고 제거해도 괜찮겠지만 Microsoft SQL Server™ 버전(여러 사용자가 공유하는 마스터 목록에 액세스하는 데 사용)의 경우 보안을 더 강화해야 합니다. 여기서는 통합 인증을 사용하므로 SQL Server에서 사용자 권한을 제한하여 잠재된 모든 보안 문제를 처리할 수 있지만 서버 역할을 사용하고 사용자의 역할 구성원을 확인하여 사용자 권한을 검사하겠습니다. 물론 원본으로 사용하는 테이블 또는 데이터베이스 개체 보안 제한도 적용되어 두 번째 보안 계층이 제공됩니다. 역할 구성원을 검사하는 StoredProc에 대한 호출을 포함하여 CanAdd의 구현은 다음과 같습니다.

Public Function CanAdd() As Boolean _
       Implements IFeedList.CanAdd
    '현재 로그온한 사용자에게 
    '테이블에 추가할 권한이 있습니까?
    'SQL Server에 
    '"FeedAdministrator" 역할이 있는지 검사하십시오.
    Return IsInRole("FeedAdministrator")
End Function

Private Function IsInRole( _
          ByVal Role As String) As Boolean
    Try
        Dim conn As New _
            SqlClient.SqlConnection( _
                Me.m_connectionString)
        conn.Open()
        Dim cmdIsInRole As New _
            SqlClient.SqlCommand( _
                "IsInRole", conn)
        cmdIsInRole.Parameters.Add( _
            "@Role", SqlDbType.NVarChar, _
            128).Value = Role
        cmdIsInRole.Parameters.Add( _
            "@RC", SqlDbType.Int)
        cmdIsInRole.Parameters( _
            "@RC").Direction = _
            ParameterDirection.ReturnValue
        cmdIsInRole.Parameters.Add( _
            "@Result", SqlDbType.Bit)
        cmdIsInRole.Parameters( _
            "@Result").Direction = _
            ParameterDirection.InputOutput
        cmdIsInRole.Parameters( _
            "@Result").Value = 0
        cmdIsInRole.ExecuteNonQuery()

        Return CBool( _
            cmdIsInRole.Parameters( _
            "@Result").Value())
    Catch ex As Exception
        Return False
    End Try
End Function

사용 가능한 피드 목록에서 피드를 선택하고 로드된 피드를 개인(로컬) 목록에 추가할 수 있도록 UI도 약간 업데이트합니다. 그림 4에서는 저장된 피드 중 하나를 선택하거나 RSS 피드의 URL을 직접 입력할 수 있는 콤보 상자와 새로운 Save 단추가 갖추어진 최종적인 인터페이스를 보여 줍니다.

그림 4. Save 단추가 포함된 최종 폼

시스템을 개발하면서 나중에 쉽게 다시 사용할 수 있도록 시스템을 분할하기로 결정했습니다. 따라서 포함된 브라우저는 이제 XSL 및 RSS 코드와 함께 그림 4의 폼에 놓인 사용자 지정 컨트롤에 결합됩니다. 실제 응용 프로그램에서는 이 코드를 사용하기 위해 SQL 연결을 전달하고 전체 폼과 관련 코드를 모두 라이브러리 프로젝트에 배치할 수 있도록 코드를 약간 변경할 것입니다. 결과적으로 기존 Windows Forms 응용 프로그램에 있는 단추로 아주 쉽게 시작할 수 있는 프로그램이 작성됩니다. 그러나 이 샘플은 독립 실행형 응용 프로그램으로 작성되어 단독으로 실행할 수 있습니다.

리소스

언제나 그렇듯이 완성된 응용 프로그램을 빌드하기 위해서는 웹의 여러 곳에 있는 리소스를 사용해야 합니다. 이 특정 샘플에서는 GotDotNet 사용자 샘플 대신 다음을 사용합니다.

  • Eric J. Smith의 뛰어난 CodeSmith  유틸리티를 사용하여 유형이 엄격히 정해진 피드 컬렉션을 만듭니다.
  • RSS Bandit의 템플릿 폴더에서 가져온 starter XSL을 사용합니다(작업 영역 도 확인).
  • XSL의 여러 부분과 Kent Sharkey의 "고객지원"을 사용합니다.

RSS 데이터의 유용한 소스와 이 기사의 코드를 사용하여 표시할 훌륭한 자료는 물론 도움이 될 만한 내용도 소개해 드리겠습니다.

물론 다른 곳에도 RSS 피드는 많지만 이러한 사이트의 피드에서는 오랫동안 충분히 작업을 지속할 수 있습니다.

코딩 문제

일부 Coding4Fun 열 끝에 사소한 코딩 문제가 발생할 경우 원한다면 이 문제를 해결할 수 있습니다. 이 기사에서는 RSS를 출력하거나 사용하는 항목을 만들어야 하는 문제가 있습니다. Visual Basic .NET, C#, J# 또는 관리되는 C++ 등 관리되는 코드가 우선 사용되지만 COM 인터페이스를 노출하는 관리되지 않는 구성 요소도 좋습니다. 직접 만든 샘플을 자유롭게 GotDotNet 에 게시하고 이에 대한 설명과 관심을 갖는 이유를 함께 작성하여 duncanma@microsoft.com으로 전자 메일 메시지를 보내 주십시오. 언제라도 여러분의 의견을 환영하지만 코드 샘플이 아니라 샘플 링크를 보내 주시면 감사하겠습니다.

애호가 콘텐트에 대한 아이디어가 있으십니까? duncanma@microsoft.com으로 알려 주시고 즐거운 코딩을 경험하시기 바랍니다.

 


Coding4Fun

Duncan Mackenzie는 주로 MSDN의 Microsoft Visual Basic .NET  콘텐트 전략가로 일하며 전업 코더를 겸하고 있습니다. 얼 그레이(Earl Grey) 차를 마셔야 일이 잘 풀린다는 그의 훌륭한 성과를 기대해 봅니다. Duncan에 대한 자세한 내용은 Duncan 사이트 를 참고하십시오.



화면 맨 위로화면 맨 위로


최종 수정일: 2005년 8월 18일
Top of Page Top of Page


Microsoft