…    일반 문자열 자체를 의미한다.

 

예:    abc - 'abc', 'aabc' (O)

 

.    개행 문자 (즉 “\n")를 제외한 임의의 문자 한 개와 매치한다.

 

예:    a.c - 'abc' (O), 'ac' (X)

 

*    바로 앞에 있는 패턴이 0번 혹은 그 이상 반복되는 것을 의미한다.

 

예:    ab*c - a + b가 0개 이상 + c

 

[]    대괄호 안에 있는 임의의 문자와 매치하는 문자 클래스이다. 그러나 첫 번째 문자가 캐럿(“^”)이면 대괄호 안에 있는 문자를 제외한 어떠한 문자와도 매치한다는 의미가 된다. 대시(“-”)가 대괄호 안에 존재하면 대시 앞뒤 문자 사이의 범위를 가리킨다. 예를 들어, “[0-9]”는 “[0123456789]”와 동일한 의미이다. 문자 클래스에 대시나 대괄호(“]”)를 포함할 수 있도록 하기 위해서, “[” 바로 다음에 나타난 문자가 “-” 혹은 “]”이면 각각의 특별한 의미 없이 문자 그대로 해석한다. POSIX는 비 영어권 문자를 다룰 때 유용한 특수 대괄호를 도입하였다. 대괄호 안에서는 “\” 문자로 시작하는 C 언어의 이스케이프 시퀀스를 제외한 나머지 모든 메타 문자들이 특별한 의미를 상실한다.

 

예:    a[bcd]c - a + b, c, d 중 한 문자 + c

예:    a[^bcd]c - a + b, c, d가 아닌 한 문자 + c

예:    a[a-z]c - a + a ~ z 중 한 문자 + c

 

^    행의 시작에서 정규 표현식의 첫 번째 문자가 매치한다. 한편 대괄호 안에서는 부정의 의미로 사용된다.

 

예:    ^123 - 123, 1230 (O) | 0123 (X)

 

$    행의 끝에서 정규 표현식의 마지막 문자가 매치한다.

 

예:    123$ - 123, 0123 (O) | 1230 (X)

 

{}    괄호 안에 하나 또는 두 개의 숫자를 담고 있을 경우 괄호 앞에 있는 패턴이 몇 번 매치해야 하는지를 가리킨다. 예를 들면 A{1, 3}의 경우 문자 A가 최소 한 번에서 연속 세 번까지 나타나야 함을 뜻한다. 중괄호 안에 특정 이름을 담고 있다면, 그 이름으로 대체한다는 것을 의미한다.

 

{n}    {n} 바로 앞의 문자가 n개다.

 

예:    ab{2}c - a + b가 두 개 + c

 

{n,}    {n,} 바로 앞의 문자가 n개 이상이다.

 

예:    ab{2,}c - a + b가 두 개 이상 + c

 

{n, m}    {n, m} 바로 앞의 문자가 n개 이상 m개 이하이다.

 

예:    ab{2,4}c - a + b가 두 개 이상 4개 이하 + c

 

\    메타 문자들의 의미를 파기하고 C 언어 이스케이프 시퀀스를 표현하는 데 사용한다. 예를 들어, “\n"은 개행 문자이지만 ”\*“은 별표 그 자체를 나타낸다.

 

예:    \*, \+, \^

 

+    앞에 있는 정규 표현식이 한 번이나 그 이상 반복하여 매치할 수 있음을 의미한다. 예를 들면 [0-9]+ 와 같이 쓰일 수 있다. 이 패턴은 “1”, “111” 혹은 “123456”에 매치하지만, 빈 문자열에는 매치하지 않는다. 만약 플러스 기호 대신 별표를 이용하면 빈 문자열도 매치된다.

 

예:    ab+c - a + b가 1개 이상 + c

 

?    앞에 있는 정규 표현식이 0번이나 한 번 나타날 수 있음을 의미한다. 예를 들어 -?[0-9]+ 와 같은 패턴은 숫자 앞에 마이너스 기호를 붙이거나 붙이지 않을지를 결정할 수 있다.

 

예:    ab?c - a + b가 없거나 1개 + c

 

|    앞에 있는 정규 표현식 혹은 뒤에 있는 정규 표현식에 매치한다.

 

예:    cow|pig|apple - cow 또는 pig 또는 apple

 

“...”    따옴표 안에 있는 문자들은 C 언어의 이스케이프 시퀀스를 제외한 모든 메타 문자의 의미를 파기하고 문자 그대로 해석한다.

 

/    ‘/’ 뒤에 있는 정규 표현식이 매치하는 경우에 한해 ‘/’ 앞의 정규 표현식에 매치한다. 예를 들면 0/1의 경우 “01”과 같은 문자열 안에 있는 “0”에 매치하지만, “0” 또는 “02”아 같은 문자열에는 매치하지 않는다. 슬래시 뒤에 있는 패턴과 매치하는 부분은 “사용되지” 않으며, 다음 토큰 검색 과정에서 사용된다. 패턴 한 개 당 슬래시 하나만 허용된다.

 

()    정규 표현식 여러 개를 새로운 정규 표현식 한 개로 묶는다. 예를 들어 (01)의 경우 문자 시퀀스 01을 의미한다. 괄호는 *, +, 그리고 |와 함께 사용하여 복잡한 패턴을 구성할 때 매우 유용하게 쓰인다.

 

(?<GroupName>...)    그룹 매칭을 할 때 <GroupName> 부분이 나중에 프로그래밍 방식으로 결과를 탐색할 때 기준으로 사용된다. 뒤이어오는 ... 부분에 식을 쓴다.

 

예:    (?<First>[0-9]{6})\-(?<Last>[0-9]{7})

 

앞서 나열한 연산자 중에서 몇몇은 ([] 처럼) 문자 하나에 대해서만 작동하는 반면 다른 연산자는 정규 표현식 전체를 상대로 작동한다는 사실에 주목하기 바란다. 보통 복잡한 정규 표현식은 간단한 정규 표현식 여러 개를 모아서 만든다.



출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1547&page=4

Posted by Sting!
,

3. 상속과 interface의 문제점

3.1. 상속

3.1.1. 상속에 있어서의 생성자 (constructor)

"child의 default 생성자가 있으면 그 생성자에는 parent의 생성자 (base()) 호출이 compile시 자동으로 삽입된다." 인라인 초기화 구문 상에서 지정해 놓은 내용이 있다면 이 내용들은 기본 생성자보다 앞서서 처리된다. 처리 순서를 요약하면 다음과 같다.

  1. 자식 클래스의 멤버 필드 내 인라인 표현식
  2. 상위 클래스의 멤버 필드 내 인라인 표현식
  3. 자식 클래스 생성자에서 지정한 상위 클래스 생성자의 호출
  4. 자식 클래스 생성자의 호출
