라이브러리
프로그래밍언어, 각종코드, 관련동향, 논문 등의 저장소
[기업 환경에 닷넷 적용하기] ② 비즈니스 레이어 설계·구현

[기업 환경에 닷넷 적용하기] ② 비즈니스 레이어 설계·구현

 

연재순서
1.제대로 된 컴포넌트를 위한 프리젠테이션 레이어 설계하기
2.비즈니스 로직 레이어 설계와 물리적 구현을 위한 필요 사항
3.엔터프라이즈 솔루션 패턴 해부하기
4.닷넷의 웹 프리젠테이션 패턴
5.닷넷 개발에서 UML과 케이스 툴의 사용

 

기업용 응용 프로그램뿐 아니라 모든 컴포넌트 기반 프로그래밍에서 가장 중요한 비중을 차지하는 계층이 비즈니스 계층이다. 데이터나 프리젠테이션 계층의 컴포넌트는 응용 프로그램의 형태에 따라 존재하지 않을 수도, 프로세스가 해당 계층을 거치지 않고도 동작할 수 있다.

하지만 비즈니스 계층의 컴포넌트는 어떠한 상황에서 실행되는 프로세스라도 결코 피해갈 수 없다. 비즈니스 계층의 컴포넌트를 설계하기 위해서는 '상태가 없는(Stateless) 동작으로서의' 의미로 비즈니스를 인식하는 것이 중요하다. 이번 글에서는 엔터프라이즈 솔루션 아키텍처에서 비즈니스 계층을 구성하는 컴포넌트를 제대로 설계하기 위한 여러 권장사항과 설계 기법들을 알아본다.

프로젝트가 진행될 때 가장 크게 고려되는 사항은 ‘비즈니스 로직을 어떻게 설계할 것인가?’이다. 특히 프리젠테이션 계층에서 제공하는 사용자 컴포넌트들이 풍부한 닷넷 환경에서는 닷넷 프레임워크가 제공하는 컴포넌트들을 거의 그대로 사용하고, 요구사항 분석에 따른 업무의 흐름을 담당하는 비즈니스 로직 계층을 어떻게 구성할 것인가가 주 관건이 된다.

MVC를 확연하게 구분할 수 있는 환경을 제공하는 프리젠테이션 계층과 달리, 닷넷에서는 비즈니스 로직 계층을 구성할 수 있는 어떤 자동화된 방법이나 구조를 특별히 제공하지는 않는다. 그렇다면 비즈니스 로직 계층에 존재하는 컴포넌트들을 어떻게 개발해야 하는지가 프로젝트의 성패를 가늠하는 가장 중요한 문제가 되는데, 이럴 때 컴포넌트를 ‘어떻게’ 구성해야 하는지에 대한 훌륭한 해답이 바로 닷넷 엔터프라이즈 아키텍처이다.

닷넷 엔터프라이즈 솔루션 아키텍처 기반에서 비즈니스 로직 계층 컴포넌트들의 구성과 데이터 액세스 계층에서 컴포넌트의 구성을 알아보자.

비즈니스 계층 컴포넌트의 구성
사실상 기업용 응용 프로그램의 핵심은 응용 프로그램이 제공하는 비즈니스 기능이라 할 수 있다. 기업용 응용 프로그램의 존재 이유는 바로 이런 비즈니스들을 어떻게 자동화할 것인가에 있다고 해도 과언이 아니다(사실 컴포넌트의 공식 명칭은 ‘비즈니스 컴포넌트’이다). 3-티어 구조의 응용 프로그램에서 비즈니스 컴포넌트 계층에 존재하는 컴포넌트들은 프리젠테이션 계층의 사용자 프로세스 컴포넌트나 컨트롤러의 호출로 일단의 프로세스를 수행한다. 닷넷 엔터프라이즈 솔루션 아키텍처에서 비즈니스 컴포넌트 계층 컴포넌트는 <그림 1>과 같은 구조로 구성된다.

사용자 삽입 이미지
<그림 1> 비즈니스 계층 컴포넌트

<그림 1>에서 알 수 있듯이 비즈니스 계층은 굳이 나누자면 세 종류의 역할을 하는 컴포넌트들로 구성된다. 비즈니스 엔티티 역할을 하는 컴포넌트는 표현되는 데이터를 포함하는 객체들로 이뤄진다. 비즈니스 컴포넌트 역할을 하는 컴포넌트는 비즈니스 엔티티 컴포넌트에 포함된 객체를 생성하고 동작들을 지정하며, 데이터 액세스 로직 컴포넌트를 호출하는 동작과 비즈니스 엔티티 객체의 상태를 변경하는 등의 오퍼레이션을 담당한다. 비즈니스 워크플로우 역할을 하는 컴포넌트는 분산 트랜잭션 또는 분산 시스템에서 프로세스의 수행이 완료될 때까지 비즈니스 작업을 조율하고 상태를 저장하는 등의 여러 동작을 수행한다. 서비스 인터페이스는 당연히 GOF 디자인 패턴에서의 전형적인 Facade 역할을 담당한다.

