다음을 통해 공유


출력 캐싱을 통한 성능 향상(C#)

작성자: Microsoft

이 자습서에서는 출력 캐싱을 활용하여 ASP.NET MVC 웹 애플리케이션의 성능을 크게 향상시킬 수 있는 방법을 알아봅니다. 새 사용자가 작업을 호출할 때마다 동일한 콘텐츠를 만들 필요가 없도록 컨트롤러 작업에서 반환된 결과를 캐시하는 방법을 알아봅니다.

이 자습서의 목표는 출력 캐시를 활용하여 ASP.NET MVC 애플리케이션의 성능을 크게 향상시킬 수 있는 방법을 설명하는 것입니다. 출력 캐시를 사용하면 컨트롤러 작업에서 반환된 콘텐츠를 캐시할 수 있습니다. 이렇게 하면 동일한 컨트롤러 작업이 호출될 때마다 동일한 콘텐츠를 생성할 필요가 없습니다.

예를 들어 ASP.NET MVC 애플리케이션이 Index라는 뷰에 데이터베이스 레코드 목록을 표시한다고 상상해 보십시오. 일반적으로 사용자가 인덱스 뷰를 반환하는 컨트롤러 작업을 호출할 때마다 데이터베이스 쿼리를 실행하여 데이터베이스 레코드 집합을 검색해야 합니다.

반면에 출력 캐시를 활용하는 경우 사용자가 동일한 컨트롤러 작업을 호출할 때마다 데이터베이스 쿼리를 실행하지 않도록 할 수 있습니다. 컨트롤러 작업에서 다시 생성되는 대신 캐시에서 뷰를 검색할 수 있습니다. 캐싱을 사용하면 서버에서 중복 작업을 수행하지 않도록 할 수 있습니다.

출력 캐싱 사용

개별 컨트롤러 작업 또는 전체 컨트롤러 클래스에 [OutputCache] 특성을 추가하여 출력 캐싱을 사용하도록 설정합니다. 예를 들어 목록 1의 컨트롤러는 Index()라는 작업을 노출합니다. Index() 작업의 출력은 10초 동안 캐시됩니다.

목록 1 – Controllers\HomeController.cs

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        [OutputCache(Duration=10, VaryByParam="none")]
        public ActionResult Index()
        {
            return View();
        }
    }
}

ASP.NET MVC의 베타 버전에서는 출력 캐싱이 와 같은 http://www.MySite.com/URL에 대해 작동하지 않습니다. 대신 와 같은 http://www.MySite.com/Home/IndexURL을 입력해야 합니다.

목록 1에서 Index() 작업의 출력은 10초 동안 캐시됩니다. 원하는 경우 캐시 기간을 훨씬 더 길게 지정할 수 있습니다. 예를 들어 컨트롤러 작업의 출력을 하루 동안 캐시하려는 경우 캐시 기간을 86400초(60초 * 60분 * 24시간)로 지정할 수 있습니다.

지정한 시간 동안 콘텐츠가 캐시된다는 보장은 없습니다. 메모리 리소스가 부족해지면 캐시가 콘텐츠를 자동으로 제거하기 시작합니다.

목록 1의 홈 컨트롤러는 목록 2의 인덱스 보기를 반환합니다. 이 보기에는 특별한 것이 없습니다. 인덱스 보기는 단순히 현재 시간을 표시합니다(그림 1 참조).

목록 2 – Views\Home\Index.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Index</title>
</head>
<body>
    <div>
    
    The current time is: <%= DateTime.Now.ToString("T") %>
    
    
    </div>
</body>
</html>

그림 1 – 캐시된 인덱스 보기

clip_image002

브라우저의 주소 표시줄에 URL /Home/Index를 입력하고 브라우저에서 새로 고침/다시 로드 단추를 반복해서 눌러 Index() 작업을 여러 번 호출하는 경우 인덱스 보기에 표시되는 시간은 10초 동안 변경되지 않습니다. 보기가 캐시되기 때문에 동일한 시간이 표시됩니다.

애플리케이션을 방문하는 모든 사용자에게 동일한 보기가 캐시된다는 것을 이해하는 것이 중요합니다. Index() 작업을 호출하는 모든 사용자는 동일한 캐시된 버전의 인덱스 뷰를 받게 됩니다. 즉, 인덱스 보기를 제공하기 위해 웹 서버에서 수행해야 하는 작업의 양이 크게 줄어듭니다.

목록 2의 보기는 정말 간단한 작업을 수행합니다. 보기는 현재 시간만 표시합니다. 그러나 데이터베이스 레코드 집합을 표시하는 보기를 쉽게 캐시할 수 있습니다. 이 경우 뷰를 반환하는 컨트롤러 작업이 호출될 때마다 데이터베이스 레코드 집합을 데이터베이스에서 검색할 필요가 없습니다. 캐싱은 웹 서버와 데이터베이스 서버에서 수행해야 하는 작업의 양을 줄일 수 있습니다.

