태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

오랜만에 포스팅을 한다.

그동안 새로운 회사에 입사를 했다.

현재 솔루션 개발 프로젝트에 참여하고 있는데, Spring.NET을 기본 프레임워크로 선정했다.

해서 Spring.NET 개발 가이드라는 문서를 하나 작성했다.
Spring.NET이 공개소스(Apache 라이센스)이니 관련 문서도 공개를 한다.


Posted by dalbong2

일반적인 기업형 애플리케이션은 대부분 N티어 구조를 갖는다.   다음 그림은 간단한 N티어 애플리케이션을 표현하고 있다. 참고로  티어는 물리적인 의미이고 레이어는 논리적인 의미라고 한다.

물리적으로 UI 레이어는 웹 애플리케이션의 경우는 웹 서버 그리고 윈폼 애플리케이션은 클라이언트 PC가 될 것이다. 그리고 서비스 레이어와 데이터 액세스 레이어는 보통 하나의 미들티어 서버에 존재한다. 물론 더 복잡한 물리적 구조로도 존재할 수 있다. 

Spring.NET의 IoC 컨테이너는 UI 애플리케이션 서버(PC)에서도 적용가능하고 미들티어 서버에서도 적용가능하다. 앞 포스트까지는 UI를 제공하는 웹 서버/사용자 PC 또는 미들티어 서버에서 실행될 수 있는 IoC 컨테이너에 대한 얘기를 했다. 그리고 UI 서버와 비즈니스 서버간의 통신 방법중의 하나인 웹 서비스에 대한 Spring.NET의 지원 얘기도 했다.

이제 남아 있는 큰 주제는 Spring.NET의 트랜잭션과 데이터 액세스 지원 얘기이다. 트랜잭션 이야기를 먼저 할지 데이터 액세스 이야기를 먼저 할지 고민하다 데이터 액세스부터 하기로 결정했다. 왜냐면 참고 문서를 읽다 보니까 그쪽이 먼저 이해가 되었다. 


■ Spring.NET의 Data액세스 지원


Spring.NET에서는 DB에 접근할 때 ADO.NET 기술을 이용할 수도 있고, NHibernate기술을 이용할 수도 있다.  우선 ADO.NET을 이용하는 기술을 알아본다. ADO.NET이 제공하는 .NET 프레임워크의 표준 API를 이용해서 DB에 접근할 수도 있다. 그렇지만 Spring.NET에서는 ADO.NET 기술을 한번 래핑한 API를 제공하는데 이 API를 사용하면 편리하다.

Spring.NET에서 제공하는 래핑 API도 두 종류로 구분할 수 있다. 하나는 "template"기반의 방식이고 하나는 객체 지향 기반의 방식이다(  이런 이름이 붙여진 것이 이해는 개인적으로 이해는 된다. 그렇지만 불행히도 지금 이것을 말로 표현할 정도는 아니다). 여튼 하나는 템플릿 방식이고 하나는 객체 지향 방식인가보다 -_-;; 이 방식에 따라서 사용자의 DB 접근 프로그래밍 스타일이 달라진다.

"template"기반의 방식을 이용하면 DB에 접근해서 작업을 하는데 AdoTemplate라는 클래스를 사용하게 된다. 즉 데이터 액세스 레이어의 객체( Data Access Object, 이하 DAO로 표현한다 )는 AdoTemplate객체를 사용해서 모든 DB 작업을 하게 된다.

객체 지향 기반의 DB 접근 방식에서는 DB에 대한 작업을 구분해서 각 작업을 클래스화했다. DB에 대한 작업은 그 성격에 따라 C( Create), R( Read), U( Update), D( Delete ) 나뉠 수 있다. 이 중에서 CUD작업은 DB에 영향을 미치기는 하나 조회하는 값이 없다. 이런 작업을 위해서 AdoNonQuery 타입을 제공하고 있고 그리고 읽기 전용의 작업을 위해서 AdoQuery 타입을 제공한다. 그리고 저장 프로시져를 이용하는 작업을 위해서 StoredProcedure라는 타입을 제공하고 있다. 그러나 이 방식의 DB 접근은 아직 널리 사용되지는 않은 모양이다.

이 포스트에서는 AdoTemplate객체를 이용하는 "template"기반의 방식를 설명할 것이다. 만약 객체 지향 기반의 프로그래밍 스타일을 알고 싶다면 Spring.NET에서 제공하는 레퍼런스 문서의 20.15절을 참고하기 바란다( 링크하나 걸어주고 싶은데 맘뿐이다. 쓰으...).