3.1.2. down cast는 본질적으로 매우 위험하다.

down cast - child의 type으로 parent를 casting - 는 parent 형태의 type이 정말 child type인지 compile시에는 알 수 없다. 실행 시에 type check가 이루어지므로 runtime시에 InvalidCastException이 발생할 가능성이 커진다.

 

"프로그래밍 시 오류는 가능한 compile 시에 처리하는 것이 좋다."

 

덧. down cast에 대한 방비책이 몇 가지가 있다. 다음은 그 예시이다.

  • 실제 형식 변환 이전에 해당 개체가 정말 지정한 형식과 호환성이 있는지 확인하는 방법: is 연산자를 통하여 주어진 개체를 해당 형식으로 down cast하는 것이 안전한지의 여부를 boolean 값으로 평가할 수 있다.
  • 형식 변환 실패 시 null 참조로 fallback하도록 만드는 방법: as 연산자를 통하여 주어진 개체를 해당 형식으로 down cast 시도하되, 호환되는 형식이 아니거나 처음부터 null 참조였을 경우에는 null을 반환하도록 할 수 있다. 이 때, C# 2.0 컴파일러를 사용 중이라면, 연이어서 변환에 실패하였을 경우 추가 fallback 처리를 다음과 같이 할 수 있다.

    string s = (sth as string) ?? String.Empty;
  • 위의 is나 as 연산자의 평가 결과와 무관하게 동작하는 경우도 있다. 클래스 내에 implicit, explicit operator method를 추가하였을 경우 상속 관계와 무관한 casting이 발행되기도 한다. implicit operator method가 주어진 형식에 대하여 존재하는 경우 해당 형식으로의 대입 구문에서 자동으로 호출되며, explicit operator method가 주어진 형식에 대하여 존재하는 경우 강제 형변환 구문을 통하여 이를 명시적으로 호출할 수 있다. 하지만 양쪽 모두 꼭 필요한 경우가 아니면 explicit operator method만을 제한적으로 사용하는 것이 혼란을 줄일 수 있다.
  • primitive type의 경우 강제 형변환을 통하여 손실 형변환을 유도할 수 있으며 이는 상속 관계로 설명될 수 있는 것이 아니다.
3.1.3. 봉인 클래스의 protected 액세스 한정자를 사용하면 경고가 발생한다.

sealed 키워드로 선언된 클래스나 구조체에서 protected 멤버를 선언하면 컴파일러 경고가 발행된다. 왜냐하면, 상속 대상이 더 이상 존재하지 않기 때문에 protected 멤버는 원래의 의미가 아닌 private 멤버로 의미가 변질되기 때문이다.

3.2. interface

3.2.1. interface는 interface일 뿐 다중 상속의 대용품이 아니다.

interface를 method signature - 추상 클래스와 같이 구현부는 없고 선언부만 있는 method -의 용도, 즉 클래스나 구조체의 프로토타입 정도로만 생각하는 것이 옳은 판단이다. 즉, interface는 abstract method가 있는 클래스와 유사하지만 상속의 의미와는 그 용도가 다르다. 공통된 type을 정의하는 것으로 보아야 하고, 어떤 공통된 기능을 "지원한다"는 의미로 해석하는것이 옳다.

 

또한 interface는 클래스를 재이용하기 위해 상속을 사용하여 캡슐화의 파괴를 수반하는 것을 방지하는 기능이 있다. 상속을 사용하면 모두 구현 후 마치 소스 코드가 여기저기 천 조각을 주워 모아 만든 '누더기' 같이 보이는 것에 한숨을 쉰 경험이 있을 것이다. 이 부분을 interface로 구현하면 보다 깔끔한 코드가 나오게 된다. 물론 public과 protected를 적절히 잘 사용해도 되긴 하지만 말이다.

 

하지만 상속은 메서드 오버라이드한 경우 클래스를 마음대로 개조해 버린 셈이 되므로 어디선가 묘한 모순이 발생하게 될 가능성도 높아질 뿐 아니라 추상 클래스의 경우 실제 구현부가 어디에 위치하는지도 애매하게 느껴질 수 있어 불안정한 코드가 되고 만다.

 

덧. 인터페이스의 설계는 최대한 단순하고 가볍게 하되, 한 번 외부에 공표한 인터페이스는 바꾸지 않는 것을 전제로 해야 한다. 인터페이스는 이 인터페이스를 구현하는 클래스들간의 약속이며 이를 깨뜨리게 될 경우 프로그램 코드의 일부 혹은 전체를 매번 새로 컴파일해서 전체 소프트웨어를 재 배포해야 하는 초과 비용을 발생하게 만든다. 즉, 작은 단위의 기능으로만 인터페이스를 신중하게 추가하고 관리해야 한다.

3.3. 상속 제대로 사용하기

"그렇다면 제대로 된 상속은 어떻게 판단할 수 있을까?"

 

상속은 'is a' 관계가 성립해야 올바르다. 즉 '서브클래스 (자식) is a 슈퍼클래스 (부모)'가 성립해야 한다. 예를 들면 'Red is a Color'는 올바른 명제이지만, 'Engine is a Car'는 'has a'' 관계이므로 상속이 아닌 Composition 관계 - 또는 - Delegation 관계로 'Engine has a Car'라는 명제가 올바르게 된다.

 

Composition은 '객체를 field가 갖게 하는 방법'을 뜻하므로 'has a' 관계가 정확히 성립한다. 상속 대신 composition과 delegation (조작이나 처리를 다른 객체에 위임)을 사용하면 다음과 같은 장점이 있다.

 

  1. 상속에서는 슈퍼 클래스가 허용하고 있는 조작을 서브 클래스에서 모두 허용하게 되지만, composition과 delegation에서는 조작을 제한할 수 있다.
  2. 클래스는 결코 변경할 수 없지만, composition하고 있는 객체는 자유롭게 변경할 수 있다. 예를 들면 학생 클래스가 항상, 영원히 학생이 아니라 나중에 취직을 하여 직장인 클래스가 되도록 할 수 있다는 의미이다.

 

상속을 composition과 delegation으로 변경하는 요령은? 여기서 Shape를 상속한 Polyline과 Circle을 변경한다면 다음과 같다.

 

  1. Shape (부모)의 공통된 내용을 구현한 구현 클래스 (ShapeImpl)를 만든다.
  2. Polyline과 Circle 클래스에서 ShapeImpl을 composition하고 부모와 공통되지 않는 method를 각각 위임받는다.
  3. ShapeImpl 클래스의 method를 추출한 IShape 인터페이스를 작성하고 Polyline과 Circle에서 이 인터페이스를 구현한다.