<그림 1>의 비즈니스 계층에서 계층 구성이 가장 단순한 경우를 생각해 보자. 비즈니스 엔티니 컴포넌트와 비즈니스 워크플로우 없이 비즈니스 컴포넌트만 사용하는 경우다. 닷넷 환경이 도입되기 이전의 ASP 개발 환경에서 즐겨 사용하던 방법으로서, 비즈니스 엔티티 컴포넌트로 ADO가 제공하는 레코드셋 등의 개체를 사용한다. 비즈니스 컴포넌트에는 데이터베이스에 액세스하여 레코드셋을 만들어 내고, 프리젠테이션 계층 역할을 하는 ASP 페이지에 레코드 셋을 넘겨줘 페이지에 보이게 하는 경우였다.

닷넷 프로그래밍이 국내에 처음 소개되던 무렵에는 비즈니스 컴포넌트를 COM 개체가 아닌 닷넷 어셈블리로 컴파일하여 사용하는 경우가 많았다. 필자 역시 웹 개발에서 레지스트리에 등록된 COM 개체를 유지 보수하기 위해 IIS를 내렸다 올렸다 했던 환경에서 운영 환경에 특정 컴포넌트를 물지 않는 닷넷 환경의 편리함에 감탄한 적이 있다.

가장 단순한 경우의 컴포넌트 구성이 반드시 틀렸다는 것은 아니다. 항상 주장하는 말이지만 닭 잡는 데는 닭 잡는 칼을 써야 하고 소 잡는 데는 소 잡는 칼을 써야 한다. 간단한 구조의 응용 프로그램이라면 이와 같이 구성하는 것이 개발 사이클이나 유지 보수성 등의 측면에서 훨씬 뛰어날 수도 있다. 하지만 기업용 응용 프로그램이라면 이야기가 달라진다. 세션 또는 닷넷에서 제공하는 추상화된 개체인 HttpContext, HttpModule 등을 사용하여 사용자를 관리하고, 수시로 변하는 데이터를 수많은 클라이언트가 열고 있는 경우라면 어떨까? 앞에서 이야기한 가장 간단한 형태의 컴포넌트 구성은 개발 당시의 생산성 측면에서나 유지 보수성, 성능 등의 모든 면에서 복잡도가 증가하게 된다.

가장 복잡한 경우는 여러 서비스를 이용하거나 분산 시스템 기반에서 응용 프로그램을 작성하는 것이다. 수없이 많은 클라이언트들이 응용 프로그램을 사용하고 분산 트랜잭션과 분산 서비스에서 비즈니스 워크플로우를 제어해야 한다면 비즈니스 엔티티 컴포넌트는 물론, COM+나 비즈토크 오케스트레이션(Biztalk Orchestration) 등을 사용하는 비즈니스 워크플로우를 모두 구성하는 것이 바람직하다. 물론, 모든 컴포넌트에는 서비스 인터페이스가 존재하여 프리젠테이션 계층 컴포넌트에서 직접 호출을 추상화하여 제공하는 창구가 필요하다.

단순하게 생각해 보자
새로운 기술이나 이론 등을 접할 때 가장 당황스러운 것은 새로운 용어를 접할 때이다. <그림 1>에서 등장하는 새로운 용어들로 인하여 장애를 느끼는 독자라면 <그림 2>를 보자. 컴포넌트의 구성은 그 컴포넌트가 어떠한 일을 해야 하는지를 결정하는(그 역할에 따라 구성하는) 것이 가장 바람직하다. <그림 2>는 비즈니스 계층의 컴포넌트들이 어떠한 역할을 해야 하는지를 잘 설명한다.

사용자 삽입 이미지
<그림 2> 비즈니스 계층 컴포넌트들의 역할

응용 프로그램 망/흐름 제어로 설명된 비즈니스 워크플로우 프로세스의 역할부터 알아보자. 이 역할을 하는 컴포넌트는 가장 쉽게 생각해 여러 대의 다른 역할을 하는 엔터프라이즈 서버로 구성됐거나 다른 도메인에 존재하는 다른 기업 간의 프로세스 흐름에서(B2B 또는 EAI 프로젝트에서) 그 망을 구축하고 프로세스의 흐름을 제어한다. 언뜻 분산 처리를 담당할 수 있는 COM+와 MSDTC를 떠올릴 수 있지만 COM+ 또는 MSDTC는 동작의 역할을 하는 비즈니스 컴포넌트에서 사용되고(물론 COM+ 또는 MSDTC가 이런 역할을 할 수 없다는 것은 아니다. 하지만 엔터프라이즈 솔루션 아키텍처에서 비즈니스 워크플로우는 그런 분산 트랜잭션보다는 더 넓은 의미에서 비즈니스 워크플로우를 포함한다), 주로 이 역할을 담당하게 되는 것은 비즈토크 오케스트레이션이다.

<그림 2>에서 동작으로 설명된 비즈니스 컴포넌트의 가장 주된 역할은 데이터를 받아들이고 반환하는 것이다. 비즈니스 컴포넌트는 캡슐화를 이용해 구현되는 높은 추상을 가지는 응용 프로그램의 동작을 구현한다. 비즈니스 컴포넌트를 통해 프리젠테이션 계층에서 응용 프로그램을 사용하는 사용자가 어떤 데이터 저장소를 사용하는지, 어떤 서비스를 이용하여 데이터를 처리하는지 등의 정보를 캡슐화해 좀 더 편리한 프로그래밍 인터페이스를 제공한다.