▶ AdoTemplate이용 구조


AdoTemplate를 이용해서 DB 데이터에 액세스하는 작업을 초 간단히 개념적으로 그렸다.

클라이언트 코드라 함은 여기서는 DAO( 데이터 액세스 객체)가 된다. 클라이언트 코드는 Spring.NET 프레임워크의 AdoTemplate에서 콜백될 객체를 넘겨준다. "콜백된다"는 것은 "다시 호출된다"는 의미로 쉽게 생각하자. 그러니까 AdoTemplate객체는 클라이언트 코드에서 넘겨받은 "콜백 객체를 다시 호출"하게 된다. 물론 AdoTemplate도 콜백객체를 어떻게 호출할지를 사전에 알 수 있다. 어떻게 아는지는 뒤에 보자. 야튼 이때 콜백 객체를 호출할때 AdoTemplate은 command 객체라는 것을 생성해서 호출 인자로 넘겨준다. command 객체에는  DB 액세스에 필요한 커넥션 정보가 포함되어 있다. 그리고 호출하는 클라이언트 코드의 트랜잭션 컨텍스트를 바탕으로 한 트랜잭션 정보도 설정되어 있다. AdoTemplate 객체를 작업하면 개발자가 작성한 소스에 DB 커넥션 정보나 트랜잭션 정보를 설정하는 코드는 없어도 된다는 이야기다.  콜백 객체에서는 넘겨받은 command 객체를 이용해서 이제 DB를 대상으로 실행 명령( sql문 또는 저장 프로시져 등 )을 수행하면 된다. 

방금 AdoTemplate 객체가 클라이언트 코드에서 넘겨받은 콜백 객체를 어떻게 호출할 수 있는지 알 수 있다고 했다. 이것은 AdoTemplate에서도 알고 있는 인터페이스와 델리게이트를 사용하기때문이다. 다시 말하면 클라이언트 코드에서 넘어오는 사용자 정의 콜백 객체는 AdoTemplate도 인식할 있는 사전에 정의한 인터페이스 또는 델리케이트를 구현한 객체여야 한다는 조건이 있다. 그 인터페이스와 델리게이트의 타입으로 ICommandCallback, CommandDelegate 정의가 되어 있다. 아래 그림에서는 이런 인터페이스 또는 델리게이트 기반의 콜백 구조를 보여주고 있다.


▶ ICommandCallback, CommandDelegate 기반의 콜백 구조


클라이언트 코드에서는 AdoTemplate객체의 Execute() 메소드를 호출할때 콜백 객체를 넘겨준다. 다음 그림을 보자. 

 

AdoTemplate 클래스에는 Execute() 메소드가 파라미터 타입에 따라서 여러 버전이 존재한다. 즉 ICommandCallback 또는 CommandDelegate이외의 다른 인터페이스, 델리게이트 객체를 받을 수도 있다는 것이다. 그림에서는 그 중에서 대표적인 두 개의 Execute() 버전을 보여주고 있다. 하나는 인터페이스 ICommandCallbakc 타입의 객체 c 를 받고 다른 하나는 델리게이트 CommandDelegate 객체 d를 받고 있다.

ICommandCallback 파라미터를 받는 버전을 먼저 보자. 우선 호출하는 코드의 컨텍스트에서 정보( DB 커넥션 정보, 트랜잭션 정보)를 구해서 command 객체를 생성한다. 그런 다음 파라미터로 받은 ICommandCallback 객체 c의 DoInCommand() 메소드를 호출하면서 콜백 객체로 방금 생성한 command객체를 넘겨준다. 이제 사용자 정의 콜백객체의 DoInCommand()에서는 넘겨받은 command객체를 이용해서 DB에대해서 명령을 수행한다.  이때 command객체에는 DB 액세스에 필요한 커넥션 정보, 실행 명령(sql, 저장 프로시져등)가 있다.

CommandDelegate 타입의 객체 d를 받는 Execute() 버전도 유사하게 작동한다. 역시 호출하는 클라이언트 코드의 컨텍스트에서 필요한 정보를 얻어서 command 객체를 생성한다. 그런 다음 넘겨받은 델리게이트 객체 d를 호출해서 command 객체를 콜백 객체로 넘겨준다. command 객체를 넘겨받은 콜백 객체의 사용자 정의 콜백함수에서는 command 객체를 이용해서 DB 액세스를 하게 된다. 이제 이 콜백 구조를 이용하는 샘플 코드를 보자.


▶  AdoTemplate를 이용하는 샘플 코드


이건 다음에 하자.

