본문 바로가기
우테코 자유테크 스터디/자유주제

[스터디] 의존관계 주입(DI : Dependency Injection)

by ♡˖GYURI˖♡ 2023. 12. 8.

최근 김영한님의 Spring 핵심 원리 - 기본편 강의를 듣고 있는데 DI라는 단어가 자주 나왔다.

도대체 DI가 정확히 뭘까? 이번 기회에 한 번 알아보겠다.

 

 

의존관계란?

"A가 B를 의존한다."라는 표현은 어떤 의미일까?

의존대상B가 변하면, 그것이 A에 영향을 미친다.
- 이일민, 토비의 스프링 3.1, 에이콘(2012), p.113

 

즉, B의 기능이 추가 또는 변경되거나 형식이 바뀌면 그 영향이 A에 미친다는 뜻이다.

 

예시를 한 번 살펴보자.

 

"햄버거 가게 요리사는 햄버거 레시피에 의존한다."

햄버거 레시피가 변경된다면, 변경된 레시피에 따라서 요리사는 햄버거를 만드는 방법을 수정해야 한다. 레시피의 변화가 요리사에게 영향을 미쳤기 때문에 "요리사는 레시피에 의존한다."고 말할 수 있다.

class BurgerChef {
    private HamBurgerRecipe hamBurgerRecipe;

    public BurgerChef() {
        hamBurgerRecipe = new HamBurgerRecipe();        
    }
}

 

위의 구조는 다음과 같은 문제점들을 가진다.

 

1. 두 클래스의 결합성이 높다.

BurgerChef 클래스는 HamburgerRecipe 클래스와 강하게 결합되어 있다는 문제점을 가지고 있다. 만약 BurgerChef가 새로운 레시피인 CheeseBurgerRecipe 클래스를 이용해야 한다면 BurgerChef 클래스의 생성자를 변경해야만 한다. 만약 이후 레시피가 계속해서 바뀐다면 매번 생성자를 바꾸어주어야 하는 등, 유연성이 떨어지게 된다.

 

2. 객체들 간의 관계가 아닌, 클래스 간의 관계가 맺어진다.

객체 지향 5원칙(SOLID) 중 "추상화(인터페이스)에 의존해야지, 구체화(구현 크래스)에 의존하면 안 된다."라는 DIP 원칙이 있다. 현재 BurgerChef 클래스는 HamburgerRecipe 클래스와 의존 관계가 있다. 즉, BurgerChef는 클래스에 의존하고 있다. 이는 객체 지향 5원칙을 위반하는 것으로 BurgerChef 클래스의 변경이 어려워지게 된다.

 

 

의존관계를 인터페이스로 추상화하기

위 BurgerChef의 예시를 다시 보자. 지금의 구현에서는 BurgerRecipe만을 의존할 수 있는 구조로 되어있다. 더 다양한 BurgerRecipe를 의존받을 수 있게 구현하려면 인터페이스로 추상화해야 한다.

 

class BurgerChef {
    private BurgerRecipe burgerRecipe;

    public BurgerChef() {
        burgerRecipe = new HamBurgerRecipe();
        //burgerRecipe = new CheeseBurgerRecipe();
        //burgerRecipe = new ChickenBurgerRecipe();
    }
}

interface BugerRecipe {
    newBurger();
    // 이외의 다양한 메소드
} 

class HamBurgerRecipe implements BurgerRecipe {
    public Burger newBurger() {
        return new HamBurger();
    }
    // ...
}

 

의존관계를 인터페이스로 추상화하게 되면, 더 다양한 의존 관계를 맺을 수가 있고, 실제 구현 클래스와의 관계가 느슨해지고, 결합도가 낮아진다.

 

지금까지 의존관계에 대해 알아보았는데, 그렇다면 의존관계 주입은 무엇일까?

 

 

의존관계 주입이란?

지금까지의 구현에서는 BurgerChef 내부적으로 의존관계인 BurerRecipe가 어떤 값을 가질지 직접 정하고 있다. (요리사가 레시피를 결정하고 있다는 것이다.) 만약 어떤 BurgerRecipe를 만들지를 버거 가게 사장님께서 정하는 상황을 상상해보자. 즉 BurgerChef가 의존하고 있는 BurgerRecipe를 외부(사장님)에서 결정하고 주입하는 것이다.

 

이처럼 의존관계를 외부에서 결정하고 주입하는 것이 의존관계 주입(DI : Dependency Injection)이다.

 

 

