안재우님의 ASP DBHelper Class 설명

프로그램&DB/C# 2009. 4. 29. 09:44 Posted by Josep.H.S

DB Access Framework

for ASP & ADO

 

 

작성자 : 안재우 (닷넥엑스퍼트 책임 컨설턴트)

lancers@dotnetxpert.com

 

      

 

1.    개요.. 4

1.1.         DB Access Framework이란?. 4

1.2.         DB Access Framework 장점.. 4

2.    사용법.. 5

2.1.         DBHelper 등록.. 5

2.2.         DBHelper 생성.. 5

2.3.         DBHelper 멤버.. 5

2.3.1.       ExecSPReturnRS. 5

2.3.1.1.    설명.. 5

2.3.1.2.    사용 예제.. 6

2.3.2.       ExecSQLReturnRS. 7

2.3.2.1.    설명.. 7

2.3.2.2.    사용 예제.. 7

2.3.3.       ExecSP. 7

2.3.3.1.    설명.. 7

2.3.3.2.    사용 예제.. 7

2.3.4.       ExecSQL. 8

2.3.4.1.    설명.. 8

2.3.4.2.    사용 예제.. 8

2.3.5.       BeginTrans. 8

2.3.5.1.    설명.. 8

2.3.5.2.    사용 예제.. 8

2.3.6.       CommitTrans. 9

2.3.6.1.    설명.. 9

2.3.6.2.    사용 예제.. 9

2.3.7.       RollbackTrans. 9

2.3.7.1.     설명.. 9

2.3.7.2.     사용 예제.. 9

2.3.8.       MakeParam.. 9

2.3.8.1.    설명.. 9

2.3.8.2.    사용 예제.. 9

2.3.9.       GetValue. 10

2.3.9.1.    설명.. 10

2.3.9.2.    사용 예제.. 10

2.3.10.     DefaultConnString. 10

2.3.10.1.     설명.. 10

2.3.10.2.     사용 예제.. 10

2.3.11.     Dispose. 10

2.3.11.1.     설명.. 10

2.3.11.2.     사용 예제.. 10

2.3.12.     collectParams. 11

2.3.12.1.     설명.. 11

 

 

1.            개요

1.1.    DB Access Framework이란?

ASP에서 개발자들이 작성하는 데이터 액세스 코드에 대해 일관성을 유지시키며, 애플리케이션에 최적의 성능을 제공해주도록 작성된 ASP 클래스 컴포넌트를 말한다.

1.2.    DB Access Framework 장점

DB Access Framework 사용해서 데이터 액세스 코드를 작성할 얻을 있는 장점은 다음과 같다.

l         코드의 일관성이 보장된다. 개발자들에 따라서 각기 다른 코드를 작성하던 것이 단일한 패턴으로 작성된다.

l         DB Access Framework VBScript Class 작성되어 사용하기 간편하다.

l         애플리케이션 상에서 ADO 코딩 패턴에 맞는 튜닝 요소들을 강제한다. , Disconnected 레코드셋, Client-side 커서, Static 커서 등의 사용이 적용된다.

l         직접 Connection Command 개체를 핸들링하지 않아도 되므로 코드가 깔끔해진다.

l         매개변수화된 쿼리(Parameterized Query), 저장 프로시저 호출, Output 매개변수 다루기 등을 매우 간편하게 수행할 있다.

l         필요에 따라 얼마든지 DB Access Framework 확장할 있다.

l         단일 Framework 통해서 데이터 액세스를 수행하므로 코드 상의 문제점을 격리시키는 효과가 있다.

 

2.            사용법

2.1.    DBHelper 등록

이는 서버 사이드 인클루드로서 별도의 설치나 등록없이 바로 사용할 있다. 어플리케이션에 올려두고 필요한 ASP 페이지에서 다음과 같은 코드로 인클루드 하여 사용하면 된다.

<!--#include File="DBHelper.asp"-->

2.2.    DBHelper 생성

생성 방법은 일반적인 클래스 인스턴스를 만드는 것과 동일하다. 사용이 끝나면 Nothing 줘서 해제시키는 역시 동일하다.

Set DBHelper = new clsDBHelper

Set DBHelper = Nothing

2.3.    DBHelper 멤버

DBHelper에서 제공되는 메서드와 속성은 다음과 같다. 멤버의 세부적인 설명 매개변수는 멤버에 해당하는 절을 참고하기 바란다.

접근자

속성/메서드명

설명

Public

ExecSPReturnRS

저장프로시저를 실행하고 레코드셋 개체를 반환한다.

ExecSQLReturnRS

SQL문을 실행하고 레코드셋 개체를 반환한다.

ExecSP

저장프로시저를 실행한다. (레코드셋 반환하지 않음)

ExecSQL

SQL문을 실행한다. (레코드셋 반환하지 않음)

BeginTrans

트랜잭션을 시작하고, Connection 개체를 반환한다.

CommitTrans

활성화된 트랜잭션을 커밋한다.

RollbackTrans

활성화된 트랜잭션을 롤백한다.

MakeParam

매개변수로 사용할 배열을 만든다.

GetValue

매개변수 배열에서 특정 매개변수의 값을 가져온다.

DefaultConnString

기본 연결 문자열

Dispose

내부의 DefaultConnection 닫고, 메모리에서 해제한다.

Private

collectParams

매개변수 배열을 Parsing하여 Parameter 개체를 생성한 Command 개체에 추가한다.

2.3.1.    ExecSPReturnRS

2.3.1.1.                       설명

저장 프로시저를 실행하고 레코드셋 개체를 반환한다.

Function ExecSPReturnRS(spName, params, connectionString)

l         spName : 실행할 저장 프로시저 이름

l         params : 매개변수 값의 배열

l         connectionString : 연결 문자열 또는 Connection 개체. Nothing이나 DBHelper 개체의 DefaultConnString 속성을 지정하면 기본 연결 문자열을 사용한다.

2.3.1.2.                       사용 예제

다음과 같이 2개의 Input 매개변수(@Beginning_Date, @Ending_Date) 1개의 Output 매개변수(@RecordCount) 가지는 저장 프로시저가 있다고 가정하자.

테스트용 SP

Create procedure TestSP2

             @Beginning_Date DateTime,

@Ending_Date DateTime,

@RecordCount int output

AS

SELECT Orders.ShippedDate, Orders.OrderID, "Order Subtotals".Subtotal,

DATENAME(yy,ShippedDate) AS Year

FROM Orders INNER JOIN "Order Subtotals" ON Orders.OrderID = "Order Subtotals".OrderID

WHERE Orders.ShippedDate Between @Beginning_Date And @Ending_Date

 

SELECT @RecordCount = @@rowcount

이를 호출하는 코드는 다음과 같다. 매개변수 배열을 만들기 위해 DBHelper MakeParam 메서드를 사용하고 있는 것과 Output 매개변수 값을 읽어오기 위해 GetValue 메서드를 사용하고 있는 것에 주의한다. 추가로 DB Access Framework 내에서는 Disconnected 레코드셋을 사용하므로, 레코드셋을 닫기 전에 Output 매개변수값을 읽어오는 것도 가능하다.

Set DBHelper = new clsDBHelper     'DBHelper 생성

RecordCount = 0        '전체 레코드 개수

 

'매개변수 배열 준비

Dim paramInfo(2)

paramInfo(0) = DBHelper.MakeParam("@Beginning_Date",adDate,adParamInput,0, "1998/01/01")

paramInfo(1) = DBHelper.MakeParam("@Ending_Date",adDate,adParamInput,0, "1998/03/01")

paramInfo(2) = DBHelper.MakeParam("@RecordCount",adInteger,adParamOutput,,0)

'paramInfo(0) = DBHelper.MakeParam("@Beginning_Date",7,1,0, "1998/01/01")

'paramInfo(1) = DBHelper.MakeParam("@Ending_Date",7,1,0, "1998/03/01")

'paramInfo(2) = DBHelper.MakeParam("@RecordCount",3,2,,0)

 

'SP 실행하고 레코드셋을 반환한다.

' 마지막 매개변수가 Nothing이면 기본 연결문자열 사용