출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1563&page=4
Posted by Sting!
,

2. 닷넷은 Pointer 환경이다? (닷넷에는 Pointer밖에 없다?)

2.1. 닷넷은 값 형식을 제외하곤 모두 Pointer이다.

"닷넷에는 포인터가 없다" - 또는 - "일부 언어에서만 호환성을 유지하기 위하여 포인터를 남겨놓았을 뿐 포인터는 자바와 마찬가지로 사용하지 않는다"라고 닷넷의 장점이라고 생각하는 것은 입문자도 외우고 있다. 하지만 이 부분은 의외로 닷넷을 혼란스럽게 하는 주범이라고 생각한다. 닷넷에 포인터가 없기는 커녕 값 형식 (Primitive (int, short, char, long, ...), 구조체, 나열 상수 (enum))을 제외하면 "포인터 밖에 없는 환경이다"라는 명제가 성립되게 된다. 사실 여기서 포인터라고 함은 C의 그것과는 조금 다른 reference(참조)로 보는 것이 온당하지만...

 

"즉, 닷넷의 클래스형의 변수는 모두 포인터이다."

 

덧. System.IntPtr, System.UIntPtr은 정수형 포인터를 가리키는 형식으로 자바가 정확하게 지원해주지 않는 C 언어의 포인터를 닷넷 환경에서는 이들 형식을 이용하여 다룰 수 있다. 그러나 이 글에서 이야기하는 개념과는 거리가 먼 상호 운용성에 관한 토픽이므로 큰 관련성이 없다.

2.2 null은 객체인가?

닷넷에서 공참조 (힙에 실제로 참조되는 object가 없는 참조)의 경우는 당연히 객체가 붙어있지 않다. 이러한 상태에서 다음의 MSIL 명령어를 호출하는 동작을 수행할 경우 NullReferenceException이 발생한다고 닷넷 SDK 문서 상에는 기술되어있다.

  • callvirt : 메서드 호출 (호출 대상이 null 참조일 경우 발생)
  • cpblk : 메모리 영역 복사 (잘못된 주소 발견시 발생)
  • cpobj : 값 형식 객체 복사 (잘못된 주소 발견시 발생)
  • initblk : 메모리 영역 초기화 (잘못된 주소 발견시 발생)
  • ldelem.<type> : 지정된 배열 인덱스의 항목을 가져오기 (배열이 null 참조일 경우 발생)
  • ldelema : 지정된 배열 인덱스의 항목의 개체를 가져오기 (배열이 null 참조일 경우 발생)
  • ldfld : 필드 값 찾기 (개체가 null 참조이고 정적 필드가 아닐 경우 발생)
  • ldflda : 필드 주소 찾기 (개체가 null 참조이고 정적 필드가 아닐 경우 발생)
  • ldind.<type> : (잘못된 주소 발견시 발생)
  • ldlen : 배열 길이 조회 (배열이 null 참조일 경우 발생)
  • stelem.<type> : 배열 요소 가져오기 (배열이 null 참조일 경우 발생)
  • stfld : 개체 참조/포인터의 값 교체 (개체가 null 참조이고 정적 필드가 아닐 경우 발생)
  • stind.<type> : 주어진 주소에 값 쓰기 (형식 불일치 발생시 발생)
  • throw : 예외 발생 명령어 (예외 발생 대상 개체가 null일 경우 발생)
  • unbox : 참조 형식 내의 값 형식 개체를 가져올 때 (참조 형식이 null일 경우 발생)

위에서 강조 표시한 항목들은 실제 C#, VB.NET 환경에서 만날 수 있는 사항들과 관련이 있는 것을 열거한 것이다. 여기서 논점이 되는 것은 null을 개체로 볼것인가 아닌가에 대한 문제이다.

 

공참조는 어떤 객체도 참조하고 있지 않는다고 단정하고 있다. 하지만 '==' 연산에 있어 두개의 객체가 모두 null이거나 동일한 객체 또는 배열 참조의 경우 true라고 되어있는것으로 봐서 서로 다른 두 객체가 동일한 null을 참조하고 있으므로 true가 된것이 아닌가 하는 생각을 할 수 있다.

 

즉, null이 Object의 instance 형태는 아니지만 개념적으로 봤을 때 null도 object라고 봐야 하지 않을까?

 

덧. C# 2.0부터 새롭게 소개된 Nullable 형식에서 사용하는 null 키워드는 그 의미가 조금 다르다. 앞서 값 형식은 참조 형식과는 달리 값 그 자체를 취급한다고 하였는데, 데이터베이스 환경에서는 이런 설정과는 또 다르게 값 형식에도 null 상태를 허용하고 있다. 이런 불일치성을 해결하기 위하여 등장한 것이 Nullable 형식인데, 특별히 이 형식에 대해서는 Nullable 형식이 클래스 형식이 아님에도 불구하고 null을 값을 지정하지 않고 Nullable 개체를 새로 만든 것으로 대체하여 처리한다.

 

int? aa = null;
System.Nullable<int> aa = null;
int? aa = new System.Nullable<int>();
System.Nullable<int> aa = new System.Nullable<int>();
// 값이 지정되지 않은 int 형식을 만들기 위하여 위의 네 구문은 모두 같습니다.

int? bb = 3;
System.Nullable<int> bb = 3;
int? bb = new System.Nullable<int>(3);
System.Nullable<int> bb = new System.Nullable<int>(3);
// 값이 지정된 int 형식을 만들기 위하여 위의 네 구문은 모두 같다.

bool b = (aa == null);
bool b = aa.HasValue;
// 값이 지정되었는지 아닌지를 판단하기 위하여 위의 두 구문은 모두 같다.
object o = (aa.HasValue ? aa.Value : null);
object o = aa ?? null;
// 값이 있는지 없는지에 따라 선택적으로 값을 가져오게 하기 위한 방안으로 위의 구문은 모두 같다.

2.3 String에 대하여

String Object에 대한 생각.

 

string str = "111222";
string a = "111";
string b = "222";
string c = "111";
string d = b;
string t = str.Substring(0, 3); // 111
string u = b.Clone().ToString(); // 222

 

