달력

42024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

ASP.NET Whidbey의 향상된 캐싱 기능

G. Andrew Duthie
Graymad Enterprises, Inc.

2004년 2월

적용 대상:
   Microsoft ASP.NET

요약: ASP.NET Whidbey에서 사용할 수 있는 새 캐싱 기능을 살펴봅니다. ASP.NET 응용 프로그램 개발자는 이 캐싱 기능을 사용하여 차세대 ASP.NET 응용 프로그램의 성능을 크게 향상시킬 수 있습니다(23페이지/인쇄 페이지 기준).

이 기사의 샘플 코드를 다운로드하십시오.

목차

소개
캐시 종속성의 확장성
SQL 캐시 무효화
사후 캐시 대체
결론
관련 서적

소개

ASP.NET의 가장 뛰어나고 유용한 기능, 그리고 논쟁의 여지가 있지만 가장 간과되는 기능 중 하나는 페이지 출력 및 임의 데이터 모두의 캐싱을 다양하게 지원하는 기능입니다. ASP.NET을 사용하면 메모리 내 렌더링된 페이지 출력, 사용자 컨트롤의 출력, 데이터 집합에서 수집한 내용 등을 저장할 수 있습니다. ASP.NET에는 간단한 시간 기준 캐시 만료에서 키 및 파일 기준 캐시 종속성에 이르기까지 캐시의 업데이트 시점을 결정하기 위한 다양한 기술도 있습니다.

현재 릴리스의 ASP.NET에서 캐싱을 사용해 본 적이 있다면 캐싱이 성능 및 확장성을 크게 향상시킬 수 있다는 것을 알고 있을 것입니다. 콘텐츠를 데이터베이스나 다시 처리된 페이지에서 제공하는 대신 메모리에서 제공하므로 성능이 향상되고, 이제 모든 페이지에 프로세서, 파일 시스템 및 데이터베이스를 사용할 필요가 없으므로 확장성이 향상됩니다.

그러나 ASP.NET 팀은 ASP.NET 캐싱의 현재 상태에 만족하지 않고 ASP.NET Whidbey에 다수의 유용한 새 캐싱 기능을 추가했습니다. 이 기사에서는 이러한 새 캐싱 기능에 대해 설명합니다. 이 기사는 다음의 세 가지 기능 위주로 설명되어 있습니다.

  • 확장 가능한 캐시 종속성
  • SQL 캐시 무효화
  • 사후 캐시 대체

캐시 종속성의 확장성

ASP.NET 1.1에서는 종속성을 사용하여 캐시 항목을 자동으로 만료할 수 있지만, 이 기능은 다소 제한되어 있습니다. 즉, 파일 및 기타 캐시 키를 종속성 항목으로 사용할 수 있지만 여기까지가 한계입니다. 물론 이것도 유용하지만 대다수의 사람들은 종속성 메커니즘을 직접 확장할 수 있게 되기를 기대해 왔습니다. 이제 더 이상 Sealed가 아닌 새로운 버전의 CacheDependency 클래스가 포함되어 있으므로 종속성 메커니즘 자체를 확장하는 것이 가능합니다. 더 이상 Sealed가 아니라는 것은 이제 이 클래스로부터 상속할 수 있다는 의미입니다. 새 버전의 클래스를 통해 개발자는 세 가지 새로운 멤버를 사용할 수 있습니다.

  • GetUniqueID - 재정의할 때 사용자 지정 캐시 종속성의 고유 식별자를 호출자에게 반환할 수 있습니다.
  • DependencyDispose - 사용자 지정 캐시 종속성 클래스에서 사용하는 리소스를 처리하는 데 사용됩니다. 사용자 지정 캐시 종속성을 만들 때 이 메서드를 구현해야 합니다.
  • NotifyDependencyChanged - 캐시 항목의 만료가 사용자 지정 캐시 종속성 인스턴스에 종속되도록 할 때 호출합니다.

마지막 두 멤버를 다음 샘플에서 사용해 보겠습니다. 예제에서는 블로그나 기타 뉴스 소스로부터 RSS(Really Simple Syndication) 피드를 읽고 결과를 XmlDocument로 캐시하는 사용자 지정 캐시 종속성을 만드는 방법을 보여 줍니다. 그런 다음 XMLDocument를 ASP.NET Xml 컨트롤과 함께 사용하여 블로그의 콘텐츠(XML 스타일시트를 사용하여 HTML로 변환됨)를 표시합니다.

사용자 지정 캐시 종속성을 만드는 첫 번째 단계는 CacheDependency 클래스로부터 상속하는 새 클래스를 만드는 것입니다. 정규화된 이름을 사용하지 않고 이 작업을 수행하려면 System.Web.Caching 네임스페이스를 가져와야 합니다.

