대부분의 어플리케이션은 유지보수가 필요하다. 불가피하게 수정을 하는 일이 발생하기도 하고, 추가 기능이 필요하기도 하다. 이 때 조금더 빠르고 간편하게 업데이트를 하기 위해서는 클래스 객체가 서로 분리되어 있어야 한다. 쉽게 말해 하나의 클래스를 수정한다고 해서 다른 클래스도 수정해야 하고, 또 다른 클래스까지 수정하는 일이 발생하지 않도록 개발해야 한다. Spring 에서 이러한 객체간의 결합도를 완화시켜주는 일을 하는 것이 바로 IoC 컨테이너(Inversion of Control) 이다. IoC 컨테이너는 객체와 객체 간의 의존성을 정의해 놓은 메타데이터를 기반으로 작동하기 때문에, 유지보수 시에는 해당 소스와 메타데이터만 수정하는 것으로 많은 노력을 줄여준다. 이 컨테이너를 IoC, 즉 “제어의 역전” 이라고 부르는 이유는 말그대로 제어가 반대로 진행되기 때문이다. 일반적으로 독립 실행형 Java 에서는 main 메소드 실행 > 의존성 생성 > 다른 메소드 실행 순으로 처리가 되지만, IoC 컨테이너를 사용하면 컨테이너가 의존성 생성 > main 메소드로 의존성 주입 > main 메소드 실행 순으로 처리된다. 그래서 Spring 의 이러한 컨테이너를 IoC 컨테이너라고 부른다.


IoC 컨테이너는 객체들로 구성된 보관소이다. 이 객체들은 XML 파일이나 어노테이션(@) 으로부터 메타데이터를 읽어와 초기화 되며, IoC 컨테이너에서 관리하는 이 객체들을 바로 bean 이라고 부른다. IoC 컨테이너는 객체(Beans) 의 생명주기를 관리하고 의존성 주입(Dependency Injection) 을 통해 각 계층이나 서비스들간의 의존성을 맞춰 준다.



컨테이너의 메타데이터


이 메타데이터는 주로 서비스 객체, 데이터 액세스 객체(DAOs), 프레젠테이션 객체, 인프라 객체, JMS Queue 등을 정의한다. 이 메타데이터에 정의된 객체를 기반으로 컨테이너를 생성하며, 컨테이너는 그 bean 이라는 모든 객체들을 관리한다. 메타데이터는 XML 파일, Java 기반, 어노테이션, 이렇게 3가지로 작성할 수 있다. 아래 각각의 장단점을 보고 어떤 방식의 메타데이터를 사용할 것인지를 결정해야 한다.


XML 기반의 메타데이터는 아키텍처의 논리 계층이나 모듈 별로 여러 XML 파일에 bean 을 정의할 수 있으며, 양이 많아질 경우 구조화에 신경을 써야 한다. ApplicationContext 인터페이스를 구현해서 bean 들을 사용할 수 있으며, 메타데이터 수정시 재컴파일 없이 반영시킬 수 있는 장점이 있다. 


Java 기반의 메타데이터는 일반적으로 @Configuration 어노테이션 클래스 내에서 @Bean 어노테이션 메소드를 사용하여 bean 을 구성한다. XML 파일이 필요 없으므로 구성이 간결하며, 에러 검출이 쉽다. 개인적으로 선호하는 방법이기도 하다. 메타데이터 구성을 한 번에 파악하기 힘들며, 소스코드와 밀접하게 결합하는 문제로 사용을 반대하는 사람도 있다.


어노테이션 기반의 메타데이터는 XML 과 Java 를 혼용하여 사용하는 것이다. 어노테이션 주입이 XML 주입 보다 먼저 수행되므로 두 가지에 모두 정의된 속성은 XML 로부터 재정의 될 것이다. 두 가지 방식을 사용하다 보니 모든 구현이 가능한 반면, 구성이 분산되기 때문에 유지보수 면에서 힘들 수도 있다.



ApplicationContext


ApplicationContext 인터페이스가 곧 Spring IoC 컨테이너이며, ClassPathXmlApplicationContext 나 FileSystemXmlApplicationContext 의 인스턴스를 생성하여 구현하는 것이 일반적이다. IoC 컨테이너는 org.springframework.beans 와 org.springframework.context 패키지를 기반으로 구현된다. 메타데이터를 기반으로 이 인터페이스를 구현하면 IoC 컨테이너를 사용할 수 있다. org.springframework.beans.factory.BeanFactory 인터페이스가 모든 타입의 객체를 관리하지만, 이를 확장한 org.springframework.context.ApplicationContext 인터페이스에 메시지 리소스 처리, 이벤트 처리, 웹 어플리케이션 지원 등의 고급 기능이 추가되어 있기 때문에, 딱 BeanFactory 의 기능만 필요한 것이 아니라면 ApplicationContext 를 사용하는 것이 편할 것이다. ApplicationContext 는 모든 bean 을 정의한 메타데이터를 기반으로 각 bean 을 검증, 생성, 초기화 하며 오류가 있을 경우 즉시 발견할 수 있다.



컨테이너의 bean 조회하는 예제


XML, Java, 어노테이션 기반의 메타데이터에서 myService 라는 bean 을 조회하는 간단한 예제를 보자. 매우 간단하므로 대충 보이는 형태만 감상하자.



XML 기반 설정


XML 메타데이터에 myService bean 정의하기.


> application-bean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation"http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean name="myService" class="com.oops4u.blog.ex.MyServiceImpl" />
</beans>
cs


XML 파일의 메타데이터를 기반으로 컨테이너를 생성하여 myService bean 조회하기.


> Process.class

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("application-bean.xml");
    MyService myService = context.getBean("myService", MyService.class);
    ...
}
cs



Java 기반 설정


Java Config 코드의 메소드에 bean 정의하기.


> AppConfig.java

@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImp();
    }
}
cs


@Configuration 어노테이션 클래스를 기반으로 컨테이너를 생성하여 @Bean 메소드인 myService 조회하기


> Process.class

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = context.getBean("myService", MyService.class);
    ...
}
cs



어노테이션 기반 설정


XML 파일과 @Configuration 을 혼용하는 어노테이션 기반 설정은 XML 기반의 방식과 유사하게 bean 을 정의하고, 해당 XML 파일로 컨테이너를 인스턴스화 한다. 그리고 기존의 XML 파일에 정의하던 각종 의존성을 @Autowired 을 비롯한 많은 어노테이션으로 Java 코드에 정의하는 방식이다. 또는 AnnotationConfigApplicationContext 를 사용하여 @ImportResource 로부터 XML 을 가져오는 방법도 있다.




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,