소개
웹 애플리케이션과 같은 상태 비저장 환경에서는 세션 상태 개념을 이해하는 것이 실제로 의미가 없습니다. 그럼에도 불구하고 효과적인 상태 관리는 대부분의 웹 애플리케이션에 꼭 필요한 기능입니다. Microsoft ASP.NET은 다른 많은 서버측 프로그래밍 환경과 마찬가지로 응용 프로그램이 사용자별 및 응용 프로그램별로 영구 데이터를 저장할 수 있도록 하는 추상화 계층을 제공합니다.
웹 애플리케이션의 세션 상태는 애플리케이션이 여러 요청에 걸쳐 캐시하고 검색하는 데이터라는 점에 유의하는 것이 중요합니다. 세션은 사이트에 연결되어 있는 동안 사용자가 보낸 모든 요청을 나타내며, 세션 상태는 세션 중에 사용자가 생성하고 소비하는 지속적인 데이터의 모음입니다. 각 세션의 상태는 서로 독립적이며 사용자 세션이 끝나면 더 이상 존재하지 않습니다.
세션 상태는 HTTP 프로토콜 및 사양을 구성하는 논리적 엔터티와 일치하지 않습니다. 세션은 기존 ASP 및 ASP.NET과 같은 서버 측 개발 환경에서 구축된 추상화 계층입니다. ASP.NET이 세션 상태를 표시하는 방식과 세션 상태가 내부적으로 구현되는 방식은 플랫폼의 인프라에 따라 다릅니다. 따라서 기존 ASP와 ASP.NET은 세션 상태를 완전히 다른 방식으로 구현하며 다음 버전의 ASP.NET에서는 더욱 개선되고 향상될 것으로 예상됩니다.
이 문서에서는 ASP.NET 1.1에서 세션 상태를 구현하는 방법과 관리되는 웹 응용 프로그램에서 세션 상태 관리를 최적화하는 방법을 설명합니다.
ASP.NET 세션 상태 개요
세션 상태는 HTTP 인프라의 일부가 아닙니다. 즉, 각 수신 요청과 세션 상태를 바인딩하는 구조적 구성 요소가 있어야 합니다. 런타임 환경(기존 ASP 또는 ASP.NET)은 Session과 같은 키워드를 허용하고 이를 사용하여 서버에 저장된 데이터 블록을 나타낼 수 있습니다. Session 개체에 대한 호출을 성공적으로 해결하려면 런타임 환경에서 처리 중인 요청의 호출 컨텍스트에 세션 상태를 추가해야 합니다. 이를 수행하는 방법은 플랫폼마다 다르지만 상태 저장 웹 애플리케이션의 기본입니다.
기존 ASP에서 세션 상태는 asp.dll 라이브러리에 포함된 자유 스레드 COM 개체로 구현됩니다. (궁금하시죠? 이 개체의 CLSID는 실제로 D97A6DA0-A865-11cf-83AF-00A0C90C2BD8입니다.) 이 개체는 이름/값 쌍의 모음으로 구성된 데이터를 저장합니다. "이름" 자리 표시자는 정보를 검색하는 데 사용되는 키를 나타내고, "값" 자리 표시자는 세션 상태에 저장된 내용을 나타냅니다. 이름/값 쌍은 각 사용자가 자신이 만든 이름/값 쌍만 볼 수 있도록 세션 ID별로 그룹화됩니다.
ASP.NET에서 세션 상태에 대한 프로그래밍 인터페이스는 기존 ASP와 거의 동일합니다. 그러나 기본 구현은 완전히 다릅니다. 전자는 후자보다 더 유연하고 확장 가능하며 더 강력한 프로그래밍 기능을 갖추고 있습니다. ASP.NET 세션 상태를 자세히 살펴보기 전에 ASP.NET 세션 인프라의 구조적 기능 중 일부를 간략하게 살펴보겠습니다.
ASP.NET에서는 들어오는 모든 HTTP 요청이 HTTP 모듈을 통해 파이프됩니다. 각 모듈은 요청에 의해 전달되는 대량의 정보를 필터링하고 수정할 수 있습니다. 각 요청과 관련된 정보를 "호출 컨텍스트"라고 하며 프로그래밍에서 HttpContext 개체로 표시됩니다. 요청 컨텍스트가 제공하는 항목 컬렉션은 단지 데이터 컨테이너일 뿐이지만 요청 컨텍스트를 상태 정보의 또 다른 컨테이너로 생각해서는 안 됩니다. HttpContext 개체는 요청을 처리하는 데 필요한 시간 외에 수명이 제한되어 있다는 점에서 다른 모든 상태 개체(예: 세션, 응용 프로그램 및 캐시)와 다릅니다. 요청이 일련의 등록된 HTTP 모듈을 통과할 때 해당 HttpContext 개체에는 상태 개체에 대한 참조가 포함됩니다. 요청이 최종적으로 처리될 수 있게 되면 관련 호출 컨텍스트가 특정 세션(Session) 및 전역 상태 개체(Application 및 Cache)에 바인딩됩니다.
각 사용자의 세션 상태 설정을 담당하는 HTTP 모듈은 SessionStateModule입니다. 이 모듈의 구조는 ASP.NET 응용 프로그램에 대한 많은 세션 상태 관련 서비스를 제공하는 IHttpModule 인터페이스를 기반으로 설계되었습니다. 세션 ID 생성, 쿠키 없는 세션 관리, 외부 상태 제공자로부터 세션 데이터 검색, 요청의 호출 컨텍스트에 데이터 바인딩이 포함됩니다.
HTTP 모듈은 세션 데이터를 내부적으로 저장하지 않습니다. 세션 상태는 항상 "상태 공급자"라는 외부 구성 요소에 저장됩니다. 상태 공급자는 세션 상태 데이터를 완전히 캡슐화하고 IStateClientManager 인터페이스의 메서드를 통해 다른 부분과 통신합니다. 세션 상태 HTTP 모듈은 이 인터페이스의 메서드를 호출하여 세션 상태를 읽고 저장합니다. ASP.NET 1.1은 표 1에 표시된 것처럼 세 가지 상태 공급자를 지원합니다.
표 1: 상태 클라이언트 공급자
공급자 설명
InProc 세션 값은 ASP.NET 작업자 프로세스(Microsoft® Windows Server® 2003의 aspnet_wp.exe 또는 w3wp.exe) 메모리에서 활성 개체로 유지됩니다. 이것이 기본 옵션입니다.
StateServer 세션 값은 직렬화되어 별도의 프로세스(aspnet_state.exe)로 메모리에 저장됩니다. 이 프로세스는 다른 컴퓨터에서도 실행될 수 있습니다.
SQLServer 세션 값은 직렬화되어 Microsoft® SQL Server® 테이블에 저장됩니다. SQL Server 인스턴스는 로컬 또는 원격으로 실행될 수 있습니다.
세션 상태 HTTP 모듈은 web.config 파일의
위 코드는 실제로 로컬 메모리의 HTTP 모듈에 의해 생성된 세션 값에 액세스합니다. 특정 상태 제공자로부터 데이터를 읽습니다(그림 1 참조). 다른 페이지도 동기적으로 세션 상태에 액세스하려고 하면 어떻게 되나요? 이 경우 현재 요청은 일관성이 없거나 오래된 데이터 처리를 중지할 수 있습니다. 이를 방지하기 위해 세션 상태 모듈은 판독기/작성기 잠금 메커니즘과 상태 값에 대한 대기열 액세스를 구현합니다. 세션 상태에 대한 쓰기 권한이 있는 페이지는 요청이 종료될 때까지 해당 세션에 대한 작성자 잠금을 유지합니다. 페이지는 @Page 지시문의 EnableSessionState 속성을 true로 설정하여 세션 상태에 대한 쓰기 권한을 요청할 수 있습니다. (이것이 기본 설정입니다). 그러나 예를 들어 EnableSessionState 속성이 ReadOnly로 설정된 경우 페이지는 세션 상태에 대한 읽기 전용 액세스 권한을 가질 수도 있습니다. 이 경우 모듈은 해당 페이지에 대한 요청이 끝날 때까지 해당 세션에 대한 판독기 잠금을 유지합니다. 결과적으로 동시 읽기가 발생합니다. 페이지 요청이 판독기 잠금을 설정하는 경우 동일한 세션의 다른 동시 요청은 세션 상태를 업데이트할 수 없지만 최소한 읽을 수는 있습니다. 즉, 읽기 전용 요청이 현재 세션에 대해 처리되고 있는 경우 보류 중인 읽기 전용 요청은 전체 액세스가 필요한 요청보다 우선순위가 높습니다. 페이지 요청이 세션 상태에 대한 작성자 잠금을 설정하면 콘텐츠를 읽거나 쓰려는지 여부에 관계없이 다른 모든 페이지가 차단됩니다. 예를 들어, 두 프레임이 동시에 세션에 쓰려고 하면 한 프레임은 쓰기 전에 다른 프레임이 완료될 때까지 기다려야 합니다. 상태 공급자 비교 기본적으로 ASP.NET 응용 프로그램은 작업자 프로세스의 메모리, 특히 Cache 개체의 전용 슬롯에 세션 상태를 저장합니다. InProc 모드를 선택하면 세션 상태가 캐시 개체 내의 슬롯에 저장됩니다. 이 슬롯은 전용 슬롯으로 표시되어 프로그래밍 방식으로 액세스할 수 없습니다. 즉, ASP.NET 데이터 캐시의 모든 항목을 열거하면 지정된 세션 상태와 유사한 개체가 반환되지 않습니다. 캐시 개체는 개인용 슬롯과 공용 슬롯이라는 두 가지 유형의 슬롯을 제공합니다. 프로그래머는 공개 슬롯을 추가하고 처리할 수 있지만 비공개 슬롯은 시스템(특히 system.web 부분에 정의된 클래스)에서만 사용할 수 있습니다. 각 활성 세션의 상태는 캐시의 전용 슬롯을 차지합니다. 슬롯의 이름은 세션 ID를 기반으로 지정되며 해당 값은 SessionStateItem이라는 선언되지 않은 내부 클래스의 인스턴스입니다. InProc 상태 공급자는 세션 ID를 가져오고 캐시에서 해당 요소를 검색합니다. 그런 다음 SessionStateItem 개체의 내용이 HttpSessionState 사전 개체에 입력되고 애플리케이션이 Session 속성을 통해 액세스합니다. ASP.NET 1.0에는 Cache 개체의 전용 슬롯을 프로그래밍 방식으로 열거 가능하게 만드는 버그가 있습니다. ASP.NET 1.0에서 다음 코드를 실행하면 현재 활성화된 각 세션 상태에 포함된 개체에 해당하는 항목을 열거할 수 있습니다. foreach(캐시의 DictionaryEntry 요소) 이 버그는 ASP.NET 1.1에서 해결되었으며 캐시된 콘텐츠를 열거할 때 시스템 슬롯이 더 이상 나열되지 않습니다. InProc은 아마도 지금까지 가장 빠른 액세스 옵션일 것입니다. 그러나 세션에 더 많은 데이터를 저장할수록 웹 서버가 더 많은 메모리를 소비하므로 잠재적으로 성능 저하 위험이 높아진다는 점을 명심하세요. Out-of-Process 솔루션을 사용하려는 경우 직렬화 및 역직렬화로 인해 발생할 수 있는 영향을 신중하게 고려해야 합니다. Out-of-Process 솔루션은 Windows NT 서비스(aspnet_state.exe) 또는 SQL Server 테이블을 사용하여 세션 값을 저장합니다. 따라서 세션 상태는 ASP.NET 작업자 프로세스 외부에 유지되며 세션 상태와 실제 저장 매체 간에 직렬화 및 역직렬화를 수행하려면 추가 코드 계층이 필요합니다. 이는 요청이 처리될 때마다 발생하며 최고 수준으로 최적화되어야 합니다. 세션 데이터를 외부 리포지토리에서 로컬 세션 사전으로 복사해야 하기 때문에 요청으로 인해 15%(out-of-process)에서 25%(SQL Server)까지 성능이 저하됩니다. 이는 대략적인 추정치일 뿐이지만 최소 영향에 가까워야 하며 최대 영향은 이보다 훨씬 높을 것입니다. 실제로 이 추정치는 세션 상태에 실제로 저장된 유형의 복잡성을 완전히 고려하지 않습니다. Out-of-Process 스토리지 시나리오에서는 세션 상태가 더 오래 유지되므로 Microsoft® IIS(인터넷 정보 서비스) 및 ASP.NET 오류로부터 보호하므로 애플리케이션이 더욱 강력해집니다. 세션 상태를 애플리케이션에서 분리함으로써 기존 애플리케이션을 웹 팜 및 웹 가든 아키텍처로 보다 쉽게 확장할 수도 있습니다. 또한 세션 상태는 외부 프로세스에 저장되므로 프로세스 루프로 인한 주기적인 데이터 손실 위험이 본질적으로 제거됩니다. Windows NT 서비스를 사용하는 방법은 다음과 같습니다. 위에서 언급한 것처럼 NT 서비스는 aspnet_state.exe라는 프로세스로, 일반적으로 C:WINNTMicrosoft.NETFrameworkv1.1.4322 폴더에 있습니다. 실제 디렉터리는 실제로 실행 중인 Microsoft® .NET Framework 버전에 따라 다릅니다. 상태 서버를 사용하기 전에 세션 저장 장치로 사용되는 로컬 또는 원격 컴퓨터에서 서비스가 준비되어 실행되고 있는지 확인해야 합니다. 상태 서비스는 ASP.NET의 일부이며 ASP.NET과 함께 설치되므로 추가 설치 관리자를 실행할 필요가 없습니다. 기본적으로 상태 서비스는 실행되고 있지 않으며 수동으로 시작해야 합니다. ASP.NET 응용 프로그램은 로드된 후 즉시 상태 서버에 대한 연결 설정을 시도합니다. 따라서 서비스가 준비되어 실행 중이어야 합니다. 그렇지 않으면 HTTP 예외가 발생합니다. 다음 이미지는 서비스의 속성 대화 상자를 보여줍니다. ASP.NET 응용 프로그램은 세션 상태 서비스가 있는 컴퓨터의 TCP/IP 주소를 지정해야 합니다. 응용 프로그램의 web.config 파일에 다음 설정을 입력해야 합니다. <구성>; stateConnectionString 속성에는 컴퓨터의 IP 주소와 데이터 교환에 사용되는 포트가 포함됩니다. 기본 컴퓨터 주소는 127.0.0.1(localhost)이고 기본 포트는 42424입니다. 이름으로 컴퓨터를 나타낼 수도 있습니다. 로컬 또는 원격 컴퓨터를 사용하는 것은 코드에 완전히 투명합니다. 이름에는 ASCII가 아닌 문자를 사용할 수 없으며 포트 번호는 필수입니다. Out-of-Process 세션 저장소를 사용하는 경우 세션 상태는 여전히 존재하며 ASP.NET 작업자 프로세스에 어떤 일이 발생하는지에 관계없이 나중에 사용할 수 있습니다. 서비스가 중단된 경우 데이터는 그대로 유지되며, 서비스 복구 시 자동으로 검색됩니다. 그러나 상태 공급자 서비스가 중지되거나 실패하면 데이터가 손실됩니다. 애플리케이션을 강력하게 만들려면 StateServer 모드 대신 SQLServer 모드를 사용하세요. <구성>; sqlConnectionString 속성을 통해 연결 문자열을 지정할 수 있습니다. 속성 문자열에는 사용자 ID, 비밀번호 및 서버 이름이 포함되어야 합니다. 이 정보는 기본적으로 고정된 이름으로 지정되므로 데이터베이스 및 초기 카탈로그와 같은 태그를 포함할 수 없습니다. 사용자 ID와 비밀번호를 통합보안 설정으로 대체할 수 있습니다. 데이터베이스를 만드는 방법은 무엇입니까? ASP.NET은 데이터베이스 환경을 구성하기 위한 두 쌍의 스크립트를 제공합니다. 첫 번째 스크립트 쌍의 이름은 InstallSqlState.sql 및 UninstallSqlState.sql이며 Session State NT 서비스와 동일한 폴더에 있습니다. ASPState라는 데이터베이스와 여러 저장 프로시저를 만듭니다. 그러나 데이터는 SQL Server 임시 저장 영역 TempDB 데이터베이스에 저장됩니다. 즉, SQL Server 컴퓨터를 다시 시작하면 세션 데이터가 손실됩니다. 이 제한 사항을 해결하려면 두 번째 스크립트 쌍을 사용하십시오. 두 번째 스크립트 쌍의 이름은 InstallPersistSqlState.sql 및 UninstallPersistSqlState.sql입니다. 이 경우 ASPState 데이터베이스가 생성되지만 테이블은 동일한 데이터베이스에 생성되고 지속됩니다. 세션에 대한 SQL Server 지원을 설치하면 세션 상태 데이터베이스에서 만료된 세션을 삭제하는 작업도 생성됩니다. 작업 이름은 ASPState_Job_DeleteExpiredSessions이며 항상 실행 중입니다. 이 작업이 제대로 작동하려면 SQLServerAgent 서비스가 실행되고 있어야 합니다. 어떤 모드를 선택하더라도 세션 상태 작업이 코딩되는 방식은 변경되지 않습니다. 언제든지 Session 속성에 대한 작업을 수행하고 정상적으로 값을 읽고 쓸 수 있습니다. 동작의 모든 차이점은 낮은 추상화 수준에서 처리됩니다. 상태 직렬화는 아마도 세션 모드 간의 가장 중요한 차이점일 것입니다. 상태 직렬화 및 역직렬화 in-process 모드를 사용할 때 개체는 해당 클래스의 활성 인스턴스로 세션 상태에 저장됩니다. 실제 직렬화 및 역직렬화가 발생하지 않으면 세션에서 생성한 모든 개체(직렬화할 수 없는 개체 및 COM 개체 포함)를 실제로 저장할 수 있으며 해당 개체에 액세스하는 데 비용이 많이 들지 않는다는 의미입니다. 프로세스 외부 상태 공급자를 선택하는 경우에는 또 다른 이야기입니다. Out-of-Process 아키텍처에서는 세션 값이 로컬 저장소 미디어(외부 AppDomain 데이터베이스)에서 요청을 처리하는 AppDomain의 메모리로 복사됩니다. 이 작업을 수행하려면 직렬화/역직렬화 계층이 필요하며 프로세스 외부 상태 공급자의 주요 비용 중 하나를 나타냅니다. 이 상황이 코드에 미치는 주요 영향은 직렬화 가능한 객체만 세션 사전에 저장할 수 있다는 것입니다. ASP.NET은 관련된 데이터 유형에 따라 두 가지 방법을 사용하여 데이터를 직렬화하고 역직렬화합니다. 기본 유형의 경우 ASP.NET은 개체 및 사용자 정의 클래스를 포함한 다른 유형에 대해 최적화된 내부 직렬 변환기를 사용하고 ASP.NET은 .NET 이진 포맷터를 사용합니다. 기본 유형에는 문자열, 날짜/시간, 부울 값, 바이트, 문자 및 모든 숫자 유형이 포함됩니다. 이러한 유형의 경우 맞춤형 직렬 변환기를 사용하는 것이 기본 일반 .NET 바이너리 포맷터를 사용하는 것보다 빠릅니다. 최적화된 직렬 변환기는 공개적으로 출시되거나 문서화되지 않습니다. 이는 단지 바이너리 리더/라이터이며 간단하지만 효과적인 스토리지 아키텍처를 사용합니다. 직렬 변환기는 BinaryWriter 클래스를 사용하여 유형의 바이트 표현을 쓴 다음 해당 유형에 해당하는 값의 바이트 표현을 씁니다. 직렬화된 바이트를 읽을 때 클래스는 먼저 바이트를 추출하고 읽을 데이터 유형을 감지한 다음 BinaryReader 클래스에서 유형별 ReadXxx 메서드를 호출합니다. 부울 및 숫자 유형의 크기는 잘 알려져 있지만 문자열의 경우에는 그렇지 않습니다. 기본 데이터 스트림에서 문자열에는 항상 고정 길이(한 번에 기록되는 7비트 정수 코드)가 접두사로 붙으며 판독기는 이 사실을 사용하여 문자열의 올바른 크기를 결정합니다. 날짜 값은 날짜를 구성하는 토큰의 총 개수만 적어서 저장됩니다. 따라서 세션을 직렬화하려면 날짜 유형이 Int64여야 합니다. 포함하는 클래스가 직렬화 가능으로 표시되어 있는 한 BinaryFormatter 클래스를 사용하여 더 복잡한 개체(사용자 정의 개체 포함)에 대해 직렬화 작업을 수행할 수 있습니다. 기본이 아닌 모든 유형은 동일한 유형 ID로 식별되며 기본 유형과 동일한 데이터 스트림에 저장됩니다. 전반적으로 직렬화 작업으로 인해 성능이 15%~25% 저하될 수 있습니다. 다만, 이는 기본형을 사용한다는 가정하에 대략적인 추정치임을 참고하시기 바랍니다. 사용되는 유형이 복잡할수록 오버헤드가 커집니다. 효율적인 세션 데이터 저장은 기본 유형을 광범위하게 사용하지 않으면 구현하기 어렵습니다. 따라서 적어도 이론적으로는 세 개의 세션 슬롯을 사용하여 개체의 세 가지 다른 문자열 속성을 저장하는 것이 전체 개체를 직렬화하는 것보다 낫습니다. 하지만 직렬화하려는 개체에 100개의 속성이 포함되어 있으면 어떻게 될까요? 100개의 슬롯을 사용하시겠습니까, 아니면 하나만 사용하시겠습니까? 대부분의 경우 더 나은 접근 방식은 복잡한 유형을 여러 개의 간단한 유형으로 변환하는 것입니다. 이 접근 방식은 유형 변환기를 기반으로 합니다. "유형 변환기"는 유형의 주요 속성을 문자열 컬렉션으로 반환하는 경량 직렬 변환기입니다. 유형 변환기는 특성을 사용하여 기본 클래스에 바인딩되는 외부 클래스입니다. 어떤 속성이 저장되고 어떻게 저장되는지 결정하는 것은 유형 작성자에게 달려 있습니다. 유형 변환기는 ViewState 저장에도 유용하며 이진 포맷터보다 더 효율적인 세션 저장 방법을 나타냅니다. 세션 수명 주기 ASP.NET 세션 관리에 대한 중요한 점은 세션 상태 개체의 수명 주기는 첫 번째 항목이 메모리 내 사전에 추가될 때만 시작된다는 것입니다. ASP.NET 세션은 다음 코드 조각이 실행된 후에만 시작된 것으로 간주됩니다. Session["MySlot"] = "Some data"; 세션 사전에는 일반적으로 개체 유형이 포함되어 있습니다. 데이터를 거꾸로 읽으려면 반환된 값을 보다 구체적인 유형으로 변환해야 합니다. string data = (string) Session["MySlot"]; 페이지가 세션에 데이터를 저장하면 해당 값은 HttpSessionState 클래스에 포함된 특별히 제작된 사전 클래스에 로드됩니다. 현재 처리된 요청이 완료되면 사전의 내용이 상태 공급자에 로드됩니다. 데이터가 프로그래밍 방식으로 사전에 저장되지 않아 세션 상태가 비어 있는 경우 데이터는 저장 매체에 직렬화되지 않으며 더 중요한 것은 ASP.NET 캐시, SQL Server 또는 NT 상태 서비스 생성에서 제공되지 않습니다. 현재 세션을 추적하기 위한 슬롯입니다. 이는 성능상의 이유이지만 세션 ID가 처리되는 방식에 중요한 영향을 미칩니다. 일부 데이터가 세션 사전에 저장될 때까지 각 요청에 대해 새 세션 ID가 생성됩니다. 세션 상태를 처리 중인 요청과 연결해야 하는 경우 HTTP 모듈은 세션 ID를 검색하고(시작 요청이 아닌 경우) 구성된 상태 공급자에서 이를 찾습니다. 데이터가 반환되지 않으면 HTTP 모듈은 요청에 대한 새 세션 ID를 생성합니다. 이는 다음 페이지에서 쉽게 테스트할 수 있습니다. <%@ Page Language="C#" Trace="true" %>;
{
Response.Write(elem.Key + ": " + elem.Value.ToString());
}
그림 2: ASP.NET 상태 서버 속성 대화 상자
<시스템.웹>;
<세션상태
모드="StateServer"
stateConnectionString="tcpip=expoware:42424" />;
;
;
<시스템.웹>;
<세션상태
모드="SQL서버"
sqlConnectionString="서버=127.0.0.1;uid=<사용자 ID>;;pwd=<비밀번호>;;"
;
;