JAVA/Spring

[Spring] 의존성 주입(Dependency injection)은 무엇인가?

이 현호 2023. 2. 27. 11:50

위와 같이 Product(상품)에서 Computer(컴퓨터)를 의존하고 있다.

외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴이 바로 의존성 주입이다.

 

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

 

의존성이란 한 객체가 다른 객체를 사용할 때 의존성이 있다고 한다.

 

위의 그림을 아래의 코드로 작성해보자.

 

public class Product {
    private Computer computer;
}

 

Store 객체가 Pencil 객체를 사용하고 있는 경우에 우리는 Store 객체가 Pencil 객체에 의존성이 있다고 표현한다.

Spring 4부터는 생성자 주입을 강력히 권장하고 있다.

 

 

 

[ 의존성 주입(Dependency Injection)이 필요한 이유 ]

예를 들어 컴퓨터라는 상품과 컴퓨터 1대를 판매하는 Product 클래스가 있다고 하자.

public class Product {
    
    private Computer computer;

    public Product() {
        this.computer = new Computer();
    }
}

 

위와 같은 예시 클래스는 크게 다음과 같은 문제점을 가지고 있다.

 

1. 두 클래스가 강하게 결합되어 있다.


위와 같은 Product 클래스는 현재 Computer 클래스와 강하게 결합되어 있다는 문제점이 있다.

 

두 클래스가 강하게 결합되어 있어서 Product에서 Computer가 아닌 Monitor와 같은 다른 상품을 판매하고자 한다면 Product 클래스의 생성자에 변경이 필요하다.

 

 

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


위의 Product와 Computer는 객체들 간의 관계가 아니라 클래스들 간의 관계가 맺어져 있다는 문제가 있다.

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

 

다른 객체의 구체 클래스(Computer인지 Monitor인지 등)를 전혀 알지 못하더라도

(해당 클래스가 인터페이스를 구현했다면) 인터페이스의 타입(Company)으로 사용할 수 있다.

 

 

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

위와 같은 문제를 해결하기 위해서는 우선 다형성이 필요하다.

Computer, Monitor 등 여러 가지 제품을 하나로 표현하기 위해서는 Company 라는 Interface가 필요하다.

그리고 Computer에서 Company 인터페이스를 구현한다.

 

public interface Company {

}

public class Computer implements Company {

}

 

이제 Product와 Computer이 강하게 결합되어 있는 부분을 제거해줘야 한다.

이를 제거하기 위해서는 다음과 같이 외부에서 상품을 주입받아야 한다.

 

 

그래야 Product에서 구체 클래스에 의존하지 않게 된다.

 

public class Product {

    private Company company;

    public Product(Company company) {
        this.company = company;
    }
}

 

이러한 이유로 Spring 이라는 DI 컨테이너가 필요한다.

Product에서 Company 객체를 주입하기 위해서는 애플리케이션 실행 시점에 필요한 객체(빈)를 생성해야 하며, 의존성이 있는 두 객체를 연결하기 위해 한 객체를 다른 객체로 주입시켜야 한다.

 

예를 들어 다음과 같이 Pencil 이라는 객체를 만들고, 그 객체를 Product로 주입시켜주는 역할을 위해  DI 컨테이너가 필요한 것이다.

 

public class BeanFactory {
    public void product() {
        // Bean의 생성
        Company computer = new Computer();
        
        // 의존성 주입
        Product product = new Product(computer);
    }
}

 

이러한 부분은 스프링 프레임워크가 완벽하게 지원을 해준다.

스프링은 특정 위치부터 클래스를 탐색하고, 객체를 만들며 객체들의 관계까지 설정해준다.

 

이러한 이유로 스프링은 DI 컨테이너라고 부른다.

 

결론

Spring은 의존성 주입을 도와주는 DI 컨테이너로써, 강하게 결합된 클래스들을 분리하고,

애플리케이션 실행 시점에 객체 간의 관계를 결정해 줌으로써 결합도를 낮추고 유연성을 확보한다.

 

이러한 방법은 상속보다 훨씬 유연하고, 한 객체가 다른 객체를 주입받으려면 반드시 DI 컨테이너에 의해 관리되어야 한다.

 

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

 

Spring 에서는 Bean들을 기본적으로 싱글톤으로 관리한다.

반응형