ASP

ASP에서 컴포넌트 다루기: 그 필요성과 비주얼 베이직 사용 컴포넌트 구현

디버그정 2008. 9. 10. 11:34

http://www.taeyo.pe.kr/Lecture/22_COM/COMP_likeJazz_01.htm

LikeJazz (likejazz@korea.com)


태요님의 게시판 강좌에 발맞춰 COM+ 구현방법을 실제 게시판 적용사례와 함께 강의해보고자 한다 .

우선 이 글은 asp 중급개발자 이상을 타겟으로 하며 게시판 리스트페이지를 예제로 하여 COM+ 컴포넌트를 구현해보도록 하겠다 .

약간의 객체지향방법론(UML), VB 로 COM+ 구현에 대한 이론적인설명이 곁들여질 예정이다 . 물론 상세한 자료는 이미 MSDN 과 Wrox 사의 책들로 많이 공개되어있으니 이론적인 부분보다는 예제를 제시하며 실무위주로 설명을 하도록 하겠다 .

자 그럼 일단 제가 예전에 만들어두었던 게시판의 리스트 페이지를 보도록 합시다 .

<%

' Title   : List.asp
' Feature : 리스트를 처리하고 페이지 네비게이션을 표시한다 .
' Purpose : 게시판의 리스트페이지
' Author  : 2001-05-17 by LikeJAzz

' 변수선언을 강요한다 .
Option Explicit

' 사용할 변수선언
Dim intPage, intPrevNext, intRow, intStartPage, intTotalPage, I
Dim strListPageName

' 사용할 객체의 선언 ASP 는 Variant 형만 선언할수있다 .
Dim oConn, oCmd, oRs

' 표시할 페이지를 Request 한다 .
intPage = Cint(Request.QueryString ("intPage"))
If intPage = 0 Then intPage = 1

' 현재 페이지 이름
strListPageName = "List.asp"
' 페이지 Navigation 표시수
intPrevNext = 10
' 한페이당 출력 글수
intRow = 10

' 시작 페이지 계산
intStartPage = Int(intPage/intPrevNext) + 1
If intPage MOD intPrevNext = 0 Then intStartPage = intStartPage - 1
intStartPage = (intStartPage -1) * intPrevNext + 1

' Connection 객체 생성
Set oConn = Server.CreateObject ("ADODB.Connection")
' ODBC 로 연결하여 오픈한다 .
oConn.Open "dsn=test;uid=sa;pwd="

' 리스트를 뽑아올 StoredProcedure 사용을 위해 Command 객체 생성
Set oCmd = New ADODB.Command
With oCmd
.ActiveConnection = oConn

.CommandType = adCmdStoredProc
.CommandText = "SP_BOARD_LIST"
.Parameters.Append .CreateParameter("PAGE", adInteger, adParamInput, , intPage)
.Parameters.Append .CreateParameter("ROW_COUNT", adInteger, adParamInput, , argRow)
.Parameters.Append .CreateParameter("TOTAL_PAGE", adInteger, adParamOutput, , 0)

' 리턴변수값을 받기위해 실행한다 . (페이징을 위해 필요) .Execute

intTotalPage = oCmd.Parameters(0).Value
' 리턴되는 레코드셋을 받기위해 한번 더 실행한다 .
Set oRs = .Execute
End With Set oCmd = Nothing ' 리스트 표시 시작 . 레코드셋이 EOF 가 될때까지 반복되며 리스트를 출력해준다 . Do Until oRs.EOF %> // 리스트 내용이 주욱 표시되는 부분 . <% oRs.MoveNext Loop %> // 여러 HTML 요소들이 표시되는부분 . <% ' Page-Navigation 이 표시되는 부분 . If intTotalPage > intPrevNext Then If intStartPage = 1 Then Response.Write " ◀ " Else Response.Write " <a href=" & strListPageName & _ "?intPage=" & intStartPage - 1 & _ "> ◀ </a> " End If For I = intStartPage To intStartPage + intPrevNext - 1 If I > intTotalPage Then Exit For Else If I = intPage Then Response.Write " <font color=red>" & I & "</font> " Else Response.Write " <a href=" & strListPageName & _ "?intPage=" & I & _ "> " & I & " </a> " End If End If Next If (intStartPage + intPrevNext > intTotalPage) Then Response.Write " ▶ " Else Response.Write " <a href=" & strListPageName & _ "?intPage=" & I & _ "> ▶ </a> " End If Else Response.Write " ◀ " For I = intStartPage To intTotalPage If I = intPage Then Response.Write "<font color=red> " & I & " </font>" Else Response.Write " <a href=" & strListPageName & _ "?intPage=" & I & _ "> " & I & " </a> " End If Next Response.Write " ▶ " End If %>

위의 asp 소스를 Visual Basic 으로 COM+ 컴포넌트를 작성하여 묶어보도록 하겠다 .

소스에 대한 설명은 위 처럼 그때그때 직접 주석으로 표시하도록 하겠다 . 하지만 실제로 저처럼 무리하게 주석을 표시할 필요는 없다 . 선언부와 로직처리부분, 표현되는부분 정도만 분리해서 표시하면 될것이다 .

간단히 위의 소스에 대해 설명을 하자면 앞부분은 리스트 표현을 위한 선언부와 약간의게시판 로직 . 아래부분은 페이지 네비게이션이 표시되는 부분이다 .

네비게이션은 웹에서 그림1 처럼 표현된다 .

그림1

일반적인 asp 페이지의 경우 html 태그들 사이에 asp 코드가 위치하게 된다 . 게시판처럼 조금은 복잡한형태 혹은 더 복잡한 로직들이 존재하는 페이지라면 소스 자체가 상당히 지저분해지고 디자이너와의 공동작업시 실수로 asp 코드들을 건드리는 상황도 발생한다 . 그래서 우리는 저 로직들을 철저히 숨기고 될수있는한 깔끔한 asp 페이지를 만들어보도록 하겠다 .

자 그럼 위의 ASP 코드를 Componet 로 묶기전에 약간의 부연설명을 곁들여보겠다 .

크게 Component 사용형태는 2가지로 나뉜다 .
1. 값을 전달받고 로직을 처리후 다시 결과값을 리턴해주는 형태 . (일반적인 형태)
2. Component 에서 로직처리후 ASP 인터페이스를 이용하여 직접 출력까지하는 형태 . (ISAPI 같은 형태)

방법론에 따르면 비지니스로직과 프리젠테이션로직을 완전히 분리해야 한다. 즉 Component 는 말그대로 로직만을 처리하고 그 결과값을 리턴해주는 1번 방법이 정석이다 .

하지만 실제로 그렇게 구현을 하다보면 오히려 프리젠테이션 로직이 더 복잡해지는 경우가 있다 . GetRows 로 레코드셋을 배열로 리턴해오면 가로세로가 뒤바뀌며 그 처리를 숫자로만 해야하므로 상당히 헷갈리게 된다 . 또한 위의 Page Navigation 같은 부분은 Component 로 묶을수 없으므로 실제로 COM+ 을 사용하는 잇점을 살리지 못한다 .