중요한 것은 비즈니스 컴포넌트는 상태(state)를 가져서는 안 되며, 단순히 동작만을 제어해야 한다는 것이다. 프리젠테이션 계층의 컨트롤러나 사용자 프로세스 컴포넌트에서 호출되는 비즈니스 컴포넌트는 그 사용이 매우 빈번하고 복잡한 프로세스의 흐름을 가질 수 있으므로 비즈니스 컴포넌트가 특정 상태를 가지고 프로세스를 제어한다면 컴포넌트를 여러 사용자가 공유할 수 없게 된다.

데이터는 반드시 각 사용자 별로 유지돼야 하므로 비 연결 지향의 ADO.NET 개체를 주로 사용하는 비즈니스 엔티티 컴포넌트가 상태를 가질 수 있거나 또는 반드시 가져야 하는 것과는 달리, 비즈니스 컴포넌트는 상태를 가질 수 없도록 설계돼야 한다. 비즈니스 컴포넌트는 데이터의 제어를 위한 동작이 주가 됨으로, 트랜잭션을 시작하고 관리할 수 있는 역할을 반드시 할 수 있어야 한다. 대부분의 응용 프로그램에서 트랜잭션의 시작은 주로 비즈니스 컴포넌트 역할의 개체들이 담당하게 되며, SQL 서버나 ADO.NET의 트랜잭션 지원 개체들이 트랜잭션 제어를 위해 사용되고, 분산 서버 환경이라면 엔터프라이즈 서비스를 이용하여 COM+와 MSDTC를 사용하게 된다.

<그림 2>에서 데이터로 표현된 비즈니스 엔티티는 데이터를 표현하는데 사용된다. 비즈니스 엔티티 컴포넌트의 인스턴스는 비즈니스 컴포넌트의 호출로 생성되며, 호출된 비즈니스 컴포넌트의 트랜잭션이 종료되기 전까지 상태를 서버 또는 클라이언트에서 유지하면서 사용된다. 비즈니스 엔티티 컴포넌트가 데이터를 표현하는 것이므로 닷넷 기반 프로그래밍에서는 ADO.NET이 제공하는 여러 포맷, 즉 데이터 리더, 데이터 셋, 형식화된 데이터 셋, XML 문서 등을 주로 사용할 수 있다. 물론 프로그래머가 직접 디자인한 사용자 정의 개체도 사용될 수 있다.

가장 권장되는 방법은 모든 데이터를 내부에 캡슐화하거나 데이터에 대한 특정한 행위를 필요로 하지 않는 경우 XML 문서나 ADO.NET이 제공하는 데이터 셋을 사용하는 것이다. 비즈니스 엔티티 컴포넌트는 스냅샷 데이터를 가져 정보의 로컬 캐시를 유지한다. 이러한 로컬 캐시를 데이터베이스의 데이터와 동기화하기 위한 Update 메쏘드 등을 제공해 줄 필요가 있다.

마지막으로 서비스 인터페이스는 전형적인 Facade로 동작하게 디자인한다. 응용 프로그램의 비즈니스 계층 컴포넌트들이 서비스로서 노출시켜야 하는 경우라면 서비스 인터페이스 역할의 컴포넌트를 반드시 포함해야 한다. 서비스 인터페이스 컴포넌트를 추가했다면 다른 응용 프로그램의 서비스 에이전트들이 서비스를 이용할 수 있도록 할 수 있다. 서비스 인터페이스를 XML 웹 서비스로 구현하여 다양한 플랫폼의 응용 프로그램이나 다양한 장비에서 동작하는 응용 프로그램들이 같은 서비스를 사용할 수 있도록 할 수 있다.

어떨 때, 어떻게 구현해야 하는가?
앞서 언급했지만 100번을 강조해도 모자람이 없는 말이 바로 ‘닭 잡는 데는 닭 잡는 칼을, 소 잡는 데는 소 잡는 칼을’ 쓰는 것이다. 응용 프로그램의 아키텍처는 간단하면 간단할수록 좋다(그렇다고 모델 1 기법이 가장 좋은 방법이라는 말은 결단코 아니다. “응용 프로그램이 복잡해진다고 생각되는 그때 클래스를 추가하라”는 말을 기억하자). 엔터프라이즈 솔루션 아키텍처에서 <그림 1>과 같은 다이어그램을 제공한다고 해서 반드시 그 구조를 지켜야 한다는 것은 절대 아니다.

응용 프로그램의 아키텍처를 설계하는 사람은 그 응용 프로그램의 역할과 복잡도를 충분히 파악하여 아키텍처를 구성해야 한다. 정답은 없다. 물론, 수학적인 공식을 사용해 요구사항 분석에 따른 역할을 파라미터로 복잡도를 분석하고 아키텍처를 설계하는 툴을 만들 수는 있다. 하지만 프로젝트라는 것이 시시각각으로 변하고 인프라의 환경에 따라 아키텍처는 유연하게 반응해야 하는 것이므로 적당한 관점에서 탄력적으로 운용될 수 있는 아키텍처를 설계하는 것이 중요하다.

