지난 시간에 DI에 대해서 알아보면서, 외부에서 의존성을 주입하는 것이 왜 중요한지 알게되었다(추상화 타입과 캡슐화의 관점에서). 만약 기억이 나지 않는다면 아래 포스팅을 다시 살펴보자.
의존성 주입 형태로 객체사이의 협력관계를 구성하기 위해서는 반드시 프로그램 어딘가에서 각 객체들을 인스턴스화 하고, 의존성을 직접 주입해주어야 한다.
프로그램 내부에 의존 관계가 적으면 상관 없겠지만, 프로젝트가 커질 수록 더 많은 등장인물(객체)이 나타나고, 이들 사이의 관계(의존성)은 굉장히 많아진다. 이 모든 의존성을 어딘가에서는 주입을 해주어야 한다는 이야기고 이는 보통 힘든일이 아니다.
그래서 우리가 사용하는 Framework가 이런 객체의 관리를 대신해주며, 이러한 디자인 패턴을 우리는 IoC(Inversion of Control)이라고 부른다.
우리가 Framework에 필요한 객체들을 등록하고, DI가 필요한 객체들 사이의 의존 관계를 적절하게 표현하면 Framework가 이 객체들을 직접 관리해준다.
Spring 공식 문서의 core-technologies에 들어가 보면 가장 먼저 나오는 것이 Ioc Container이다.
Spring에서는 BeanFactory(ApplicationContext)가 IoC Container로서 행동하고, 객체들을 Bean으로 등록해 직접 인스턴스화, 의존성 주입을 담당한다.
이렇게 제어권이 framework로 역전되면 우리는 코드를 작성할 때 위에서 언급했던 복잡한 의존성 주입과정을 신경쓰지 않을 수 있다. 대신 Framework가 원하는 방식의 규칙(Annotataion)만 지켜주면 되는 것이다.
@Controller
class OwnerController(
private val ownerService: OwnerService
) {
// ...
}
@Service
class OwnerService(
private val ownerRepository: OwnerRepository
){
// ..
}
OwnerController
객체의 경우 OwnerService
라는 객체를 의존하고 있으며, 생성자 주입 방식으로 의존성을 주입받기를 원한다. 하지만 우리가 직접 작성한 코드에는 의존성 주입 과정이 없다.
대신 Spring이 @Controller
를 보고 이를 Bean
에 등록하며, OwnerService
역시 @Service
를 보고 Bean
에 등록한다. 이후 서로의 의존관계에 따라 객체를 인스턴스화 하고 주입해준다.
(Spring BeanFactory의 구체적인 동작 방식은 추후에 다시 공부 후 작성할 예정이다)
연관된 3개의 포스팅으로 해당 부분을 마무리 하기 전에 흐름을 한 번 정리해보자.
이렇게 흐름을 따라 각각의 개념을 우리가 왜 배워야 하는지, 왜 이러한 구조로 코드를 작성해야 하는지 천천히 이해해보자. 단순히 강의를 보고 생성자 주입이 좋더라! 라고 하지말고 상황에 따라 어떤 DI 방식이 좋은지도 생각해보면서 유연하게 사고할 수 있는 능력을 기르자.