그래서 우리는 로직과 밀접한 관련이 있는 출력부분까지 COM+ 에서 처리하게끔 2번 방법으로 작성해보고자 한다. 여기서 Microsoft 에서 제시하는 Component 를 작성함으로서 얻게되는 잇점을 잠깐 살펴보겠다 .

1. Perfomance and scalability : 이진형태의 코드이므로 interpreting 에 비해 더 빠르고 더 적은 메모리를 요구한다 .
2. Business logic encapsulation : 코드를 객체지향화 해주며 디버깅, 배포, 업그레이드를 더 쉽게 해준다 . 또한 여러 애플리케이션간에 재사용이 가능하다 .
3. Separation of data and user interface (UI) : 비지니스 로직과 데이타를 건들지 않고 UI 를 쉽게 개선할수있다 . 빠르게 급변하는 Web 애플리케이션에서 중요한 부분이다 .

기술을 만든 벤더에서 제시하는 내용이므로 너무 장점만을 부각시키려 하고 있다 . 하지만 실제로 Component 를 구현함으로 인해 여러가지 단점도 발생한다 .

프리젠테이션 로직을 쉽게 개선할수있다고 하지만 만약 비지니스로직이 변경될경우 (+1 을 계산하는 로직인데 +2 를 해야할경우) dll 을 이진호환성으로 재컴파일 해야한다 . IIS 를 중지시키고 구성요소서비스에서 해당 COM+ 애플리케이션을 시스템종료 시킨후 해당 dll 파일을 교체하고 다시 IIS 를 되살려야 한다 .

로직이 변경될때마다 이 작업을 반복해야한다는건 상당히 번거롭다 . 무정지시스템을 구현해야하는 웹서비스라면 치명적이다 .

이외에도 자칫 잘못하다가는 Versioning 문제가 발생한다 . 한때 악명높던 DLL Hell 을 기억하시는지 ?

그래서 Wrox 의 Professional Active Server Pages 3 에서언급한 컴포넌트의 단점에 개인적인 사견을 덧붙여 아래와 같이 정리해 보았다 .

1. 컴포넌트는 디버깅이 더 힘들다 . : 스크립트 디버깅이 불가능하다 . 디버깅을 위해 WinDBG 등의 프로그램을 사용한다면 배보다 배꼽이 더 큰 경우가 아닐까 ?

2. 컴포넌트는 수정하기 더 힘들다 . : asp 페이지는 수정후 단순히 저장만 하면 바로 적용되지만 컴포넌트는 서비스를 내린후 재 컴파일 과정을 거쳐야 한다 . 이는 편집-실행-디버그 사이클을 길게 만든다 .

3. 어설프게 개발된 컴포넌트는 다운된다 . : 버그가 많은 컴포넌트는 asp 애플리케이션을 다운시킬수 있다 . 전적으로 스크립트와 시스템 컴포넌트로 구성되어있는 asp 애플리케이션(즉, VB 수준의 컴포넌트)은 거의 다운되지 않는다 . 저자는 asp 애플리케이션을 High-isolated (격리됨) 으로 설정하고 컴포넌트를 라이브러리 패키지로서 실행시킬것을 권장한다 .

4. 어설프게 개발된 컴포넌트는 Memory Leakage (메모리누수) 를 발생시킨다 . : 자원을 제대로 반환해주지 않는다면 메모리를 비롯한 다른 자원들을 누수시킬수 있다 . 이런 현상이 오래 지속되면 컴포넌트는 웹서버를 사용 불가능 상태로 만들 수도 있다 . (일반적으로 VB 수준의 ADO 로 구성된 컴포넌트에서는 이런일이 발생하지 않는다 .)

5. 어설프게 개발된 컴포넌트는 높은 스트레스를 받는 상황에서 견뎌내지 못한다 . : WAST(Web Application Stress Tool)로 스트레스 테스트를 거칠것을 권장한다 . 특히 멀티프로세서 시스템(2cpu 이상)에서 Dead-Lock 과 Access-Violation 은 주의해야 한다 .

6. 많은 컴포넌트들은 스레드 연결성을 가진다 . : 컴포넌트가 Session 이나 Application 객체안에 저장될때 잘 작동하지 않는다 . (물론 잘 작동한다해도 이는 추천하지 않는 행동이다 .) 특히 VB, MFC 로 작성된 컴포넌트가 그렇다 . VB 는 아직 Neutral 이나 Both-threaded 를 지원하지 않는다 .

7. 컴포넌트는 종종 멀티프로세서 시스템들로 확장되지 않는다 . : Lock 을걸고 Thread-safe 로 작동하므로 단일프로세서(1 cpu)에서는 잘 작동하지만 멀티프로세서 시스템(2 cpu 이상)에서는 Serializable 을 발생시킬수 있다 .

8. 그외 많은 페이지들이 컴포넌트로 변환시킬만한 가치를 가지지 않는다 . : 작은 페이지나 어쩌다 한번 실행되는 페이지들은 컴포넌트로 재가공할 필요가 없다 . 어짜피 그들을 컴포넌트로 변환시켜도 시스템의 성능이 확연하게 개선되지 않는다 . 로그를 분석후 어떤 페이지를 컴포넌트로 변환시킬지 찾아보라 .


대략적인 법칙 : 적어도 100줄 이상의 스크립트가 있으며, 그 스크립트안에 몇개의 큰 순환 루프들이 들어있지 않는 한 그 페이지를 컴포넌트로 변환시킬 필요는 없다 .

위에 언급한 것들 중에서 최소한 Visual Basic 으로 컴포넌트를 작성하는 이 글에서 고려해야 할 사항은 1번, 2번, 8번 정도가 되겠다 .

하지만 4번의 경우 객체를 제때 반환해주는것은 조은습관이며 차후 Visual C++ 의 ATL 을 사용해 저수준의 컴포넌트를 작성할때에도 많은 도움이 된다 .

또한 6번의 경우 Session, Application 에 컴포넌트 객체를 담아두는것은 추천하지 않는다 . (그렇게 구현해야만 하는 경우도 존재하지 않는다 .) 물론 FTA(Free-Threaded Apartment), NTA(Neutral-Threaded Apartment) 는 현재의 Visual Basic 에선 구현할수도 없다 .

대략적인 법칙에 언급된것처럼 컴포넌트화 해야하는 경우를 추려본다면 10만 이상의 트래픽을 지닌 사이트에서 로그인, 게시판, 쇼핑카트 정도에 구현하는것을 추천한다 . 성능개선 효과를 얻을수 있을것이다 .

보통 게시판은 로직 하나로 인자값만 달리하여 다양한형태로 구현해내게 되는데 아이러브스쿨같이
글수가 1000만을 넘어가고 사용자의 액세스가 빈번한 경우(아마 트래픽의 대부분일듯)라면 게시판을 컴포넌트로 작성하게 된다면 상당한 효과를 얻을수 있을것이다 . (아마도 그렇게 작성했을 것이다 .)

물론 위의 글은 UML 방법론을 전혀 고려하지 않은 철저히 퍼포먼스 위주로 언급한 글이다 .

