거의 모든 ASP.NET 응용 프로그램은 사용자 세션의 데이터를 추적해야 합니다. ASP.NET은 세션 상태 값을 저장하기 위해 HttpSessionState 클래스를 제공합니다. 각 HTTP 요청에 대한 HttpSessionState 클래스의 인스턴스는 정적 HttpContext.Current.Session 속성을 사용하여 애플리케이션 전체에서 액세스할 수 있습니다. Page 또는 UserControl의 Session 속성을 사용하면 모든 Page 및 UserControl에서 동일한 인스턴스에 대한 액세스가 더 간단해집니다.
HttpSessionState 클래스는 키가 String 유형이고 값이 Object 유형인 키/값 쌍의 컬렉션을 제공합니다. 이는 Session이 매우 유연하며 Session에 거의 모든 유형의 데이터를 저장할 수 있음을 의미합니다.
그러나 (항상 그러나 있지만) 이러한 유연성은 비용 없이 제공되지 않습니다. 비용은 애플리케이션에 버그가 쉽게 유입될 수 있다는 점입니다. 도입될 수 있는 많은 버그는 단위 테스트로는 발견되지 않으며 아마도 어떤 형태의 구조화된 테스트로도 발견되지 않을 것입니다. 이러한 버그는 애플리케이션이 프로덕션 환경에 배포된 경우에만 나타나는 경우가 많습니다. 버그가 표면화되면 버그가 어떻게 발생했는지 파악하고 버그를 재현하는 것이 불가능하지는 않더라도 매우 어려운 경우가 많습니다. 이는 수리하는 데 비용이 매우 많이 든다는 것을 의미합니다.
이 문서에서는 이러한 유형의 버그를 방지하는 데 도움이 되는 전략을 제시합니다. 이는 Facade라는 디자인 패턴을 사용합니다. 즉, HttpSessionState 클래스에서 제공하는 매우 자유로운 인터페이스(모든 애플리케이션의 요구 사항을 충족할 수 있음)를 특정 애플리케이션용으로 특별히 제작된 잘 설계되고 제어된 인터페이스로 래핑합니다. 디자인 패턴이나 파사드 패턴에 익숙하지 않은 경우 "파사드 디자인 패턴"을 인터넷에서 빠르게 검색하면 풍부한 배경 지식을 얻을 수 있습니다. 그러나 이 기사를 이해하기 위해 디자인 패턴을 이해할 필요는 없습니다.
이 문서에 표시된 예제 코드는 C#으로 작성되었지만 개념은 모든 .NET 언어에 적용 가능합니다.
문제는 무엇입니까?
기사의 이 섹션에서는 Facade 없이 HttpSessionState 클래스에 직접 액세스할 때 발생하는 문제를 설명합니다. 소개될 수 있는 버그의 종류를 설명하겠습니다.
다음은 세션 상태 변수에 액세스하기 위해 작성된 일반적인 코드를 보여줍니다.
// 세션 변수 저장
세션["some string"] = anyOldObject;
// 세션 변수 읽기
DateTime startDate = (DateTime)Session["StartDate"];
문제는 HttpSessionState가 제공하는 유연한 인터페이스에서 발생합니다. 키는 단지 문자열일 뿐이고 값은 강력한 형식이 아닙니다.
문자열 리터럴을 키로 사용
문자열 리터럴이 키로 사용되는 경우 컴파일러는 키의 문자열 값을 확인하지 않습니다. 간단한 입력 오류로 새 세션 값을 생성하는 것은 쉽습니다.
세션["수신"] = 27;...
세션["수신됨"] = 32;
위 코드에서는 두 개의 별도 세션 값이 저장되었습니다.
이와 같은 대부분의 버그는 단위 테스트를 통해 식별되지만 항상 그런 것은 아닙니다. 값이 예상대로 변경되지 않은 것이 항상 명확하지 않을 수도 있습니다.
상수를 사용하면 이런 종류의 버그를 피할 수 있습니다:
private const string received = "received";...
세션[수신] = 27;...
세션[수신] = 32;
유형 검사 없음
세션 변수에 저장되는 값에 대한 유형 검사는 없습니다. 컴파일러는 저장되는 내용의 정확성을 확인할 수 없습니다.
다음 코드를 고려하십시오.
Session["maxValue"] = 27;...
int maxValue = (int)Session["maxValue"];
다른 곳에서는 다음 코드를 사용하여 값을 업데이트합니다.
세션["maxValue"] = 56.7;
"maxValue" 세션 변수를 maxValue int 변수로 읽는 코드가 다시 실행되면 InvalidCastException이 발생합니다.
이와 같은 대부분의 버그는 단위 테스트를 통해 식별되지만 항상 그런 것은 아닙니다.
실수로 키 재사용
세션 키에 대해 각 페이지에 상수를 정의하더라도 의도치 않게 여러 페이지에서 동일한 키를 사용할 수 있습니다. 다음 예를 고려하십시오.
한 페이지의 코드:
private const string edit = "edit";...
세션[편집] = true;
첫 번째 페이지 뒤에 표시되는 두 번째 페이지의 코드:
private const string edit = "edit";...
if ((bool)세션[편집])
{
...
}
관련 없는 세 번째 페이지의 코드:
private const string edit = "edit";...
세션[편집] = 거짓;
두 번째 페이지가 표시되기 전에 어떤 이유로 세 번째 페이지가 표시되면 값이 예상한 값과 다를 수 있습니다. 코드는 계속 실행될 것이지만 결과는 잘못될 것입니다.
일반적으로 이 버그는 테스트에서 발견되지 않습니다. 버그가 나타나는 것은 사용자가 특정 페이지 탐색 조합(또는 새 브라우저 창 열기)을 수행할 때만 발생합니다.
최악의 경우, 아무도 버그가 나타났음을 알지 못하고 데이터를 의도하지 않은 값으로 수정하게 될 수도 있습니다.
실수로 키 재사용 - 다시
위의 예에서는 동일한 데이터 유형이 세션 변수에 저장되었습니다. 저장되는 항목에 대한 유형 검사가 없기 때문에 호환되지 않는 데이터 유형 문제도 발생할 수 있습니다.
한 페이지의 코드:
Session["FollowUp"] = "true";
두 번째 페이지의 코드:
Session["FollowUp"] = 1;
세 번째 페이지의 코드:
Session["FollowUp"] = true;
버그가 나타나면 InvalidCastException이 발생합니다.
일반적으로 이 버그는 테스트에서 발견되지 않습니다. 버그가 나타나는 것은 사용자가 특정 페이지 탐색 조합(또는 새 브라우저 창 열기)을 수행할 때만 발생합니다.
우리는 무엇을 할 수 있나요?
첫 번째 빠른 수정
우리가 할 수 있는 첫 번째이자 가장 간단한 일은 세션 키에 문자열 리터럴을 사용하지 않도록 하는 것입니다. 항상 상수를 사용하여 간단한 타이핑 실수를 피하십시오.
개인 const 문자열 제한 = "제한";...
세션[제한] = 27;...
세션[제한] = 32;
그러나 상수가 로컬로 정의되면(예: 페이지 수준에서) 의도치 않게 동일한 키를 재사용할 수 있습니다.
더 나은 빠른 수정
각 페이지에서 상수를 정의하는 대신 모든 세션 키 상수를 단일 위치로 그룹화하고 Intellisense에 표시될 문서를 제공하세요. 문서에는 세션 변수가 사용되는 용도가 명확하게 나와 있어야 합니다. 예를 들어 세션 키에 대해서만 클래스를 정의합니다.
public static class SessionKeys
{
/// <요약>
/// 최대 ...
/// </summary>
public const string Limit = "한계";
}
...
세션[SessionKeys.Limit] = 27;
새 세션 변수가 필요한 경우 이미 사용된 이름을 선택하면 SessionKeys 클래스에 상수를 추가할 때 이를 알 수 있습니다. 현재 어떻게 사용되고 있는지 확인하고 다른 키를 사용해야 하는지 결정할 수 있습니다.
그러나 우리는 여전히 데이터 유형의 일관성을 보장하지 않습니다.
훨씬 더 나은 방법 - Facade 사용
애플리케이션의 단일 정적 클래스(파사드) 내에서만 HttpSessionState에 액세스하세요. 페이지 또는 컨트롤의 코드 내에서 Session 속성에 직접 액세스할 수 없으며 Facade 내 이외의 HttpContext.Current.Session에 직접 액세스할 수 없습니다
. 모든 세션 변수는 Facade 클래스의 속성으로 노출됩니다.
이는 모든 세션 키에 대해 단일 클래스를 사용하는 것과 동일한 이점과 다음과 같은 이점을 제공합니다.
세션 변수에 입력되는 내용을 강력하게 입력합니다.
세션 변수가 사용되는 코드에서는 캐스팅할 필요가 없습니다.
세션 변수에 입력되는 내용을 검증하는 속성 설정자의 모든 이점(단순한 유형 이상).
세션 변수에 액세스할 때 속성 게터의 모든 이점. 예를 들어 변수에 처음 액세스할 때 변수를 초기화합니다.
세션 파사드 클래스의 예
다음은 MyApplication이라는 애플리케이션에 대한 Session Facade를 구현하는 예제 클래스입니다.
무너지다
/// <요약>
/// MyApplicationSession은 ASP.NET 세션 개체에 대한 외관을 제공합니다.
/// 세션 변수에 대한 모든 액세스는 이 클래스를 통해 이루어져야 합니다.
/// </summary>
공개 정적 클래스 MyApplicationSession
{
# 지역 비공개 상수
//------------------------------------------------ --------
private const string userAuthorisation = "UserAuthorisation";
private const string teamManagementState = "TeamManagementState";
private const string startDate = "StartDate";
private const string endDate = "EndDate";
//------------------------------------------------ --------
# endregion
# 지역 공용 속성
//------------------------------------------------ --------
/// <요약>
/// 사용자 이름은 현재 사용자의 도메인 이름과 사용자 이름입니다.
/// </summary>
공개 정적 문자열 사용자 이름
{
get { return HttpContext.Current.User.Identity.Name; }
}
/// <요약>
/// UserAuthorisation에는 다음에 대한 인증 정보가 포함되어 있습니다.
/// 현재 사용자.
/// </summary>
공개 정적 UserAuthorisation UserAuthorisation
{
얻다
{
사용자 인증 userAuth
= (UserAuthorisation)HttpContext.Current.Session[userAuthorisation];
// UserAuthorisation이 만료되었는지 확인
만약에 (
userAuth == null ||
(userAuth.Created.AddMinutes(
MyApplication.Settings.Caching.AuthorisationCache.CacheExpiryMinutes))
< 날짜시간.지금
)
{
userAuth = UserAuthorisation.GetUserAuthorisation(사용자 이름);
사용자 인증 = userAuth;
}
userAuth를 반환합니다.
}
비공개 세트
{
HttpContext.Current.Session[userAuthorisation] = 값;
}
}
/// <요약>
/// TeamManagementState는 현재 상태를 저장하는 데 사용됩니다.
/// TeamManagement.aspx 페이지.
/// </summary>
공개 정적 TeamManagementState TeamManagementState
{
얻다
{
(TeamManagementState)HttpContext.Current.Session[teamManagementState]를 반환합니다.
}
세트
{
HttpContext.Current.Session[teamManagementState] = 값;
}
}
/// <요약>
/// StartDate는 레코드를 필터링하는 데 사용되는 가장 빠른 날짜입니다.
/// </summary>
공개 정적 DateTime StartDate
{
얻다
{
if (HttpContext.Current.Session[startDate] == null)
DateTime.MinValue를 반환합니다.
또 다른
return (DateTime)HttpContext.Current.Session[startDate];
}
세트
{
HttpContext.Current.Session[startDate] = 값;
}
}
/// <요약>
/// EndDate는 레코드를 필터링하는 데 사용되는 가장 늦은 날짜입니다.
/// </summary>
공개 정적 DateTime EndDate
{
얻다
{
if (HttpContext.Current.Session[endDate] == null)
DateTime.MaxValue를 반환합니다.
또 다른
return (DateTime)HttpContext.Current.Session[endDate];
}
세트
{
HttpContext.Current.Session[endDate] = 값;
}
}
//------------------------------------------------ --------
# 끝지역
}
이 클래스는 값이 명시적으로 저장되지 않은 경우 기본값을 제공할 수 있는 속성 getter의 사용을 보여줍니다. 예를 들어 StartDate 속성은 DateTime.MinValue를 기본값으로 제공합니다.
UserAuthorisation 속성에 대한 속성 getter는 UserAuthorisation 클래스 인스턴스의 간단한 캐시를 제공하여 세션 변수의 인스턴스가 최신 상태로 유지되도록 합니다. 이 속성은 또한 개인 설정자의 사용을 보여 주므로 세션 변수의 값은 Facade 클래스의 제어 하에서만 설정할 수 있습니다.
Username 속성은 한때 세션 변수로 저장되었지만 더 이상 이 방식으로 저장되지 않는 값을 보여줍니다.
다음 코드는 Facade를 통해 세션 변수에 액세스하는 방법을 보여줍니다. 이 코드에서는 캐스팅을 수행할 필요가 없습니다.
// 세션 변수 저장
MyApplicationSession.StartDate = DateTime.Today.AddDays(-1);
// 세션 변수 읽기
DateTime startDate = MyApplicationSession.StartDate;
추가 혜택
파사드 디자인 패턴의 또 다른 이점은 애플리케이션의 나머지 부분에서 내부 구현을 숨긴다는 것입니다. 아마도 미래에는 기본 제공 ASP.NET HttpSessionState 클래스가 아닌 다른 세션 상태 구현 메커니즘을 사용하기로 결정할 수도 있습니다. 단지 외관의 내부만 변경하면 됩니다. 애플리케이션의 나머지 부분에서는 다른 어떤 것도 변경할 필요가 없습니다.
요약
HttpSessionState에 대한 Facade를 사용하면 세션 변수에 액세스하는 훨씬 더 강력한 방법을 제공합니다. 이는 구현하기가 매우 간단한 기술이지만 큰 이점이 있습니다.