참고   또한 쉽게 액세스할 수 있도록 Timer 클래스에 대해 System.Threading을, XmlDocument 클래스에 대해 System.Xml을 가져옵니다.

클래스 정의는 다음과 비슷합니다.

Imports SystemImports System.WebImports System.ThreadingImports System.Web.CachingImports System.XmlNamespace v2_Caching Public Class BlogCacheDependency Inherits CacheDependency '여기에 클래스 코드 입력End Namespace(참고: 프로그래머 코멘트는 샘플 프로그램 파일에는 영문으로 제공되며 기사에는 설명을 위해 번역문으로 제공됩니다.)

그 다음에는 일부 멤버 변수를 추가합니다.

' 타이머는 분리된 스레드에서 종속성 검사를 수행하는 데 사용됩니다.Shared TickTock As TimerDim m_PollTimeSec As Integer = 300Dim m_RSS As XmlDocumentDim m_Feed As String

Timer 클래스는 RSS 피드를 폴링하여 변경 사항을 찾는 데 사용됩니다. 다른 변수는 폴 시간, 초기 RSS가 포함될 XmlDocument, 그리고 RSS 피드의 URL입니다.

XmlDocument를 표시할 수 있도록 Public Property를 추가합니다.

Public ReadOnly Property RSS() As XmlDocument Get Return m_RSS End GetEnd Property

RSS를 클라이언트에 반환하기만 하면 되므로 이 속성은 ReadOnly로 표시되고 Set 접근자가 없어야 합니다.

그 다음에는 BlogCacheDependency 클래스의 두 주요 요소인 생성자와 CheckDependencyCallback 메서드를 살펴보겠습니다. 생성자는 아래에 표시되어 있습니다.

' BlogCacheDependency 생성자Public Sub New(ByVal Feed As String, ByVal PollTimeSec As Integer) m_Feed = Feed m_PollTimeSec = PollTimeSec m_RSS = GetRSS(m_Feed) If TickTock Is Nothing Then TickTock = New Timer(New _ TimerCallback(AddressOf CheckDependencyCallback), Me, _ (m_PollTimeSec * 1000), (m_PollTimeSec * 1000)) End IfEnd Sub

생성자는 검색 및 검사할 RSS 피드의 URL이 들어 있는 문자열과 피드의 각 폴링 요청 사이 시간(초)이 들어 있는 정수를 받습니다. 해당 값이 로컬 멤버 변수에 저장된 후 m_RSS 멤버 변수는 GetRSS 메서드를 호출하여 채워집니다. 이 과정은 아래에서 곧 설명할 예정입니다. 마지막으로 생성자는 Timer 멤버 변수가 인스턴스화되었는지 확인하고, 인스턴스화되어 있지 않으면 타이머의 새 인스턴스를 만들어서 m_PollTimeSec 멤버 변수의 값을 기준으로 CheckDependencyCallback 메서드를 주기적으로 호출하도록 설정합니다.

아래에 표시된 CheckDependencyCallbackBlogCacheDependency(Sender 인수로부터 CheckDependencyCallback에 할당됨) 형식의 로컬 인스턴스 변수를 만든 다음 GetRSS 메서드에서 추출한 새 XmlDocument를 만들고 OuterXml 속성 값을 m_RSS 멤버 변수(생성자에서 검색한 원래 XmlDocument가 포함되어 있음)의 해당 속성 값과 비교합니다. 두 값이 동일한 경우에는 아무런 작업도 수행되지 않습니다. 하지만 서로 다른 경우에는 코드가 NotifyDependencyChanged를 호출하여 ASP.NET 캐시 엔진으로 하여금 캐시된 항목을 제거하도록 합니다.

Public Sub CheckDependencyCallback(ByVal Sender As Object) Dim BCD As BlogCacheDependency = Sender Dim NewRSS As XmlDocument = GetRSS(m_Feed) If Not NewRSS.OuterXml = m_RSS.OuterXml Then BCD.NotifyDependencyChanged(BCD, EventArgs.Empty) End IfEnd Sub

GetRSS 메서드는 매우 간단합니다.

Function GetRSS(ByVal Feed As String) As XmlDocument Dim RSSDoc As New XmlDocument RSSDoc.Load(m_Feed) Return RSSDocEnd Function

이 메서드는 간단히 XmlDocument 형식의 로컬 변수를 만들고 XmlDocumentLoad 메서드를 호출하여 m_Feed 변수에 포함된 URL을 이 메서드에 전달하고 XmlDocument를 호출자에게 반환합니다. RSS 피드를 검색하는 것은 매우 간단합니다. RSS는 XML이므로 단 몇 줄의 코드로 RSS 피드를 아주 쉽게 가져올 수 있습니다. 나중에는 XSL을 사용하여 RSS 피드를 표시하도록 지정하는 것이 매우 간단하다는 것을 알게 될 것입니다.

