티스토리 뷰



[.NET/ASP.NET] - 실전 ASP.NET Session [1] - 쿠키를 이용한 상태관리와 위험성
[.NET/ASP.NET] - 실전 ASP.NET Session [2] - 상태관리의 종류
[.NET/ASP.NET] - 실전 ASP.NET Session [3] - 다양한 세션 관리 방법
[.NET/ASP.NET] - 실전 ASP.NET Session [4] - 세션상태 마이그레이션

 

 

이 단원은 ASP.NET 의 어느 책을 보아도 나오는 반드시 나오는 챕터이죠. 그만큼 기본적이고 중요한 부분입니다. 왜냐하면 웹이라는 것은 기본적으로 아무런 상태를 저장할 수 없기 때문입니다. 하지만, ASP.NET 을 이용하면 다양한 방법을 통해 상태를 쉽게 할 수 있고, 쉽게 간과할 수 있는 부분을 다시 한번 되새길 수 있도록 구성해 보았습니다.

 

Application 개체

 

이 프로퍼티는 HttpApplicationState 의 인스턴스 입니다. 대부분의 ASP.NET 의 상태관리 개체는 키(Key) 와 값(Value) 이 쌍을 이루는 컬렉션 형태입니다. 따라서 다음과 같이 사용할 수 있습니다.

 

HttpApplicationState app = HttpContext.Current.Application;

app["Key"] = "Value";

 

Response.Write( app["Key"].ToString() );

 

하지만, Page 클래스에 Application 라는 프로퍼티로 정의가 되어 있기 때문에, 다음과 같이 더 간결하게 사용할 수 있습니다.

 

Application["Key"] = "Value";

 

Application 개체는 웹 응용 프로그램의 전역적인 값을 유지할 수 있습니다. 웹 응용 프로그램의 설정이나 세션에 관계없이 전역적인 변수를 저장하는데 유용합니다. 또한, 프로세서가 실행되고 있는 서버의 메모리에 값을 저장하고 있기 때문에 빠르게 데이터를 엑세스 할 수 있습니다. 웹 응용 프로그램은 Worker Processer 의 가상 디렉토리로써 AppDomain 이라는 응용 프로그램 도메인에 속하게 됩니다. 다시 말하자면, HttpRuntime / HttpApplicationState / Page 개체는 AppDomain 에 격리되고, 그렇기 때문에, Application 개체는 응용 프로그램 간에 값을 공유할 수 없습니다.

 

Application 개체는 프로세서가 실행되는 서버의 메모리에 값을 저장하기 때문에, Application 개체에 저장할 데이터는 작으면 작을 수록 좋습니다.

 

[그림1] IIS ASP.NET 리소스 처리 아키텍쳐