위의 소스를 보고 다음이 참인지 생각해 보자. (== 연산자나 Equals 메서드는 값을 비교하는 것이므로, ReferenceEquals 메서드를 이용하여 같은 개체들인지 확인해보기로 한다.)

  1. String.ReferenceEquals(str, (a+b)): 이것은 두 개의 참조와 하나의 참조를 비교했으므로 당연히 false이다.
  2. String.ReferenceEquals(a, b): 이것은 당연히 false이다.
  3. String.ReferenceEquals(d, b): d 참조에 b의 참조를 복사한 것이므로 결국 d는 b를 가리킨다. 따라서 true이다.
  4. String.ReferenceEquals(a, t): a와 t는 둘 다 값이 "111"이다. 하지만 이것은 서로 다른 참조를 가져 false이다. 그렇다면 다음 5번도 false일까?
  5. String.ReferenceEquals(a, c): 이것은 true이다. 4번의 경우와는 구분되는 것이, 이와 같이 프로그래머가 코드 상에 직접 기입해넣은 문자열도 실행 중에는 하나의 완벽한 객체로 분류된다. a와 c는 이렇게 코드 상에 서술된 문자열에 대한 같은 참조를 서로 나누어 받은 것이므로 a와 c는 코드 상에 서술되어있던 문자열에 대한 참조를 나누어받았을 뿐이다. 그러므로 이런 결과가 나타나게 된다.

2.4 객체 지향의 캡슐화 파괴 주의

"object pointer를 반환하는 getter method는 객체 지향의 캡슐화가 파괴될 가능성이 있다." 이는 object형의 field (member variable)의 getter에서 이 object를 그냥 반환하면 이 object를 받은 쪽이나 참조하고 있는 다른쪽에서 이 object의 내용을 변경하게 되므로 사실 캡슐화 (은닉)는 이루어지지 않았다고 보는 것이 정확하다.

 

"이럴 경우 object를 clone(복제)하여 반환하지 않아도 되는지를 반드시 생각해 본다."

 

object의 복사에는 shallow copy와 deep copy가 있다.

 

// (참고) Member에는 두 개의 field (Identity Class 형의 id와 Family Class 형의 family)가 있다.

// Shallow Copy Example
public Member ShallowCopy()
{
    Member newer = new Member();
    newer.id = this.id;
    newer.family = this.family;
    return newer;
}

// Deep Copy Example
public Member DeepCopy()
{
     Member newer = new Member();
     newer.id = new Identity(this.id.Identity, this.id.Name);
     newer.family = new Family(this.family.FamilyName, this.family.FamilyInfo);
     return newer;
}

 

위 소스에서 보듯이 Shallow Copy는 object를 복사하여 반환한 것 처럼 보이지만, 사실은 Member Object만 새로 생성되었을 뿐 Member의 field는 newere와 this 둘다 서로 같은 힙의 id와 family를 참조한다. 하지만 두 번째 method인 Deep Copy의 경우 member field를 새로 생성하여 복사하므로 서로 다른 id와 family이다.

 

클래스를 직접 구현하고 있을 동안 System.Object 클래스의 protected 메서드 (외부에서는 사용할 수 없지만 내부적으로 상속되어져 내려오는 메서드) 중 하나인 MemberwiseClone 메서드를 이용하여 자기 자신의 단순 복사본을 생성할 수 있다. 하지만 Deep Copy를 구현하려면 ICloneable 인터페이스를 이용하여 별도로 Clone 메서드를 통하여 구현하도록 하는 것이 바람직하다. 하지만 ICloneable 인터페이스를 구현했다고 해서 이 개체가 반드시 Deep Copy를 지원하는 것이라고는 볼 수 없다.*

(참고) object를 immutable (변하지 않는, 불변의 객체)로 만드는 요령

  1. 모든 field (member variable)를 생성자(constructor)를 이용하여 초기화한다.
  2. 모든 field는 private으로 선언하고, getter property는 만들되 setter property는 만들지 않는다. 이로서 모든 속성들을 readonly property로 변경한다.

즉, 값을 변경하기 위해서는 object를 다시 만들어야만 하는 불편은 있지만 안전하게 사용하고자할 때 유용한 방법이다.

2.5 배열에 대하여

2.5.1 배열은 object인가?

CLR에서 배열은 object로 취급되어 object와 같이 기술된다. 클래스의 상속도로 보았을 때에도 모든 배열은 암시적으로 System.Array를 부모 클래스로 하며, System.Array는 다시 System.Object를 부모 클래스로 한다. 그리고 int[] iarr = new int[10]; 구문에서처럼 new 연산자로 Heap 영역에 object를 생성하므로 object임을 알 수 있다.

2.5.2. 배열의 length는 왜 Java와 달리 프로퍼티인가?

Java와는 달리 닷넷의 배열은 길이를 가져오기 위한 방법으로 getter property를 이용하는데, Java이든 닷넷이든 이 과정은 컴파일러의 트릭과 함께 CLR의 협조를 기반으로 이루어지는 특수한 논리가 숨어있다.

 

어찌되었든 간에 배열의 길이를 조사하는 것 자체는 필드도 아니고 메서드도 아니고, 프로퍼티도 아닌 단순한 기계 명령어 하나로 통일된다고 봐야 한다. (이전 섹션에서 언급한 ldlen 명령 참고) 그러나 이런 식의 문법적 요소를 남겨두는 이유는, 언어 호환성을 위한 것으로 컴파일러 제작자가 아닌 일반 소프트웨어 개발자들에게는 피상적인 의미 그 이상이 되지는 않는다.

2.5.3. readonly와 배열에 대하여...

우리가 흔히 앞에서도 나온바 있지만 readonly는 값을 변경할 수 없는 것이라고만 생각하지 object로 되어있을 경우 그 object는 변경 가능하다는 것을 잊곤한다. 배열도 object이므로 마찬가지다.

 

readonly int[] iarr = new int[5]; 일 경우 iarr = null; 은 오류로 취급되지만, iarr[3] = 5;는 오류가 발생하지 않는다. 즉, readonly로 지정되어있는것은 iarr이지 iarr이 가리키는 곳의 배열의 요소들은 아니다.

2.5.4. 닷넷에서의 다차원 배열은 논리적인 개념일 뿐이다.

가령 2차원 배열처럼 보이는 int[][] iarr 또는 int[,] iarr은 논리적으로는 행열 구조처럼 보일 수 있지만 실제 컴퓨터 메모리 배치는 그렇지 않다. 두개의 배열이 각각 구분되어있는 상태에서 서로 유기적으로 통합된 상태이거나, 지정된 크기만큼의 메모리 공간을 사용하는 1차원 배열이 재구성된 것으로 이해하는 것이 올바르다.

2.6. 인수 (parameter/argument) 전달의 개념

2.6.1. 닷넷에서 parameter (argument) 전달은 무조건 'call by value'이다.