Set rs = DBHelper.ExecSPReturnRS("TestSP2", paramInfo, Nothing)

 

'레코드셋 개체(rs) 작업을 수행….

 

'@RecordCount Output 매개변수값 읽어오기

recordCount = DBHelper.GetValue(paramInfo, "@RecordCount"))

 

rs.Close

 

Set rs = Nothing

 

DBHelper.Dispose

Set DBHelper = Nothing

2.3.2.    ExecSQLReturnRS

2.3.2.1.                       설명

SQL 문을 실행하고 레코드셋 개체를 반환한다. 주로 Select 쿼리 수행 시에 사용한다.

Function ExecSQLReturnRS(strSQL, params, connectionString)

l         strSQL : 실행할 SQL

l         params : 매개변수 값의 배열

l         connectionString : 연결 문자열 또는 Connection 개체. Nothing이나 DBHelper 개체의 DefaultConnString 속성을 지정하면 기본 연결 문자열을 사용한다.

2.3.2.2.                       사용 예제

Set DBHelper = new clsDBHelper      'DBHelper 생성

 

'SQL 쿼리를 실행하고 레코드셋을 반환한다.

' 마지막 매개변수가 Nothing이면 기본 연결문자열 사용

Set rs = DBHelper.ExecSQLReturnRS("SELECT * FROM Products", Nothing, Nothing)

 

'레코드셋 개체(rs) 작업을 수행….

 

rs.Close

 

Set rs = Nothing

DBHelper.Dispose

Set DBHelper = Nothing

2.3.3.    ExecSP

2.3.3.1.                       설명

저장 프로시저를 실행하지만, 레코드셋 개체를 반환하지 않는다.

Sub ExecSP(spName, params, connectionString)

l         spName : 실행할 저장 프로시저 이름

l         params : 매개변수 값의 배열

l         connectionString : 연결 문자열 또는 Connection 개체. Nothing이나 DBHelper 개체의 DefaultConnString 속성을 지정하면 기본 연결 문자열을 사용한다.

2.3.3.2.                       사용 예제

@CustomerID라는 매개변수를 받아 Customers 테이블에서 삭제하는 RemoveCustomer라는 SP 있다고 가정하고, 이를 호출하는 코드는 다음과 같다.

Set DBHelper = new clsDBHelper      'DBHelper 생성

 

'매개변수 배열 준비

Dim paramInfo(0)

paramInfo(0) = DBHelper.MakeParam("@CustomerID",adChar,adParamInput,5, "ALFKI")

 

'SP 실행한다.

' 마지막 매개변수가 Nothing이면 기본 연결문자열 사용

DBHelper.ExecSP "RemoveCustomer", paramInfo, Nothing

 

DBHelper.Dispose

Set DBHelper = Nothing

2.3.4.    ExecSQL

2.3.4.1.                       설명

SQL 문을 실행하지만, 레코드셋 개체를 반환하지 않는다. 주로 Insert/Update/Delete 쿼리 수행 시에 사용한다.

Sub ExecSQL (strSQL, params, connectionString)

l         strSQL : 실행할 SQL

l         params : 매개변수 값의 배열

l         connectionString : 연결 문자열 또는 Connection 개체. Nothing이나 DBHelper 개체의 DefaultConnString 속성을 지정하면 기본 연결 문자열을 사용한다.

2.3.4.2.                       사용 예제

Set DBHelper = new clsDBHelper      'DBHelper 생성

 

'SQL 쿼리를 실행한다.

' 마지막 매개변수가 Nothing이면 기본 연결문자열 사용

DBHelper.ExecSQL("DELETE Customers WHERE customerID='ALFKI' ", Nothing, Nothing)

 

DBHelper.Dispose

Set DBHelper = Nothing

2.3.5.    BeginTrans

2.3.5.1.                       설명

Connection Open하고, 새로운 ADO 트랜잭션을 시작한 트랜잭션을 시작한 Connection 개체를 반환한다.

Function BeginTrans (connectionString)

l         connectionString : 연결 문자열 또는 Connection 개체. Nothing이나 DBHelper 개체의 DefaultConnString 속성을 지정하면 기본 연결 문자열을 사용한다.

2.3.5.2.                       사용 예제

다음은 BeginTrans, CommitTrans, RollbackTrans 사용하는 예제이다. BeginTrans 반환한 conn 개체를 ExecSQL, CommitTrans, RollbackTrans 전달하고 있다.

또한 SELECT 쿼리의 경우, 트랜잭션에 포함되면 테이블이 잠기는 현상이 발생하므로 트랜잭션에 포함하지 않도록 별도의 Connection 쓰고 있음에 유의한다(또는 쿼리에서 with nolock 키워드를 사용한다).

Set DBHelper = new clsDBHelper

 

'트랜잭션 시작

'Nothing이면 기본 연결문자열(DefaultConnString) 사용

Set conn = DBHelper.BeginTrans(Nothing)

 

'트랜잭션에 포함될 쿼리를 실행한다.

'마지막 매개변수로 BeginTrans에서 반환한 conn 쓴다.

DBHelper.ExecSQL "Update Customers ….", Nothing, conn

DBHelper.ExecSQL "Update Customers ….", Nothing, conn

 

'트랜잭션에 포함하지 않을 쿼리를 실행한다.

DBHelper.ExecSQL "SELECT ….", Nothing, Nothing

 

 

'특정 조건에 따라 커밋 또는 롤백 수행

If ….. Then

 

'커밋

DBHelper.CommitTrans conn

Else

'롤백

DBHelper.RollbackTrans conn

End If

 

Set DBHelper = Nothing

2.3.6.    CommitTrans

2.3.6.1.                       설명

주어진 Connection 개체의 트랜잭션을 커밋한 Connection 개체를 닫고 Nothing 처리한다.

Sub CommitTrans (connectionObj)

l         connectionObj : 트랜잭션을 시작한 Connection 개체. BeginTrans 반환한 것을 사용한다.

2.3.6.2.                       사용 예제

2.2.5.2 있는 BeginTrans 예제를 참조한다.

2.3.7.    RollbackTrans

2.3.7.1.                       설명

주어진 Connection 개체의 트랜잭션을 롤백한 Connection 개체를 닫고 Nothing 처리한다.

Sub RollbackTrans (connectionObj)

l         connectionObj : 트랜잭션을 시작한 Connection 개체. BeginTrans 반환한 것을 사용한다.

2.3.7.2.                       사용 예제

2.2.5.2 있는 BeginTrans 예제를 참조한다.

2.3.8.    MakeParam

2.3.8.1.                       설명

매개변수로 사용할 배열을 만든다.

Function MakeParam(PName,PType,PDirection,PSize,PValue)

l         PName : 매개변수의 이름

l         PType : 데이터 타입으로 DataTypeEnum 하나

l         PDirection : 매개변수의 종류로 ParameterDirectionEnum 하나

l         PSize : 매개변수의 최대 길이(문자열 매개변수는 지정 필요)

l         PValue : 매개변수의

2.3.8.2.                       사용 예제

Dim paramInfo(2)

paramInfo(0) = DBHelper.MakeParam("@Beginning_Date",adDate,adParamInput,0, "1998/01/01")

paramInfo(1) = DBHelper.MakeParam("@Ending_Date",adDate,adParamInput,0, "1998/03/01")

paramInfo(2) = DBHelper.MakeParam("@RecordCount",adInteger,adParamOutput,,0)

코드는 MakeParam 사용하는 대신 Array 함수를 사용할 수도 있다. Array 함수 사용 반드시 MakeParam 매개변수처럼 5개의 요소를 순서대로 선언해야 한다.

Dim paramInfo(2)

paramInfo(0) = Array("@Beginning_Date",adDate,adParamInput,0, "1998/01/01")

paramInfo(1) = Array("@Ending_Date",adDate,adParamInput,0, "1998/03/01")

paramInfo(2) = Array("@RecordCount",adInteger,adParamOutput,,0)

2.3.9.    GetValue

2.3.9.1.                       설명

매개변수 배열에서 특정 매개변수의 값을 가져온다. 주로 저장 프로시저를 실행 Output 매개변수값을 가지고 사용한다.