비즈니스 워크플로우 컴포넌트
비즈니스 워크 플로우는 주로 비즈토크 오케스트레이션으로 구현되며, 다른 벤더의 솔루션이나 프로그래머가 개발한 사용자 정의 컴포넌트로도 구현될 수 있다. 다음과 같은 경우에 비즈니스 워크플로우를 포함하여 구성할 수 있다.

◆ 복잡한 단계의 비즈니스 트랜잭션을 처리하는 경우 :트랜잭션의 깊이가 너무 깊어지면 비즈니스 컴포넌트만으로는 감당할 수 없는 경우가 많아진다. 비즈니스 트랜잭션이 여러 서비스와 연동하는 경우에 프로세스의 각 단계를 내부에 캡슐화하는 비즈니스 컴포넌트를 작성하고 비즈니스 워크플로우가 비즈니스 컴포넌트를 조정하도록 하는 것이다. 소매 응용 프로그램에서 주문을 진행하는 프로세스의 경우가 대표적인 것이다.

◆ 장기 트랜잭션을 포함하는 프로세스를 관리하는 경우 :트랜잭션의 깊이가 깊지는 않더라도 처리되는 프로세스의 기간이 길어지는 경우가 있다. B2B 솔루션에서 파트너 업체와의 주문 처리 등이 이런 경우인데, 주문 처리에서 입고 처리까지가 하나의 트랜잭션으로 관리돼야 하는 경우, 즉 워크플로우의 상태를 장기간 유지해야 하는 경우에 비즈니스 워크플로우를 포함해 구성하는 것을 고려할 수 있다. 비즈토크 오케스트레이션은 이러한 비즈니스 워크플로우를 처리할 수 있도록 디자인되었다.

◆ 비즈토크 서버와 응용 프로그램간의 어댑터나 커넥터를 구성하여 서비스 에이전트와 통신하는 경우 :응용 프로그램이 비즈토크 서버와 통신해야 하는 경우 비즈토크 서버의 발신 데이터를 수신하여 비즈니스 엔티티를 생성하거나 데이터베이스에 입력하는 경우 등의 프로세스가 발생한다. 또한 여러 비즈토크와 통신하는 경우에 이러한 제어를 추상화하기 위한 비즈니스 워크플로우 프로세스가 필요하다.

비슷한 절차가 필요하지만 다음과 같은 경우에는 비즈니스 워크플로우를 생략하고 비즈니스 컴포넌트만으로 구현할 수 있다.

◆ 비즈니스 흐름이 상태를 유지할 필요가 없는 경우 :상태를 유지하는 흐름이 아닌 단순 스탭으로만 이뤄지는 단일 서버의 프로세스라면 워크플로우 프로세스 없이 비즈니스 컴포넌트의 메쏘드를 순차적으로 호출함으로써 해결할 수 있다.

◆ 비즈니스 트랜잭션이 단일 트랜잭션으로 구성되는 경우 :하나의 트랜잭션으로 모든 절차를 완료할 수 있는 경우라면 비즈니스 워크플로우 프로세스를 구현할 필요가 없다.

◆ 데이터 구조를 직접적으로 액세스하는 경우 :비즈니스 워크플로우 프로세스는 자신이 상태를 가지고 비즈니스 컴포넌트를 컨트롤하는 절차를 수행하므로 비즈니스 컴포넌트가 사용하는 비즈니스 엔티티의 데이터에 직접 액세스하는 경우에는 사용이 까다로워진다.

◆ 장기적인 흐름 컨트롤이 아닌 로직에 대한 상세 컨트롤인 경우 :비즈니스 워크플로우는 비즈니스 컴포넌트가 운영하는 여러 절차를 추상화한 프로세스를 운영하므로 로직에 대한 상세 컨트롤에서는 사용하기가 어려워진다.

비즈니스 워크플로우에서 반드시 고려해야 하는 점은 가격대 성능비적인 차원이다. 비즈토크 오케스트레이션은 어떠한 비즈니스 프레임워크보다 훌륭하게 동작하지만, 마이크로소프트(이하 MS)가 판매하는 엔터프라이즈 서버 제품군들 중 가장 가격이 높은 서버에 속하며, 그 전문가를 고용하는 가격 또한 가장 비싼 편에 속한다. 비즈니스 컴포넌트만을 사용하여 워크플로우를 구성하는 것이 서버를 도입하는 것보다 비용/기간 면에서 저렴하다면, 굳이 비즈니스 워크플로우를 포함하여 아키텍처를 설계하지 않아도 된다.

비즈니스 컴포넌트
비즈니스 컴포넌트는 어떠한 상황에서도 반드시 존재해야 하는 컴포넌트이다. 닷넷 환경이 처음 도입됐을 때 ASP.NET의 코드 비하인드 파일이 이런 비즈니스 컴포넌트 역할을 한다고 설명하는 경우가 많았으나 코드 비하인드 파일은 MVC 모델에서의 컨트롤러 역할을 수행해야 하고, 비즈니스 로직은 비즈니스 컴포넌트에서 구현해야 한다. N-티어 구조 응용 프로그램에서 각 계층은 느슨한 결합도로 조합되어 각 계층의 컴포넌트가 변화더라도 다른 계층의 컴포넌트에는 영향을 주지 않는 구조로 디자인되어야 한다. 코드 비하인드는 닷넷 아키텍처상 독립된 비즈니스 계층의 컴포넌트로 동작하는 것이 불가능하다.

