Dependency Injection(1/2)

Always·2024년 9월 26일
1

Backend&Devops

목록 보기
4/15

Dependency란?

  • A가 B를 의존한다는 것은 A가 B에 의존함에 따라서 B가 변경되면, A도 필연적으로 변경되는 것을의미합니다.
    • 변경되는 것은 코드가 변경될 수 도 있고, 나온 결과가 달라 질 수도 있습니다.
  • 예를 들어서 다음 처럼 아메리카노를 파는 카페의 서비스가 있습니다.

  • 현재 카페는 아메리카노만을 4000원에 파는 서비스를 하고 있습니다.
    • 그러나 만약 아메리카노만을 파는 것이 아닌 쥬스를 팔고 싶다면, Americano가 아닌 Juice로 클래스를 바꿔야할 것입니다.
    • 또한 기존의 Americano의 가격이 만약 5000원으로 변경된다면 sellAmericano의 return값또한 5000원으로 변경됩니다.
  • 즉, 아메리카노의 코드나 클래스가 변경됨에 따라서 카페 서비스의 코드,결과가 변경되므로, CaffeService는 Americano클래스에 의존합니다.

Injection이란?

  • 주입을 한다는 것은 객체가 가지고 여러 값 또한 객체를 주입하는 시점에 정해줍니다.
Americano americano=new Americano();
americano.setPrice(4000);
  • 위처럼 아메리카노의 가격을 주입해줌으로써 아메리카노의 가격은 주입되는 시점에 정해집니다.

그렇다면 Depdency Injection이란?

  • 즉, DI는 의존성이 있는 객체에 대해서 주입하는 시점에 어떤 객체를 사용할 지 정해줍니다.

  • 위 처럼 카페에 팔릴 제품이 뭔지는 카페서비스의 생성 시점에 주입하여서 정해줍니다.

그렇다면 DI를 왜 써?

  • 객체간의 강한 의존성을 약화시켜줍니다.
    • 자바의 다형성을 이용해서 이를 실현시킬 수 있습니다
    • 기존의 카페서비스에서는 아메리카노만 팔았는데, 이 대신 쥬스만 팔고 싶다고 합니다.
    • 이 때 만약에 위의 CaffeService클래스를 이용해서 구현을 한다면, 기존의 CaffeService코드를 변경해야하므로, 유연성이 떨어집니다
      • 또한 후에 새로운 음료로 바꾸고 싶을 때도 CaffeService클래스 내부를 변경해야하므로, 수정에 닫혀있어야하는 OCP원칙에도 어긋납니다.
    • 만약 Americano와 Juice의 인터페이스로 Drink로 두고, 코드를 짠다면 아래와 같이 짤 수 있습니다.

  • 위처럼 인터페이스를 활용하여, drink를 구현하여, juice,americano를 구현하면 DI를 활용하여, CaffeService에 대한 OCP원칙을 지킬 수 있으며, 자연스럽게 DIP원칙 역시 지켜집니다.
  • 즉 후에 새로운 drink를 추가하는 경우에, service단의 코드는 변경하지 않고, 새로운 drink클래스만 추가하면 됩니다.
  • 결국 기존의 강한 의존성이 약화된것을 확인 할 수 있습니다.
  • 강한 의존성이 약화되면서, 요구사항이 변경되었을 때 유연하게 대처가 가능하며, 위처럼 테스트 코드에서 잘 활용할 수 있습니다.

DI의 종류

  • 의존관계 주입에는 생성자 주입, setter주입, field 주입,메서드 주입이 존재합니다.

  • 생성자 주입

    • 위의 사례처럼 객체가 생성될 때, di가 되는 방식을 말합니다.

      public CaffeService(Drink drink){
              this.drink=drink;
      }
  • Setter 주입

    • 말 그대로 setter를 호출 할 때 주입 되는 의존관계입니다.
    • 변경, 선택 가능성이 있는 의존관계에 사용합니다
      • 주입된 객체가 후에 변경되는 경우에 사용된다고 보면 됩니다.

        public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
        }
  • field 주입

    • spring의 @Autowired 어노테이션을 이용해서 필드에 직접 주입하는 경우를 말합니다

      @Component
      public class OrderServiceImpl implements OrderService {
      @Autowired
      private MemberRepository memberRepository;
      @Autowired
      private DiscountPolicy discountPolicy;
      	
    • field 주입을 이용하면 Spring이 무조건적으로 관여하여서, Test코드 작성시 유연한 테스트 코드 작성이 어렵습니다.

    • 위의 테스트 코드는 생성자 주입으로 작성이 된 서비스 객체를 이용했기에 가능합니다
    • 만약 field주입으로 구현을 한다면, 만약 CaffeService에 다른 음료를 주입하고 싶다면, 실제 코드를 일일히 바꿔야할 것입니다.
  • 메서드 주입

    • 메서드 주입은 말 그대로 메서드를 호출하여 주입 하는 것입니다. 실질적으로 setter와 비슷합니다

      public void init(MemberRepository memberRepository, DiscountPolicy
      discountPolicy) {
      this.memberRepository = memberRepository;
      this.discountPolicy = discountPolicy;
      }

생성자 주입을 주로 사용하는 이유

  • 불변성: 의존관계가 한 번 주입된 후 변경되지 않으므로, 객체가 안전하게 유지됩니다.
    - 만약 setter,메서드 주입을 이용한다면 개발자도 사람인지라서 실수로 잘못 주입 할 수 있습니다.
  • 누락 방지: 의존성이 빠져있다면 컴파일 오류를 통해 바로 확인할 수 있습니다.
  • final 키워드 사용 가능: 생성자 주입으로 의존성을 주입하면 final로 선언할 수 있어, 컴파일 시 오류를 사전에 방지할 수 있습니다

필드 주입을 이용한 Test코드 작성(개인적인 생각)

@SpringBootTest
public class RoadmapServiceTest {
    @Autowired
    private RoadmapService roadmapService;
    @BeforeEach
    public void setUp() {
        roadmapService.updateAllRoadmaps();
    }
,,,
  • 개인적으로는 만약에 테스트 코드에서 springboot를 어차피 사용해야되며, 주입되는 객체의 종류가 단순히 한개라면, 필드 주입을 이용한 Test code작성은 매우 간편하게 작성 할 수 있습다고 생각하며, 조금더 간편하게 이용할 수 있을 것이라고 생각합니다.
    • 객체의 종류가 단순히 한개라는 것은 형제 객체들이 없는 것을 의미합니다

결론: DI(Dependency Injection)의 필요성과 활용

*DI(Dependency Injection)는 객체 간의 강한 의존성을 약화시키고 유연성**을 높이기 위해 사용되는 설계 패턴입니다. DI를 통해 객체 간의 결합도를 낮추고, 유지보수성과 확장성을 높일 수 있습니다. 다음 시간에는 IOC 컨테이너,@Autowired등의 spring에서 di가 이용되기 위해서 어떤 도구를 주는지 에 대해서 다루겠습니다.

profile
🐶개발 블로그

0개의 댓글