마지막으로 사용자 지정 종속성 클래스가 처리될 때 몇 가지를 정리해야 합니다. DependencyDispose 메서드를 재정의하고, 이 안에서 Timer 인스턴스를 처리하면 됩니다.

Protected Overrides Sub DependencyDispose() TickTock = Nothing MyBase.DependencyDispose()End Sub

이제 사용자 지정 캐시 종속성 클래스의 코드가 완료되었습니다. 50줄도 안 되는 코드가 전부입니다. Whidbey의 새로운 코드 컴파일 기능  덕분에 클래스를 웹 사이트의 Code 폴더에 저장하기만 하면 ASP.NET에서 클래스를 자동으로 컴파일합니다.

사용자 지정 캐시 종속성을 사용하는 것은 이보다 더 쉽습니다. 사용자 지정 캐시 종속성 클래스를 사용하여 ASP.NET Xml 컨트롤을 사용하는 RSS 피드를 검색하고 표시하는 ASP.NET 페이지가 아래에 표시되어 있습니다. 코드는 입력된 URL의 캐시에 항목이 있는지 테스트를 통해 확인합니다. 항목이 없는 경우에는 레이블 텍스트를 설정하여 항목이 캐시에서 추출되지 않았음을 나타낸 다음 BlogCacheDependency 클래스의 새 인스턴스를 만들어서 RSS 피드의 URL과 폴링 사이의 초 단위 시간(이 경우 1시간을 나타내는 3600)을 전달합니다. 그런 다음 사용자가 지정한 URL을 키로, 종속성 인스턴스의 RSS 속성을 값으로 사용하여 Cache에 항목을 삽입하고 캐시된 항목이 종속성 인스턴스에 종속되도록 설정합니다. 마지막으로 코드는 캐시된 항목에 Xml 컨트롤의 Document 속성을 설정하고, "feeds.xsl"에 TransformSource 속성을 설정합니다. "feeds.xsl"은 RSS를 HTML로 변환하는 스타일시트로, today.icantfocus.com 웹 사이트(http://today.icantfocus.com/blog/archives/entries/000430/)(영문 사이트)에 있습니다.

<%@ page language="VB" %><%@ import namespace="v2_Caching" %><script runat="server"> Sub Button1_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Label2.Text = "예" If Cache(TextBox1.Text) Is Nothing Then Label2.Text = "아니요" '변경 내용을 60분마다 폴링하도록 종속성을 만듭니다. Dim dep As New BlogCacheDependency(TextBox1.Text, 3600) Cache.Insert(TextBox1.Text, dep.RSS, dep) End If FeedXml.Document = Cache(TextBox1.Text) FeedXml.TransformSource = "feeds.xsl" End Sub</script><html><head runat="server"> <title>사용자 지정 캐시 종속성 예제</title></head><body> <form runat="server"> 표시할 RSS 피드를 입력하십시오: <asp:textbox id="TextBox1" runat="server"> </asp:textbox> <asp:button id="Button1" onclick="Button1_Click" runat="server" text="피드 가져오기" /> <hr /> 캐시에서 가져옴: <asp:label id="Label2" runat="server" forecolor="#FF3366"> </asp:label> <br /> 피드: <asp:xml id="FeedXml" runat="Server" /> <br /> </form></body></html>

그림 1은 페이지의 모양을 보여 줍니다.

그림 1. CustomDependency.aspx

사용자가 유효한 피드 URL(이 예제에서는 URL의 유효성을 검사하지 않으므로 사용자가 유효하지 않은 URL을 입력하면 예외가 발생)을 입력하고 피드 가져오기 단추를 클릭하면 그림 2와 비슷한 결과가 나타납니다. 캐시에서 피드가 추출되지 않는 것을 볼 수 있습니다.

그림 2. 블로그 피드 표시

후속 요청에서는 그림 3과 같이 캐시에서 피드를 제공합니다.

그림 3. 캐시에서 피드 제공

피드가 변경되는 경우 다음 폴링 간격에서 Timer 코드가 캐시된 항목을 무효화하므로 결과가 그림 4와 같아집니다.

그림 4. 업데이트된 피드

다음은 사용자 지정 캐시 종속성을 개발할 때 주의해야 할 마지막 사항입니다. 외부 리소스, 특히 사용자가 제어할 수 없는 외부 리소스를 폴링하는 코드를 개발할 때는 두 가지를 염두에 두어야 합니다.

  • 폴링 메커니즘이 해당 리소스에 미치는 부하에 주의해야 합니다. 다른 사람이 호스팅하는 RSS 피드를 30초마다 폴링하면 문제가 발생할 수 있습니다. 대부분의 RSS 피드는 그렇게 자주 업데이트되지 않기 때문입니다.
  • 폴링 중인 RSS 피드를 사용할 수 없게 되는 불가피한 경우를 처리하기 위한 예외 처리 코드를 포함시켜야 합니다.

SQL 캐시 무효화

웹 개발자에게 데이터 및 출력 캐싱이 모두 제공하는 가장 큰 이점 중 하나는 요청할 때마다 부하가 많이 걸리는 리소스를 반복적으로 처리하고 검색해야 하는 부담을 피할 수 있게 해 주는 것입니다. 물론 데이터베이스와의 정보 교환이 가장 부하가 많이 걸리는 리소스 중 하나이므로 캐싱은 데이터 집합의 메모리 내 복사본 또는 데이터 중심의 캐시된 페이지 버전을 저장하는 데 특히 유용합니다.

문제는 최신 데이터를 필요로 하는 페이지가 있는 경우 발생합니다. ASP.NET v1.1에서는 캐시 기간을 아주 짧게 설정하거나, 부분 페이지 캐싱을 사용하여 페이지에서 변경되지 않을 부분만 캐시하는 방법 외에는 캐시된 데이터가 오래된 데이터가 되지 않도록 하는 방법이 기본적으로 제공되지 않습니다.

이를 해결하기 위해 ASP.NET Whidbey에는 Microsoft SQL Server™ 데이터의 변경 사항을 기준으로 캐시된 항목 또는 출력 캐시된 페이지를 무효화할 수 있는 기능이 추가되었습니다. 이 기능은 매우 간단하게 구현할 수 있으며 SQL Server 7, 2000 및 Yukon에서 지원됩니다.

SQL Server 7/2000

ASP.NET 팀에서 데이터베이스 캐시 무효화 기능을 SQL Server 7 및 2000에 제공할 때 겪은 어려움 중 하나는 두 버전 모두 특정 테이블의 데이터가 변경될 때 원하는 당사자에게 이를 알릴 수 있는 메커니즘이 기본 제공되어 있지 않다는 점이었습니다. 물론 트리거를 사용하는 방법(트리거가 sp_MakeWebTask를 호출하여 SQL Server 웹 길잡이로 하여금 테이블의 내용을 특정 페이지에 쓰도록 한 다음 이 페이지를 파일 캐시 종속성으로 사용하는 방법이 있음)이 있지만, 이 경우 성능 향상은 고사하고 사이트가 차단되어 전혀 작동하지 않게 되는 상황이 발생할 위험도 있습니다.

이전 예제에서 이에 대한 해결 방법의 기초를 이미 살펴보았습니다. ASP.NET 팀에서는 사용자가 지정한 일정에 따라 지정된 데이터베이스를 폴링하여 변경 내용을 찾는 SqlCacheDependency 클래스라는 사용자 지정 캐시 종속성을 만들었습니다. 이 클래스는 변경된 데이터를 찾으면 캐시를 무효화하므로, 새로운 데이터를 얻을 수 있습니다.

폴링 및 비교 메커니즘을 좀 더 단순하게 만들기 위해 SQL 7/2000의 SQL 캐시 무효화 작업에서는 변경 사항을 모니터링할 각 데이터베이스/테이블에 대한 1회 설정이 필요합니다. 이 설정은 aspnet_regsqlcache.exe 명령줄 도구를 통해 수행합니다. 이 기능은 Whidbey 알파 릴리스와는 별도로 제공되지만, 베타 릴리스에는 마법사 및 명령줄 UI를 모두 제공하는 aspnet_regsql.exe라는 또 다른 도구에서 제공될 예정입니다. aspnet_regsqlcache.exe는 Whidbey 릴리스의 기본 프레임워크 디렉터리에 있습니다.

캐시 무효화를 위해 Pubs 샘플 데이터베이스를 사용하려면 다음 명령을 실행하기만 하면 됩니다. 다음 명령에서 -S 매개 변수는 로컬 SQL Server 인스턴스를 지정하는 반면 -E 매개 변수는 유틸리티로 하여금 트러스트된 연결을 사용하여 SQL Server에 연결하도록 합니다. 그러기 위해서는 로그인 계정이 SQL Server 컴퓨터 및 필요한 데이터베이스에 액세스할 수 있어야 합니다.

aspnet_regsqlcache.exe -S (local) -E -d Pubs -ed

작성자 테이블을 사용하려면 다음 명령을 실행하기만 하면 됩니다.

aspnet_regsqlcache.exe -S (local) -E -d Pubs -t Authors -et

다음 명령을 실행할 수도 있습니다.

aspnet_regsqlcache.exe -?

그러면 사용할 수 있는 모든 명령줄 매개 변수가 표시됩니다.

데이터베이스를 설정한 후에는 연결 정보 및 폴링 빈도를 지정해야 합니다. 아래와 같이 web.config에 있는 <cache>라는 새로운 구성 요소를 사용하면 됩니다. 여기에서는 역시 ASP.NET Whidbey에 새로 추가된 connectionStrings 섹션을 사용하여 응용 프로그램에 사용되는 연결 문자열을 저장합니다. 베타 릴리스에는 ID 및 암호 정보의 암호화를 지원하는 기능도 추가될 예정입니다.

<?xml version="1.0" encoding="UTF-8" ?><configuration> <connectionStrings> <add name="PubsConnYukon" connectionString="User ID=<id>;pwd=<password>;Initial
Catalog=pubs;Data Source=192.168.0.108" /> <add name="PubsConn" connectionString="Initial Catalog=pubs;Data
Source=(local);Trusted_Connection=true" /> </connectionStrings> <system.web> <cache> <sqlCacheDependency enabled="true" pollTime="30000"> <databases> <add name="Pubs" connectionStringName="PubsConn" /> </databases> </sqlCacheDependency> </cache> </system.web></configuration>

위 예제에서는 <sqlCacheDependency> 요소를 사용하여 데이터베이스 캐시 종속성(응용 프로그램의 전역 설정)을 사용하도록 설정하고 기본 폴링 시간을 30000밀리초(30초)로 설정했습니다. <databases> 요소에는 이 응용 프로그램에 대해 설정된 모든 데이터베이스가 포함되며, 이 경우에는 connectionStringName 특성을 사용하여 Pubs 데이터베이스를 추가하는 <add> 요소만 하나 있습니다. connectionString 특성을 사용하여 연결 문자열을 <add> 요소에 직접 지정할 수도 있습니다. name 특성은 나중에 이 종속성을 참조하는 데 사용되는 이름을 지정합니다.

<cache> 요소를 구성한 후에는 구성된 종속성을 사용하는 것이 매우 간단합니다. 종속성은 세 가지 방법 중 하나로 구성할 수 있습니다. 즉, 출력 캐시 API 또는 캐시 API를 통해 구성하거나 선언적으로 구성할 수 있습니다.

다음 코드는 출력 캐시 API를 통해 종속성을 사용하여 Authors 테이블에서 데이터가 변경될 때 페이지의 출력 캐시를 무효화하는 방법을 보여 줍니다.

참고   이 코드에서는 System.Web.Caching 네임스페이스를 가져왔다고 가정합니다.
Dim sqlDependency As New SqlCacheDependency("Pubs", "Authors")Response.AddCacheDependency(sqlDependency)Response.Cache.SetValidUntilExpires(True)Response.Cache.SetExpires(DateTime.Now.AddMinutes(60))Response.Cache.SetCacheability(HttpCacheability.Public)

이 코드는 일반적으로 Page_Load 이벤트 처리기에 있습니다.

구성된 종속성을 Cache 클래스와 함께 사용하려면 이제 어느 정도 친숙한 다음 코드를 사용해야 합니다. 이 코드는 Pubs Authors 테이블에서 데이터 집합을 검색하여 구성된 SqlCacheDependency와 함께 캐시하거나, 데이터가 변경되지 않은 경우 단순히 캐시된 데이터 집합을 반환합니다.

Dim Key As String = "Authors"If (Cache(Key) Is Nothing) Then Label1.Text = "캐시에 없음…" Dim connection As New _ SqlConnection(ConfigurationSettings.ConnectionStrings("Pubs")) Dim adapter As New _ SqlDataAdapter("SELECT * FROM Authors", connection) Dim DS As New DataSet adapter.Fill(dataSet) Cache.Insert(Key, DS, New SqlCacheDependency("Pubs", "Products"))Else Label1.Text = "캐시에 있음…"End IfReturn Cache(Key)

마지막으로 간단하게 선언적인 @ OutputCache 지시문을 사용하여 전체 페이지 출력을 캐시하고, 새 sqlDependency 특성을 사용하여 DatabaseAlias:TableName 형식으로 종속성을 지정할 수도 있습니다. 여기에서 DatabaseAlias는 web.config에서 설정한 <add> 요소의 name 특성이 지정하는 이름이고, TableName은 변경 사항을 폴링할 테이블의 이름입니다. 다음 코드는 Authors 테이블에서 데이터를 추출하여 GridView 컨트롤에 바인딩한 다음 앞에서 구성한 종속성에 따라 페이지 출력을 캐시하는 페이지를 보여 줍니다.

<%@ page language="VB" %><%@ outputcache duration="9999" varybyparam="none" sqldependency="Pubs:Authors" %><script runat="server"> Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Sqldatasource1.ConnectionString = _ ConfigurationSettings.ConnectionStrings("PubsConn") CacheStatus.Text = "이 페이지를 마지막으로 렌더링한 시간: " & _ DateTime.Now.ToLongTimeString End Sub</script><html><head runat="server"> <title>DB Cache invalidation on SQL Server 2000</title></head><body> <form runat="server"> <h2><asp:label id="CacheStatus" runat="Server"/></h2> <asp:gridview id="Gridview1" datasourceid="Sqldatasource1" bordercolor="#CC9966" borderstyle="None" borderwidth="1px" backcolor="White" cellpadding="4" runat="server"> <alternatingrowstyle font-italic="False" font-bold="False"> </alternatingrowstyle> <rowstyle forecolor="#330099" backcolor="White" font-italic="False" font-bold="False"> </rowstyle> <headerstyle forecolor="#FFFFCC" backcolor="#990000" font-italic="False" font-bold="True"> </headerstyle> <footerstyle forecolor="#330099" backcolor="#FFFFCC" font-italic="False" font-bold="False"> </footerstyle> </asp:gridview> <asp:sqldatasource id="Sqldatasource1" runat="server" selectcommand="SELECT * FROM authors" providername="System.Data.SqlClient" > </asp:sqldatasource> </form></body></html>

위 페이지(이 기사의 샘플 코드에도 있음)는 Authors 테이블의 데이터가 변경될 때까지 해당 출력을 캐시합니다. 데이터가 변경되면 업데이트된 데이터를 검색하고 페이지 출력을 다시 한 번 캐시합니다.

SQL Server "Yukon"

SQL Server "Yukon"에서는 새로운 알림 인프라 덕분에 데이터베이스 캐시 무효화를 더욱 쉽게 사용할 수 있습니다. Yukon 데이터로 작업하는 경우 SqlCacheDependency 클래스의 생성자 오버로드를 사용하여 데이터를 검색하는 데 사용된 SqlCommand 인스턴스를 기준으로 종속성을 생성할 수 있습니다. 그러면 백그라운드에서 Yukon 데이터베이스 서버의 알림 메시지를 받기 위한 수신기 개체가 만들어지고 응용 프로그램이 데이터 변경 알림의 대상으로 등록됩니다. 데이터 변경 알림을 받으면 종속성과 관련이 있는 캐시된 항목이 제거됩니다.

Yukon에서 제공하는 알림 서비스의 이점은 특수 테이블, 트리거 또는 저장 프로시저를 사용하여 데이터베이스를 설정할 필요가 없다는 것입니다. Yukon에서는 폴링이 필요하지 않으므로 web.config에서 특수 설정을 수행할 필요도 없습니다. Yukon에서 데이터베이스 캐시 무효화의 이점을 활용하려면 데이터를 검색하는 데 사용된 SqlCommandSqlCacheDependency 클래스의 인스턴스에 연결하는 코드 한 줄만 추가하면 됩니다.

Dim SqlDep As New SqlCacheDependency(SqlCmd)

그리고 SQL Server 7 또는 2000에서와 마찬가지로 종속성을 사용하면 됩니다. 따라서 출력 캐시를 사용하는 페이지를 캐시하는 코드는 다음과 같습니다. 이 코드는 이 기사의 샘플 파일에 있는 SqlCacheInvalidation_Yukon.aspx 페이지에도 사용됩니다.

Dim SqlConn As New _SqlConnection(ConfigurationSettings.ConnectionStrings("PubsConnYukon"))Dim SqlCmd As New SqlCommand("SELECT * FROM Authors", SqlConn)Dim SqlDep As New SqlCacheDependency(SqlCmd) SqlConn.Open()Gridview1.DataSource = SqlCmd.ExecuteReader()Gridview1.DataBind()SqlConn.Close()Response.AddCacheDependency(sqlDependency)Response.Cache.SetValidUntilExpires(True)Response.Cache.SetExpires(DateTime.Now.AddMinutes(60))Response.Cache.SetCacheability(HttpCacheability.Public)

Cache 클래스를 사용하는 데이터 집합을 캐싱하는 코드는 약간 다르지만 기본 개념은 동일합니다. 명령과 SqlCacheDependency를 연결하는 코드는 굵게 표시되어 있습니다.

Dim Key As String = "Authors"If (Cache(Key) Is Nothing) Then Label1.Text = "캐시에 없음…" Dim connection As New _ SqlConnection(ConfigurationSettings.ConnectionStrings("Pubs")) Dim adapter As New _ SqlDataAdapter("SELECT * FROM Authors", connection) Dim DS As New DataSet adapter.Fill(dataSet) Cache.Insert(Key, DS, New SqlCacheDependency(adapter.SelectCommand))Else Label1.Text = "캐시에 있음…"End IfReturn Cache(Key)

이것이 전부입니다. 물론 주의해야 할 점이 두 가지 있습니다. 첫째, Yukon 데이터베이스의 데이터에 액세스하는 데 사용되는 계정에는 대상 데이터베이스에 쿼리 알림 구독을 요청할 수 있는 충분한 권한이 있어야 합니다. 둘째, Yukon의 PDC 빌드에는 버그가 있기 때문에 데이터 변경에 대한 알림을 받으려면 sa 계정을 사용하여 데이터베이스에 로그인해야 합니다.

중요   특수한 경우 가 아니면 어떤 응용 프로그램에서도 sa 계정을 사용하여 데이터에 액세스하거나 데이터를 수정해서는 안 됩니다. 따라서 Yukon의 PDC 빌드에서 SQL 캐시 무효화를 사용하려면 인터넷(웹 서버 및 데이터베이스 서버 포함)에 액세스할 수 없는 응용 프로그램만을 사용해야 합니다.

사후 캐시 대체

마지막으로 다룰 새 기능은 다른 기능이 처리하지 못한 부분을 처리하는 사후 캐시 대체입니다. 많은 응용 프로그램에서, 캐싱에 별로 적합하지 않은 아주 작은 동적 코드에 출력 캐싱을 사용하고자 하는 경우가 종종 있습니다. 사후 캐시 대체를 사용하면 자리 표시자 역할을 하는 컨트롤을 페이지에 추가하여 런타임에 문자열 출력을 반환하는 지정된 메서드를 호출하도록 할 수 있습니다. 이러한 방법으로 캐시를 원하는 대로 사용할 수 있습니다.

또한 사후 캐시 대체는 새로운 기능 중에서 가장 사용하기 쉽습니다. 다음의 두 가지 방법 중 하나로 대체 위치를 선택합니다.

  • Response.WriteSubstitution 메서드를 호출하고 이 메서드에 원하는 대체 메서드 콜백에 대한 참조를 전달합니다.
  • 원하는 위치의 페이지에 <asp:substitution> 컨트롤을 추가하고 methodname 특성을 콜백 메서드의 이름으로 설정합니다.

두 번째 방법의 이점은 문자열 출력이 렌더링되는 위치를 좀 더 정확하게 지정할 수 있다는 것입니다. 두 방법 모두에서 기간, 위치 등을 지정하는 페이지에 @ OutputCache 지시문을 추가하여 페이지 출력을 캐시할 수도 있습니다. 사후 캐시 대체의 또 다른 이점은 이 기능을 활용하는 사용자 지정 컨트롤을 작성할 수 있다는 것입니다. 예를 들어 ASP.NET 팀에서는 AdRotator 컨트롤이 사후 캐시 대체를 인식하도록 다시 작성했습니다. 즉, AdRotator 컨트롤을 페이지에 추가하고 해당 페이지를 출력 캐시하면 AdRotator 컨트롤이 사후 캐시 대체를 자동으로 활용하여 지정된 광고를 올바르게 표시하므로 사용자는 광고를 표시하기 위해 별다른 작업을 하지 않아도 됩니다.

다음 코드는 사후 캐시 대체를 사용하는 페이지의 예를 보여 줍니다. 이 페이지는 Pubs 데이터베이스의 작성자 테이블에서 데이터를 검색하여 GridView에 바인딩하고 AdRotator 컨트롤로부터 광고를 표시합니다. 이 페이지의 Page_Load에는 페이지를 만든 시간이 기록되는 레이블이 들어 있습니다. 또한 <asp:substitution> 컨트롤이 페이지에 추가되어 있고 methodname 특성이 Substitute로 설정되어 있습니다. Substitute는 원하는 문자열(이 경우 단순히 현재 시간이 포함된 문자열)의 출력을 반환하는 메서드의 이름입니다.

<%@ page language="VB" %><%@ outputcache duration="30" varybyparam="none" %><script runat="server" language="vb"> Shared Function Substitute(ByVal MyContext As HttpContext) As String Dim str As String = "캐시된 페이지에 콘텐츠를 추가했습니다!<br/>" str &= "현재 시간: " & DateTime.Now.ToLongTimeString Return str End Function Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) SqlDataSource1.ConnectionString = _ ConfigurationSettings.ConnectionStrings("PubsConn") Label1.Text = "페이지 작성 시간: " + DateTime.Now.ToLongTimeString End Sub</script><html><head id="Head1" runat="server"> <title>Untitled Page</title></head><body> <form id="Form1" runat="server"> <h1> <asp:label id="Label1" runat="server">Label</asp:label> </h1> <br /> <br /> <asp:adrotator id="AdRotator1" width="468px" height="60px" advertisementfile="ads_tall.xml" runat="server" /> <asp:substitution id="Substitution1" methodname="Substitute" runat="server" /> <br /> <br /> <asp:gridview id="GridView1" autogeneratecolumns="False" datasourceid="SqlDataSource1" datakeynames="au_id" runat="server" > <columnfields> <asp:boundfield sortexpression="au_id" datafield="au_id" readonly="True" headertext="만든 이 ID"> </asp:boundfield> <asp:boundfield sortexpression="au_fname" datafield="au_fname" headertext="이름"> </asp:boundfield> <asp:boundfield sortexpression="au_lname" datafield="au_lname" headertext="성"> </asp:boundfield> </columnfields> <rowstyle forecolor="#000066"> </rowstyle> <headerstyle forecolor="White" backcolor="#006699" font-bold="True"> </headerstyle> </asp:gridview> <asp:sqldatasource id="SqlDataSource1" providername="System.Data.SqlClient" selectcommand="SELECT [au_id], [au_fname], [au_lname] FROM dbo.[authors]" runat="server" > </asp:sqldatasource> </form></body></html>

