✍️ 학습 목표
- IoC
- DI
📌 IoC (Inversion of Control)
IoC란? Inversion of Control의 약자로 단어 그대로 해석하면 '제어의 역전'이라는 뜻이다.
이전에는 프로그램의 흐름을 개발자가 주도했지만 스프링에서는 이 흐름이 달라진다.
메소드나 객체의 호출을 개발자가 아닌 스프링 컨테이너에서 담당한다.
스프링 컨테이너는 IoC 컨테이너라고도 불린다.
✔️ IoC 컨테이너
- IoC 컨테이너란? 객체를 생성, 관리 그리고 의존성을 관리해주는 컨테이너이다.
- IoC 컨테이너가 관리하는 객체를 Bean이라고 하는데, Bean을 저장해서 BeanFactory라고도 불린다.
- BeanFactory는 하나의 인터페이스이다. 의존 관계 외에 다양한 기능을 제공하는데 이 BeanFactory에 이런저런 기능을 추가한 인터페이스를 ApplicationContext라고 한다.
✔️ 설정 메타 정보
- IoC 컨테이너의 기본적인 역할은 빈을 생성하고 관리하는 것이다.
- 여기서 설정 메타 정보란? Bean을 어떻게 만들고 동작시킬 지에 관한 정보이다.
- BeanDefinition을 추상화해서 Java, XML 등 다양한 형식의 코드를 받아들일 수 있다.
- 어떤 코드인지 상관없이 BeanDefinition만 알면 빈 설정 메타 정보를 만들어낼 수 있다.
결국 스프링이 객체의 생명주기를 관리해주기 때문에 객체 간의 결합도를 줄이고 유연한 코드를 작성할 수 있게 해준다.
IoC 덕분에 스프링의 다른 주요 특징인 DI와 AOP를 가능케 된다.
📌 DI (Dependency Injection)
DI란? Dependency Injection의 약자로 '의존성 주입'이라는 뜻이다.
DI를 이해하기 위해서는 우선 dependency부터 이해해야한다.
✔️ dependency(의존 관계)
만약 자동차가 오래되서 어떤 부품을 교체해야할 때 단순히 해당 부품만 교체해주면 된다.
객체 지향 프로그래밍에서도 마찬가지로 어떤 클래스에 대한 변경이 필요하면 다른 클래스에 영향을 미치면 안된다.
여기서 'A가 B를 의존한다'는 B가 수정되면 A에 영향을 미치는 것을 말한다.
아래 코드에서 기계공은 작업에 따라 자신의 행동이 바뀌게 된다. 따라서 기계공은 작업에 의존한다라고 할 수 있다.
// Example
class Mechanic {
private Work work;
public Mechanic() {
work = new Work();
}
}
반면에 아래 코드는 기계공이 작업에만 의존하는 구조를 벗어나기 위해서 다양한 작업을 인터페이스로 추상화했다.
// Example
class Mechanic {
private Work work;
public Mechanic() {
work = new changeEngine();
// work = new changeBattery();
// work = new changeBrake();
}
}
interface Work {
newWork();
}
class changeEngine implements Work {
public Engine newEngine() {
return new changedEngine();
}
}
이렇게 의존 관계를 인터페이스로 추상화하면 더 다양한 의존 관계를 맺을 수 있고 클래스와의 관계도 느슨해져 결합도가 낮아지게 된다.
✔️ 그래서 DI는?
지금까지 보인 예제에서는 Mechanic 내부에서 의존 관계인 Work가 어떤 값을 가질지 직접 정한다.
이 때 DI(의존성 주입)는 어떤 작업을 할 지 기계공장이 정하는 것이라고 볼 수 있다.
// Example
class Mechanic {
private Work work;
public Mechanic(Work work) {
this.work = work;
}
}
// 외부에서 dependency를 주입
new Mechanic(new changeEngine());
new Mechanic(new changeBrake());
new Mechanic(new changeSuspention());
- 이렇게 외부에서 의존 관계를 결정하는 것을 DI(의존성 주입)라고 한다. 3가지 방법이 존재한다.
- 스프링에서는 프레임워크 즉, 스프링 컨테이너가 빈을 주입해준다.
1️⃣ 필드 주입 (Filed)
// Example
public class WorkController {
@Autowired
private Work work;
}
- 변수 선언부에 @Autowired만 붙여주면 된다.
- 사용하기 쉬우나 스프링 컨테이너와 결합도가 커지는 등 다양한 문제가 있어 지양하는 방법이다.
2️⃣ 수정자 주입 (Setter)
// Example
public class WorkController {
private Work work;
public void setWork(Work work) {
this.work = work;
}
}
- setter 메소드에 @Autowired 어노테이션을 붙여준다.
- 선택적인 의존성을 사용할 수 있다. 다만 이 점이 순환 참조 등의 문제를 발생시킬 수 있다.
3️⃣ 생성자 주입 (Constructor)
// Example
public class WorkController {
private Work work;
public work(Work work) {
this.work = work;
}
}
- 생성자에 @Autowired 어노테이션을 붙여준다. 가장 권장되는 방법이다.
- 불변성을 보장해주고 NPE(Null Pointer Exception)를 방지할 수 있다.
'Web > Spring' 카테고리의 다른 글
[Spring] Spring의 MVC (0) | 2022.09.14 |
---|---|
[Spring] Spring의 DB 접근 기술 (0) | 2022.08.23 |
[Spring] Spring vs Spring Boot (2) (0) | 2022.08.23 |
[Spring] Spring vs Spring Boot (1) (0) | 2022.08.19 |
[Spring] Spring Data JPA (2) (0) | 2022.08.13 |
댓글