Java2EE Framework/Spring 2.0

스프링 프레임워크의 핵심(Inversion of Control)

본클라쓰 2010. 6. 2. 08:47

 

 스프링 프레임워크가 가지는 가장 핵심적인 기능은 IoC 이다. IoC(Inversion of Control)란 '제어의 역전'이다. 전통적인 어플리케이션 제작을 보면 클래스는홀로 사용되어지지 않는다. 다른 클래스를 가지고 있거나 부모 클래스를 상속받아 사용하는 등, 거의 모든 클래스가  has-a  관계나 is-a 관계를 가지고 있다. 이런 부분을 의존성이라고 부르며 한 객체가 다른 객체에 의존하고 있음을 뜻한다. 즉, has-a 관계나 is-a 관계를 가지고 있는 객체들은 의존성이 있다 말할 수 있다.

 

 의존성 관계를 가진 클래스의 인스턴스를 생성하기 위해 개발자들이 직접 new 연산자를 사용하거나 Factory패턴을 통해 인스턴스를 생성하여 사용하였다. 이렇게 프로그램으로 객체를 생성하고 관리하는 것이 프로그램을 닫혀있게 만들었다.

 

 스프링 프레임워크가 가지는 가장 핵심적인 기능은 IoC이다. IoC는 객체 생성과 의존성을 개발자가 아닌 컨테이너가 맡는다는 개념이다. 즉, 인스턴스의 생성과 관리를 개발자에게 뺏어 컨테이너에게 넘겨주었다. 개발자는 이제 더 이상 객체 생성에 신경쓸 필요없이 마치 객체가 생성되어 있는 것처럼 프로그램 코드를 작성하고 설정 데이터에 객체간의 의존성을 정의하면 컨테이너가 알아서 객체를 생성해 프로그램을 수행하게 만든다. 이 방법은 객체의 관계를 느슨하게 연결할 수 있다. 스프링 프레임워크를 light-weigth 프레임워크라고 부르는 이유에는 IoC 개념이 있다.

 

 

■ 서블릿과 EJB 기반의 컨테이너의 객체 생성

 

 자바 기반의 어플리케이션을 개발하기 위해선 자바 객체를 생성하고 서로 간의 의존 관계(소유)를 연결시키는 작업에 대한 제어권이 개발자에게 있었다. 하지만 서블릿과 EJB 기반의 개발은 객체 생성의 제어권이 개발자에게서 컨테이너로 넘어갔다.

 

 서블릿, EJB가 나타나기 전까지 모든 클래스를 개발자들이 직접 생성할 수 있었지만 서블릿과 EJB의 경우에는 개발자들이 서블릿과 EJB를 직접 생성하고 싶어도 이 객체들은 직접 생성하여 제어할 수 없게 되어 버렸다. 객체 생성에 대한 제어권이 컨테이너에게 넘어가면서 객체의 생명주기(Life Cycle)를 관리하는 권한을 컨테이너들이 전담할 수 밖에 없게 되었다. 서블릿과 EJB 기반하에서 개발자들은 서블릿, EJB 스펙이 제공하는 대로 어플리케이션을 구현할 수 밖에 없는 사항으로 바뀌게 된 것이다.

 

 IoC가 말하는 제어권 역전이란 이처럼 객체의 생성과 소멸까지의 생명주기의 관리를 컨테이너가 관리함을 의미한다. 즉, 스프링 컨테이너는 객체에 대한 생성 및 생명주기를 관리하는 기능을 제공한다. 이와 같은 이유로 Spring 프레임워크를 Spring 컨테이너, IoC 컨테이너와 같은 용어로 부르는 경우도 종종 볼 수 있다.

 

 서블릿 컨테이너는 서블릿을 관리하기 위한 컨테이너이고, EJB 컨테이너는 EJB를 관리하기 위한 컨테이너이다. 그렇다면 Spring 컨테이너는 무엇에 대한 컨테이너인가? Spring 컨테이너는 POJO를 관리하기 위한 컨테이너이다. POJO란 특정 인터페이스에 종속되지 않은 자바빈과 같은 클래스를 말한다. 이말은 서블릿 클래스 안에서 사용되어 지는 비즈니스 로직을 담당하는 DAO, DT, VO 같은 객체를 뜻한다. 특정 인터페이스에 종속된 클래스는 클래스를 작성해야 할 때 작성할 규칙을 가지고 있는 클래스를 말하며 대표적인 것이 서블릿 클래스이다.

 

 

