TIL no.99 - Spring - 1 - IoC Container

박준규·2020년 3월 21일
1

Spring

목록 보기
1/2

Spring Triangle 중 하나인 IoC에 대해 알아보겠습니다.

1. IoC란?

IoC는 Inversion of Control의 약어입니다.
"제어권의 역전"이라는 말인데 한국말로 직역하니 이게 뭐야..? 싶습니다.

다음 두 class를 통해 알아보도록 하겠습니다.

class Service1 {
    private Repository repository = new Repository();
}
class Service2 {
    private Repository repository;
    
    public Service2(Repository repository) {
    	this.repository = repository;
    }
}
  • 공통점

Service1과 Service2의 공통점은
Service라는 class의 instance는 어떤 행동을 하기 위해
Repository라는 class의 instance가 필요합니다.

이렇게 어떤 class의 instance가 다른 class의 instance를 필요로 하는 경우 의존성(Dependency)이 있다고 표현합니다.

  • 차이점

이제, 두 Service의 차이점을 살펴보겠습니다.

Service1의 경우 Repository의 instance를 자신이 직접 만들어서 사용합니다.

Service2의 경우, Repository의 instance를 사용하지만 직접 생성하지 않고 생성자를 통해 미리 만들어져 있는 객체를 받아옵니다.
즉, Repository의 instance를 만드는 일은 Service의 일이 아닌 것입니다.

그럼 Service2 class 밖에서 다음과 같은 일이 일어나야 합니다.

Repository repository = new Repository();
Service2 service2 = new Service2(repository);

Service 밖에서 repository라는 Dependency를 생성, 관리하고 주입해줘야합니다.

  • IoC Container

이렇게 밖에서 의존성을 관리(Control)하고 주입해주기 때문에 제어권의 역전이라고 표현합니다. 자신이 사용할 Dependency 관리를 다른 곳에서 대신 하기 때문이죠.

그리고 Spring에서는 위와 같이 Dependency를 일일이 생성,관리,주입을 해줄 필요가 없습니다.
Spring IoC Container가 해주기 때문이죠.
Spring IoC Container는 Bean을 관리해주고 필요한 곳에 주입해줍니다.

Bean이란 Spring IoC Container에 의해 관리되는 instance를 뜻합니다.

IoC에 대한 자세한 내용은 Martin Fowler의 글을 참조해주세요.

2. IoC Container

Spring이 제공하는 IoC Container는
Bean을 생성하고
Bean들 사이의 Dependency를 엮어주며
Bean을 제공하는 일을 합니다.

Spring 프로젝트에서 생성한 모든 class가 전부 Bean으로 등록되지는 않습니다. 그렇다면 IoC Container 안에 어떻게 Bean으로 등록할까요?

특정한 annotation을 사용하거나
특정한 interface를 상속하는 경우 Spring IoC Container 안에 Bean으로 등록됩니다.

의존성 주입을 영어로 Dependency Injection이라고 합니다.

Dependency Injection은 Spring IoC Container에 등록되어 있는 Bean끼리만 가능합니다.

3. Bean

Spring IoC Container가 관리하는 instance를 Bean이라고 합니다.

그러므로 일반적으로 new 연산자를 통해 생성하는 instance는 Bean이 아닙니다.

코드를 통해 비교해보겠습니다.

@Service
class Service {
    private Repository repository;
    
    public Service(Repository repository) {
        this.repository = repository;
    }
}

Service라는 class는 @Service라는 annotation을 통해 Bean으로 등록해주었습니다. 이제 위 class의 instance는 Spring IoC Container가 관리하는 것이죠.

@Controller
class Controller {
    private Service service;
    
    public Controller(Service service) {
        this.service = service;
    }
    
    public void getBean() {
    	System.out.println("It is Bean" + this.service));
        System.out.println("It is not Bean" + new Service());    
    }
}

