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

스프링 프레임워크 개요

스프링은 그 이름 자체로도 많은 의미를 내포하고 있다. 봄! 이 얼마나 설레는 단어인가? 봄이라는 이름만으로도 무거운 J2EE의 사용으로 지친 개발자들에게 이제 겨울이 끝나고 새로운 계절이 돌아오고 있음을 함축적으로 표현해내고 있다. 스프링은 로드 존슨이 쓴 「Expert one-on-one J2EE Design and Development」란 책에서 소개된 소스코드를 기반으로 2003년 2월 오픈소스로 시작된 프로젝트이다. 스프링이 추구하는 바는 크게 두 가지이다.

[1] 복잡하고 무거운 J2EE 기술의 사용을 쉽고 가볍게 만들어주고, 자연스럽게 검증된 최상의 실천 사례들을 구현하도록 함으로써 좋은 프로그램이 작성될 수 있도록 유도한다.

[2] 기존의 잘 알려진 기술들을 프레임워크 내에서 일관된 방법으로 쉽게 사용할 수 있도록 돕는다.

이를 위해 스프링은 다른 프레임워크와는 차별화된 다음과 같은 특징을 가진다.

◆ 스프링은 EJB를 사용하건 하지 않건 관계없이 비즈니스 객체들을 효과적으로 구성하고, 관리하는 방법을 제공하는 데 초점을 맞춘다.

◆ 스프링은 계층화된 아키텍처를 갖고 있으며, 그 중 어떤 부분도 독립적으로 사용될 수 있도록 모듈화되어 있다. 뿐만 아니라 각각의 모듈은 일관된 방법으로 사용할 수 있기 때문에 한번 익숙해지고 나면 사용이 무척 쉽다.

◆ 스프링은 전체 프로젝트의 설정을 관리할 수 있는 일관된 방법을 제공함으로써, 개발자들이 각종 프로퍼티 파일을 작성하지 않도록 유도한다. 이것은 IoC라는 스프링의 특징 때문인데, 객체들간의 의존성이 따로 관리됨으로써 비즈니스 로직이 EJB로 개발되었건 일반 자바 객체로 개발되었건 동일한 방법으로 해당 로직을 이용할 수 있는 이점도 추가된다.

◆ 스프링 기반으로 작성된 애플리케이션은 스프링의 API에 의존하지 않는다. 이것은 어떤 애플리케이션 서버와도 쉽게 연동되도록 하며, 심지어 스프링을 사용하지 않았을 때조차도 비즈니스 로직의 재사용이 가능해지는 요인이 된다.

◆ 스프링은 AOP 지원을 통해 주요 비즈니스 로직과 시스템 전반에 걸친 기능 모듈을 완벽히 분리해내도록 도와준다.

◆ 스프링은 작성된 코드에 대한 유닛 테스트를 쉽게 할 수 있도록 도와준다.

스프링의 기능과 사용 시나리오
현재 스프링은 1.0 버전이 출시된 상태이다. 스프링 홈페이지(www.springframework.org)에서 spring-framework-1.0.2-with-dependencies.zip 파일을 다운받기 바란다. 이 파일은 의존성 있는 관련 라이브러리가 모두 포함된 버전이다. 스프링의 전체 기능은 크게 7개의 모듈로 구성된다(<표 1>).

<그림 2> 스프링의 기능 요소



<표 1> 스프링의 기능 요소


스프링 배포 파일의 압축을 풀면 dist란 디렉토리가 나타난다. 그 안에 있는 spring.jar가 앞에서 언급한 스프링의 모든 기능을 포함하는 파일이다. 각각의 기능 중 필요한 부분을 따로 사용할 경우를 위해 패키지별로 묶은 별도의 JAR 파일이 함께 제공된다.

스프링을 이용한 일반적인 형태의 웹 애플리케이션은 <그림 3>과 같은 구조를 가진다. 톰캣, 웹로직, 웹스피어, JBoss를 포함한 어떤 웹 애플리케이션 서버에서도 동작된다. IoC 컨테이너의 핵심인 Core 패키지와 AOP 지원을 위한 AOP 패키지, 기능을 구현한 빈 객체에 대한 접근을 제공하는 Context 패키지는 반드시 포함되어야 한다. 대부분 데이터베이스 처리를 수행하기 때문에 DAO 패키지도 일반적으로 포함된다.

스프링 애플리케이션은 일반적으로 ORM 솔루션을 채택하고, 특히 하이버네이트와 궁합이 잘 맞기 때문에 하이버네이트를 설치하고 ORM 패키지를 이용하는 경우가 일반적이다. 그 위에서 Web 패키지를 기반으로 Web MVC 패키지를 이용해서 애플리케이션을 개발한다.

<그림 3> 시나리오 1. 완전한 형태의 스프링 웹 애플리케이션


만일 스프링의 Web MVC 패키지를 이용하지 않고, 스트럿츠나 웹워크를 제어 계층에 사용하고 싶다면 Web MVC를 스트럿츠가 대체하는 <그림 4> 외에 같은 구조로 웹 애플리케이션이 개발될 것이다.

<그림 4> 시나리오 2. 서드파티 WAF와 ORM을 연계한 웹 애플리케이션


이 밖에도 EJB를 사용하는 경우 AbstractEnterpriseBean이라는 POJO를 이용해서 EJB를 스프링에서 관리하도록 하고, SlsbInvoker를 이용해서 EJB에 대한 접근 경로를 제공하는 EJB 사용 유형이 있다. 또한 웹 서비스를 포함한 다른 애플리케이션과 연동하는 경우를 위해 Remote 패키지가 제공되기도 한다.

스프링은 그 자체로도 한 권의 책을 쓸 수 있을 만큼 방대한 기술이다. 그 모두를 짧은 연재를 통해 소개한다는 것은 불가능하다. 그러한 이유로 스프링에 대한 소개는 이쯤에서 마무리하고, 스프링을 이해하기 위해 반드시 알아야 하는 IoC(Inversion of Control) 컨테이너의 개념과 AOP(Aspect Oriented Programming)를 간단히 설명하고, 2개의 작은 예제를 소개하는 것으로 이번 글을 마무리하겠다. 부족한 설명은 필자들이 운영하는 VSSH 포럼이나, 스프링 관련 기사 및 자료들을 정리한 리소스 맵을 통해 여러분 스스로 익혀나가기를 바란다.

IoC 컨테이너와 AOP
스프링은 다른 프로젝트에서 개발된 컴포넌트를 조립해서 응집력 있는 애플리케이션의 개발이 가능하도록 도와주는 IoC 컨테이너이며, 다른 말로 경량급 컨테이너(Lightweight Container)라고도 한다. IoC 컨테이너의 또 다른 종류로는 PicoContainer와 아파치의 아발론, 그리고 HiveMind 등이 있다.

그 중에서 현재 가장 널리 사용되고 있는 것은 스프링과 PicoContainer이다. IoC 컨테이너는 다른 컨테이너와 달리 애플리케이션 코드와 컨테이너간의 의존성을 최소화하는 것이 특징이다.

IoC 컨테이너가 컨테이너에 대한 의존성을 최소화하면서 컴포넌트를 엮어주는 일을 수행하는 밑바탕에는 제어 역행화(Inversion of Control)라는 개념이 깔려 있다. 제어 역행화는 리팩토링의 저자이기도 한 마틴 파울러의 홈페이지에 잘 정의되어 있다.

제어 역행화라는 용어는 직관적이지 못하기 때문에, 또 다른 말로 연관성 삽입(Dependency Injection)이라고도 불려진다. 연관성 삽입 패턴은 컴포넌트의 설정을 그것의 사용에서 분리해야 한다는 원칙(The principle of separating configuration from use)에서 출발한다. 그러한 원칙을 위한 또 다른 사례는 J2EE 패턴 중 서비스 로케이터(Service Locator) 패턴이다.

의존성 삽입 패턴을 이해하기 위해 간단한 예를 하나 살펴보도록 하자. 어느 특정 감독이 만든 영화를 검색해서 그 결과를 전달해주는 컴포넌트를 사용하는 것이다. 코드에서 보여지는 findAll()이라는 메쏘드를 가지는 finder 객체가 필요하단 사실을 알 수 있다.

일반적으로 이럴 때 기능 확장을 위해 MovieFinder와 같은 인터페이스를 작성하게 된다.


public interface MovieFinder {
    List findAll();
}


영화 정보가 콜론으로 구분된 CSV 파일에 기록되어 있다면, MovieFinder 인터페이스를 구현한 ColonDelimitedMovieFinder 클래스가 필요할 것이다. 또한 그 정보는 MovieLister에 생성자를 이용해서 초기화될 것이다.


class MovieLister...
    private MovieFinder finder;
    public MovieLister() {
        finder = new ColonDelimitedMovieFinder("movies1.txt");
    }