토비의 스프링에서는 다음의 세 가지 조건을 충족하는 작업을 의존관계 주입이라 말한다.

  • 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스만 의존하고 있어야 한다.
  • 런타임 시점의 의존관계는 컨테이너나 팩토리같은 제 3의 존재가 결정한다.
  • 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.

 

 

DI 구현 방법

DI는 의존관계를 외부에서 결정하는 것이기 때문에, 클래스 변수를 결정하는 방법드이 곧 DI를 구현하는 방법이다. 런타임 시점의 의존관계를 외부에서 주입하면 DI 구현이 완성된다. 스프링 공식 문서에 따르면 다음과 같은 2가지 주요 방법을 가진다고 한다.

 

1. 생성자 주입

class BurgerChef {
	private BurgerRecipe burgerRecipe;
    
    public BurgerChef(BurgerRecipe burerRecipe) {
    	this.burgerecipe = burgerRecipe;
    }
}

class BurgerRestaurantOwner {
	private BurgerChef burgerChef = new BurgerChef(new HamburgerRecipe());
    
    public void changeMenu() {
    	burgerChef = new BurgerChef(new CheeseBurgerRecipe());
    }
}

 

생성자 주입은 이와 같이 생성자를 이용해 의존 관계를 주입한 것이다. 생성자의 주입은 생성자의 호출 시점에 1회 호출되는 것이 보장된다. 따라서 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우에 사용할 수 있다.

 

 

 

2. 수정자 주입 (Setter 메서드)

class BurgerChef {
	private BurgerRecipe burgerRecipe = new HamburgerRecipe();
    
    public void setBurgerRecipe(BurgerRecipe burgerRecipe) {
    	this.burgerRecipe = burgerRecipe;
    }
}

class BurgerRestaurantOwner {
	private BurgerChef burgerChef = new BurgerChef();
    
    public void changeMenu() {
    	burgerChef.setBurgerRecipe(new CheeseBurgerRecipe());
	}
}

 

이러한 방법은 생성자 주입과는 다르게 주입받는 객체가 변경될 가능성이 있는 경우에 사용된다.

 

이 외에도 필드 주입, 일반 메서드 주입 등의 방법이 있지만, 스프링은 생성자 주입을 사용하는 것을 권장한다. 의존 관계 주입의 변경이 필요한 상황은 거의 없다. 하지만 수정자 주입이나 일반 메서드 주입을 이용하면 불필요하게 수정의 가능성을 열어두게 된다. 생성자 주입을 통해 변경의 가능성을 배제하고, 불변성을 보장하는 것이 좋다.

 

 

 

DI 장점

1. 의존성이 줄어든다.

앞에서 설명했듯, 의존한다는 것은 그 의존대상의 변화에 취약하다는 뜻이다. (대상이 변화하였을 때, 그에 맞춰 수정해야하기 때문) DI로 구현하게 되었을 때, 주입받는 대상이 변하더라도 그 구현 자체를 수정할 일이 없거나 줄어들게 된다.

 

2. 재사용성이 높은 코드가 된다.

기존에 BurgerChef 내부에서만 사용되었던 BurgerRecipe를 별도로 구분하여 구현하면, 다른 클래스에서 재사용이 가능하다.

 

3. 테스트하기 좋은 코드가 된다.

BurgerRecipe의 테스트를 BurgerChef 테스트와 분리하여 진행할 수 있다.

 

4. 가독성이 높아진다.

BurgerRecipe의 기능들을 별도로 분리하게 되어 자연스레 가동성이 높아진다.

 

 

 

 


https://tecoble.techcourse.co.kr/post/2021-04-27-dependency-injection/

 

의존관계 주입(Dependency Injection) 쉽게 이해하기

이번 글에서는 DI(의존성 주입, 의존관계 주입)의 개념을 설명한다. DI란 용어가 주는 위압감과 부담감 때문에 이해를 미뤄뒀거나, 처음 접하는 분들이 쉽게 이해할 수 있도록 쉽게 설명하고자 한

tecoble.techcourse.co.kr

https://code-lab1.tistory.com/122

 

[Spring] 의존관계 주입(Dependency Injection), 의존성 주입, DI란?

의존관계(Dependency)란? 의존관계 주입(Dependency Injection)에 대하여 알아보기 전에 의존관계가 무엇인지 알아야 한다. 의존관계는 의존 대상 B가 변하면, 그것이 A에 영향을 미칠 때 A는 B와 의존관계

code-lab1.tistory.com