2005년 3월 30일 수요일

[UML 제대로 알기] ③ 개발 프로세스에 따른 UML 실전 모델링

[UML 제대로 알기] ③ 개발 프로세스에 따른 UML 실전 모델링
개발 프로세스에 따른 UML 실전 모델링
연재순서
1회. 가능성·확장성 품고 등장한 UML 2.0
2회. 초보자를 위해 다각도로 살펴본 UML
3회. 바로 알고 제대로 쓰는 UML 실전 모델링
4회. 닷넷 환경에서 UML 툴 활용 가이드
5회. 표준을 넘나드는 UML의 적절한 사용
고재현
2005/03/26
UML과 개발 프로세스라는 것이 만나게 되면 각 프로세스 별로 UML을 활용하여 모델링을 하는 방법이 약간씩 달라지기 때문에 사용상 주의가 필요하게 된다. 이와 더불어 웹 애플리케이션 개발을 위한 UML 모델링은 어떻게 하면 좋은지에 대해서도 생각해볼 필요가 있다.

이번 글에서는 지금까지 UML을 이용하여 소프트웨어 개발을 수행하면서 느꼈던 개념상의 모호함과 모델링 시 주의할 점에 대해 살펴보고자 한다.

약 7년 전 필자는 UML에 관하여 세미나를 하려고 어느 업체를 방문한 적이 있었다. 세미나를 막 시작하려고 하는 순간 어느 분께서 'UML이 이번에 새로 나온 XML의 한 종류인가 보죠?'라고 질문을 했다. 과연 이 난관을 어떻게 헤쳐 나가야 할지 막막한 순간이 UML에 관한 이야기를 하려고 하면 지금도 떠오른다.

UML이 우리나라에 처음 소개되던 시기에는 많은 분들이 객체지향이나 모델링 언어에 대한 개념이 일반적이지 않았다. 하지만 약 7년이 지난 지금은 소프트웨어 개발을 하는 대부분의 종사자들은 UML이라는 것이 무엇이고 어디에 쓰는 것인지 알고 있다. 또한 소프트웨어를 개발하기 위해서는 적어도 UML 다이어그램 몇 개 정도는 그려줘야 할 것 같은 생각을 갖고 있다. 많은 인식의 변화가 있었지만 지금 이 순간에도 여전히 UML이라는 것을 제대로 활용하여 소프트웨어를 개발하고 있는지 제대로 알고 있는지는 의문을 갖지 않을 수 없다.

UML과 모델 그리고 산출물
UML과 모델, 산출물은 비슷한 것 같지만 실은 아주 다른 개념이다. 우리가 소프트웨어라는 것을 개발하기 위해 '무엇을 만들 것인가?'라는 질문에 답하기 위해서는 대상이 되는 문제영역을 정확히 알 필요가 있다. 소프트웨어 개발팀 혹은 개발자는 문제영역에 대하여 정확하게 이해하기 위한 가장 좋은 방법은 모델을 통하는 것이다. 모델은 문제영역의 현실을 단순화시킴으로써 우리가 만들고자 하는 시스템을 더 잘 이해할 수 있게 한다.

『The UML User Guide』에 의하면 모델링을 통해 우리는 다음과 같은 4가지 목적을 얻을 수 있다고 한다.

◆ 모델은 시스템을 현재 또는 원하는 모습으로 가시화하도록 도와준다.

◆ 모델은 시스템의 구조와 행동을 명세화할 수 있게 한다.

◆ 모델은 시스템을 구축하는 틀을 제공한다.

◆ 모델은 우리가 결정한 것을 문서화 한다.

즉, 복합한 시스템에 대해 모델을 만드는 이유는 그러한 시스템을 전체적으로 이해할 수 없기 때문이다. UML은 앞에서 언급한 4가지 목적에 충실히 부합하기 때문에 소프트웨어를 개발함에 있어 모델링 언어로 UML을 이용한다.

UML 다이어그램이 산출물인가?
필자는 어느 프로젝트의 개발 프로세스 멘터링를 하기 위해 한 업체를 방문한 적이 있었다. 그 곳에서 도착하여 간단한 미팅을 한 후 가장 먼저 요구했던 정보는 고객과 합의한 활동과 산출물의 목록이었다. 그 문서에는 역시 예상대로 요구사항 산출물에 '유스케이스 다이어그램'이라는 것이 있었고 분석 산출물에 '클래스 다이어그램'이라는 것이 들어 있었다. 이외 여러 부분에 'XXX 다이어그램'이 눈에 띄었다.

UML 다이어그램이 산출물이 될 수 있는가? 결론적으로 말하자면 UML 다이어그램은 산출물이 아니다. 나중에 자세히 설명하겠지만 산출물의 한 종류가 모델이고 모델을 표현하기 위한 수단 중에 하나가 UML 다이어그램이다.

예를 들어 UML의 클래스 다이어그램을 생각해 보자. 다양한 종류의 산출물은 그 산출물이 표현하고자 하는 정보를 보여주기 위해 UML 다이어그램을 이용한다. 유스케이스 실현(Realization)의 VOPC(View of Participating Classes)에서도 사용하고 비즈니스 조직을 표현하고자 할 때도 클래스 다이어그램을 사용할 수 있다. 또한 아키텍처 메커니즘을 표현하고자 할 때도 클래스 다이어그램을 이용한다. 유스케이스 다이어그램의 경우도 비즈니스 유스케이스 모델을 표현할 때도 쓰이고 시스템 유스케이스 모델을 표현할 때도 쓰인다. 다양한 산출물에 UML 다이어그램을 쓴다.

요구사항에서 혹은 분석/설계에서 산출물로 클래스 다이어그램이라고 명시해 놓으면 과연 어떤 클래스 다이어그램인지 알 수 있을까? 또한 시퀀스 다이어그램이 산출물 목록에 버젓이 들어있다면 그 다이어그램은 과연 어떤 정보를 표현하려고 하는 다이어그램인지 알 수 없다. 다이어그램은 산출물의 의도를 표현하기 위해 사용하는 하나의 표현도구이다. 산출물은 절대로 아니다.

우리의 목적은 산출물이다
앞에서 UML은 산출물이 아니며 모델은 산출물의 한 종류라는 것의 이야기했다. 그렇다면 산출물을 무엇을 의미하는가? 일반적으로 산출물이라는 것은 종이 형태의 문서로 알고 있다. 하지만 산출물은 프로세스를 통해 프로젝트 기간 동안 만들어지는 의미를 갖는 모든 것을 일컫는다. 그 중 하나가 모델이며 실행 가능한 바이너리 형태의 컴포넌트, 문서, 소스코드, 스크립트 등이 해당한다. 산출물(Artifact)의 정의를 보면 다음과 같다.

산출물은 어떤 정보의 일부로서 1) 프로세스를 통해 만들어지고 변경되고, 사용되며 2) 책임영역을 정의하며 3) 버전컨트롤의 대상이 된다. 산출물은 모델, 모델요소, 또는 문서 등이 될 수 있으며 문서는 다른 문서의 일부분으로 포함 될 수 있다(A piece of information that: 1) is produced, modified, or used by a process, 2) defines an area of responsibility, and 3) is subject to version control. An artifact can be a model, a model element, or a document. A document can enclose other documents).

모델(model) : 유스케이스 모델, 분석 모델, 설계 모델과 같이 다른 산출물을 포함하는 형태를 갖는 산출물

모델 요소(model element) : 다른 산출물 내부에 포함되어 있는 산출물로 예를 들면 유스케이스 모델 안의 유스케이스, 액터와 설계 모델 내부의 설계 클래스, 설계 서브시스템 등과 같은 산출물

문서(document) : 문서로 존재하는 산출물로 비즈니스 케이스, 소프트웨어 아키텍처 문서 등과 같은 산출물

◆ 소스코드나 실행 가능한 것(컴포넌트) 등

<그림 1>은 산출물과 모델 그리고 UML과의 관계를 클래스 다이어그램으로 표현한 것이다.

<그림 1> 산출물의 종류와 UML간의 관계

모델링은 개발기간을 늘이는 원흉?
필자는 지금까지 여러 프로젝트에서 직접 개발에 참여하기도 했고 컨설팅 또는 멘터링 형태로 참여하면서 사람들로부터 많은 질문을 받아왔지만 그 중에 가장 많이 하는 질문은 “우리 프로젝트에 UML이 꼭 필요하겠습니까? 프로젝트 규모가 좀 작으면 UML을 적용하지 않아도 괜찮지 않을까요?”라는 것과 “UML을 적용하면 개발이 좀 늦어지지 않겠습니까?”라는 두 가지 질문이 대표적이다.

우선 첫 번째 질문의 의미를 생각해 보자. 프로젝트 규모가 작으면 UML을 굳이 적용하지 않고 두어 명 개발자들이 그냥 뚝딱뚝딱 시스템을 만들면 되지 않을까 하는 것이다. 필자도 비슷한 생각으로 모델링을 하지 않고 프로젝트를 진행해 본적이 있었다. 개발자 3명이 기능 수 약 100개 정도의 시스템을 개발한 적이 있었는데, 작고 간단한 시스템이었으므로 순진한 생각에 그냥 테이블 정도만 정리해 놓고 코딩부터 시작하면 쉽고 금방 될 줄 알았다.

하지만 상황은 그렇게 호락호락하지 않았다. 용어와 기능부터 명확히 정의되지 않았고, 시작부터 개발자간의 서로 다른 이해수준은 프로젝트를 암울하게 만들었다. 어렴풋한 이해를 바탕으로 만들어지는 코드는 만들고 나서 뜯어고치는 코드가 더 많아 졌고 비즈니스 컴포넌트간의 인터페이스로 수시로 어긋났다. 결국 간단하게나마 모델을 만들면서 진행하기로 했고 모델링 시간 약간 줄이려고 하다가 우왕좌왕한 만큼 시간을 허비한 결과로 나타났다. 꼭 UML이 아니더라도 어떤 형태로든지 모델링은 해놓았어야 했고 그것이 업계 표준인 UML로 했더라면 더 좋지 않았을까 하는 후회가 든다.

혼자 만들더라도 역시 UML로 모델링을 하는 것이 개발하는 중에도 좋고 개발하고 난 뒤를 생각해봐도 반드시 필요하다는 생각이다. 개발을 마치고 난 뒤에 누군가 그 시스템을 유지보수 해야 한다면 전 개발자가 UML을 이용하여 만들어 놓은 모델을 보고 아마 감사하게 생각 할 것이다. 또 개발하고 있는 순간에도 몇 명밖에 없지만 그 사람들끼리 의견 교환이나 문제 영역에 대한 공통적인 이해를 돕기 위해서도 필요하다. 혼자 개발하더라도 생각이 정리가 잘 안되면 UML로 아이디어를 정리하는 판에 다른 사람과 생각을 맞추는데 필요가 없겠는가?

“UML을 적용하면 개발 기간이 늦어진다”. 어떻게 보면 약간 황당할 정도의 이야기임에 틀림없다. UML 때문에 개발기간이 늘어나는 것이 아니라 익숙지 않은 UML을 모델링 언어로 선택함으로써 배우는 기간이 더 늘어나는 것이 정확한 의미가 아닐까 싶다. 심지어 어떤 프로젝트 매니저는 개발은 그냥 개발대로 나가고 산출물은 산출물대로 만들어 나가서 나중에 소스코드가 나오게 되면 역공학(Reverse Engineering)을 통해 UML 모델을 만들어내자는 제안을 하기도 했다. 모델과 코드간의 동기화를 위해 역공학을 이용하지만 과연 이것이 시간을 줄이는 방법일까?

개발 프로세스와 과도한 모델링
무엇을 모델링 할 것인가? 어느 정도의 정밀도 수준으로 모델링 할 것인가? 모델링의 범위는 어떻게 설정해야 하는가? 이러한 질문에 답하기 위해 이것저것 고민해보기도 하고 업계에 종사하는 분들과 메신저로 혹은 술자리에서 때로는 프로젝트 회의 시간에 토의를 많이 해 보았다. 그 중 필자가 알고 지내는 대부분의 개발자들의 한결같은 주장은 '과도한 모델링'의 압박에 시달린다는 것이었다. 이야기를 해 본 결과 실제로 개발자들이 느끼는 '과도한 모델링'의 기준은 주관적이었지만 몇몇 주장은 일견 수긍이 가기도 했다.

그 중에 하나가 프로세스의 적용 문제였는데 지구상에 존재하는 모든 프로젝트는 서로 다른 시스템을 만들고 있기 때문에 모든 프로젝트 천편일률적으로 적용되는 모델링 범위나 정밀도 수준은 없다. 하지만 그것을 프로젝트의 규모나 도메인의 종류 등의 일반화된 기준으로 묶고 그 기준에 해당하는 개발 프로세스를 제공해야 한다. 예를 들어 현재 우리나라에서는 RUP(Rational Unified Process)나 마르미와 같은 프로세스 프레임워크를 많이 사용한다. 또 SI 업체에서는 자체적으로 발전시켜온 방법론이나 앞에서 언급한 RUP 또는 마르미를 기반으로 하여 자체 프로세스를 개발하여 사용한다.

문제는 각 프로젝트마다 프로세스를 조정(Tailoring)해서 적용해야 하는데 현실은 그렇지 않다는 것이다. 심지어 이런 이야기를 들은 적도 있다. "CBD를 적용하기엔 우리 프로젝트 규모에 좀 안 어울리지 않나요? 아는 사람한테 물어봤더니 규모가 작으면 CBD가 안 어울린다고 하더라고요. 대신 굳이 CBD를 하려면 UML 모델링 툴을 반드시 도입해서 진행해야 한다고 하던데…" 누가 이 분에게 그런 이야기를 했는지 모르지만 이 정도 되면 거의 사기에 가깝다. 그 분이 생각한 CBD라는 것이 무엇이었는지 어림짐작되는 대목인데 마치 신발에 발을 맞추는 꼴이 되어 버렸다.

발을 보호하는 것이 1차적인 목표이고 멋도 부리는 것이 신발을 신는 2차 목표라고 보면, 우선 성공적인 프로젝트를 수행하기 위해 프로세스를 도입하고 이왕 개발할 것 좀 체계적으로 가이드를 받아보자는 목적도 있을 것이다. 객체지향이나 CBD와 같은 단어의 의미는 일단 논외로 하고 앞에서 언급한 프로세스 조정에 관하여 이야기 해보자.

프로세스 조정
앞에서 언급했듯이 RUP나 마르미는 일반적이고 범용적인 개발 프로세스이다. 세 달간 3명이 개발하는 프로젝트와 2년간 50명이 개발하는 프로젝트 모두 개발 프로세스가 같을 수 있겠는가? MS 워드와 같은 워드프로세서를 개발하는 것과 은행을 위한 전산 시스템을 개발하는 개발 프로세스가 같을 수 있겠는가?

서로 다른 특성을 갖는 프로젝트를 위해 프로세스 조정(Tailoring)이란 작업을 하게 되는데 여기서 유의해야 할 것은 조정할 수 있는 영역을 제한해야 한다는 것이다. 프로젝트 현장에서 편의대로 프로세스를 고치거나 누락시켜서는 안되고 프로세스 조정의 결과는 승인되어야 한다. 더 나아가 프로젝트 세부 유형에 따른 개발 프로세스를 제공한다면 더할 나위 없이 좋을 것이다. 프로세스의 대표적인 구성 요소로는 작업 영역, 작업 흐름, 활동, 산출물, 역할 등이 있다. 필자는 이 모든 것이 프로세스 조정 대상이 될 수 있다고 생각한다.

RUP의 경우 '프로세스 엔지니어'가 프로세스 조정 작업을 진행하고 마르미 경우 ‘도구 및 방법론 관리자’가 그 역할을 수행한다(XP의 경우는 코치라고 한다. 편의상 이 글에서는 모두를 프로세스 엔지니어라고 칭하겠다). 이런 역할의 특성상 다른 사람과의 의견 교환이나 대립이 있기 때문에 프로세스 엔지니어는 소프트웨어 개발에 관한 폭넓은 지식을 갖고 있어야 하며 의사소통 능력이 있어야 한다. 프로젝트 관리자나 프로젝트 리더 또는 시스템 분석가나 아키텍트를 설득해야 할 일이 있을 수도 있다.

프로젝트 관리자나 프로젝트 리더는 프로젝트 관리 관점으로 개발 프로세스를 바라볼 것이고 시스템 분석가나 아키텍트는 소프트웨어 시스템을 개발하는데 중요한 결정을 내리기 위한 관점으로 프로세스를 바라보며 자신의 의견을 제시 할 것이다. 이들의 의견을 통합하고 조정하며 고객의 요구를 반영하여 해당 프로젝트의 상황에 맞는 프로세스로 조정하게 된다.

<그림 2> 프로세스 엔지니어의 활동과 산출물

<그림 2>에서 프로세스 엔지니어는 현 개발 조직 평가, 개발 케이스(Development Case) 작성, 프로젝트 전용 템플릿 작성의 세 가지 활동을 하게 된다. 이에 따른 개발 조직 평가, 개발 케이스 프로젝트 전용 템플릿의 세 가지 산출물을 작성하는데 여기서 가장 핵심적인 산출물은 개발 케이스이다. 특정 프로젝트를 위한 프로세스를 조정하고 개발 케이스를 작성하기 위해서는 프로젝트의 상황을 이해 할 필요가 있는데 이를 위하여 현 개발 조직에 대한 평가가 이루어져야 한다. 또한 개발 조직 이외에도 고객에 대한 평가도 동시에 이루어져야 한다.

예를 들어, 기업을 위한 애플리케이션을 개발한다고 가정한다면 개발 완료 후에 고객은 유지보수를 위한 산출물을 요구할 것이다. 프로세스 엔지니어는 개발 조직 및 고객을 대상으로(좀 더 의미를 확장하자면 이해관계자를 의미 할 수 있다) 현 개발 조직에 대한 평가를 할 때 프로세스, 툴, 사람들의 능력, 사람들의 태도, 고객들, 사용 기술, 문제점, 개선 분야의 관점에서 개발 조직의 현재 상태를 평가한다.

프로세스 조정을 위한 요구사항을 파악하고 이를 근거로 프로세스 조정을 실시한다. 프로세스 조정의 결과는 개발 케이스이다. 프로세스 조정에 관한 자세한 사항을 알고 싶으면 RUP나 마르미 또는 Barry Boehm과 Richard Turner가 쓴 『Rebalancing Your Organization's Agility and Discipline』를 참조하면 도움이 될 것이다.

케이스 툴을 쓰면 생산성이 높아지는가?
Steve McConnell은 그의 저서인 『Professional Software Development(2003)』에서 코끼리에 관한 재미있는 이야기를 소개했다. 이야기를 요약하면 이렇다.

고대 이집트 어느 왕의 피라미드를 만드는 공사가 한 참 진행 중이었다. 엄청나게 큰 암석을 사람들이 일일이 날라야 했으므로 공사의 진척은 매우 더디었다. 그래서 공사 프로젝트 관리자는 고민 끝에 어디선가 코끼리라는 엄청난 동물이 있는데 이 동물은 사람이 운반하는 것 보다는 몇 배 혹은 몇십 배 높은 생산성은 낸다는 이야기가 얼핏 기억이 기억났다. 이 동물만 들여오면 지금까지 지연되었던 일정을 단번에 만회할 수 있을 것 같은 생각이 들었다.

공사 프로젝트 관리자는 그 동물을 도입하기로 결심하고 일을 추진했다. 코끼리라는 동물을 들여놓고 보니 과연 듣던 대로 몸집도 엄청나고 그에 걸맞게 힘도 대단했다. 공사 프로젝트 담당자는 입가에 미소를 머금으며 공사현장에 코끼리를 투입하라는 지시를 내렸다. 그러나 그 동물을 투입하는 순간 순조롭게 진행될 것 같은 공사가 점점 이상하게 진행되고 일정도 생각했던 것만큼 줄여지지 않았다. 줄여지기는커녕 오히려 사람이 진행했던 것보다 더 늦어지는 조짐이 보였다.

