Notice
Recent Posts
Recent Comments
관리 메뉴

Developer Gonie

7. 의존성 주입(DI, Dependency Injection) 이란 본문

K-DigitalTraining 강의/11. Spring

7. 의존성 주입(DI, Dependency Injection) 이란

이대곤 2022. 8. 23. 19:39

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

다음은 의존성 주입을 설명하기 위한 예시이다.

 

Car 인터페이스가 존재하고,

이를 상속(implement)받아 drive메소드를 완성한 Grandeur, Morning 클래스가 존재하며,

아무것도 상속받지 않은 Consumer 클래스도 존재하는데, Car 인터페이스 타입의 car 멤버변수를 가진다.

이렇게 설계한 경우 Consumer 객체가 취향에 따라 car 멤버변수의 값으로 

Grandeur 객체를 갖거나 Morning 객체를 갖는게 가능하다.(Grandeur, Morning 모두에 의존 가능한 Consumer )

 

만약 이와 다르게 Car 인터페이스가 존재하지 않고,

아무것도 상속받지 않은 상태로 구현된 Grandeur, Morning 클래스가 존재한다면

Consumer 클래스를 작성할 때

Grandeur만 가질 수 있는 Consumer1 클래스(Grandeur에만 의존하는Consumer1)를 작성해야하고,

Morning만 가질 수 있는 Consumer2 클래스(Morning에만 의존하는Consumer2)를 따로 또 작성해야 할 것이다.

재사용성이 현저히 떨어진다.

 

다른 말로 전자는 결합도가 느슨하다고 표현하며, 후자는 결합도가 강하다고 표현한다.

결합도가 느슨해질수록 재사용성이 높아진다.

이렇듯 더 다양한 타입의 객체에 의존 받을 수 있게 구현하려면 인터페이스로 추상화해야 한다.

 

전자의 방식으로 설계된 경우 Consumer 클래스의 car 변수가 인터페이스 타입이라서 여러 타입의 객체를 받을 수 있지만

그래도 객체가 만들어질 때는 이 인터페이스를 상속받은 클래스중 하나로 정해주어 모호한 상태를 없애줘야 한다.

이것은 프로그래머가 해줄 일인데 어디에 의존하게 할 지 그 의존관계를 외부에서 결정하고 주입하는 것이 DI 이다.

 

클래스 변수의 타입이 추상화된 상태에서 벗어나 구체적으로 되도록 결정하는 방법들이 곧 DI를 구현하는 방법이다.

 

DI를 구현하는 가장 기본적인 방법으로

1) 생성자를 이용한 방법이 있고,

2) setter 메소드를 이용한 방법이 있다.

 

바로 아래쪽에 main 함수를 가지고 있는 Main 클래스를 각각의 방식으로 따로 구현해 보았는데

이를 살펴보면 DI가 무엇인지 알 수 있을 것이다.

public class Main {
	public static void main(String[] args) {
		
        Morning morning = new Morning();
    
		Consumer consumer = new Consumer();
        
		// setter 메소드에 의해 모호했던 car 변수의 타입이 Morning으로 명확해지는 순간(이것이 DI)
		consumer.setCar(morning); 
	}
}
public class Main {
	public static void main(String[] args) {
		
        Morning morning = new Morning();
    
    	// 생성자에 의해 모호했던 car 변수의 타입이 Morning으로 명확해지는 순간(이것이 DI)
		Consumer consumer = new Consumer(morning);
 
	}
}
public interface Car {
	public String drive();
}
public class Grandeur implements Car{

	public Grandeur() {
		System.out.println("Grandeur 생성자 호출됨");
	}
	
	@Override
	public String drive() {
		return "Grandeur-drive";
	}
}
public class Morning implements Car{

	public Morning() {
		System.out.println("Morning 생성자 호출됨");
	}
	
	@Override
	public String drive() {
		return "Morning-drive";
	}
}
public class Consumer implements Person{

	private String name;
	private Car car;
	
	Consumer(Car car){
		this.car = car;
	}
    
	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
    
	public void setCar(Car car) {
		this.car = car;
	}
    
	public Car getCar() {
		return car;
	}

	@Override
	public String personDrive() {
		return car.drive();
	}
}

의존 관계를 분리하여, 주입을 받는 코드 구현의 장점

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

 

1. 의존성이 줄어든다.
의존한다는 것은 그 의존대상의 변화에 취약하다는 것이다.(대상이 변화하였을 때, 이에 맞게 수정해야함) 
DI로 구현하게 되었을 때, 주입받는 대상이 변하더라도 그 구현 자체를 수정할 일이 없거나 줄어들게됨.


2. 재사용성이 높은 코드가 된다.
인터페이스를 상속받아 구현하면, 위의 예시와 같이 Consumer1, Consumer2 클래스를 따로 만들 필요가 없어

재사용성이 높아진 코드가 된다.

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

인터페이스를 상속받아 구현된 여러 클래스의 테스트를 주입되는 객체만 바꿔가며 테스트가 가능해진다.


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

Comments