개인적인 의견은 아직은 UML 을 적용한다고 뚜렷한 이득을 볼수없으며 오히려 디버깅 사이클을 더 길게 만들어 유지보수에 더 곤란을 겪을것이라 생각한다 . 물론 모델링이란 측면에서 본다면 충분히 가치있는 일이지만 퍼포먼스측면에서 본다면 아직이다라는 소리다 . 물론 COM+ 의 성능은 지속적으로 개선되고 있으며 .NET 이후에 객체지향방법론은 확실히 자리 잡을것이다 .

하지만 현재로서는 클라이언트나 개발자에게나 가장 중요한것은 퍼포먼스 이기에 주로 퍼포먼스 위주로 언급을 하겠다 .

사설이 길었다 . 코드 한줄 안보여주고 설명으로만 때우려는 사람을 나역시도 무척 싫어한다 . ^^; 하지만 기초와 이론은 중요한법 . 탄탄한 기본기위에 고난이도의 기술을 구현할수있다는 사실을 명심하자 .

자 그럼 직접 위에 언급한 소스를 가지고 Component 를 구현해보도록 하겠다 . 비베로 Component 를 만드는 방법에 대해서는 안다는 가정하게 진행하도록 하겠다 . 상세한 자료는 태요님강좌와 데브피아등에서 많은 정보를 얻을수있을것이다 .

우선 VB 컴포넌트 소스이다 .
프로젝트명은 bc_Board 로 하고 클래스명은 List 로 하겠다 . 그대로 클래스모듈에 바로 코딩하면 된다 .
SELECT 만 할것이므로 트랜잭션은 1. No Required 로 지정한다 .


' Title : 게시판 리스트 컴포넌트
' Feature : 리스트를 처리하고 페이지네비게이션을 표시한다 .
' Author : 2001-05-21 by LikeJAzz

' 참조해야할것 :
' 1. Microsoft Active Server Pages Object Library 를 참조한다 .
' 2. Microsoft ActiveX Data Object 2.5 Library (2.6도 당연히 무방)를 참조한다 .
' 3. COM+ Services Type Library 를 참조한다 .

' 변수선언 강요
Option Explicit
' ObjectControl 을 사용한다 . Activate, Deactivate, CanBePooled 등의 이벤트를 사용할수있다
Implements ObjectControl Private oContext As ObjectContext
' 사용할 ODBC 명을 상수로 정의한다 .
' 좀더 유동적인 처리를 위해 값을 레지스트리에 저장하거나
' 좀더 빠른처리를 위해 OLE-DB를 사용해도 무방하다 .

Const CONNECT_STRING = "dsn=test;uid=sa;pwd="
Dim oConn As ADODB.Connection
' 사용할 asp 객체를 미리 선언해둔다 .
Dim oResponse As Response
Dim oRequest As Request Dim intTotalPage As Integer, intStartPage As Integer
Dim intPrevNext As Integer, intPage As Integer
Public Function ShowMeListPage(ByVal argPage As Integer, _
ByVal argPrevNext As Integer, _
ByVal argRow As Integer) As Variant

On Error GoTo ErrHandler

' Response, Request, Server, Application, Session 등의 asp 객체를 사용하고자
' 할경우 MTS 없이 사용하면 오류가 발생한다 .
' 이는 MTS 없이는 컴포넌트가 Context 를 관리할수 없기때문이며, 어떤 클라이언트에게
' asp 구문을 수행해야 하는지 알길이 없기 때문이다 .
' 아래와 같이 Context 내에서 객체를 호출해야 MTS Excusive API 가 작동되며 컴포넌트가
' asp 페이지의 Context 에 접근하고 원하는 asp 의 기능을 직접 컴포넌트내에서
' 사용할수가 있다 .

Set oResponse = oContext("Response")
Dim oCmd As ADODB.Command
Dim oRs As ADODB.Recordset
intPage = argPage
intPrevNext = argPrevNext

' 시작페이지 계산
intStartPage = Int(intPage / intPrevNext) + 1
If intPage Mod intPrevNext = 0 Then intStartPage = intStartPage - 1
intStartPage = (intStartPage - 1) * intPrevNext + 1
' 리스트를 반환하는 StoredProcdure를 사용한다 .
Set oCmd = New ADODB.Command
With oCmd
.ActiveConnection = oConn

.CommandType = adCmdStoredProc
.CommandText = "SP_BOARD_LIST"
.Parameters.Append .CreateParameter("PAGE", adInteger, adParamInput, , intPage)
.Parameters.Append .CreateParameter("ROW_COUNT", adInteger, adParamInput, , argRow)
.Parameters.Append .CreateParameter("TOTAL_PAGE", adInteger, adParamOutput, , 0)

' 리턴변수값을 받기위해 실행한다 . .Execute

intTotalPage = oCmd.Parameters(0).Value
' 리턴되는 레코드셋을 받기위해 한번 더 실행한다 .
Set oRs = .Execute
End With
Set oCmd = Nothing
' 레코드셋을 배열로 반환하여 리턴해준다 .
ShowMeListPage = oRs.GetRows
' 다른 메소드에서 사용할 인자값을 전달시켜주기 위해 쿠키를 사용한다 .
' 원래 일반적인 COM 컴포넌트의 경우 메소드간에 혹은 메소드와 프로퍼티간에
' 인자값 전달이 가능한데 COM+ 에 올린 상태에서는 어떤방법을 써도 메소드간에
' 인자값을 전달시킬수 없었다 .
' 결국 쿠키를 사용했고 별로 깔끔하지 못한 방법이 되었다 . 원인과 해결책을 ' 아시는분은 메일주시면 감사 . ^^;

oResponse.Cookies("Board")("intTotalPage") = intTotalPage
oResponse.Cookies("Board")("intPrevNext") = intPrevNext
oResponse.Cookies("Board")("intStartPage") = intStartPage
oResponse.Cookies("Board")("intPage") = intPage

oContext.SetComplete

Set oRs = Nothing
Set oCmd = Nothing

Exit Function

ErrHandler:
oContext.SetAbort
Set oRs = Nothing
Set oCmd = Nothing
' 에러가 발생할경우 에러메시지를 반환한다 .
Err.Raise Err.Number, Err.Source, Err.Description
End Function

Private Sub ObjectControl_Activate()
On Error GoTo ErrHandler
Set oContext = GetObjectContext
' 퍼포먼스 향상을 위해 Early Binding 을 한다 . (asp 에선 Late Binding 밖에 할수 없다 .)
Set oConn = New ADODB.Connection

oConn.Open CONNECT_STRING oContext.SetComplete Exit Sub
ErrHandler:
oContext.SetAbort
' 에러가 발생할경우 에러메시지를 반환한다 .
Err.Raise Err.Number, Err.Source, Err.Description
End Sub
Private Function ObjectControl_CanBePooled() As Boolean
' 이부분은 True이든 False든 아무런 효과를 줄수없다 .
' 아직 Visual Basic 에선 Object Pooling 이 되지 않기 때문이다 .

ObjectControl_CanBePooled = False
End Function

Private Sub ObjectControl_Deactivate()
' 열린상태라면 닫고 개체를 반환한다 .
If oConn.State = adStateOpen Then oConn.Close
Set oConn = Nothing
Set oContext = Nothing
End Sub