Function GetValue(params, paramName)

l         params : 매개변수 배열로 ExecXXXX 명령의 두번째 매개변수로 전달되는

l         paramName : 값을 가져오고자 하는 매개변수의 이름

2.3.9.2.                       사용 예제

2.2.1.2 있는 ExecuteSPReturnRS 예제를 참조한다.

2.3.10.                        DefaultConnString

2.3.10.1.                  설명

DBHelper ExecXXXX 명령 수행 시에 마지막 매개변수가 Nothing이면, 기본 연결 문자열을 사용한다. 기본 연결 문자열 값이 바로 DefaultConnString이다.

Sub Dispose()

2.3.10.2.                  사용 예제

Set DBHelper = new clsDBHelper

DBHelper.ExecSQL query1, Nothing, Nothing                               암시적으로 기본 문자열 사용

DBHelper.ExecSQL query2, Nothing, DBHelper.DefaultConnString   명시적으로 기본 문자열 사용

2.3.11.                        Dispose

2.3.11.1.                  설명

DBHelper 내부적으로 ExecXXXX 명령 수행 시에 마지막 매개변수가 Nothing이면, 기본 연결 문자열(DefaultConnString) 사용해서 DefaultConnection 개체를 만들어 , 명령 수행 시에는 개체를 사용하게 된다. 그래서 페이지를 종료부분에 Dispose 메서드를 호출하여 DefaultConnection 닫고 메모리에서 해제해야 한다.

Sub Dispose()

2.3.11.2.                  사용 예제

아래 코드에서는 Dispose 사용 예제를 보여준다. query1 수행할 DefaultConnection 생성되며, query2에서는 이를 그대로 사용한다. 그러나 다른 연결문자열을 사용하는 query3 경우 새로운 Connection 생성해서 사용하게 된다.

Set DBHelper = new clsDBHelper

DBHelper.ExecSQL query1, Nothing, Nothing       ‘DefaultConnection 생성, Open

DBHelper.ExecSQL query2, Nothing, Nothing          생성된 DefaultConnection 계속 사용

DBHelper.ExecSQL query3, Nothing, DBHelper.AnotherDB       새로운 Connection 개체 생성

 

DBHelper.Dispose                              ‘DefaultConnection 닫고, 해제

Set DBHelper = Nothing

2.3.12.                        collectParams

2.3.12.1.                  설명

매개변수 배열을 Parsing하여 Parameter 개체를 생성한 Command 개체에 추가한다.

Function collectParams(cmd,,argparams)

l         cmd: Command 개체

l         argparams : 매개변수 배열로 ExecXXXX 명령의 두번째 매개변수로 전달되는

메서드는 private 메서드이므로 직접 사용하지는 않는다. 또한 메서드 내의 내용을 수정할 때는 주의하기 바란다.

 

[출처] -  안재우님의 ASP DBHelper Class에 대한 설명입니다.

혹시 일본식 라면집에 가보셨나요? 저는 여자친구와 갔다가 곰탕같은 라면을 시켜먹었는데(으웩...) 그게 8년전 애기입니다.

개인적으로 라면은 우리나라, 삼양라면을 좋아합니다. 가끔 너구리도 먹어주시면 흐뭇해지고요. 그리고 일요일엔 짜파게티죠~

오늘 이 자리를 빌어 저와같이 라면을 좋아하시는(돈이 없어 라면을 드시던간에-_-) 우리나라 프로그래머님들을 위해

저만의 비법을 알려드리겠습니다. 물론 어떤 댓가를 바라진 않습니다. 다만 응용한 결과를 저에게도 알려주시면 고마울뿐이죠.

 

아래는 제가 만든 첫번째 라면입니다.

 

 

소스를 보니 텅~ 빈 생성자 하나에 설명이라는 속성 그리고 가격이라는 메소드가 있군요.

설명이 무안 할 정도로 단순하지만.. 얼마나 복잡하게 될 수 있는지 보기로 하죠.

 

이제 이런 형태로 된장라면, 간장라면, 곰탕라면, 짜장라면, 김치라면, 카레라면, 조개라면, 사노라면, 짬뽕라면,

된장조개라면, 고추조개라면, 짜장된장라면, 고추짜장라면, 고추된장카레조개짬뽕라면.. 등등등..

 

지면상-_- 나머지 라면들을 나열하는것은 생략하고 이 정도만 만들어 보죠.

대충 복사해서 이름하고 가격만 바꿔주면 될듯 보입니다.

자.. 이제 평소에 잘하시는 ctrl+c, ctrl+v 신공을 발휘할때가 왔네요.

 

그런데 가만 들여다보면 단순하진 않습니다. 그렇게 쉽게 해결 될 문제가 아닌거죠.

그 이유로 고추조개라면의 경우로 예를 들면 가격()을 구현해야 하는데

아래 소스에서 보다시피 참조되는 라면들의 가격을 반영해야 합니다.

 

 

만약 고추/된장/카레/조개/간장/양파/해삼/멍게/오징어/짬뽕라면 이라면... 그외

카레/조개/간장/양파/해삼/멍게라면,  해삼/멍게/오징어/짬뽕라면 등등 다양한 조합들이 있으므로

가격이란 메소드 작성이 쉽지 않습니다.

 

더구나 이 글을 보신 분들의 요청에 의해 라면의 종류는 더 늘고 복잡해질지 모릅니다.

 

... 왜 문제가 되는지 생략 ...

 

여기에 이 문제를 해결 할 비법이 있습니다.

 

우선 라면에대해 다시 생각해 볼 필요가 있습니다.

다 알다시피 라면은 어떤 재료가 들어가냐에 따라서 종류가 달라지고 어떤 라면이 될지가 결정됩니다.

그리고 재료는 하나이상 복합적으로 들어갈 수 있고(안들어갈수도 있군요) 이에따라 라면의 가격도 달라집니다.

이 둘의 관계는 달리말하면 라면은 재료에 함수적 종속성을 가지게 됩니다. 재료가 라면을 결정짓게 되는것이죠.

 

따라서 우리는 라면과 재료의 관계를 재정립하고 이를 코드로 분리해서 관리 할 필요성이 있습니다.

라면은 재료에의해 설명도 가격도 달라지므로 둘을 합치거나 또는 서로가 포함관계에 있으면 코드의 중복과

복잡도가 증가하고 재료비나 또는 가격을 계산하는 로직이 변경될 경우 수정에 많은 힘이 드는 상황이 발생합니다.

 

해결책은 아래와 같이 라면과 재료라는 추상화된 객체를 만드는것으로부터 시작합니다.

여기서 핵심은 재료로 라면에 어떠한 행위를 한 뒤에라도 라면으로써 사용이 가능해야 합니다. (현실도 그렇죠)

기본라면에 어떤 재료를 넣어 만들어진 라면도 라면이고, 그렇게 만들어진 라면에 다시 재료를 추가해도 라면이라는 말이죠.

따라서 재료 클래스는 추상화된 라면 클래스를 구현또는 상속받아야 합니다. 재료 as 라면이 성립되어야 하니까요.

또 재료만의 속성과 메소드를 가질수 있으니 이것을 객체로 만드는데는 이의가 없습니다.

 

(인터페이스로 구현해도 됩니다. 하지만 여기선 추상클래스로..)

 

 

이걸 바탕으로 아래와 같이 고추라는 재료를 만들수 있습니다.

재료의 생성자에 가공(요리) 할 대상인 라면의 레퍼런스를 넘겨주는것에 주목 할 필요가 있습니다.

 

 

설명 속성은 넘겨받은 라면에 재료명을 더해주는것만으로 깔끔하게 완성된 코드입니다.

마찬가지로 가격 메소드 또한 가공(요리) 할 라면가격에 재료(고추)의 가격을 더해주면 됩니다.

이렇게 기본라면에 고추를 넣은 후에도 라면으로봐야죠. 가공(요리)된 라면에 다시 고추를 넣어도 라면입니다.

그럼 고추고추라면이 되겠군요. -_- 

 

끝에가면 예제가 있으나 우선 사용예를 보겠습니다.

 

라면 고추라면 = new 고추( new 라면인스턴스() );

