Skip to content

Latest commit

 

History

History
145 lines (89 loc) · 6.34 KB

File metadata and controls

145 lines (89 loc) · 6.34 KB

스프링 의존성 주입(Dependency Injection)

의존성 주입의 개념과 필요성

의존성 주입(Dependency Injection)이란?

Spring 프레임워크는 3가지 핵심 프로그래밍 모델을 지원하고 있는데 그 중 하나가 의존성 주입이다.

의존성 주입이란 외부에서 두 객체 간 관계를 설정해주는 디자인 패턴으로, 인터페이스 사이에 둬서 클래스 레벨에서는 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입 하여 유연성을 확보하고 결합도를 낮출 수 있게 해준다.

의존성이란 한 객체가 다른 객체를 사용할 때 의존성이 있다고 한다. 예를 들어 다음과 같이 Store 객체가 Pencil 객체를 사용하고 있는 경우에 우리는 Store 객체가 Pencil 객체에 의존성이 있다고 표현한다.

public class Store{

    private Pencil pencil;

}

image

두 객체 간 관계(의존성)을 맺어주는 것을 의존성 주입이라고 하며 생성자 주입, 필드 주입, 수정자 주입 등 다양한 주입 방법이 있다. 스프링 4 이후부터는 생성자 주입을 권장하고 있다.

[생성자 주입의 장점]

  • 생성자 호출 시점에 딱 한번만 호출되는 것을 보장하여 불변하게 설계가 가능(필드를 final로 선언 가능)
  • 생성자 주입을 사용하면 의존성 주입 누락을 컴파일 단계에서 막을 수 있다.
  • 필드 주입, 수정자 주입과는 다르게 객체 생성시점에서 순환참조를 방지할 수 있다.
  • 테스트 코드를 순수 자바로 테스트 하는 것이 가능하다. (테스트에 용이)

image

의존성 주입(Dependency Injection)의 필요성

예를 들어 연필이라는 상품과 1개의 연필을 판매하는 Store 클래스가 있다고 하자.

public class Store {

    private Pencil pencil;

    public Store() {
        this.pencil = new Pencil();
    }

}

이 코드의 문제점은 다음과 같다.

  1. 두 클래스가 강하게 결합되어 있음
  2. 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어짐

[ 1. 두 클래스가 강하게 결합되어 있음 ]

  • 위 코드와 같은 Store 클래스는 현재 Pencil 클래스와 강하게 결합되어 있다는 문제점이 있다.
  • Store에서 Pencil이 아닌 Food와 같이 다른 상품을 취급하자고 할 때 Store 클래스에 생성자 변경이 이루어지며 유연성이 떨어진다.
  • 다른 상품을 판매하기 위해 생성자만 다르고 나머지는 중복되는 Store 클래스를 파생하는 것은 좋지 않다.

[ 2. 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어짐 ]

  • 위 코드의 Store와 Pencil은 객체들 간의 관계가 아니라 클래스들 간의 관계가 맺어져 있다는 문제가 있다.

  • 올바른 객체지향적 설계라면 객체들 간 관계가 맺어져야 한다.

  • 객체들 간에 관계가 맺어졌다면 다른 객체의 구체 클래스(Pencil인지 Food인지 등)를 전혀 알지 못하더라도 (Interface를 구현한 상태라면) 인터페이스의 타입으로 사용할 수 있다. (구현에 의존하면 안되고 추상화에 의존해야 한다)

  • 결국 위 같은 문제점이 발생한 근본적인 이유는 Store에서 불필요하게 어떤 제품을 판매할 지에 대한 관심이 분리되지 않았기 때문이다. Spring에서는 DI를 통해 이를 해결했다.

의존성 주입(Dependency Injection)을 통한 문제 해결

위 같은 문제를 해결하기 위해서는 다형성이 필요하다. Pencil과 Food 등 여러 가지 제품을 하나로 표현하기 위한 Interface 인 Product가 필요하다.

public interface Product {

}

public class Pencil implements Product {

}

Store와 Pencil이 강하게 결합되어 있는 기존 코드를 수정해주어야 한다. 이를 제거하기 위해서는 다음과 같이 외부에서 상품을 주입(Injection)받아야 한다. 그래야 Store에서 구체 클래스에 의존하지 않는다.

public class Store {

    private Product product;

    public Store(Product product) {
        this.product = product;
    }

}

여기서 Spring의 DI 컨테이너를 사용하는 이유를 알 우 있는데, 우선 Store에서 Product 객체를 주입하기 위해선느 애플리케이션 실행 시점에 필요한 객체(Bean)을 생성해야 하며, 의존성이 있는 두 객체를 연결하기 위해 한 객체를 나머지 객체로 주입시켜야 하기 때문이다.

코드처럼 Pencil이라는 객체를 만들고 그 객체를 Store로 주입시켜주는 역할을 위해 DI 컨테이너가 필요하다.

public class BeanFactory {

    public void store() {
        // Bean의 생성
        Product pencil = new Pencil();
    
        // 의존성 주입
        Store store = new Store(pencil);
    }
    
}

이러한 개념은 제어의 역전(Inversion of Control, IoC)라고 불리기도 한다.

어떠한 객체를 사용할지에 대한 책임은 프레임워크에게 넘어갔고, 자신은 수동적으로 주입받는 객체를 사용하기 때문이다.

의존성 주입 정리

의존성 주입 정리

한 객체가 어떤 객체(구체 클래스)에 의존할 것인지는 별도의 관심사이다. Spring은 의존성 주입을 도와주는 DI 컨테이너로써, 강하게 결합된 클래스들을 분리하고, 애플리케이션 실행시점에 객체 간 관계를 설정해줌으로써 결합도를 낮추고 유연성을 확보한다.

이러한 방법은 상속보다 유연하지만 한 객체가 다른 객체를 주입받기 위해서는 반드시 DI 컨테이너에 의해 관리되어야 한다. 때문에 의존 관계를 계속해서 생성하고 소멸하는 데는 오버헤드가 발생하기 때문에 Spring에선 기본적으로 Bean을 싱글톤(Singleton)으로 관리한다.

  • 두 객체 간의 관계라는 관심사의 분리
  • 두 객체 간의 결합도를 낮춤
  • 객체의 유연성을 높임
  • 테스트 작성을 용이하게 함

참조 :

https://mangkyu.tistory.com/150