Public Sub ShowMePageNavigation(ByVal strListPageName As String)
Dim I As Integer
' 사용할 asp 객체를 Context 내에서 호출한다 .
Set oResponse = oContext("Response")
Set oRequest = oContext("Request")
' 쿠키에서 ShowMeListPage 에서 처리했던 인자값들을 불러온다 .
intTotalPage = oRequest.Cookies("Board")("intTotalPage")
intPrevNext = oRequest.Cookies("Board")("intPrevNext")
intStartPage = oRequest.Cookies("Board")("intStartPage")
intPage = oRequest.Cookies("Board")("intPage")
' Page-Navigation 이 표시되는 부분 .
If intTotalPage > intPrevNext Then
If intStartPage = 1 Then
oResponse.Write " ◀ "
Else
oResponse.Write " <a href=" & strListPageName & _
"?intPage=" & intStartPage - 1 & _
"> ◀ </a> "
End If For I = intStartPage To intStartPage + intPrevNext - 1
If I > intTotalPage Then
Exit For
Else
If I = intPage Then
oResponse.Write " <font color=red>" & I & "</font>"
Else
oResponse.Write " <a href=" & strListPageName & _
"?intPage=" & I & _
"> " & I & " </a> "
End If
End If
Next If (intStartPage + intPrevNext > intTotalPage) Then
oResponse.Write " ▶ "
Else
oResponse.Write " <a href=" & strListPageName & _
"?intPage=" & I & _
"> ▶ </a> "
End If
Else
oResponse.Write " ◀ "

For I = intStartPage To intTotalPage
If I = intPage Then
oResponse.Write "<font color=red> " & I & "</font>"
Else
oResponse.Write " <a href=" & strListPageName & _
"?intPage=" & I & _
"> " & I & " </a> "
End If
Next
oResponse.Write " ▶ "
End If
Set oResponse = Nothing
Set oRequest = Nothing
oContext.SetComplete End Sub

다 작성하여 dll 을 만들었다면 구성요소서비스에 COM+ 로 등록을 시켜줘야 한다 .
ObjectContext 를 사용하였으므로 등록시키지 않고 사용하려 한다면 오류를 발생할것이다 . 등록하는법에 관해서는 여러곳에 자료가 많으니 자세한 내용은 생략하겠다 . (죄송ㅡ_ㅡ)

다음은 리스트를 불러오는 Stored Procedure 의 소스이다 . (원래의 asp 페이지에서도 사용한다 .)

/* Title : SP_BOARD_LIST 
Feature : 게시판 리스트에 쓰일 레코드셋과 전체페이지수를 리턴한다 .
Author : 2001-05-21 by LikeJAzz */

CREATE PROCEDURE SP_BOARD_LIST
@PAGE INT,
@ROW_COUNT INT,
@TOTAL_PAGE INT OUTPUT
AS
DECLARE @SQL VARCHAR(150)
DECLARE @TOTAL_RECORDS INT
SELECT @TOTAL_RECORDS = COUNT(*) FROM tblTEST
SET @TOTAL_PAGE = CEILING (@TOTAL_RECORDS / @ROW_COUNT)

/* ADO 의 RecordCount, AbsolutePage 등의 속성을 쓰지 않고 DB tier 에서 원하는 만큼 의 레코드셋을 뽑아내게끔 쿼리를 구성했다 . 태요님 강좌에 피드백남겨주신분의 쿼리를 써보았는데 꽤 유용했다 . 감사 ^^; */
SET @SQL = 'SELECT TOP ' + CONVERT(VARCHAR(10), @ROW_COUNT)
SET @SQL = @SQL + ' field1, field2, field3 FROM (SELECT TOP '
SET @SQL = @SQL + CONVERT(VARCHAR(10), (@TOTAL_RECORDS - (@PAGE - 1) * @ROW_COUNT)) SET @SQL = @SQL + ' field1, field2, field3 FROM tblTEST ORDER BY seq ASC) A '
SET @SQL = @SQL + 'ORDER BY A.field1 DESC'

EXEC (@SQL)

자 그럼 이제 결론을 내보자 . 과연 asp 페이지는 얼마나 깔끔해 졌을까 ? 아래처럼 되었다 .

<%

' Title   : List.asp
' Feature : 리스트를 처리하고 페이지 네비게이션을 표시한다 .
' Purpose : 게시판의 리스트페이지
' Author  : 2001-05-17 by LikeJAzz

' 변수선언을 강요한다 .
' Option Explicit

' 사용할 변수선언
Dim intPage, intPrevNext, intRow, intStartPage, intTotalPage, I
Dim strListPageName

' 사용할 객체의 선언 asp 는 Variant 형만 선언할수있다 .
' Dim oConn, oCmd, oRs

' 표시할 페이지를 Request 한다 .
intPage = Cint(Request.QueryString ("intPage"))
If intPage = 0 Then intPage = 1

' 현재 페이지 이름
strListPageName = "List.asp"
' 페이지 Navigation 표시수
intPrevNext = 10
' 한페이당 출력 글수
intRow = 10

Dim arrRs

' 컴포넌트를 호출한다 .
Set oBoard = Server.CreateObject ("bc_Board.List")
' Getrows 로 리턴된 레코드셋을 배열에 받아낸다 .
arrRs = oBoard.ShowMeListPage(intPage, intPrevNext, intRow)


' 리스트 표시 시작 . 레코드셋이 EOF 가 될때까지 반복되며 리스트를 출력해준다 .
For I = 0 To Ubound(arrRs, 2)
%>


// 리스트 내용이 주욱 표시되는 부분 .


<%
  ' 출력할때는 아래와 같이 출력한다 . (가로세로가 바뀜에 주의하자)
	Response.Write arrRs(0, I)
Next %> // 여러 HTML 요소들이 표시되는부분 . <% ' Page-Navigation 이 간단히 메소드 호출로 표시된다 . oBoard.ShowMePageNavigation(strListPageName) Set oBoard = Nothing %>

asp 코드가 한결 깔끔해졌다 .

로직을 실수로 건드릴 염려도 없고 디자인이 다른 같은형식의 리스트 페이지라면 컴포넌트의 재사용도 가능하다 . 물론 트래픽이 많은 페이지라면 컴포넌트로 인해 퍼포먼스도 상당히 향상될것이다 .


unsolved Problem :

컴포넌트내의 메소드와 프로퍼티간에 인자값 전달이 되지 않는다 . 쿠키를 사용하는 조금은 지저분한 방법으로 해결했지만 분명 COM+ 에 올린 상태가 아닌 일반적인 COM 컴포넌트에서는 같은 클래스라면 메소드와 프로퍼티간에 변수값이 전달된다 . 하지만 COM+ 상태에서는 서로 다른 메소스들끼리 인자값을 전달시켜주지 못했고 결국 쿠키로 전달이라는 극단적인 방법밖에 취할수가 없었다 . 원인과 해결책을 아시는분은 메일주세요 ^^;


