[인프런] 백기선님의 강의 「예제로 배우는 스프링 입문 (개정판)」 을 듣고 정리
스프링 IoC
IoC = Inversion of Control
IoC란 제어권이 역전되었다. 는 의미다.
일반적인 경우, 자기가 만든 의존성을 자기가 사용한다.
이 의존성에 대한 제어권이 역전되었다는 것은 이 제어권을 원래는 자기자신이 가지고 있었는데 다른 누군가에게 넘어갔다는 의미이다.
다음 코드를 봐보자.
class OwnerController {
private OwnerRepository repository = new OwnerRepository();
}
위 코드에서 OwnerController 클래스는 내부에서 OwnerRepository 객체를 직접 만들어 쓴다. 이 경우, 의존성에 대한 제어권이 자기자신에게 있다고 할 수 있다.
class OwnerController {
private OwnerRepository repo;
public OwnerController(OwnerRepository repo) {
this.repo = repo;
}
}
그런데 이 코드에서는 OwnerController가 OwnerRepository를 사용은 하지만 OwnerRepository 객체를 내부에서 만들지는 않는다. 누군가가 밖에서 줄 수 있게끔 생성자를 통해 받아온다.
의존성을 만드는 일을 더이상 OwnerController가 하지 않고 누군가가 OwnerController밖에서 해준다. 그렇기 때문에 제어권이 역전되었다고 볼 수 있다.
이렇게 의존성을 주입해주는 것을 Inversion of Control이라고 한다.
그래서 Dependency Injection(의존성 주입)도 일종의 IoC라고 볼 수 있다.
스프링 IoC 컨테이너
주로 하는 일 : 빈(Bean)을 만들고, 빈들 사이의 의존성을 엮어주며, 빈들을 제공해준다.
빈 설정
- 이름 또는 ID
- 타입
- 스코프
IoC 컨테이너는 BeanFactory 또는 ApplicationContext 중 하나를 사용한다. 이 중 ApplicationContext는 BeanFactory를 상속받으며, 그 외에도 더 다양한 기능을 가지고 있다.
알아둬야 할 점은 「의존성 주입은 빈끼리만 가능하다.」 는 것이다.
빈(Bean)이란 스프링이 관리하는 객체를 의미한다.
모든 객체가 다 빈으로 등록되어 있진 않다. 빈은 상속, Annotation, 직접 등록 등 다양한 방법으로 등록될 수 있는데, 이때 서로 간의 의존성 주입을 IoC 컨테이너가 해준다.
즉, 스프링 IoC 컨테이너 안에 들어있는 객체끼리만 의존성 주입을 해줄 수 있다. 밖에 있는 객체들은 X
스프링 빈(Bean)
빈(Bean)이란 스프링 IoC 컨테이너가 관리하는 객체를 의미한다.
그냥 객체를 만들었다고 해서 다 Bean이 아니다. Bean과 일반적인 객체의 차이는 스프링 IoC 컨테이너, 즉 ApplicationContext가 알고있는 객체냐 아니냐, ApplicationContext가 만들어서 그 안에 담고 있는 객체냐 아니냐다.
그냥 new로 객체를 만들면 Bean이 아니다.
그렇다면 어떻게 스프링 컨테이너 안에 Bean을 만들까?
크게 2가지 방법이 있다.
- @Component
- @Repository
- @Service
- @Controller
- @Configuration 등등....
- 또는 직접 일일히 XML이나 자바 설정 파일에 등록하는 방법
이 중 @Component Annotation을 이용하는 방법에 대해 설명해 보자면, Annotation Processor(어노테이션 처리기) 중에 스프링 IoC 컨테이너가 사용하는, 스프링 IoC 컨테이너를 만들고 그 안에 Bean을 등록할 때 사용하는 인터페이스들이 있는데, 그런 인터페이스들을 Lifecycle Callback이라 한다.
여러 Lifecycle Callback 중에는 이런 @Component가 붙어있는 Model Class를 찾아서 그 클래스의 인스턴스를 만들어서 Bean으로 등록하는, 그런 일을 처리하는 Annotation Processor(어노테이션 처리기)가 등록되어 있다.
@SpringBootApplication 안에 @ComponentScan이란 Annotation이 있는데 이 Annotation이 어느 지점부터 컴포넌트를 찾아봐야 할지를 알려준다. 해당 위치부터 모든 하위 패키지에 있는 모든 클래스를 다 찾아본뒤 @Component 등을 사용하는 Class를 찾아 Bean으로 등록하는 것이 ComponentScan의 기능이다.
Bean으로 등록하는 코드의 예시를 살펴보자. 첫번째로 @Component Annotation을 사용해서 Bean으로 등록하는 방법이다.
@Controller
public class SampleController {
}
클래스 위에 @Component를 붙여주기만 하면 된다. ComponentScan이 알아서 찾아서 Bean으로 등록한다.
두번째로, 직접 Bean으로 등록하는 방법이 있다.
@Configuration
public class SampleConfig {
@Bean
public SampleController sampleController() {
return new SampleController;
}
}
@Bean을 사용해 클래스 내에서 직접 Bean으로 등록해 준다.
이 밖에도 XML 파일에서 직접 빈으로 등록할 수 있다.
그렇다면 꺼내쓸 때는 어떻게 할까?
이것도 크게 2가지 방법이 있다.
- @Autowired 또는 @inject
- 또는 ApplicationContext에서 getBean()으로 직접 꺼내쓴다.
@Autowired를 사용해 IoC 컨테이너 안에 들어있는 Bean을 주입받아서 사용할 수 있다.
@Autowired
private OwnerRepository owners;
의존성 주입 (Dependency Injection)
스프링이 제공하는 다양한 의존성 주입 방법이 있다. 하나씩 살펴보자.
@Autowired / @inject를 사용할 수 있는 지점이 많다.
- 생성자
- 필드
- Setter
스프링 버전 4.3부터 어떠 클래스에 생성자가 하나뿐이고, 그 생성자로 주입받는 reference 변수들이 Bean으로 등록되어 있다면 그 Bean을 자동으로 주입해주도록 하는 기능이 스프링 프레임워크에 추가되었다. (= @Autowired 생략이 가능하다.)
@Controller
public class OwnerController {
private OwnerRepository owners;
// @Autowired -> 생략 가능
public OwnerController(OwnerRepository repo) {
this.owners = repo;
}
}
생성자가 아닌 필드로 바로 주입받는 방법이 있다.
위 코드를 이렇게 고치면 된다.
@Controller
public class OwnerController {
@Autowired
private OwnerRepository owners;
}
Setter에 @Autowired를 붙이는 방법
@Controller
public class OwnerController {
private OwnerRepository owners;
@Autowired
public void setOwners(OwnerRepository owners) {
this.owners = owners;
}
}
스프링 IoC 컨테이너가 OwnerController 인스턴스를 만들고나서 Setter를 통해서 IoC 컨테이너에 들어있는 Bean 중에 OwnerRepository 타입을 찾아서 여기에 넣어준다.
이렇듯 의존성을 주입받는 방법은 여러가지가 있지만 이 중에서 생성자를 사용하는 방법을 권장한다.
생성자를 사용하는 방법이 좋은 이유는 필수적으로 사용해야하는 reference 옵션에 이 인스턴스를 만들지 못하도록 강제할 수 있기 때문이다. 이게 무슨 소리냐면 위 코드에서 OwnerController 클래스는 OwnerRepository가 없으면 제대로 동작하지 않는 클래스라고 하자. OwnerRepository는 OwnerController 클래스에게 반드시 있어야 할 객체다. 따라서 OwnerRepository 객체가 제대로 주입되지 않으면 아예 OwnerController 클래스인스턴스가 만들어지지 않는다. 이걸 강제하기 위한 가장 좋은 수단이 생성자를 사용하는 것이다.
필드 Injection이나 Setter Injection은 일단 이 클래스의 인스턴스를 만들 수가 있다.
하지만 그 자체가 장점이 될 수 있는 경우가 있는데 바로 순환 참조(Circular Dependency)가 발생하는 경우다. 경우에 따라 클래스 A와 B가 서로를 참조할 때 둘다 생성자 Injection을 사용하고 있다면 둘다 못 만든다. 그런 경우에는 필드 또는 Setter Injection을 사용해서 일단 인스턴스를 만든 다음 서로의 인스턴스를 주입하면 된다. 이렇게 상호 참조하는 의존성 문제를 해결할 수 있다.
'공부 > Spring' 카테고리의 다른 글
[스프링 인 액션] Chapter 3 데이터로 작업하기 (0) | 2022.05.08 |
---|---|
[스프링 인 액션] Chapter 2 웹 어플리케이션 개발하기 (0) | 2022.04.18 |
[스프링 인 액션] Chapter 1 스프링 시작하기 (0) | 2022.04.08 |
[스프링 프레임워크 Core] 스프링 IoC 컨테이너와 빈 (0) | 2020.06.06 |
스프링 AOP & PSA (0) | 2020.05.31 |