[IoC 컨테이너와 빈] 1. 스프링 IoC 컨테이너와 빈
[전체 목차]
- IoC 컨테이너와 빈
- 스프링 IoC 컨테이너와 빈
- Resource / Validation
- 데이터 바인딩
- SpEL
- 스프링 AOP
- Null-Safety
Inversion of Control: 의존 관계 주입 (Dependency Injection)이라고도 하며,
어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는게 아니라, 주입 받아 사용하는 방법을 말함.
1. 스프링 IoC 컨테이너
- BeanFactory
- 애플리케이션 컴포넌트의 중앙 저장소
- 빈 설정 소스로부터 빈 정의를 읽어들이고, 빈을 구성하고 제공한다.
생성자라는 걸 사용해서 주입을 받아서 사용을 하는걸 IoC라고 한다.
이건 스프링이 없어도 장치만 있다면 충분히 할수 있다.
2. 빈
1) IoC 컨테이너를 사용하는 이유는?
여러 개발자들이 스프링 커뮤니티에서 논의해서 만들어낸..
여러가지 Dependency Injection 방법과 베스트 practice들, 노하우가 쌓여있는 프레임워크이기 때문이다.
스프링의 이 IoC 컨테이너 안에있는 객체를 빈이라고 부른다.
컨테이너라고 부르는 이유는, 그 안에 IoC 기능을 제공하는 빈들을 담고 있기 때문.
빈들이 컨테이너에 들어있고, 우리는 그 빈들을 가져와서 사용할 수 있다.
스프링 초기에는 xml로 설정하는게 대세였다.
현재와서는 어노테이션 기반의 Dependency Injection 을 지원하기 시작했다.
2) BeanFactory
Spring IoC 컨테이너의 가장 최상위에 있는 인터페이스는 BeanFactory라는 인터페이스이다.
다양한 구현체들이 있는데. 이 인터페이스가 가장 핵심이다.
위 링크에 나오는 이런 다양한 라이프 사이클들을 관리하는 인터페이스들을 통해서 스프링의 여러가지 기능들을 더 제공해 줄수 있다.
컨테이너 안에 있는 빈들을 이 인터페이스들을 사용해서 가공하는 것이 가능하기 때문이다.
가장 중요한 메서드 중엔 뭐 getBean()
같은 것들이 있다.
3) @Repository
랑 @Service
와 같은 어노테이션으로 왜 빈으로 등록하는가?
왜 IoC 컨테이너가 관리를 하게 했는가?
의존성 주입때문이다. 의존성 주입을 받으려면 빈으로 들어가 있어야 받을수 있다.
또, 싱글톤으로 관리하고 싶을 때도 IoC 컨테이너를 사용하면 굉장히 편리하다.
스프링 IoC 컨테이너에 등록되는 빈들은 기본적으로 싱글톤 스코프로 빈으로 등록이 된다. (싱글톤 <-> 프로토타입)
우리가 기본적으로 빈으로 등록할 때, 아무런 어노테이션을 붙이지 않았다면, 그 빈들은 모두 싱글톤 스코프로 등록이 된다.
따라서, 애플리케이션 전반에서 우리가 스프링 IoC 컨테이너로부터 ExampleService 또는 ExampleRepository를 받아서 사용한다면, 그 인스턴스들은 항상 같은 객체일 것이며, 효율적일 것이다.
메모리면에서도 효율적이고,
이미 컨테이너 안에 미리 만들어놨던 그 객체를 사용하기 때문에 런타임 시에 성능 최적화에도 유리하다. (지연 초기화 기능 등도 제공 됨)
또한, 매번 만들어서 사용하는 프로토타입에 비해,
특히 테이터베이스와 어떤 일을 하는 리파지터리 객체들은 만드는 비용이 비쌀텐데,
싱글톤으로 쉽게 사용할 수 있다면 충분히 큰 장점이 된다.
또, 스프링 IoC 컨테이너에 빈이 등록 되면 다양한 라이프사이클 인터페이스를 지원해준다.
예를 들어, 어떤 빈이 만들어졌을때 추가적인 작업을 하고 싶고하면,
@PostConstruct
와 같은 라이프사이클 콜백에 해당하는 어노테이션을 사용해서, 특정 메서드가 빈 생성시 실행되게 할수 도 있다.
4) Repository 객체를 만드는 비용이 비싼이유는?
각 엔티티에 대한 매핑 정보를 로드하고,
이를 기반으로 데이터베이스 스키마와 연관된 메타데이터를 초기화하는 과정에서 상당한 비용이 발생한다.
Spring Data JPA에서 메타데이터는 엔티티와 데이터베이스 테이블 간의 매핑 정보를 포함한다.
예를 들어, @Entity
, @Table
, @Column
, @Id
등의 어노테이션은 메타데이터를 정의하며,
이 정보를 통해 JPA는 객체와 데이터베이스 테이블을 연결한다.
5) 의존성 주입을 받지 않는 경우 단위테스트가 힘든 이유는?
의존성 주입이 없으면,
테스트 대상 객체가 외부 의존성에 직접 결합되어 독립적으로 검증하기가 어려워지고,
모킹이 어려워 테스트 속도가 느려지고 결과의 신뢰성이 낮아진다.
의존성 주입을 통해 테스트 대상을 격리하고, 모킹할 수 있어야 단위테스트가 간편하고 효율적으로 수행 될수 있다.
만약, 의존성 주입없이 테스트코드를 작성하면 해당 객체에서 사용하는 다른 객체에 대해 직접 의존성을 넣어줘야하는데..
그럼 객체간에 강한 결합이 생긴다. (생성에 서로 직접적인 관계가 생김)
생성자가 변경되거나 하면 그 객체를 직접 주입하는 객체의 생성자에서도 코드 변경이 필요할수 있음.
그러나, 의존성 주입을 하면 이미 다 독립적으로 만들어진 객체 빈들이 이미 있고,
테스트할때 해당 객체가 필요하면 생성자로 의존성만 넣어서 관계만 맺어주면 되므로 생성에 직접적인 관계는 없이 느슨한 결합이다.
이렇게 되면 필요에 따라 생성된 다른 객체로 바꿔 낄수도 있기때문에 모킹된 가짜 객체를 끼워 넣어서도 테스트가 쉽게 가능하다.
3. ApplicationContext
1) IoC 컨테이너에 중요한 인터페이스
- BeanFactory (최상위 인터페이스)
- ApplicationContext (실질적으로 가장 많이 사용)
- BeanFactory를 상속 받음.
- BeanFactory가 가지는 IoC 컨테이너의 속성을 가지고있으면서 추가적으로 여러 기능들을 가지고 있다.