비즈니스 컴포넌트는 이미 존재하는 로직을 캡슐화하는 수준 높은 추상화를 제공하고, 변화 가능한 존재하는 비즈니스 기능들을 조합하는 등의 역할을 수행해야 한다. 객체지향 개발 방법에서 여러 디자인 패턴들을 응용하여 비즈니스 컴포넌트를 작성함으로써 이러한 빠른 변화에 능동적으로 대처할 수 있는 높은 추상성과 유지 보수성을 가지는 응용 프로그램을 작성할 수 있게 된다.

비즈니스 컴포넌트를 작성할 때 절대로 간과하면 안 되는 것이 트랜잭션의 지원이다. 비즈니스 컴포넌트는 비즈니스 엔티티의 상태를 관리 및 수정하고 생성하는 역할을 담당할 수 있다. 따라서 비즈니스 엔티티 또는 데이터베이스에 존재하는 데이터의 일관성과 무결성을 지키기 위해 트랜잭션을 반드시 고려해서 작성해야 한다. 비즈니스 컴포넌트에서 트랜잭션 처리는 크게 두 가지로 고려될 수 있다. ADO.NET의 ITransaction 개체를 사용하여 트랜잭션을 처리하거나 또는 여러 서버로 구성된 응용 프로그램의 경우 COM+가 이용하는 MSDTC를 사용한다.

비즈니스 컴포넌트를 작성할 때 주의해야 하는 것은 앞서 이야기했듯이 컴포넌트에 상태가 있어서는 안된다는 점이다. 비즈니스 컴포넌트 팩토리에서 언급한 컴포넌트 인스턴스의 3대 원칙을 그대로 준수해야 하는 진짜 컴포넌트가 이 비즈니스 컴포넌트가 된다. 상태가 없고 메시지 패싱을 원칙으로 하는 등의 비즈니스 컴포넌트의 기본 원칙에 따라 작성돼야 한다. 비즈니스 컴포넌트의 설계 권고 사항은 다음과 같다.

◆ 가급적이면 메시지 기반의 통신을 사용한다.

◆ 서비스 인터페이스로 노출된 프로세스에서 메쏘드가 두 번 호출되거나, 같은 인자 값을 가지며 같은 방식의 메시지가 전달되었을 때 데이터의 무결성을 유지할 수 있도록 설계되어야 한다 :서비스 인터페이스로 노출된 프로세스는 페이지의 리로드나 같은 서비스의 동시 사용 등으로 인해 같은 식별자를 가지는 데이터를 두 번 사용하거나 여러 명이 동시에 사용하게 되는 경우가 빈번하게 일어난다. 이럴 때를 대비할 수 있는 처리를 해야 하는데, 가장 좋은 방법은 아예 이런 프로세스를 원천적으로 봉쇄하거나 메시지 큐 등을 사용하여 직렬화하고 검증하는 등의 정책이 필요하다.

◆ 비즈니스 컴포넌트는 어떤 서비스 사용자의 컨텍스트에서도 사용될 수 있도록 한다 :펫샵 3.0을 설치해 본 독자라면 겪어봄직한 이야기이다. 펫샵은 데이터베이스 연결 문자열을 Web.Config 파일에 암호화하여 저장한다. 암호화된 연결 문자열에는 Trusted Connection을 사용하도록 설정되어 있다. 이런 설정으로 인해 데이터베이스 연결에서 오류가 발생하는 경우를 봤을 것이다. 이 때 impersonate 속성을 설정해 줘 응용 프로그램에 접근하는 사용자를 ASP.NET으로 인격화(Impersonation)하는 방법을 사용하면 데이터베이스에는 접근 가능하지만 필요한 어셈블리를 로드할 수 없다는 오류를 만나게 된다. 윈도우의 사용자 권한 정책과 데이터베이스의 연결 등의 여러 문제를 고려해 어떤 사용자라도 특별한 인격화 과정을 거치지 않고 비즈니스 컴포넌트에 접근할 수 있도록 설계해야 한다.

◆ 입력 매개변수와 반환 값은 일관성 있는 포맷으로 선택하고 유지한다 :반환 값으로 ADO.NET에 포함되어 있는 DataSet 개체 또는 스키마가 지정된 XML 파일 등을 사용하는 것이 가장 바람직하며, 비즈니스 엔티티에 사용자 정의 컴포넌트를 사용할 경우 일관된 정책 수립이 필요하다.

◆ 트랜잭션 격리 수준에 신경써라 :트랜잭션 격리 수준에는 정말 정답이 없다. 데이터베이스에서 데드락을 막을 수 없듯이 격리 수준은 파악된 요구사항에 따라 적절히 수립하는 수밖에 없다. 어이없는 경우는 트랜잭션 하에서 여러 명령이 수행되도록 작성하고, 수행되는 명령에서 오류가 발생하는 경우에 대한 처리를 잊어버리는 경우다. 이럴 때는 격리 수준이 ReadCommetted로 설정됐다면 데이터베이스에서 강제로 트랜잭션을 종료하기 전까지 데이터는 잠기게 된다. 어렵지 않고 기본적인 수준의 권고사항이지만 이 때문에 발생하는 어이없는 오류가 한 번씩 생기기 마련이다.