이상으로 간략하게나마 강좌를 마친다 . 휴 역시 강좌를 적는다는건 글을 적는다는건 힘든일이다 . 이글을 적으면서도 얼마나 많이 생각하면서 글을 적었는지 모르겠다 .

꾸준히 글을 올려주시는 태요님이 존경스러울뿐이다 .

앞으로 기회가 닿는다면 msdn 의 샘플사이트 Duwamish Online 을 분석한글 혹은 Visual C++ 의 ATL 을 사용하여 가장 최적화된 컴포넌트 작성법에 대해 강좌를 적어볼 생각이다 .

아무쪼록 부족한 제글을 읽어주신 여러분들께 감사드리며 피드백은 이메일 likejazz@korea.com 이용해주시면 됩니다 . 강좌에 대한 내용외에 질문들은 되도록이면 자제해주셨으면 좋겠습니다%2


http://www.taeyo.pe.kr/Lecture/22_COM/COMP_likeJazz_02.htm
두번째 시간이다 .

이번에는 저번시간에 문제가 되었던 COM+ 의 State 에 대해서 다루어보도록 하겠다 . 사실 그동안 COM+ 컴포넌트를 여러 번 만들어 오면서 State 문제에 봉착한 적이 없었는데 알고봤더니 그것은 무의식중에 전부 Stateless 한 컴포넌트를 만들어서 였다 .

덕분에 State 에 대해 많은 공부를 하였고 태요님의 오픈소스 마인드를 본받아 그 노하우를 공개하도록 하겠다 . 아울러 이자리를 빌어 저번강좌에 피드백 남겨주신 많은 분들게 감사의 말씀 드린다 .

또한 이번강좌는 주제의 성격상 이론적인 설명이 대부분을 차지할것이다 . 실무예제를 보여주며 실무에 바로 적용할수 있게끔 하겠다는 본인의 의도와는 조금 빗나가긴 하지만 가능한한 다양한 예제를 제시하며 최대한 실무에 근접하게끔 노력하겠다 . 아무쪼록 이쁘게 봐주세요 *ㅡ_ㅡ*

우선 주제는 Stateful or Stateless 가 되며 그와 연관된다고 생각되는 Transaction, Just-in-Time (JIT) Activation (Windows 2000 에서 '적시활성화'라고 번역되어있다 . ), Context, Shared Property Manager 등에 대해서도 언급해보도록 하겠다 .

VB 의 관점에서 작성될것이므로 Object Pooling 은 다루지 않겠다 . VC++ ATL 로 이를 구현할수 있지만 이는 지금의 강좌의 주제에서 벗어나고 내용또한 상당히 방대한 분량이 될수 있으므로 제외시키겠다 .


* Object Pooling

그럼 VB 에서 왜 Object Pooling 이 되지 않는가 ? 그것은 VB 가 STA(Single-Threaded Apartment)만을 지원하는 스레드 친화적(Thread Affinity) 인 문제점 때문이다 .


그림1. VB 로 작성되어 개체풀링(Object Pooling)이 비활성화 되어있는 상태

Platform SDK Documentation on COM+ 을 보면 Poolable 한(즉, Object Pooling 이 가능한) 컴포넌트 작성을 위한 조건으로 크게 4가지를 제시하고 있다 .

* They must be stateless :
   stateless 라고 ? 이제 낯익은 단어다 . stateless 즉, 클라이언트간에 State 를 전달시켜줄수없다 .

* They must have no thread affinity :
   바로 이점이 VB 가 Poolable 한 컴포넌트를 만들지 못하는 문제점이다 .
   스레드 친화적이기 때문에 Poolable 한 컴포넌트를 작성할수 없다 .

* They must be aggregatable :
   집합화(aggregatable)가 되어야한다 . COM+ 가 wrapper 를 Poolable 한 컴포넌트 주위를 감싸서
   집합화를 관리하기 때문이다 .

* They must implement IObjectControl :
   지난강좌에서 이를 구현하였다 . 단순히 상단에 Implements ObjectControl 을 적어주면 이 컨트롤의
   인터페이스를 사용하여 Object 의 수명(Life Time)과 State 를 관리할것이다 .

현재의 VB 에서 Object Pooling 이 되지 않는 문제는 VB.NET 에서 말끔히 해결될것이다 .


* Object ?

UML 에서 언급하는 Object 란 무엇인가 ? Object = State + Behavior 이다 .

State 와 Behavior 를 구분하는 소스를 보자 . Don Box 가 작성한것이며 C++ 의 형태이다 . 아주 간단한 형태이므로 굳이 VB 로 재작성하지 않아도 이해할수있을것이다 .


class Dog { Int m_nHairs; public: void Shed(void) { m_nHairs -= 100; } };

이 Object 에서 m_nHairs 는 state 가 되고 Shed 는 behavior 를 나타내는 메소드가 된다 . 물론 비즈니스 로직은 100을 빼는것이다 . ㅡ_ㅡ

아래에 State Level 4 를 설명할 때 이에 대해 다시 한번 언급하도록 하겠다 .


* Context

지난시간에 우리는 Context 내에서 ASP 객체를 작동시켜보았다 . 왜 ASP 객체를 Context 내에서 작동시켜야 하는지에 대해서는 지난번 소스에 직접 주석으로 달아놓았으니 지난시간의 강좌를 참조하면 되겠다 .

개인적으로 Context 가 COM+ 컴포넌트의 가장 큰 특징이라고 생각한다 . IIS 의 객체를 사용할수있게 해줄뿐만 아니라 트랜잭션관리, 보안등의 다양한 기능을 수행한다 . 이 강좌의 주제인 State 도 사실은 Context 가 제일 먼저 가지게 된다 . (Don Box 는 Level 0 의 단계라고 언급했다 .)

* JIT(Just-In-Time) Activation

JIT 가 작동되면 클라이언트가 이 Object 를 참조하고 있더라도 COM+ 가 인스턴스를 반환해줄수있다 . 즉 클라이언트에게 작동중이라고 사기치는것이다 . 물론 메소드가 호출되면 COM+ 은 즉시(Just-In-Time) Object 를 클라이언트에게 재작동시켜줄것이다 .

그럼 이런 멋진 기능을 어떻게 사용하는가 ?

코드를 아니 그림을 보자 .



그림2. JIT Activation (적시활성화) 를 간단히 체크만 하면 작동한다 .


COM+ 에서 작동하고 있는 Transactional Components 라면 JIT 는 선택사항이 아니다 .
(JIT Activation 을 사용하지 않는 상태에서 SetComplete 혹은 SetAbort 문장이 호출되면 에러를 발생한다 .)

저번 강좌에 피드백을 남겨주신 많은분들께서 JIT 옵션은 자유롭게 껏다가 켤수 있는것처럼 말씀해주셨는데 실제로 JIT 는 트랜잭션이 작동중일때는 필수사항이다 . 끄면 에러가 난다는 말이다 .
(1. No Required 라고 설정한 경우라도 마찬가지다 . SetComplete 가 호출되면 에러가 발생한다 .)