처음 호출될 때 이 페이지는 그림 5에 표시된 출력을 반환합니다. "page created" 레이블과 대체 메서드에 의한 시간 출력이 일치하는 것을 볼 수 있습니다.

그림 5. PostCache.aspx의 초기 출력

페이지를 새로 고치면 출력이 캐시됨에도 불구하고 그림 6과 같이 AdRotator 출력과 Substitute 메서드의 출력은 모두 업데이트되는 반면 페이지의 나머지 부분은 캐시된 상태로 남아 있습니다.

그림 6. 사후 캐시 대체를 통해 캐시된 출력

이 기사의 샘플 파일에는 이 기술을 사용하려는 사용자를 위해 Response.WriteSubstitution을 사용하는 PostCache_RWS.aspx라는 페이지도 포함되어 있습니다.

결론

몇 가지 간단한 기능을 추가함으로써 ASP.NET 팀은 ASP.NET의 유용한 부분을 더욱 훌륭하게 만들었습니다. 다음과 같은 기능이 추가되었습니다.

  • 데이터 중심 페이지 출력 또는 데이터 집합을 캐시하고 데이터가 수정될 때 캐시를 자동으로 삭제할 수 있는 기능
  • 사용자 지정 캐시 종속성을 만들 수 있는 기능
  • 사후 캐시 대체를 통해 런타임에 텍스트 조각을 캐시된 페이지에 동적으로 추가할 수 있는 기능

