1. IoC

IoC (Inversion of Control) 개념에 대하여 소개하려고 한다.

자바에서는 객체 의존성을 관리하기 위한 방법으로 다음과 같은 방식을 사용한다.

  1. 객체가 직접 의존 관계에 있는 객체들의 생성자를 호출하는 것으로 의존성을 인스턴스화한다.
  2. look-up 패턴을 활용하여 의존성들을 찾아 배치한다.

일반적인 자바 프로그램은 main()을 시작점으로 메소드들이 호출된다. 하지만 IoC는 이런 흐름을 사용하지 않는다. 제어 권한 자체가 자기 자신에게 있지 않다. 또 자기가 어떻게 사용되는지도 알 수 없다.

IoC는 제어의 역전이다.

그렇다면 스프링은 클래스에 어떤 의존성이 필요한지 어떻게 알 수 있는 것일까. 바로 스프링은 우리가 전달하는 설정 메타 데이터를 사용한다. 이 설정에는 어떤 객체를 사용해야 하는지와 그 의존성이 무엇인지가 명시되어 있다. 스트링 컨테이너를 설정하기 위한 일반적인 방법은 applicationContext.xml에 설정 메타 데이터를 넣는 것이다. 스프링 2.5부터는 컨테이너를 구성하는데 애노테이션 기반의 설정을 사용할 수 있다. 3.0 버전부터는 XML 설정 없이 자바 기반의 설정도 사용 가능하다. 점점 xml은 사용하지 않는 추세이므로 후자가 유리하다고 할 수 있다.


2. IoC 장점

이러한 IoC의 장점은 인터페이스 기반 설계가 가능하며, 컴포넌트 재사용성이 증가하고, 효율적인 의존성 관리가 가능하다는 장점이 있다.

일반적인 클래스 호출 방식은 클래스 내에 선언과 구현이 결합성이 높아 변화가 어렵지만, IoC는 팩토리 패턴의 장점을 통해 결합성이 낮고 실행 시점에 클래스간의 관계가 형성된다. 

Bean Factory, Application Context라고도 부르는 IoC 컨테이너는 POJO의 생성, 초기화, 소멸에 대한 권한을 갖고 있다.

당연히 개발자가 아닌 컨테이너가 인스턴스를 관리하게 된다. 또 당연히 POJO 클래스와 설정 메타 정보가 필요하다.

여기서 Bean(빈)이란 IoC 컨테이너에 의하여 관리되는 객체를 뜻한다.

여러 속성을 가지고 있는데 id, name, scope, constructor-args, property 등이 핵심이다.


3. IoC 구현 방법

이런 IoC를 구현하는 방법에는 DL과 DI 등이 있다.

  • DL은 Dependency Lookup의 약자이고 <의존성 검색> 정도로 해석할 수 있다.
  • DI는 Dependency Injection의 약자이고 <의존성 주입> 정도로 해석할 수 있다.

DL은 Bean에 접근하기 위해 컨테이너에서 제공하는 API를 통해 Bean을 lookup 한다.

Object obj = cts.lookup(이름);


4. DI (Dependency Injection)

DI는 각 클래스 사이에 필요한 의존 관계를 Bean 설정 정보 기반으로 컨테이너가 자동으로 연결한다. 어떻게 자동으로 연결하느냐하면 Bean 설정 정보를 바탕으로 한다.

DL을 사용하면 컨테이너의 종속성이 생기게 되는데 이를 줄이기 위해 DI 기법을 사용한다.

DI는 <생성자 방식 - Constructor Injection><수정자 방식 - Setter Injection>으로 구현할 수 있다.

4.1. 생성자 방식

생성자 방식은 <constructor-arg> 태그를 사용한다. 

<beans>
  <bean name="school" class="my.package.School">
    <constructor-args>
      <bean class="my.package.Student1"/>
    </constructor-args>
    <constructor-args>
      <bean class="my.package.Student2"/>
    <constructor-args>
  </bean>
</beans>

그런데 의존 객체가 많아지면 많아질 수록 사용하기는 불편하다.

4.2. 수정자 방식

수정자 방식은 <property> 태그를 사용한다.

<beans>
  <bean name="school" class="my.package.School">
    <property name="student1" ref="student1"/>
    <property name="student2" ref="student2"/>
  </bean>
  <bean id="student1" class="my.package.Student1">
  <bean id="student2" class="my.package.Student2">
</beans>

생성자 방식보다는 좀 더 확장성이 있다고나 할까...

DI를 사용하면 의존하고 있는 클래스가 변경되어도 구체클래스가 아닌 인터페이스를 의존하고 있기 때문에 구체클래스를 변경하고자 하면 의존 설정에 다른 구체클래스만 설정하면 된다. 이렇게 결합도가 낮아지면 유지보수와 확장에 유리하다.


5. DL (Dependency Lookup)

저장소에 있는 Bean에 접근하기 위해 컨테이너가 제공하는 API를 이용하여 Bean을 lookup하는 방식이다.