라면 고추고추라면 = new 고추( new 고추( new 라면인스턴스() ) );

 

고추고추라면.가격() 메서드를 호출하면 ( 고추재료가격 + ( 고추재료가격 + 라면가격) ) 과 같은 계산이 이뤄지게 됩니다.

 

그럼 고추와 같은 방법으로 조개라는 재료도 만들어보죠.

 

 

자.. 이제 재료도 대충 만들었으니 재료들을 넣을 라면을 만들어 볼 차례입니다.

아직 실체화된 라면이 없었습니다. 여기선 그냥 삼양라면을 만들어 보겠습니다.

 

 

이제서야 라면을 요리 할 시간이 되었군요.

재료도 구비되었으니 이제 라면을 재료로 새롭게 재탄생시킬수 있습니다.

 

 

라면을 고추로 감싸서 고추라면을,

라면을 조개로 감싸고 다시 고추로 감싸니까

고추조개삼양라면이 만들어집니다.

 

고추조개삼양라면을 양파라는 재료 클래스로 감싸주면?

양파고추조개삼양라면이 되겠죠.

 

 

실행 결과화면

 

 

어떤가요?

 

라면 클래스(여기서는 추상화된 라면 클래스를 구현한 삼양라면을 말합니다) 자체는 손대지 않았습니다.

단지 재료로 감싸준것 뿐이죠. 또 단가의 계산도 간결하게 해결 되었습니다.

 

그런데 결과화면을 보니 고추/조개/삼양라면 순이 아니라 읽고 부르기엔 웬지 이상합니다.

이 문제를 수정하기 위해 기존 클래스들을 수정해야 할까요?

우리는 방금 원본 클래스에 손도 대지않고 원하는 기능을 구현하는 방법을 배웠습니다.

따라서 이번에도 그런 방식으로 해보는게 좋겠군요.

 

아래와 같이 DescriptionDeco라는 클래스를 만들어봤습니다.

역시 여기서의 핵심도 라면이라는 추상화 클래스를 상속받고 생성자에선 가공해야 할 라면 클래스를 넘겨받는것이죠.

이렇게 하면 기존 코드의 변경이 일어나지 않습니다.

 

(단지 설명을 출력하는 기능에대한 확장이므로 가격은 변동이 없습니다. 공짜란 말이죠.)

 

적용 예입니다.

 

 

결과화면

 

 

 

* 실행가능한 전체소스를 첨부하였으니 참고하세요.

마무리

 

별로 쓴 내용도 없는데 이걸 작성하는데 2시간이나 걸렸네요. 원래는 대충 설명하고 비주얼한 예제를 쓸려고 했는데

이 속도로는 어렵겠군요. -_-  이외에도 좀 더 많은 할 애기가 있지만 여기서 끊는게 좋겠습니다.

아시는 분들은 이미 아셨을수도 있는데 위 내용은 Head First Design Patterns 책을 약간 패러디한것으로

데코레이션 패턴에 해당하는 내용입니다. 미숙한 글이나마 도움이 되었으면 합니다.

 

 

용어정리

 

추상화 클래스인 라면을 Component,

이 클래스를 상속받아 구현된 삼양라면을 ConcreateComponent라고 합니다.

Decorator는 추상화된 재료 클래스 입니다.

 

일반적으로 Component는 어떤 행위를 정의한 인터페이스와 같이 추상화된 객체일 수 있습니다.

이걸 상속받아 구현된 ConcreateComponent는 Decorator가 감싸게 될 클래스이고요.

 

Decorator Pattern의 경우 객체의 변형없이 상속또는 구현을 통해 객체의 기능을

유연하게 확장 할 수 있는 방법으로 자주 사용됩니다.

실무에서 부담없이 적용해 볼 만한 몇개 안되는 패턴중에 하나입니다.



출처 : 데브피아 유경문 (lazykoder) 씨의 C# 라면만들기 강좌입니다.

웹써핑중에 라면 만들기 강좌를 활용한 좋은 예가 있어 소개해드릴까 합니다.

원래 라면 만들기 강좌 다음 버전은 어렵지 않으면서도 활용하기 좋은 다른 패턴을 쉽게 풀어보려고 했는데

한 놈만 제대로 이해한다면 다른 패턴들을 공부할때 쉽게 수긍 할 수 있는 부분이 많다고 봤습니다.

어째든.. 그냥 긁어다 붙이면 너무 성의가 없으니 기사를 적절히 끊어서 설명하겠습니다.

 Continuing with this idea of showing alternatives using the same example, I thought it might be interesting to show the Decorator Pattern which can be used when the Policy Injection Application Block and PostSharp may be overkill for your application.

In the two examples mentioned above I created a Stopwatch Attribute that timed the ILogger.Write Method used in our console application:

 

public interface ILogger

{

    void Write(string message);

}

 

public class ConsoleLogger : ILogger

{

    public void Write(string message)

    {

        Console.WriteLine();

        Console.WriteLine("*** In Logger ***");

        Console.WriteLine(string.Format

            ("message : {0}", message));

    }

}

 

참고로 이 기사를 쓴 분은 MS의 log 관련 application block을 쓰는 모양인데

저는 log4net을 씁니다. 그래서 관련 코드에대해서는 설명이 부족할 수 있습니다. 

 

음.. 어째든 위 소스를 보니 ConsoleLoggerILogger라는 추상화된 컴퍼넌트를 구현하고 있군요.

아직 데코레이터 클래스는 나오지 않았습니다. 아마 이 놈이 라면이 될 모양입니다.

ConsoleLogger에 Write()라는 기능을 나중에 동적으로 추가시키려고 하는것이겠죠.

 

그런데 라면 만들기때는 abstract class를 사용했습니다. 왜냐하면 공통된 구현이 많아서

최상위 클래스에서 일부 구현을 해주는게 편했기 때문이죠. 어떤 객체를 추상화의 수준을

interface냐 abstract class냐는 상황에 따라 다릅니다. 거의 대부분이 Interface를 쓰지만요.

 

Creating these attributes is pretty simple for use in the Policy Injection Application Block and PostSharp. However, we can avoid the use of these 3rd party libraries by creating a StopwatchLoggerDecorator Class that will achieve the same results:

 

public class StopwatchLoggerDecorator : ILogger

{

    private readonly ILogger _inner;

 

    public StopwatchLoggerDecorator(ILogger inner)

    {

        _inner = inner;

    }

 

    public void Write(string message)

    {

        Stopwatch sw = new Stopwatch();

        sw.Start();

 

        _inner.Write(message);

 

        sw.Stop();

 

        Console.WriteLine();

        Console.WriteLine(string.Format("Time Elapsed: {0} ms", sw.ElapsedMilliseconds));

    }

}

 

아.. 바로 데코레이터가 만들어졌군요. 라면 만들기때와는 달리 데코레이터의 추상화는 없는것 같습니다.

이 경우를 단일 데코레이터라고 합니다. 그런데 라면 만들기때는 재료(데코레이터)가 다양했습니다.

다음과 같이 아무리 감싸도 결국 라면이되는 표현  new 재료2(new 재료1(new 라면()))..

이렇게 되기위해서 데코레이터는 자기가 감싸야 될 클래스의 추상화 클래스를 구현할 필요가 있었습니다.

c# 코드상에서 재료 as 라면과 같은 표현이 가능해야 된다고도 했었죠.

 

라면 만들기에서 재료라는 데코레이터는 다양했었고 각 재료들마다의 개별적인 메서드들이

있을수 있었기 때문에 재료라는 데코레이터의 추상화를 만들어주는것이 필요했습니다.

그리고 각 재료들은 그 추상화된 객체를 구현하도록 했었지요.  

이것을 다중 데코레이터라고 합니다.

 

소스로 돌아가서.. 역시 이번 데코레이터도 ConsoleLogger가 구현한 ILogger를 구현했군요.

이건 공식에 가깝습니다. 이 패턴에서는 자기가 감싸게 될 놈의 추상화 객체를 구현해야만 합니다.

 

이 데코레이터는 Write()가 수행되는 시간을 계산하는 기능이 추가되어있습니다.