펫샵의 예를 보자. 지난 글에서도 살펴봤듯이 펫샵은 잘 설계된 아키텍처에 따라 구성된다. 펫샵의 구조는 <그림 3>에서 살펴볼 수 있듯이 엔터프라이즈 솔루션 아키텍처의 원칙을 그대로 따른다(비즈니스 워크플로우는 존재하지 않는다).

사용자 삽입 이미지
<그림 3> 비주얼 스튜디오 닷넷의 속성창에서 본 펫샵 응용 프로그램 구조


<그림 3>은 아키텍처 기반으로 구현된 응용 프로그램의 예를 들어 설명하기 위해 앞으로 계속 참조할 것이므로 유심히 봐두길 바란다. 전체 솔루션에 포함된 11개의 프로젝트 중 BLL 프로젝트가 비즈니스 컴포넌트에 해당한다. BLL 컴포넌트는 여러 개의 파일로 구성되어 있고, 작성된 클래스들은 비즈니스 컴포넌트 작성 권고사항에 따라 작성되었다. <그림 4>의 클래스 다이어그램에서도 알 수 있듯이 상태가 없는 오퍼레이션만을 가지고 있는 것을 알 수 있다.

 

사용자 삽입 이미지
<그림 4> BLL 컴포넌트의 클래스 다이어그램

펫샵은 주문 처리를 위해 분산 데이터베이스를 사용한다. 분산 데이터베이스의 트랜잭션은 닷넷의 엔터프라이즈 서비스를 사용한다. <리스트 1>은 분산 트랜잭션을 위해 엔터프라이즈 서비스를 사용하는 OrderInsert 클래스의 소스코드이다. 주문을 수행하는 Insert 메쏘드에서 주문 처리를 위한 주문 정보 데이터베이스와 재고 관리를 위한 재고 정보 데이데이터 베이스 등 두 데이터베이스에서 분산 트랜잭션을 처리한다.

 <리스트 1> OrderInsert 클래스의 소스코드
사용자 삽입 이미지
using System;
using System.Collections;
using System.EnterpriseServices;
using System.Runtime.InteropServices;
using PetShop.Model;
using PetShop.IDAL;

namespace PetShop.BLL {
  [Transaction(System.EnterpriseServices.TransactionOption.Required)]
  [ClassInterface(ClassInterfaceType.AutoDispatch)]
  [ObjectPooling(MinPoolSize=4, MaxPoolSize=4)]
  [Guid("14E3573D-78C8-4220-9649-BA490DB7B78D")]
  public class OrderInsert : ServicedComponent {
    private const string ACID_USER_ID = "ACID";
    private const string ACID_ERROR_MSG = "ACID test exception thrown for distributed transaction!";

    protected override bool CanBePooled() {
      return true;
    }

    [AutoComplete]
    public int Insert(OrderInfo order) {
      // 주문 정보 데이터베이스 입력
      IOrder dal = PetShop.DALFactory.Order.Create();
      int orderId = dal.Insert(order);
      // 재고 정보 데이터베이스 처리
      Inventory inventory = new Inventory();
      inventory.TakeStock( order.LineItems);

      if (order.UserId == ACID_USER_ID)
        throw new ApplicationException(ACID_ERROR_MSG);

      return orderId;
    }
  }
}
사용자 삽입 이미지

<리스트 2>는 주문 정보를 반환하는 아주 썰렁한 클래스 OrderRead 클래스의 소스코드이다. 어렵고 복잡하게 짠 코드가 잘 작성된 것은 아니라는 점을 확연히 보여준다. GetOrder 메쏘드는 잘 알려진 정수형의 파라미터를 받아들이고 주문의 정보를 표현하는 비즈니스 엔티티 컴포넌트에 포함된 OrderInfo 객체를 반환하도록 디자인되었다.

 <리스트 2> OrderRead 클래스 소스코드
사용자 삽입 이미지
using System;

//References to PetShop specific libraries
using PetShop.Model;
using PetShop.IDAL;

namespace PetShop.BLL {
  public class OrderRead{
    public OrderInfo GetOrder(int orderId) {
      if (orderId < 1)
        return null;
      IOrder dal = PetShop.DALFactory.Order.Create();
      return dal.GetOrder(orderId);
    }
  }
}
사용자 삽입 이미지

<리스트 2>의 GetOrderInfo 메쏘드에는 if (ordered < 1)이라는 조건절이 포함되어 있다. 이는 주문 정보 번호가 유효한지 아닌지를 검사하는 유효성 검사를 수행하는 코드다. 비즈니스 컴포넌트는 프리젠테이션 계층의 컴포넌트가 아닌 다른 계층의 컴포넌트들, 즉 서비스 인터페이스, 다른 비즈니스 컴포넌트 등에서도 호출할 수 있으므로 이러한 유효성 검사는 반드시 필요하다. 다음은 비즈니스 컴포넌트의 구현에서 지켜야 하는 규칙들이다.