조은 기능인만큼 단점도 존재한다 . 그것은 바로 트랜잭션이 종료되더라도 JIT 는 stub 와 wrapper 를 그대로 물고있기 때문이다 . (다음 요청시 계속 작동되고 있었던것처럼 사기쳐야 되므로) 여기에 차지하는 메모리는 1KB 정도이다 . 대략 stub 가 400bytes, wrapper 가 600bytes 정도를 물고 있다 . 아래 그림을 참조하면 이해가 빠를것이다 .



그림3. MTS(COM+) 와 Client 간의 연결상태 . (JIT Activation 이 작동할시 Stub 와 Context Wrapper 는 그대로 물고있게 된다 . 즉 1KB 정도의 메모리를 항상 점유하고 대기하게된다 .)
만약 JIT 를 사용하지 않는다면 인스턴스를 소멸시키고 stub, wrapper 등을 다시 만드는 작업을 거쳐야 되므로 퍼포먼스 저하를 가져온다 . 또한 SetComplete, SetAbort 가 호출되는 컴포넌트에는 JIT 는 선택이 아닌 필수사항이다 . 대부분의 경우라면 이 옵션은 항상 활성화 해두도록 하자 .
* Transaction

Transaction 하면 뭐가 생각나는가 ?

ACID 라고 대답한다면 당신은 Database 에 대해 어느정도 해박한 지식을 가지고 있는것이다 .

이름 잘짓기로 소문난 MS 가 지었을것이다라고 생각할수 있겠지만 아쉽게도 이는 MS의 작품이 아니라 1980년에 Harder 와 Reuter 가 소개한것이다 . 간략히 풀어보면 Atomicity, Consistency, Isolation, Durability 이다 . 이에 대한 자세한 내용은 Database 책의 Transaction 부분을 참조하시면 된다 . (억지로 붙인듯한 이름이라는 냄새가 없잖다 .)

State 를 언급하기에 앞서 Transaction 을 언급한 이유는 바로 Transaction 이 State 와 밀접한 관련을 맺고 있기 때문이다 . 특히 ACID 의 3번째 속성 Isolation 즉 고립성으로 인하여 트랜잭션 진행중에는 다른 트랜잭션으로부터 State 를 공유할 수가 없고 트랜잭션이 종결되면 (SetComplete 나 SetAbort 가 호출되면) 모든 State 를 반환한다 .

쉽게 말해 COM+ 가 Stateless 를 강요하는 것은 (물론 David S. Platt 은 NO 라고 대답하지만) Transaction 의 Isolation 속성 때문인것이다 .

* State

이번강좌의 주제인 State 에 대해 본격적으로 다루어 보도록 하겠다 .

Microsoft System Journal (이하 MSJ, 2000년 3월호부터 MSDN 매거진에 통합되었다 .) 1998년 3월호 (벌써 3년전의 컬럼이다 . 그당시 벌써 이런문제에 대해 고민하고 해결책을 제시했다는게 놀라울따름이다 . 물론 이는 .NET 이 아직 출시되지도 않았는데 벌써 1년전부터 .NET 관련 컬럼들이 올라오는것과 같은 맥락이라 볼수있겠다 . MS 직원(Technical Evangelist)이나 혹은 컨설턴트가 아니라면 당신이 필드에서 일하고있는 개발자라면 .NET 은 최소한 1년후의 얘기가 될것이고 그동안에는 실무에서 쓸기회가 없을것이다 . 당연히 그에 대한 문제점이나 노하우가 1년은 더 기다려야 발생할수 있을것이고 그때가 되면 2년전의 컬럼들을 뒤적이게 될것이다 . 그렇죠 ? ^^;)

어쨋든 MSJ 98년 3월호 Don Box 의 컬럼에 이 문제에 대해서 최초로 제기되었고 그에대한 상세한 설명과 해결책(까지는 아니지만)이 제시되어있다 .

아래는 98년 3월 MSJ 에 실린 Don Box 가 그린(직접 그렸을까 ?) State 를 보관하는 4가지 장소이다 .



그림4. Don Box 가 언급한 State 가 보관되는 4가지 장소
Understanding COM+ 의 저자 David S. Platt 이 이를 좀더 구체화하여 표로 그렸다 . 아래와 같다 .

그림5. David S. Platt 이 정리한 Object State Location

그럼 이 4가지 State 의 보관방법에 대한 부연설명과 실제 VB 코드로 구현해보도록 하겠다 .
1) Level1 : Client


이것은 지난강좌에 쓰인방법이다 . 가장 무난한 방법이라고 볼수있겠다 . 웹개발자라면 쿠키외에 별다른 대안이 없겠지만 Win32 응용프로그램의 형태라면 FileSystem 이 될수도 있겠고 Registry 가 될수도 있겠다 .

이 부분에 대해선 소스를 생략하고 저번강좌에 쿠키에 쓰는 소스를 참조하시면 되겠다 .

2) Level2 : Object's Member Variables
지난번 강좌를 쓸 때 분명히 컴포넌트내에서 Global Variable(전역변수)로 선언하고 메소드간에 인자값을 전달하려 했으나 값이 전달되지 않았다 . 왜 일까 ?

이유는 SetComplete 문장 때문이었다 . SetComplete 가 호출되면 모든 자원을 시스템으로 반환한다 . 즉 전역변수는 한 트랜잭션내에서만 존재할수 있다는 얘기다 . (앞서 언급한 트랜잭션의 Isolation 속성때문이다 .) SetComplete 문장으로 (물론 SetAbort 해서 취소한 경우도 마찬가지이다 .) 트랜잭션을 종료했을때는 모든 자원을 시스템으로 반환한다 .

그리하여 COM+ 컴포넌트가 사용한 자원들이 모두 시스템으로 반환되었기 때문에 두번째 메소드(지난번 강좌에서 페이지네비게이션)를 호출했을 때 인자값을 전달받지 못했던 것이다 .

아래 예제 코드를 제시하겠다 . VB Active-X Dll 에서 작성하면 된다 .
' Title : COM+ Test P/G
' Feature : State 저장방법에 대해 Test 하는 App
' Author : 2001-05-28 by LikeJAzz
' 참조해야할것 :
' 1. COM+ Service Type Library
' 실제로 Transaction 은 사용하지 않을것이므로 MTSTransactionMode 를 1. No Required 로 설정한다 .


Option Explicit

Implements ObjectControl

Private oContext As ObjectContext
' State 를 보관하기 위한 전역변수 선언
Dim strTestStateVariable As String

Public Function Method1() As Variant
    strTestStateVariable = "Hello World!"
    Method1 = strTestStateVariable
    oContext.SetComplete
End Function

Public Function Method2() As Variant
    ' Method1 에서 사용된 State 를 전달받는다 .
    Method2 = strTestStateVariable
    oContext.SetComplete
End Function

Private Sub ObjectControl_Activate()
    Set oContext = GetObjectContext
    oContext.SetComplete
End Sub

Private Function ObjectControl_CanBePooled() As Boolean
    ObjectControl_CanBePooled = False
End Function


Private Sub ObjectControl_Deactivate()
    oContext.SetComplete
    Set oContext = Nothing
End Sub

빨갛게 표시한부분을 주의깊게 보기 바란다 . 참고로 여기에서는 예제를 보여주기 위해 SetComplete 만 표시했지만 실무에 적용할때는 에러 핸들링을 하고 SetAbort 도 정의해 주어야 한다 . 지난시간의 강좌에 작성되어져 있으니 참조하면 된다 .