핵심은 역시 생성자에서 ILogger라는 자기가 감싸게 될 추상화 객체를 인자로 받는 것이죠.

이런식으로 내부적으로 다른 객체를 생성하지 않고 관계를 맺는것이 모든 패턴들의 특징입니다.

(이런 관계를 is-a 관계라고 하고 반대로 객체안에서 객체를 생성하게 되는것을 has-a관계라고 합니다.)

 

우리는 Write() 기능이 데코레이터 되었다는걸 명확히 알 수 있습니다.

ILogger를 구현한 클래스의 Write()를 재사용하면서 그 기능에 스톱와치 클래스의 기능까지 추가했습니다.

 

그런데 Stopwatch라는 클래스는 우리가 소스를 건들일수 없는 상용 컴퍼넌트였다면..

생각해보세요. Log에 그 기능을 추가하기 위해선 applicatoin block의 소스를 뜯어 고치던가 했어야 했던지

합치지 못하고 분리해서 호출했어야 할지도 모르죠. 데코레이터 패턴을 안다면 그런 생각자체를 못하겠지만..

 

어째든 이름도 잘 지었네요. 이 데코레이터의 이름은 그래서 StopwatchLoggerDecorator 이군요.

이런식으로 네이밍 하는것은 권장되는 사항입니다. 코드만 봐도 그 의도가 드러나기 때문에

따로 주석을 쓸 이유도 없습니다.

 

아래에 이 데코레이터를 적용한 예가 있습니다.

 

The Decorator Class implements the same ILogger Interface and takes the "real" ILogger in its constructor, ConsoleLogger. Inevitably it passes the Write Call onto the real logger, but before doing so starts a Stopwatch. After the call to ConsoleLogger is finished ( _inner.Write ), it stops the Stopwatch and displays the elapsed time in the console.

In keeping with the examples mentioned above, one can register the ILogger in your IoC Container / Dependency Injection Tool of choice. Here is an example using Unity:

 

IUnityContainer container = new UnityContainer();

 

 

// Register Logger

ILogger consoleLogger = new ConsoleLogger();

 

ILogger logger = new StopwatchLoggerDecorator(consoleLogger);

 

container.RegisterInstance<ILogger>(logger,

    new ContainerControlledLifetimeManager());

 

 

// Use Logger

var registeredLogger = container.Resolve<ILogger>();

 

registeredLogger.Write("Hello!");

 

Console.ReadLine();

 

The results are as follows:

 

Decorator Pattern

 

 

위 소스에서 데코레이터를 적용한 부분은 아래와 같습니다.

 

ILogger consoleLogger = new ConsoleLogger();

ILogger logger = new StopwatchLoggerDecorator(consoleLogger);

 

이 부분만 보세요. 라면 만들기때와 마찬가지로 라면을 재료로 요리했군요.

간단히 logger.Write(); 써주면 결과화면처럼 나오게 됩니다.

(Write()의 호출이 원문과 다르긴 하지만.. 실제로 저렇게 써도 되는거니 넘어가죠. -_-)

 

이 기사를 쓴 목적이 ConsoleLogger를 변경하지 않고도 새로운 기능을 추가하는것이였는데 성공적입니다.

그런데 우리의 고객이 이왕 출력되는거 html 출력으로도 보고 싶어합니다.

여러분은 어쩌시겠습니까? Write() 메서드를 오버로딩하시겠다고요? 왜이러시나요..

(한번 해보세요.. 되는지.. -_-)

Write() 메서드에 인자를 받게 만들어 내부에서 if를 써서 분기를 하시겠다고요? 역시 안됩니다.. -_-

HTMLLoggerDecorator를 만드셔야죠. 그리곤 사용은 아래와 같이 쓸수있어야 합니다.

 

new HTMLLoggerDecorator(consoleLogger);

또는 new HTMLLoggerDecorator(new StopwatchLoggerDecorator(consoleLogger))

 

HTMLLoggerDecorator는 어떻게 구현해야 할까요? (html 태그를 어렵게 적용시키려 하지 마세요-_-)

 

참고로.. new StopwatchLoggerDecorator(new HTMLLoggerDecorator(consoleLogger))와 같이 쓰게되면

이상한 출력결과가 나올수 있습니다. 감싸는 순서가 HTMLLoggerDecorator 만든 의도와는 달리 사용이

되었기 때문인데요. 이런 문제들은 팩토리나 빌더같은 다른 생성패턴들과 결합해서 써야 될 필요성이

있습니다. 팩토리나 빌더는 다음에 좋은 예가 떠오르면 글을 올려보도록 하겠습니다.

 

이 글이 패턴을 이해하는데 조금이나마 도움이 되었기를 바랍니다.

 

전체 기사 링크

http://codebetter.com/blogs/david.hayden/archive/2008/09/30/decorator-pattern.aspx


출처 : 데브피아 C#마을 유경문 (lazykoder) 씨가 기사 내용을 강좌 형식으로 풀어 써주셨습니다.

Mass SQL 인젝션으로 삽입된 악성코드 일괄 삭제 하기!

최근 쿠키 취약점을 토대로 들어오는 Mass SQL 인젝션의 경우, 기존 DB 내용을 변조하지 않고, 악성코드가 덧붙여 삽입되는 형식이기 때문에 악성코드 일괄 삭제 쿼리로 손쉽게 정상 상태로 복원이 가능하다. 하지만 SQL 인젝션 특성 상 언제 또 코드가 바뀔지 모르는 일이기 때문에 현재에 안주해서는 안 된다. 반드시 웹 취약점을 보완해야 한다는 것을 명심하시길…

SQL 인젝션으로 인해 DB 내용이 변조되었을 때 SQL 인젝션 코드를 역으로 이용하여 악성코드만 삭제하는 쿼리다. 참고로 DB 내용의 일부분이 손상 및 변조되고, 악성코드가 그 자리에 치환되는 수법이라면 정상 상태로 복원되지 않는다.

당시 이 쿼리를 만들기 위해 여러 웹사이트들을 참조했었는데 죄송스럽게도 지금은 정확하게 기억이 나지 않아서… (만일, 본인이 만들었던 스크립트라면 댓글 부탁 드립니다. 출처 명시해 드리겠습니다.)

이하 주의 사항 입니다.

이 스크립트의 단점이라 하면 로그인 아이디가 해당 DB에 DBO 권한을 가지고 있어야 하며 테이블의 소유자가 DBO로 설정되어 있어야 합니다. 물론 그 부분를 보안하기 위해 추가 처리가 되어있긴 합니다만, 필히 참고해 주셔야 하는 부분이므로 미리 공지합니다. 또한 SA 계정이나 윈도우즈 로그인 등을 통해 DB 서버의 최상위 권한으로 쿼리를 실행시켜 주어야 합니다. 그 외 중요한 부분들은 주석으로 설명이 되어 있으니 필히 주석을 참고해 주십시오. 궁금하신 사항들은 댓글로 부탁 드립니다.

이미 로그인 아이디가 DBO 권한을 가지고 있고, 테이블의 소유자가 DBO로 설정되어 있는 경우와 그렇지 않을 경우로 나누어 SQL 파일을 공개합니다. 다운로드에 유념해 주시기 바라며 실제 스크립트를 돌리시기 전에는 미리 원본 DB를 반드시 백업 받아 두세요.

 [다운로드1] 로그인 아이디가 해당 DB의 DBO 권한을 가지고 있고, 테이블의 소유자가 DBO로 설정되어 있는 경우.

 [다운로드2] 로그인 아이디가 해당 DB의 DBO 권한을 가지고 있지 않고, 테이블의 소유자 역시 해당 로그인 아이디로 설정되어 있는 경우.

 