공사 프로젝트 관리자는 상황파악에 나섰는데 공사 현장에 가서 자세히 관찰을 해보니 문제를 바로 파악할 수 있었다. 바로 코끼리를 다룰 수 있는 사람이 없었던 것이었다. 그의 공사팀은 그런 동물을 부리면서 일을 한 적이 단 한번도 없었기 때문에 기존의 공사프로세스와 엄청난 혼선을 빗었다. 또 코끼리에 대해 아는 것이 전혀 없었기 때문에 코끼리를 돌보지 못해 걸핏하면 앓아눕기 일 수였고 몇몇 코끼리는 도망가 버려 엄청난 비용을 들여 도입한 코끼리가 무용지물이 되어 버렸다. 공사 진행은 그 만큼 지연되었고 코끼리를 포기할 수밖에 없었다"


필자가 보기엔 그 코끼리가 바로 케이스 툴이 아닌가 싶다. 프로젝트 생산성을 높이기 위해 케이스 툴을 도입하는 것은 바람직하지만 그 생산성을 높이기 위해서는 일정 기간의 훈련이 필요하다. 요즘 나오는 케이스 툴은 기능도 막강하고 더불어 복잡하다. 또 종류도 많아 수많은 케이스 툴과 연동도 필요하다. 일례로 IBM 래쇼날의 스위트 제품은 내장하고 있는 서브 제품만 해도 10여 가지가 넘는다. 다른 케이스 도구도 사정은 다를 바 없다.

프로젝트 현장의 요구는 이러한 제품을 좀 편하게 사용하고 싶어 한다. 소프트웨어 개발 라이프 사이클 전반에 걸쳐 케이스 툴을 적용하려고 서로 다른 회사 목적이 다른 툴을 서로 연계시켜야 한다. 이 일은 대단히 복잡하고 가변적이어서 앞의 요구를 충족시키려면 프로세스적으로나 케이스 툴에 대한 상당한 지식과 수준의 기술이 요구된다.

요구사항관리 도구와 분석/설계를 위한 모델링 도구와의 연계 부분, 모델링 도구와 개발 및 테스팅 도구와의 연계 부분이 대표적인 연계 부분이다. 또한 각 케이스 툴에서 쏟아져 나오는 다양한 모델과 문서들을 통합적으로 관리할 수 있는 형상 및 변경 관리와 각 작업영역(요구사항, 분석/설계, 구현, 테스트 등)을 기반으로 하여 반복 개념으로 프로젝트를 관리해야 하므로 실제 케이스 툴이 소프트웨어 개발을 도와주려면 아주 많은 것들을 고민해야 한다.

특히 프로젝트 현장에서 따르고 있는 프로세스를 정확히 지원해줘야 하므로 케이스 툴 자체의 유연성도 갖춰야 한다. 케이스 툴은 단어의 의미에서와 같이 소프트웨어 개발을 정말로 도와주는 케이스 툴이 되어야 하지 않을까 싶다.

머릿속 아이디어를 모델로 구체화하기
필자는 항상 프로젝트를 진행하다 보면 항상 딜레마에 빠지는 문제가 있다. 프로젝트에 참여하는 사람들이 UML을 잘 모르는 경우이다. UML을 모델링 언어로 채택한 프로젝트는 그 프로젝트에 참여하는 모든 사람들이 UML을 알고 있어야 한다는 전제조건이 따른다.

하지만 현실은 어떠한가? 프로젝트를 수주해야만 먹고 사는 개발 업체의 경우 UML을 알던 모르던 일단 수주를 해야만 하는 상황이다. 그 회사의 개발팀 구성원이 UML을 잘 알고 있다면 문제가 없겠지만 잘 모르는 경우 심하면 그 구성원 중 단 한 명도 제대로 알지 못하는 경우 문제가 아닐 수 없다.

이 때 가장 많이 활용하는 방법은 UML을 알고 있는 사람이나 업체를 고용하여 먼저 샘플을 만들게 한 후 그것을 템플릿으로 하여 모델링을 진행하는 것이다. 물론 이런 프로젝트가 많아야 필자 같은 사람들이 먹고 살긴 하겠지만 프로젝트 입장에서는 참 못마땅한 일이 아닐 수 없다.

예를 들어 UML을 알고 있는 사람이 시스템의 한 부분에 대하여 클래스 다이어그램을 그렸다고 하자. 그 클래스 다이어그램이 샘플로 나오면 다른 모든 사람들은 그 샘플을 기반으로 모델링을 시작하게 된다. 맞는지 틀리는지 자신의 판단 기준이 없는 채로 샘플을 '다른 이름으로 저장하기'로 해서 따로 하나는 만들어 놓은 다음 다이어그램을 그리고 저장을 한다. 예전에 학교에서 다른 친구 소스코드 구해다가 변수명 좀 바꾸고 약간 짜깁기해서 리포트를 제출하는 것과 비슷한 상황이 된다.

다이어그램이야 만들어 졌지만 모델링을 하는 사람이 그 모델이 적합한지 아닌지 판단할 수 없으므로 그려진 다이어그램을 들고 샘플을 만든 사람에게 달려와 이것저것 물어본다. 물론 어쩔 수 없는 상황이고 그런 과정을 거치면서 사람들의 모델링 실력이 향상되겠지만 모르면서 그림을 그리는 사람도 답답하고 프로젝트 진행에도 문제가 많다.
잘 모르는 사람이 프로젝트를 하는, 어떻게 보면 시작부터 이상하긴 하지만 어쩔 수 없다고 보고 조금이나 도움이 되도록 머릿속의 아이디어를 모델로 구체화 하는 과정을 소개하면 조금 낫지 않을까 싶다.

시작은 글쓰기부터
만들고자하는 모델이나 그리고자 하는 다이어그램에 따라 접근 방식이 다르겠지만 결국 시작은 머릿속의 생각을 글로 정리하는 것부터 시작한다. 글로 쓰기 귀찮다면 메모장이나 화이트보드에 본인의 생각을 간단하게나마 자유로운 형식으로 정리하면서 시작하게 된다.

예를 들어, 고과장이 애플리케이션 프레임워크를 모델링 한다고 가정해 보자. 프레임워크를 모델링하는 사람이 UML을 설마 모르진 않겠지만 이 사람이 어떤 과정을 거치면서 머릿속 아이디어에서부터 구체적인 모델을 만들어 가는지 살펴보자. 고과장은 프레임워크의 한 부분을 모델링하기 위해 고심하다가 머릿속에서만 맴도는 아이디어를 글로 써보기로 했다.

클라이언트에서 어떠한 메시지를 보내면 프레임워크에서는 그 메시지를 해석하여 어떤 비즈니스 로직을 수행할 지 결정하여 수행한다. 메시지에 따라 수행하는 비즈니스 로직은 메시지가 변해도 동일한 비즈니스 로직이 수행되어야 하거나 동일한 메시지에도 상황에 따라 비즈니스로 로직이 바뀔 수 있어야 한다.

써 놓고 읽어보니 대충 어떤 방향으로 만들어야 할지 느낌이 좀 오기 시작했다. 왠지 잘 될 것 같은 느낌으로 입가엔 미소를 머금으며 모델링 도구를 실행시켰다.

모델링을 시작하다
우선 고과장은 클라이언트와 클라이언트가 보내는 메시지를 받는 무엇인가가 있어야 하지 않을까 하는 생각이 들었다. 또 어떤 비즈니스 로직을 수행할지 판단하는 객체도 있어야 할 것 같고, 클라이언트가 보낸 메시지에 대응하는 비즈니스 로직을 갖고 있는 정보를 갖고 있는 객체도 있어야 할 것 같다.

고과장은 우선 '클라이언트', '메시지 판단자', '메시지와 비즈니스 로직 맵핑 정보' 등이 객체가 되지 않을까 생각했다. 모델링 툴을 띄워놓고 세 가지 객체를 그리고 나니 이들 간의 관계와 각 객체가 어떤 일을 해야 하는지(Responsibility)가 알쏭달쏭 했다. 그래서 이 세 가지 객체를 놓고 이들 객체가 서로 어떻게 인터랙션(Interaction)하는지 알아보기 위해 시퀀스 다이어그램을 그려보기로 했다.

<그림 3> 개념 정리를 위한 시퀀스 다이어그램

이렇게 그려놓고 보니 객체 간에 어떻게 교류하는지 눈에 들어와 더 구체적으로 생각할 수 있게 되었다. 고과장은 시퀀스 다이어그램을 보면서 차근차근 짚어보기로 했다. 클라이언트가 메시지를 '메시지 판단자'에게 보내면 해당 메시지에 대응하는 비즈니스 로직을 알려주고 메시지 판단자가 비즈니스 로직을 수행하여 클라이언트에게 보내주는 깔끔한 형태로 정리된 것 같았다.

뭔가 빠진 것 같은데?
하지만 객체 간에 어떻게 교류하는지 눈에는 보였지만 왠지 무엇인가 빠진 듯한 느낌이 들었다. 시퀀스 다이어그램을 쳐다보고 있으니 메시지 판단자가 '비즈니스 로직 수행'을 하는 것이 메시지 판단자의 역할에 비해 좀 오버하는 듯한 느낌이 들었다. 또 비즈니스 로직 수행이 잘 되었는지 아닌지 판단하는 부분도 있어야 할 듯 했다. 그래서 '비즈니스 로직 수행자'라는 이름은 좀 이상하지만 이런 객체를 따로 빼 두어 시퀀스 다이어그램을 보완하기로 했다.

<그림 1> 산출물의 종류와 UML간의 관계

비즈니스 로직 수행자를 넣어 시퀀스 다이어그램을 다시 그려보니 이제 뭔가 맞아 돌아가는 느낌이 들었다.

<그림 5>"그림 3"의 클래스 다이어그램

UML의 시퀀스 다이어그램과 클래스 다이어그램을 통해 객체간에 교류와 각 객체의 책임(Responsibility)를 찾아냈다.

설계를 위한 모델로 발전
이제 개념적으로 정리한 모델을 바탕으로 좀 더 구현하기 좋은 형태로 발전시켜 보자. 구현을 위해서는 좀 구체적인 형태로 정의하고 실제적인 비즈니스 로직 처리와 에러 처리를 표현하기 위한 무엇인가가 필요했다. 고과장은 고민 끝에 웹 서버로부터 아이디어를 얻었다. 웹 서버는 웹 브라우저와 커뮤니케이션을 위해 Request와 Response를 사용한다. 브라우저의 요청은 Request에 담아 웹 서버로 보내고 웹 서버의 응답은 Response에 담아 브라우저로 보내게 된다. 이런 비슷한 개념을 프레임워크에 적용해보면 어떨까 생각했다.

즉, 클라이언트의 요청은 BusinessRequest에 담아 보내고 서버의 응답은 BusinessResponse에 담아 보내되, 비즈니스 로직 수행시 Exception이 발생하면 BusinessResponse에 담아 메시지 판단자가 처리하게 하면 어떨까 하는 생각을 했다. 고과장은 이렇게까지 생각이 정리되자 시퀀스 다이어그램을 통해 아이디어를 구체화 해보기로 했다. 시퀀스 다이어그램은 <그림 6>과 같다.

<그림 6> 보다 더 구체화된 시퀀스 다이어그램

클라이언트 쪽의 예상 소스코드도 넣어 보고 메시지를 비즈니스ID라는 용어로 바꿔봤다. 또한 비즈니스ID의 해당 비즈니스 로직을 실행시키기 위해서는 리플렉션(Reflection)이라는 기술도 써야 할 것 같고 Exception 처리를 위한 부분도 따로 둬야겠다는 생각이 들어 BusinessResponse에 BusinessExcuteHelper가 Exception을 담아주는 형태로 모델링을 했다.

<그림 6>의 시퀀스 다이어그램을 보면 중간 중간 노트가 많이 달려있는 것을 볼 수 있다. 시퀀스 다이어그램의 중간 중간을 보면 샘플 코드나 리플랙션, Exception과 같은 구현을 염두 한 노트를 볼 수 있다. UML로 모델링을 할 때 노트의 역할은 필수 불가결한데 해당 다이어그램의 이해를 돕기 위해서나 모델러의 의도를 명확히 하기 위해서 또는 아직 불분명한 부분이 있을 경우 판단에 도움이 될 만한 주석을 달기 위한 수단으로 이용하면 좋다.

구체화된 모델
자 이제 구현을 위한 시퀀스 다이어그램도 그렸고 구체적으로 어떻게 접근하면 될지 방안이 섰기 때문에 최종 클래스 다이어그램을 그려보기로 했다. 클래스 다이어그램을 그리면서 고과장은 클라이언트가 보내는 메시지와 해당 비즈니스 로직 정보를 어떤 형태로 할 까 고민하다 XML 형태로 하는 것이 가장 좋을 것 같다는 판단이 들었다.

애플리케이션이 실행되면서 XML에 있는 정보를 읽어 캐싱하고 있는 형태로 만들고 그 역할은 BusinessMapper라는 객체에 해당 정보를 Map에 담아 두기로 했다. BusinessMapper는 단 하나만 존재해야 하므로 싱글턴(Singleton) 패턴을 적용하기로 했다(실제 구현을 하기 위해서는 보다 더 구체적으로 모델링을 해야겠지만). <그림 7>는 고과장의 아이디어를 반영한 최종 클래스 다이어그램이다.

<그림 7> 고과장 생각을 정리한 최종 클래스 다이어그램

이 클래스 다이어그램에서 XXX_ServerPage나 XXX_Action, XXX_ActionForm, XXX_Mgr과 같이 XXX라는 접두어가 붙은 클래스는 비즈니스 로직 개발자들이 직접 만들어야 하는 부분이다. 개발자들은 XXX라는 접두어가 붙은 클래스만 개발하면 되고 고과장이 만든 프레임워크에 끼워 넣어 어떤 메시지가 어떤 비즈니스 로직과 맵핑되는지 XML 파일만 편집하면 되는 구조로 모델링이 되었고 고과장의 생각대로 메시지와 비즈니스 로직과의 느슨한 관계를 유지할 수 있는 모델이 만들어 졌다.

일단 이런 구조의 프레임워크가 좋은 것인지 아닌지는 일단 논외로 하고 머릿속의 아이디어를 모델로 구체화하는 과정을 다시 한번 정리하자면 우선 1) 머릿속의 아이디어를 글로 정리하고 2) 정리한 글을 바탕으로 UML로 바꿔 본다. 이때 동적인 측면과 정적인 측면을 고려하고 전에 정리한 글에서 UML로 변환하는 과정에서 빠진 것은 없는지 미처 생각하지 못한 것들이 있는지 확인한다. 그냥 글로 확인하는 것보다는 UML의 시퀀스 다이어그램이나 엑티비티 다이어그램 등을 이용하여 확인하면 보다 더 정확하게 모델링할 수 있다. 3) 이렇게까지 진행이 되었으면 실제 구현을 위한 모델로 발전시켜 본다. 특정 플랫폼이나 개발언어, 미들웨어 등을 고려하면서 그려나간다.

결국 1)~3)까지가 실제 모델링을 통해 아이디어를 구체화 시켜나가는 모든 것이다. 1)번을 잘 하기 위해 각종 기법(예를 들어, 유스케이스나 유저스토리 등)이 동원되고 2)번을 잘 하기 위해 CRC 카드와 같은 기법이 사용된다. 3)번 역시 마찬가지로 각종 설계 패턴이나 J2EE 패턴과 같은 것을 참조한다. 개발 프로세스에 따라 어떤 모델을 만드는가, 어떤 산출물을 만드는가에 따라 그려야 할 다이어그램이나 모델의 정밀도가 다르겠지만 결국 1)~3)까지의 행위를 반복함으로써 우리가 얻고자 하는 모델을 얻어낼 수 있다.

쉽고도 어려운 유스케이스 모델링
1960년대 후반쯤 이바 야콥슨에 의해 만들어진 유스케이스는 1990년대에 들어서면서 아주 폭넓게 사용하는 요구사항 파악 및 분석 기법으로 자리 잡았다. 우리나라의 경우 아마도 대부분의 프로젝트에서는 유스케이스라는 이름의 산출물을 만들고 있고 만들었을 것이다. 필자도 여러 프로젝트를 하면서 유스케이스를 써서 요구사항 분석을 했었다.

초기 접근하기에 개념적으로 어려운 부분이 별로 없기 때문에 누구나 의욕적으로 참여하여 유스케이스를 작성해 해 나간다. 하지만 이 유스케이스라는 것이 단순해 보이지만 결코 만만치 않은 특성을 갖고 있다. 처음에는 쉬운 듯하지만 진행해 나갈수록 어려워지는 경향이 있다. 또한 프로젝트의 진행 상태나 규모에 따라 유스케이스 작성 방식이 달라진다. 이러한 특성 때문에 바쁜 프로젝트에서 뭔가 별 고민을 하지 않고 쉬운 작성 형식이나 방법을 목말라하는 개발자들에게는 혼돈의 연속일 수밖에 없다.

필자 개인적으로 유스케이스에 관해 잘 설명한 도서는 Alistair Cockburn의 『Writing Effective Use Cases (2001)』이다. 영어에 어려움을 겪는 분들은 번역본도 나와 있으니 구해서 한번 꼭 읽어보길 바란다. 이 책의 내용 중에 유스케이스 작성시 주의할 점이 있는데 그 중 현장에서 실제로 많이 나타나는 몇 가지를 소개하고자 한다. 일부 주의사항은 책에서 발췌하고 필자가 느낀 점을 중심으로 설명해본다.

유스케이스는 산문체 수필이다
유스케이스 다이어그램을 갖고 지지고 볶던 독자들에게는 약간 의아한 말일 수 있다. 사실 유스케이스는 텍스트 기반의 서술이다. UML의 유스케이스 정의를 보아도 '유스케이스는 시스템 전체나 유스케이스 일부 행동을 명세화 하고 순차적으로 발생하는 활동들을 서술하는 것이다. 시스템은 이러한 활동을 수행하여 액터에게 원하는 결과를 준다'라고 되어 있다. 그래픽으로는 타원으로 표현하고 중심에는 몇 단어로 이루어진 이름이 있다.

프로젝트를 진행하면서 다이어그램에 얽매여 오로지 다이어그램만으로 유스케이스를 작성하려고 하면 관련 팀은 점점 어려움으로 빠져 들게 된다. 유스케이스 다이어그램 그 자체가 담고 있는 정보는 매우 한정적이다. 유스케이스명과 어떤 액터와 관계가 있는지를 나타내는 선 몇 개, 복잡하게 뒤 얽힌 '포함(include)와 확장(extends)을 표현하는 점선들이 뒤덮여 있을 뿐이다. 사람들이 그 다이어그램을 보고 요구사항이 뭔지 정확하게 알 수 있을까?

단어 몇 개로 이루어진 유스케이스 명을 보고 무엇을 하는 유스케이스인지 추측을 할 뿐이다. 다이어그램의 정보가 충분치 않으므로 답답한 마음에 다이어그램에 갖가지 정보를 넣으려고 하고 유스케이스의 목표 수준은 점점 내려가고 복잡해지는 현상이 나타난다. 결국 아무도 이해할 수 없는 다이어그램이 만들어지면서 오히려 팀간의 명확한 이해의 공유는커녕 혼란만 가중시키는 결과를 낸다. 유스케이스 다이어그램만을 놓고 이틀 동안 논쟁만 하는 개발팀을 실제로 보았다. 다시 한번 강조하지만 유스케이스는 텍스트 형식의 서술이다. 액터와 유스케이스 간에 어떤 일이 일어나는지 글로 적음으로써 이해를 명확히 할 수 있다.

유스케이스와 사용자 매뉴얼
유스케이스 교육을 들어 봤거나 프로젝트를 해 본 분들이면 아마도 귀가 따갑게 유스케이스는 GUI에 관한 부분은 적지 않는 것이라고 들어왔을 것이다. 유스케이스를 작성하면서 유저 인터페이스에 관한 내용을 언급하기 시작하면 유스케이스가 점점 사용자 매뉴얼이나 화면 설계서처럼 변해간다.

