IoC(Inversion Of Control)는 제어의 역전이다. IoC가 무엇인지에 대해서는 이 글을 참고하면 이해가 쉬울 것 같다!
우리가 다루고 있는 모든 객체를 하나의 부품이라고 생각해보자...!
그렇다면 스프링이 갖고있는 아주 기본적인 역할 중 하나는 부품을 조립해주는 능력이다.
스프링은 객체의 생성과 객체 간의 의존성을 관리해주는 기능을 한다.
이 개념을 이해하기 위해서는 DI(Dependency Injection)과 IoC(Inversion of Control) 컨테이너이라는 두 용어에 대해 알고 있어야 한다.
지난 포스팅에서 DI에 대해 다루었으니, 오늘은 IoC 컨테이너에 대해 알아보려고 한다!
부품을 조립(객체를 생성)하기 위해서는 주문서가 있어야 한다. 어떤 부품이 얼마나 필요하고, 어떤 부품이 다른 것에 속하는 더 작은 부품이 될 지에 대해서 스프링이 알 수 있도록 도와주어야 한다.
💋 Spring의 IoC(Inversion of Control) 컨테이너가 뭘까?
IoC 컨테이너는 객체를 생성하고 관리하는 역할을 한다.
제어의 역전이라고 번역되는 IoC가 무엇인지에 대해서도 이전의 포스팅에서 다룬 적이 있다.
우리가 사용하던 자바 프로그램에서는 프로그래머가 직접 객체를 생성하고, 원하는 클래스 내에서 다른 객체를 생성해 사용했다. 이 경우 프로그래머가 객체의 생명 주기를 관리하고 있다.
제어의 역전이 일어나면, 프로그래머가 아닌 다른 무언가가 관리를 위임하게 된다. 프로그래머의 제어 권한을 다른 주체에게 넘기는 것을 IoC(제어의 역전)라고 한다. Spring은 직접 자바 객체를 생성하고 관리하기 때문에 이 관리 위임 주체는 Spring이 된다.
우리가 어떤 부품이 얼만큼 필요하다는 주문서를 만들어 스프링에게 주면, 스프링은 주문서대로 객체를 생성해서 담을 수 있는 그릇이 필요한데, 이런 그릇을 소프트웨어에서는 일반적으로 '컨테이너'라고 부른다.
IoC 컨테이너라는 이름에는 단순히 객체를 생성만 하는 것이 아니라, 내부에서 객체를 조립(의존성 주입)까지 한다는 의미를 더 포함하고 있다고 볼 수도 있다.
Spring IoC 컨테이너는 객체의 생성과 관리, 객체간의 의존성을 관리 하는 기능을 통해서, 유연하고 확장 가능한 애플리케이션을 만드는 것에 도움을 준다.
Spring IoC 컨테이너는 다양한 설정 방식을 제공한다.
XML, Java Configuration, Annotation 등의 방식을 사용하여 IoC 컨테이너를 설정할 수 있고, IoC 컨테이너가 생성하는 객체의 종류나 의존성 등을 유연하게 관리할 수 있다.
💋 Spring IoC 컨테이너의 생명 주기는 어떻게 될까?
Spring 공식문서에서 설명하는 IoC 컨테이너의 생명주기는 아래와 같다.
✔ Resource 로딩
IoC 컨테이너가 사용할 설정 파일을 로드하는 단계이다. 설정 파일에는 어떤 객체를 생성하고 어떻게 의존성을 가질지에 대한 정보가 포함되어 있어서, 이 설명서를 보고 사용할 Bean 객체를 정의할 수 있다.
Spring에서 지원하는 설정 파일 형식은 다양한데, 그 종류로는 XML, Properties, Annotation 등이 있다. 이 단계에서는 실제로 Bean 객체를 생성하는 건 아니고, 설정 파일에서 정의한 Bean 객체들을 메모리에 로드하고 Bean 객체간의 의존성을 해결하는 일을 한다.
✔ Bean 설정
Bean 객체를 구성하는 메타 데이터를 읽어들인다. 이런 메타데이터는 XML, Annotation 등에서 정의된다.
메타 데이터 내용으로는 Bean의 이름과 타입, 의존성, 생성자의 파라미터, 프로퍼티 값, 초기화와 소멸 메서드 등이 있다.
이 단계에서는 이런 정보들을 읽어서, IoC 컨테이너가 사용할 수 있는 형태로 변환한다.
*메타 데이터: 데이터를 설명하는 데이터다. 데이터의 속성, 구조, 형식에 대한 내용을 담고 있고, 데이터를 분류, 검색, 공유, 보안 등의 목적으로 사용된다.
✔ Bean 인스턴스화
Bean 설정 단계에서 변환한 정보를 기반으로 Bean 객체를 만드는 단계이다!
부품을 만드는 단계라고 생각하면 이해하기 쉽다.
이 단계에서는 Bean 클래스의 생성자를 실행해 객체를 생성한다.
✔ 의존성 주입
IoC 컨테이너가 Bean 간의 의존성을 주입한다. 부품을 조립하는 단계라고 생각하면 좋다.
이 단계에서는 빈이 필요로 하는 다른 빈을 주입한다.
[개인적 궁금증] Resource 로딩 단계에서도 Bean 의존성을 해결한다는데, 뭐가 다른거지?
1단계 Resource 로딩 단계에서는 Bean 객체가 실제로 생성되기 이전에 발생한다. 설정 파일에서 Bean의 정보를 읽어들이는 단계이기 때문에, 아직 Bean 간의 의존성을 완전히 해결했다기보다는, 의존하는 Bean이 존재하는 지만 검사하고, 메모리에 로드할 뿐이다. 실제로 의존성이 주입되지는 않는다.
4단계에서는 Bean 객체가 생성된 이후에 발생한다. 따라서 실제로 Bean간의 의존성이 주입된다. IoC 컨테이너는 Bean 객체가 의존하는 다른 Bean 객체를 검색하고, 주입한다.
✔ Bean 초기화 콜백
Bean 인스턴스가 생성된 후에, IoC 컨테이너가 빈을 초기화하기 위한 콜백 메서드를 호출한다.
콜백 메서드 호출을 통해서 빈이 구성되고 준비되었다는 것을 알린다.
* 콜백 메서드(Callback Method): 어떤 이벤트가 발생하면 미리 등록해둔 메서드를 자동으로 호출하는 것이다.
[개인적 궁금증] Bean 인스턴스를 생성한 후에 왜 별도로 초기화를 하는거지?
Bean에는 종종 생성자를 호출하는 것 만으로 초기화할 수 없는 작업들이 존재한다.
예를 들어서, 외부 리소스에 대한 연결 작업, 초기 데이터 로딩 작업과 같은 것들을 해야지만, 완전히 초기화되어 '사용 가능'한 상태가 될 수 있다. 의존성 주입 이후에 초기화 콜백 메서드를 호출해서, 부족한 초기화 작업을 수행한다.
Spring에서는 초기화 콜백 메서드를 지원하기 위해 InitializingBean 인터페이스의 afterPropertiesSet() 메서드나 @PostConstruct 어노테이션을 제공한다.
✔ Bean 사용
IoC 컨테이너가 빈을 사용한다.
이 단계에서는 빈이 요청되어 사용된다.
✔ Bean 소멸 콜백
빈이 더 이상 필요하지 않을 때, IoC 컨테이너는 빈의 소멸 메소드를 호출하고 해당 빈을 제거한다.
이 단계에서는 빈이 더 이상 사용되지 않을 때 콜백 메서드를 호출하여 리소스를 해제한다.
Spring에서는 소멸 콜백 메서드를 지원하기 위해 DisposableBean 인터페이스의 destroy() 메서드나 @PreDestroy 어노테이션을 제공한다.
사용자가 만든 빈은 이러한 콜백 메서드를 이용하여 초기화 작업이나 소멸 작업을 수행할 수 있어서, 더 안정적으로 사용될 수 있다.
✔ Resource 해제 (Context 종료)
IoC 컨테이너 자체가 종료되기 때문에, 빈들도 함께 소멸된다.
IoC 컨테이너의 생명 주기는 애플리케이션이 시작될 때 시작되며, 애플리케이션이 종료될 때 함께 소멸한다.
빈 객체는 IoC 컨테이너가 관리되기 때문에, IoC 컨테이너가 종료될 때 빈 객체도 함께 소멸한다.
💋 참고자료
'JAVA' 카테고리의 다른 글
[JAVA] 프로세스와 스레드: 개념, Java의 쓰레드 구현, I/O Blocking (0) | 2023.09.08 |
---|---|
[JAVA] 좋은 객체 지향 설계의 5가지 원칙 (SOLID) (0) | 2023.05.01 |
[JAVA] 좋은 코드가 되려면 꼭 지켜야 할 기본적인 컨벤션 (0) | 2023.04.13 |
[JAVA] 제네릭(Generic)이란? (0) | 2023.04.12 |
[JAVA] 원시값 포장과 VO(Value Object) (0) | 2023.03.30 |