DI (Dependency Injection)

박준서·2024년 10월 16일
8

Web

목록 보기
6/16
post-thumbnail

의존성을 가지고 있다는 것

  • 의존성을 가지고 있다는 것은 자바에서 has a 관계에 있다는 것이다.
  • 우리가 만약 멀리 떨어져잇는 사람들과 통화(business logic)를 한다고 하자. 이 때, 우리는 당연하게도 전화기를 가져야 (has a) 한다.
    • 이 때, ‘전화기는 우리의 의존성’이 된다.

의존관계와 DI (의존 관계 주입)

  • 빈의 컨테이너로써의 스프링은 빈의 생성, 관계 설정, 객체 관리 까지, 즉 Bean의 라이프사이클을 담당한다.
public class PhoneUser {
	// Has a 관계 발생
	SPhone phone;
	
	// 직접 객체 생성
	public PhoneUser() {
			phone = new SPhone();
	}
}
  • 위 코드를 보면 PhoneUser는 SPhone를 사용하기 위해 이를 멤버 변수로 선언하고 사용한다. 이 것이 has a 관계이다.

  • 이 때, SPhone을 PhoneUser의 의존성이라고 하고 PhoneUser는 SPhone에 의존하고 있다고 한다.

    • PhoneUser을 위의 예시에서 우리라고 생각하고, 전화를 하기 위해 SPhone이라는 휴대폰이 필요하다고 생각하면 된다.
  • 근데 만약 위의 코드에서, 우리가 휴대폰을 변경해서 LPhone으로 변경해야 한다고 하자.

public class PhoneUser {
	// 의존관계의 변경
	LPhone phone;
	
	// 직접 객체 생성
	public PhoneUser() {
			phone = new LPhone(); // 생성되는 객체 변경 필요
	}
}
  • 위의 코드를 보면 SPhone이 적혀있던 코드를 모두 LPhone으로 바꿔줘야 한다.
  • 즉, 의존성이 변경 될 때 마다 의존하는 객체가 변경되어야 한다. 이는 유지보수적 측면에서 심각하다.

의존성 객체가 변경될 때 의존하는 객체의 변경 없애기

  • 위의 코드를 객체가 tight coupling 하다 라고 한다. 이 객체를 loose coupling으로 바꾸어 유지보수성을 높여보자.

interface 활용

  • 의존성인 LPhone과 SPhone에 인터페이스를 적용해서 PhoneUser와 loose coupling 하도록 만들어준다.
  • interface SmartPhone을 만든다.
public class PhoneUser {
	// 의존관계의 변경
	SmartPhone phone; // has a 대상을 인터페이스로 변경
	
	// 직접 객체 생성
	public PhoneUser() {
			phone = new LPhone(); // 구현체가 변경되면 여전히 코드 수정 필요 
	}
}
  • 위에서 휴대폰이 SPhone으로 변경되더라도, 여전히 SmartPhone 타입으로 사용하기 때문에 수정해야 할 부분이 줄어든다.
  • 하지만 SPhone으로 구현체가 변경되면 SPhone으로 코드를 변경해야 한다.
    • 직접 객체를 만드는 순간에 종속성이 생긴다.

Factory Pattern의 적용

  • 객체를 공장에서 찍어내서 공급하는 형태이다.
public class PhoneFactory {
	public static SmartPhone getPhone(String maker) {
		if(maker.equalsIgnoreCase("s") {
			return new SPhone();
		} else {
			return new LPhone();
		}
	}
}
  • 이를 사용하는 PhoneUser
public class PhoneUser {
	private SmartPhone phone;
	
	public PhoneUser(SmartPhone phone) { // 생성자를 이용한 주입
		this.phone = phone;
	}
	
	public void setPhone(SmartPhone phone) { // setter을 이용한 주입
		this.phone = phone;
	}
	
	public SmartPhone getPhone() {
		return this.phone;
	}
}
  • PhoneUser는 의존성인 phone 타입의 객체를 생성자와 setter을 통해서 주입받고 있다.
  • 결과적으로, PhoneUser는 SPhone 또는 LPhone의 구현체에서 완전히 독립함으로써, 의존성 변경 시 코드 수정이 발생하지 않게 되었다.
  • 새로운 SmartPhone이 공급되더라도, 수정되어야 할 것은 설정을 담는 PhoneFactoru 이며, 비즈니스 로직을 가지는 PhoneUser는 영향을 받지 않는다.

💡 하지만 위와 같은 Factory Pattern은 너무 복잡하다

  • 시스템 복잡도가 너무나 증가 했다.
  • 후에 Bean 재사용성을 위해서 Singleton Pattern도 필요해 지고, 라이프 사이클 까지 고려하면 개발자가 고민할 내용이 너무 많아진다.

DI를 통한 의존성 주입

  • 스프링은 위와 같은 문제들을 DI, 즉 의존성 주입이라는 것으로 처리한다.
  • 스프링은 SPhone, LPhone, PhoneUser와 같은 객체를 Bean으로 관리한다.
  • Bean 객체의 생성은 스프링 컨테이너가 담당한다.
public class PhoneUser {
	private SmartPhone phone;
	
	public PhoneUser(SmartPhone phone) {
		this.phone = phone;
	}

}
  • 위 코드에서 phone이 할당되는 시점은, 스프링 컨테이너가 생성자 메서드를 호출하면서 SmartPhone 타입의 객체를 전달한 후 이다.
  • 위와 같이 의존성인 SmartPhone을 PhoneUser에서 만들지 않고 외부에서 넣어주는 것을 의존성 주입이라고 한다. 이 일을 메타설정에 의해 스프링 프레임워크가 해준다는 것이 중요한 것이다
profile
Back-End Developer

0개의 댓글

관련 채용 정보