◆ 트랜잭션의 시작점이 된다. 비즈니스 컴포넌트가 참여하는 트랜잭션을 지원해야 한다.
◆ 입력과 출력에 대한 유효성을 검사할 수 있어야 한다.
◆ 데이터 액세스 로직을 호출하여 데이터를 가져오거나 갱신할 수 있어야 한다.
◆ 설치된 서비스 에이전트를 통하여 외부 서비스를 호출할 수 있어야 한다.
◆ 비즈니스 워크플로우를 초기화할 수 있어야 한다.

<그림 5>는 다른 비즈니스 컴포넌트와 상호 작용하는 전형적인 비즈니스 컴포넌트의 모습을 보여준다.

사용자 삽입 이미지
<그림 5> 비즈니스 컴포넌트

그리고 비즈니스 컴포넌트가 작성될 때는 흐름 제어를 위한 패턴인 순차 호출식의 파이프라인 패턴과 이벤트 호출식의 이벤트 패턴이 사용된다. 일반적으로 비동기적인 서비스를 포함하고 있으면 파이프라인 패턴을, 모든 활동의 초기 값이 동일하고 각 활동 간에 메시지 패싱이 필요 없을 때 이벤트 패턴을 사용한다.

비즈니스 엔티티 컴포넌트
비즈니스 엔티티는 전체 아키텍처를 고려할 때 반드시 필요한 사항은 아니다. 기업용 응용 프로그램에서 데이터가 존재하지 않는 경우는 없겠지만, 특히 ASP.NET 응용 프로그램에서 사용자 정의 비즈니스 엔티티를 작성하지 않고 ADO.NET에 포함된 데이터 셋이나 XML 문서를 사용하는 경우도 많다. 데이터 셋은 복잡한 정보를 포함할 수 있어 굳이 사용자 정의 비즈니스 엔티티를 포함하지 않아도 되며, 데이터 셋은 XML로 바로 변환할 수 있어 작업 및 문서 기반으로 프로젝트를 진행할 수 있기 때문이다.

사실 거의 모든 기업용 응용 프로그램에서 비즈니스 엔티티를 사용한다. 일반적으로 상태를 표현하는 정보를 사용자 정의 엔티티에 포함한다. 사용자 정의 비즈니스 엔티티 컴포넌트 내의 객체는 상태를 표시하기 위한 전역변수 등의 데이터와 복잡한 데이터를 표현하기 위한 컬렉션이나 데이터셋을 포함한다. 이후 포함된 이들 컬렉션과 데이터셋 등의 개체를 프로퍼티로 지정하도록 설계하여 구현된 비즈니스 엔티티 컴포넌트가 자신을 호출하는 사용자 프로세스 컴포넌트 또는 비스니스 컴포넌트 등에 노출시켜 주는 기법을 사용한다.

다시 <그림 3>의 펫샵 모델을 살펴보자. 펫샵에서는 Model 컴포넌트가 비즈니스 엔티티 컴포넌트로 사용된다. 전형적인 모델을 보여준다. <그림 6>은 Model 컴포넌트의 클래스 다이어그램이다.

사용자 삽입 이미지
<그림 6> Model 컴포넌트의 클래스 다이어그램

<그림 6>의 클래스 다이어그램에서 알 수 있듯이 비즈니스 엔티티 컴포넌트는 단순히 자신을 생성하는 생성자만을 포함하고 있을 뿐 어떤 동작도 포함하고 있지 않다. 비즈니스 엔티티 컴포넌트는 MVC 모델에서 Model 역할을 담당하게 되는데, 모델은 단순히 데이터로서 존재해야지 데이터베이스에 직접 접근하거나 트랜잭션을 초기화해서는 안 된다. 비즈니스 컴포넌트에 의해 생성이 불가피하거나 비즈니스 컴포넌트의 로직이 불필요하게 복잡해 질 경우에는 데이터 액세스 로직 컴포넌트를 이용하는 것이 바람직하다. 비즈니스 엔티티는 어떤 잠재적인 행위를 가지고 단지 데이터를 표현하는 용도로서만 사용돼야 한다. 비즈니스 엔티티 컴포넌트의 설계 권고사항은 다음과 같다.

◆ 사용자 정의 엔티티 구현이 반드시 필요한지를 면밀한 요구사항 분석을 통해 파악한다 :사용자 정의 엔티티를 구현하는 것은 복잡성의 증가로 인한 개발 비용 상승을 가져올 수 있다. 필요하지 않다면 굳이 작성할 필요가 없지만, 작성하는 것이 비즈니스 컴포넌트의 코드 가독성과 복잡성을 떨어뜨리는 효과를 가져 오기도 한다.

◆ 사용자 정의 비즈니스 엔티티가 필요하다면 객체지향의 특성을 이용해서 다형적으로 구현한다 :공통 타입을 정의하고, 기본 클래스 또는 인터페이스의 서브타입으로 설계하여 비즈니스 컴포넌트의 개발 부담을 덜 수 있도록 설계해야 한다.