전체적인 시스템 구조는 <그림 5>와 같은 형태이다. MovieLister 클래스는 MovieFinder 인터페이스 정보만을 이용해서 기능을 구현할 수 있지만, 해당 기능을 사용하기 위해 MovieFinderImpl 클래스 중에서 ColonDelimitedMovieFinder를 이용한다는 구체적인 설정 정보가 클래스의 코드에 포함되어 있다. 이것은 데이터가 DB나 XML로 변경되어 RDBMovieFinder나 XMLMovieFinder로 교체될 경우 코드를 수정해서 다시 빌드해야 한다는 사실을 의미한다.

<그림 5> 일반적인 제어 흐름을 통한 의존성 표현


사실 MovieLister는 정보가 CSV 파일에 기록되어 있건, DB에 기록되건, XML에 기록되건 영향을 받지 않아야 정상이라고 할 수 있다. 어떻게 하면 이처럼 불필요한 의존 관계를 없앨 수 있는 것일까?

<그림 6> 제어역행화 패턴을 통한 의존성 삽입


그 해답은 설정 정보에 따라 어떤 구현 객체를 사용할 것인지를 결정하는 어셈블러를 이용해서 <그림 6>에서 보여지는 구조로 애플리케이션을 개발하는 것이다.

MovieLister 클래스에서 선언된 MovieFinder 타입의 finder를 초기화하는 방법은 크게 2가지가 있다. 첫 번째는 생성자를 통해 속성을 초기화하는 것이고, 두 번째는 setMovieFinder()와 같은 Setter 메쏘드를 이용하는 것이다. 이러한 설정을 자동화하는 어셈블러를 구현해 놓은 것이 바로 IoC 컨테이너이다.

연관성 삽입은 크게 Constructor Injection, Setter Injection, Interface Injection의 3가지 유형을 가진다. Constructor Injection이 생성자를 이용해서 의존성을 설정해주는 방법이고, Setter Injection이 Setter 메쏘드를 이용해서 의존성을 설정해주는 방법이다. Pico Container는 Constructor Injection을 주로 사용하고, 스프링은 자바 빈 규칙을 이용한 Setter Injection을 주로 사용한다.

스프링의 IoC적인 특징은 AOP를 구현하는 핵심적인 원리가 되기도 한다. AOP는 아직 국내에는 생소한 분야이다. 김대곤님이 작성한 간단한 소개글을 통해 AOP의 개념을 잡아보도록 하자.

자금 이체를 하는 프로그램을 작성한다고 가정해 보자. 출금 계좌와 입금 계좌 그리고 이체 금액을 입력받아 SQL 문장 또는 함수 한 번 돌리는 것으로 모든 프로그래밍이 끝나는가? 그렇지 않다. 해킹을 방지하기 위해 사용자가 적절한 보안 프로그램을 설치했는지 점검하는 코드도 있어야 하고, 사용자가 인증되었는지 점검하는 코드도 써야 하고, 상대방 은행에서 적절하게 처리되었는지도 점점해야 하고, 혹시 사용자가 이체 버튼을 두 번 누른 것은 아닌가 체크해야 하고, 시스템 로그도 남겨야 한다.

즉, 구현하려고 하는 기능 뿐 아니라 보안, 인증, 로그, 성능과 같은 다른 기능들도 녹아 있어야 한다는 뜻이다. 어쩌면 이체를 위한 코드보다 잡다한 다른 측면의 문제들을 다루는 코드가 더 길어질 수 있다. 이런 코드들은 입금이나 출금 같은 다른 곳에서도 공통적으로 사용되는 것이다.

<그림 7> AOP의 개념


구현하려고 하는 비즈니스 기능들을 AOP에서는 Primary(Core) Concern이라는 용어로 표현한다. 보안, 로그, 인증과 같이 시스템 전반적으로 산재된 기능들은 Cross-cutting concern이라고 부른다. AOP는 Cross-cutting concern을 어떻게 다룰 것인가에 대한 새로운 패러다임이라고 할 수 있다.

그럼 AOP가 등장하기 이전에 우리는 어떻게 Cross-cutting Concern을 처리해왔을까? 매우 간단하다. Primary Concern를 구현한 프로그램에 함께 포함시켰다. Primary concern, Cross-cutting concern이 하나의 프로그램 안에 들어가게 되면, 프로그램을 이해하기가 힘들고, Cross-cutting concern 코드가 여기저기에 산재되어 수정하기 힘들게 된다. 당연히 생산성 떨어지고, 품질 떨어지고, 유지보수 비용은 많이 들게 된다.

그럼 AOP는 Cross-cutting concern를 어떻게 처리하는가? AOP에서는 Primary Concern 구현하는 코드 따로, Cross-cutting concern 구현하는 코드도 따로 작성한다. 나중에 2개를 조합한 완벽한 애플리케이션이 만들어지는 것이다.

AOP에서는 Cross-cutting concern 구현한 코드를 Advice라고 하며, Primary concern 구현한 코드를 Code라고 부른다. Code와 Advice를 연결해주는 설정 정보를 Point-cut이라고 하며, 둘을 조합해서 애플리케이션으로 완성하는 과정을 Weaving(조합)이라고 부른다.

기술적 용어로서의 ‘Aspect’는 Advice와 Point-cut을 함께 지칭하는 단어이다. Point-cut은 어떤 Advice를 Code 어느 위치에 둘 것인가 하는 것이다. 스프링의 AOP 패키지는 이러한 AOP 개념을 구현한 것으로, 그 기반에는 설정을 이용으로부터 분리하는 의존성 삽입 패턴이 녹아 있다.

예제 1. 스프링의 WebMVC를 이해하자
이제 스프링을 이해하기 위해 2가지 예제를 살펴볼 것이다. 첫 번째 예제는 스프링 공식 홈페이지에 소개된 것으로 순수하게 스프링이 제공하는 기능만을 이용해서 개발된 예제이다.

국내에는 스프링과 관련한 자료가 전혀 없는 관계로 초보자들을 위해 이 예제를 편역하고 몇 가지 설명을 추가해 총 4부 8개의 강좌로 재구성해서 VSSH 포럼에 등록해 두었다. 자세한 설명을 원하는 독자는 관련 자료를 참고하기 바란다.

여기서 소개하는 예제는 앞에서 소개한 강좌 중 2부까지 구현된 샘플이다. 예제를 실행해보기 위해서는 톰캣과 Ant, JDK 등이 설치되어 있어야 한다. 예제(springapp.zip)를 다운로드한 다음, 작업 디렉토리에서 압축을 풀면 build.properties 파일이 보일 것이다.

해당 파일을 열어 배포될 경로와 톰캣 홈, 그리고 톰캣 관리자의 URL/아이디/패스워드 정보를 자신의 환경에 맞게 변경한다. 그런 다음 build와 deploy 타겟을 ant를 이용해서 순서대로 실행한다. 웹 브라우저를 이용해서 http://localhost:8080/springapp로 접속하면 아래와 같은 결과 화면을 볼 수 있을 것이다.

<그림 8> 스프링 WebMVC 예제화면


우선 예제의 web.xml 설정부터 살펴보도록 하자. DispatcherServlet이 springapp란 이름으로 등록되어 있고, htm으로 끝나는 모든 URL 패턴이 해당 서블릿으로 매핑되어 있다. DispatcherServlet은 스트럿츠의 ActionServlet의 기능과 유사한 일종의 FrontController 서블릿이다.


<web-app>
  <servlet>
    <servlet-name>springapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springapp</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>
</web-app>


그렇다면 DispatcherServlet이 처리하는 제어 행위를 위한 struts-config.xml과 같은 설정 파일이 필요할 것이다. 스프링 MVC 패키지에서는 그 파일을 해당 서블릿 이름에 "-servlet.xml"을 붙여 작성하도록 권장하고 있다. 앞의 경우 서블릿의 이름을 springapp로 주었기 때문에 springapp-servlet.xml이 설정 파일이 되는 것이다.

설정 파일을 보면, <beans>라는 루트 엘리먼트 밑에 <bean>이라는 설정이 반복되어 적용되고 있다. 이것이 스프링에서 BeanFactory를 이용해서 제공하고 있는 Setter Injection을 통해 설정과 그 사용을 분리하는 방법이다.


<beans>
    <bean id="springappController" class="web.SpringappController">
        <property name="productManager">
            <ref bean="prodMan"/>
        </property>
    </bean>
    <bean id="prodMan" class="bus.ProductManager">
        <property name="products">
            <list>
                <ref bean="product1"/>
                <ref bean="product2"/>
                <ref bean="product3"/>
            </list>
        </property>
    </bean>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hello.htm">springappController</prop>
            </props>
        </property>
    </bean>
    ... (생략) ...
</beans>