( 위 그림과 더불어 참고 하세요 http://www.windowsdevcenter.com/pub/a/windows/2004/03/02/inside_iis.html )

 

ASP.NET 은 요청에 대해 스레드 풀에 요청을 할당하여 그 스레드로 페이지를 실행시킵니다. 페이지의 수행 주기가 끝나게 되면 해당 스레드는 스레드 풀에 반환되게 됩니다. ASP.NET 에 대한 요청은 이러한 다중 스레드 환경에서 Application 개체의 Input 이 발생하게 되면 동시성에 의해 원치 않는 데이터가 저장될 수 있습니다. 그렇기 때문에, Application 개체의 초기화는 Global.asax Application Start 이벤트를 통해 개체를 초기화 하도록 해야 합니다.

 

만약, Application Start/End 외에 다른 곳에서 Application 에 데이터를 Input 하기 위해 잠금/잠금 해제의 코드를 작성해 주시면 됩니다.

 

Application.Lock();

Application["Count"] = (int)Application["Count"] + 1;

Application.UnLock();

 

위와 같이 다중 스레드에서 Application 개체를 동기화 하기 위해 Lock() UnLock() 메서드를 통해 잠금을 수행할 수 있습니다.

 

Application 사용시

1.      데이터의 크기가 작을 때 경우 이용

2.      메모리에서 I/O 가 발생하기 때문에 잦은 입출력에 용이

3.      잦은 I/O 가 발생하는 페이지에 사용시 반드시 동기화 코드를 작성

 

 

ViewState 개체

 

ViewState 는 현재 페이지의 상태를 유지하기 위해 사용됩니다. 기본적으로 ViewState 히든필드(Hidden Field) 형태로 HTML 로 내려보내지며, 포스트 백이 발생하게 되면 Hidden Field 를 전송하여 페이지 상태를 유지할 수 있습니다.

 

[그림2] 소스 보기를 통해 본 ViewState

 

ViewState 는 계층적인 형태로 표현이 되는데, 자세한 정보는 태요 사이트ViewState 데이터를 분석하자를 참고 하시기 바랍니다.

 

ViewState 는 여느 상태 관리 컬렉션과 같이 간단하게 사용할 수 있습니다.

 

ViewState["CategoryID"] = "1234";

Response.Write(ViewState["CategoryID"].ToString());

 

특별한 기교 없이 쉽게 구현이 가능하지요.

 

ViewState 는 위의 [그림2] HTML 소스를 보는 것과 같이 상태 정보를 HTML 에서 정보를 가지고 있기 때문에 서버의 리소스가 필요하지 않습니다. 서버의 리소스가 필요하지 않기 때문에, 실제로 가장 많이 사용하는 상태 관리 기법이면서, ASP.NET 플랫폼의 서버 컨트롤이나 Form Runat=”Server” ( , Form 의 모든 요소를 서버에서 처리 ) 하기 때문에, 많은 부분의 상태 관리가 자동화 되어 있습니다.

 

이것이 강점이자 단점이 되는 것이 ASP.NET ViewState 입니다. Form 의 상태를 서버에서 처리를 하고, 서버 컨트롤이라는 편리한 ASP.NET 컨트롤을 이용하고, 이 컨트롤들은 스스로 상태 관리 능력을 가지고 있습니다. Control SaveViewState LoadViewState 메서드를 override 함으로써 컨트롤 스스로 상태 관리 능력을 부여할 수 있기 때문입니다. 하지만 GridView DataList 와 같은 많은 기능을 가지고 있는 컨트롤들은 바인딩 되는 DataSource 에 따라 ViewState 는 눈덩이 처럼 불어나게 됩니다. 아무 생각 없이 서버 컨트롤만을 사용하다 보면 ViewState 의 용량만 10 Kb, 20 Kb 가 훌쩍 넘어 버립니다. 우리나라와 같이 네트워크 인프라가 원활한 환경이라면 콧방귀를 뀌고 우습게 여길 수 도 있지만, 모바일 장치나 인터넷 속도가 느린 외국에서 서비스를 해야 할 경우 상황은 달라지게 됩니다.

 

ASP.NET ViewState Serializable 형태여야 합니다. ViewState HTML 코드로 랜더링 하기 위해서 이러한 직렬화 과정을 거치게 되고, 이것을 Base64 인코딩을 통해 [그림2] 와 같이 문자열로 변환하게 됩니다. 포스트백을 통해 Base64 디코딩과 역직렬화 과정을 거쳐 개체로 반환되게 됩니다. 수십/수백명의 동시 접속자가 예상되는 서비스에서는 ViewState 는 곧 CPU 부하를 증가시키고, 서버를 늘릴 수 밖에 없는 비용적인 측면을 생각하면 개인이나 기업에게는 잠재적인 부담일 수 밖에 없습니다.

 

해결책으로 가장 좋은 방법은 ViewState 를 사용하지 않는 것입니다. 대부분의 ViewState 는 서버 컨트롤에서 발생하기 때문에, 서버 컨트롤의 사용량을 줄이는 것이 좋은 방법이 될 수 있습니다. 더 나아가, Form Runat=”Server” 속성을 날려버리고, 여러개의 form 으로 나누어 서버로 전송되는 트래픽의 양을 줄여 ViewState 의 연산을 없애 버리는 것도 좋은 방법입니다.

 

다음 코드는 ViewState 를 사용하지 않는 최적화된 HTML 입니다.

 

<body>

    <form id="form1">

    <div>

             <input id="txtName" name="txtName" type="text" value="<%= txtName %>" />&nbsp;

             <input id="btnSend" name="btnSend" type="submit" value="Send" />

    </div>

    </form>

</body>

 

Form 태그의 Runat=”Server” 속성을 제거해 버렸습니다. 그렇기 때문에, Send 버튼을 클릭하게 되면 txtName 의 텍스트박스는 상태 관리가 되지 않습니다. 포스트백(Submit) 이 발생하면 txtName 의 텍스트박스는 입력된 값이 모두 사라지게 됩니다. Form Runat=”Server” 를 제거하면 기본적인 ViewState 조차 생기지 않습니다.

 

그럼 여기에 상태 관리를 위한 코드를 삽입해 보면..

 

public partial class _Default : PageBase

{

      

       protected string txtName;

 

       protected void Page_Load(object sender, EventArgs e)

       {

             try

             {

                    this.txtName = Request.Params["txtName"];

             }

             catch (Exception ex)

             {

                    Response.Write(ex);

             }

       }

}

 

위 코드를 삽입하게 되면, Send 버튼 클릭 후에도 txtName 의 텍스트박스는 이전에 입력했던 값을 유지하게 됩니다. 서버컨트롤과 자동화된 ASP.NET 의 상태 관리 없이 개발자가 직접 코드를 작성하여 상태 관리를 하는 방법입니다.

 

짧은 제 경험상, 기업의 내부 인트라넷의 경우 다양한 요구사항과 업무 처리를 위해 서버 컨트롤과 더불어 다양한 기능의 상용 컴포넌트와 컨트롤을 사용하기도 합니다. 하지만, 웹 서비스와 같이 불특정 다수에게 노출이 되는 중~대규모 사이트라면 서버컨트롤을 자재하여 사용하기도 하지만 아예 위의 방법처럼 코드를 통해서 상태 관리를 하기도 합니다. 생산성 측면에서 본다면, 당연히 개발 속도는 자동화된 ASP.NET ViewState 를 사용하는 것보다 느릴 수 있지만, 서버로 전송되는 트래픽의 양이 줄고, 서버의 CPU 연산이 줄게 되어 최종 사용자(End User) 측면에서는 빠른 응답 속도를 기대할 수 있습니다.

 

하지만, ViewState 를 포기한다는 것은 굉장히 어려운 문제일 수 있습니다. Form Runat=”Server”를 사용하지 않고, 수동적으로 상태 관리를 하게 되면, 많은 편리한 ASP.NET 의 기능을 사용할 수 없기 때문이죠. 그 대표적인 예가, ASP.NET AJAX ScriptManager 컨트롤 또한 사용할 수 없게 되고, 내부적으로 SaveViewState LoadViewState 를 사용하는 모든 서버 컨트롤(Label 외 몇 가지 컨트롤은 제외…)은 사용할 수 없게 됩니다. 때문에, 적절한 대안을 찾아야 하는데 바로 Control 이란 부모 클래스에는 EnableViewState 속성이 대안이 될 수 있습니다.

 

EnableViewState 는 컨트롤이 ViewState 를 사용할 것인지의 여부를 결정합니다. DataGrid 를 예로 들자면, 기본적인 EnableViewState 속성이 True 이기 때문에, 최초 한번의 DataBind 을 하고, 포스트백이 발생하여도 상태는 유지되지만, DataGrid 의 속성을 EnableViewState=False 로 변경하면, DataSource 의 데이터를 더 이상 ViewState 로 관리하지 않게 됩니다. 모든 컨트롤의 EnableViewState 속성을 변경해야 한다면, Page EnableViewState 도 있습니다. Page EnableViewState 는 더 이상 그 Form 은 모든 ViewState 를 관리 하지 않겠다는 것을 의미하며, 지저분한 ViewState 를 더 이상 생성하지 않습니다. (, 포스트백인지 아닌지의 여부를 감시하는 일부의 Hidden Field 를 생성할 수 있습니다.)

 

위의 내용과 더불어 EnableViewState MAC 이라는 것이 있습니다. 높은 수준의 데이터 무결성이나 데이터의 훼손이 큰 경우 EnableViewStateMac 속성을 True 로 지정해야 합니다. (기본값은 False) 입니다. 지난 아티클의 RewritePath 포스트백(Postback) 문제와 해결 방법을 통해 포스트백의 URL 을 지정해 줄 수 있는데, 이러한 비 정상적인 방법(?) 을 이용하게 되면, 서버 측에서는 ViewState 가 훼손되었거나, 데이터가 변경된 것으로 인지할 수 있습니다. 또한, 가끔씩 UpdatePanel 을 이용해 프로그램을 하다보면 개발자의 잘못된 알고리즘으로 인하여 ViewState 가 손실되거나 변경되는 경우가 있습니다. 이러한 경우들에 대해 EnableViewStateMac=False 를 통해 데이터의 무결성 검사 등의 ViewState 의 상태를 검사하지 않을 수 있게 됩니다.

 

하지만, 결정적으로 ViewState 는 클라이언트에 내려지고, 다시 이 ViewState 는 서버로 전송되는 형태이기 때문에, ViewState 는 언제든지 조작의 가능성이 있습니다. 간단한 툴을 이용해서 ViewState 가 어떤 정보를 가지고 있는지 쉽게 알 수 있기 때문에, ViewState 의 데이터를 항상 신뢰하면 안됩니다. ASP.NET 2.0 부터 이러한 ViewState 를 암호화 할 수 있는 기능이 제공됩니다. 간단히 @Page 지시자에 ViewStateEncryptionMode="Always" 를 넣음으로써 페이지의 ViewState 를 암호화 할 수 있습니다. 모든 웹 응용 프로그램의 페이지에 이러한 속성을 주기 위해서 web.config system.web 섹션에

 

<system.web>

       <pages viewStateEncryptionMode="Always">

 

로 지정해 주기만 하면 됩니다.

 

이것의 기본 속성은 Auto 입니다. Auto 는 아무런 ViewState 암호화가 수행되지 않습니다. , 개발자의 코드를 통해 페이지의 암호화를 지정해 줄 수 있는데,

 

Page.RegisterRequiresViewStateEncryption();

 

의 명시적인 호출로 Auto 상태에서 ViewState 암호화를 지정해 줄 수 있습니다. , 암호화를 수행하게 되면 그만큼의 CPU 의 부하도 늘게 된다는 것 또한 잊어서는 안됩니다.

 

ViewSate 사용시

1.      프로젝트가 어떤 용도의 서비스인지 잘 판단하여, 상태관리를 자동화 할 것인지, 수동화 할 것인지 결정

2.      EnableViewState MAC / ViewStateEncryptionMode 유용할 있지만, 남용하게 되면 CPU 파워만 증가시킨다

 

 

Sessoin 개체

 

이제야 Session 개체가 나왔습니다. 3회차부터 이 Session 개체를 더 자세히 볼 예정이기 때문에, 여기에서의 Session 은 가볍게 보도록 하겠습니다.

 

여느 상태관리 개체와 마찬가지로 사용법은 매우 간단합니다.

 

Session.Add("Key1","Value1");

                          

Session["Key2"] = "Value2";

 

 [그림3] 브라우져 요청 별 쿠키 생성

 

기본적으로 HTTP 프로토콜을 이용한 요청은 독립적으로 요청을 처리합니다. 요청에 대해 상태를 유지할 수 없기 때문에 어떤 사용자에 의한 요청인지 알 수 있는 방법이 없습니다. 그렇기 때문에, Session 은 쿠키와 브라우져에 의존적입니다. 이전 포스팅의 실전 ASP.NET Session [1] - 쿠키를 이용한 상태관리와 위험성에서도 설명했듯이, 브라우져가 사이트에 요청을 할 때 생성이 되고, 브라우져를 닫게 되면 쿠키도 삭제된다고 하였습니다. , 만료시간이 지정된 경우에 텍스트 형태로 사용자의 하드 디스크에 저장이 됩니다.

 

[그림4] 브라우져별 요청에 대한 쿠키 생성

 

브라우져는 의해 처음 사이트에 요청을 하게 되면 쿠키를 생성하게 됩니다. 때문에 브라우져 별로 처음으로 사이트를 요청하게 되면 쿠키를 생성하는데 그것이 바로 ASP.NET 이 상태관리를 위한 Session ID 를 쿠키로 생성하게 됩니다. 이러한 행위는 사용자별로 고유한 세션 저장소에 정보를 저장하기 위해서이며, 사이트에 접속한 여러 사용자를 구별한 수 있는 키(Key) 가 되기도 합니다. 좀 더 정확하게 말하면, 이러한 Session ID AppDomain 별로 웹 응용프로그램의 Application ID 가 세션의 키값으로 사용되어 집니다. 그렇게 때문에, AppDomain 간의 Session 의 값은 공유할 수 없게 됩니다.

 

ASP.NET Session 은 기본적으로 쿠키를 통해 Session ID 를 인식합니다. 하지만, 특정 사용자의 브라우져 상태는 쿠키를 허용하지 않을 수 도 있습니다. 또한 사용자 임의로 쿠키를 허용하지 않도록 할 수 도 있습니다. ASP.NET 은 쿠키 기반의 세션 유지가 아닌 쿼리 문자열을 통해 Session ID 를 유지할 수 도 있습니다.

 

system.web 섹셕의 sessionState 요소를 통해 아래와 같이 cookieless true 로 설정하면, 더 이상 쿠키 기반으로 Session ID 를 유지하지 않고, 쿼리 문자열을 통해 Session ID 를 식별하게 됩니다.

 

<system.web>

       <sessionState cookieless="true"></sessionState>

 

[그림5] 쿼리 문자열을 통한 Session ID 유지

 

ASP.NET 은 보다 자동화된 Session ID 를 유지하도록 해 줍니다. 가령, 사용자의 브라우져나 쿠키를 허용하지 않거나, 임의로 쿠키를 허용하지 않도록 설정한 경우 cookieless AutoDetect 로 설정하게 되면, ASP.NET 쿠키를 통해 Session ID 를 유지할 것인지, 쿼리 문자열로 Session ID 를 유지할 것인지를 스스로 결정하게 됩니다. 기본적으로 cookieless 속성은 디폴트로 UseCookies 로 설정되어 있기 때문에,

 

<sessionState cookieless="AutoDetect"></sessionState>

 

과 같이 개발자가 직접 수정해 주셔야 합니다.

 

기본적은 Session 설정은 In-of-process 를 통해 처리되게 됩니다. In-of-process ASP.NET Worker Process 를 통해 처리가 되며, Worker Process 가 구동되는 서버의 메모리에 저장이 됩니다. Application 개체와 마찬가지로 Session 개체는 In-of-Process 로 처리가 될 경우 웹 응용프로그램의 빌드나 web.config 의 설정이 바뀔 경우 개체의 데이터는 모두 잃어버릴 수 있습니다. 명시적으로 sessionState mode 속성을 지정하지 않으면 디폴트로 InProc 입니다

 

Session 을 잃게 된다는 것은 웹 서비스에게 치명적일 수 있습니다. 한가지 예를 들어보면, 쇼핑몰 사이트가 있습니다. 상품을 결재를 하기 위해서 대부분 여러 단계를 거쳐 인증을 하고, 결재가 진행됩니다. 하지만, 사이트 관리자는 서버의 치명적인 버그를 발견하여 수정하고 웹 어플케이션을 업데이트 한다면, 결재중인 사용자의 Session 은 끊겨버리게 됩니다. 물론, 이러한 결재 오류에 대한 정책이 잘 정립 되어 운영 된다면 문제가 없겠지만, 데이터베이스를 조작하지 못하는 서버 관리자만을 채용하는 소형 쇼핑몰일 경우 이러한 문제는 쉬쉬 하며 넘어갈 일만은 아닐 것입니다.

 

[그림6] 다양한 Session State 유지 계획

 

하지만, 여기서도 문제라면 서버의 메모리는 제한적입니다. 때문에 In-of-process 가 아닌, Out-of-process 로 전환할 필요가 생기게 됩니다. 또한 대형 사이트일 경우 Web Farm 으로 부터 여러 개의 Server Farm 을 구성하여 분산처리 할 경우 보다 나은 성능과 관리의 이점이 있습니다. , 한가지 Out-of-process Serializable 개체만을 Session 에 저장할 수 있다는 제한이 있습니다.(당연하겠지만)

 

다음 아티클에서 Session State 를 유지하는 다양한 방법을 알아 보고, 방법에 대한 보다 나은 이슈를 제시할 예정입니다.

 

용어설명 참고

위키디피아 Web Farm(Server Farm)

Web Garden – MSDN ASP.NET 작업자 프로세스

 

Session 사용시

1.      소규모 사이트일 경우 In-of-process 를 권장

2.      Session 은 어떻게 사용 하느냐도 중요하지만 어떻게 운영하는가에 대한 기술적인 계획이 필요

 


댓글