11 ASP.NET 상태 관리
ASP.NET에서는 응용 프로그램의 상태를 관리하기 위한 여러 가지 기법을 제공합니다. 세션, 쿠키 등 ASP.NET에 유용하게 사용할 만한 개체들의 기능을 구분해서 공부해보겠습니다.
11.1 ASP.NET 상태 관리 기법 소개 ASP.NET의 상태 관리(State Management)는 응용 프로그램을 제작할 때 필요한 데이터를 임시 또는 반영구적으로 서버 또는 클라이언트에 보관하고 사용하는 방법을 말합니다. ASP.NET에서 제공하는 상태 관리 기능은 두 가지로 클라이언트 측과 서버 측 기능으로 나뉜다. 클라이언트 측 상태 관리 기법
- Hidden Field
- View State
- Cookies
- Control State
- Query Strings
서버 측 상태 관리 기법
- Session
- Application
응용 프로그램에서 사용되는 대부분의 데이터는 데이터베이스에 저장되어 관리됩니다. 이번 절에서는 데이터베이스를 사용하지 않고 간단하게 데이터를 저장하는 기능을 정리해봅니다. 클래스명 설명 HttpCookie (Cookies) 클라이언트 측 웹브라우저에 간단한 데이터를 보관하여 서버 성능을 향상시킬 수 있는 쿠키 개체를 생성합니다. Cache 서버 측 메모리에 데이터를 보관하여 응답속도를 향상시킬 수 있는 캐시 개체를 생성합니다. ViewState 해당 웹 페이지 내에서만 어떤 정보를 임시로 보관하고자 할 때 가장 최적화된 방법을 제공합니다. Application 전체 웹 사이트 내에서 공유되어야 할 항목을 저장하는 데 사용됩니다. Session 전체 웹 사이트 내에서 현재 사용자에게만 제공되는 항목을 저장하는데 사용됩니다. QueryString 현재 페이지에서 다음 페이지로 간단한 정보를 전달할 때 사용합니다.
11.1.1 ASP.NET에서 쿠키 저장 및 읽기 초간단 샘플 코드
public ActionResult AddCookie()
{
HttpCookie cookie = new HttpCookie("CookieDemo");
cookie["Username"] = "Test1";
cookie["Password"] = "Test2";
Response.Cookies.Add(cookie);
return Content("쿠키 저장 완료");
}
public ActionResult GetCookie()
{
HttpCookie cookie = Request.Cookies["CookieDemo"];
string results = cookie["Username"] + " " + cookie["Password"];
return Content("쿠키 읽기 완료: " + results);
}
11.2 [실습] ASP.NET 상태 관리를 위한 여러 가지 클래스 사용 11.2.1 소개 ASP.NET에서 주로 사용되는 임시 데이터 저장 관련 개체들을 비교해보며 해당 개체를 웹 폼에 적절하게 사용할 수 있는 아이디어를 얻는다.
<참고> 이번 실습 과정을 "ASP.NET 상태 관리와 관련된 개체 5가지 살펴보기"라는 제목의 동영상 강좌로도 마련하였으니 참고하기 바랍니다. https://youtu.be/M6LVqxheHT8 </참고>
11.2.2 따라하기 (1) Visual Studio를 실행하고 ASP.NET 웹 응용 프로그램을 다음과 같이 생성합니다. 이름 위치 ASP.NET 템플릿 선택 폴더 및 핵심 참조 추가 DevStateManagement C:\ASP.NET ASP.NET 4.6 템플릿-Empty Web Forms
(2) 솔루션 탐색기에서 DevStateManagement 웹 프로젝트에 마우스 오른쪽 버튼을 클릭하여 <추가 > 새 항목> 메뉴를 클릭한 후 다음과 같이 웹 프로젝트에 새로운 항목을 추가합니다. 템플릿 이름 이름 웹 폼 FrmStateManagement.aspx 웹 폼 FrmStateShow.aspx 다음 그림은 사용하지 않는 기본 제공 폴더를 삭제한 프로젝트 모습입니다. 그림 11 1 생성된 DevStateManagement 프로젝트
(3) Global.asax 파일을 열고 다음과 같이 코드를 입력합니다. 주석 처리 부분은 제외합니다.
using System;
namespace DevStateManagement
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
// 응용 프로그램이 시작될 때 실행되는 코드입니다.
Application["Now"] = DateTime.Now;
}
protected void Session_Start(object sender, EventArgs e)
{
// 새 세션이 시작할 때 실행되는 코드입니다.
Session["Now"] = DateTime.Now;
}
protected void Session_End(object sender, EventArgs e)
{
// 새 세션이 끝날 때 실행되는 코드입니다.
// 참고: Web.config 파일에서
// sessionstate 모드가 InProc로 설정되어 있는 경우에만
// Session_End 이벤트가 발생합니다.
// 세션 모드가 StateServer 또는 SQLServer로
// 설정되어 있는 경우에는 이 이벤트가 발생하지 않습니다.
}
protected void Application_End(object sender, EventArgs e)
{
// 응용 프로그램이 종료될 때 실행되는 코드입니다.
}
protected void Application_Error(object sender, EventArgs e)
{
// 처리되지 않은 오류가 발생할 때 실행되는 코드입니다.
}
}
}
(4) FrmStateManagement.aspx 페이지를 열고 다음과 같이 코드를 작성하여 폼을 구성합니다. 참고로, 직접 코드를 작성해야하는 부분이 아닌 웹 폼 페이지 생성시 기본 제공되는 소스의 줄바꿈 및 들여쓰기는 약간 조정된 상태로 소스가 제공됩니다.
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="FrmStateManagement.aspx.cs"
Inherits="DevStateManagement.FrmStateManagement" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>상태 관리</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h3>서버에 저장</h3>
Application 개체: <asp:TextBox ID="txtApplication" runat="server" /><br />
Session 개체: <asp:TextBox ID="txtSession" runat="server" /><br />
Cache 개체: <asp:TextBox ID="txtCache" runat="server" /><br />
<h3>클라이언트에 저장</h3>
Cookies 개체: <asp:TextBox ID="txtCookies" runat="server" /><br />
ViewState 개체: <asp:TextBox ID="txtViewState" runat="server" />
<hr />
<asp:LinkButton ID="btnSave" runat="server"
OnClick="btnSave_Click">상태 변수에 데이터 저장</asp:LinkButton>
<asp:LinkButton ID="btnPostBack" runat="server">다시 게시</asp:LinkButton>
</div>
</form>
</body>
</html>
현재 웹 폼의 <디자인 보기>로 이동하면 다음과 같은 형태로 폼을 확인할 수 있습니다.
그림 11 2 상태 관리 테스트를 위한 메인 페이지 구성
(5) FrmStateManagement.aspx.cs 파일을 열고 다음과 같이 코드를 입력합니다.
using System;
namespace DevStateManagement
{
public partial class FrmStateManagement : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// 처음 로드할 때만 출력
if (!Page.IsPostBack)
{
// 애플리케이션 변수와 세션 변수는 주로 Global.asax에서 선언
this.txtApplication.Text = Application["Now"].ToString();
this.txtSession.Text = Session["Now"].ToString();
// 저장된 캐시, 쿠키, 뷰상태가 있다면 출력
if (Cache["Now"] != null)
{
this.txtCache.Text = Cache["Now"].ToString();
}
if (Request.Cookies["Now"] != null)
{
this.txtCookies.Text =
Server.UrlDecode(Request.Cookies["Now"].Value);
}
if (ViewState["Now"] != null)
{
this.txtViewState.Text = ViewState["Now"].ToString();
}
}
}
protected void btnSave_Click(object sender, EventArgs e)
{
// 각각의 상태 변수에 데이터 저장
Application["Now"] = this.txtApplication.Text;
Session["Now"] = this.txtSession.Text;
Cache["Now"] = this.txtCache.Text;
Response.Cookies["Now"].Value = Server.UrlEncode(txtCookies.Text);
ViewState["Now"] = this.txtViewState.Text;
Response.Redirect("FrmStateShow.aspx");
}
}
}
(6) FrmStateShow.aspx 페이지를 열고 다음과 같이 코드를 작성하고 폼을 구성합니다.
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="FrmStateShow.aspx.cs" Inherits="DevStateManagement.FrmStateShow" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>저장된 상태 보기</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h3>서버에 저장</h3>
Application 개체:
<asp:TextBox ID="txtApplication" runat="server" /><br />
Session 개체:
<asp:TextBox ID="txtSession" runat="server" /><br />
Cache 개체:
<asp:TextBox ID="txtCache" runat="server" /><br />
<h3>클라이언트에 저장</h3>
Cookies 개체:
<asp:TextBox ID="txtCookies" runat="server" /><br />
ViewState 개체:
<asp:TextBox ID="txtViewState" runat="server" />
</div>
</form>
</body>
</html>
현재 웹 폼의 <디자인 보기>로 이동하면 다음과 같은 형태의 폼을 확인할 수 있습니다.
그림 11 3 상태 관리 확인 위한 페이지 구성
(7) FrmStateShow.aspx.cs 파일을 열고 다음과 같이 코드를 작성합니다.
using System;
namespace DevStateManagement
{
public partial class FrmStateShow : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.txtApplication.Text = Application["Now"].ToString();
this.txtSession.Text = Session["Now"].ToString();
if (Cache["Now"] != null)
{
this.txtCache.Text = Cache["Now"].ToString();
}
if (Request.Cookies["Now"] != null)
{
this.txtCookies.Text =
Server.UrlDecode(Request.Cookies["Now"].Value);
}
// 뷰상태 개체는 해당 페이지에서만 살아있는 변수다.
if (ViewState["Now"] != null)
{
this.txtViewState.Text = ViewState["Now"].ToString();
}
}
}
}
(8) FrmStateManagement.aspx 페이지를 웹브라우저로 실행하면 Global.asax 파일에 저장된 시간이 두 텍스트박스에 출력됨을 확인할 수 있습니다. 그림 11 4 상태 관리 테스트 메인 페이지 실행
(9) 캐시 개체, 쿠키 개체, 뷰상태 개체의 텍스트박스에 데이터를 입력한 후 <상태 변수에 데이터 저장> 버튼을 클릭합니다. 그러면 FrmStateShow.aspx 페이지로 이동(Redirect)하여 ViewStat 개체를 제외한 나머지 상태 데이터에 저장된 값이 출력됩니다. 만약 <상태 변수에 데이터 저장> 버튼을 클릭하지 않고 <다시 게시> 버튼을 누르면 텍스트 박스에 입력된 내용은 변하지 않고 계속 출력됩니다. 이는 내부적으로 뷰 상태 개체에 해당 데이터가 저장됨을 의미합니다.
그림 11 5 새로운 데이터 입력
(10) FrmStateShow.aspx 페이지를 웹브라우저로 실행하면 앞서 FrmStateManagement.aspx 페이지에서 저장된 데이터가 출력됩니다. 하지만 ViewState 개체는 FrmStateManagement.aspx 페이지에서만 살아 있는 변수이기에 새로운 페이지인 FrmStateShow.aspx는 그 값을 잃어버린다. 그림 11 6 상태 관리 관련 개체에 저장된 내용 출력
(11) 웹브라우저 주소 입력란의 URL을 복사합니다. 새로운 웹브라우저를 열고, 주소 입력란에 복사한 URL을 붙여넣기한 후에 다시 한 번 현재 페이지를 요청하면 다음 그림과 같이 출력됩니다. 서버에 저장된 Application, Session, Cache 개체는 새로운 브라우저를 열어도 그대로 읽어와서 출력합니다. 클라이언트에 저장된 Cookies와 ViewsState 개체는 새로운 웹브라우저가 새로운 클라이언트를 나타내므로 저장된 값을 잃어버린다.
그림 11 7 새로운 브라우저에서 출력되는 모습
11.2.3 마무리 웹 폼에서 데이터베이스가 아닌 페이지와 페이지 간에 데이터를 저장하고 읽어오는 기능을 다섯 가지 개체를 통해서 살펴보았습니다. 이러한 개체들을 언제 어떻게 써야 하는지 각각의 기능을 하나씩 사용해보면서 그 의미를 더욱 자세히 알아가기 바랍니다.
<참고> Session을 사용하여 서버에 일정 시간 글 등록을 방지하기 서버에 데이터를 저장하는 Session 개체를 사용하면 일정 시간 동안 글 등록을 방지하는 기능을 간단히 구현할 수 있습니다. 다음과 같이 FrmButtonClickOnce.aspx 페이지가 있다고 가정합니다. 일반적으로 웹 페이지에서 버튼을 클릭하면 클릭 이벤트가 발생합니다. 이때 저장 버튼을 연속으로 클릭하면 그때마다 클릭 이벤트가 발생합니다. 예를 들어 게시판에서 저장 버튼을 연달아 클릭하면 데이터가 중복되어 저장될 수 있습니다. 이러한 중복 저장을 방지하는 코드는 여러가지가 있는데 이번 강의에서 다룬 세션 개체를 사용하면 비교적 쉽게 구현할 수 있습니다.
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="FrmButtonClickOnce.aspx.cs"
Inherits="DevStateManagement.FrmButtonClickOnce" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>초간단 중복 방지</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txtInput" runat="server"></asp:TextBox>
<asp:Button ID="btnSave" runat="server" Text="저장" OnClick="btnSave_Click" />
<asp:Label ID="lblDisplay" runat="server"></asp:Label>
</div>
</form>
</body>
</html>
코드 숨김 파일에서 버튼 클릭에 대한 이벤트 처리기를 작성합니다. 현재 버튼이 클릭되는 순간에 현재 시간을 Session 개체에 저장합니다. 만약, 기존에 저장된 Session 개체가 있다면, 현재 시간과 비교해서 5초 안에 다시 버튼을 클릭했다면, “잠시 대기할게요^^” 메시지가 출력되도록 하여 5초 안에는 딱 한번만 버튼 클릭에 대한 로직을 처리할 수 있게 합니다.
using System;
namespace DevStateManagement
{
public partial class FrmButtonClickOnce : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnSave_Click(object sender, EventArgs e)
{
if (Session["ClickTime"] != null)
{
DateTime originDate = Convert.ToDateTime(Session["ClickTime"]);
TimeSpan objTs = DateTime.Now - originDate;
if (objTs.TotalSeconds < 5) // 5초 동안에 한번만 저장 가능
{
lblDisplay.Text = "잠시 대기할게요^^";
return;
}
}
Session["ClickTime"] = DateTime.Now; // 재 초기화
lblDisplay.Text = txtInput.Text; // 저장 로직 실행
}
}
}
다음 그림은 처음 글쓰기한 상태입니다. 그 다음 그림을 보면 5초 내에 두번 이상 클릭했을 때 대기 화면을 계속해서 보여줍니다.
글쓰기 제한 입력 폼 1
글쓰기 제한 입력 폼 2 </참고>
추가 학습:<참고> __VIEWSTATE 이 너무 커서 너무 느리게 작동하는 문제를 해결할 수 없네요? 별도의 메모리 관리나 성능관리 등은 어떻게 방법 알려주세요?
ASP.NET Web Forms을 사용하여 폼 기반 페이지를 만들고 사용할 때에는 폼 내부에서 사용되는 내용들은 ViewState에 저장됩니다. 그러다보니, 특정 페이지를 실행 후 다른 페이지로 이동하지 않고 해당 페이지에서 데이터 저장 및 출력 등의 작업을 지속하다보면 뷰 상태 데이터가 계속해서 증가됩니다. 그러다보니 이런 것을 해결하기 위해서는 데이터 입력 후 다시 리스트에 출력되는 형태의 구조라면, 데이터 입력, 수정, 삭제 후 현재 페이지를 다시 로드하는걸 권장합니다. Response.Redire() 메서드를 사용하여 현재 페이지를 다시 로드해서 실행하면 뷰 상태 데이터가 적은 상태에서 실행되기에 이 방법을 제일 권장합니다. 참고로, ASP.NET MVC 또는 ASP.NET Core를 사용하면 뷰 상태 데이터를 전혀 사용하지 않으니 성능상의 이점을 가질 수 있습니다. </참고>
11.3 Global.asax 파일(전역 응용 프로그램 클래스) Global.asax 파일은 전역 응용 프로그램 클래스라고 합니다. 웹 프로젝트 루트에 위치하며 웹 사이트에 처음으로 사용자가 들어올 때, 각각의 사용자가 들어올 때, 각각의 사용자가 나갈 때, 최종적으로 마지막 사용자가 나갈 때 등에 대한 정보를 얻어 이에 대한 후속 조치 기능을 구현할 수 있는 이벤트를 제공합니다. Global.asax 파일은 Application과 Session 레벨 이벤트를 제공하며 주요 이벤트는 다음과 같습니다.
Application_Start: 웹 프로젝트 가동 후 처음으로 사용자가 방문했을 때 발생 Application_End: 웹 프로젝트 가동 후 마지막 사용자가 나간 후 발생. 일반적으로 마지막 사용자의 최종 요청이 끝나고 20분 후에 발생 Session_Start: 각각의 사용자가 방문할 때 발생 Session_End: 각각의 사용자가 나가고 20분 후에 발생
다음과 같이 책에서는 다루지 않는 이벤트도 많으므로 MSDN을 활용해보겠습니다.
Application_BeginRequest Application_AuthenticateRequest Application_Error 등
다음 코드는 Global.asax 파일의 주요 이벤트를 사용하여 현재 접속자 수를 구하는 간단한 기능을 구현하는 샘플입니다. 간략한 순서와 코드 조각을 먼저 살펴본 후 자세한 내용은 실습 예제에서 구현해 보도록합니다. <번호스타일>
- Application_Start 이벤트에서는 CurrentVisit라는 이름으로 Application 전역 변수를 생성하고 0으로 초기화합니다.
- 각각의 사용자가 방문할 때마다 실행되는 Session_Start 이벤트에서는 이 CurrentVisit 변수의 값을 1씩 증가시켜 현재 접속자 수를 증가시킵니다.
- Session_End 이벤트에서는 각각의 사용자가 나간 후 카운트 변수를 1씩 감소시킵니다.
- 최종적으로 Application_End 이벤트에서 카운트 변수를 소멸시킵니다. </번호스타일>
using System;
namespace WebGlobal
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
// [1] Application 전역 변수 생성
Application["CurrentVisit"] = 0; // 현재 사용자
}
protected void Session_Start(object sender, EventArgs e)
{
// [2] 각 사용자 방문시 1씩 증가
Application.Lock();
Application["CurrentVisit"] =
(int)Application["CurrentVisit"] + 1;
Application.UnLock();
}
protected void Session_End(object sender, EventArgs e)
{
// [3] 각 사용자 접속 종료 후 1씩 감소
Application.Lock();
Application["CurrentVisit"] =
(int)Application["CurrentVisit"] - 1;
Application.UnLock();
}
protected void Application_End(object sender, EventArgs e)
{
// [4] 마무리
Application["CurrentVisit"] = null;
}
}
}
이와 같이 작성된 코드를 기반으로 특정한 웹 폼 페이지에서는 다음 코드처럼 Application 전역변수의 값을 출력하여 현재 접속자의 수를 표현할 수 있습니다.
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(Application["CurrentVisit"]);
}
11.4 [실습] 현재 접속자 수 표시 카운터 만들기 11.4.1 소개 Global.asax 파일을 사용하여 웹 사이트에 현재 접속하고 있는 사용자 수를 구하는 방법을 표시하는 웹 카운터 기능을 구현해보겠습니다.
11.4.2 따라하기 (1) Visual Studio를 실행하고 ASP.NET 웹 응용 프로그램을 다음과 같이 생성합니다. 이름 위치 ASP.NET 템플릿 선택 폴더 및 핵심 참조 추가 DevCounter C:\ASP.NET ASP.NET 4.6 템플릿-Empty Web Forms, MVC, Web API
(2) 솔루션 탐색기에서 DevCounter 웹 프로젝트에 마우스 오른쪽 버튼을 클릭하여 <추가 > 새 항목> 메뉴를 클릭한 후 다음과 같이 웹 프로젝트에 새로운 항목을 추가합니다. 템플릿 이름 이름 전역 응용 프로그램 클래스 Global.asax 웹 폼 FrmCounter.aspx
(3) Global.asax 파일을 열고 다음과 같이 코드를 입력합니다. 주석 처리 부분은 제외합니다.
using System;
namespace DevCounter
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
// [1] 사이트 통계 1/3
Application["CurrentVisit"] = 0; // 현재 사용자
}
protected void Session_Start(object sender, EventArgs e)
{
// [2] 사이트 통계 2/3
Application.Lock();
Application["CurrentVisit"] =
Convert.ToInt32(Application["CurrentVisit"]) + 1; // 현재 사용자
Application.UnLock();
}
protected void Session_End(object sender, EventArgs e)
{
// [3] 사이트 통계 3/3
Application.Lock();
Application["CurrentVisit"] =
(int)Application["CurrentVisit"] - 1; // 현재 사용자
Application.UnLock();
}
protected void Application_End(object sender, EventArgs e)
{
// Empty
}
}
}
(4) FrmCounter.aspx 페이지를 열고 다음과 같이 카운터 표시를 위한 레이블 등록 폼을 구성합니다.
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="FrmCounter.aspx.cs" Inherits="DevCounter.FrmCounter" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>카운터 표시</title>
</head>
<body>
<form id="form1" runat="server">
<div>
현재: <asp:Label ID="lblNow" runat="server"></asp:Label><br />
</div>
</form>
</body>
</html>
그림 11 8 카운터 표시를 위한 레이블 등록
(5) FrmCounter.aspx.cs 파일을 열고 다음과 같이 코드를 입력합니다.
using System;
namespace DevCounter
{
public partial class FrmCounter : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// 현재 접속자 수 표시
lblNow.Text = Application["CurrentVisit"].ToString();
}
}
}
(6) FrmCounter.aspx 페이지를 실행합니다. 실행한 후 웹 브라우저 창을 하나 더 띄운 다음 같은 경로로 접속합니다. 그러면 다음과 같이 처음에는 1로 시작해서 새로운 브라우저를 띄울 때마다 카운터가 1씩 증가합니다. 참고로 웹 브라우저를 새로 띄우지 않고 새로고침 버튼을 누른다거나, 새 탭을 띄우면 같은 세션의 웹 브라우저로 인식하기에 카운터가 올라가지 않습니다.
그림 11 9 카운터 표시 페이지 실행
11.4.3 마무리 현재 웹 사이트에서 활동중인 사용자의 수를 표시할 수 있습니다. Global.asax 파일의 세 가지 이벤트를 사용했습니다. 웹 사이트 처음 가동시 카운트 변수를 0으로 초기화하고 각각의 사용자가 처음 접속할 때마다 1씩 증가시켜 주었습니다. 각각의 사용자가 나간 후 기본값으로 20분 뒤에 1씩 감소시켜주는 로직을 사용했습니다.
11.5 캐싱을 사용한 웹 페이지 성능 향상 기법 ASP.NET에서는 자주 바뀌지 않는 페이지에 대한 내용을 서버 측 캐시에 저장하고 있다가 클라이언트의 요청이 들어오면 바로 출력시켜줍니다. 응답 속도를 개선할 수 있는 기법으로 이를 캐싱(Caching)이라고 합니다. 웹 폼에 캐싱 기능을 적용할 때는 선언형으로 적용하는 방법과 코드 기반으로 적용하는 방법 두 가지가 있습니다.
캐싱을 적용할 때에는 OutputCache 지시문(디렉티브)을 사용합니다. <%@ OutputCaChe Duration=”초” VaryByParam=”None 또는 파라미터 이름 또는 ” %> 주요 속성들 아래와 같습니다. (필요하면 MSDN에서 검색) Duration Location VaryByCustom VaryByHeader VaryByParam VaryByParm=”None”, “”, “Name”, “Name;Age”
11.5.1 캐시 프로파일(Cache Profiles) 캐싱 정보를 Web.config 파일에 미리 설정 후 OutputCache 지시문의 CacheProfile 속성을 통해서 미리 설정된 프로필 정보를 읽어오는 방식입니다. 좀 더 자세한 내용은 MSDN을 참고합니다.
11.5.2 페이지 조각 캐싱(Page fragment caching) 웹 폼 사용자 정의 컨트롤을 사용하면 메뉴, 헤더, 푸터 등을 조각 단위로 캐싱을 처리할 수 있습니다. 이를 사용하려면 UseControl에 OutputCache 지시자를 설정하면 됩니다.
11.6 [실습] 캐싱을 이용한 웹 페이지 성능 향상 11.6.1 소개 웹 페이지의 성능을 향상시킬 수 있는 캐싱 기능을 ASP.NET에서 아주 손쉽게 적용하는 두 가지 예를 살펴보겠습니다.
11.6.2 따라하기 (1) Visual Studio를 실행하고 ASP.NET 웹 응용 프로그램을 다음과 같이 생성합니다. 이름 위치 ASP.NET 템플릿 선택 폴더 및 핵심 참조 추가 DevCaching C:\ASP.NET ASP.NET 4.6 템플릿-Empty Web Forms, MVC, Web API
(2) 솔루션 탐색기에서 DevCaching 웹 프로젝트에 마우스 오른쪽 버튼을 클릭하여 <추가 > 새 항목> 메뉴를 클릭한 후 다음과 같이 웹 프로젝트에 새로운 항목을 추가합니다. 템플릿 이름 이름 웹 폼 사용자 정의 컨트롤 FrmCachingWebUserControl.ascx 웹 폼 FrmCaching.aspx 웹 폼 FrmCachingResponseCache.aspx
(3) FrmCachingWebUserControl.ascx 파일을 열고 다음과 같이 웹 폼 사용자 정의 컨트롤 폼을 구성합니다. 아래 코드를 살펴보면 OutputCache 지시문이 Duration과 VaryByParam 속성을 사용하여 추가로 선언되었습니다. 이 지시문에 의해서 현재 웹 폼 사용자 정의 컨트롤은 5초 동안에 메모리에 탑재되어 동일한 페이지를 보여줄 것입니다. 5초 동안 여러 번 호출해도 처음 실행한 내용이 계속 출력된 후 5초 지나면 다시 캐싱이 초기화되어 다시 실행되는 구조입니다.
<%@ Control Language="C#" AutoEventWireup="true"
CodeBehind="FrmCachingWebUserControl.ascx.cs"
Inherits="DevCaching.FrmCachingWebUserControl" %>
<%@ OutputCache Duration="5" VaryByParam="None" %>
현재 시간(웹 폼 사용자 정의 컨트롤):
<asp:Label ID="lblTimeWebUserControl" runat="server" Text="Label"></asp:Label>
VaryByParam=”None”: 페이지별(GET, POST 당 하나씩) 유니크한 캐싱 인스턴스가 하나 생성됩니다. VaryByParam=”*”: 모든 쿼리스트링에 대해서 캐싱이 적용됩니다. VaryByParam=”특정쿼리스트링”: 세미콜론 기호를 구분해서 하나 이상의 쿼리스트링에 대하여 캐싱이 적용됩니다.
<디자인 보기>에서 본 화면은 다음과 같습니다. 단순한 텍스트로 표현되지만 웹 폼 사용자 정의 컨트롤인 ASCX 파일은 버튼 컨트롤 같은 다른 컨트롤과 달리 도구 상자에만 등록이 되지 않았지 하나의 완성된 컨트롤로 사용될 수 있습니다. 완성된 기능을 조각 페이지인 ASCX 파일에 구현하고 이를 하나 이상의 웹 폼에서 재사용할 수 있는 구조입니다. 그림 11 10 웹 폼 사용자 정의 컨트롤 폼 구성
(4) FrmCachingWebUserControl.ascx.cs 파일을 열고 다음과 같이 코드를 입력합니다. 페이지 로드할 때마다 현재 시간을 레이블에 출력되는 예제인데 캐싱이 적용되지 않으면 새로 고침할 때마다 호출이 되는 것이고 캐싱이 적용되면 5초에 한번만 호출되는 개념으로 보면 됩니다.
using System;
namespace DevCaching
{
public partial class FrmCachingWebUserControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
// 현재 시간 출력: 5초에 한 번씩만 출력
lblTimeWebUserControl.Text = DateTime.Now.ToString();
}
}
}
(5) FrmCaching.aspx 페이지를 열고 다음과 같이 캐싱 테스트 페이지에 웹 사용자 정의 컨트롤 등록 폼을 구성합니다. 웹 폼 사용자 정의 컨트롤을 웹 폼에 떨어뜨려 놓은 후 다음 소스 코드와 비교해보라. 솔루션 탐색기에서 FrmCachingWebUserControl.ascx 파일을 드래그해서 FrmCaching.aspx 페이지의 디자인 영역 또는 소스 영역으로 드롭하게 되면 자동으로 Register 지시문이 생성됩니다. 물론, 직접 다음 코드처럼 입력해도 무관합니다.
그림 11 11 캐싱 테스트 페이지에 웹 사용자 정의 컨트롤 등록
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="FrmCaching.aspx.cs" Inherits="DevCaching.FrmCaching" %>
<%@ Register Src="~/FrmCachingWebUserControl.ascx"
TagPrefix="uc1" TagName="FrmCachingWebUserControl" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>캐싱(Caching)</title>
</head>
<body>
<form id="form1" runat="server">
<div>
현재 시간(웹 폼):
<asp:Label ID="lblTimeWebForms" runat="server" Text="Label"></asp:Label>
<hr />
<uc1:FrmCachingWebUserControl runat="server" ID="FrmCachingWebUserControl" />
</div>
</form>
</body>
</html>
(6) FrmCaching.aspx.cs 파일을 열고 다음과 같이 코드를 입력합니다.
using System;
namespace DevCaching
{
public partial class FrmCaching : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// 현재 시간 출력: 매번 바로 출력
lblTimeWebForms.Text = DateTime.Now.ToString();
}
}
}
(7) FrmCaching.aspx 페이지를 웹브라우저로 실행합니다. 이 페이지는 캐싱이 설정된 페이지입니다. 새로 고침 버튼을 몇 번인가 누르면 다음 그림처럼 웹 폼에서 설정된 시간은 지속적으로 업데이트되는 반면, 웹 사용자 정의 컨트롤에서 설정된 시간은 5초에 한 번씩 업데이트되는 것을 볼 수 있습니다. 이처럼 자주 바뀌지 않는 페이지를 매번 실행하지 않고 서버 측 메모리에 저장하고 있다가 바로 출력시켜 주는 방식을 사용하여 웹 페이지의 성능을 향상시킬 수 있습니다.
그림 11 12 캐싱이 설정된 페이지 실행 결과
(8) FrmCachingResponseCache.aspx 페이지에는 아무런 코드를 작성하지 않고 코드 숨김 페이지인 FrmCachingResponseCache.aspx.cs 페이지를 열고 다음과 같이 코드를 입력합니다. 아래 코드는 공식과 같은 코드 기반의 캐싱 설정 방법을 보여줍니다. 더 많은 옵션을이 있지만, 이 책에서는 다루지 않습니다. 2020년 현재 이 예제는 IE에서만 실행이되고 다른 웹브라우저에서는 지원이 되지 않습니다.
using System;
using System.Web;
namespace DevCaching
{
public partial class FrmCachingResponseCache : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// 코드 기반으로 캐싱 기능 적용하기
// 현재 날짜 출력
Response.Write(DateTime.Now.ToString());
// 캐싱 설정
Response.Cache.SetCacheability(HttpCacheability.Public);
// 캐싱 유효기간 설정
Response.Cache.SetExpires(DateTime.Now.AddSeconds(60));
// 매개 변수 방식 지정
Response.Cache.VaryByParams["*"] = true;
}
}
}
(9) FrmCachingResponseCache.aspx 페이지를 웹브라우저로 실행한 후 새로고침을 해봅니다. 이 페이지는 코드 기반 캐싱 설정 페이지입니다. 1분 동안은 처음 로드할 때 출력된 시간에서 변경되지 않습니다. 그림 11 13 코드 기반 캐싱 설정 페이지 실행
11.6.3 마무리 웹 폼 또는 웹 폼 사용자 정의 컨트롤에서 자주 바뀌지 않거나 지정된 시간 동안 페이지를 새로 요청하지 않고 캐싱된 정보를 바로 클라이언트 측에 전송하는 방식으로 성능을 향상시킬 수 있는 기법을 알아보았습니다. 캐싱 기능은 거의 바뀌지 않는 페이지이지만, 자주 요청되는 페이지에 설정하면 좋다.
11.7 환경 설정 파일인 Web.config
One ASP.NET인 ASP.NET 웹폼 프로젝트는 기본적으로 프로젝트 루트에 Web.config 파일을 가진다. Web.config 파일은 웹프로젝트 관련 환경 설정 파일이 들어오는 곳입니다. 일단은 기본값만 사용해도 큰 문제는 없지만, 큰 규모의 앱으로 확장하거나 특정 기능 사용 시에는 Web.config 파일을 건드려야할 경우가 생긴다. 일단 이번 절에서는 Web.config 파일의 두 가지 영역인 <appSettings>
섹션과 <connectionStrings>
섹션을 사용해봅니다. 참고로 크로스 플랫폼 기반의 ASP.NET Core에서는 JSON(JavaScript Object Notation) 기반의 appsettings.json 파일이 Web.config 파일의 기능 대부분을 대체합니다.
11.8 [실습] Web.Config 파일의 정보를 읽어오는 클래스 사용하기 11.8.1 소개 Web.config 파일의 특정 요소의 값을 읽어오는 WebConfigurationManager 클래스와 ConfigurationManager 클래스를 사용해봅니다.
11.8.2 따라하기 (1) DevConfiguration 이름으로 ASP.NET Web Forms 빈 프로젝트를 생성합니다. 이름 위치 ASP.NET 템플릿 선택 폴더 및 핵심 참조 추가 DevConfiguration C:\ASP.NET ASP.NET 4.6 템플릿-Empty Web Forms
(2) 웹 프로젝트 루트에 있는 Web.config 파일을 연 다음 기본 제공 코드는 놔두고, 다음 코드를 추가합니다. 다음 코드의 <configuration>
섹션 아래에 <appSettings>
섹션과 <connectionStrings>
섹션을 추가하고 키와 값의 쌍으로 데이터를 입력합니다.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<!-- 프로그램 내에서 사용되는 설정값 저장 -->
<!--[1] 사이트 이름 -->
<add key="SITE_NAME" value="ASP.NET"/>
<add key="COMPANY_NAME" value="길벗출판사"/>
</appSettings>
<connectionStrings>
<!--[2] 데이터베이스 연결 문자열 -->
<add name="ASPNETBOOKTEST_ConnectionString"
connectionString="server=;database=;uid=;pwd=;"
providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
</system.web>
<system.codedom>
</system.codedom>
</configuration>
(3) FrmConfiguration.aspx 파일명으로 웹 폼을 만들고 다음과 같이 UI 소스 코드를 작성합니다.
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="FrmConfiguration.aspx.cs"
Inherits="DevConfiguration.FrmConfiguration" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>
<%= System.Web.Configuration.
WebConfigurationManager.AppSettings["SITE_NAME"].ToString() %>
</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblSITE_NAME" runat="server"></asp:Label>
<hr />
<asp:Label ID="lblCOMPANY_NAME" runat="server"></asp:Label>
<hr />
<asp:Label ID="lblASPNETBOOKTEST_ConnectionString" runat="server"></asp:Label>
</div>
</form>
</body>
</html>
(4) FrmConfiguration.aspx.cs 파일을 열고 다음과 같이 코드를 작성합니다. 클래스 두 개를 사용해서 같은 작업을 진행할 수 있습니다.
using System;
using System.Configuration;
using System.Web.Configuration;
namespace DevConfiguration
{
public partial class FrmConfiguration : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
lblSITE_NAME.Text =
WebConfigurationManager.AppSettings["SITE_NAME"].ToString();
lblCOMPANY_NAME.Text =
ConfigurationManager.AppSettings["COMPANY_NAME"].ToString();
lblASPNETBOOKTEST_ConnectionString.Text =
WebConfigurationManager.ConnectionStrings[
"ASPNETBOOKTEST_ConnectionString"].ConnectionString;
}
}
}
(5) FrmConfiguration.aspx 페이지를 시작 페이지로 설정 후 <웹 브라우저로 보기> 또는 [Ctrl]+[F5]를 눌러서 실행하면 다음 그림처럼 Web.config 파일의 정보가 웹 브라우저 화면에 출력됩니다.
그림 11 14 Web.config 파일에 설정된 값 출력
11.8.3 마무리 웹 사이트 전체에서 공통으로 사용되는 어떤 값들은 Application 전역 변수를 사용하거나 이번 실습에서 살펴본 Web.config 파일에 키 값으로 데이터를 저장해 놓은 것을 사용합니다. Application 전역 변수와 Web.config 파일에 키 값으로 저장해 놓는 것의 차이점을 살펴보겠습니다. Web.config 파일의 내용은 XML로 구성되어 있으므로 컴파일이 필요 없이 메모장(Notepad.exe) 같은 프로그램으로 내용을 변경하면 바로 웹 사이트에 적용됩니다. Application 전역 변수는 내용이 수정되면 반드시 웹 사이트가 빌드(컴파일)되어야 사용할 수 있습니다. 이번 실습은 ASP.NET의 여러 영역에서 계속 사용됩니다. 일단은 이 정도로 정리하고 뒤에서 좀더 자세히 설명합니다.