안녕하세요
스프링 공부를 하면서 제가 배운 것을 블로그에 정리를 하겠습니다.
오늘은 DI, IoC에 대해서 자세히 설명할게요

DI(Dependency Injection)
DI(Dependency Injection)란 스프링이 다른 프레임워크와 차별화되어 제공하는 의존관계 주입 기능으로,
객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입시키는 방식이다.
DI(의존성 주입)을 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아집니다. 의존관계 주입을 사용하면 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있다.
자바로 프로그래밍 하면서 객체를 생성할 때 직접 클래스에 new 연산자를 이용하여 생성하였습니다. 하지만 DI는 개발자가 직접 코딩을 하여 객체를 생성하는 것이 아니라, 컨테이너가 이를 생성시켜 주는 것입니다. 그렇게 된다면 코드에서 직접적인 연관관계가 발생하지 않아 각 클래스들이 변경이 자유로워집니다. 이를 느슨한 결합이라고 합니다.
각 클래스들간 결합도가 높게 되면 나중에 프로젝트 복잡해질 시 유지보수가 힘들게 됩니다. 따라서 각 클래스들 간 연관 관계를 클래스 자체 내에서 맺어주는 것이 아니라 스프링 자체에서 설정을 통해 연관관계를 맺어줌으로써 결합도를 낮춰줍니다.
"A가 B를 의존한다"는 말은 의존 대상 B가 변하면 그것이 A에 영향을 미친다는 것이다.
밑에 예를 들어서 설명을 해드리겠다.
치킨 집 사장님이 치킨 집 레시피에 의존합니다. 치킨 레시피가 변화하게 되었을 때
변화된 레시피에 따라서 사장님은 치킨 만드는 방법을 수정을 해야합니다.
레시피의 변화가 사장님의 행위에 영향을 미쳤기 때문에 요리사는 레시피에 의존한다고 말할 수가 있습니다.
class ChickenChef {
private ChickenChefRecipe chickenchefRecipe;
public ChickenChef() {
chickenchefRecipe = new ChickenChefRecipe();
}
}
의존 관계를 인터페이스로 추상화
ChickenChef의 예시를 보면 ChickenChefRecipe만을 의존할 수 있는 구조로 되어있다.
다양한 Chicken을 의존받을 수 있게 구현하려면 인터페이스로 추상화해야 합니다.
class ChickenChef {
private ChickenRecipe chickenRecipe;
public ChickenChef() {
chickenRecipe = new GarlicChickenRecipe();
// chickenRecipe = new OnionChickenRecipe();
// chickenRecipe = new SpicyChickenRecipe();
}
}
interface ChickenRecipe{
Chicken newChicken();
}
class GarlicChickenRecipe implements ChickenRecipe{
@Override
public Chicken newChicken() {
return new Chicken();
}
}
지금까지의 구현에서는 ChickenChef 내부적으로 의존 관계인 ChickenRecipe가 어떤 값을 가질지 직접 정하고 있다. 만약 어떤 ChickenRecipe를 만들지를 치킨 사장님이 정하는 상황을 상상해보면, 즉 ChickenChef가 의존하고 있는 ChickenRecipe를 외부(사장님)에서 결정하고 주입하는 것입니다.
이처럼 그 의존관계를 외부에서 결정하고 주입하는 것이 DI(의존관계 주입)입니다.
DI 구현 방법은
-생성자 이용-
DI는 의존관계를 외부에서 결정하는 것이기 때문에, 클래스 변수를 결정하는 방법들의 곧 DI를 구현하는 방법이다.
런타임 시점의 의존관계를 외부에서 주입하여 DI 구현이 완성된다.
Chicken 주인장이 어떤 레시피를 주입하는지 결정하는 예시로 설명을 해봅시다
class ChickenChef {
private ChickenRecipe chickenRecipe;
public ChickenChef(ChickenRecipe chickenRecipe) {
this.chickenRecipe = chickenRecipe;
}
}
class ChickenOwner{
private ChickenChef chickenChef = new ChickenChef(new ChickenRecipe());
public void changeMenu(){
chickenChef = new ChickenChef(new SpicyChickenRecipe());
}
}
그 외에 Setter 주입과 필드 주입이 있는데 스프링에서는 생성자 주입을 요구를 합니다.
이유 설명은 이 블로그에서 참고 부탁드려요
IoC(Inversion of Control)
IoC(Inversion of Control)란 "제어의 역전"이라는 의미로, 말 그대로 메서드나 객체의 호출 작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것을 의미합니다.(프레임워크)
IoC는 제어의 역전이라고 말하며, 간단히 말해 "제어의 흐름을 바꾼다"라고 한다.
객체는 의존성을 역전시켜 객체 간의 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여 가독성 및 코드 중복, 유지 보수를 편하게 할 수 있게 한다.
기존에는 다음과 같은 순서로 객체가 만들어지고 있습니다.
- 객체 생성
- 의존성 객체 생성 - 클래스 내부 생성
- 의존성 객체 메서드 호출
하지만, 스프링에서는 다음과 같은 순서로 객체가 만들어지고 있습니다.
- 객체 생성
- 의존성 객체 주입
스스로 만드는 것이 아니라 제어권을 스프링에게 위임하여 스프링이 만들어놓은 객체를 주입합니다. - 의존성 객체 메소드 호출
스프링이 모든 의존성 객체를 스프링이 실행될 때 다 만들어주고 필요한 곳에 주입시켜줌으로써 Bean들이 싱글턴 패턴의 특징을 가지며, 제어의 흐름을 사용자가 컨트롤하는 것이 아니라 스프링에게 맡겨 작업을 처리하게 합니다.
※참고※
https://mangkyu.tistory.com/125
https://mangkyu.tistory.com/150
https://velog.io/@gillog/Spring-DIDependency-Injection
'java and sping' 카테고리의 다른 글
| [Spring] JPA에 Enum 사용법 (0) | 2023.03.22 |
|---|---|
| [Spring] DTO를 사용하는 이유? (0) | 2023.03.18 |
| [Spring] Spring Data JPA에서의 페이지네이션과 정렬 (1) | 2023.03.17 |
| [Spring] JPA 데이터베이스 스키마 자동 생성 - 속성 (0) | 2023.02.16 |
| [Java] Lombok 롬복 라이브러리란? (1) | 2023.01.02 |