전체 애플리케이션의 동작 과정을 한번 살펴보자.

<그림 9> 스프링 WebMVC 예제동작 원리


웰컴 파일인 index.jsp에는 hello.htm에 대한 링크가 걸려있다. hello.htm에 대한 요청은 web.xml의 설정에 의해 springapp로 이름지어진 스프링의 web 패키지의 DispatcherServlet으로 전달된다. DispatcherServlet은 스프링의 core 패키지에 있는 BeanFactory를 이용해 IoC 컨테이너의 기본 원리에 따라 동작된다. 설정 파일인 springapp-servlet.xml을 통해 모든 의존성이 결정되어 제어가 반전되는 현상을 볼 수 있다.

<그림 10> VSSH 고객등록 예제화면


우선 <prop key="/hello.htm">springappController</prop>에 의해 hello.htm 요청을 SpringappController 클래스가 처리하게 되는데 그 사실을 DispatcherServlet은 전혀 모른다. SpringappController는 요청을 처리하는 과정에서 ProductManager의 구현을 필요로 하는데, 그게 어떤 클래스의 인스턴스인지를 본인은 모른다. 설정에 의해 자동적으로 bus.ProductManager가 결정되고, 해당 인스턴스가 거꾸로 SpringappController에 찾아와 연결된다. 이러한 제어의 반전이 스프링을 IoC(Inversion of Control) 컨테이너라고 부르게 하는 이유다.

다음 코드는 스트럿츠의 액션과 유사한 역할을 수행하는 Controller 구현 샘플이다. ProductManager를 사용하고 있는데 신기하게도 setProductManager() 메쏘드만 있을 뿐, 인스턴스를 초기화하는 호출이 이루어지지 않는다. 스프링이 빈 설정을 참고해서 자동화하기 때문이다. handleRequest()의 결과로 ModelAndView가 리턴되어 넘어가는데, 이것은 스트럿츠의 ActionForward와 컨텍스트 정보를 합친 것으로 이해하면 되겠다.


public class SpringappController implements Controller {
protected final Log logger = LogFactory.getLog(getClass());
    private ProductManager prodMan;
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String now = (new java.util.Date()).toString();
        logger.info("returning hello view with " + now);
        Map myModel = new HashMap();
        myModel.put("now", now);
        myModel.put("products", getProductManager().getProducts());
        return new ModelAndView("hello", "model", myModel);
    }
    public void setProductManager(ProductManager pm) {
        prodMan = pm;
    }
    public ProductManager getProductManager() {
        return prodMan;
    }
}


예제 2. 스프링과 스트럿츠, 하이버네이트를 통합한 예제
그럼 스프링을 기존의 스트럿츠 환경과 어떻게 연동할 수 있을까? 대표적인 ORM인 하이버네이트와는 어떻게 연동할 수 있을까? 이를 소개하기 위해 간단한 회원 관리 샘플을 개발했다. 이 예제는 벨로시티와 스트럿츠, 스프링, 하이버네이트를 통합하고자 하는 VSSH의 최종적인 모습을 보여주며, DBMS는 HSQLDB를 사용했다.

예제 파일(vssh.zip)을 다운받아 자신의 작업 디렉토리에 압축을 풀고, was.properties 파일을 열어 자신의 운영 환경에 맞게 경로를 수정한다. 그런 다음, ant를 이용해서 makeDist, war-deploy 태스크를 순서대로 실행하면 빌드 및 배포 과정이 끝난다. 웹 브라우저를 이용해서 http://localhost:8080/vssh-b01에 접속하면 아래와 같은 결과 화면을 볼 수 있을 것이다.

이 예제는 다음 강좌에서 다시 자세히 설명할 예정인데, 미리 상세한 내용을 알고 싶은 분은 LoveLazur의 홈페이지를 참고하기 바란다. 이번 예제부터는 복잡하기 때문에 이클립스와 같은 오픈소스 IDE를 이용하는 것이 좋다.

스트럿츠와 스프링의 연동 방법은 각각을 이해하고 있다면 아주 간단히 처리된다. WEB-INF/lib 디렉토리에 각각의 라이브러리 JAR 파일을 추가한 다음, 스트럿츠 설정 파일인 struts-config.xml에 플러그인 설정만 하나 추가하면 되는 것이다. 플러그인으로 설정한 ContextLoader는 전달된 설정 파일을 이용해서 전체 애플리케이션 구성에 사용될 ApplicationContext를 구성하는 역할을 수행한다.


<struts-config>
<form-beans> ... </form-beans>
<action-mappings> ... </action-mappings>
<message-resources parameter="messages"/>
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
     <set-property property="contextConfigLocation"
            value="/WEB-INF/applicationContext.xml, /WEB-INF/action-servlet.xml"/>
</plug-in>
</struts-config>


applicationContext.xml에는 애플리케이션 로직과 관련된 객체들을 앞서 설명한 빈 설정 방식으로 연동해서, 제어 역행화가 이루어지도록 하면 된다.


<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url"><value>jdbc:hsqldb:data/vsshdb</value></property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
  <!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="mappingResources">
<list><value>model/Customer.hbm.xml</value></list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<!-- Transaction manager , can replace class Attribute ex.Transaction-->
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<bean id="customerDAO" class="dao.CustomerDAOImpl">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>    
</beans>


주의할 점은 모든 클래스에서 사용되는 객체 참조에서 항상 자바 빈 규칙에 따르는 setter()를 만들고, 직접 인스턴스를 초기화하지 않고 객체들의 의존성을 설정 파일에 적어줌으로써 스프링이 자동으로 의존성 관계를 삽입하도록 해야 한다는 것이다.

다음의 CustomerAction을 살펴보면 ICustomerBizManager 타입의 cmgr이라는 인스턴스를 갖고 있는데, setCustomermanager()란 메쏘드만 있을 뿐 어디에도 초기화하는 루틴이 포함되어 있지 않다. 그런데도 cmgr.createCustomer()를 사용하고 있는 것을 볼 수 있다.


public class CustomerAction extends BaseAction {
private ICustomerBizManager cmgr = null;
public void setCustomerManager(ICustomerBizManager cmgr) { //def.
this.cmgr = cmgr;
}
public ActionForward list(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
request.setAttribute("custlist", cmgr.getCustomers());
return mapping.findForward("list");
}
public ActionForward create(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
DynaActionForm custForm = (DynaActionForm) form;
cmgr.createCustomer((Customer) custForm.get("cust"));
ActionMessages messages = new ActionMessages();
messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
                "cust.saved"));
return list(mapping, form, request, response);
}
}


이것은 action-servlet.xml과 applicationContext.xml에서 다음과 같은 설정이 되어 있으므로 그 정보에 따라 적절한 customerManager가 결정되기 때문에 가능해진다.


<beans>
<bean name="/cust" class="control.CustomerAction" singleton="false">
<property name="customerManager">
<ref bean="customerBizManager"/>
</property>
</bean>
<bean id="customerBizManager"
        class="org.springframework.transaction.interceptor.
TransactionProxyFactoryBean">
<property name="transactionManager"><ref local="transactionManager"/></property>
<property name="target"><ref local="customerBizManagerTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>    
</beans>


대안 기술에 대해 관심을 갖자
지금까지 스프링 프레임워크의 필요성을 설명하고, 기본 개념이라 할 수 있는 연관성 삽입 패턴과 AOP에 대해 간단히 살펴보았다. 또한 스프링의 특징과 스프링을 활용한 예제를 소개했다. 스프링이 아직 국내에 소개된 적이 없다보니 외국의 문서와 예제, 서적들을 뒤적거리면서 관련 자료를 정리하는데 한달이 넘는 시간이 소요되었다.

하지만 아쉽게도 그동안 정리한 내용을 모두 소개하기에는 할당된 지면이 너무 부족해 꼭 필요한 부분만을 알려주는데 그친 듯하다. 설명이 미흡한 부분은 필자들의 VSSH 포럼을 활용하고, 궁금한 점이 있으면 언제든 질문해 주기 바란다.

솔직히 말해 이 글을 쓰고 있는 필자도 여러분보다 몇 달 먼저 스프링을 공부한 정도의 수준밖에 되지 않는다. 미국, 일본, 중국, 독일 어디서나 스프링 관련 자료를 쉽게 구할 수 있었지만, 안타깝게도 국내에는 전혀 자료가 없었다.

해외에서는 크게 주목을 받고 있는 스프링과 IoC 컨테이너에 대한 내용들이 왜 국내에서는 전혀 다루어지지 않고 있는지를 곰곰이 생각해본다. 우리네 개발자들은 촉박한 개발 일정에 쫓겨 신기술이나 대안 기술들을 공부할 만큼 다들 여유가 없는 것일까? 아니면 그러한 기술을 이미 익히고 있는 개발자들이 자신의 머리 속에 그 지식을 꼭꼭 숨겨두고 공개하지 않는 것일까?