이에 대한 자세한 설명은 뒤로 미루고 우선 Presentation 코드부터 작성해보겠다 . (asp 전문 사이트인만큼 asp 로 작성해 보도록 하겠다 . 허나 실제로 asp 와 VB 코드는 큰 차이가 없다 .)
<%
Set obj = Server.CreateObject ("COMplus.StateTest")
With obj
   Response.Write .Method1
   Response.Write "<br>"
   Response.Write .Method2
End With
Set obj = Nothing
%>

실행결과는 아래와 같이 될것이다 .


Hello, World! 가 단 한번만 찍혔다 . 이말은 2번째 Method 에 State 가 전달되지 않았다는 소리다 .

이유가 뭘까 ? 앞에서 언급했듯이 SetComplete 문장 때문이다 . 실제로 트랜잭션을 사용하지는 않지만 SetComplete 는 작동을 한다 . 즉 SetComplete 명령을 받으면 모든 상황을 종료시키고 자원을 시스템에 반환해준다 . 물론 State 도 포함이다 .

그럼 소스를 어떻게 수정하면 될까 ? 위에서 빨간색으로 표시한 SetComplete 를 지우면 된다 . (즉 트랜잭션 주기를 길게 잡으란 소리다 .) Deactivate 이벤트에 SetComplete 는 지울 필요가 없다 . 어짜피 컴포넌트가 비활성화되면 모든 State 가 반환되므로 그냥 놔두는것이 조을것이다 .

SetComplete 를 지우고 해당 COM+ 을 시스템종료 시킨후 VB 에서 dll 을 이진호환성 설정하고 재컴파일 해보자 . 그리고 다시 실행시켜보자 .


두번 다 찍힌다 . 즉 State 가 두번째 Method 로 전달된 것이다 .

자 그럼 지난번 강좌에 쓰인 게시판 소스에서 ShowMeListPage 메소드와 ShowMePageNavigation 메소드간에 State 를 전달해 주려면 ?

ShowMeListPage 메소드에서 SetComplete 문장을 제거해주면 된다 . 트랜잭션 주기를 길게 잡으면 된다 . (트랜잭션이 길어짐으로 해서 발생하는 퍼포먼스 저하는 감안해야 할것이다 . 하지만 지난강좌에는 실제로 트랜잭션은 걸지 않았으므로 아마 퍼포먼스 저하는 없을것이다 .)

또한 JIT Activation 을 활성화 해두어야 한다 . 그 이유는 앞서 언급했다 . (비활성화된 상태에서는 SetComplete 에서 에러를 발생한다 .)


3) Level3 : Resource dispencer (Shared Property Manager or Pooled Object)

Level 3 의 경우 2가지 방법이 제시되어있다 . (이외에 ODBC 도 Resource dispencer 의 역할을 수행하지만 여기서는 언급하지 않겠다 .) 앞에서도 여러 번 언급하였지만 VB 에서 Object Pooling 은 해당사항이 없기 때문에 Shared Property Manager (이하 SPM) 에 대한 경우만 설명해보겠다 .

SPM 은 프로세스내에서(dllhost.exe) Object간에 State 를 공유할수있게 해주는놈이다 . 이 말은 Client 간에도 State 를 공유할수 있다는 소리다 . (Level2 이하는 Client 간에 State 공유는 할수 없다 .)

지난강좌에서 컴포넌트의 단점을 언급할 때 Dead-Lock(DB 의 Dead-Lock 이 아니다 .), Access Violation, Serizable 등의 문제가 발생한다고 했는데 (물론 이는 VB 수준에서는 거의 일어나지 않지만) SPM 을 사용하면 이 문제가 해결된다 . (Lock 을 제어하고 사용자간의 Shared Property 를 보호하는 신호등역활을 한다고 Platform SDK 에 쓰여있다 .)

SPM 을 사용하면 트랜잭션위치 바깥에 State 를 위치 시킬수 있고 다른 트랜잭션에서 이 State 를 공유할수 있다 .

이놈을 사용하려면 약간의 추가 코딩이 필요하다 . 그렇다고 이렇게 대충 설명만 하고 얼렁뚱땅 넘어가면 안되겠죠? 아래 VB 로 작성한 코드를 제시한다 .


' Title : COM+ Test P/G
' Feature : State 에 상태저장방법에 대해 Test 하는 App
' Author : 2001-05-30 by LikeJAzz

' 참조해야할것 :
' 1. COM+ Service Type Library (NT 때와는 달리 2000에서는 이놈 하나만 참조하면 된다 .)

Option Explicit

Implements ObjectControl

Private oContext As ObjectContext

Public Function Method1() As Variant
    Dim strValue As String
    Dim bGroupExists As Boolean
    
    ' Shared Property Group Manager 를 선언, 생성하고
    Dim spmMgr As New SharedPropertyGroupManager
    ' Shared Property Group 을 선언한다 .
    Dim spmGroup As SharedPropertyGroup
    ' Hello 라는 Property Group 을 생성하고 
    Set spmGroup = spmMgr.CreatePropertyGroup("Hello", LockSetGet, Process, bGroupExists)

    Dim spmProperty As SharedProperty
    ' World 라는 Property 를 덧붙인다 .
    Set spmProperty = spmGroup.CreateProperty("World", bGroupExists)
    
    strValue = "Hello World!"
    ' 즉 Hello 그룹의 World 프로퍼티에 "Hello World!" 라는 값을 집어넣는다 .
    spmProperty.Value = strValue
    Method1 = strValue
    oContext.SetComplete
End Function


Public Function Method2() As Variant
    Dim bGroupExists As Boolean
    
    ' 여기에서는 값을 읽어내겠지만 문법은 위 메소드와 동일하다 .
    Dim spmMgr As New SharedPropertyGroupManager
    Dim spmGroup As SharedPropertyGroup
    Set spmGroup = spmMgr.CreatePropertyGroup("Hello", LockSetGet, Process, bGroupExists)
    Dim spmProperty As SharedProperty
    
    ' Hello 그룹의 World 프로퍼티의 값을 읽어온다 .
    Set spmProperty = spmGroup.CreateProperty("World", bGroupExists)
    
    ' 값이 존재한다면 GroupExists 는 True 가 되고 존재하지 않는다면 False 가 된다 .
    If bGroupExists = False Then
        Method2 = "Property Does Not Exist"
    Else
        Method2 = spmProperty.Value
    End If
    oContext.SetComplete
End Function

Private Sub ObjectControl_Activate()
    Set oContext = GetObjectContext
    oContext.SetComplete
End Sub

Private Function ObjectControl_CanBePooled() As Boolean
    ObjectControl_CanBePooled = False
End Function


Private Sub ObjectControl_Deactivate()
    oContext.SetComplete
    Set oContext = Nothing
End Sub


OK , 끝이다 . 이제 State 를 메소드끼리 전달할수있을뿐만 아니라 Client 끼리도 State 를 전달할수 있다 . 말 그대로 Shared 가 되는것이다 .