MVC 뷰에서 %@ OutputCache %> 지시문 페이지를 <사용하지 마세요. 이 지시문은 Web Forms 세계에서 흘러나와 ASP.NET MVC 애플리케이션에서 사용하면 안 됩니다.

콘텐츠가 캐시되는 위치

기본적으로 [OutputCache] 특성을 사용하는 경우 콘텐츠는 웹 서버, 프록시 서버 및 웹 브라우저의 세 위치에 캐시됩니다. [OutputCache] 특성의 Location 속성을 수정하여 콘텐츠가 캐시되는 위치를 정확하게 제어할 수 있습니다.

Location 속성을 다음 값 중 하나로 설정할 수 있습니다.

· 모든

· 클라이언트

· 다운스트림

· 서버

· 없음

· ServerAndClient

기본적으로 Location 속성의 값은 Any입니다. 그러나 브라우저 또는 서버에서만 캐시할 수 있는 상황이 있습니다. 예를 들어 각 사용자에 대해 개인 설정된 정보를 캐싱하는 경우 서버의 정보를 캐시해서는 안 됩니다. 다른 사용자에게 다른 정보를 표시하는 경우 클라이언트에서만 정보를 캐시해야 합니다.

예를 들어 목록 3의 컨트롤러는 현재 사용자 이름을 반환하는 GetName()이라는 작업을 노출합니다. Jack이 웹 사이트에 로그인하고 GetName() 작업을 호출하는 경우 작업은 문자열 "Hi Jack"을 반환합니다. 이후에 Jill이 웹 사이트에 로그인하고 GetName() 작업을 호출하면 "Hi Jack" 문자열도 가져옵니다. 이 문자열은 Jack이 처음에 컨트롤러 작업을 호출한 후 모든 사용자에 대해 웹 서버에 캐시됩니다.

목록 3 – Controllers\BadUserController.cs

using System.Web.Mvc;
using System.Web.UI;

namespace MvcApplication1.Controllers
{
    public class BadUserController : Controller
    {
        [OutputCache(Duration = 3600, VaryByParam = "none")]
        public string GetName()
        {
            return "Hi " + User.Identity.Name;
        }
    }
}

목록 3의 컨트롤러가 원하는 방식으로 작동하지 않을 수 있습니다. Jill에게 "안녕 잭" 메시지를 표시하고 싶지 않습니다.

서버 캐시에 개인 설정된 콘텐츠를 캐시해서는 안 됩니다. 그러나 성능을 향상시키기 위해 브라우저 캐시에 개인 설정된 콘텐츠를 캐시할 수 있습니다. 브라우저에서 콘텐츠를 캐시하고 사용자가 동일한 컨트롤러 작업을 여러 번 호출하는 경우 서버 대신 브라우저 캐시에서 콘텐츠를 검색할 수 있습니다.

목록 4의 수정된 컨트롤러는 GetName() 작업의 출력을 캐시합니다. 그러나 콘텐츠는 서버가 아닌 브라우저에서만 캐시됩니다. 이렇게 하면 여러 사용자가 GetName() 메서드를 호출할 때 각 사용자는 다른 사람의 사용자 이름이 아니라 자신의 사용자 이름을 가져옵니다.

목록 4 – Controllers\UserController.cs

using System.Web.Mvc;
using System.Web.UI;

namespace MvcApplication1.Controllers
{
    public class UserController : Controller
    {
        [OutputCache(Duration=3600, VaryByParam="none", Location=OutputCacheLocation.Client, NoStore=true)]
        public string GetName()
        {
            return "Hi " + User.Identity.Name;
        }
    }
}

목록 4의 [OutputCache] 특성에는 OutputCacheLocation.Client 값으로 설정된 Location 속성이 포함되어 있습니다. [OutputCache] 특성에는 NoStore 속성도 포함됩니다. NoStore 속성은 프록시 서버와 브라우저에 캐시된 콘텐츠의 영구 복사본을 저장해서는 안 됨을 알리는 데 사용됩니다.

출력 캐시 변경

경우에 따라 동일한 콘텐츠의 다른 캐시된 버전을 원할 수 있습니다. 예를 들어 master/세부 정보 페이지를 만들고 있다고 상상해 보십시오. master 페이지에는 영화 제목 목록이 표시됩니다. 제목을 클릭하면 선택한 동영상에 대한 세부 정보가 표시됩니다.

세부 정보 페이지를 캐시하면 클릭한 동영상에 관계없이 동일한 동영상에 대한 세부 정보가 표시됩니다. 첫 번째 사용자가 선택한 첫 번째 동영상이 모든 이후 사용자에게 표시됩니다.

[OutputCache] 특성의 VaryByParam 속성을 활용하여 이 문제를 해결할 수 있습니다. 이 속성을 사용하면 양식 매개 변수 또는 쿼리 문자열 매개 변수가 다를 때 동일한 콘텐츠의 캐시된 다른 버전을 만들 수 있습니다.

예를 들어 목록 5의 컨트롤러는 Master() 및 Details()라는 두 개의 작업을 노출합니다. Master() 작업은 영화 제목 목록을 반환하고 Details() 작업은 선택한 동영상에 대한 세부 정보를 반환합니다.