유스케이스는 향후 작성할 분석/설계 모델이나 화면 설계 등의 모델의 기본 정보를 제공한다. 또한 유스케이스로 추적(Trace)할 수 있다. 아니 추적되어야 한다. 사용자 매뉴얼이나 화면 설계서가 분석/설계 모델의 상위 요건이 될 수 있는가? 결코 그렇지 않다. 유스케이스는 적당한 목표수준으로 작성함으로써 상위 요건으로써의 역할을 다 할 수 있어야 한다.

포함과 확장의 오남용
어떤 유스케이스 다이어그램을 보면 다이어그램 가득히 실선과 점선이 어지럽게 꼬여 있는 그림을 가끔 본다. 왜 이런 다이어그램이 나오는지는 앞에서 언급하였다. 필자의 경우 이런 다이어그램은 십중팔구 뭔가 잘못되었을 것이라는 예감이 뇌리를 스친다. 우선 복잡하면 제대로 파악할 수 없고 서로 이해를 명확하게 하지 못했을 가능성이 높으며 이해도가 떨어지는 상황에서 유스케이스가 제대로 작성되었을 리가 없다.



유스케이스 다이어그램이 복잡하면 각 유스케이스의 이벤트 흐름을 작성하면서 아주 여러 부분에 포함(include)과 확장(Extends)이 나타나게 되고 결국 전체적으로 유스케이스의 유지보수를 어렵게 만든다. 유지보수가 어렵게 되면 요구사항을 정확히 담는데 점점 힘들어지고 현실과 동떨어진 그저 서로 다른 이해수준으로 각자의 머릿속에만 존재하는 요구사항이 나오게 된다.

최초 확장이라는 개념이 등장하게 된 이유는 이전 시스템의 요구사항 파일을 건드릴 수 없다는 실행 지침 때문이었다(Alistair Cockburn, 2001). 그 당시 개발팀의 임무는 새로운 서비스를 추가하는 것이었고 기존의 요구사항 문서를 건드릴 수 없는 상황에서 원래의 요구사항은 한 줄도 건드리지 않은 채 새로운 요구사항을 추가했다. Alistair Cockburn은 확장이 필요할 경우 다이어그램 상에서 보여주지 말고 기존 유스케이스 안에서 확장 유스케이스의 참조를 그저 텍스트로 서술할 것을 권유한다. 다이어그램에 복잡하게 확장을 표현함으로써 정작 중요한 유스케이스를 볼 수 없게 만들기 때문이다. 필자도 그의 주장에 동의 한다.

케이스 툴로 유스케이스 작성하기
케이스 툴로 유스케이스를 작성한다는 것이 잘못되었다는 것은 아니다. 오히려 케이스 툴이 활용상의 문제로 인해 정확한 유스케이스를 작성하는데 걸림돌로 작용하는 현상을 이야기하고 싶은 것이다. 필자는 지금까지 프로젝트를 진행하면서 유스케이스의 고단함에 대해 무척 많은 고민을 했었다. 일반적으로 개발자들은 작문을 싫어하는 습성이 있지만 요구사항을 파악하고 분석하는 사람들은 일반 고객과의 의사소통이 많기 때문에 산문 형식의 문서 작성(Technical Writing)에 능숙해야 함에도 불구하고 대부분의 프로젝트에서는 이러한 사실을 애써 외면한다.

작성하기 귀찮은 유스케이스를 케이스 툴로 그리고 끝내 버리려는 생각을 한다. 사실 필자도 예외는 아니어서 웬만하면 케이스 툴에서 모든 걸 해결하고 싶은 유혹에 항상 시달렸다. 하지만 대부분의 케이스 툴은 유스케이스를 서술하기 위한 조그만 다이얼로그 창만을 제공한다. 유스케이스를 기술한 문서를 따로 하이퍼링크 방식으로 케이스 툴과 연결하는 방법을 주로 취했는데 사실 힘들고 불편하다. 만약 어떤 케이스 툴의 기능 중에 일정한 유스케이스 작성 형식의 레이아웃을 디자인할 수 있고 그 템플릿 안에서 유스케이스를 작성하면 다이어그램이 역으로 만들어지는 케이스 툴이 있다면 얼마나 좋을까 하는 생각을 해본다. 언제쯤이면 편하게 유스케이스 모델링을 할 수 있는 케이스 툴이 나올까?

분석/설계 모델과 MDA
분석(分析)은 그 단어의 의미에서도 알 수 있듯이 무엇인가를 어떠한 목적 때문에 나누어(分) 쪼개는(析)것이다. 소프트웨어 개발에서 나누어 쪼개야 할 필요가 있는 부분은 문제 영역으로 시스템을 개발하는 사람들이 문제 영역을 올바로 이해하기 위한 목적으로 분석을 한다. 이해한 것을 즉시 시스템으로 만들 수 없으므로 중간에 문제 영역의 이해와 실 시스템간의 가교역할을 하는 것이 있는데 그것이 바로 설계이다.

사실 웬만하면 설계를 하지 않고 시스템을 만들고 싶은데 구현 언어나 플랫폼 등의 영향을 많이 받기 때문에 분석한 결과를 바로 시스템화하기 힘든 것이다. 현재 분석에서 설계를 거치지 않고 바로 실 시스템으로 건너가기 위한 노력이 진행되고 있는데 독자 여러분들도 잘 알다시피 MDA(Model Driven Architecture)가 그것이다.

현재 소프트웨어를 만드는 사람들의 작업 행태는 당연하게 생각되지만 지루하게도 4단계를 거친다. 문제 영역을 파악하고 분석하여 그 결과를 바탕으로 설계한 후 소프트웨어를 구현한다. 만약 정말 MDA가 활성화 된다면 중간에 1가지 단계가 빠지는 3단계 개발 공정이 나올 것이며 그저 요구사항 파악해서 분석 모델을 만들어 놓으면 실행 시스템이 튀어나오는 정말 환상적인 세상이 오지 않을까 싶다. 그런데 정말 이런 세상이 가능할까?

현 컴퓨팅 시스템은 다 계층 분산 시스템인 관계로 많은 설계 요소와 다양한 관련 기술이 필요하다. 이 모든 기술을 모두 알 수 없으므로 특정 기술 기반을 가진 사람들이 한 프로젝트에 모여 개발을 하고 그 중 충분한 경험과 깊은 지식을 갖고 있는 누군가가 아키텍트라는 역할로 소프트웨어 개발을 이끌고 나간다.

아키텍트라는 일반 비즈니스 로직 개발자들의 개발 자유도를 통제하기 위해 '메커니즘'이 라는 것도 만들어 놓고, 마음이 안 놓이는지 '프레임워크'라는 것도 만들어 놓아 오직 비즈니스 로직 구현에 몰두할 수 있게 만들어 놓는다. 비즈니스 로직 개발하는 것도 '패턴'이라는 이름으로 개발하는 방식을 제한하는 경우가 많다. MDA에서는 이러한 부분을 프로파일로 작성한다. 이 프로파일을 바탕으로 비즈니스 분석 모델을 빌드하면 실행 컴포넌트가 나오게 된다.

현재도 제품으로 출시되어 있는 MDA 솔루션들이 있다. 필자가 보기엔 어떤 솔루션은 무늬만 MDA인 것도 있고 실제 MDA의 비전에 상당히 근접한 솔루션도 있었는데 모델링 도구에서 모델을 만들고 OCL을 작성해 놓으면 놀랍게도 실제로 시스템이 작동했었다. 다만 일부 표준을 지원하지 않아 문제가 되긴 했지만 말이다.

하지만 언젠가는 MDA가 우리 피부에 와 닿을 정도로 현실화 되리라 믿는다. 초창기 컴퓨팅 환경에서 고급 언어로 일컫는 3세대 언어들은 그 당시 개발자들에겐 환상적인 꿈의 개념이었다고 한다. 기계어와 어셈블리 수준에서 프로그래밍을 하던 그들에게는 자연어와 비슷한 모양의 개발 언어는 얼마나 환상적으로 보였을까? 지금 우리는 이와 같은 과도기를 겪고 있다고 필자는 생각한다. 독자 여러분들도 그런 시대를 대비하여 하루빨리 모델링 능력을 한껏 끌어올리기 바란다.

자바 웹 애플리케이션 모델링
만약 개발 프로세스가 비슷하다면 그 프로세스를 적용하여 개발하는 모든 애플리케이션 모델링의 방법은 거의 유사하다. 웹 애플리케이션이라고 해서 별다르게 특이한 것은 없겠지만 웹 애플리케이션의 특성상 몇 가지 짚고 넘어갈 부분이 있다. 사실 엄밀하게 말 하면 웹이라는 특성도 있지만 자바의 특성으로 인해 모델링 방법이 약간 달라지는 수준으로 볼 수 있다. 특히 현 엔터프라이즈 컴퓨팅 환경은 다 계층 분산 시스템이므로 계층과 분산에 관한 부분의 모델링이 강조된다.

또한 사용자 웹 인터페이스 모델링도 중요한 부분으로 생각할 수 있다. 웹 페이지의 특성상 HTML이라는 제약으로 인해 약간 특이한 점이 있다. 웹 페이지 모델링에서 폼(form)과 같은 구성요소는 스테레오 타입(stereo type)으로 정의하여 모델링을 한다. 또 웹 페이지는 화면의 흐름에서 해당 정보를 갖고 다니거나 세션을 참조하기 때문에 어디까지 해당 정보를 갖고 다닐지 세션을 어떻게 참조할 지가 모델링의 주요 포커스가 된다. 일단 개요 수준은 여기까지 하고 실제 예제를 보면서 모델링이 어떻게 진행되는지 살펴보자.

분석 모델
좀 전에도 언급했듯이 웹 애플리케이션도 역시 다른 시스템과 마찬가지로 요구사항 파악 및 분석으로부터 시작한다. 요구사항을 위한 모델링으로 유스케이스를 이용하는 것은 이미 앞에서 이야기했고 특집 4부에서 자세히 다룰 예정이므로 유스케이스 자체에 관한 설명은 하지 않겠다. 또한 웹 애플리케이션이나 다른 기술을 이용한 애플리케이션이나 요구사항과 분석 모델링의 기법은 사실 별반 다르지 않다. 분석 모델은 구현과 관계가 없는데 분산(distribution), 영속성(persistency), 커뮤니케이션(communication), 인증(authentication)과 같은 메커니즘과 상관없는 개념적인 모델이기 때문이다. 즉, 유스케이스 모델을 기반으로 분석관점으로 유스케이스 실현(realization)을 표현한 객체모델이 바로 분석 모델이다.

<그림 8> 분석 클래스 다이어그램의 예

<그림 8>은 분석 모델의 유스케이스 실현(realization)에 참여하는 분석클래스 다이어그램이다. RUP에 의하면 분석클래스는 <<boundary>>, <<control>>, <<entity>> 등 세 가지 종류의 스테레오 타입을 갖는다. 원래 스테레오 타입은 태기드 벨류(tagged value)와 함께 UML의 확장 메커니즘의 한 종류이기 때문에 필요하다면 추가적인 스테레오타입을 정하여 사용하여도 무방하다.

사용자 웹 인터페이스 모델링
사용자 웹 인터페이스 모델링 부분은 웹 애플리케이션을 개발하면서 모델링 하기에 가장 껄끄러운 부분이 아닌가 생각된다. Jim Conallen은 그의 저서 『Modeling Web Application Architectures with UML(2002)』에서 웹 인터페이스 모델링을 자세히 설명하고 있다. 한마디로 요약하자면 웹 애플리케이션 모델링을 위한 다양한 '스테레오 타입'을 만들어 냈다.

필자가 보기에는 실무 프로젝트에서 과연 이런 노가다 작업을 할 필요가 있을까 생각이 되지만 순수 모델링 관점에서 볼 때 웹 페이지를 모델링 하기 위한 작업으로서 의미는 있다고 본다. <그림 9>는 그가 제안한 웹 페이지를 모델링 예제이다. 참고하기 바란다.

<그림 9> Jim Conallen이 제안한 웹 인터페이스 모델링 예

실무 프로젝트에서 Jim Conallen이 제안한 방법으로 모델링 하는 경우를 본적은 없고 필요한 부분만을 발췌해 많이 단순화시킨 형태로 모델링을 진행한다. 사용자 인터페이스 모델은 화면 흐름, 참여 화면의 설명, 화면 이동 맵, 화면 클래스 다이어그램, UI 프로토타입 등으로 구성된다. 이중 UML로 모델링 하는 몇 가지를 소개하겠다.

화면 흐름
화면 흐름의 경우 비즈니스 로직 처리와는 전혀 관계없이 오직 해당 작업을 수행하기 위한 화면 관점에서 모델링을 한다.

<그림 10> 시퀀스 다이어그램으로 표현한 화면 흐름도

화면 이동 맵
화면 이동 맵은 각 화면의 구성 요소를 클래스 다이어그램으로 표현한 것이다.

<그림 11> 클래스 다이어그램으로 표현한 화면 이동 맵

웹 애플리케이션을 위한 프레임워크 모델링
자바를 이용하여 웹 애플리케이션을 개발하게 되면 우선 여러 제약 사항들이 나타나게 된다. 앞서 이야기한 페이지 제어 문제, 세션 처리 문제, 데이터 처리 문제, 다양한 프로토콜의 처리 문제, 까다로운 보안 문제, 웹과 분산 컴포넌트간의 동적 호출 문제, 예외처리 문제, 웹 애플리케이션과 레거시(legacy)와 인터페이스 문제 등 각 메커니즘에 관해 다루어야 할 부분이 많다. <그림 12>는 각 메커니즘을 고려한 일반적인 웹 애플리케이션의 시스템 구성도이다.

<그림 12> 일반적인 웹 애플리케이션의 시스템 구성도

이 그림에서 메커니즘으로 정리해야 할 주요 부분은 여러 가지가 있지만 그 중에서 Web Server와 Web App Server와의 연동 부분으로 JSP에서 어떠한 Action을 통해 EJB 비즈니스 컴포넌트를 수행시키고자 하는 부분과 데이터 처리하는 부분, 타 시스템과 인터페이스 기술을 만약 웹 서비스로 할 경우 웹 서비스 클라이언트와 웹 서비스 서버간의 메커니즘 등을 들 수 있다. 몇 부분에 대한 메커니즘에 대하여 모델을 보면서 살펴보자.

JSP에서 Action을 통해 비즈니스 컴포넌트를 수행하는 부분
이 부분의 경우 프리젠테이션 레이어와 비즈니스 레이어간의 느슨한 결합을 지원하기 위해 J2EE 패턴 중에 '비즈니스 델리게이트 패턴(business delegate pattern)'을 사용할 수 있다. 원래 비즈니스 델리게이트 패턴은 비즈니스 서비스 컴포넌트와 일어나는 복잡한 원격 통신에 관련된 사항을 클라이언트로부터 숨기기 위해 사용한다. 비즈니스 델리게이트에 관해 자세한 것을 알고 싶으면 자바 웹 사이트나 코어 J2EE 패턴을 참고하면 된다.

다음에 소개할 프로젝트의 상황 때문에 약간 변경이 되었는데 비즈니스 델리게이트는 원격 통신에 관한 처리와 함께 해당 프로젝트의 사정으로 EJB 비즈니스 컴포넌트와 일반 자바 클래스 형태의 비즈니스 컴포넌트, 그리고 웹 서비스를 통해 타 시스템의 비즈니스 컴포넌트도 함께 수행해야 했다.

따라서 비즈니스 컴포넌트가 EJB건 일반 자바 클래스건 SOAP 기반의 통신이던 간에 클라이언트는 그저 비즈니스 컴포넌트 아이디와 비즈니스 컴포넌트 쪽에 넘겨줘야 할 파라미터만 알고 있으면 되도록 설계하였다. 해당 비즈니스 컴포넌트는 비즈니스 델리게이트에서 리플렉션을 이용해 동적으로 수행되도록 했다.

<그림 13> 비즈니스 델리게이트 부분의 클래스 다이어그램

<그림 13>에서 BusinessMapper의 역할은 XML 형태의 컨피규레이션(Configuration)로 빠져있는 파일을 읽어 웹 애플리케이션이 기동할 때 캐싱하게 된다. 이 컨피규레이션 파일에 담겨있는 정보를 바탕으로 BusinessMap 클래스가 모델의 역할을 하게 되는데 BusinessMapper는 BusinessMap을 자신이 갖고 있는 Map에 담아 갖고 있는다. 또한 BusinessMapper는 JVM상에 오직 하나만 존재해야 했으므로 '싱글턴 패턴(singleton pattern)'을 적용하였다. 이것을 실제로 구현하기 위해서 필자는 자카르타의 커먼스(commons)의 다이제스터(digester)를 이용하여 구현했다.

<그림 14> 비즈니스 델리게이트 부분의 시퀀스 다이어그램

외부 시스템과 연동 부분
웹 서비스의 SOAP을 이용한 통신을 하기 위해서는 웹 서비스 RMI 리모트 인터페이스를 상속한 인터페이스를 상속한 스텁(stub)을 통해 비즈니스 컴포넌트를 수행하게 된다. 이 부분은 위에서 설명한 비즈니스 델리게이트 부분과 아주 관련이 깊은데 일반 자바 비즈니스 컴포넌트의 경우 같은 머신의 동일 컨텍스트에 존재하므로 스트럿츠 액션(Struts Action)에서 비즈니스 델리게이트를 통해 바로 부르면 비즈니스 델리게이트는 그저 리플렉션을 통해 해당 비즈니스 컴포넌트를 실행하면 된다.

하지만 웹 서비스 비즈니스 컴포넌트인 경우는 다른 머신에 존재하는 컴포넌트이므로 원격 호출에 대한 부분 동일한 방법으로 서비스의 위치를 찾아낼 수 있도록 J2EE 패턴 중에 '서비스 로케이터 패턴(service locator pattern)'을 이용하였다. 이 부분 역시 약간의 변형이 가해졌는데 다이내믹 바인딩 시간이 비교적 길기 때문에 서버에서 받아온 스텁을 웹 애플리케이션 서버 쪽에 풀(pool)을 하나 만들어 스텁을 한번 가져온 이후 풀에 넣어놓고 다음 호출부터는 풀에서 꺼내어 쓰도록 설계하였다. 필자는 이런 설계를 구현하기 위해 자카르타 커먼스에 있는 풀(pool)을 이용하였다. EJB의 경우도 이와 거의 유사하다. <그림 15, 16>은 웹 서비스 부분의 클래스 다이어그램과 시퀀스 다이어그램이다.

<그림 15> 웹 서비스 부분의 클래스 다이어그램

<그림 16> 웹 서비스 부분의 시퀀스 다이어그램

데이터 처리 부분
필자가 이 부분을 설계하고 구현해 놓고 보니 사실 메커니즘이라기보다는 일종의 유틸리티성의 헬퍼 클래스 성격이 짙었다. 주요 특징 중에 하나는 SQL 쿼리를 소스코드에 직접 넣지 않고 XML 형식의 파일로 뽑아내었다는 것이다. 데이터베이스를 통해 데이터를 주고받아야 하는 비즈니스 컴포넌트 입장에서는 쿼리 아이디와 Object 배열에 담긴 파라미터를 QueryHelper에 넘겨주면 쿼리를 실행하고 결과를 넘겨준다.

결과는 여러 형태로 받을 수 있는데 종류로는 Map 형태, Map의 리스트 형태, 특정 Bean 형태, Bean의 List 형태로 Object 객체에 담아 받아 올 수 있다. 필자는 이런 설계를 구현하기 위해 자카르타 커먼스에 있는 디비유틸(DBUtil)을 이용하였다. <그림 17, 18>은 데이터 처리 부분의 클래스 다이어그램과 시퀀스 다이어그램이다.