Posted by dalbong2

IoC( Inverse of Control  제어권의 역전, 역제어)와 DI( Dependencies Injection)은 같은 의미로 사용된다. 시간적으로 보면 IoC라는 용어가 먼저 나왔고 뒤에 Martin Fowler라는 사람이 개념상 더 적절하지 않냐면서 내놓은 것이 DI다. 필자의 눈에는 차이점을 잘 모르겠고, 개발자에게는 당장 별로 중요한 차이는 아닐듯하다. 컨테이너가 객체를 생성할때 그 객체가 필요로 하는 의존 객체들을 자동 생성해서 할당해준다는 것이다.

컨테이너가 (대상)객체들을 생성할때 의존객체들을 할당해주기 위해서는 대상객체는 의존 객체들을 외부에서 받아들일 수 있는 public 입구(?)가 있어야 한다. 그래야 컨테이너가 대상 객체를 생성해서 공개된 입구로 객체를 할당해 줄 수 있다. 대상객체 내부에서 private으로 생성하는 객체들에 대해서는 컨테이너가 할당해줄 수 없다. 공개된 입구는 어떤 것이 있는가. 바로 public 생성자,public setter 속성,  public 메소드가 있다. 즉 contructor injection, setter injection, method injection DI가 수행될 수 있기 위해서는 각각에 해당하는 public API가 있어야 한다.

constructor injection : 파라미터가 없는 기본 constructor밖에 없다면 DI고 뭐고 수행될 것이 없겠지만, 만약 파라미터가 있는 공개 constructor라면 constructor injection의 대상이 될 수 있다. 즉 대상 객체를 컨테이너가 생성할때, 파라미터에 해당하는 필요한 인자들이 있다면 컨테이너가 알아서 스스로 인자들을 생성해서 대상 객체를 생성하는데 사용하는 것을 constructor injection이라고 한다.

namespace X.Y

{

    public class Foo

    {

        public Foo(Bar bar, Baz baz)

        {

            //...

        }

    }

    public class Bar

    {

        //...

    }


    public class Baz

    {

        //...

    }

}

코드에서는 Foo의 객체를 생성할때 Bar타입의 bar객체와 Baz 타입의 baz객체를 이용하고 있다. 코드에서 Foo객체를 요구하면 bar, baz를 자동으로 생성해서 이것들을 이용해서 Foo객체를 생성해서 반환해준다.

constructor injection의 대상이 되려면 대상 객체와 constructor의 파라미터들이 모두 컨테이너가 인식할 수 있도록 설정되어 있어야 한다. 설정 방법은 조금 후에 앞에서 봤던 예제의 web.config의 내용으로 알아볼 것이다.

setter injection : 대상객체가 필요한 의존 객체를 대상 객체에 할당해 주기 위해서는 당연히 getter 속성 메소드는 이용할 수 없다. 따라서 property injection이라고 해도 setter injection을 의미하게 된다. 그러나 property injection같은 용어는 아직 보지 못했다. setter 메소드를 이용해서 의존 객체를 할당하는데, 물론 쓰기 가능한 모든 속성을 자동 할당하지는 않는다. 대상 객체를 생성하고 나서 그 객체의 속성중에서 설정을 통해서 요청한 속성에 대해서만 injection이 발생한다.

대상객체를 의존 객체들로 초기화하기 위해서 constructor DI를 사용할지 setter DI를 사용할지에 대한 규칙은 없다. 직접 개발을 하고 있는 상태라면 개발자의 기호에 맞게 사용하면 될 것 같다. 그러나 이미 개발되어 있는 3th파티 클래스의 코드를 구할 수가 없다면 이미 구현되어 있는 상태에 따라 달라질 수 밖에 없다. 예를 들어 공개된 속성이 없고 모든 의존 객체들을 대상 객체 constructor에서 모두 받는다면 어쩔 수 없이 contructor DI를 사용해야 할 것이다.

constructor injection, setter injection에 비해서 method injection은 그렇게 많이 사용되지 않는다. 이것에 대해서는 추후에 알아보겠다.

이제 constructor, setter injection에 대한 설정을 알아보도록 하자.

<objects xmlns="http://www.springframework.net">


   <!-- Aspect -->

   <object id="CommonLoggingAroundAdvice" type="Spring.Aspects.Logging.CommonLoggingAroundAdvice, Spring.Aspects">

    <property name="Level" value="Debug"/>

  </object>

   <!-- Service -->

   <object id="calculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"/>

  <object id="calculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">

    <property name="target" ref="calculator"/>

    <property name="interceptorNames">

      <list>

        <value>CommonLoggingAroundAdvice</value>

      </list>

    </property>

  </object>