지금 자바 커뮤니티는 주류 기술의 버전 향상과 신기술의 출현 그리고 오픈소스 기반의 다양한 대안 기술의 등장으로 어느 때보다 급격한 기술 변화를 눈앞에 두고 있다. 아직 그러한 변화를 느끼지 못하고 있는 개발자라면 지금이 바로 그러한 변화에 대한 대비를 시작해야 할 때임을 깨달아야 한다. 또한, 그 과정에서 얻어진 노하우를 공유함으로써 더 큰 이익이 자신에게 돌아온다는 사실도 잊지 말기 바란다. @

이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by 백성용 헬로우보이
스프링 프레임워크 개요
스프링은 그 이름 자체로도 많은 의미를 내포하고 있다. 봄! 이 얼마나 설레는 단어인가? 봄이라는 이름만으로도 무거운 J2EE의 사용으로 지친 개발자들에게 이제 겨울이 끝나고 새로운 계절이 돌아오고 있음을 함축적으로 표현해내고 있다. 스프링은 로드 존슨이 쓴 「Expert one-on-one J2EE Design and Development」란 책에서 소개된 소스코드를 기반으로 2003년 2월 오픈소스로 시작된 프로젝트이다. 스프링이 추구하는 바는 크게 두 가지이다.

[1] 복잡하고 무거운 J2EE 기술의 사용을 쉽고 가볍게 만들어주고, 자연스럽게 검증된 최상의 실천 사례들을 구현하도록 함으로써 좋은 프로그램이 작성될 수 있도록 유도한다.

[2] 기존의 잘 알려진 기술들을 프레임워크 내에서 일관된 방법으로 쉽게 사용할 수 있도록 돕는다.

이를 위해 스프링은 다른 프레임워크와는 차별화된 다음과 같은 특징을 가진다.

◆ 스프링은 EJB를 사용하건 하지 않건 관계없이 비즈니스 객체들을 효과적으로 구성하고, 관리하는 방법을 제공하는 데 초점을 맞춘다.

◆ 스프링은 계층화된 아키텍처를 갖고 있으며, 그 중 어떤 부분도 독립적으로 사용될 수 있도록 모듈화되어 있다. 뿐만 아니라 각각의 모듈은 일관된 방법으로 사용할 수 있기 때문에 한번 익숙해지고 나면 사용이 무척 쉽다.

◆ 스프링은 전체 프로젝트의 설정을 관리할 수 있는 일관된 방법을 제공함으로써, 개발자들이 각종 프로퍼티 파일을 작성하지 않도록 유도한다. 이것은 IoC라는 스프링의 특징 때문인데, 객체들간의 의존성이 따로 관리됨으로써 비즈니스 로직이 EJB로 개발되었건 일반 자바 객체로 개발되었건 동일한 방법으로 해당 로직을 이용할 수 있는 이점도 추가된다.

◆ 스프링 기반으로 작성된 애플리케이션은 스프링의 API에 의존하지 않는다. 이것은 어떤 애플리케이션 서버와도 쉽게 연동되도록 하며, 심지어 스프링을 사용하지 않았을 때조차도 비즈니스 로직의 재사용이 가능해지는 요인이 된다.

◆ 스프링은 AOP 지원을 통해 주요 비즈니스 로직과 시스템 전반에 걸친 기능 모듈을 완벽히 분리해내도록 도와준다.

◆ 스프링은 작성된 코드에 대한 유닛 테스트를 쉽게 할 수 있도록 도와준다.

스프링의 기능과 사용 시나리오
현재 스프링은 1.0 버전이 출시된 상태이다. 스프링 홈페이지(www.springframework.org)에서 spring-framework-1.0.2-with-dependencies.zip 파일을 다운받기 바란다. 이 파일은 의존성 있는 관련 라이브러리가 모두 포함된 버전이다. 스프링의 전체 기능은 크게 7개의 모듈로 구성된다(<표 1>).

<그림 2> 스프링의 기능 요소


<표 1> 스프링의 기능 요소

스프링 배포 파일의 압축을 풀면 dist란 디렉토리가 나타난다. 그 안에 있는 spring.jar가 앞에서 언급한 스프링의 모든 기능을 포함하는 파일이다. 각각의 기능 중 필요한 부분을 따로 사용할 경우를 위해 패키지별로 묶은 별도의 JAR 파일이 함께 제공된다.

스프링을 이용한 일반적인 형태의 웹 애플리케이션은 <그림 3>과 같은 구조를 가진다. 톰캣, 웹로직, 웹스피어, JBoss를 포함한 어떤 웹 애플리케이션 서버에서도 동작된다. IoC 컨테이너의 핵심인 Core 패키지와 AOP 지원을 위한 AOP 패키지, 기능을 구현한 빈 객체에 대한 접근을 제공하는 Context 패키지는 반드시 포함되어야 한다. 대부분 데이터베이스 처리를 수행하기 때문에 DAO 패키지도 일반적으로 포함된다.

스프링 애플리케이션은 일반적으로 ORM 솔루션을 채택하고, 특히 하이버네이트와 궁합이 잘 맞기 때문에 하이버네이트를 설치하고 ORM 패키지를 이용하는 경우가 일반적이다. 그 위에서 Web 패키지를 기반으로 Web MVC 패키지를 이용해서 애플리케이션을 개발한다.

<그림 3> 시나리오 1. 완전한 형태의 스프링 웹 애플리케이션

만일 스프링의 Web MVC 패키지를 이용하지 않고, 스트럿츠나 웹워크를 제어 계층에 사용하고 싶다면 Web MVC를 스트럿츠가 대체하는 <그림 4> 외에 같은 구조로 웹 애플리케이션이 개발될 것이다.

<그림 4> 시나리오 2. 서드파티 WAF와 ORM을 연계한 웹 애플리케이션

이 밖에도 EJB를 사용하는 경우 AbstractEnterpriseBean이라는 POJO를 이용해서 EJB를 스프링에서 관리하도록 하고, SlsbInvoker를 이용해서 EJB에 대한 접근 경로를 제공하는 EJB 사용 유형이 있다. 또한 웹 서비스를 포함한 다른 애플리케이션과 연동하는 경우를 위해 Remote 패키지가 제공되기도 한다.

스프링은 그 자체로도 한 권의 책을 쓸 수 있을 만큼 방대한 기술이다. 그 모두를 짧은 연재를 통해 소개한다는 것은 불가능하다. 그러한 이유로 스프링에 대한 소개는 이쯤에서 마무리하고, 스프링을 이해하기 위해 반드시 알아야 하는 IoC(Inversion of Control) 컨테이너의 개념과 AOP(Aspect Oriented Programming)를 간단히 설명하고, 2개의 작은 예제를 소개하는 것으로 이번 글을 마무리하겠다. 부족한 설명은 필자들이 운영하는 VSSH 포럼이나, 스프링 관련 기사 및 자료들을 정리한 리소스 맵을 통해 여러분 스스로 익혀나가기를 바란다.

IoC 컨테이너와 AOP
스프링은 다른 프로젝트에서 개발된 컴포넌트를 조립해서 응집력 있는 애플리케이션의 개발이 가능하도록 도와주는 IoC 컨테이너이며, 다른 말로 경량급 컨테이너(Lightweight Container)라고도 한다. IoC 컨테이너의 또 다른 종류로는 PicoContainer와 아파치의 아발론, 그리고 HiveMind 등이 있다.

그 중에서 현재 가장 널리 사용되고 있는 것은 스프링과 PicoContainer이다. IoC 컨테이너는 다른 컨테이너와 달리 애플리케이션 코드와 컨테이너간의 의존성을 최소화하는 것이 특징이다.

IoC 컨테이너가 컨테이너에 대한 의존성을 최소화하면서 컴포넌트를 엮어주는 일을 수행하는 밑바탕에는 제어 역행화(Inversion of Control)라는 개념이 깔려 있다. 제어 역행화는 리팩토링의 저자이기도 한 마틴 파울러의 홈페이지에 잘 정의되어 있다.

제어 역행화라는 용어는 직관적이지 못하기 때문에, 또 다른 말로 연관성 삽입(Dependency Injection)이라고도 불려진다. 연관성 삽입 패턴은 컴포넌트의 설정을 그것의 사용에서 분리해야 한다는 원칙(The principle of separating configuration from use)에서 출발한다. 그러한 원칙을 위한 또 다른 사례는 J2EE 패턴 중 서비스 로케이터(Service Locator) 패턴이다.

의존성 삽입 패턴을 이해하기 위해 간단한 예를 하나 살펴보도록 하자. 어느 특정 감독이 만든 영화를 검색해서 그 결과를 전달해주는 컴포넌트를 사용하는 것이다. 코드에서 보여지는 findAll()이라는 메쏘드를 가지는 finder 객체가 필요하단 사실을 알 수 있다.