<그림 17> 데이터 처리 부분의 클래스 다이어그램

<그림 18> 데이터 처리 부분의 시퀀스 다이어그램

컴포넌트 모델링
컴포넌트 모델링의 과정은 컴포넌트를 식별하고 그것의 인터페이스를 정의하고 컴포넌트의 내부는 어떻게 작동하며 구현을 어떻게 할 건지에 대한 종합적인 과정이 필요하다. CBD(Component Based Development)에는 많은 이견들이 있지만 이 글에서는 현존하는 기법을 통해 컴포넌트 식별 방법을 아주 간략하게 설명하고 컴포넌트 모델링과정이 어떻게 진행되는지 궁금해 하는 독자들을 위해 각종 다이어그램을 보면서 살펴보기로 한다.

Catalysis : 타입과 조인트 액션을 중심으로 상호작용을 분석하여 네트워크 다이어그램을 만들고, 결합력과 응집력을 고려한 메트릭을 만들어 컴포넌트 식별한다.

마르미III : 유스케이스/클래스 연관 메트릭을 사용하여 각 유스케이스와 관련 있는 클래스를 클러스터링 하여 컴포넌트 후보로 등록한 다음 다른 유스케이스도 분석하여 이전에 후보로 등록되었던 컴포넌트가 쓰이면 나머지 유스케이스도 활용할 수 있도록 인터페이스를 공개한다. 유스케이스/클래스 연관 메트릭을 분석하기가 좀 어려운 것 같다. 실무에서 활용 가능할지 의문시 된다.

UML Components : 비즈니스 타입 모델에서 타입을 식별하고 이 타입을 관리할 책임이 있는 인터페이스를 정의한다. 식별된 인터페이스의 상호작용을 분석함으로써 서로간의 의존관계를 파악/정제하여 컴포넌트를 제안한다. 분석가의 경험에 많이 의존하는 경향이 있다.

RUP : 분석/설계가 상당히 진행된 후 각 클래스를 종횡으로 묶는 방식을 취한다. 초기 컴포넌트 식별은 불가능 하다.

컴포넌트 인터페이스 정의
어찌됐던 간에 컴포넌트를 식별했으면 인터페이스를 정의한다. 이 단계에서 정의되는 컴포넌트 인터페이스는 Business Facade의 성격으로 각 인터페이스를 명세화 한다.

<그림 19> 컴포넌트 인터페이스

컴포넌트 내부 설계
컴포넌트 내부 설계는 앞에서 정의한 인터페이스를 구현하기 위한 설계를 일컫는다.

<그림 20> 컴포넌트 내부 설계 클래스 다이어그램

EJB 컴포넌트 설계
EJB 컴포넌트 자체에 관한 설계로 <그림 21>은 Customer 비즈니스 컴포넌트의 워크플로우를 관리하고 클라이언트 요청에 대해 Facade 역할을 수행하는 Stateless Session Bean 에 대한 클래스 다이어그램이다.

<그림 21> EJB 컴포넌트 설계 클래스 다이어그램

유능한 모델러로 거듭나기
지금까지 개발 프로세스 측면에서 바라본 모델링과 자바 웹 애플리케이션을 위한 모델을 보았다. 사실 아주 많은 내용을 한정된 페이지에 집어넣으려고 하다 보니 주마간산 격이 되어버렸는지도 모르겠다. 모델링이라는 것이 개인적인 주관과 경험에 많은 영향을 받는 것은 사실이다.

필자는 지금까지 모델링과 관련된 프로젝트와 교육을 여러 번 해보았지만 아직까지 잘된 모델링은 바로 이런 것이라고 꼭 집어 이야기하기 힘들다. 다만 어떤 모델은 보면 왠지 푸근하고 어떤 모델은 왠지 어색하다는 느낌이 든다. 그 느낌이 뭐라는 것도 아직 정확히 모르겠지만 말이다. 모델링을 잘 하기 위해 어떻게 트레이닝을 해야 할까라고 고민하던 중에 필자가 겪었던 짧은 경험이 떠올랐다.

올해 여름에 비슷한 일을 하는 사람들과 함께 어느 업체에 방문했던 적이 있었다. 날씨는 아주 더웠지만 차 안은 에어컨을 켜서 시원했다. 문제는 주차장에 차를 세워놓고 몇 시간 동안 뜨거운 여름 뙤약볕 아래 차를 세워놔야 하므로 일을 마치고 돌아올 시점에는 차 안이 불가마 찜질방 수준으로 될 거라는 것은 충분히 예상할 수 있는 상황이었다. 그때 누군가 “그늘 아래 세워놔야 할 텐데”라고 하자 어떤 사람이 “그늘 오리엔티드(Oriented)구먼”이라고 응수했다. 그러자 또 누군가 “오리엔티드는 아니지, 오리엔티드는 왠지 그늘 자체가 되어야 하는 듯한 느낌을 준다고, 그늘 드리븐(Driven)이 맞지 않을까? 드리븐은 목표를 향해 돌진하는 느낌을 주자나? 결국 우리는 지금 그늘을 찾아가고 있는 거고”라고 했다. 이때 필자는 “음~ 정확히 말하자면 그늘 베이스드(Based)가 어울릴 것 같은데? 우리는 그늘을 베이스로 깔고 앉아야 하잖아?”라고 주장하여 모두들 웃으며 짧은 말장난을 했던 기억이 있다.

예로 든 이야기에서 그 상황에 오리엔티드(Oriented)나 드리븐(Driven), 베이스드(Based)라는 용어의 의미에 정확하게 맞았는지는 일단 논외로 하겠지만 실제 모델링을 하는 상황에서는 이와 같은 일들이 자주 일어난다. 모델링을 하는 사람들은 그 상황에 맞는 의미를 정확히 파악하여 모델을 만들기 위한 노력을 끊임없이 해야 하고 특히 용어, 단어의 의미, 그리고 단어간의 관계에 대하여 고민해보고 정확하게 짚고 넘어가는 버릇을 들여야 하지 않을까 싶다.

우리가 지금 주제로 이야기하는 UML을 이용한 모델링이라는 것이 결국 모호성이 아주 높은 자연 언어를 몇 개의 모델링 요소로 표현하는 작업이기 때문에 소프트웨어 모델링을 할 때만 모델에 대하여 생각하는 것이 아니라 실생활 속에서 패러다임에 맞게 사고하는 지속적인 훈련을 해야만 한다.

앞에서 MDA에 관하여 짧게 언급 했지만 아마 앞으로 소프트웨어 개발에 있어서 모델은 점점 더 중요한 요소로 부각될 것이다. 필자도 그렇고 이 글을 읽는 독자 여러분도 지속적인 수련을 통해 더욱 유능한 모델러로 거듭나리라 믿는다.@

* 이 기사는 ZDNet Korea의 제휴매체인 마이크로소프트웨어에 게재된 내용입니다.

PracticalGuideDataGrids

26 members have rated this article. Result:
Popularity: 6.35. Rating: 4.49 out of 5.

Copyright

The contents of this document can be freely copied and disseminated with the condition that the document retains reference to BioXing, the author and its Copyright notice. In addition, the document or any part of the document cannot be incorporated into commercial products without prior permission of BioXing and its author. BioXing retains all rights to commercial use of this document.

Table of Contents

1 Introduction

The purpose of this document is to provide a practical guide to using Microsoft’s .NET DataTables, DataSets and DataGrid. Most articles illustrate how to use the DataGrid when directly bound to tables within a database and even though this is an excellent way to use the DataGrid, it is also able to display and manage programmatically created and linked tables and datasets composed of these tables without being bound to a database. Microsoft’s implementation has provided a rich syntax for populating and accessing rows and their cells within tables, for managing collections of tables, columns, rows and table styles and for managing inserts, updates, deletes and events. Microsoft’s Visual Studio .NET development environment provides detailed explanations and code examples for the classes, which is excellent for obtaining a quick reference to a method or property, but not for understanding how they all fit together and are used in applications.

This article will present different ways to create and manage bound and unbound tables and datasets and to bind them to DataGrids for use by WebForms and WinForms. The different behaviors of the DataGrid depending upon whether it is in a WebForm or a WinForm will be presented. In addition, copying DataGrid content to the clipboard, importing and exporting in XML and printing will be presented. Techniques for linking DataGrid content to features within graphics objects to provide an interactive UI will be discussed in the last section.

The intent of this article is not to be a complete reference for all methods and members of the classes used for building Tables, Datasets and DataGrids, but to illustrate systematically how to build and manage them using their essential methods and properties. The article will also show equivalent ways for working with these entities to illustrate programming flexibility options. Once they are built the tables can be added to a database and/or the content easily extracted for updating existing database tables.

The structure and features of a table created using the DataTable class is at the heart of using the DataGrid; therefore, it will be presented first. Next the DataSet that manages collections of independent and linked tables will be presented followed by the DataGrid that displays and provides an interactive UI for its Tables and Datasets. All example code will be written in C# and the periodic table with its elements and isotopes will be used as a model and source of data.

2 Overview

Figure 1 is a summary view of the essential relationships between the DataGrid, DataGridTableStyles, DataSets and DataTables and their various methods and properties. This article will delve into the details of each of these components describing how to create them, to fill them with data and how they work together and thereby provide a clearer understanding of this relationship diagram.

Figure 1 DataGrid, DataSet and DataTable relationship diagram

Next...

Tables.

About Pete2004


Ph.D. in BioPhysics and over 20 years of experience in managing, architecting and hands-on developing software systems for biotechnology companies that produced cutting edge instrumentation and data systems. These include DNA and Peptide Synthesizers, cDNA, oligo and Protein microarrays and mass spectrometers used for protein sequencing.

In 2001 founded BioXing (pronounced Bio-Crossing) which has architected and developed an extensible relational database repository and software system that includes Web Services, Client Workstation and Web Based applications. The system is used to track, manage, integrate and data mine disparate laboratory data, protocols and experiments and link to reference proteomic and genomic data.

BioXing also does consulting and development for biotechnology companies.

Click here to view Pete2004's online profile.


2005년 3월 29일 화요일

[펌] Using Web Services for Remoting over the Internet. By Roman Kiss

Introduction