■ 스프링과 EJB 그리고 Non EJB

 

 스프링은 서블릿 안에 사용되고 있던 POJOs  클래스의 객체 생성과 소멸과 의존서을 개발자가 아닌 spring 프레임워크가 맡는 다는 의미이다. 왜 이와 같은 클래스 생성과 소멸을 spring이 맡겠다는 것인가?

 

 이에 대한 해답은 간단하다. EJB 아키텍처 기반으로 어플리케이션을 개발할 경우 개발 과정이 복잡하고 테스트하기 어려운 점은 있었지만 EJB 컨테이너가 제공하고 있는 기능들은 너무 매력적이다.

 

 Non EJB 아키텍처의 경우 개발 속도가 빠르고 테스트의 용이성이 뛰어나지만 컨테이너 기능을 지원하지 않음으로써 EJB 컨테이너가 지원했던 기능들을 개발자들이 직접 구현하는 불편함을 감수할 수밖에 없었다. 이와 같은 각각의 장단점을 보완하는 방법으로 POJO 객체는 개발자들이 직접 개발을 하지만 사용에는 POJO를 관리할 수 있는 컨테이너를 만들어서 EJB 컨테이너가 지원하는 기능들을 지원하는 것이다. 이 때문에 마지막까지 개발자들에게 남아 있던 POJO에 대한 제어권을 컨테이너에게 넘긴 것이다.

 

 위의 말을 한번에 이해할 수 있다면 그 사람은 서블릿 기반의 Non EJB 개발을 해 본 사람일 것이다. Non EJB 개발을 할 때 겪는 문제점 들을 해결하기에 Spring 프레임워크는 대단히 매력적인 프레임워크이다.

 

 Non EJB 개발에 가장 큰 걸림돌은 역시 계층간 역할의 분담일 것이다. 복잡함을 줄이기 위해 계층간 역할을 분담을 했지만 실제적으로 계층을 정확히 구분하기 매우 어렵다. 나중에 개발 단위가 커지면 계층간의 역할이 모호해지는 경우도 대부분이다. 이는 앞에서 얘기한 바와 같이 명확하게 계층간의 구분이 이루어지고 잘 설계된 아키텍처를 가지지 않는 Non EJB 아키텍처의 한계이다.

 

 위의 얘기를 쉽게 풀이하자면 개발자가 직접 작성해 사용해오던 자바빈을 이제는 컨테이너가 관리하겠다는 말이다. 즉, 개발자는 자바빈 객체를 스프링 컨테이너에 등록해 놓기만 하면 객체 생성과 소멸 등의 부수적은 작업은 컨테이너가 알아서 해주겠다는 말이다. 이렇게 객체와 객체간의 의존성을 개발자에게서 컨테이너로 가져가는 것이 IoC의 핵심이다. 그럼 IoC는 어떻게 구성되어 있는가? 이 부분에 대해 알아 보자.

 

 

■ IoC를 구성하고 있는 DL(Dependency Lookup)과 DI(Dependency Injection)

 

 IoC를 명확하게 이해하고 있는가? 그럼 다음부분의 설명이 쉬워진다. IoC란 객체 생성과 소멸, 의존성을 컨테이너가 관리함을 뜻한다. 그럼 컨테이너는 어떤 객체가 생성되고 어떤 객체가 어떤 객체를 소유하고 있는지 어떻게 알 수 있을까? 그것은 설정파일을 통해 관리되고 컨테이너가 설정파일을 해석하는 과정을 거치기 때문에 알 수 있는 것이다. 즉, 모든 IoC 컨테이너는 각 컨테이너에서 관리해야 하는 객체들을 별도의 저장소(repository)에서 관리한다.

 

 서블릿 클래스를 등록하는 web.xml 설정파일처럼 각각의 객체를 지정하는 설정파일을 IoC 컨테이너가 가지고 있는 것이다. EJB 컨테이너는 ejb-jar.xml 에 객체의 정보를 저장혹, 서블릿은 web.xml 에 객체의 정보를 저장한다. 이처럼 Spring 프레임워크도 POJO들을 관리하기 위해 별도의 저장소를 가진다. Spring 프레임워크는 이 같은 저장소로 xml 파일 형태와 자바 properties 파일 형태를 이용한다.

 

□ DL(Dependency Lookup) - EJB

 

 Dependency Lookup은 저장소에 저장되어 있는 빈(Bean)에 접근하기 위해 개발자들이 컨테이너에서 제공하는 API를 이용해 사용하고자 하는 빈(Bean)을 Lookup 하는 것을 말한다. 이와 같이 저장소에 의해 관리되고 있는 빈을 개발자들이 직접 lookup하여 사용하는 것을 dependency lookup 이라고 한다. 이와 달리 dependency injection 은 각 계층 사이, 각 클래스 사이에 필요로 하는 의존관계가 있다면 이 같은 의존관계를 컨테이너가 자동적으로 연결시켜 주는 것을 말한다.

 

 Dependency Lookup을 사용할 경우 저장소에 있는 빈은 lookup하기 위해 컨테이너에서 제공하는 API와 의존관계가 발생한다. 이처럼 컨테이너 API와 의존관계를 많이 가지면 가질수록 애플리케이션이 컨테이너에 대하여 가지는 종속성은 증가할 수밖에 없다. 따라서 가능한 dependency lookup을 사용하지 않는 것이 컨테이너와의 종속성을 줄일 수 있게 된다. 컨테이너와의 종속성을 줄이기 위한 방법으로 Dependency Injection 을 통해 가능하다.

 

 

□ DI(Dependency Injection) - Spring

 

 스프링 프레임워크에서 사용하는 방식으로 각 클래스 사이의 의존관계를 빈 설정(Bean definition) 정보를 바탕으로 컨테이너가 자동적으로 연결해 주는 것을 말한다. 컨테이너가 의존관계를 자동적으로 연결시켜 주기 때문에 개발자들이 컨테이너API를 이용하여 의존관계를 관여할 필요가 없게 되므로 컨테이너API에 종속되는 것을 줄일 수 있다.

 

 개발자들은 단지 빈 설정 파일(저장소 관리 파일)에 의존관계가 필요하는 정보를 추가하기만 하면 된다. 스프링 프레임워크는 각 클래스 사이의 의존관계를 관리하기 위한 방법으로 Setter Injection, Constructor Injection, Method Injection 의 세 가지 유형을 제공하고 있다.

 


참조 : Spring 프레임워크 워크북 // 지은이 : 박재성, 한빛미디어