일반적으로 이럴 때 기능 확장을 위해 MovieFinder와 같은 인터페이스를 작성하게 된다.

public interface MovieFinder {
    List findAll();
}

영화 정보가 콜론으로 구분된 CSV 파일에 기록되어 있다면, MovieFinder 인터페이스를 구현한 ColonDelimitedMovieFinder 클래스가 필요할 것이다. 또한 그 정보는 MovieLister에 생성자를 이용해서 초기화될 것이다.

class MovieLister...
    private MovieFinder finder;
    public MovieLister() {
        finder = new ColonDelimitedMovieFinder("movies1.txt");
    }

전체적인 시스템 구조는 <그림 5>와 같은 형태이다. MovieLister 클래스는 MovieFinder 인터페이스 정보만을 이용해서 기능을 구현할 수 있지만, 해당 기능을 사용하기 위해 MovieFinderImpl 클래스 중에서 ColonDelimitedMovieFinder를 이용한다는 구체적인 설정 정보가 클래스의 코드에 포함되어 있다. 이것은 데이터가 DB나 XML로 변경되어 RDBMovieFinder나 XMLMovieFinder로 교체될 경우 코드를 수정해서 다시 빌드해야 한다는 사실을 의미한다.

<그림 5> 일반적인 제어 흐름을 통한 의존성 표현

사실 MovieLister는 정보가 CSV 파일에 기록되어 있건, DB에 기록되건, XML에 기록되건 영향을 받지 않아야 정상이라고 할 수 있다. 어떻게 하면 이처럼 불필요한 의존 관계를 없앨 수 있는 것일까?

<그림 6> 제어역행화 패턴을 통한 의존성 삽입

그 해답은 설정 정보에 따라 어떤 구현 객체를 사용할 것인지를 결정하는 어셈블러를 이용해서 <그림 6>에서 보여지는 구조로 애플리케이션을 개발하는 것이다.

MovieLister 클래스에서 선언된 MovieFinder 타입의 finder를 초기화하는 방법은 크게 2가지가 있다. 첫 번째는 생성자를 통해 속성을 초기화하는 것이고, 두 번째는 setMovieFinder()와 같은 Setter 메쏘드를 이용하는 것이다. 이러한 설정을 자동화하는 어셈블러를 구현해 놓은 것이 바로 IoC 컨테이너이다.

연관성 삽입은 크게 Constructor Injection, Setter Injection, Interface Injection의 3가지 유형을 가진다. Constructor Injection이 생성자를 이용해서 의존성을 설정해주는 방법이고, Setter Injection이 Setter 메쏘드를 이용해서 의존성을 설정해주는 방법이다. Pico Container는 Constructor Injection을 주로 사용하고, 스프링은 자바 빈 규칙을 이용한 Setter Injection을 주로 사용한다.

스프링의 IoC적인 특징은 AOP를 구현하는 핵심적인 원리가 되기도 한다. AOP는 아직 국내에는 생소한 분야이다. 김대곤님이 작성한 간단한 소개글을 통해 AOP의 개념을 잡아보도록 하자.

자금 이체를 하는 프로그램을 작성한다고 가정해 보자. 출금 계좌와 입금 계좌 그리고 이체 금액을 입력받아 SQL 문장 또는 함수 한 번 돌리는 것으로 모든 프로그래밍이 끝나는가? 그렇지 않다. 해킹을 방지하기 위해 사용자가 적절한 보안 프로그램을 설치했는지 점검하는 코드도 있어야 하고, 사용자가 인증되었는지 점검하는 코드도 써야 하고, 상대방 은행에서 적절하게 처리되었는지도 점점해야 하고, 혹시 사용자가 이체 버튼을 두 번 누른 것은 아닌가 체크해야 하고, 시스템 로그도 남겨야 한다.

즉, 구현하려고 하는 기능 뿐 아니라 보안, 인증, 로그, 성능과 같은 다른 기능들도 녹아 있어야 한다는 뜻이다. 어쩌면 이체를 위한 코드보다 잡다한 다른 측면의 문제들을 다루는 코드가 더 길어질 수 있다. 이런 코드들은 입금이나 출금 같은 다른 곳에서도 공통적으로 사용되는 것이다.

<그림 7> AOP의 개념

구현하려고 하는 비즈니스 기능들을 AOP에서는 Primary(Core) Concern이라는 용어로 표현한다. 보안, 로그, 인증과 같이 시스템 전반적으로 산재된 기능들은 Cross-cutting concern이라고 부른다. AOP는 Cross-cutting concern을 어떻게 다룰 것인가에 대한 새로운 패러다임이라고 할 수 있다.

그럼 AOP가 등장하기 이전에 우리는 어떻게 Cross-cutting Concern을 처리해왔을까? 매우 간단하다. Primary Concern를 구현한 프로그램에 함께 포함시켰다. Primary concern, Cross-cutting concern이 하나의 프로그램 안에 들어가게 되면, 프로그램을 이해하기가 힘들고, Cross-cutting concern 코드가 여기저기에 산재되어 수정하기 힘들게 된다. 당연히 생산성 떨어지고, 품질 떨어지고, 유지보수 비용은 많이 들게 된다.

그럼 AOP는 Cross-cutting concern를 어떻게 처리하는가? AOP에서는 Primary Concern 구현하는 코드 따로, Cross-cutting concern 구현하는 코드도 따로 작성한다. 나중에 2개를 조합한 완벽한 애플리케이션이 만들어지는 것이다.

AOP에서는 Cross-cutting concern 구현한 코드를 Advice라고 하며, Primary concern 구현한 코드를 Code라고 부른다. Code와 Advice를 연결해주는 설정 정보를 Point-cut이라고 하며, 둘을 조합해서 애플리케이션으로 완성하는 과정을 Weaving(조합)이라고 부른다.

기술적 용어로서의 ‘Aspect’는 Advice와 Point-cut을 함께 지칭하는 단어이다. Point-cut은 어떤 Advice를 Code 어느 위치에 둘 것인가 하는 것이다. 스프링의 AOP 패키지는 이러한 AOP 개념을 구현한 것으로, 그 기반에는 설정을 이용으로부터 분리하는 의존성 삽입 패턴이 녹아 있다.

예제 1. 스프링의 WebMVC를 이해하자
이제 스프링을 이해하기 위해 2가지 예제를 살펴볼 것이다. 첫 번째 예제는 스프링 공식 홈페이지에 소개된 것으로 순수하게 스프링이 제공하는 기능만을 이용해서 개발된 예제이다.

국내에는 스프링과 관련한 자료가 전혀 없는 관계로 초보자들을 위해 이 예제를 편역하고 몇 가지 설명을 추가해 총 4부 8개의 강좌로 재구성해서 VSSH 포럼에 등록해 두었다. 자세한 설명을 원하는 독자는 관련 자료를 참고하기 바란다.

여기서 소개하는 예제는 앞에서 소개한 강좌 중 2부까지 구현된 샘플이다. 예제를 실행해보기 위해서는 톰캣과 Ant, JDK 등이 설치되어 있어야 한다. 예제(springapp.zip)를 다운로드한 다음, 작업 디렉토리에서 압축을 풀면 build.properties 파일이 보일 것이다.

해당 파일을 열어 배포될 경로와 톰캣 홈, 그리고 톰캣 관리자의 URL/아이디/패스워드 정보를 자신의 환경에 맞게 변경한다. 그런 다음 build와 deploy 타겟을 ant를 이용해서 순서대로 실행한다. 웹 브라우저를 이용해서 http://localhost:8080/springapp로 접속하면 아래와 같은 결과 화면을 볼 수 있을 것이다.

<그림 8> 스프링 WebMVC 예제화면

우선 예제의 web.xml 설정부터 살펴보도록 하자. DispatcherServlet이 springapp란 이름으로 등록되어 있고, htm으로 끝나는 모든 URL 패턴이 해당 서블릿으로 매핑되어 있다. DispatcherServlet은 스트럿츠의 ActionServlet의 기능과 유사한 일종의 FrontController 서블릿이다.

<web-app>
  <servlet>
    <servlet-name>springapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springapp</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>
</web-app>

그렇다면 DispatcherServlet이 처리하는 제어 행위를 위한 struts-config.xml과 같은 설정 파일이 필요할 것이다. 스프링 MVC 패키지에서는 그 파일을 해당 서블릿 이름에 "-servlet.xml"을 붙여 작성하도록 권장하고 있다. 앞의 경우 서블릿의 이름을 springapp로 주었기 때문에 springapp-servlet.xml이 설정 파일이 되는 것이다.