This article describes a design and implementation (C#) of the Remoting over Internet using the Web Service as a gateway into the Remoting infrastructure. The Web Service Gateway (Custom Remoting Channel) allows to enhance the remoting channel over Internet and its chaining with another heterogeneous channel. Consuming a remote object over Internet is full transparently and it doesn't require any special implementation from the remoting via intranet. The Web Service Gateway enables to create a logical model of the connectivity between the different platforms and  languages. Before than we will go to its implementation details, let's start it with usage and configuration issue. For some demonstration purpose I will use a MSMQ Custom Remoting Channel (MSMQChannelLib.dll), which I described in my previously article [1]. I am assuming that you have a knowledge of the .Net Remoting and Web Service.

Usage

Consuming a remote object over Internet using the Web Service Gateway is very straightforward and it actually requires only to install the following assemblies:

  • WebServiceChannelLib , this is a Custom Remoting Channel on the client side to forward a remoting message to the Web Service Gateway over Internet (outgoing message).
  • WebServiceListener, this is a Web Service (gateway) to listen an incoming message from the client side and forward it to the local remoting infrastructure (incoming message).

Note that the above assemblies have to be installed (into the GAC) both on the server and client sides when a remote callback is used. 

The next step is to configure a server and client host sides. Their configuration are depended from the actually application. Let me assume, we want to call a remote object driven by MSMQ custom channel over internet. Their config files might look like the following snippets:

server.exe.config

 <configuration> <system.runtime.remoting> <application > <service> <wellknown mode="Singleton" type="MyRemoteObject.RemoteObject, MyRemoteObject" objectUri="endpoint" /> </service> <channels> <channel type="RKiss.MSMQChannelLib.MSMQReceiver, MSMQChannelLib" listener=".\ReqChannel"/> <channel type="System.Runtime.Remoting.Channels.Tcp.TcpChannel, System.Runtime.Remoting" port="8090" /> </channels> </application> </system.runtime.remoting> </configuration>

The above server config file will register two channels to listen an incoming message for the remote well known singleton object.

client.exe.config

This is an example of the client config file to register our Custom Remoting Channel.

<configuration> <system.runtime.remoting> <application> <client > <wellknown type="MyRemoteObject.RemoteObject, RemoteObject" url="ws://localhost/WebServiceListener/Listener.asmx; tcp://localhost:8090/endpoint/RemoteObject" /> </client> <channels> <channel type="RKiss.WebServiceChannelLib.Sender, WebServiceChannelLib" mode="soap"/> </channels> </application> </system.runtime.remoting> </configuration>

The ws is a Custom Remoting Client channel to dispatch an IMessage over internet using a binary respectively soap mode formatter. Note that the mode is a CustomChannelProperty and its default value is binary.

web.config

This is a Web Service config file. The following snippet is its part. The Web Service gateway is also a local remoting client, therefore a client (sender) channel is requested to be registered. The following snippet shows a configuration of the two channels - Tcp and MSMQ.

<system.runtime.remoting>  <application >   <channels>    <channel type="System.Runtime.Remoting.Channels.Tcp.TcpChannel, System.Runtime.Remoting"/>    <channel type="RKiss.MSMQChannelLib.MSMQSender, MSMQChannelLib"                  respond=".\RspChannel" admin=".\AdminChannel" timeout="30" priority="10"/>   </channels>  </application> </system.runtime.remoting>

Activating a remote object

The well known remote object (WKO) is activated by its consumer using the GetObject method mechanism. The proxy is created based on the remote object metadata assembly installed in the GAC (see an argument objectType).  The remoting channel is selected by the objectUrl argument. The url address in this solution has two parts :

  •  connectivity to the Web Service gateway over internet
  •  connectivity to the Remote object over intranet within the Web Service gateway

 Between the primary and secondary addresses is a semicolon delimiter as it is shown the below:

string objectUrl = @"ws://localhost/WebServiceListener/Listener.asmx; msmq://./reqchannel/endpoint"; 

Using this objectUrl design pattern allows an easy selection of the web service gateways on the Internet. Note that the ws custom remoting channel will trim this primary address and forward only its secondary part. In this solution, the objectUrl represents a physical path of the logical connectivity between the consumer and remote object regardless of how many channels are needed. In this example, the Web Service gateway resides on the localhost and it should be replaced by the real machine name.

Finally, the following code snippet shows an activation of the remote object:

 // activate a remote object Type objectType = typeof(MyRemoteObject.RemoteObject); string objectUrl = @"ws://localhost/WebServiceListener/Listener.asmx; msmq://./reqchannel/endpoint"; RemoteObject ro = (RemoteObject)Activator.GetObject(objectType, objectUrl);

Note that a metadata (assembly) of the remote object must be installed into the GAC in the places such as client, Web Service gateway and server host.

That's all for the client/remoteObject plumbing issue over the Internet.

The following pictures shows this connectivity:

 

   

Now, to understand how the message flows between the heterogeneous channels over the Internet, have a look at the following paragraphs:

Concept and Design

Concept of the Remoting over Internet is based on dispatching a remoting message over Internet using the Web Service features as a transport layer. The following picture shows this solution:

Client activates a remote WKO to obtain its transparent proxy. During this process the custom remoting channel (ws) is initiated and inserted into the client channel sink chain. Invoking a method on this proxy, the IMessage is created which it represents a runtime image of the method call at the client side. This IMessage is passed into the channel sink. In our solution to the custom client (sender) channel ws. This channel sink has a responsibility to convert IMessage to the SoapMessage in the text/xml format pattern and send it over Internet to the Web Service gateway. The following picture shows this:

The Web Service gateway has two simply WebMethods, one for the SoapMessage format and the other one for a binary  format encoded by base64 into the text string. The first one method enables to use a call from an unknown client, as opposite in the binary formatting message for .Net client.

Lets continue with the IMessage/SoapMessage flows on the Web Service gateway side as it is shown on the above picture. The text/xml formatted SoapMessage sent over Internet might look like the following snippet:

Text/XML formatted SoapMessage Request.

<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:a3="http://schemas.microsoft.com/clr/nsassem/MyRemoteObject/MyRemoteObject, Version=1.0.772.24659, Culture=neutral, PublicKeyToken=ec0dd5142ae7a19b" xmlns:a1="http://schemas.microsoft.com/clr/ns/System.Runtime.Remoting.Messaging"> <SOAP-ENV:Body> <System.Runtime.Remoting.Messaging.MethodCall id="ref-1"> <__Uri id="ref-2" xsi:type="SOAP-ENC:string">msmq://./reqchannel/endpoint</__Uri> <__MethodName id="ref-3" xsi:type="SOAP-ENC:string">get_Id</__MethodName> <__TypeName id="ref-4" xsi:type="SOAP-ENC:string">MyRemoteObject.RemoteObject, MyRemoteObject, Version=1.0.772.24659, Culture=neutral, PublicKeyToken=ec0dd5142ae7a19b</__TypeName> <__Args href="#ref-5"/> <__CallContext href="#ref-6"/> </System.Runtime.Remoting.Messaging.MethodCall> <SOAP-ENC:Array id="ref-5" SOAP-ENC:arrayType="xsd:ur-type[0]"> </SOAP-ENC:Array> <a1:LogicalCallContext id="ref-6"> <User href="#ref-8"/> </a1:LogicalCallContext> <a3:User id="ref-8"> <FirstName id="ref-9" xsi:type="SOAP-ENC:string">Roman</FirstName> <LastName id="ref-10" xsi:type="SOAP-ENC:string">Kiss</LastName> </a3:User> </SOAP-ENV:Body> </SOAP-ENV:Envelope> 

This string request has to be de-serialized back to the SoapMessage object, which is a clone object of the sender's origin. After that, we have an enough information to perform a Method call on the remote object. Conversion of the SoapMessage to IMessage needs to use some trick, therefore there is no class in .Net namespaces to do it. The trick is based on creating a RealProxy wrapper using the remote object type and its endpoint url address and overriding an Invoke method of  the base RealProxy class. Using the Reflection (late binding) to invoke the remote method, the RealProxy wrapper will catch the IMessage before its processing in the channel sink. Now, the Invoke method can perform updating the IMessage by original images such as LocicalCallContext and url address. After that, the IMessage is the same like on the client side over Internet. Now it's easy to forward this IMessage to the Message Sink calling its SyncProcessMessage method. The rest is done by a remoting paradigm. 

The SyncProcessMessage returns an IMessage which it represents a ReturnMessage from the remote method. Now the process is going to reverse into the text/xml format of the SoapMessage response . I will skip it this process for its simplicity and I will continue on the client custom channel (ws) where a response message has been returned. Before that, have a look the text/xml formatted SoapMessage response how it has been sent back to the custom channel over Internet:

Text/XML formatted SoapMessage Response.

<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:a3="http://schemas.microsoft.com/clr/nsassem/MyRemoteObject/MyRemoteObject, Version=1.0.772.24659, Culture=neutral, PublicKeyToken=ec0dd5142ae7a19b" xmlns:a1="http://schemas.microsoft.com/clr/ns/System.Runtime.Remoting.Messaging"> <SOAP-ENV:Body> <a1:MethodResponse id="ref-1"> <__Uri xsi:type="xsd:ur-type" xsi:null="1"/> <__MethodName xsi:type="xsd:ur-type" xsi:null="1"/> <__MethodSignature xsi:type="xsd:ur-type" xsi:null="1"/> <__TypeName xsi:type="xsd:ur-type" xsi:null="1"/> <__Return xsi:type="xsd:int">0</__Return> <__OutArgs href="#ref-2"/> <__CallContext href="#ref-3"/> </a1:MethodResponse> <SOAP-ENC:Array id="ref-2" SOAP-ENC:arrayType="xsd:ur-type[0]"> </SOAP-ENC:Array> <a1:LogicalCallContext id="ref-3"> <User href="#ref-5"/> </a1:LogicalCallContext> <a3:User id="ref-5"> <FirstName id="ref-6" xsi:type="SOAP-ENC:string">Roman</FirstName> <LastName id="ref-7" xsi:type="SOAP-ENC:string">Kiss</LastName> </a3:User> </SOAP-ENV:Body> </SOAP-ENV:Envelope> 

The result of the remoting call has to be de-serialized into the SoapMessage and then an IMessage can be generated by the function ReturnMessage. This IMessage is returned back to the remoting client infrastructure. In this point, the process of the remoting over Internet is done and the rest is a regular remoting mechanism. 

As I mentioned earlier, the Web Service gateway has two methods, one has been described the above using the SoapFormatter and the other one is using a BinaryFormatter.

Using the Binary Formatted Message.

The .Net clients can use a remoting over Internet in more efficient (faster) way using the BinaryFormatter. The design implementation is more straightforward than using the SoapFormatter. The IMessage is serialize/deserialize by the BinaryFormatter to/from memory stream. Than this stream image is encoded/decoded using the Base64 conversion class to/from text string. Note that this text string is not readable. The IMessage is plugged-in into the server remoting infrastructure using the RemotingServices.Connect and RemotingServices.ExecuteMessage functions from the Remoting namespace. Thanks for these functions, they really saved my time.

Limitation note.

The limitation of the above solution is done by the Web Service functionality. You cannot handle a distributed transaction over Internet. In this case, the Web Service gateway represents a non-transactional client and remote object is a root of the transaction.

Implementation

The implementation is divided into two assemblies - Custom Client Channel and Web Service gateway. Their implementation is straightforward without using any third party library support. I am going to concentrate only for these parts related to the IMessage processing. More details about the design and implementation of the Custom Remoting Channel can be found it in [1].

WebServiceChannelLib

This is a Custom Client Channel assembly to process an outgoing remoting message over Internet. The Client Message Sink has an implementation both message processing such as SyncProcessMessage and AsyncProcessMessage. Based on the m_mode value, the IMessage can be formatted by the SoapFormatter or BinaryFormatter.

The SyncProcessMessage function initiates the Web Service client proxy generated by the VS. There is a small modification in its constructor. This proxy is a generic for any location of the Web service, that's why the url address is pass through its constructor instead of its hard coding. Note that any Exception will be converted into the IMessage format and send back to the client.

Sender:

// IMessageSink (MethodCall) public virtual IMessage SyncProcessMessage(IMessage msgReq) { IMessage msgRsp = null; try { msgReq.Properties["__Uri"] = m_ObjectUri; Service webservice = new Service(m_outpath); if(m_mode == "SOAP") { // serialize IMessage into the stream (SoapMessage) MemoryStream reqstream = new MemoryStream(); SoapFormatter sf = new SoapFormatter(); RemotingSurrogateSelector rss = new RemotingSurrogateSelector(); rss.SetRootObject(msgReq); sf.SurrogateSelector = rss; sf.AssemblyFormat = FormatterAssemblyStyle.Full; sf.TypeFormat = FormatterTypeStyle.TypesAlways; sf.TopObject = new SoapMessage(); sf.Serialize(reqstream, msgReq); ISoapMessage sm = sf.TopObject; reqstream.Position = 0; StreamReader sr = new StreamReader(reqstream); string request = sr.ReadToEnd(); reqstream.Close(); sr.Close(); // call web service string respond = webservice.SyncProcessSoapMessage(request); // return messages StreamWriter rspsw = new StreamWriter(new MemoryStream()); rspsw.Write(respond); rspsw.Flush(); rspsw.BaseStream.Position = 0; ISoapMessage rspsoapmsg = (ISoapMessage)sf.Deserialize(rspsw.BaseStream); rspsw.Close(); if(rspsoapmsg.ParamValues[0] is Exception) { throw rspsoapmsg.ParamValues[0] as Exception; } else { object returnVal = rspsoapmsg.ParamValues[4]; object[] OutArgs = rspsoapmsg.ParamValues[5] as object[]; LogicalCallContext lcc = rspsoapmsg.ParamValues[6] as LogicalCallContext; ReturnMessage rm = new ReturnMessage( returnVal, //Object return OutArgs, //Object[] outArgs OutArgs.Length, //int outArgsCount lcc, //LogicalCallContext callCtx msgReq as IMethodCallMessage //IMethodCallMessage mcm ); msgRsp = rm as IMessage; } } else { msgReq.Properties["__Uri2"] = m_ObjectUri; // workaround!  // serialize and encode IMessage BinaryFormatter bf = new BinaryFormatter(); MemoryStream reqstream = new MemoryStream(); bf.Serialize(reqstream, msgReq); reqstream.Position = 0; string request = Convert.ToBase64String(reqstream.ToArray()); reqstream.Close(); // call Web Service string respond = webservice.SyncProcessMessage(request); // decode and deserialize IMessage byte[] rspbyteArray = Convert.FromBase64String(respond); MemoryStream rspstream = new MemoryStream(); rspstream.Write(rspbyteArray, 0, rspbyteArray.Length); rspstream.Position = 0; msgRsp = (IMessage)bf.Deserialize(rspstream); rspstream.Close(); } } catch(Exception ex) { Trace.WriteLine(string.Format("Client:SyncProcessMessage error = {0}", ex.Message)); msgRsp = new ReturnMessage(ex, (IMethodCallMessage)msgReq); } return msgRsp; } public virtual IMessageCtrl AsyncProcessMessage(IMessage msgReq, IMessageSink replySink) { IMessageCtrl imc = null; if(replySink == null) // OneWayAttribute { Trace.WriteLine("Client-[OneWay]Async:CALL"); SyncProcessMessage(msgReq); } else { Trace.WriteLine("Client-Async:CALL"); // spawn thread (delegate work) delegateAsyncWorker daw = new delegateAsyncWorker(handlerAsyncWorker); daw.BeginInvoke(msgReq, replySink, null, null); } return imc; }

The Web Service Client Proxy changes:

[System.Web.Services.WebServiceBindingAttribute(Name="ServiceSoap", Namespace="http://tempuri.org/")] public class Service : System.Web.Services.Protocols.SoapHttpClientProtocol { [System.Diagnostics.DebuggerStepThroughAttribute()] public Service(string uri) { this.Url = uri; } ... } 

WebServiceListener

This is a Web Service gateway to listen a MethodCall formatted into the string request. There are two different WebMethods for this process: SyncProcessMessage and SyncProcessSoapMessage. The functions have a simple logic divided into tree steps:

  • Decoding and de-serializing of the request message
  • Invoking the Remote Method
  • Encoding and serialization of the response message
[WebMethod] public string SyncProcessMessage(string request) { // Request: decoding and deserializing  byte[] reqbyteArray = Convert.FromBase64String(request); MemoryStream reqstream = new MemoryStream(); reqstream.Write(reqbyteArray, 0, reqbyteArray.Length); reqstream.Position = 0; BinaryFormatter bf = new BinaryFormatter(); IMessage reqmsg = (IMessage)bf.Deserialize(reqstream); reqmsg.Properties["__Uri"] = reqmsg.Properties["__Uri2"]; // work around!! reqstream.Close(); // Action: invoke the Remote Method  string[] stype = reqmsg.Properties["__TypeName"].ToString().Split(new Char[]{','}); // split typename Assembly asm = Assembly.Load(stype[1].TrimStart(new char[]{' '})); // load type of the remote object Type objectType = asm.GetType(stype[0]); // type string objectUrl = reqmsg.Properties["__Uri"].ToString(); // endpoint object ro = RemotingServices.Connect(objectType, objectUrl); // create proxy TraceIMessage(reqmsg); IMessage rspmsg = RemotingServices.ExecuteMessage((MarshalByRefObject)ro, (IMethodCallMessage)reqmsg); TraceIMessage(rspmsg); // Response: encoding and serializing  MemoryStream rspstream = new MemoryStream(); bf.Serialize(rspstream, rspmsg); rspstream.Position = 0; string response = Convert.ToBase64String(rspstream.ToArray()); rspstream.Close(); return response; } [WebMethod] public string SyncProcessSoapMessage(string request) { IMessage retMsg = null; string response; try { Trace.WriteLine(request); // Request: deserialize string into the SoapMessage SoapFormatter sf = new SoapFormatter(); sf.TopObject = new SoapMessage(); StreamWriter rspsw = new StreamWriter(new MemoryStream()); rspsw.Write(request); rspsw.Flush(); rspsw.BaseStream.Position = 0; ISoapMessage soapmsg = (ISoapMessage)sf.Deserialize(rspsw.BaseStream); rspsw.Close(); // Action: invoke the Remote Method  object[] values = soapmsg.ParamValues; string[] stype = values[2].ToString().Split(new Char[]{','}); Assembly asm = Assembly.Load(stype[1].TrimStart(new char[]{' '})); Type objectType = asm.GetType(stype[0]); string objectUrl = values[0].ToString(); RealProxyWrapper rpw = new RealProxyWrapper(objectType, objectUrl, soapmsg.ParamValues[4]); object ro = rpw.GetTransparentProxy(); MethodInfo mi = objectType.GetMethod(values[1].ToString()); object retval = mi.Invoke(ro, values[3] as object[]); retMsg = rpw.ReturnMessage; } catch(Exception ex) { retMsg = new ReturnMessage((ex.InnerException == null) ? ex : ex.InnerException, null); } finally { // Response: serialize IMessage into string Stream rspstream = new MemoryStream(); SoapFormatter sf = new SoapFormatter(); RemotingSurrogateSelector rss = new RemotingSurrogateSelector(); rss.SetRootObject(retMsg); sf.SurrogateSelector = rss; sf.AssemblyFormat = FormatterAssemblyStyle.Full; sf.TypeFormat = FormatterTypeStyle.TypesAlways; sf.TopObject = new SoapMessage(); sf.Serialize(rspstream, retMsg); rspstream.Position = 0; StreamReader sr = new StreamReader(rspstream); response = sr.ReadToEnd(); rspstream.Close(); sr.Close(); } Trace.WriteLine(response); return response; }

The implementation of the steps are depended from the type of formatter such as SoapFormatter or BinaryFormatter. The first and last steps are straightforward using the Remoting namespace classes. The second one (action) for the SoapFormatter message needed to create the following class to obtain IMessage of the MethodCall:

public class RealProxyWrapper : RealProxy { string _url; string _objectURI; IMessageSink _messageSink; IMessage _msgRsp; LogicalCallContext _lcc; public IMessage ReturnMessage { get { return _msgRsp; }} public RealProxyWrapper(Type type, string url, object lcc) : base(type) { _url = url; _lcc = lcc as LogicalCallContext; foreach(IChannel channel in ChannelServices.RegisteredChannels) { if(channel is IChannelSender) { IChannelSender channelSender = (IChannelSender)channel; _messageSink = channelSender.CreateMessageSink(_url, null, out _objectURI); if(_messageSink != null) break; } } if(_messageSink == null) { throw new Exception("A supported channel could not be found for url:"+ _url); } } public override IMessage Invoke(IMessage msg) { msg.Properties["__Uri"] = _url; // endpoint msg.Properties["__CallContext"] = _lcc; // caller's callcontext _msgRsp = _messageSink.SyncProcessMessage(msg); return _msgRsp; } }// RealProxyWrapper

Test

I built the following package to test functionality of the WebServiceListener and WebServiceChannelLib assemblies. Note that this package has only test purpose. Here is what you downloaded it:

  • ConsoleClient, the test console program to invoke the call over Internet - client machine
  • ConsoleServer, the host process of the MyRemoteObject - server machine
  • MyRemoteObject, the remote object - server machine
  • WebServiceChannelLib, the custom client channel
  • WebServiceListener, the Web Service listener  - server machine

To recompile a package and its deploying in your environment follow these notes:

  • The folder WebServiceListener has to be moved to the virtual directory (inetpub\wwwroot).
  • The MyRemoteObject assembly has to be install into the GAC on  the server machine
  • The WebServiceChannelLib assembly has to be install into the GAC on the client machine
  • (option) The MSMQChannelLib assembly [1] has to be install into the GAC on the server machine
  • The solution can be tested also using the same machine (Win2k/Adv Server)
  • Use the Echo WebMethod on the test page of the WebServiceListener to be sure that this service will be work 

The test case is very simple. First step is to launch the ConsoleServer program. Secondly open the ConsoleClient program and follow its prompt text. If everything is going right you will see a response from the remote object over Internet. I am recommending to make a first test on the same machine and then deploying over Internet.

Conclusion

In this article has been shown one simple way how to implement a solution for remoting over Internet. I used the power of .Net Technologies such as SOAP, Remoting, Reflection and Web Service. The advantage of this solution is a full transparency between the consumer and remote object. This logical connectivity can be mapped into the physical path using the config files, which they can be administratively changed.

[펌] .NET Remoting 아키텍처 고찰

.NET Remoting 아키텍처 고찰

Pat Martin
Microsoft Corporation

2003년 5월

적용 대상:
    Microsoft .NET Framework
    Microsoft .NET Remoting

요약: 이 기사는 분산 다중 계층 응용 프로그램 설계의 일부로서 .NET Remoting 사용을 고려하는 사용자를 대상으로 합니다. RPC 기술의 장단점으로 인해 편리함과 동시에 다소 불편함을 겪었던 개발자의 관점에서 이 기술의 성능을 설명하겠습니다. 독자가 .NET Remoting에 대한 실제 사용법은 아니더라도 개념적으로는 알고 있는 것으로 가정합니다.

제품 기능 절은 가능하면 Remoting을 사용해 무언가를 설계하려는 사람에게 유용합니다. 최적의 사용 절은 Remoting을 사용해 무언가를 작성하려는 사람에게 유용합니다. Remoting 및 웹 서비스 절에서는 어떤 기술을 언제 사용할지에 대한 일부 혼란을 없애 줍니다. 요약 내용에서는 전체 내용을 간략히 요약합니다.

목차

개요
제품 기능
최적의 Remoting 사용
Remoting 및 ASP.NET 웹 서비스
요약 내용
추가 리소스

개요

.NET Remoting은 응용 프로그램 도메인 간에 RPC를 관리하기 위해 선택된 기술로 설명되어 왔습니다. 응용 프로그램 도메인은 공용 언어 런타임의 독립적인 단위입니다. 도메인은 프로세스 내에서 만들어지고 실행됩니다. 이는 CLR과 비CLR 관리 프로세스의 프로세스 간 통신(Interop)과는 차이가 있습니다. 이러한 RPC 통신의 최근 유형은 특히 “웹” 상에서 웹 서비스의 도메인(일반적인 용어를 사용할 경우)처럼 보입니다. 아쉽게도 겉보기에 분명한 이런 차이점은 기사 An Introduction to Microsoft .NET Remoting Framework  에서 다음과 같이 언급되었듯이 IIS에서 .Net Remoting 서버가 호스팅될 수 있기 때문에 명확하게 구별되지 않습니다.

".NET Remoting objects can be exposed as a Web Service by hosting them in IIS..."

일부 Microsoft 고객에게는 .NET Remoting과 관련해 다소 혼란이 있을 수 있습니다. 흔히 받는 질문은 "언제 Remoting을 사용해야 합니까?", "Remoting이 언제 NTLM을 지원할 것입니까?", "어떻게 원격 대화를 확보할 수 있습니까?", "COM+은 어떻습니까?", "Remoting이 어떻게 트랜잭션을 관리합니까?" 등입니다.

이 기사에서는 이런 질문에 대한 대답과 더불어 최적의 .NET Remoting 사용법을 설명하고 현재 제공되는 기능에 대한 개요를 제공합니다. 요약 내용에서는 특별히 웹 서비스 및 새로 등장하고 있는 GXA(글로벌 XML 웹 서비스) 사양과 관련해 이 기술의 향후 비전을 제시합니다 .

제품 기능 절은 주로 TechED N.Z. 2002에 소개된 정보로부터 발췌했습니다. 이 프레젠테이션에서는 분산 솔루션에서 Remoting을 사용할 수 있는 여러 가지 방법을 강조하고 Remoting의 장단점에 대해 소개되었습니다.

최적의 사용 절은 다중 계층 .NET 응용 프로그램을 사용하는 동안 개인적으로 경험한 내용을 토대로 하고 있습니다. 개발하는 동안 간단하면서도 최적의 사용법을 여러 가지 찾아냈으며 이를 여기에 나열했습니다.

일부 절에 있는 자료는 Microsoft 내에서 이 기술과 그 발전에 대해 잘 이해하고 있는 사람들과 나눈 비공식 대화를 토대로 하고 있습니다. 여기 제시된 정보는 향후 제품 출시 계획이나 일정과는 관계가 없습니다.

제품 기능

이 절에서는 .NET Remoting이 제공하는 제품의 기능과 특징을 설명합니다.

클라이언트/서버 통신

.NET Remoting은 응용 프로그램 도메인에서 동기 및 비동기식 RPC 대화를 모두 관리하는 효과적인 방법을 제공합니다. 원격 개체 코드는 서버 활성 개체와 클라이언트 활성화 개체 모두의 경우에서와 같이 서버에서 실행되거나 원격 개체가 클라이언트/서버 연결을 통해 일련화된 클라이언트에서 실행됩니다. 어느 경우에도 간단한 초기화 과정과 구성이 완료되고 나면 프로그래밍 구문은 매우 단순하며 이를 위해 작성해야 할 코드량도 매우 적습니다. 원격 개체(참조에 의해 마샬링된 프록시)의 사용은 프로그래머에게 분명합니다. 예를 들어 초기 Windows RPC 메커니즘에서는 IDL 도구를 사용한 정교한 유형과 마샬링 지식을 요구하였고 RPC 클라이언트 및 서버의 스텁 관리를 개발자에게 부담시켰습니다. Remoting은 .NET에서 RPC를 훨씬 잘 제공하며 흔히 쉽게 이해되는 .NET 데이터 유형을 사용하면 초기 RPC 메커니즘에 존재했던 자료형 불일치 위험을 없애줍니다.

Remoting은 기본적으로 XML 인코딩 SOAP나 원시 이진 메시지 형식으로 HTTP나 TCP 프로토콜을 사용해 통신하도록 구성할 수 있습니다. 사용자 지정 프로토콜(채널)이나 메시지 형식(포맷터)은 개발자가 작성할 수 있으며 필요한 경우 Remoting 프레임워크에 의해 사용될 수 있습니다. 통신 프로토콜 선택과 마찬가지로 서버나 클라이언트 구성 요소 모두에서 포트를 선택할 수 있습니다. 여기서 큰 장점은 기본적인 통신을 연결하고 실행하는 것이 매우 쉽다는 것입니다.

하지만 상태 관리와 관련해 통신 유형을 선택해야 합니다. 이 절의 나머지 부분에서는 Remoting이 제공하는 여러 가지 선택 가능한 통신 및 이와 관련한 설계 정보를 설명합니다.

서버 활성 개체

서버 활성 개체는 서버에 의해 수명이 조절되는 개체입니다. 이 개체는 클라이언트가 개체의 메서드를 최초로 호출하는 경우 필요할 때에만 서버에 의해 만들어집니다. 서버 활성 개체는 기본 생성자만 지원합니다. 매개 변수화된 생성자를 가지고 원격 개체를 사용하려면 클라이언트 활성화 또는 동적 게시(아래 참조)를 사용하면 됩니다. 서버 활성 개체는 해당 위치(URL)가 미리 게시되어 알려지므로 잘 알려진 개체 유형으로도 간주됩니다. 서버 활성 개체를 위한 활성화 모드로 SingletonSingleCall 두 가지가 있으며 아래에서 설명됩니다. 서버로 활성화된 유형의 인스턴스를 만들려면 응용 프로그램을 프로그래밍 방식이나 정적 구성을 통해 구성하면 됩니다. 서버 활성화 구성은 매우 단순합니다. 예를 들어 다음 코드는

<service>
  <wellknown mode="SingleCall" type="Hello.HelloService, Hello" 
                   objectUri="HelloService.soap" />
</service>

SingleCall로 설정된 활성화 모드를 사용한 서버로 활성화된(잘 알려진) 유형을 기술하고 있습니다. 서버로 활성화되는 Remoting 구성에 대한 자세한 내용은 MSDN에서 .Net Framework Developer's Guide 'Server-Side Registration'을 참조하십시오.

Singleton

이런 개체는 어떤 시점에도 메모리에 둘 이상의 인스턴스가 없으며 모든 클라이언트를 해당 인스턴스가 처리한다는 점에서 기본 Singleton 설계 패턴을 따릅니다. 그러나 이러한 유형은 유형에 따른 기본 수명을 가집니다. 아래에 나오는 개체 수명 관리 절을 참조하십시오. 즉 클라이언트가 원격 클래스의 같은 인스턴스에 대한 참조를 항상 받는 것은 아닙니다. 이 사실은 상태 관리에 대해 흥미로운 정보를 부여하는 것으로서 Remoting 패턴이 고전적인 Singleton 모델(똑같이 하려면 개체 ID가 필요)의 어디로부터 갈라져 나왔는지를 밝혀 줍니다. 설계에서 고전적인 Singleton의 상태 관리 패턴을 필요로 하는 경우 두 가지 문제 해결 방법이 있습니다. 한 가지 방법은 호스트 응용 프로그램 도메인이 실행되는 한 개체가 메모리에 보관되도록 기본적인 개체 임대 동작을 다시 정의하는 것입니다. 다음 코드는 이러한 방법을 보여 줍니다.

public class MyClass : MarshalByRefObject
{
  public override Object InitializeLifetimeService()
  {
      return null;
  }
}

언급했던 대로 이 메커니즘은 개체를 메모리에 묶어 두어 개체가 재사용되지 못하게 하지만 이는 호스트 응용 프로그램이 실행되는 동안만 가능합니다. IIS 호스팅을 위해 IIS 또는 Remoting 세션을 호스팅하는 IIS 프로세스가 재활용될 경우(여러 가지 이유로 발생 가능) 개체는 소멸됩니다.

Remoting을 가지고 스레드에 안전한 Singleton 상태 데이터에 완전히 의존하려면 다음 세 가지를 수행해야 합니다.

  1. 위에서 설명한 대로 임대가 무제한인 임대 메커니즘으로 다시 정의합니다.
  2. 수명에 대해서 전적으로 통제할 수 있는 시스템 서비스처럼 자체 내부 프로세스 안에서 원격 서버를 호스트하십시오. 이 프로세스도 재활용될 수 있지만 재활용되고 있는 IIS 작업자 프로세스보다 좀 더 확연한 동작이라 발견될 확률이 높습니다. 이 메커니즘에 대한 자세한 내용은 아래에서 제품 기능 절을 참조하십시오.
  3. 클라이언트로부터의 동시 요청을 처리하기 위해 다중 스레드가 사용될 것이므로 스레드에 안전한 원격 서버를 개발하십시오. 이렇게 하면 공유 리소스에 대한 동시 쓰기를 관리하며 일반적으로 정적 메모리에 대한 공유 액세스를 처리할 수 있습니다.

SingleCall

SingleCall 원격 서버 유형은 항상 클라이언트 요청당 하나의 인스턴스를 가집니다. 다음의 메서드 호출에 대해서는 다른 인스턴스가 처리합니다. 설계 면에서 SingleCall 유형이 제공하는 기능은 매우 단순합니다. 이 메커니즘을 사용할 경우 상태 관리는 제공되지 않으므로 상태 관리를 필요로 할 경우에는 나쁘겠지만 필요로 하지 않을 경우에는 이상적입니다. 이 모드에서는 요청당 정확히 하나의 인스턴스만 얻으므로 상태보다는 로드 균형과 가용성만 중요할 경우 이를 선택하는 것이 좋습니다. 개발자가 원할 경우 SingleCall 개체에 대해 자신이 고안한 상태 관리를 제공할 수 있지만 새 개체의 식별 정보가 각각의 새 메서드 호출로 인스턴스를 생성하므로 이 상태 데이터는 개체 내에 상주하지 않습니다.

동적 게시

서버 활성화 방법에서 마지막으로 살펴볼 유형은 동적 게시입니다. 이 서버 활성화 유형은 프로그래밍 방식의 게시 메커니즘을 제공함으로써 개체 생성에 대한 보다 강력한 제어를 할 수 있습니다. 특정 개체를 특정 URL에 게시할 수 있으며 옵션으로 매개 변수화된 생성자를 사용할 수 있습니다. 구조적으로 동적 게시는 서버로 활성화되는 Singleton 유형이 다소 변형된 것이라고 볼 수 있습니다. 동적 게시  에 대한 정보는 .NET Framework Developer's Guide에서 찾을 수 있습니다.

클라이언트 활성화 개체

클라이언트 활성화 개체는 클라이언트가 new 또는 Activator.CreateInstance()를 호출할 때 만들어집니다. 클라이언트는 수명 임대 시스템을 사용하여 이러한 인스턴스의 수명에 관여할 수 있습니다. 이 활성화 메커니즘은 설계 유연성 면에서 최고입니다. 클라이언트가 개체를 활성화하려면 클라이언트 활성화를 사용해 활성화 요청을 서버로 보냅니다. 이 메커니즘을 사용하면 매개 변수화된 생성자와 고유한 클라이언트당 연결 상태 관리를 모두 사용할 수 있습니다. 클라이언트 활성화를 사용해 각 클라이언트는 자체의 특정 서버 인스턴스로 처리되며 이는 다중 호출에 대해 개체 상태를 절약하게 해 줍니다. 하지만 분산 처리되고 있다는 것과 개체가 실제로 외부 프로세스에서 실행될 뿐만 아니라 다중 계층 응용 프로그램의 경우 외부 시스템에서 실행된다는 것을 잊기 쉬우므로 이러한 개체 사용에 신중해야 하며 인터넷을 통해 속성을 설정하지 않는 것이 좋습니다. 여기서는 chatty 인터페이스보다는 chunky 인터페이스가 기준입니다. 성능을 위해서는 높은 응집성/낮은 결합도를 포기해야 할 수도 있습니다. 클라이언트로 활성화되는 유형의 인스턴스를 만들려면 응용 프로그램을 프로그래밍 방식으로 구성하거나 정적 구성을 사용하면 됩니다. 서버에서의 클라이언트 활성화 구성은 꽤 단순합니다. 예를 들어 다음 코드는

<service>
  <activated type="Hello.HelloService, Hello" 
             objectUri="HelloService.soap" />
</service>

ClientActivated 유형을 기술하고 있습니다. 클라이언트로 활성화되는 유형에서는 유형 자체만으로도 활성화가 가능하므로 더 이상 URL이 필요하지 않습니다. 또한 wellknown 태그 대신 activated 태그가 사용되었습니다. 클라이언트로 활성화되는 Remoting 구성에 대한 자세한 내용은 MSDN에서 .Net Framework Developer's Guide 'Registering Remote Objects Using Configuration Files'를 참조하십시오.

확장성

원격 메서드 호출을 처리하는 동안 .NET Remoting은 포맷된 “메시지”를 Remoting “채널”을 통해 클라이언트로부터 서버로 보냅니다. 메시지 형식과 채널은 완벽한 확장성과 사용자 지정 기능을 가집니다. 기본 채널이나 포맷터를 사용자 지정으로 제작된 구성 요소로 교체할 수 있습니다. 메시지가 통과되는 동안 다양한 “싱크 지점”에서 인터셉트되거나 변경될 수 있어 메시지의 사용자 지정이 가능합니다. 사용자 지정 메커니즘은 .NET Framework 개발자 가이드(싱크 및 싱크 체인)에 설명되어 있으며 이미 인터넷에 수많은 사용자 지정 채널과 포맷터가 등장하고 있습니다(예: 명명된 파이프 채널 구현). 이런 확장성은 대체로 흥미를 끌지 못하는데 왜냐하면 이 기술로 제공되는 기본 포맷터와 채널이 이미 가장 넓은 범위(특히 SOAP 메시지 포맷터를 사용하는 TCP와 HTTP)를 목표로 하고 있기 때문입니다. 그러나 다양한 솔루션을 고려하게 되는 설계 초기 단계에서는 이러한 특징을 기억해 볼 만 합니다.

예외 전파

.NET Remoting은 Remoting 전체에 걸쳐 예외 전파를 지원합니다. 이는, 예를 들어 DCOM에서와 비교했을 때 오류 코드 사용에 대한 주요한 개선 사항입니다.

Remoting 예외에서 예외 클래스를 순차 가능화하고 ISerializable 인터페이스를 구현하는 것은 좋은 방법입니다. 이렇게 하면 예외가 Remoting 영역에 걸쳐 올바르게 일련화되며 생성하는 동안 사용자 지정 데이터가 예외에 추가됩니다. 원격화되고 일관성 있게 사용되어야 하는 예외를 위해 자체 예외 클래스를 정의하는 것이 좋습니다. 모든 예외는 이런 식으로 Catch되고 올바르게 전파되어야 하며 처리되지 않은 예외를 Remoting 영역에서 허용하면 안 됩니다.

개체 수명 관리

.NET Remoting은 원격 개체의 수명을 관리하기 위한 풍부한 메커니즘을 제공합니다. 서버 개체가 어떠한 상태도 대기시키지 않을 경우, 예를 들어 SingleCall 개체의 경우처럼 이 프로세스에 대해서는 염려할 필요가 없습니다. Remoting 인프라가 작업하는 대로 그냥 두면 개체는 필요할 때 가비지로 수집됩니다. 서버 활성화 Singletons든 클라이언트 활성화 개체든 상태를 대기시키면 수명 관리 프로세스, 즉 개체 임대에 관여해야 할 수도 있습니다. 이미 최소로만 관여하면 되는 단순하고 유용한 방법 한 가지를 살펴보았는데 이는 위 Singletons 설명에서와 같이 InitializeLifetimeService 메서드를 다시 정의하는 것입니다. 이렇게 하면 개체를 호스트하는 프로세스가 실행되는 동안에는 개체를 보유할 수 있습니다. 개체 수명 프로세스는 어떻게 작동할까요?

개체 관리를 위해 Remoting이 제공하는 메커니즘은 임대 원리를 기반으로 하고 있습니다. 개체를 소유하지는 못하지만 값을 치르는 동안에는 계속 사용할 수 있습니다. 이 프로세스는 나중에 자세히 설명하겠습니다. 우선 COM에서 개체 정리를 어떻게 처리하는지 잠시 설명하겠습니다. DCOM은 개체가 여전히 실행 중인지를 확인하기 위해 Ping과 참조 계수 메서드를 결합해 사용합니다. 둘 다 오류가 발생하기 쉬우며 네트워크 대역폭에 의존적입니다. 참조 계수의 전체 원리는 매우 이해하기 어려우며 제대로 사용한 경우에도 불안정합니다. 참조 계수로 작업하기 위해 적용해야 할 간단한 규칙이 여러 가지 있었으며 현재도 많습니다. COM 개체를 위한 IUnknown 인터페이스에는 AddRefRelease 메서드가 포함되며 개발자는 이를 적절한 때에 호출해 주어야 합니다. 때로 프로그래머가 이를 잘못 처리해 개체가 제거되지 않고 관련 메모리가 누수되기도 합니다.

반면 Remoting의 임대 기반 수명 관리 시스템은 임대 개체, 스폰서, 임대 관리자를 결합하여 사용합니다. 응용 프로그램 도메인은 각기 임대 관리자를 포함하고 있어 Singleton 또는 클라이언트로 활성화된 각 개체를 위한 임대 개체 참조를 도메인 내에서 대기시킵니다. 각 임대는 0 이상의 관련 스폰서를 가지며 임대 관리자가 임대가 만료된 것을 확인할 경우 임대를 갱신할 수 있습니다. 이 임대 기능은 ILease 인터페이스를 통해 Remoting 인프라가 제공하며 이미 살펴본 InitializeLifetimeService 호출을 통해 얻습니다. ILease 인터페이스는 개체 수명을 관리하는 데 사용되는 수많은 속성을 정의합니다.

  • InitialLeaseTime. 임대가 처음 얼마 동안 유효한지를 결정합니다.
  • RenewOnCallTime. 각 메서드 호출 후 임대는 이 시간 단위에서 갱신됩니다.
  • SponsorshipTimeout. 스폰서의 임대 만료 알림 후 Remoting이 기다리는 시간입니다.
  • CurrentLeaseTime. 임대가 만료될 때까지의 시간입니다(읽기 전용).

임대가 만료되면 임대 관리자는 임대 스폰서에게 임대를 갱신할지를 묻습니다. 원하는 스폰서가 없을 경우에는 관련 개체 참조가 해제됩니다.

스폰서는 원격 개체에 대한 임대를 갱신할 수 있는 개체입니다. 스폰서가 되려면 클래스는 MarshalByRefObject로부터 파생되어야 하며 ISponsor 인터페이스를 구현해야 합니다. 임대 하나가 여러 스폰서를 가질 수 있습니다.

이러한 임대 관리 메커니즘은 인터페이스에 대한 프로그래밍 방식과 관련해 .NET Framework Developer's Guide의 수명 임대  에 설명되어 있으므로 여기서는 다루지 않습니다. 그러나 참고할 만한 것은 상태를 가지는 원격 개체의 수명 관리를 위해 이렇게 풍부한 메커니즘이 존재한다는 것입니다. 위에서 언급했던 대로 임대 메커니즘을 완전히 무시하거나 프로세스 컨테이너가 실행되는 동안 메모리에 개체를 대기시키기 위해 사용하거나 본격적으로 사용할 수 있습니다.

원격 서버 호스팅

.NET 원격 서버를 호스트하는 데에는 여러 가지 방법이 있는데 크게 두 개의 항목으로 나누어 볼 수 있으며 다음과 같습니다.

ASP.NET에서의 IIS 호스팅

사용 가능한 표준 기능으로 IIS에서 원격 서버 쪽의 개체를 호스트하는 기능이 있습니다. 이는 보안 및 스케일 가능에 대한 지원 등을 포함해 많은 장점을 제공합니다.

IIS에서 개체를 호스트하려면 다음을 수행하십시오.

  1. 원격 클래스를 개발하여 MarshalByRefObject로부터 상속받습니다. 또는 클래스를 순차 가능하도록 선언합니다.
  2. IIS 관리자를 사용하여 가상 웹 응용 프로그램을 만듭니다.
  3. 클래스를 포함한 어셈블리를 가상 웹 응용 프로그램의 하위 폴더 bin으로 복사합니다.
  4. Remoting 서버의 구성 정의를 대기시키기 위해 web.config 파일을 만들어 이를 웹 응용 프로그램의 가상 루트에 넣습니다.

이로써 작업이 완료됩니다. 그러나 숙지해야 할 몇 가지 제약이 있습니다.

  • IIS 호스팅에 대한 응용 프로그램 이름을 지정할 수 없습니다. 가상 응용 프로그램 이름이 됩니다.
  • httpchannel을 사용해야 합니다.
  • Remoting 클라이언트가 웹 응용 프로그램이기도 한 경우 시작하는 동안 RemotingConfiguration.Configure를 호출해야 합니다. 이것은 보통 Global.asax 파일의 Application_Start 메서드에 포함시킵니다. <client> 태그를 사용해 클라이언트 웹 응용 프로그램을 자동으로 구성할 수 없습니다.
  • IIS가 포트 할당을 담당하므로 포트를 지정하지 마십시오. 필요한 경우 가상 응용 프로그램을 위한 포트를 지정하기 위해 IIS 관리자를 사용할 수도 있습니다.

Remoting 응용 프로그램 도메인은 Aspnet_wp.exe 작업자 프로세스 내에서 호스트되며 기본으로 해당 프로세스의 존재를 가정합니다.

참고   현재 ASP.NET에 버그가 하나 있는데 Aspnet_wp.exe 작업자 프로세스의 프로세스 식별 정보를 "시스템"이나 로컬 컴퓨터 계정으로 설정해야 합니다. 기본 설정인 machine.config 내의 "machine"이 올바르게 구성되지 않아 IIS의 도메인 컨트롤러에서 호스트될 경우 ASP.NET 응용 프로그램이 오류 500, "내부 서버 오류"로 인해 실패합니다. 이 버그와 관련해 컴퓨터 계정을 올바르게 구성하는 방법에 대한 설명서가 부족한 게 사실입니다.

IIS에서 호스트할 경우 여러 가지 기능상의 혜택, 즉 스케일 가능, 스레딩, 감사, 인증 및 보안 통신 기능이 기본으로 제공됩니다. ASP.NET 작업자 프로세스는 항상 실행되며 machine.config 내의 <processModel> 요소를 사용한 스레드 및 폴트 관리와 관련한 미세 조정으로부터 영향을 받습니다. 요컨대 IIS에서 사용 가능한 장점과 기능은 원격 서버에 개방되어 있습니다.

하지만 단점도 많습니다. TCP보다 느린 HTTP를 사용해야 합니다. 또한 IIS는 Singleton 상태를 파괴하는 ASP.NET 작업자 프로세스를 순환할 수도 있습니다. 클라이언트의 다음 호출이 Singleton을 다시 시작하게 하므로 이 점은 설계 요구에 따라 문제가 될 수도 있고 안 될 수도 있습니다. 작업자 프로세스를 재활용하지 않게 IIS를 구성할 수 있지만 이 기능은 특히 IIS 5에서는 제한적이라 결국 광범위에 영향을 미칠 수도 있습니다. 요컨대 원격 서버의 보안이 필요한 경우에는 IIS 호스팅이 확실한 방법이라는 것입니다. 성능은 시스템 테스트/사용 중 실제로 문제가 발생하는 경우에만 문제가 되며 또한 이 문제는 하드웨어 상에서 반드시 해결 방법을 찾을 수 있습니다.

IIS의 인증 고려 사항

인증 옵션

.NET Remoting은 자체의 보안 모델이 없습니다. 인증 및 권한은 채널과 호스트 프로세스, 이 경우 IIS에 의해서 수행됩니다. Remoting을 위해 Windows 인증이 지원되며 <authentication mode="Windows"/>를 설정함으로써 web.config 내에서 구성됩니다. Remoting 클라이언트는 쿠키에 액세스할 수 없고 로그온 페이지에 리디렉션될 수 없으므로 폼(Forms)이나 Passport 인증을 지원하지 않습니다. 원격 서버는 비대화형 사용을 위해 설계되었습니다.

원격 개체에 자격 증명 전달

ASP.NET 작업자 프로세스 내에서 원격 개체가 호스트되고 Windows 인증을 위해 구성되고 나면 채널의 자격 증명 속성을 사용해 사용할 자격 증명을 지정해야 합니다. 이렇게 하지 않을 경우 자격 증명 전달 없이 원격 호출이 만들어집니다. 그 결과로 종종 http에 대한 액세스가 거부됩니다. 원격 개체 프록시(Remoting 클라이언트 프로세스)를 호스트하는 프로세스 자격 증명을 사용하려면 채널의 자격 증명 속성을 프로세스 자격 증명 캐시로 관리되는 DefaultCredentials로 설정하십시오. 이는 웹 클라이언트용 채널 요소, 즉 <channel ref="http" useDefaultCredentials="true"/> 사용하여 선언적으로 하거나 다음 코드를 사용하여 프로그램 방식으로 할 수도 있습니다.

IDictionary channelProperties;
channelProperties = ChannelServices.GetChannelSinkProperties(proxy);
channelProperties["credentials"] = CredentialCache.DefaultCredentials;

특정 자격 증명을 원격 개체 호출과 함께 전달하려면 기본 자격 증명, 즉 <channel ref="http" useDefaultCredentials="false"/> 설정을 사용 안 함으로 구성하고 다음 코드를 사용하십시오.

IDictionary channelProperties =
ChannelServices.GetChannelSinkProperties(proxy);
NetworkCredential credentials;
credentials = new NetworkCredential("username", "password", "domain");
ObjRef objectReference = RemotingServices.Marshal(proxy);
Uri objectUri = new Uri(objectReference.URI);
CredentialCache credCache = new CredentialCache();
// "authenticationType"를 "Negotiate", "Basic", "Digest",
// "Kerberos" 또는 "NTLM"로 대체하십시오.
credCache.Add(objectUri, "authenticationType", credentials);
channelProperties["credentials"] = credCache;
channelProperties["preauthenticate"] = true;
참고   위와 같이 preauthenticate 속성을 TRUE로 설정하면 WWW-Authenticate 헤더가 초기 요청과 함께 전달됩니다. 이렇게 하면 웹 서버가 원래의 요청에 대한 액세스 거부를 중단하고 후속 요청에서 인증을 수행합니다.

IIS 외부에서 호스팅

IIS 외부에서 원격 호스팅을 하는 데에는 많은 옵션이 있습니다. 이러한 옵션은 다음과 같습니다.

콘솔 응용 프로그램 내에서 호스팅

개발자는 Remoting 인프라를 시작한 다음 “중단하지 않고 계속 작동하는” 콘솔 응용 프로그램을 작성할 수 있습니다. 이렇게 중단하지 않고 계속 작동하는 이유는 여기에 응용 프로그램 도메인이 들어 있어서 원격 호출을 호스트하기 때문입니다. 작성하기는 매우 쉽습니다. 원격 호스트 구성 파일 이름을 전달하면서 RemotingConfiguration.Configure 메서드를 호출한 다음 키 누르기나 특정 메시지 수신과 같은 이벤트가 프로세스를 제거하게 할 때까지 기다립니다.

이 방법은 중간 계층에서 IIS를 필요로 하지 않는다는 장점이 있지만 아직 프로덕션용으로는 준비되지 않았으며 데모, 개발, 테스트에 유용합니다. 즉 유용하지만 사용 범위가 매우 제한적입니다.

GUI 응용 프로그램 내에서 호스팅

개발자는 Remoting 인프라를 시작한 다음 “물고 있는” Windows GUI 응용 프로그램을 작성할 수도 있습니다. 실행을 계속해야 하는 유일한 이유는 여기에 응용 프로그램 도메인이 들어 있어서 원격 호출을 호스트하기 때문입니다. 여기에서의 개발 방법은 콘솔 응용 프로그램 사용입니다. Remoting 호스트는 직접 시작하거나 사용자 조작으로 시작할 수 있습니다. 이 방법 역시 중간 계층에서 IIS를 필요로 하지 않는다는 장점이 있으며 데모와 테스트에 유용합니다. 이것의 변형은 채팅 유형의 응용 프로그램 같은 피어-투-피어(논리적으로) winforms 응용 프로그램입니다. 이것 역시 사용이 제한적입니다.

시스템 서비스 내에서 호스팅

시스템 서비스 개념 자체 만큼 Remoting 인프라가 기능을 제공하지 못한다는 점에서 이 가능성은 좀 더 흥미로워집니다. 시스템 서비스는 컴퓨터가 시작될 때 시작되어 중단하라고 할 때까지 계속되게 구성할 수 있으며 이 점이 원격 호스팅에 있어서 이상적입니다. IIS 응용 프로그램도 가상 응용 프로그램에 대해 “높은 고립 모드”를 설정함으로써 유사하게 작동하도록 구성할 수 있습니다. 이 기사에서 다룬 내용 외에도 이와 관련한 정보가 매우 많습니다. 고객들은 이 메커니즘에 대해 그 유용성을 의심하는 질문들을 해 왔습니다. 먼저 장점을 몇 가지 살펴보겠습니다. 서비스 자체의 장점에 대해서는 이미 언급했습니다. 또한 호스트 프로세스의 활성화에 대해 완벽히 통제할 수 있습니다. 예를 들어 동적 게시나 클라이언트 활성화를 사용하도록 선택할 수 있습니다. 사용자 프로필을 로드할 수 있고 이진 인코딩 메시지를 가지고 TCP를 통해 높은 성능을 얻을 수 있으므로 IIS가 필요하지 않습니다.

하지만 단점도 꽤 많습니다. 우선 필요할 경우 자신의 인증과 인증 메커니즘을 작성해야 합니다.

시스템 서비스 역시 Remoting 서버와 같이 유용하려면 스케일이 가능하고 재입력이 가능해야 하는데 이러한 기능은 다중 계층 분산 응용 프로그램에서 필요합니다. 예를 들어 IIS가 없다면 호스팅 서비스에서 자체 감사와 인증을 관리해야 하지만 IIS에서는 둘 다 표준으로 제공됩니다.

이러한 이유로 인해 시스템 서비스 호스팅 장치는 한 번 교환을 하기 위해 메시지가 대기열에 있거나, 보안 문제가 없거나, TCP를 통해 IPSec이 사용 가능한 환경 등과 같이 제한된 환경에서만 사용할 수 있습니다.

엔터프라이즈 서비스 관리

원격 구성 요소가 COM+ 환경에 참여하고 COM+ 컨텍스트에서 실행하려면 ServicedComponent로부터 상속받아야 합니다. System.EnterpriseServices 네임스페이스에 제공된 다른 기능과 함께 ServicedComponent는 CLR 구성 요소가 지시하는 트랜잭션 요구 사항 및 서버 프로세스 실행 속성 등 여러 COM+ 속성을 지정할 수 있게 합니다. 강력한 명명 및 regsvcs 명령 사용과 더불어 원격 구성 요소는 일반적인 COM+ 환경의 일부가 될 수 있습니다.

원격 구성 요소가 MarshalByRefObject로부터 상속받아야 하고 COM+ 구성 요소가 ServicedComponent 로부터 상속받아야 한다면 그리고 .NET 관리 코드에 다중 상속이 없다면 어떻게 하면 될까요? 다행히 ServicedComponentContextBoundObject로부터 파생되며 이는 필요한 MarshalByRefObject로부터 파생됩니다. Remoting 위에 바로 COM+ 통합을 작성할 수 있으며 이로써 개체 풀링, 분산 트랜잭션 지원, 역할 기반 보안과 같은 엔터프라이즈 서비스가 제공하는 혜택을 제공해 줍니다. 그런데 이를 수행하는 방법과 향후 점검과 관련해 이런 방법이 구조적으로 이루어지는 방법에 대한 예가 빠졌습니다.

COM+ 컨텍스트 인프라와 Remoting 컨텍스트 인프라가 시간이 지나면서 유사해질 것이라는 예측이 맞을 것입니다. 정확히 언제 어떻게 이렇게 될지는 현재 확실하지 않습니다.

최적의 Remoting 사용

늘 그렇듯이 분산 구성 요소를 개발하고 테스트하는 데에는 프로젝트 비용과 개발자의 노고가 필요합니다. 다음 지침에서는 이러한 노력을 통해 얻은 정보를 제공합니다.

시작하기

기사 Basic Remoting Task List  에서는 Remoting을 처음 설정할 때 수행해야 하는 작업의 검사 목록을 잘 제시해 줍니다. 프로세스를 진행할 때 위 기사를 참고 자료로 사용하면 좋습니다. 다음은 수행해야 하는 단계를 간단히 요약한 것입니다.

호스트 작업

  • 응용 프로그램 도메인, 활성화 모델, 채널, 포트 및 게시를 선택하여 서비스를 설계합니다.
  • 호스트 응용 프로그램 도메인을 구성합니다(예: IIS / 시스템 서비스).
  • 호스트 활성화, 채널 및 프로토콜 설정을 구성합니다. RemotingConfiguration.Configure를 호출하여 로드한 구성 파일을 사용하는 것이 좋습니다.
  • 클라이언트가 사용하도록 인터페이스를 게시합니다. 자세한 내용은 아래에서 '인터페이스 게시 옵션' 절을 참조하십시오.

클라이언트 작업

  • 응용 프로그램 도메인과 활성화 모드를 선택하여 클라이언트를 설계합니다.
  • 채널과 포트의 등록 여부를 고려합니다.
  • 원격 유형 메타데이터를 얻습니다.
  • 클라이언트 응용 프로그램 도메인을 구현합니다.
  • 클라이언트 활성화 모드와 더불어 응용 프로그램 이름, 채널 및 개체 URI와 같은 기타 유형 정보를 구성합니다. RemotingConfiguration.Configure를 호출하여 로드한 구성 파일을 사용하는 것이 좋습니다.

포맷 옵션

Remoting은 표준 방법으로 HTTP 채널에서 SOAP나 Binary 포맷터를 사용하거나 TCP 채널에서 Binary 포맷터를 사용하도록 구성할 수 있습니다. 이 구성은 일반적으로 클라이언트 구성 파일에서 적절한 항목을 만드는 것과 정적 RemotingConfiguration.Configure 메서드를 호출하는 것으로부터 영향을 받습니다.

예를 들어 HTTP에서 Binary 포맷터를 사용하기 위해 Remoting 연결을 구성하려면 다음과 같이 구성 항목을 만들어야 합니다.

<channel ref="http" useDefaultCredentials="true" port="0">
  <clientProviders>
    <formatter ref="binary"/>
  </clientProviders>
</channel>

여기서 "channel ref"는 HTTP 프로토콜을 식별하고 "formatter ref"는 채널(이 경우는 이진 채널)을 통해 보내는 메시지 형식을 식별합니다.

아쉽게도 개발 중 HTTP 채널에서 Binary 포맷터를 사용하면 서버에서 발생한 오류가 잘못 전달되어 예기치 못한 문제를 일으킵니다. 예를 들어 일반적인 서버 오류나 액세스 위반은 둘 다 클라이언트에 잘못 보고됩니다. 이는 Binary 포맷터를 사용하는 클라이언트 쪽 Remoting 구성 요소가 이진 형식의 메시지 반환을 기대하기 때문입니다. 일반 텍스트 오류 결과는 올바르게 해석되지 않으며 다음과 같이 보고됩니다.

처리되지 않은 예외 유형 'System.Runtime.Serialization. SerializationException'이 mscorlib.dll에서 발생했습니다. 추가 정보: BinaryFormatter 버전 비호환성. 예상 버전 1.0. 받은 버전 1008738336.1684104552.

이 오류는 대부분의 경우 버전 비호환성 때문이 아니라 클라이언트 쪽에서 텍스트 오류 응답을 분석하는 능력이 없기 때문에 발생합니다. 이 프로토콜 결함은 제품의 향후 버전에서 해결될 것으로 기대되며 개발 중에는 SOAP 포맷터를 사용할 것을 강력히 추천합니다. 성능에 유리한 것으로 입증이 되면 포맷터가 binary로 전환될 수도 있지만 이는 성능이 반드시 필요하고 성능 혜택이 큰 경우에만 해당될 것입니다.

인터페이스 게시 옵션

Remoting 서버를 설계해 작성하고 나면 클라이언트가 컴파일 시에 참조를 확인하고 동적 프록시 개체를 만드는 데 사용할 수 있도록 서버가 제공하는 인터페이스를 게시해야 합니다. 이를 위한 방법에는 여러 가지가 있습니다. 여기서 다루어야 할 내용은 많지만 몇 가지만 먼저 설명하겠습니다.

  • 정적 필드와 메서드는 원격화되지 않으며 .NET Remoting은 항상 일부 형식의 인스턴스 구성원만 처리합니다.
  • 개인 메서드 / 유형은 원격화되지 않습니다.
  • MarshalByRef 유형은 참조에 의해 원격화되며 순차 가능한 유형은 값이 복사되고 코드는 클라이언트 프로세스에서 실행됩니다.
  • Equals, GetHashCode, MemberwiseClone 등과 같은 개체의 가상 메서드는 로컬에서 실행됩니다.

이러한 설계상의 유의 사항과 함께 Remoting 서버가 내보낼 인터페이스 게시 옵션은 다음과 같습니다.

  • 클라이언트가 컴파일 시 사용하도록 서버 쪽 어셈블리를 제공합니다. 클라이언트에서는 구현이 아닌 인터페이스만 필요하므로 이렇게까지 할 필요가 없으며 권장하지 않습니다.
  • Remoting 서버는 웹 서비스로서 가능하지만 SOAP / HTTP 클라이언트용 Remoting 서버는 서버 개체와 해당 메서드를 기술하는 WSDL(Web Services Description Language) 파일을 제공할 수 있습니다. .NET Framework SDK와 함께 제공되는 SOAPSUDS 유틸리티는 메타데이터 역할을 할 WSDL 파일 생성에 사용될 수 있습니다. Remoting 인터페이스용 WSDL이 웹 서비스의 WSDL과 완벽히 호환되지 않으므로 이 방법은 Remoting보다는 즉, 엄격히 말하면 asmx 측면에서 웹 서비스에 적합합니다. .NET Framework Tools 문서의 Soapsuds Tool  에서 Soapsuds 유틸리티에 대해 자세히 설명합니다.
  • 인터페이스를 별도 라이브러리에 선언하고 이 라이브러리를 클라이언트에 배포합니다. 인터페이스를 구현하는 서버 클래스를 게시하면 클라이언트는 이를 사용해 구현하는 인터페이스에 대한 프록시를 얻을 수 있습니다. 이는 구체적인 인터페이스로서 명확한 설계를 위한 지침이 되므로 매우 중요합니다. 이 방법에서는 인터페이스의 인스턴스를 만들 수 없으므로 서버 활성 개체를 위해서만 사용될 수 있습니다(제품 기능 절 참조).
  • 클라이언트를 위해 메타데이터 역할을 하도록 SOAPSUDS를 사용해 대리 클래스를 작성합니다. Remoting 서버 어셈블리에 대해 SOAPSUDS를 실행할 수 있으며 메타데이터로서 즉시 사용이 가능한 출력 어셈블리나 응용 프로그램에 즉시 포함이 가능한 원본 파일을 생성할 수 있습니다. 이 메커니즘은 하나의 계층 안에 있는 개체가 다른 계층에 있는 원격 개체에 액세스하기를 원하는 다중 계층 응용 프로그램 작성에 유용합니다. 이는 매우 흥미로운 방법으로서 위 소개 절에서 언급한 다중 계층 응용 프로그램에서 사용됩니다.

다음 폴더에 대한 명령 창이 열려 있다고 가정해 봅시다.

$FRAMEWORKSDK\Samples\Technologies\Remoting\Basic\RemotingHello\Service

다음과 같이 작성할 수 있습니다. soapsuds -id:. -types:Hello.HelloService,Hello -oa:HelloInterface.dll

이렇게 하면 출력 어셈블리 HelloInterface.dll을 생성하는데 여기에는 현재 디렉터리의 Hello 어셈블리에서 찾은 Remoting 서버 Hello.HelloService에 대한 메타데이터가 들어 있습니다. 이 어셈블리는 즉시 클라이언트가 사용할 수 있습니다. Remoting 서버 위치는 표준 Remoting 구성 당 런타임에서 제공되는 구성 데이터를 기반으로 파생됩니다. 클라이언트 어셈블리를 위해 생성된 MSIL,

ldfld object [System.Runtime.Remoting]System.Runtime.Remoting.Services.RemotingClientProxy::_tp

는 Remoting 서버 구현을 사용하지 않고 SOAPSUDS로 생산된 메타데이터를 사용해 제작된 프록시 클래스를 사용하고 있음을 분명히 보여 줍니다.

SOAPSUDS는 Binary 포매팅과 작동하도록 보장/지원되지 않습니다. 출력 어셈블리 메타데이터에 있는 특정 SOAP "스텁"의 일부를 포함/매핑합니다.

Remoting 인터페이스는 가능한 한 단순하게 유지하는 것이 좋습니다. "chunky" 대 "chatty" 인터페이스를 사용하십시오. 즉 설계에서 원격 호출의 수를 제한하십시오. 이렇게 하면 어떤 상황에서 매개 변수를 중복해서 전달하게 할 가능성이 있습니다. 원격 인터페이스를 실제 구현으로부터 별도의 클래스에 유지하십시오. 이렇게 하면 필요할 경우 Remoting 계층을 쉽게 다른 기술로 대체할 수 있는 "facade" 유형 패턴을 사용할 수 있습니다.

오류 관리

이 절에서는 Remoting 솔루션 개발 및 사용 중 만날 수 있는 여러 가지 오류 시나리오에 대해 설명합니다. 모든 경우에 표준 계측 및 모니터링 방법이 여전히 적용된다는 것을 기억하는 것이 좋습니다. 이벤트 로그는 네트워크 모니터 등의 도구처럼 여전히 중요한 정보원입니다. 네트워크 모니터는 특히 클라이언트 / 서버 Remoting 대화를 자세히 살펴보는 데 사용될 수 있습니다. 중간 계층 Remoting 서버는 Visual Studio .NET과 함께 제공되는 표준 디버깅 도구를 사용하여 여전히 디버그할 수 있습니다. 예를 들어 Remoting 서버가 IIS로 호스트되는 곳에서 중단점은 디버그 세션을 ASP.NET 작업자 프로세스에 연결함으로써 설정될(소스 제공 가능) 수 있습니다(Visual Studio .Net | Debug | Processes | Attach). 그러나 Remoting은 자체의 고유한 오류 집합을 가지고 있으며 다음은 이 중 일부에 대한 설명입니다. 참고로 모든 오류는 .NET Framework SDK와 함께 제공되는 Basic Remoting Hello Sample 버전을 사용하여 발생시킨 것입니다. 서버와 클라이언트는 하나의 컴퓨터에서 실행되었습니다. 네트워크로 연결된 경우에도 증상은 같지만 http / TCP 제한 시간 설정때문에 실패하기까지 시간이 조금 더 걸립니다.

MarshalByRef 누락

참조에 의한 Remoting이 주어진 클래스를 위해 일하려면 해당 클래스는 한 가지 작업만 해야 하며 이는 MarshalByRefObject로부터 상속받은 것이어야 합니다. 개발자가 이것을 잊어버렸다고 가정해 봅시다. 그러면 System.Runtime.Remoting.RemotingException 예외 유형이 발생하며 여기에서 'Missing MarshalByReference'가 있음을 알려줍니다.

RemotingException을 발견하고 올바르게 처리하는 것은 프로그래머에게 달려 있습니다. 이 개발자는 기억해야 할 한 가지 사실을 잊었다는 것을 상기하십시오.

MarshalByRefObject로부터 상속 받는 것이 해결 방법임을 기억하십시오.

잘 알려진 서버 활성화에 대한 잘못된 서버 종점

서버 활성화를 위해(제품 기능 절 참조) Remoting 서버는 수신하고 있는 종점을 선언합니다. 종점은 일반적으로 개체 URI(원격 개체의 잘 알려진 이름), 프로토콜 및 포트 번호를 포함합니다. 물론 이들은 모두 올바르게 구성될 수 있습니다.

잘못된 URI

Basic Remoting Hello Sample 서비스가 제공하는 해당 URI는 HelloService.soap이며 관련된 web.config 파일 내에서 다음과 같이 지정됩니다.

<configuration>
  <system.runtime.remoting>
    <application>
      <service>
        <wellknown mode="SingleCall" type="Hello.HelloService, Hello"
                   objectUri="HelloService.soap" />
      </service>
    </application>
  </system.runtime.remoting>
</configuration>

이 서비스는 IIS로 호스트됩니다. IIS 호스팅은 접미사로 .rem 또는 .soap를 사용하는 URI가 필요합니다. 서버에서 .rope를 사용해 봅시다. 이 경우 RemotingException이 발생하며 'Object </Hello.soap> has been disconnected or does not exist at the server'라는 메시지가 표시됩니다.

URI이 일치하는지 확인하십시오. IIS가 Remoting 서버를 호스트할 경우 URI는 .rem 또는 .soap로 끝나야 합니다.

불일치하는 프로토콜 / 포트

이 테스트를 위해 콘솔 호스트 서버로 전환하겠습니다. 구성 파일은 다음과 같습니다.

<configuration>
  <system.runtime.remoting>
    <application name="RemotingHello">
      <service>
        <wellknown mode="SingleCall" type="Hello.HelloService, Hello"
                   objectUri="HelloService.soap" />
      </service>
      <channels>
        <channel ref="http" port="8000" />
      </channels>
    </application>
  </system.runtime.remoting>
</configuration>

서버는 프로토콜을 TCP로 변경하고 클라이언트는 http를 사용하게 둔다고 가정해 봅시다.

다시 RemotingException이 발생합니다. 이번에는 'The underlying connection was closed: An unexpected error occurred on a receive'라는 메시지가 표시됩니다.

포트를 잘못 사용해도 똑같은 예외가 발생하며 유일한 차이점은 실패하는 데 좀 더 시간이 걸린다는 것입니다. 서버와 클라이언트에서 포트와 프로토콜은 일치해야 합니다.

URI 누락

또 다른 가능성은 원격 서버가 실행되고 있지 않는 경우입니다. 예를 들어 서버는 IIS로 호스트되는데 가상 응용 프로그램이나 관련 어셈블리가 누락된 것입니다. Basic Hello Remoting 서버를 다시 한번 사용하여 가상 응용 프로그램 RemotingHello가 실행되기를 기대하지만 그렇지 않을 경우 호출 코드에 따라 처리되지 않은 예외를 받게 됩니다. 이번에는 'Cannot load type clr:Hello.HelloService, Hello'라는 예외가 표시됩니다.

이런 경우 가상 응용 프로그램이 실행 중이며 필요한 어셈블리가 관련 bin 하위 폴더에 올바르게 위치해 있는지 확인합니다.

요컨대 서버에서 정의되는 종점은 서버 활성화를 위해 클라이언트에서 올바르게 참조되어야 하며 따라서 포트, 프로토콜 및 URI이 모두 일치해야 합니다. 이와 관련해 실수하기가 매우 쉽습니다. 따라서 서버 위치는 다음 예와 같이 정의됩니다.

<service>
   <wellknown mode="SingleCall" type="Hello.HelloService, Hello" 
              objectUri="HelloService.soap" />
</service>

그리고 클라이언트 설정은 다음과 같이 합니다.

<client url="http://localhost/RemotingHello">
   <wellknown type="Hello.HelloService, Hello" 
              url="http://localhost/RemotingHello/HelloService.soap" />
</client>

여기서 URL은 Remoting 서비스를 호스팅하는 IIS 가상 응용 프로그램을 표시하며 유형은 클래스와 어셈블리 이름을 표시합니다.

Remoting 및 ASP.NET 웹 서비스

IT 설계에서 가장 좋으면서 동시에 가장 나쁜 것 중의 하나는 선택할 수 있는 구조적 구성 요소가 매우 많을 경우입니다. 웹 서비스와 .NET Remoting도 이 경우에 해당하며 간혹 어떤 목적으로 어떤 기술을 사용할지를 결정하기가 어려울 때가 있습니다. 물론 정답은 문제 해결에 최적인 기술을 선택하는 것입니다. “항상 웹 서비스를 사용해라”든지 “웹 서비스는 Remoting의 하위 집합이니 Remoting이 최선이다" 등의 조언은 듣지 않는 것이 좋습니다. 이 절에서는 특정 시나리오에서 어떤 기술을 왜 선택해야 하는지에 관련해서 두 기술을 비교해 보겠습니다.

ASP.NET 웹 서비스 vs. .NET Remoting

웹 서비스란 '웹 상에서 사용 가능한 서비스다'라는 정의를 먼저 살펴봅시다. 별 도움이 되지 않는 정의지요? 이를 "SOAP와 HTTP를 통해 액세스되며 어드레스할 수 있는 처리 단위로서, 이 처리 단위는 WSDL을 통해 기술되고 UDDI를 통해 게시될 수 있다."라고 정의해 볼 수 있을 것입니다 이 정의는 HTML을 브라우저로 되돌려 보내는 웹 서버와 웹 서비스를 구분시키므로 좀 더 유용합니다. .NET Remoting과의 비교를 위해 웹 서비스의 정의를 웹 상에서 사용 가능한 프로그램 방식의 서비스라는 점에 특별히 중점을 두겠습니다. 정의에 따르면 예를 들어 WSDL을 사용해 클라이언트에서 HTTP로 액세스할 수 있는 원격 호스트가 웹 서비스입니다. 이를 염두에 두고 Microsoft ASP.NET 웹 서비스 구현에 중점을 두어 분산 솔루션에서 .NET Remoting 대신 ASP.NET 웹 서비스를 "접착제"로서 선택할 때 어떤 요소를 고려해야 할까요?

상호 운용성

Microsoft에서는 다른 유형의 시스템 간에 상호 운용성이 필요한 경우 개방형 표준(SOAP, XML, HTTP)을 사용하는 웹 서비스가 올바른 선택이며 .NET Remoting은 결코 상호 운용성을 위해 좋은 솔루션이 아니라고 반복해서 언급하고 있습니다. 모든 참가자가 CLR로 관리되는 유형이 같은 시스템에서는 .NET Remoting이 올바른 선택일 수도 있습니다. 꽤 광범위하지만 좀 구분해 두는 것이 유용하겠습니다. .NET 원격 개체의 클라이언트는 .NET의 클라이언트여야 합니다. 느슨하게 연결된 SOAP 클라이언트(예: Unix 프로세스)가 웹 상에서 여러분의 기능을 어드레스할 수 있어야 한다면(웹, 여기서는 인터넷으로) 웹 서비스가 올바른 선택일 것입니다. 인트라넷에서는 당연히 이와 같은 제약이 없습니다. 즉 모든 클라이언트는 .NET 클라이언트일 수 있으며 이 구성에는 .NET Remoting도 포함됩니다. 이와 유사하게 중간 (응용 프로그램) 계층이 방화벽 뒤에 있고 웹 계층과 직접 통신하는 환경에서도 .NET Remoting이 고려될 수 있습니다.

강한 유형 지원

.Net Remoting은 모든 관리 유형, 클래스, 인터페이스, 열거, 개체 등을 지원합니다. 이는 흔히 "풍부한 유형 충실도"로 분류됩니다. 여기서 핵심은 클라이언트 및 서버 구성 요소 모두 응용 프로그램 도메인에서 실행되는 CLR 관리 개체이며 데이터 형식과 관련된 상호 운용성은 중요하지 않다는 점입니다. 근본적으로 폐쇄형 시스템을 가지고 있습니다. 대화 양단은 서로 완벽히 이해되며 따라서 통신하는 데 사용하는 데이터 및 개체 유형과 관련해 이 점을 이용할 수 있습니다.

이기종 시스템이 있을 경우 이런 시스템 사이의 상호 운용성을 고려해 보아야 합니다. 상호 운용성 있는 데이터 유형과 관련해서는 신중을 기해야 합니다. 예를 들어 웹 서비스 데이터 형식 정의는 데이터 형식 의미론에 있어서 XSD(XML Schema Definitions)에 의존합니다. XSD를 사용하여 기술되고 SOAP에서 상호 운용되는 모든 데이터 형식을 사용할 수 있습니다. 그러나 일부 데이터 형식의 사용은 제외되는데 예를 들어 unsigned char나 열거형에 대한 W3C XSD 표시는 없으며 컬렉션은 예외나 데이터 집합과 같이 다른 웹 서비스 구현에서 다르게 처리됩니다. 또 하나의 문제는 내부 필드와 속성이 웹 서비스 호출을 통해 전달되지 않는다는 것입니다. 이는 자체적으로는 심각한 문제는 아니지만 다른 기술에서도 상호 운용이 필요한 시스템을 설계하고 테스트할 때에는 고려해봐야 할 요소입니다. 왜냐하면 송신이 가능하다고 수신을 보장하는 것은 아니기 때문입니다.

다른 유형의 시스템 간에 상호 운용성이 필요한 경우에는 이를 해결할 기술로 .NET Remoting을 고려해서는 안 됩니다. 폐쇄형 CLR 관리 솔루션에서는 고려해 볼 수도 있습니다.

상태 관리

클라이언트 활성화든 Singleton이든 활성화 모드를 기반으로 하는 .Net Remoting을 사용해 상태 관리를 수행하는 여러 가지 방법을 이미 살펴봤습니다. HTTP(제한 시간 없는 상태 비저장 프로토콜)로 클라이언트당 연결 상태를 관리하는 것은 .NET Remoting과 웹 서비스 모두에게 부담스럽고 비실용적인 방법입니다. 그럼에도 불구하고 상태를 유지해야 할 경우 Remoting은 각 개체별로 솔루션을 제공합니다. 웹 서비스는 이 클라이언트당 연결 상태 관리를 제공하지 않지만 ASP.NET 세션과 응용 프로그램 개체에 대한 연결은 여전히 제공합니다.

수명 관리

상태 관리와 관계된 것이 수명 관리입니다. 이미 살펴본 대로 Remoting은 원격 개체의 수명 관리를 위해 기능적으로 우수한 메커니즘을 제공합니다. 웹 서비스 개체는 웹 서비스 호출에 따라 오고 갑니다. 개념적으로 동기와 비동기 모두에 해당됩니다. 이런 점에서 웹 서비스는 Remoting과 비교해 볼 때 단일 호출 형식으로 수행됩니다. Remoting은 원격 개체의 활성화와 제거에 대해 훨씬 강력한 통제를 제공합니다. 이는 여러분 설계와 관련이 있을 수도 있고 없을 수도 있습니다.

값 호출 vs. 참조 호출

웹 서비스 호출로 전달된 개체는 일련화되어 값에 의해 전달됩니다. Remoting에 전달된 개체나 호출된 개체 자신은 값 또는 참조에 의해 전달될 수 있습니다. 일련화된 원격 개체 메서드는 클라이언트에서 처리됩니다. Remoting이나 웹 서비스를 선택할 때에는 이러한 의미론적 차이를 고려해야 합니다. 이와 같은 고려 사항은 여러분이 해결할 문제의 성격에 따라 중요할 수도 있고 중요하지 않을 수도 있습니다.

지원되는 프로토콜

웹 서비스 호출은 HTTP를 통해 SOAP로 인코딩된 XML에 제한됩니다. Remoting이 TCP 전송 모드를 사용하거나 인프라가 사용자 지정 프로토콜을 지원하도록 확장시킬 수 있습니다. 예를 들어 명명된 파이프를 사용한 Remoting 구현은 www.gotdotnet.com, jhawk 사용자 샘플 절에서 볼 수 있습니다.

다음은 NamedPipe 추가 정보에서 Remoting의 확장성을 보여 주는 예입니다.

플러그형 채널 아키텍처를 사용해 IChannel* 인터페이스를 구현함으로써 채널을 .NET Remoting에 연결합니다.
명명된 파이프 채널은 다음 기능을 지원합니다.
* 명명된 파이프를 통한 통신
* 동기식 메시지
* 비동기식 메시지
* 일방형 메시지
* 콜백
* 채널 싱크
* 채널 속성
* 파이프 이름 자동 생성

명명된 파이프가 필요할 경우 Remoting은 솔루션을 제공합니다. 그러나 SSPI NTLM 인증 솔루션과 마찬가지로 이 솔루션은 현재 Microsoft에서 지원하지 않습니다. Microsoft는 조만간 이 요구 사항을 해결할 것으로 보입니다.

성능

설계에서 성능이 중요할 경우 Remoting은 TCP에서 이진 메시지 형식을 사용해 몇 가지 중요한 성능상의 혜택을 제공합니다. 이 기사에 설명된 결과를 낳는 데 사용된 환경은 기사 Performance Comparison: .NET Remoting vs. ASP.NET Web Services에 모두 설명되어 있습니다.

다음은 위 기사로부터 요약한 성능 통계입니다.

범례: ASMX - 웹 서비스, 기타 모든 Remoting 솔루션
WS는 원격 구성 요소를 호스트하는 Windows 서비스를 지칭

그림 1. 성능 통계

기사에서는 성능과 관련한 수치에 대해서 다음과 같이 설명합니다.

“위에서 보여 주듯이 Windows 서비스의 역할을 하는 호스트와 함께 WS_TCP_Binary는 개체가 TCP 채널과 Binary 포맷터를 사용하도록 구성됩니다. 이는 다른 분산 기술보다 성능이 뛰어납니다. 이런 이유는 이진 데이터를 HTTP보다 더 효율적인 원시 TCP 소켓을 통해 전송하기 때문입니다. 데이터를 인코딩/디코딩하지 않아도 되기 때문에 처리 오버헤드가 더 적습니다. WS_TCP_Binary와 가장 느린 방법 사이에 60% 정도의 성능 차를 볼 수 있습니다.
IIS_HTTP_Binary에서는 WS_HTTP_Binary와 같이 동일한 이진 페이로드를 생성하지만 IIS(Inetinfo.exe)에서 Aspnet_wp.exe로 추가 프로세스 홉이 있기 때문에 더 느립니다. 같은 이유로 IIS_HTTP_SOAP와 WS_HTTP_SOAP 사이에 성능 차가 생깁니다.
WS_HTTP_Binary 및 WS_TCP_SOAP는 매우 유사한 성능을 제공합니다. 전자에는 HTTP를 구문 분석하는 추가 오버헤드가 있고 후자에는 SOAP를 구문 분석하는 추가 오버헤드가 있지만 이 경우 HTTP 구문 분석과 SOAP 구문 분석의 오버헤드는 같습니다.
ASP.NET XML 일련화는 .NET Remoting SOAP 일련화보다 더 효율적이기 때문에 ASP.NET 웹 서비스는 IIS_HTTP_SOAP 및 WS_HTTP_SOAP보다 성능이 더 좋습니다. 위에서 보여 주듯이 ASP.NET 웹 서비스는 성능면에서 IIS_HTTP_Binary와 매우 유사합니다.”

원시 속도가 최우선 순위일 경우 이 “60% 성능 차”는 중요할 수 있습니다. 여기서 단점은 TCP 프로토콜을 사용하기 위해 Windows 서비스 내에서 서버를 호스트해야 한다는 점입니다. 위의 원격 호스팅 절을 참조하십시오. 실제로 이는 보안과 성능을 맞바꾸는 것으로서 "인터넷이나 안전하지 않은 인트라넷에서는 권장되지 않는” 방법입니다.

요약

ASP.NET 웹 서비스는 XML 기반으로서 실용성을 위해 HTTP 사용을 필요로 하며(IIS로 호스트될 경우) 단순한 프로그래밍 모델과 강력한 상호 플랫폼 지원을 제공합니다. 이 서비스는 SoapExtensions를 사용하여 어느 정도의 확장성을 보장합니다(예: 데이터 스트림 암호화). Remoting은 좀 더 복잡한 프로그래밍 모델이지만 유형 충실도, 상태 관리 및 프로토콜과 메시지 형식의 확장성 면에서 분명히 장점을 제공합니다. Remoting은 .NET 클라이언트 외의 클라이언트는 사용할 수 없으므로 인트라넷 클라이언트는 원격 호스트로 직접 연결할 수 없습니다. Remoting은 IIS 외부에서 호스트될 경우에는 안전한 모델이 아닙니다. IIS로 호스트된 Remoting은 SSL 등의 보안 프로토콜 사용을 포함해 ASP.NET과 동일한 보안 기능을 제공합니다. 다른 플랫폼과의 상호 운용성을 고려할 필요가 없고 클라이언트와 서버 구성 모두 완전히 통제 가능한 경우에는 .NET Remoting을 고려해 보십시오. Remoting을 사용할 때에는 관련 보안과 스케일 가능 인프라의 장점을 얻을 수 있도록 비 IIS 호스팅보다는 HTTP 채널을 사용하는 IIS 호스팅을 선택하십시오. 이와 같이 하려면 물론 여러분은 자신의 솔루션으로 IIS를 통제할 수 있는 위치에 있어야 합니다. 이것이 불가능하다면 Remoting은 관리하기에 너무 힘들 수도 있습니다. 이 점은 해결할 문제의 특성에 따라 달라집니다. .NET Remoting은 .NET 클라이언트를 필요로 하므로 가장 빠른 포맷터를 사용해야 합니다. 따라서 SOAP보다 좀 더 나은 성능을 제공하는 Binary 포맷터를 선택하십시오. 위의 최적의 사용 절에서는 이 포맷터를 개발 단계가 아닌 릴리즈 시에 적용하도록 권고했습니다.

요약 내용

.NET Remoting은 특정 유형의 분산 솔루션에서 채택할 수 있는 유용한 도구입니다. 지원할 수 있는 프로토콜과 메시지 형식 면에서 확장성 있는 모델이며 특정 시나리오에서 우수한 성능을 제공합니다. .NET Remoting은 인트라넷에 직접 배포할 수는 없으며 서버 개체는 IIS로 호스트해야 IIS 통제 내에서 실행되는 프로세스에 ISS가 제공하는 보안 및 성능 혜택을 얻을 수 있습니다.

Remoting은 클라이언트와 서버 모두 CLR로 관리되는 프로세스인 “폐쇄형” 분산 솔루션을 대상으로 고려해봐야 합니다. IPSec 등의 보안 TCP 채널이나 HTTP를 통해 인트라넷 솔루션 내의 어떤 계층에 있는 구성 요소, 방화벽을 통해 .NET 웹 계층과 대화하는 중간 계층 응용 프로그램 구성 요소를 예로 들 수 있습니다. 이 경우 SOAP 포맷터를 사용해 솔루션을 증명하고 나면 HTTP 채널을 Binary 포맷터와 함께 선택해야 합니다.

CLR이 아닌 클라이언트와의 상호 운용성이 필요한 시스템에는 데이터 형식과 관련한 일부 위험을 방지해 주는 ASMX 웹 서비스를 사용하십시오. 강력한 유형 지원 절을 참조하십시오.

TCP를 사용한 IIS 외부 호스팅에는 성능상의 혜택이 있지만 대신 사용자 지정 보안이 필요합니다.

설계 및 구현

Remoting의 구현과 구성은 비교적 쉽습니다. 먼저 Remoting 호스트, 프로토콜 및 활성화 모델을 선택해야 합니다. 설계와 구현을 가능한 한 단순하게 유지하는 한편 어떤 인터페이스 게시 메커니즘이 여러분 솔루션에 가장 적합할지를 신중히 고려하십시오. 권장되는 방법은 인터페이스만 게시하는 것으로 가장 깔끔한 개념적 모델이지만 클라이언트 활성화 개체에는 사용할 수 없습니다. 디버거, 이벤트 로그 및 네트워크 모니터는 모두 유용한 개발 도구이며 이들 모두 원격 구성 요소 개발에 도움이 됩니다.

Remoting의 미래

Remoting은 언제 사용하고 웹 서비스는 언제 사용하느냐와 같은 질문은 용어 정의가 명확하지 않다는 점에서 어려운 질문입니다. 웹 서비스의 정의가 불분명한 한편 Remoting을 웹 서비스로 기능하도록 구성할 수 있기 때문입니다.

앞으로 Remoting과 ASMX 기술은 병합될 것으로 보입니다. 현재로서는 위에서 설명한 것과 같이 어떤 기술을 언제 사용할지에 대한 적당한 그림을 보여줄 뿐입니다.

현재 개발은 라우팅, 보안 및 트랜잭션을 지원하는 GXA 구현에 주력하고 있습니다. 이 지원은 SOAP 헤더의 사용을 기반으로 하여 현재 웹 서비스의 기능 확장을 목표로 하고 있습니다. 이 기사에서 설명한대로 고전적인 의미에서 GXA가 .NET Remoting에 접근하지는 못하지만 상태 관리, 트랜잭션 관리 등과 같은 많은 문제(일부는 문제 해결을 위해 Remoting을 선택하겠지만)를 해결해 줍니다. 현재 GXA 구현으로 웹 서비스가 당면한 많은 문제를 해결하기 시작했지만 가능한 한 기술 면에서 불가지론적인 태도로 이러한 문제를 해결하는 것이 원래 목표입니다. GXA의 발전이 웹 서비스와 .Net Remoting 모두에 끼치는 영향을 지켜보는 것은 매우 흥미로울 것입니다.