이러한 기능 때문에 ASP.NET 응용 프로그램 개발자는 더 이상 캐싱을 사용하지 않을 이유가 없습니다. 이로 인해 차세대 ASP.NET 응용 프로그램의 성능은 매우 발전할 것입니다.

관련 서적


저자 소개

G. Andrew Duthie는 Graymad Enterprises, Inc.  설립자이자 대표이며 Microsoft Web 개발 기술에 대한 교육과 컨설팅을 맡고 있습니다. Andrew는 Active Server Pages가 도입된 이후 다중 계층 웹 응용 프로그램을 개발해 오고 있습니다. 또한 Microsoft ASP.NET Programming with Microsoft Visual Basic(영문), Microsoft ASP.NET Programming with Microsoft Visual C#(영문) 및 ASP.NET in a Nutshell(2판)(영문)을 비롯한 다수의 ASP.NET 관련 서적을 저술했습니다. Andrew는 Software Development, Dev-Connections family of conferences, Microsoft Developer Days 및 VSLive 이벤트에서 자주 강연을 하며 .NET 사용자 그룹에서도 INETA(International .NET Association)  강연자 협회 회원으로 강연을 맡고 있습니다. Andrew에 대한 자세한 내용을 알아보려면 회사 웹 사이트인 http://www.graymad.com/ 을 방문하십시오.

작성자 메모

이 기사를 작성하는 것은 물론 이 기사의 예제에 사용된 코드 샘플의 초안을 만들어 주신 ASP.NET 팀의 캐싱 분야 상근 전문가인 Rob Howard에게 감사를 드립니다.



화면 맨 위로화면 맨 위로


최종 수정일: 2004년 8월 3일
Posted by tornado
|