목록 5 – Controllers\MoviesController.cs

using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;

namespace MvcApplication1.Controllers
{
    public class MoviesController : Controller
    {
        private MovieDataContext _dataContext;

        public MoviesController()
        {
            _dataContext = new MovieDataContext();
        }

        [OutputCache(Duration=int.MaxValue, VaryByParam="none")]
        public ActionResult Master()
        {
            ViewData.Model = (from m in _dataContext.Movies 
                              select m).ToList();
            return View();
        }

        [OutputCache(Duration = int.MaxValue, VaryByParam = "id")]
        public ActionResult Details(int id)
        {
            ViewData.Model = _dataContext.Movies.SingleOrDefault(m => m.Id == id);
            return View();
        }


    }
}

Master() 작업에는 값이 "none"인 VaryByParam 속성이 포함됩니다. Master() 작업이 호출되면 동일한 캐시된 버전의 마스터 뷰가 반환됩니다. 양식 매개 변수 또는 쿼리 문자열 매개 변수는 무시됩니다(그림 2 참조).

그림 2 - /Movies/Master 보기

clip_image004

그림 3 - /Movies/Details 보기

clip_image006

Details() 작업에는 값이 "Id"인 VaryByParam 속성이 포함됩니다. Id 매개 변수의 다른 값이 컨트롤러 작업에 전달되면 다른 캐시된 버전의 세부 정보 보기가 생성됩니다.

VaryByParam 속성을 사용하면 캐싱이 더 많고 더 적지 않다는 것을 이해하는 것이 중요합니다. Id 매개 변수의 각 버전에 대해 다른 캐시된 버전의 세부 정보 보기가 만들어집니다.

VaryByParam 속성을 다음 값으로 설정할 수 있습니다.

* = 폼 또는 쿼리 문자열 매개 변수가 다를 때마다 캐시된 다른 버전을 만듭니다.

none = 다른 캐시된 버전을 만들지 않음

매개 변수의 세미콜론 목록 = 목록의 폼 또는 쿼리 문자열 매개 변수가 다를 때마다 다른 캐시된 버전 만들기

캐시 프로필 만들기

[OutputCache] 특성의 속성을 수정하여 출력 캐시 속성을 구성하는 대신 웹 구성(web.config) 파일에 캐시 프로필을 만들 수 있습니다. 웹 구성 파일에서 캐시 프로필을 만들면 몇 가지 중요한 이점이 있습니다.

먼저 웹 구성 파일에서 출력 캐싱을 구성하여 컨트롤러 작업이 하나의 중앙 위치에서 콘텐츠를 캐시하는 방법을 제어할 수 있습니다. 하나의 캐시 프로필을 만들고 여러 컨트롤러 또는 컨트롤러 작업에 프로필을 적용할 수 있습니다.

둘째, 애플리케이션을 다시 컴파일하지 않고 웹 구성 파일을 수정할 수 있습니다. 프로덕션에 이미 배포된 애플리케이션에 대해 캐싱을 사용하지 않도록 설정해야 하는 경우 웹 구성 파일에 정의된 캐시 프로필을 수정하기만 하면 됩니다. 웹 구성 파일의 변경 내용은 자동으로 검색되고 적용됩니다.

예를 들어 목록 6의 <캐싱> 웹 구성 섹션은 Cache1Hour라는 캐시 프로필을 정의합니다. 캐싱> 섹션은 <웹 구성 파일의 <system.web> 섹션 내에 나타나야 합니다.

목록 6 – web.config대한 캐싱 섹션

<caching>
<outputCacheSettings>
    <outputCacheProfiles>
        <add name="Cache1Hour" duration="3600" varyByParam="none"/>
    </outputCacheProfiles>
</outputCacheSettings>
</caching>

목록 7의 컨트롤러는 [OutputCache] 특성을 사용하여 컨트롤러 작업에 Cache1Hour 프로필을 적용하는 방법을 보여 줍니다.

목록 7 – Controllers\ProfileController.cs

using System;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    public class ProfileController : Controller
    {
        [OutputCache(CacheProfile="Cache1Hour")]
        public string Index()
        {
            return DateTime.Now.ToString("T");
        }
    }
}

목록 7에서 컨트롤러가 노출하는 Index() 작업을 호출하면 동일한 시간이 1시간 동안 반환됩니다.

요약

출력 캐싱은 ASP.NET MVC 애플리케이션의 성능을 크게 향상시키는 매우 쉬운 방법을 제공합니다. 이 자습서에서는 [OutputCache] 특성을 사용하여 컨트롤러 작업의 출력을 캐시하는 방법을 알아보았습니다. Duration 및 VaryByParam 속성과 같은 [OutputCache] 특성의 속성을 수정하여 콘텐츠가 캐시되는 방법을 수정하는 방법도 알아보았습니다. 마지막으로 웹 구성 파일에서 캐시 프로필을 정의하는 방법을 알아보았습니다.