설정 파일을 보면, <beans>라는 루트 엘리먼트 밑에 <bean>이라는 설정이 반복되어 적용되고 있다. 이것이 스프링에서 BeanFactory를 이용해서 제공하고 있는 Setter Injection을 통해 설정과 그 사용을 분리하는 방법이다.

<beans>
    <bean id="springappController" class="web.SpringappController">
        <property name="productManager">
            <ref bean="prodMan"/>
        </property>
    </bean>
    <bean id="prodMan" class="bus.ProductManager">
        <property name="products">
            <list>
                <ref bean="product1"/>
                <ref bean="product2"/>
                <ref bean="product3"/>
            </list>
        </property>
    </bean>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hello.htm">springappController</prop>
            </props>
        </property>
    </bean>
    ... (생략) ...
</beans>

전체 애플리케이션의 동작 과정을 한번 살펴보자.

<그림 9> 스프링 WebMVC 예제동작 원리

웰컴 파일인 index.jsp에는 hello.htm에 대한 링크가 걸려있다. hello.htm에 대한 요청은 web.xml의 설정에 의해 springapp로 이름지어진 스프링의 web 패키지의 DispatcherServlet으로 전달된다. DispatcherServlet은 스프링의 core 패키지에 있는 BeanFactory를 이용해 IoC 컨테이너의 기본 원리에 따라 동작된다. 설정 파일인 springapp-servlet.xml을 통해 모든 의존성이 결정되어 제어가 반전되는 현상을 볼 수 있다.

<그림 10> VSSH 고객등록 예제화면

우선 <prop key="/hello.htm">springappController</prop>에 의해 hello.htm 요청을 SpringappController 클래스가 처리하게 되는데 그 사실을 DispatcherServlet은 전혀 모른다. SpringappController는 요청을 처리하는 과정에서 ProductManager의 구현을 필요로 하는데, 그게 어떤 클래스의 인스턴스인지를 본인은 모른다. 설정에 의해 자동적으로 bus.ProductManager가 결정되고, 해당 인스턴스가 거꾸로 SpringappController에 찾아와 연결된다. 이러한 제어의 반전이 스프링을 IoC(Inversion of Control) 컨테이너라고 부르게 하는 이유다.

다음 코드는 스트럿츠의 액션과 유사한 역할을 수행하는 Controller 구현 샘플이다. ProductManager를 사용하고 있는데 신기하게도 setProductManager() 메쏘드만 있을 뿐, 인스턴스를 초기화하는 호출이 이루어지지 않는다. 스프링이 빈 설정을 참고해서 자동화하기 때문이다. handleRequest()의 결과로 ModelAndView가 리턴되어 넘어가는데, 이것은 스트럿츠의 ActionForward와 컨텍스트 정보를 합친 것으로 이해하면 되겠다.

public class SpringappController implements Controller {
protected final Log logger = LogFactory.getLog(getClass());
    private ProductManager prodMan;
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String now = (new java.util.Date()).toString();
        logger.info("returning hello view with " + now);
        Map myModel = new HashMap();
        myModel.put("now", now);
        myModel.put("products", getProductManager().getProducts());
        return new ModelAndView("hello", "model", myModel);
    }
    public void setProductManager(ProductManager pm) {
        prodMan = pm;
    }
    public ProductManager getProductManager() {
        return prodMan;
    }
}

예제 2. 스프링과 스트럿츠, 하이버네이트를 통합한 예제
그럼 스프링을 기존의 스트럿츠 환경과 어떻게 연동할 수 있을까? 대표적인 ORM인 하이버네이트와는 어떻게 연동할 수 있을까? 이를 소개하기 위해 간단한 회원 관리 샘플을 개발했다. 이 예제는 벨로시티와 스트럿츠, 스프링, 하이버네이트를 통합하고자 하는 VSSH의 최종적인 모습을 보여주며, DBMS는 HSQLDB를 사용했다.

예제 파일(vssh.zip)을 다운받아 자신의 작업 디렉토리에 압축을 풀고, was.properties 파일을 열어 자신의 운영 환경에 맞게 경로를 수정한다. 그런 다음, ant를 이용해서 makeDist, war-deploy 태스크를 순서대로 실행하면 빌드 및 배포 과정이 끝난다. 웹 브라우저를 이용해서 http://localhost:8080/vssh-b01에 접속하면 아래와 같은 결과 화면을 볼 수 있을 것이다.

이 예제는 다음 강좌에서 다시 자세히 설명할 예정인데, 미리 상세한 내용을 알고 싶은 분은 LoveLazur의 홈페이지를 참고하기 바란다. 이번 예제부터는 복잡하기 때문에 이클립스와 같은 오픈소스 IDE를 이용하는 것이 좋다.

스트럿츠와 스프링의 연동 방법은 각각을 이해하고 있다면 아주 간단히 처리된다. WEB-INF/lib 디렉토리에 각각의 라이브러리 JAR 파일을 추가한 다음, 스트럿츠 설정 파일인 struts-config.xml에 플러그인 설정만 하나 추가하면 되는 것이다. 플러그인으로 설정한 ContextLoader는 전달된 설정 파일을 이용해서 전체 애플리케이션 구성에 사용될 ApplicationContext를 구성하는 역할을 수행한다.

<struts-config>
<form-beans> ... </form-beans>
<action-mappings> ... </action-mappings>
<message-resources parameter="messages"/>
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
     <set-property property="contextConfigLocation"
            value="/WEB-INF/applicationContext.xml, /WEB-INF/action-servlet.xml"/>
</plug-in>
</struts-config>

applicationContext.xml에는 애플리케이션 로직과 관련된 객체들을 앞서 설명한 빈 설정 방식으로 연동해서, 제어 역행화가 이루어지도록 하면 된다.

<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url"><value>jdbc:hsqldb:data/vsshdb</value></property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
  <!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="mappingResources">
<list><value>model/Customer.hbm.xml</value></list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<!-- Transaction manager , can replace class Attribute ex.Transaction-->
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<bean id="customerDAO" class="dao.CustomerDAOImpl">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>    
</beans>

주의할 점은 모든 클래스에서 사용되는 객체 참조에서 항상 자바 빈 규칙에 따르는 setter()를 만들고, 직접 인스턴스를 초기화하지 않고 객체들의 의존성을 설정 파일에 적어줌으로써 스프링이 자동으로 의존성 관계를 삽입하도록 해야 한다는 것이다.

다음의 CustomerAction을 살펴보면 ICustomerBizManager 타입의 cmgr이라는 인스턴스를 갖고 있는데, setCustomermanager()란 메쏘드만 있을 뿐 어디에도 초기화하는 루틴이 포함되어 있지 않다. 그런데도 cmgr.createCustomer()를 사용하고 있는 것을 볼 수 있다.

public class CustomerAction extends BaseAction {
private ICustomerBizManager cmgr = null;
public void setCustomerManager(ICustomerBizManager cmgr) { //def.
this.cmgr = cmgr;
}
public ActionForward list(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
request.setAttribute("custlist", cmgr.getCustomers());
return mapping.findForward("list");
}
public ActionForward create(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
DynaActionForm custForm = (DynaActionForm) form;
cmgr.createCustomer((Customer) custForm.get("cust"));
ActionMessages messages = new ActionMessages();
messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
                "cust.saved"));
return list(mapping, form, request, response);
}
}

이것은 action-servlet.xml과 applicationContext.xml에서 다음과 같은 설정이 되어 있으므로 그 정보에 따라 적절한 customerManager가 결정되기 때문에 가능해진다.

<beans>
<bean name="/cust" class="control.CustomerAction" singleton="false">
<property name="customerManager">
<ref bean="customerBizManager"/>
</property>
</bean>
<bean id="customerBizManager"
        class="org.springframework.transaction.interceptor.
TransactionProxyFactoryBean">
<property name="transactionManager"><ref local="transactionManager"/></property>
<property name="target"><ref local="customerBizManagerTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>    
</beans>

대안 기술에 대해 관심을 갖자
지금까지 스프링 프레임워크의 필요성을 설명하고, 기본 개념이라 할 수 있는 연관성 삽입 패턴과 AOP에 대해 간단히 살펴보았다. 또한 스프링의 특징과 스프링을 활용한 예제를 소개했다. 스프링이 아직 국내에 소개된 적이 없다보니 외국의 문서와 예제, 서적들을 뒤적거리면서 관련 자료를 정리하는데 한달이 넘는 시간이 소요되었다.

하지만 아쉽게도 그동안 정리한 내용을 모두 소개하기에는 할당된 지면이 너무 부족해 꼭 필요한 부분만을 알려주는데 그친 듯하다. 설명이 미흡한 부분은 필자들의 VSSH 포럼을 활용하고, 궁금한 점이 있으면 언제든 질문해 주기 바란다.