이상입니다. 그럼~


  • 다중 파일 어셈블리

    • myAseembly.dll에 포함된 세 개 파일 모두 하나의 어셈블리에 속함

    • 하지만 파일 시스템에서는 이들 파일을 세 개의 개별 파일로 인식

    • Util.netmodule 파일은 아무러 어셈블리 정보를 포함하지 않기 때문에 모듈로 커파일

    • 어셈블리가 만들어질 때 MyAssembly.dll과 Util.dll및 Graphic.bmp와의 관계를 나타내도록 어셈블리 매니페스트가 MyAssembly.dll에 추가된다.

    • 코드 디자인시 단일 파일 어셈블리로 구성할 지, 다중 파일 어셈블리로 구성할 지, 다중 파일 어셈블리로 구성한다면 어떻게 구조화할 것인지를 결정해야함.

 

  • namespace와 assembly

    • namespace : data type들을 그룹화하는 논리적 개념

      • 하나의 네임스페이스는 여러 개의 어셈블리로 구성되어 질 수 있고,

      • 하나의 어셈블리는 여러 개의 네임스페이스로 구성되어질 수 있다.

  • 다중 파일 어셈블리 만들기

    • 다른 언어로 작성된 모듈을 결합하려는 이유로 사용

    • 자주 사용되지 않는 data type에 대해 필요할 때만 다운로드 되도록 모듈로 분리하고자 할 때 사용

    • Enterprise 환경에서 개발시 여러 개발자가 각각의 모듈로 생성하고, 이를 하나의 어셈블리로 통합

    • 생성 방법

      • 1단계 : 어셈블리를 구성하는 부속 파일에 대한 모듈 생성

      • 2단계 : 다른 모듈에 대한 참조를 사용하여 주 모듈 컴파일

        • /addmodule : 옵션을 사용하여 1단계에서 생성한 부속모듈에 대한 reference 추가

      • 3단계 : 어셈블리 링커를 사용하여 다중 파일 어셈블리 만들기

    • 1단계 : 어셈블리를 구성하는 부속파일에 대한 모듈 생성

 csc /t:module MyCode.cs // MyCode.netmodule 이라는 모듈 생성

 csd /out:util.netmodule /t:module MyCode.cs  // 모듈명 지정하여 생성

    • 2단계 : 부속 모듈에 대한 참조를 사용하여 주 모듈 컴파일

 csc /addmodule:util.netmodule /t:module Client.cs

 // Client.netmodule을 생성하면서 util.netmodule에 대한 reference 추가

      • 위 두 과정을 하나의 명령문으로 처리 가능

 csc /out:Client.exe Client.cs /out:util.netmodule MyCode.cs

 

    • 3단계 : 어셈블리 링커를 사용하여 다중 파일 어셈블리 만들기

      • al<moudle name> <module name>...

      • /out:<file name>

      • /main:<method name>

      • /target:<assembly file type>

 al Client.netmodule util.netmodule /main:MainClientApp.Main /out:MyAssembly.exe /target:exe

 

 

  • 어셈블리 링커

    • Assembly Linker는 모듈 또는 리소스 파일인 하나 이상의 파일에서 어셈블리 매니페스트가 있는 파일을 생성

      • /out : 필수옵션으로 AL이 만드는 파일의 이름을 지정

      • /embed : 모듈이 지정하는 리소스를 어셈블리 매니페스트가 포함된 이미지에 포함시킨다.

      • /link : 리소스 파일을 어셈블리에 링크

      • /main : 모듈을 실행 파일로 변환할 때, 실행 파일의 진입점에 해당하는 method

      • /target : 출력 파일의 파일 형식을 lib(코드 라이브러리), exe(콘솔 응용 프로그램) 또는 win(Windows 기반 응용 프로그램)으로 지정, default는 lib

 

[첨부 파일 : 소스 파일]

출처 : (주)인터데브 솔루션 개발 사업부 박준호님의 동영상 강좌

  • COM에 대하여

    • 이미 사용중인 검증된 코드의 재사용을 위해 COM 활용

    • 서로 다른 언어로 작성된 바이너리의 타입을 공유하기 위한 방법으로 COM 서버 생성

    • 버전관리의 문제

      • Client에서 COM 서버를 호출할 때 COM 서버의 버전을 확인할 방법이 제공되지 않는다.

      • 때문에 새로운 버전의 COM 서버를 설치하면 타입라이브러리를 새로 수정하고, 레지스트리를 업데이트하며, 상황에 따라 Client의 코드를 수정하는 상황도 발생(DLL 지옥)

    • 배포의 문제

      • COM 서버의 정보를 레지스트리에 등록하는 과정은 어렵지 않다.

      • 하지만 COM 서버의 위치나 이름이 바뀌게 되면, 레지스트리 변경이 쉽지 않게 된다. 이 경우 이 COM 서버를 접근하는 Client의 구성정보를 모두 변경해야 한다.

 

  • 어셈블리(Assembly) 특징

    • DLL 지옥의 탈출

      • 같은 COM DLL에 대한 서로 다른 버전을 동시 제공 가능

      • Client가 원하는 버전을 파악하여 해당 버전의 COM DLL을 정확히 로드

    • 레지스트리에 등록하지 않음

      • 어셈블리는 레지스트리 등록 대신 Assembly 내에 자신에 대한 메타데이터를 포함하게 된다.

      • 배포는 해당 파일을 원하는 위치에 복사하는 것으로 끝남.

    • COM 처럼 dll과 exe의 형태

    • 배포의 단위로써 코드 재사용 및 버전 관리를 가능하게 하는 단위

    • class 접근제한자인 internal의 허용 단위

 

  • Assembly Architecture

    • 하나의 assembly는 다중 모듈로 구성

      • 모듈(module)이란 유효한 파일의 이름으로 소스코드 파일이나 리소스(예 : 이미지 파일)

    • 일반적인 어셈블리는 네 가지 요소로 구성

    • 단일 파일 어셈블리

      • assembly metadata

      • type metadata

      • MSIL 파일

      • 리소스

    • 다중 파일 어셈블리

      • 자주 사용되지 않는 유틸리티 코드를 다른 모듈(별도의 소스파일)로 분리

      • size가 큰 리소스를 분리

      • .NET Framework에서는 파일이 참조될 때만 다운로드 하므로 이처럼 자주 참조되지 않는 코드와 리소스를 다중파일 어셈블리로 분리하여 구성하면 코드를 최적화 시키는 것이 가능

 

  • 어셈블리 매니페스트(Assembly Manifast)

    • Assembly의 핵심요소

      • 어셈블리 참조에 필요한 모든 정보를 내장

      • ID : 이름, 버전, 컬처, 공개 키

      • 파일목록 : 하나의 어셈블리는 하나 이상의 파일로 구성

      • 참조된 어셈블리 목록 : 외부에서 참조되는 어셈블리에 대한 정보

      • Permission 정보 : 이 어셈블리를 실행하기 위해 필요한 권한 정보

      • 형식 참조 정보 : reference와 그 reference의 선언/구현 사항이 포함된 파일을 매핑하는 정보

 

  • 매니페스트의 저장 방법

    • 단일 파일 어셈블리에서는 어셈블리를 구성하는 파일에 포함

    • 다중 파일 어셈블리에서는 별도의 독립실행형 매니페스트를 구성하거나 어셈블리를 구성하는 하나의 dll에 저장될 수 있다.

 

  • 단일 파일 어셈블리 만들기

    • 어셈블리 매니페스트, 형식 정보, 구현 코드 등의 하나의 파일로 구성된다.

    • 생성

csc MyCode.cs // MyCode.cs 모듈에 대한 어셈블리(MyCode.exe) 생성

 

    • 출력 파일명 지정

csc /out:MyAssembly.exe MyCode.cs

 

    • 라이브러리 생성

                    csc /out:MyCodeLibrary.dll /t:library MyCode.cs

 

 

  • 공유 어셈블리와 전용 어셈블리

    • 공유 어셈블리(shared assembly)

      • 재사용 가능한 코드에 대한 어셈블리의 생성(COM의 dll, exe처럼)

      • SN(Strong Name)을 가지고 있어야 한다.(sn.exe)

        • 버전 정보를 포함하고 있어야 한다.

      • GAC(Global Assembly Cache)에 등록해야 한다.(gacutil.exe)

    • 전용 어셈블리(private aseembly)

      • 응용 프로그램과 동일한 디렉토리 또는 서브 디렉토리에 존재

      • 버전관리나 클래스 이름 충돌의 문제가 없음

      • 특정 응용 프로그램에 종속적인 어셈블리를 구성할 때 사용

 

  • 어셈블리 정보 확인

    • Intermediate Language Disassembler Utility

    • ILDasm.exe

      • GUI를 통해 .NET assembly(EXE 혹은 DL)의 정보를 확인할 수 있다.

 

  • 버전 관리

    • 소스 코드 내에서 직접 입력 가능

    • [assembly:AssemblyVersion("1.0.1.0")]

      • 주버전.부버전.수정버전.빌드번호

      • 수정버전과 빌드번호는 *로 생략가능

    • [assembly:AssemblyVersion("1.0.*")]

    • 공유 어셈블리의 경우 버전이 다를 경우 함께 사용할 수 있다(서로 버전의 library를 함께 서비스 할 수 있다.)

 

