이번장에서는 스프링 트라이앵글 개념중 제어 역전(IoC, Inversion of Control)에 대해서 알아 보고자 한다.
아래와 같이, 자바에서는 일반적으로 자기(OwnerController)가 사용할 의존성을 자기가 만들어 사용한다.
class OwnerController {
private OwnerRepository repo = new OwnerRepository();
}
하지만 스프링의 제어 역전에서는 OwnerController가 의존성을 직접 관리하지 않고 아래 코드로 동작하게 된다.
class OwnerController {
//OwnerRepository를 직접 만들지 않는다.
private OwnerRepository ownerRepository;
//생성자를 통해 받아오는데 OwnerController가 하는 일이 아니다.
public OwnerController(OwnerRepository ownerRepository){
this.repo = ownerRepository;
}
}
OwnerController 객체를 만들기 위해서는 OwnerRepository객체를 필요로 하는데 이 객체를 생성하는일이 OwnerController 자체적으로 하는게 아닌, 외부로부터 주입을 받아야 생성이 가능하다.
즉, 의존성이 외부에서 주입(DI, Dependency Injection)이 되는 상황이다. 제어 역전(IoC)는 의존성(DI)을 통해 이루어짐을 확인할 수 있다.
제어 역전의 이점은 객체지향 프로그래밍과 아주 관련이 있다. 객체지향 프로그래밍은 각 객체마다 자기의 역할과 책임을 온전히 다하며 서로 협력하며 변경에 유연한 프로그래밍을 할 수 있는 프로그래밍 기법이다.
즉 객체마다 높은 응집도와 낮은 결합도를 이루어 나간다는 것이 핵심이다.
제어의 역전을 통하여 변경에 유연한 코드 구조를 가져갈 수 있다.
그렇다면 외부에서 의존성 주입은 누가 해주는걸까?
스프링 프레임워크에서 의존성 주입은 일반적으로 , 스프링 IoC 컨테이너가 담당한다.
이 컨테이너가 의존성을 주입해준다. 모든 객체를 다 주입해주는 것은 아니고, 빈으로 등록된 객체들을 주입해주게 된다. 빈(Bean)이란 IoC 컨테이너가 관리하고 있는 자바 객체를 의미한다.
이로써 스프링 IoC 컨테이너가 수 많은 객체 생명주기를 관리하고, 의존 관계를 설정해주게되어 개발자는 비즈니스 로직에 집중할 수 있게 된다.
간단한 예제 코드를 보자면, IoC 컨테이너 역할을 하는 ApplicationContext 클래스를 볼 수 있으며 이 클래스는 BeanFactory라는 컨테이너를 상속받아서 빈팩토리보다 다양한 역할을 할 수 있는 클래스이다.
@Autowired
ApplicationContext applicationContext;
@Test
public void getBean(){
OwnerController bean = applicationContext.getBean(OwnerController.class);
assertThat(bean).isNotNull();
}
OwnerController ownerController = new OwnerController();
OwnerController ben = applicationContext.getBean(OwnerController.class);
위의 객체는 자바에서 생성되는 일반 객체이고, 아래는 빈이다.
이 두개의 차이점은 IoC컨테이너가 알고 있는 객체인지의 여부이다.
IoC 컨테이너에서 관리하고 있는 Bean들이 존재해야 역전 제어가 가능하다가
그렇다면 IoC 컨테이너에 Bean을 주입해야 하는데 그 방법으로는 크게 2가지가 존재한다.
Component Scanning
해당 어노테이션이 적혀있는 클래스들을 보고, 우리가 빈을 등록할 객체에 대해서 직접등록하는 것이 아니라 SpringBootApplication에서 Component Scan을 통해서 해당 어노테이션을 확인하면, 자동으로 빈으로 등록해주게 된다.
@Configuration //설정 관련 애노테이션
public class SampleConfig {
@Bean // Bean으로 아래 객체 직접등록
public SampleController sampleController(){
return new SampleController();
}
}
@Configuration 또한, @Component 안에 속한 어노테이션이므로, 컴포넌트 스캔에 포함된다.
@Bean => 컴포넌트 스캔을 하면서, Bean들이 IoC 컨테이너 안에 등록이 된다.
의존성을 주입하는 방법은 3가지가 존재하는데
자세한 방법은 포스트를 참고하면 된다.
참고자료
예제로 배우는 스프링 입문(개정판)