그렇다면 정말 Client 끼리 State 가 전달될까 ? 간단한 테스트를 해보겠다 . 아래 2종류의 asp 페이지가 있다 .

Test1.asp

<% Set obj = Server.CreateObject ("COMplus.StateTest") With obj Response.Write .Method1 Response.Write "<br>" Response.Write .Method2 End With Set obj = Nothing %>

Test2.asp

<% Set obj = Server.CreateObject ("COMplus.StateTest") With obj Response.Write .Method2 End With Set obj = Nothing %>

Test1.asp 부터 실행시켜야 된다 . 결과는 ?



메소드간에 State 가 전달되었다 . (정확히 말하면 SPM 에 저장한 State 를 공유한 것이다 .)

이제 우리가 만든 COM+ 는 작동을 시작할것이다 . 아래처럼 ..



그림6. 위에서 SPM 을 사용하여 만든 COM+ 가 2048 번 프로세스ID 에서 작동을 시작한다 .
이제 Test2.asp 를 실행시켜보자 . 결과는 ?

State 가 Client 끼리도 공유가 되었다 . 이 State 는 2048 번 프로세스가 작동하고 있는한 유효할것이다 . (2048번 dllhost.exe 가 작동하는한)

즉 컴포넌트내에서 아무리 SetComplete 을 호출하여도 State 는 사라지지 않는다는 말이다 . 왜냐면 SPM 으로 도망간 State 는 트랜잭션의 바깥에 위치하기 때문이다 .

엄밀히 말하면 트랜잭션의 ACID 속성을 위배한것이라고 말할수 있다 . 하지만 우리에게 중요한건 법칙이나 규칙이 아니다 . 원하는 퍼포먼스를 발휘하고 원하는 결과를 도출해 낸다면 그걸로 끝이다 . 최소한 개발자인 우리에겐 ..
4) Level4 : Resource Manager

Resource Manager (이하 RM) 가 머지 ? 처음 RM 이란 단어를 접했을 때 이게 뭘까 한참 고민했었다 .

RM 이란 COM+ 의 Resource 를 관리해 줄 수있는 제품들이다 . 현재 이에 해당하는 제품들에는 MS-SQL 서버 6.5,7.0,200 , MSMQ , Windows 오라클 7.3 이상, 유닉스 오라클 8이상, DB2, Informix 등을 언급할 수가 있다 .

쉽게 말해 우리는 Database 를 RM 이라고 생각하면 된다 . 즉 DB 에 State 를 저장한다는 소리다 . 가장 견고하고 가장 확실한 방법이 되겠지만 반면에 성능저하는 가장 심할것이다 . (DB 접근을 최소화 해야 퍼포먼스가 조아진다는건 다들 잘 아시리라 생각한다 .)

당연한 말이지만 DB 에 저장된 State 는 모든 Client 끼리 공유할수 있다 .

David S. Platt 은 이 방식이 가장 어렵다고 말했다 . 하지만 우리가 늘상 하는일이 ADO 를 사용하여 DB 를 읽고 쓰는일이 아닌가 ? 굳이 예제를 제시하지 않아도 충분히 이해하고 적용할수 있을것이다 . (따로 예제코드를 제시하지 않겠다 .)

참고로 RM 을 사용하는 방식은 UML 에 어긋난다 .

왜 ?

앞에서도 언급했듯이 Object = State + Behavior 이기 때문이다 .

State 를 분리하는것은 방법론에 어긋난다 .
하지만 RM 을 사용하는 것이 가장 견고하게 State 를 보관할수있다 . 물론 성능저하는 가장 심하다 . (모든 사용자의 State 를 DB 에 저장한다고 생각해보라)

어쨋거나 우린 아키텍트가 아닌 개발자이고 가급적이면 더욱 안전한 더욱 고성능을 내기 위해 방법론을 위배하더라도 그것은 방법론을 따라가지 못하는 현재의 기술수준을 탓해야 될것이다 .


* Conclusion


이론적인 설명은 장황했지만 실제로 소스로 구현해보면 별거 아니네 ? 라고 생각되는 사람이 많을것이다 . 그만큼 COM+ 의 내부구조가 잘 설계되어있고 직관적인 UI 를 가지고 있으며 Visual Basic 이라는 언어가 번거로운 작업을 도맡아 처리해주며 개발자에게 생산성을 제공해준다는 뜻이다 .

UI 에 대해 오랫동안 프로그래밍 해온 사람이라면 잘 설계된 , 직관적이고 편안한 UI 를 표현하기 위해 그 기반구조는 얼마나 복잡해져야 하는지 잘 알것이다 .

간략하게 한두줄의 코드로 표현되지만 그 밑바탕에서 어떡 작업들이 수행되는지 이번기회를 통하여 많이들 공부하셨으면 하는 바램이다 .

아울러 본 강좌의 주제인 State 에 대해서 확실히 이해하셨으면 한다 . (본인 역시 이번기회를 통하여 많이 공부하였다 . ^^*)

asp 를 전문으로 다루는 이 사이트에서 자꾸 COM+ 얘기만 하니 미안한감이 든다 .

하지만 asp 만으로 할 수 있는 기술들은 이미 태요님의 친절하고 자세한 강좌와 책등을 통하여 대부분 공개되었다 . 이제는 asp 는 디자이너의 영역으로까지 확대되고 있으며 asp 는 다른 기술들과의 매개체로서 그 역할을 수행하고 있다 .

웹개발자로서 웹프로그래머서 한단계 올라서길 원한다면 물론 Win32 개발자(VB 등의 RAD 툴, MFC 등의 툴을 사용하여 주로 UI 를 구현해내는데 집중했던 개발자) 역시 마찬가지다 .

COM+ 은 반드시 거쳐야할 관문이다 . 또한 앞으로 다가올 .NET 환경에서 적응하려면 COM+ 을 배제하고선 살아남을수 없을것이다 .

꾸준히 노력하여 뒤쳐지지 않는 한단계 앞서나가는 개발자가 되자!

자 그럼 다음에 만날때까지 Happy Programming!


참조서적 :
Understanding COM+ (Microsoft Press)

참조사이트 :
Microsoft System Journal 98년 3월호 ActiveX/COM 컬럼
http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0398/activex0398.htm&nav=/msj/0398/newnav.htm
Microsoft System Journal 99년 12월호 Basic Instincts 컬럼
http://www.microsoft.com/msj/defaultframe.asp?page=/msj/1299/Instincts/Instincts1299.htm
MSDN Magazine 2000년 12월호 Ten Tips for Maximizing COM+ Performance
http://msdn.microsoft.com/msdnmag/issues/1200/comtips/comtips.asp
Knowledge Base : Q191235 Use the Shared Property Manager in MTS through VB Code
http://support.microsoft.com/support/kb/articles/Q191/2/35.ASP
Platform SDK Documentation on COM+
http://msdn.microsoft.com/library/default.asp?URL=/library/psdk/cossdk/complusportal_9o9x.htm

'ASP' 카테고리의 다른 글

IIS 서버 콤포넌트 만들기...  (0) 2008.09.10
ASP 강좌  (1) 2008.09.09