◆ 컬렉션이나 구조체 대신 데이터 셋이나 XML 문서를 내부적으로 유지한다 :데이터 셋이나 XML 문서는 복잡한 데이터를 표현할 수 있다. 또한, 닷넷 프레임워크의 Collections 프레임워크는 다형적으로 아주 잘 설계되어 있으므로 여러 사용자의 부담을 줄일 수 있다.

◆ 비즈니스 엔티티 인터페이스를 디자인하고 공용 속성을 제공한 후 구현은 서브 클래스에 위임하는 공통적인 다형성을 사용한다 :엔티티의 속성에 대한 프로퍼티, 엔티티가 포함하는 컬렉션 접근자, Load, Save, Validate등의 컨트롤 메쏘드와 속성을 정의하여 구현한다.

◆ 내부 데이터를 표현하는 메타데이터로부터 자유롭게 설계한다 :XSD 스키마 처럼 유효성 검사 규칙을 분리하여 외부 호출자가 규칙에 영향을 미치지 않도록 설계한다.

◆ 데이터베이스 액세스에 대한 일관성을 유지한다 :모든 데이터 액세스는 데이터 액세스 로직에 일임한다.

제대로 된 소프트웨어를 먼저 생각하자
필자가 코스매니저로 있는 정보기술원의 닷넷 5기 과정에서 최종 프로젝트로 닷넷 기반의 개인용 블로그 솔루션을 개발하고 있다. 욕심으로는 무버블 타입 같은 수준의 높은 추상성을 가지는 개인용 블로그를 독촉했지만 취업 등의 여러 이유로 인해 테터툴즈(Tatter tools) 수준의 블로그 솔루션이 개발될 듯하다.

블로그에는 트랙백과 RSS를 포함해야 하는데 9개월 정도 공부한 한 학생이 이틀 만에 구현했다. 다른 사이트나 책을 참조하는 것 같지 않았고 수업 시간에 공부한 적도 없는데 금방 구현한 것이다. 닷넷은 워낙에 좋은 성능과 많은 기능들을 가진 플랫폼이라 RSS의 개념 정도만 파악하면 쉽게 구현할 수 있다.

국내 닷넷 커뮤니티를 돌아다니다 보면 사용자 지정 컨트롤을 만들어서 배포도 하고 소스도 공개하는 것들을 많이 볼 수 있다. 하지만 공개되는 소스코드들의 수준이 그렇게 뛰어나지도 않을 뿐더러, 그렇게 만들어진 사용자 정의 컨트롤들이 어떻게 사용될 수 있을지 의심스럽다. 국내 유명한 닷넷 커뮤니티들을 돌아다니다가 외국의 오픈소스 커뮤니티와 비교해보면 한숨이 저절로 난다.

닷넷 플랫폼에서 개발하고 있는 여러 개발자들은 다음과 같은 질문을 스스로 던져볼 필요가 있을 것 같다. ‘닷넷이 정말 뛰어난 플랫폼인가?’ 그렇다고 스스로에게 대답을 했다면 ‘왜 뛰어난 플랫폼인가?’를 다시 한번 생각해 봐야 한다. 단순히 생산성이 좋아서? 그렇다고 생각했다면 그 사람은 틀림없는 초보 개발자이거나 단순 코더 밖에 되지 않는다. “닷넷 플랫폼이 어떤 이유에서 뛰어나다고 말할 수 있는가?”라는 질문에 대한 대답을 이전의 ASP나 PHP 개발과 비교해 “편해서”, “개발 환경이 좋아서”라는 대답들이 머리에 먼저 떠오른다면 조금 더 깊이 있는 공부가 필요하다.

어쩌면 내 게시판에서 RSS를 돌려볼 수 있을 것은 중요한 문제가 아니다. 잘 파악된 요구사항을 기반으로 이러한 요구사항을 만족하기 위해 RSS가 어떤 역할을 할 수 있는가? RSS가 전체 아키텍처에서 다른 컴포넌트들과 어떻게 유기적으로 조합될 수 있는가? 등 이런 것들이 훨씬 중요하다.

소스포지(sourceforge.net)에서 Liferay 엔터프라이즈 포탈이라는 오픈소스 솔루션을 검색하고 웬만하면 한번 써보기 바란다. 3년간 다섯 명의 개발자가 개발했다는 이 솔루션을 보면 닷넷의 최전방에 서 있다는 필자로서는 저절로 한숨이 난다.

닷넷 환경에서는 왜 이런 솔루션이 나오지 않는가? 이런 환경을 쉽게 구축하기 위한 모든 인프라는 이미 닷넷 플랫폼에 모두 들어 있다. “국내에서는 솔루션을 만들어 봤자 안 팔려”라는 말을 하기 전에 먼저 소프트웨어를 제대로 만들어 보겠다는 생각을 해야 한다. 물론, 그런 솔루션은 튼튼한 기반 아키텍처 위에 구축될 수 있고, 닷넷 엔터프라이즈 솔루션 아키텍처는 튼튼한 반석이 되어준다.@

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

 

김상훈 (동명정보대학 정보기술원 연구원)

2005/08/10

 

사용자 삽입 이미지

 

 

 

 

원문 :http://www.zdnet.co.kr/builder/dev/dotnet/0,39031607,39137358,00.htm

  Comments,     Trackbacks