[첨부 파일 : 소스 파일]

출처 : (주)인터데브 솔루션 개발 사업부 박준호님의 C# 동영상 강좌

  • 전처리기 지시문(Preprocess Directive)

    • Compile 시에만 처리되는 명령문

    • 특정 조건에서만 Compile 되도록 처리하기 위한

    • 여러 버전의 Apllication 생성을 위해

      • standard Edition

      • Enterprise Edition

    • 디버깅 정보를 제공하기 위해

      • Debug를 위한 버전(debugging을 위한 기능 추가)

      • Release를 위한 버전(Debugging을 위한 기능 제거)

    • #으로 시작되는 지시문을 제공

 

  • #define, #undef

    • Compiler에게 해당 기호의 정의 및 제거를 알림

 #define DEBUG            // DEBUG 기호의 정의

 #define ENTERPRISE   // ENTERPRISE 기호의 정의

 #define STANDARD     // STANDARD 기호의 정의

 #undif DEBUG             // DEBUG 기호의 제거

 #undif ENTERPRISE    // ENTERPRISE 기호의 제거

 #undif STANDARD       // STANDARD 기호의 제거

 

    • #if, #endif, #else, #elif 등과 함께 사용

    • attribute와 함께 사용 가능하다

 

  • #if, #endif, #else, #elif

    • 특정코드만 Compile되도록 조건을 명시

 #define ENTERPRISE

 

 #if (ENTERPRISE && STANDARD)

          Console.WriteLine("ENTERPRISE 버전입니다.");

 #elif (STANDARD)

          Console.WriteLine("STANDARD  버전입니다.");

 #else

          Console.WriteLine("TRIAL 버전입니다.");

 #endif

 

  • #warning, #error, #line

    • Compile 시 사용자 정의 메시지를 출력하기 위한 용도

 # if (ENTERPRISE && STANDARD)

          #error ENTERPRISE와 STANDARD가 같이 정의되었습니다.

          Console.WriteLine("ENTERPRISE 버전입니다.");

 #elif (STANDARD)

          #warning STANDARD 버전을 생성하고 있습니다.

          Console.WriteLine("STANDARD  버전입니다.");

 #else

          Console.WriteLine("TRIAL 버전입니다.");

 #endif

 

 #line 24 "여기는 24번 라인입니다." // 24번 라인이 compile 된다는 표시

 

  • #region, #endregion

    • 하나의 코드블럭을 형성하는 단위를 나타냄

    • Visual Studio.NET에서 코드블럭의 확장/축소가 가능함

 

 

[첨부 : 소스 파일]

출처 : (주)인터데브 솔루션 개발 사업부 박준호님의 C# 동영상 강좌

 

  • 객체의 생성과 소멸

    • 객체의 생성

      • new 연산자를 사용하여 class의 instance인 객체(object)를 생성

      • 이 때 객체가 생성되는 곳이 Managed Heap

    • 객체의 소멸

      • C++ 같은 경우 소멸자(destructor)를 정의하여 메모리 관리(리소스 해제)가 가능

      • C# 에서는 Garbage Collector가 메모리를 관리

        • 소멸자가 존재하지만 언제 호출되는지 예측할 수 없다.

        • 때문에 소멸자는 사용하지 않을 것을 권장

      • 불가피하게 객체의 소멸 작업을 해주어야 하는 경우

        • DB, 파일 및 네트워크 연결과 같은 것은 Unmanaged Resource 리소스 해제 시

        • IDisposable interfaceDispose() 사용

 

  • Garbage Collector

    • garbage

      • new 연산자를 통해 객체를 생성(managed heap)하여 사용하다가 해제된 메모리 공간

    • garbage collector

      • garbage collector는 managed heap이 부족하다고 판단되는 시점에 불필요한 객체의 Finalize()를 호출하여 객체를 소멸시키는 작업을 수행

    • 강제적인 garbage collector 작동

      • System namespace의 GC Class에 있는 Collect() method를 호출하여 garbage collector를 강제적으로 작동 시킬 수 있다.

      • 시스템에 성능 저하를 유발할 수 있어 불가피한 상황이 아니라면 강제 호출은 피해야 한다.

        • 예) 대용량의 객체를 사용한 뒤 시스템 부하를 줄여야 하는 상황

 

  • Gabarge Collection과 Generations

    • 생성된지 오래된 객체와 최근에 생성된 객체를 비교할 때 오래된 객체가 더 오랫동안 heap 영역에 남을 가능성이 높다.

      • Main()에 생성된 객체와 다른 method에서 생성된 객체를 비교

    • 0세대 : 최근에 생성된 객체, 단 한번도 garbage collection에 mark된 적이 없는 것

    • 1세대 : garbage collection이 mark했으나 아직 살아있는 객체, 즉 heap에 여유공간이 많아 제거하지 않은 객체

    • 2세대 : garbage collection이 mark한 후 , 한번 체크하고 갔지만 아직 살아있는 객체

    • garbage collection이 일어나면

      • 먼저 0세대에 해당하는 객체들을 mark하고 리소스 해제, 남은 객체는 다음 세대로 바꿈

      • 0세대 객체를 해제한 뒤에도 heap이 부족하면 1세대 객체도 mark후 해제

      • 그래도 부족하면 2세대도 mark 후 해제

 

  • System.GC class member

    • Collect()

      • GC가 heap에 있는 객체에 대해 FInalize()를 호출하게 함

      • 또한 특정 세대의 객체를 해제

    • GetGeneration()

      • 객체가 현재 속해있는 세대를 리턴

    • MaxGeneration

      • 시스템에 있는 2세대(최종 세대)를 리턴

    • ReRegisterForFinalize()

      • 현재 객체가 Finalize()를 호출하지 못하도록 되어 종료될 수 없을 때 종료 가능(finalizable) 상태로 재 등록

    • SupperessFinalize()

      • 현재 객체의 Finalize()를 호출하지 못하도록 설정

    • GetTotalMemory()

      • heap 영역의 사용중인 전체 memory를 리턴(byte 단위)

 

  • 소멸자(destructor)

    • new 연산자를 사용하여 객체를 생성하면 managed heap이라는 영역에 할당한다. 그 뒤

    • .NET Runtime은 자동적으로 Finalize() method를 지원하는가(소멸자가 구현되었는가)를 체크

    • 지원할 경우, 해당 객체의 reference를 finalization queue에 저장

    • GC가 garbage collection을 수행할 때 이 queue에 있는 객체들부터 처리하기 위해 소멸자를 호출

    • 때문에 소멸자만 믿고 기다리는 것은 좋은 방법이 아님

      • 더욱이 lock과 관련있는 작업이라면...

    • 소멸자는 Compile되면 Finalize()로 변환된다

    • Finalize()는 명시적으로 호출할 수 없기 때문에 소멸자를 사용해야 한다.

 class AnyClass{

          ~AnyClass(){

                          // 정리작업 수행

          }

}

 

 

 

 protected override void Finalize(){

            try{

                        // 정리작업 수행

            }

            finally{

                        base.Finalize();

            }

}

 

 

  • IDisposable interface와 Dispose()

    • GC는 관리되는 객체가 더 이상 사용되지 않을 경우 해당 객체에 할당된 리소스를 자동으로 해제하지만 garbage collection이 언제 발생할 지 예측할 수 없다

    • DIspose()는 코드내에서 리소스를 해제하도록 명시하는 방법

    • 객체가 더 이상 사용되지 않는 시점에 바로 리소스가 해제됨

    • Dispose()는 해당 객체의 GC.SuppressFinalize 메소드를 호출해야 한다

      • GC가 자동으로 호출하는 Finalize()는 리소스를 많이 소모하기 때문에 Dispose()를 통해 개체가 이미 정리된 경우라면 GC에서 개체의 Finalize 메소르드를 호출할 필요가 없다.

      • 현재 finalization queue에 있는 경우(소멸자가 구현된 경우) GC.SupperessFinalize()는 GC에 의해 Finalize 메소드가 호출되는 것을 억제

    • DIspose()를 구현할 때

      • class에 포함된 모든 리소스는 물론,

      • 상속 받았다면 base class의 Dispose()도 호출하여

      • 해당 base class의 모든 리소스도 해제해 주도록 한다.

 