솔직히 말해 이 글을 쓰고 있는 필자도 여러분보다 몇 달 먼저 스프링을 공부한 정도의 수준밖에 되지 않는다. 미국, 일본, 중국, 독일 어디서나 스프링 관련 자료를 쉽게 구할 수 있었지만, 안타깝게도 국내에는 전혀 자료가 없었다.

해외에서는 크게 주목을 받고 있는 스프링과 IoC 컨테이너에 대한 내용들이 왜 국내에서는 전혀 다루어지지 않고 있는지를 곰곰이 생각해본다. 우리네 개발자들은 촉박한 개발 일정에 쫓겨 신기술이나 대안 기술들을 공부할 만큼 다들 여유가 없는 것일까? 아니면 그러한 기술을 이미 익히고 있는 개발자들이 자신의 머리 속에 그 지식을 꼭꼭 숨겨두고 공개하지 않는 것일까?

지금 자바 커뮤니티는 주류 기술의 버전 향상과 신기술의 출현 그리고 오픈소스 기반의 다양한 대안 기술의 등장으로 어느 때보다 급격한 기술 변화를 눈앞에 두고 있다. 아직 그러한 변화를 느끼지 못하고 있는 개발자라면 지금이 바로 그러한 변화에 대한 대비를 시작해야 할 때임을 깨달아야 한다. 또한, 그 과정에서 얻어진 노하우를 공유함으로써 더 큰 이익이 자신에게 돌아온다는 사실도 잊지 말기 바란다. @

* 이 기사는 ZDNet Korea의 자매지인 마이크로소프트웨어에 게재된 내용입니다 
이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by 백성용 헬로우보이

출처 http://www.microsoft.com/Korea/MSDN/netframework/technologyinfo/overview/

Microsoft .NET는 무엇입니까?

Microsoft .NET은 차세대 배포 응용 프로그램을 작성, 실행할 수 있게 해주는 플랫폼으로, 클라이언트, 서버 및 개발자 도구를 포괄하며 다음과 같이 구성됩니다.

  • 개발자가 SOAP, XML(eXtensible Markup Language) 및 HTTP와 같은 표준 프로토콜을 사용하는 네트워크를 통해 프로그래밍 방식으로 기능을 제공하는 웹 응용 프로그램, 스마트 클라이언트 응용 프로그램 및 XML Web Services 응용 프로그램을 작성할 수 있게 해주는 .NET Framework 프로그래밍 모델.
  • Visual Studio .NET과 같이 .NET Framework 프로그래밍 작업을 수행할 수 있는 신속한 응용 프로그램 통합 개발 환경을 제공하는 개발자 도구.
  • Windows 2000, SQL Server 및 BizTalk Server를 포함하여 XML Web Services와 응용 프로그램을 통합, 실행, 운영 및 관리하는 서버 집합.
  • Windows XP, Windows CE 및 Microsoft Office XP와 같이 개발자가 일련의 장치와 기존 제품에 걸쳐 풍부하고 유용한 사용자 작업 환경을 전달하는 데 도움이 되는 클라이언트 소프트웨어.

.NET Framework는 무엇입니까?

.NET Framework는 웹 응용 프로그램, 스마트 클라이언트 응용 프로그램 및 XML Web Services를 작성, 구축 및 실행할 수 있게 해주는 .NET 플랫폼의 프로그래밍 모델로, 다른 복잡한 작업을 관리해 줌으로써 개발자가 응용 프로그램의 비즈니스 논리 코드를 작성하는 데 집중할 수 있도록 합니다. .NET Framework에는 공용 언어 런타임과 클래스 라이브러리가 포함되어 있습니다.

공용 언어 런타임

공용 언어 런타임은 언어 통합, 보안 적용 및 메모리/프로세스/스레드 관리와 같은 런타임 서비스를 담당하며 개발 시 수명 관리, 강력한 형식의 이름 지정, 언어 간 예외 처리, 동적 바인딩 등의 기능을 통해 개발자가 비즈니스 논리를 재사용 가능한 구성 요소로 변환하기 위해 작성해야 하는 코드의 양이 줄어들도록 도움을 줍니다.

클래스 라이브러리

기본 클래스는 입/출력, 문자열 조작, 보안 관리, 네트워크 통신, 스레드 관리, 텍스트 관리, 사용자 인터페이스 디자인 기능 등의 표준 기능을 제공합니다.

Microsoft ADO.NET 데이터 클래스는 지속적인 데이터 관리를 지원하며 표준 SQL 인터페이스를 통해 지속적인 데이터 저장소를 조작하는 데 필요한 SQL 클래스를 포함합니다. XML 클래스는 XML 데이터 조작과 XML 검색 및 변환을 수행할 수 있도록 합니다. Microsoft ASP.NET 클래스는 웹 응용 프로그램과 XML Web Services의 개발을 지원합니다.

Windows Forms 클래스는 Windows 기반 스마트 클라이언트 응용 프로그램의 개발을 지원합니다. 즉, 클래스 라이브러리는 .NET Framework가 지원하는 모든 언어에 걸쳐 일관성 있는 공통 개발 인터페이스를 제공합니다.

자세한 통계 정보 및 시장 동향

  • .NET Framework는 2002년 1월부터 사용되기 시작하였으며 3,500,000장의 CD와 700,000회의 다운로드를 통해 전세계 4,000,000명 이상의 개발자가 베타 릴리스를 사용하고 있습니다.
  • 20개 이상의 프로그래밍 언어에 대한 컴파일러를 .NET Framework와 함께 사용할 수 있습니다.
  • 현재 많은 서드 파티 공급업체에서 사용 가능한 구성 요소와 컨트롤을 제공하고 있습니다.
  • Microsoft에서는 .NET Framework 기반 응용 프로그램을 적극적으로 구축하고 있습니다. MSN과 Microsoft.com Smart 404는 .NET Framework로 이미 전환된 많은 Microsoft 응용 프로그램의 일부에 불과합니다.

신속한 개발

.NET Framework의 다중 언어 지원 기능을 사용하면 개발자가 해당 작업에 가장 적합한 프로그래밍 언어를 사용하고 단일 응용 프로그램 내에서 여러 언어를 조합할 수 있습니다. 발표에 따르면 .NET Framework에서는 20개 이상의 상업용/교육용 프로그래밍 언어가 지원됩니다.

.NET Framework의 연결 작업을 없앤 구성 요소 기반 디자인은 개발자가 작성해야 하는 코드 수를 줄입니다. Sun에서 제공하는 최상의 응용 프로그램 예제인 Java Pet Store의 .NET 기반 버전인 .NET Pet Shop 예제는 J2EE(Java 2 Enterprise Edition) 버전과 같은 기능을 구현하지만 J2EE 버전보다 코드를 2/3 적게 사용합니다.

업계의 평가

"경험 등의 요소를 고려하고 이것이 원본이 아닌 수정된 형태의 구현이라는 사실을 감안하더라도 .NET Framework 및 Microsoft Visual Studio .NET을 사용하면서 솔루션의 초기 릴리스보다 두 배 정도 생산성이 향상되었습니다." Tore Lode, 수석 개발자, CyberWatcher

향상된 작업

  • Sun에서 제공하는 Java Pet Store의 .NET 기반 구현인 .NET Pet Shop에서 성능은 J2EE 버전보다 28배나 높고 지원되는 동시 사용자 수는 6배나 많으며 CPU 사용률은 1/6밖에 되지 않습니다.
  • .NET Framework는 성능과 확장성 측면에서 기존의 ASP(Active Server Pages) 기술보다 월등한 이점을 제공합니다.

모든 결과는 Windows 2000 Advanced Server를 토대로 얻은 것이며 Microsoft ASP와 Microsoft ASP.NET은 SQL Server 2000 데이터베이스에서 실행되고 있습니다.

Web application benchmark, peak throughput

Peak throughput distributed, transactions per second

Web service peak throughput

업계의 평가

"매일 수백만 번 페이지를 표시하는 동안 1초 미만의 페이지 로드가 발생합니다. 2000년 12월 23일에 구축한 이후 2001년 10월 3일까지 작동 중지 시간이 1분도 되지 않았기 때문에 J2EE 솔루션과 비교해서 $1,300,000을 절약했습니다." Stephen Forte, CTO, Zagat Survey

"기존의 비슷한 프로젝트와 비교해 보면 구축 기간이 주 단위가 아닌 시간 단위로 측정되고 있습니다." Ferdy Khater, 응용 프로그램 개발 부문 이사, Continental Airlines

빠른 아키텍처

전세계의 많은 회사들이 .NET Framework 고유의 XML Web Services 통신 메커니즘을 사용하여 공급자 및 고객과 빠르고 쉽게 통합하고 있습니다.

업계의 평가