값 형식의 경우 호출한 쪽의 변수값은 호출 받은 method 내에서 값이 변경되어도 변경되지 않는다. 참조 형식의 경우도 참조되는 object에 대해서는 함께 변경되지만 참조 포인터는 call by value이다. object를 가리키는 pointer는 call by value로 변경되지만 Heap의 실제 object 내용은 변경되지 않는다.

2.6.2. C와 같은 언어는 static linking이지만, 닷넷은 dynamic linking이다.

따라서 닷넷은 클래스가 처음에 한꺼번에 메모리에 로드되는것이 아니라 런타임시에 그것이 필요해 졌을 때 로드되고 링크된다. static field의 영역도 클래스가 로드되는 시점에서야 비로소 확보된다. 이렇게 되면 최초 가동 시간이 단축되고 끝까지 사용하지 않는 클래스의 경우 신경 쓸 필요가 없어지게 된다.

 

따라서 static field는 프로그램이 시작되어 해당 클래스가 필요해 졌을 때 CLR이 알아서 load/link 해준다. 즉, static field는 프로그램이 실행되기 시작할 때 부터 끝날 때 까지 계속해서 존재하는 것이라고 보면 된다.

(참고) linking의 의미

link된다는 것은 클래스가 memory에 loading될 때 특정 메모리 번지에 loading되는 데 이 메모리 번지는 loading될 때 마다 다른 번지 수에 loading된다. 이 때의 메모리 주소값 (Java에서는 실제 메모리 값이 아닐 수 있다)을 현재 실행 중인 프로그램에서 알 수 있도록 하여 해당 클래스에 대한 참조가 가능하도록 연결하는 과정이다.

 

정적 (static) link라는 것은 이러한 메모리에 대한 주소 정보를 컴파일 시에 compiler가 미리 결정하는 것이고, 동적 (dynamic) link라는 것은 프로그램 수행 중 결정되는 것을 의미한다. 정적인 link의 경우 직접적으로 메모리의 번지값이 할당되는 것이 아니라 offset 값 (기준 위치로부터의 index 값)으로 연결시킨다.

2.7 GC에 대하여 잠깐!

2.7.1. Garbage Collection은 만능이 아니다.

닷넷에는 free가 없다. GC가 알아서 해준다. 하지만 GC 수행 중에는 프로그램의 퍼포먼스가 크게 떨어질 수 있기 때문에 GC가 자주 발생하지 않도록 프로그램을 설계하는 것이 좋다. 서비스 되고 있는 시스템에서도 가끔 시스템이 응답이 늦어지는 시점이 있는데, 이는 GC가 수행되고 있기 때문일 가능성이 높다.

 

그렇다면 GC가 자주 발생하지 않도록 해야 하는데 가장 좋은 방법은 무엇일까? 그것은 바로 불필요한 객체를 생성하지 않는 것이 아닐까?

 

닷넷에 free가 없다는 것은 매력적이다. 그 이유는 두 개의 변수가 heap 내에서 하나의 object를 reference하고 있을 경우 실수로 하나의 변수만 free해버리면 나머지 하나는 dangling pointer라하여 reference pointer가 모르는 사이에 사라져버려 곤경에 처하는 것을 예방해 주기 때문이다.

 

참고로 System.Object 클래스에는 Finalize 메서드가 있어 GC 수행 시점에 호출되는 메서드가 있지만 이것은 GC가 언제 수행될지 알 수 없으므로 과신하면 안된다.

 

덧. 상호 연동성 프로그래밍을 많이 하는 경우, 소멸자와 함께 반드시 생각해야 할 것이 IDisposable 인터페이스의 구현이다. 이 인터페이스를 구현하여, 언제 호출될지 알 수 없는 소멸자가 아닌 명시적으로 호출 가능한 소거 함수를 배치하여 필요없을 때 외부 리소스 (플랫폼 호출을 통하여 부른 malloc 같은 함수들의 메모리 블럭)들을 미리 제거할 수 있다. 그리고 소멸자나 Finalize 메서드와도 Dispose 메서드를 연결시켜서 Dispose가 이루어지지 않아서 발생하는 리소스 누수를 예방해야 한다.

2.8 닷넷 Pointer 결론

2.8.1 결국 닷넷에는 포인터가 있는 것인가, 없는 것인가?

닷넷은 Heap 내의 Object를 참조(reference)하고 있고, 참조는 결국 개념이 포인터와 유사한 것이므로, 닷넷에 포인터가 없다는 것은 어불성설이다.

 