[첨부 : 소스 파일]

출처 : (주)인터데브 솔루션 개발 사업부 박준호님의 C# 동영상 강좌



 
  • Exception(예외 처리)
    • Exception : Application이 실행중일 때 발생하는 오류를 처리하는 것
    • .NET에서 제공하는 구조화된 예외처리 방법
    • .NET에서 exception은 하나의 object
    • exception은 어떤 비정상적인 error 상황이 발생되었을 때, 그 문제에 대한 정보를 제공하는 객체
    • system이나 사용자가 정의한 모든 exception은 System.Exception에서 파생된 것


  • System.Exception class member property
    • HelpLink
      • 현재 예외와 관련된 도움말 파일에 대한 링크를 설정하거나 리턴
    • Message
      • 현재 발생한 Exception을 설명하는 text를 리턴
    • Source
      • Exception을 발생시킨 application이나 객체 이름을 설정하거나 리턴
    • StackTrace
      • Exception을 발생한 method의 stack 상세 정보를 리턴
    • TargetSite
      • Exception을 발생시킨 method name을 리턴
    • InnerException
      • 현재 예외를 발생시키는 Exception의 인스턴스를 리턴

 

  • try, catch, finnaly 개요
    • try : 프로그램이 수행되는 일반적인 코드
    • catch : exception 발생시 처리하는 코드
    • finally : 리소스를 해제하는 것과 같이 exception 발생 유무를 떠나 수행되어야 하는 코드

 

       

      try{

               // 일반 코드, exception 발생 가능성이 있는 코드

      }

       

      catch{

               // exception 발생시 처리되어야 하는 코드

      }

       

      finally{

               // exception 발생 유무와 상관 없이 수행되어야 하는 코드

      }

       

 

  • catch
    • 중첩된 exception 처리기를 지정할 수 있다.
    • 이때 지정된 순서대로 exception 처리기를 찾아 자신에 알맞는 처리기를 수행하고 더이상의 처리기를 찾지 않는다.
    • 때문에 catch를 다중으로 정의할 때, 상세한 것을 처리하는 처리기를 먼저 지정하고, 보다 일반적인 처리기는 뒤에 지정한다.
       

      catch(IndexOutofRangeException e){

                       // 배열 첨자가 넘어섰을 때 Exception 처리

      }

      catch(FormatException e){

                       // 형변환 오류시 Exception 처리

      }

      catch(Exception e){

                       // 일반 예외 처리

      }

      catch{

                       // C++ 등 Exception이 관리되지 않는 라이브러리에서 발생된 것 처리 

      }

 

 

  • 중첩된 try catch finally 구문
     

    try{

           // A : F, G 수행

           try{

                     // B -> 안쪽 Catch에서 exception 처리기를 찾아 실행, 안쪽에 처리기가 없다면 안쪽 finally를 수행하고,

                                 바깥쪽 catch에서 exception 처리기를 찾음. 이 때, E부분은 수행되지 않음.

           }

            catch(FormatException e){

                     // C -> 안쪽 finally를 수행하고, 바깥쪽 catch에서 exception 처리기를 찾음. 이 때, E 부분은 수행되지 않음.

           }

            finally{

                     // D -> 바깥쪽 catch 에서 exception 처리기를 찾음. 이 때, E 부분은 수행되지 않음

           }

            // E : F, G 수행

    }

    catch(Exception e){ // F 예외처리      }

    finally{ // G 정리작업                        }

 

 

  • custom exception
    • 별도의 class에 처리되기 원하는 예외 상황에 대한 상세하게 캡슐화하는 것
    • System.Exception class에서 파생
    • custom exception class내에 catch 블록에서 사용할 member field나 method를 정의하거나 overridding
    • 단순히 base class의 member를 호출하여 정의할 수도 있음

 

  • throw exception
    • 코드가 실행중일 때 일정한 조건을 충족하지 않는 경우에 throw 문을 통해 원하는 타입의 예외를 발생시킬 수 있다.
    • throw 키워드 뒤에 해당하는 exception의 instance를 생성하면 된다.

   throw new IndexOutOfRangeException("배열 첨자가 잘못되었습니다.");

 


출 처 : (주) 인터데브 솔루션 개발 사업부 박준호님 C# 동영상 강좌

박준호님의 C# 강좌 - 이벤트(Event)

프로그램&DB/C# 2009. 4. 14. 17:09 Posted by Josep.H.S
  • Event
    • 어떤 사건이 발생했을 때 Application에 통보하는 방법
      • 이벤트 발생기(publisher) : 이벤트 발생시 다른 객체에 이벤트 발생을 통보하는 일을 담당하는 객체
      • 이벤트 처리기(Subscriber) : 이벤트 발생시 실제 호출될 method(이벤트 핸들러)가 등록된 객체

 

  • Event 선언
    • Event와 함께 사용될 delegate의 정의 확인
      • delegate는 호출할 method의 형식만 정의한 것이기 때문에 해당 method의 형식이 이미 지정된 delegate가 있다면 그대로 사용이 가능하다

public delegate void AnyEventHandler(object sender, EventArgs e);

 

    • event 키워드를 사용하여 class에 event member 정의
      • public, protected, private, internal 등의 접근제한자와 static, new 등의 키워드

class MyClass{

         public event AnyEventHandler MyEvent;

}

  • Event 호출
    • class에 event를 선언하면 해당 event를 지정된 delegate 형식의 member field처럼 사용 가능
    • 이 class의 객체를 생성하는 클라이언트가 delegate를 event에 연결하지 않았으면 event는 Null이 된다.
    • delegate를 event에 연결하였다면 delegate를 참조하게 된다. 따라서 event 호출은 일반적으로 우선 Null인지 확인한 다음 event를 호출하여 실행

protected virtual void AnyMethod(EventArgs e){

        if(MyEvent != null)

                MyEvent(this, e);

}

    • event는 해당 event를 선언한 class에서만 호출 가능

 

  • Event 연결
    • event를 선언한 class 밖에서 event는 emmber field처럼 보이지만 할 수 있는 작업은 delegate를 추가하거나 제거하는 일만 가능하다
      • delegate 추가

anyobject.MyEvent += new AnyEventHandler(CalledMethod);

      • delegate 제거

anyobject.MyEvent -= new AnyEventHandler(CalledMethod);

 

  • Event와 Delegate
    • Event를 발생시키는 코드는 delegate instance를 호출하는 것과 동일
    • event member가 정의된 class를 사용하는 측에서 이 class의 instance가 발생하는 event를 받기 위해서는 delegate instance를 생성하고 event member에 delegate instance를 추가해야 한다.

 

  • Event guideline
    • Event Handler(이벤트 발생시 호출되는 method)는 .NET의 event guideline에 따라 아래의 두개의 parameter를 받는 메쏘드로 정의 된다.
      • object class type parameter sender
        • 동일한 Event Handler가 복수의 이벤트 발생기에 등록되어 질 수 있으므로 sender는 일반적으로 Event를 발생한 이벤트 발생기 객체에 대한 참조를 포함한다.
      • EventArgs class의 sub class type parameter e
        • 이벤트 발생기로부터 Event Handler로 전달할 부가적인 정보

void Onclick(object sender, EventArgs e){

          // 이벤트를 처리하는 코드

}

 

 

[파일 첨부 : 소스 파일]

출처 : (주)인터데브 솔루션 개발 사업부 박준호님의 C# 동영상 강좌