"협력업체 입장에서는 XML Web Services를 통해 자사 콘텐트에 액세스하기가 이전보다 훨씬 쉬워지고 자사 콘텐트를 가져오기, 저장 및 관리하는 데 사용되는 인프라를 구축할 필요가 없어질 것입니다. 이를 융통성 있는 새 라이센스 옵션과 결합하면 훨씬 더 유용한 패키지를 협력업체에 제공할 수 있을 것입니다." Stephen Forte, CTO, Zagat Survey

"XML Web Services를 사용하면 코드에서 사용자 데이터, 보안 문제 및 기존 데이터베이스와의 통합을 처리하는 방법을 포털과 엔터프라이즈에 쉽게 알릴 수 있습니다. 특히 최소한의 지원만으로 협력업체 시스템을 자사 시스템에 통합할 수 있게 해주는 자동 문서 생성 및 웹 페이지 테스트 기능이 유용합니다." Tore Lode, 선임 개발자, CyberWatcher

활기찬 사용자 커뮤니티

영어, 일본어, 독일어 및 스페인어를 비롯한 여러 언어로 다양한 주제에 대해 논의하는 사용자 그룹과 토론 목록이 전세계적으로 많습니다.

.NET Framework와 .NET Framework용 프로그래밍 언어를 다루는 300개 이상의 발행물이 이미 발행되었거나 곧 발행될 예정입니다. 주요 발행물은 다음과 같습니다.

  • .NET Framework Essentials, O'Reilly Press
  • Professional ASP.NET, Wrox Press Ltd.
  • Visual Basic to Visual Basic .NET, Sams Publishing

서적과 문서에 대한 자세한 내용은 MSDN Developer Bookstore (US) 또는 .NET Books 사이트 (US)를 참조하십시오.

교육과 행사에 대한 자세한 내용은 .NET Framework Training and Events 페이지 (US)를 참조하십시오.

참조, 링크, 소스 및 추가 정보

Microsoft .NET 웹 사이트에서 최신 버전의 .NET Framework를 구할 수 있습니다.

.NET Framework를 지원하는 언어 컴파일러는 다음 프로그래밍 언어용으로 발표되었습니다.

지원되는 프로그램 언어
APL Fortran Pascal
C++ Haskell Perl
C# Java Language Python
COBOL Microsoft JScript® RPG
Component Pascal Mercury Scheme
Curriculum Mondrian SmallTalk
Eiffel Oberon Standard ML
Forth Oz Microsoft Visual Basic®
  • NET Pet Shop의 소스 코드 및 백서 (US) 를 찾아보십시오.
  • .NET Framework를 기반으로 작성된 고객 솔루션에 대한 자세한 내용은 사례 연구 페이지 (US) 의 전체 .NET Framework 사례 연구 목록을 참조하십시오.
  • XML Web Services에 대한 자세한 내용은 MSDN 사이트의 웹 서비스 (US) 부분을 참조하십시오.

.NET Framework를 기반으로 작성된 고객 솔루션

Zagat: Microsoft .NET을 통해 새로운 기회 인식
Zagat Survey는 인터넷으로 사업 영역을 확장하고 새 조사 방식을 사용할 수 있는 새로운 콘텐트 관리 시스템을 필요로 했습니다. 새로운 콘텐트 관리 시스템은 Microsoft .NET Framework와 Visual Studio .NET으로 작성되었으며 XML을 사용하여 웹 사이트와 데이터 입력 서비스 공급자(서류로 된 기존의 Zagat 조사 자료를 전자 문서 형태로 변환하는 서비스를 제공)로부터 평가 및 의견을 수집합니다. 편집자는 화면 편집 환경을 통해 이 정보에 액세스한 다음 조사 내용을 토대로 "종합적인" 평가를 도출합니다. 새 콘텐트가 준비되면 콘텐트 관리 시스템이 다시 XML을 사용하여 회사 웹 사이트뿐 아니라 Zagat의 문서 형식의 안내서를 발행하는 데 사용되는 Adobe FrameMaker 기반 응용 프로그램에도 이 정보를 전달합니다.

XML Web Services를 사용한 결과, Zagat은 완전히 새로운 비즈니스 모델, 즉 협력업체에 관련 데이터를 전달할 전담 직원을 두지 않고도 콘텐트를 공유하고 집계할 수 있는 기회를 협력업체에 보다 많이 제공하고 사용량에 따른 비용 청구 모델을 새로 도입하여 많은 고객을 만족시킬 수 있었습니다. Zagat은 20명으로 조직된 개발팀만으로 이 모든 작업을 수행할 수 있습니다.

Marks & Spencer: 사기 감지 시스템 구축
매주 10,000,000명 이상의 고객이 300개 이상의 영국 내 점포에 들르는 의류, 식료품, 가정용품 및 금융 서비스 분야의 선도적 소매업체인 Marks & Spencer는 신용 카드 사기를 주요 위험 요소로 인식하고 있습니다. 이 소매업체에서는 13억 달러에 달하는 현재의 시스템을 사전에 사기 행위를 막을 수 있는 솔루션으로 교체하고자 했습니다.

.NET Framework를 기반으로 구축된 새 거래 분석 시스템은 의심스러운 사기 거래를 거의 즉석에서 식별할 수 있습니다. .NET Framework의 다중 플랫폼 통합 기능은 여러 데이터베이스에 분산되어 있을 수 있는 다양한 기준(환불 비율이 높은 카드 계정, 모든 거래 유형의 통계적 등급, 사기 행위를 공모할 수 있는 직원 등)에 따라 각 거래를 즉석에서 확인할 수 있도록 합니다. 거래가 이러한 기준 중 하나라도 충족하면 휴대폰을 통해 점포 관리자를 호출하며 의심스러운 고객이 점포를 벗어나기 전에 점포 관리자가 금전 등록기로 해당 사항을 확인할 수 있는 경우도 있습니다.

Buy.com: .NET을 통한 고객 만족도 향상
Buy.com은 혁신적인 웹 사이트에 주어지는 다양한 수상 경력을 지닌 회사로, 언제나 고객 만족도 향상을 위해 노력하고 있습니다. 두 명의 Buy.com 개발자가 단 2주만에 .NET Framework를 사용하여 개별화된 쇼핑 포털을 만들었는데, 이 쇼핑 포털에서는 고객이 계정 정보, 주문 상태 및 각 쇼핑객의 개인 기본 설정에 따른 개별 선택 제품에 즉시 액세스할 수 있습니다.

또한 Buy.com은 계열사에 콘텐트를 빠르고 안정적으로 배포하는 방법을 강구하고 있었습니다. "계열사에 이 정보를 시기 적절하게 배포하려면 각 협력업체가 필요로 하는 다양한 형식의 전자 메일, 전화 및 파일 전송을 함께 사용해야 하는데, 이 때 지연되는 경우가 발생하면 고객 만족도가 낮아질 수 있습니다."라고 Buy.com의 IT 응용 프로그램 부문 이사인 Navid Nobakht씨는 말합니다. .NET 플랫폼을 사용하여 XML Web Services를 통해 계열사 사이트에 필요한 콘텐트를 제공함으로써 Buy.com은 계열사 사이트에 실시간으로 콘텐트를 배포하는 재사용 가능한 표준 기반 방법을 찾았습니다.

이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by 백성용 헬로우보이
 아래의 이미지를 확대(클릭)하거나 여기를 클릭해서 보세요~

사용자 삽입 이미지

이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by 백성용 헬로우보이

제 블로그는 자료를 모아놓는 곳으로 이용하고 있지만, 이번 프로젝트 후에는 프로젝트에 사용된 프레임워크에 대한 상세한 사용법을 연재할까 합니다.

이번이 첫번째 제가 쓴글이네요. (그래도 여전히 자료 수집용 블로그로 활용해야지~~)

현재 진행중인 프로젝트 내에 프레임워크를 적용시의 장점과 단점에 대한 개인적인 소견을 말씀드리면 아래와 같습니다. 물론 이번 프로젝트는 프레임워크를 적용할거지만~ 아직까지 고민 고민하고 있네요.

프레임워크 적용시 장점
  1. 개발자들의 성향과 상관없는 통일화된 코드규칙 적용에 따른 향후 유지보수성 향상
  2. 개발 생산성 향상 ( 프레임워크는 Library가 제공되고 있고 여기에 MVC패턴이 적용 된 반제품이다.)
  3. 코드 재활용성 향상

프레임워크 적용시 단점

  1. 개발자들의 프레임워크에 대한 이해가 부족하여 학습시간이 요구된다.
  2. 한 프로젝트 내의 모든 개발자들에게 동일한 스킬(프레임워크에 대한 이해 및 활용도)이 요구된다.

시작이 반이다~~~ 이제 반은 했으니... 나머지 반은 열심히 ~ 부지런히~ 파이팅~~

모두 모두 즐거운 12월 되세요~

이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by 백성용 헬로우보이