</objects>

<objects/>요소안에 3개의 객체가 등록되어 있다. 첫번째 등록된 객체를 보면, 어셈블리 Spring.Aspects에 포함되어 있는 Spring.Aspects.Logging.CommonLoggingAroundAdvice 타입이 CommonLoggingAroundAdvice라는 id로 등록되어 있다. 그리고 이 객체는 public 속성으로 Level 이 있는데, 이것의 값을 "Debug"로 설정하고 있다. 지금은 Spring.Aspects.Logging.CommonLoggingAroundAdvice 타입이 뭐하는 녀석인지 몰라도 된다.

애플리케이션의 코드상에서 이 객체( id="CommonLoggingAroundAdvice")를 요청하게 되면 컨테이너는 일단 주어진 타입 정보를 통해서 인스턴스를 하나 생성하고 나서 Level 속성에 Debug 값을 설정한다.
애플리케이션이 시작되면 CommonLoggingAroundAdvice 인스턴스가 하나 생성되고( 기본적으로 Spring.NET 컨테이너에 생성되는 객체는 singleton이다), Level 속성에 Debug값을 설정한다.

근데 CommonLoggingAroundAdvice 타입의 Level 속성을 보면 반환값의 타입이 enum LogLevel 라는 열거형을 사용하고 있다. 그러나 여기에 할당될 값은 value어트리뷰트에 문자열 "Debug"이 설정되어 있다. 문자열을 LogLevel이라는 열거형에 할당할 수 있나? 없다. 그럼 어떻게 ? 똑똑한 Spring.NET 컨테이너는 대상 객체의 Level 속성이 LogLevel 열거형이라는 것을 알고 "Debug"를 열거형의 문자열 값중의 하나로 인식해서 LogLevel.Debug를 속성에 할당해준다. 즉 문자열 "Debug"를 열거형 값 LogLevel.Debug로의 변환을 자동으로 해준다. 이 과정에 TypeConvert라는 것이 사용되고 있다는 것을 알아두고 나중에 더 깊이 있는 공부를 할 필요가 있겠다.

두번째 객체는 "calculator"라는 id로 Spring.Calculator.Services.AdvancedCalculator 타입의 객체가 등록되고 있다. 이 객체에서는 DI를 위한 설정은 없다. 단순히 객체 등록만 하고 있다.

세번째로 등록되는 객체에서도 두 개의 setter injection을 위한 설정이 있다.  ProxyFactoryObject 타입을 보면 공개된 두개의 속성 Target, InterceptorNames이 있다. 대소문자는 구분하지 않고 있다.  대상객체 ProxyFactoryObject의 Target 속성은 object 타입을 받는 속성이다. <property/>요소의 ref 어트리뷰트를 사용해서 Target 속성에 할당될 객체를 지정하고 있다. ref 어트리뷰트의 값으로는 XML에 설정된 다른 객체의 id(또는 name)값을 지정해주면 된다. 두번째 속성 InterceptorNames는 문자열 배열 string[]이다. 문자열 배열을 지정할때는 <list/> 하위 요소를 사용해서 필요한대로 <value/>요소를 사용해서 값을 추가하면 된다.

ProxyFactoryObject를 코드에서 요청할때 컨테이너는 인스턴스를 하나 생성하고나서 두개의 공개 속성을 통해서 초기화를 마친 후 준비된 객체를 코드에 반환한다.

예제에서는 나와 있지 않지만, 앞에서 보인 Foo 객체에 대한 constructor injection을 위한 설정을 보이면 다음과 같다.

<object id="fooObject" type="X.Y.Foo, X.Y">

  <constructor-arg name="bar" ref="barObject"/>

  <constructor-arg name="baz" ref="bazObject"/>

  <!--추가된 인자-->

  <constructor-arg name="arg" value="strArg"/>

</object>

<obejct id="barObject" type="X.Y.Bar,X.Y"/>

<object id="bazObject" type="X.Y.Baz,X.Y"/>

컨테이너가 생성되면서 설정된 모든 객체들에 대한 정보(Object definitions)들이 컨테이너에 로딩된다.  그리고 나서 DI가 수행되는 것은 실제로 대상 객체에 대한 인스턴스가 요청될때이다. DI가 수행될때 의존 객체들은 XML 설정 파일이 아니라 컨테이너의 로딩된 object definitions에서 검색된다. 따라서 XML상 의존 객체들이 대상 객체보다 뒤에 설정되어 있어도 상관없다.