주. 이 부분에 대해 Object를 이해하시면 족히 이런 문제는 사라질 것으로 봅니다. 클래스에 대한 인스턴스 (object)들은 reference로 밖에 가질(참조될)수 없기 때문입니다. 컴파일러 입장이 아닌 기반 언어들 (C#, VB.NET, J#, C++ CLR 등)의 사상을 가지고 이해하는 것이 좋을듯 합니다.

 

java.sun.com의 Java programmer's FAQ에 이런 질문 글이 올라온 적이 있다. "Java에는 pointer가 없다고 하는데, linked list는 어떻게 만들어야 하는가?"라는 질문이었는데 이에 대한 답이 참 명쾌하다. 이것은 비슷한 메카니즘을 가지는 다른 GC 환경 플랫폼에서도 동일하게 적용되는 것이다.

 

Java에 관한 많은 오해 중에서 이것이 가장 심각한 것이다. 포인터가 없기는 커녕 Java에 있어 객체지향 프로그래밍은 오로지 Pointer에 의해 행해진다. 다시 말해 객체는 항상 포인터를 경유해서만 access가 이루어지며 결코 직접적으로 access 되지 않는다. pointer는 reference (참조)라고 불리며 당신을 위해 자동으로 참조된다.

 

닷넷에 포인터가 없다고 주장하는 모든 서적과 글들의 내용은 혼란을 야기할 가능성이 크다. 이러한 주장은 GC 플랫폼 환경을 정확히 숙지하지 않은 사람의 서술일 가능성이 높으므로 재고해보아야 할 표현이다.



출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1562&page=4
Posted by Sting!
,

'개발자가 놓치기 쉬운 자바의 기본 원리' 라는 글의 내용을 보고 같은 내용을 닷넷을 기준으로 서술한다면 재미있을 것 같다는 생각에 곧바로 글을 옮겨적어봅니다. 좋은 소스를 제공해주신 다음 커뮤니티본부 커뮤니티개발1팀의 전성호님께 감사드립니다. 이 글이 닷넷에 입문하시는 분들이나 기본적인 원리때문에 고민하셨던 분들께 좋은 역할을 해주기를 바랍니다.

 

출처: http://dna.daum.net/technote/java/PrincipleOfJavaInternalForDeveloperEasyToLost

 

요약: 개발자가 놓치기 쉬운 닷넷의 기본 원리에 대하여 기본적이긴 하지만 개발하면서 느끼고 경험한 내용을 정리하였다.

1. 객체 지향의 구멍 static

1.1 닷넷은 객체 지향 환경이다?

"닷넷은 자바와 마찬가지로 객체지향 언어이다" 라는 주장을 자주 접하게 된다. 만일 이것이 사실이라면 닷넷을 사용하는 한 "기존의 절차 지향 프로그래밍을 전혀 할 수 없을게 아닌가?" 라는 생각이 들지만 사실은 그렇지 않다. static을 이용하면 비 객체지향 언어처럼 코딩할 수 있다.

 

static method, static property, static event는 instance가 아닌 클래스에 속하는 method로, class method, class property, class event라고 부른다. 반대로 static이 아닌 method, property, event은 instance method, instance property, instance event라고 부른다.

 

static method, static property에는 this가 없다. instance method, property에는 숨겨진 파라미터로 this가 건네진다. (아래 "객체지향에 흔히 있는 오해" 참고) 하지만 static method, static property는 절차 지향의 함수와 동일하므로 숨겨진 파라미터 this는 없다. 그래서 static method, static property에서는 전달한 this가 없으므로 instance method, instance property를 호출하거나 instance field를 참조할 수 없는 것이다.

(참고) 객체지향에 흔히 있는 오해

  • 오해 1. "객체 지향에서는 객체 끼리 서로 메시지를 주고 받으며 동작한다."라는 말을 듣고 다음과 같이 생각할 수 있다. "객체 지향에서는 객체가 각각 독립하여 움직인다는 것인가, 그러면 각 객체에 독립된 thread가 할당되어 있단 말인가?" 그렇지 않다. "메세지를 보낸다"라는 것은 단순히 각 객체의 함수 호출에 불과하다.
  • 덧. 메세지를 보낸다라는 개념을 같은 컴퓨터 내에서가 아닌 네트워크 상의 관점으로 확장을 하였을 때, 우리가 흔히 알고 있는 XML Web Service, DCOM, CORBA와 같은 Remote Procedure Call 기반 통신 기법으로 발전하게 되는 것이다.
  • 오해 2. "객체 지향에서는 method, property, event가 class에 부속되어 있다"는 말을 듣고 다음과 같이 생각할 수 있다. "그러면 instance별로 method의 실행 코드가 복제되고 있는 것이 아닌가?" 물론 이것도 오해다. method의 실행 코드는 종래의 함수와 동일한 어딘가 다른 곳 (CLR의 메모리 영역 내)에 존재하며 그 첫 번째 파라미터로 객체의 포인터 this가 건네질 뿐이다.
  • 오해 3. "그렇다면 각 instance가 method의 실행 코드를 통째로 갖고 있지 않는 것은 확실하지만, method의 실행 코드의 포인터는 각 instance별로 보관하고 있는 것이 아닌가?" 이것은 약간 애매한 오해이긴 하다. CLR 스펙에서는 JVM 스펙에서와 마찬가지로 별도의 메모리 영역 내에 실행 코드를 가지고 있는 상황에서 각 메서드 호출 시 Stack Frame이 생성되어 실행되고 실행 완료 시 복귀 주소를 전달한다.

1.2 전역 변수

static에서 public field는 전역 변수 (global variable, 글로벌 변수)이다. 여기서 "글로벌 변수는 왜 안되는가"에 대해 잠깐 생각해 본다. 우리는 흔히 "글로벌 변수는 될수있는한 사용하지 않는 것이 좋다"라고 한다. 그 이유는 글로벌 변수는 어디서든 참조할 수 있고 값을 변경할 수 있기 때문이다.

 

또한 파라미터나 리턴값으로 교환해야할 정보를 글로벌 변수를 경유(사용)하여 건네주면 함수의 역할이 불분명해지고 흐름도 애매해진다. 마지막 이유로는 "글로벌 변수는 하나밖에 없다"는 것이다. 이는 어디서 이 값을 변경했는지 알 수 없게 하는 지름길이고 실무에서도 간혹 발생하긴 하지만, 이 하나밖에 없는 변수가 버전업으로 두개가 필요하게 되었을 때 확장도 대형 프로젝트에서는 힘들어진다.

 

따라서 static에서 public은 readonly나 const를 붙여 상수나 읽기 전용 변수로 사용해야지 그 외의 용도는 자제하는 것이 좋을 것이다.

 

(참고) readonly 초기화에서의 주의점. 예를 들어 다음과 같은 코드를 보았을 때 우려되는 점은 무엇인가?

 

public static readonly System.Drawing.Color White = new System.Drawing.Color(255, 255, 255);

 

readonly 변수는 인라인 문장과 생성자 권역 내에서 한 번 초기화되면 변경이 불가능한데 object로 초기화할 경우 WHITE라는 필드가 변경될 수 없는 것이지 그것이 가리키는 객체는 아니라는 점이다.

 

과거 신규 서비스 개발시 readonly 변수 필드에 설정파일을 읽어 cache하는 singleton class의 특정 member를 이용하여 초기화할 경우 이 멤버값이 변경되면 readonly 변수의 값이 변경되었는데 프로그램에서는 이상한 짓을 하는 원인을 찾기가 상당히 어려웠던 경험을 하고난 후 부터 이런 코드는 냄새나는 코드로 여겨지게 되었다.

 

static은 글로벌 변수와 동일하므로 남발해서는 안된다. static을 사용할 경우 다음 두 가지는 최소한 기억한다.

  1. static field는 readonly의 경우와 달리 정말 "하나여도 되는지" 여부를 잘 생각해야 한다.
  2. static method는 주저하지 말고 쓰되 다음 두 가지의 경우 매우 활용적이다.
  3. 다른 많은 클래스에서 사용하는 Utility Method 군을 만드는 경우. (주로 Utility Class의 method)
  4. 클래스 안에서만 사용하는 "하청 메서드 (private method)". 이유를 예를 들어 설명하면, 아래와 같은 조금은 과장된 클래스가 있다고 하자.

 

public class T
{
    private int a;
    private int b;
    private int c;

    private int Calc()
    {
        c = a + b;
        return c * c;
    }

    // ....other method or getter/setter...
}

 

위의 클래스 T의 경우 내부에서는 Calc라는 instance 함수를 사용하게 되면 c의 값이 매번 변하게 된다. 이는 무심코 하는 실수로 클래스 내에서 private method는 모든 멤버 instance 변수에 접근 가능하게 되면서 발생하게 된다. c의 값이 변하지 않기를 바랄 수 있다. 이 때 안전한 방법은 다음과 같이 Calc 하청 메서드를 static method로 수정하면 안전하다.

 

private static int Calc(int a, int b)
{
    int c = a + b;
    return c * c;
}

 

여기서 a와 b는 멤버 변수를 접근할 수 없어 전달해야 한다. (static에는 this가 없어 instance field를 참조할 수 없다는 것은 이미 위에서 설명했다.) 또한 c도 같은 이유로 사용할 수 없어 로컬 변수로 선언하여 사용하고 있다. 이럴 경우 메서드가 약간 커질 수 있지만 instance member 변수를 안전하게 사용할 수 있다는 장점이 있다. 이것은 static을 다시한번 생각하게 하는 좋은 예가 되었을 것이다.



출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1561&page=4
Posted by Sting!
,

[3티어 방식의 장점]

1. 보안측면에서 유리해 진다.

제대로 된 2티어 방식에서는 보안을 위해서, 해당 DB에서 사용자마다 DB 접속 계정을 만들고 DB 객체(Table, 저장프록)
참조에 대한 권한을 일일이 설정해야 하지만, 이렇게 하면 너무 사용자 관리가 복잡해지고, 프로그램 변경도 어렵게 된다.
이 때문에 2티어에서 주로 많이 취하는 방식은, DB에 커넥션할 때 admin(MSSQL인 경우 sa계정) 계정으로 접속하고,
사용자 정보 테이블을 DB에 만들어 두고 단순히 계정과 암호를 보관하는 용도로 사용한다.
이렇게 함으로 인해서 프로그램은 편해질지 몰라도, 실행 파일 내부에 admin 계정에 대한 정보가 고스란히 존재하여,
해커들이 쉽게 admin 계정을 알 수 있게 된다. 특히 폼에 놓여진 데이타베이스 연결 콤포넌트 속성 일부로
admin 계정 정보를 저장했다면, 정말 치명적이다. 해커들은 아주 쉽사리 폼에 대한 정보를 얻을 수 있기 때문이다.
3티어 방식은 DB 연결에 대한 어떤 정보도 클라이언트 보관한 필요가 없다. 따라서 보안 측면에서 훨씬 좋다.
2티어는 성격상 외부에서의 DB연결을 허용해야 하지만, 3티어는 DB 직접 연결을 허용하지 않는다.

2. 3티어 대용량 서비스에서 2티어에 비해서 유리하다.

이는, 2티어 방식에서는 한 사용자당 한 커넥션을 가져야 하며, 그 사용자가 프로그램을 종료하기 전까지는
그 커넥션을 계속 가지고 있어야 하기 때문이다. 반면 3티어에서는 대부분 connectless 방식을 취한다.
즉 웹서버 같이 서비스 요청/응답 후 즉시 연결을 차단하는 방식을 사용하기 때문에, 커넥션 수가 제한될 때 월등히 유리하다.
DB 커넥션은 대부분의 RDB에서는 별도로 부과되는 라이센스 비용이기 때문에, 적은 수의 커넥션으로
많은 사용자들에게 서비스할 수 있다는 점은 경비적으로 3티어가 2티어에 비해서 좋은 점이다.
물론 2티어에서도 순수 코딩으로 3 티어같이 connectionless방식을 구현할 수도 있을 것이다. 그러나 이렇게 할 경우 잦은 connect,
disconnect는 프로그램 속도를 느리게 하고, 서버에 도리어 부담을 줄 수도 있다. 반면 3티어는 커넥션 풀링 등의 개념을 사용하여
이런 부담을 줄일 수 있다.

3. 클라이언트 배포가 단순해 진다.

3티어 방식에서 클라이언트는 실행파일만 배포하면 될 뿐, ADO, ODBC, BDE같은 DB 연결 라이브러리 설치가 전혀 필요없다.
이는 어플 서버만 디비에 직접 연결할 뿐, 클라이언트는 DB 가 무엇인지 전혀 알 필요가 없기 때문이다.
2티어의 경우 클라이언트가 100대이고, 연결방식이 BDE라고 가정했을 때,
이 100대에 대해서 BDE를 업데이트해야할 필요성이 발생했다면, 얼마나 끔직한 일인가?
BDE 업데이트를 하려면 단순 파일들의 덮어쓰기 말고도 레지스트리 정보도 수정해 줘야 하므로,
자동 업데이트 프로그램을 만들기도 쉽지 않다.
그러나 3티어에서는 클라이언트 실행 파일만 자동 업데이트하는 로직을 구현해 주면 된다. 대부분의 경우 이는 매우 쉽다.

4. SQL 문장이 클라이언트 프로그램에 있을 필요가 없다.

3티어 방식에서 DB에 요청을 하는 것은 어플 서버만 담당하므로, 클라이언트는 전혀 SQL문장을 사용할 필요가 없다.
3항에서 언급했듯이 클라이언트는 어플 서버하고만 통신할 뿐, 디비 서버와는 전혀 통신하지 않는다.
따라서 모든 요청은 어플 서버에 대한 원격 함수 호출만 존재할 뿐, 어플 서버 쪽으로
직접 SQL 문을 날리는 경우는 보안적인 이유로 지극히 제한된다.
보통 2티어인 경우, 클라이언트 프로그램 내부에 수많은 SQL문들이 존재하고 이를 디비 서버로 전송할 것이다.
실행 파일 내부에 SQL 문이 존재한다는 것은, 해킹과 크랙의 가능성을 높여주고, 디비 구조를 추정해볼 수 있는 힌트가 된다.

5. 다중 DB 사용이 용이하다.

기존 프로그램이 MS SQL 서버를 사용해서 만들었다고 가정하자. 이를 오라클에서도 동작할 수 있도록 변경하려면,
2티어 방식에서는 클라이언트 모든 프로그램을 수정해야 할 것이다. 3티어는 어플 서버 쪽으로 모든 SQL 문이 집중되어 있으므로,
비교적 디비 전환이 슆다(물론 거저 먹기씩으로 아주 간단하다는 말은 절대 아니다).
오라클, MSSQL은 각자 자기만의 특수한 SQL구문이 있을 수가 있는데, 각 디비마다 이런 최적화를 어플 서버에 집중할 수 있으므로,
특정 디비에 대한 고유한 기능을 살리면서, 클라이언트에는 별다른 영향을 주지 않게 할 수 있다.
이와 비슷한 경우로, 실제 테이블의 필드명이 CUST_NO이지만, 클라이언트에서는 CustID라는 필드명을 사용하고 싶을때도
마찬가지다. 어플 서버가 실제 필드명과 클라이언트 필드명을 매핑하는 역할을 할 수 있다.



회사 PPT 자료 만들때 쓴던건데 생각나서 올려봅니다~ 참고하세요~
 
아래의 리플들...

좋은 내용입니다.
근데 2티어가 왜 커넥션을 물고 가야하는지 이해가 안되네요.
오라클의 transaction scope 이 session 별로 이루어 지기 때문에, 오라클에서만 해당 하는 내용일듯..

--------------------------------------------------------------------------------------------------

대용량 DB의 경우를 예를들어봅시다..

우선
클라이언트 1대가 접속을 한다면 커넥션이 1개가 생깁니다.
클라이언트 10000대가 접속을 한다면 커넥션이 10000개가 생기죠..


connection 을 시작할 경우 절차를 생각해봅시다..

'DB 서버 너 살았니????" (ping을 날립니다)
"나 살았어~~~~~~ 접속해~~~" (request 합니다)
"나한테 남은 물약이 있니?" (쿼리를 수행합니다)
"물약 5개 남았어"
"나 물약1개 먹을께"
"OK 물약이제 4개 남았어"
" 수고했어 안녕~~~"

---------------------7번 데이터를 주고받았습니다.

이를 connectionless 방식으로 생각해보겠습니다.

-- connection
'DB 서버 너 살았니????"
"나 살았어~~~~~~ 접속해~~~"
"나한테 남은 물약이 있니?"
"물약 5개 남았어"
" 수고했어 안녕~~~"
-- disconnection

-- connection
'DB 서버 너 살았니????"
"나 살았어~~~~~~ 접속해~~~"
"나 물약1개 먹을께"
"OK 물약이제 4개 남았어"
" 수고했어 안녕~~~"
-- disconnection


-------------10번 데이터를 주고받았습니다.


이는 클라이언트 1대의 경우 7번과 10번이 나옵니다
이를 10000대의 클라이언트라고 생각하면 답은 나옵니다..

2티어에서는 connection방식이든 connectionless 방식이든
connection 연결된 수는 10000개가 생길수 밖에 없으니깐요~

--------------------------------------------------------------------------------------------------

제 의미 전달이 잘못된 듯하네요.
위의 오라클은 transaction comit 단위가 session 이라 어쩔 수 없다지만,
mssql 경우 이미 커넥션 풀을 지원해 주고 있기 때문이란 뜻이에요.
connection string 을 기준으로, 커넥션 풀을 관리한다고 알고 있는데;;

--------------------------------------------------------------------------------------------------

2tear의 경우 client에서 바로 DB connetion을 할 것입니다.
문제는 이 connection을 dispose을 하더라도 DB서버와 클라이언트는 항상 connetion을 하나를 물고있습니다.
즉, 클라이언트의 수가 늘어날수록 DB서버의 connection수는 증가한다고 생각하시면될 것입니다.
실제로 간단한 테이블 만들어서 클라이언트에서 그 테이블의 자료을 처음에 가지고오고 dispose()를 하면...
보통
using(DBconnection)
{
}
이렇게 사용하시겠죠?
DB에서는 그 테이블이 포함된 DB가 삭제가 안될것입니다. 프로그램 종료전까지 말입니다.
3tear의 경우 실제 DB와 connection부분이 서버에서 이루어지기 때문에 DB는 단지 해당 서버와 connection을
물고 있으면 되는 것입니다.

--------------------------------------------------------------------------------------------------

또 답변을 달게 되는군요 -_-
생각하길 커넥션을 여는 만큼 커넥션이 열린다고 생각하기 쉽지만,
그것인 닷넷개체이든 DBMS 내부적이든 커넥션 풀을 관리하고 있습니다.
내가 커넥션을 연다고 열리는 것이 아니라, 이 내부적인 커넥션 풀이 모든 것을 관리하지요..
실제로 커넥션을 닫아도, 일정 시간 커넥션은 열려있게 되고, 이것또한 커넥션 풀이 관리를 하지요..
무한적 늘어나날 수 있는 커넥션을, 풀로 관리를 한다는 겁니다.

여기서 제가 오라클 얘길 했는데,
오라클은 트랜잭션의 커밋을 위해 세션 단위로 커밋을 하기 됩니다.
즉, 2tier 라면 반드시, 1인 1커넥션이 있어야 트랜잭션 스콥에 포함이 됩니다.

사용하는 디비가 오라클이라면 1클라이언트 : 1커넥션이 성립이 되지만,,
MSSQL 이라면 반드시 그렇지 않다라는 거죠 -_-;;
이해 되셨는지 ...

--------------------------------------------------------------------------------------------------

SQL DB가 connection 풀을 지원하지만 확실히 2tear 와 3tear에서의 차이는 있습니다.
전 그점을 이야기 하고 싶었던 겁니다.
2tear에서 클라이언트에 직접 connection을 열면 각 클라이언트마다 SQL DB에서 각각의 프로세서 ID를 부여 하고
있다는 것을 확인 하실수 있을 겁니다.
다른 예로 한 클라이언트에서 같은 connection string으로 여러개의 동일프그램을 실행 시켜 확인해보면
각각의 프로세서 ID을 부여하고 있고 close() or dispose()을 하여도 여전히 서로 물고 있다가 일정시간 더이상
작업이 없으면 프로세서를 해당 프로세서를 해제합니다.
하지만 3tear의 경우 클라이언트가 아닌 서버에서 DB와 connection을 연결하기 때문에 이와 같이 행동하지 앖습니다.
결론은 2tear 와 3tear에서 physical tear상황마다 틀리지만 logical tear로는 3tear로 가는것이 맞다는 것이
저의 의견입니다.
물론 이외도 보완상이라던지 관리 및 scalability등도 3tear의 장점이죠 ^^

--------------------------------------------------------------------------------------------------

하하하.. 그렇군요.
하지만 요즘은 3-tier 로 많이 구축이 되긴 하지만, 일부에서는 3~4 tier 가 아닌,,
터미널이나 콘솔형태의 서버를 많이 구축 하기도 한답니다.
바로, 서버의 비용적인 측면도 있겠지만, 분산 시스템에서 유지/보수가 과거의 터미널이나 콘솔 형태보다
몇 배 많이 들어가게 된다는 점입니다.
그리고, 클라이언트에게 권한을 할당하는 방식이 아닌, 서버가 모든 권한을 가짐으로써 보안 문제에도 예전 터미널 방식이 훨씬 유리하겠죠.
당연히 3-tier 로 가야 한다는 발상은 요즘 시대에 조금 위험한 듯 합니다^^

어찌되었건 제가 하고 싶었던 얘기는 커넥션에 대한 얘기인데, 약간 본질이 흐려진 것 같네요.


출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1582&page=3

Posted by Sting!
,