Controller가 주입받고 있는 service는 @Service라는 애노테이션으로 Bean등록이 되어있는 instance입니다.
그리고 Controller의 getBean이라는 메서드에서 사용되는
this.service와 new Service()는 서로 같은 class의 instance이지만 서로 다른 instance이죠.

그리고 this.service는 Spring IoC Container에 의해 Dependency를 주입받은 Bean이며 new Service()로 생성된 instance는 Spring IoC Container가 관리하지 않는 객체 즉, Bean이 아닌 객체입니다.

4. Dependency Injection

의존성을 주입하는 여러 방법이 있습니다.

위에서 본 방법은 생성자에서 dependency를 명시해줌으로써 Spring이 DI를 알아서 해주었습니다. 이 기능은 Spring 4.3 이상부터 지원되었습니다.

그렇다면 Spring 4.3 이전 버전에서는 어떻게 DI가 이뤄졌을까요?

class Controller {
    private Servcie service;
    
    @Autowired
    public Controller(Service service) {
        this.service = service;
    }
}

위와 같이 @Autowired 라는 annotation을 이용해야 했습니다.

그런데, Spring 4.3부터는 class의 생성자가 하나뿐이고 생성자가 참조하는 변수들이 Spring IoC Container에 등록되어있는 Bean들이라면 @Autowired annotation을 생략하더라도 DI가 이뤄지도록 기능이 추가되었습니다.

생성자를 통해 DI를 하는 방법말고도
field에 @Autowired annotation을 통해 DI를 해줄 수 있습니다.

class Controller {

    @Autowired
    private Servcie service;

}

위와 같이 하더라도 의존성을 주입받아 사용할 수 있습니다.

하지만, Spring에서 권장하는 방법은 생성자를 이용하는 것입니다.
왜냐하면 필수적으로 필요한 의존성(Bean)이 없다면 instance를 만들 수 없게 강제성을 띄도록하기 위함입니다.

그렇다면 field를 통해 의존성을 주입받는 방법이 있는 이유는 뭘까요?

circular dependency 문제를 손쉽게 해결할 수 있기 때문입니다.
circular dependency란 서로 의존한다는 것 입니다.

역시 코드를 바로 보는게 좋겠죠.

class A {

    B b;
    
    public A(B b) {
        this.b = b;
    }
}

A는 B라는 class의 instance가 필요합니다.
그러므로 생성자를 통해 b라는 instance를 주입받습니다.
그런데, 만약 B라는 class에서도 위와 같이 A의 instance가 필요하고 생성자를 통해 DI가 이뤄지고 있다면 이 둘은 instance를 생성할 수 없습니다.

A를 만드려면 B를 받아야 한다.
B가 없으니 B를 만들자.
B를 만드려면 A를 받아야 한다.
A가 없으니 A를 만들자.

Over and Over... 위와 같은 상황이 발생하겠죠?

그러므로 field injection은 위와 같이 circular dependency 상황일 때 사용할 수 있습니다.

하지만, 애플리케이션을 설계할 때 위와 같은 상황이 일어나지 않게 설계하는 것이 중요하겠습니다.

Summary

Java Class간의 Dependency를 Spring이 관리해준다.
그러므로 Dependency가 필요한 Class의 경우 자신이 Dependency를 생성하고 관리할 필요가 없다.

이를 Inversion Of Control이라 한다. Control하는 주체에 대한 Inversion이기 때문이다.

이 과정에서 Spring은 Dependency가 필요한 class의 instance를 생성할 때, 알아서 dependency를 주입해주는데 이를 Dependency Injection이라고 한다.

그리고 이렇게 주입되는 instance를 Bean이라고 하며 이러한 행위들은 Spring IoC Container에 의해 행해진다.

profile
devzunky@gmail.com

2개의 댓글

comment-user-thumbnail
2020년 4월 6일

2주 넘게 learn up 하지 않고 계시네요.

1개의 답글