앞에서 소개한 문서의 5.3. Dependencies 절을 보면 알겠지만, DI를 위한 설정 표현이 여러 버전이 있다. 예를 들어 다음과 같은 표현도 있다.

<property name="target">

   <ref object="calculator"/>

</property>

각기 표현마다 다른 장단점이 있을 수 있다. 또한 이곳에는 기타 여러가지 설정을 위한 요소 및 어트리뷰트에 대해서 설명한 내용이 있다. 한번 정독해보는 것도 좋을 듯 싶다.

오늘은 여기까지.  자자!

추가 설명

컨테이너에 등록을 했으면 이제 코드에서 객체를 얻는 API도 필요할 것이다. Spring 컨테이너에 등록( 또는 생성)되어 있는 객체에 대한 참조를 얻는 API는 다음과 같다.

IApplicationContext ctx = ContextRegistry.GetContext();

IAdvancedCalculator firstCalc = (IAdvancedCalculator) ctx.GetObject("calculatorService");

IApplicationContext는 System.Core.dll의 Spring.Context 네임스페이스에 포함되어 있다.

Posted by dalbong2

이제 다시 개발 프레임워크 얘기로 가 보도록 하겠다. 이제부터는 Spring.NET 프레임워크를 알아볼 것이다. 앞에까지는 Unity Application Block을 알아봤는데 사실 필자는 이것보다 Spring.NET 프레임워크에 대해서 먼저 들었다. 그러나 마음먹고 공부해본적은 없다. 알고 있는 것은 단지 오픈 소스 프로젝트라는 것 그리고 Spring이라는 이름으로 자바쪽에서 먼저 나왔고 Spring.NET 프레임워크는 자바 버전이 .NET쪽으로 포팅된 것이라는 것 정도이다.

며칠동안 틈나는 대로 Spring.NET 레퍼런스를 읽어보고 있다.  www.springframework.net에 가보면 문서 및 관련 소스를 받아 볼 수 있다. 원서라서 속도가 나질 않아서 아직 다 읽어보지는 못했다. Unity 블럭에서처럼  포스팅을 하면서 공부를 해야 할 것같다. Spring과 관련된 자바진영의 문서는 꽤 있는 듯하다. 그래서 개념은 자바진영의 문서를 통해서 잡고 구현만 .NET진영의 문서를 참조해야 겠다고 생각했다. 그래서 한글로 된 자바진영의 책을 먼저 읽었고 다음으로 앞의 사이트에서 다운받은 레퍼런스 문서로 공부하고 있는 중이다. 이 문서의 메인 주제들을 정리해보면 다음과 같다.

▶ IoC 개념

▶ Configuring

  - configuring object with xml

  - creating objects automatically

  - using parent and child object definition

▶ 객체 Scope

▶ 객체 생명 주기 관리 - 생명주기 관련 interfaces

- IInitializingObject/ init-method

- IDisposable / destory-method

- IObjectPostProcessor

▶ Spring.NET 커스터마이징

- 객체 생성 -> IObjectPostProcessor.PostProcessBeforeInitailization -> IInitializeingObject/init-method의 콜백메소드 AfterPropertiesSet -> IObjectPostProcessor.PostProcessAfterInitailization

▶ 메세지 리소스 관리/사용하기

▶ Validation framework

▶ Aspect Oriented Programming with Spring.NET

▶ 트랜잭션 관리

▶ 예외처리

▶ Object Relational Mapping( ORMapping )

▶ Spring.NET Web Framework

▶ ASP.NET Ajax

▶ Enterprise Application에 Spring.NET 적용 전략

▶ Testing

이 주제들 하나 하나가 모두 굵직 굵직하다. 이것들을 하나씩 붙잡고 개념과 구현을 설명해나가야 할까하는 문제는 아직 결정을 내리지 못하고 있다. 이것들을 모두 설명하기에는 너무 많은 시간이 소모될 것 같고 그다지 투자 대비 효과도 좋지 않을 것 같다는 생각이다.

그렇다면 개념별 Quick start 샘플 중심으로 갈 것이냐 아니면 하나의 샘플로 시작해서 개념들을 완성시켜나가는 방식으로 갈 것이냐. 아니면 Spring.NET 사이트에서 제공하고 있는 샘플 중심으로 공부를 해 나가야 할 것이냐. 아직 결정하지 못했다.

다음 포스트가 언제 올려질지는 모르겠지만, 그 포스트가 올려질때 Spring.NET 스터디 진행방법도 결정될 것으로 보인다.

Posted by dalbong2