본문 바로가기

좋아하는 것_매직IT/1.spring

5.Spring, 의존성 주입에 대해서 이해한번 해봅시다. ^^

반응형

클래스의 의존성이란?

  • 대부분의 자바 클래스는 다른 클래스에 의존함.
  • 일반적으로 잘 디자인된 모든 애플리케이션은 여러개의 Layer 가 존재하고, 모든 Layer 는 각자의 명확한 책임을 가지게된다.
    • 다시말해서, 각각 Layer 는 아래와 같다.
      • Bussiness Layer 
        • Bussiness 로직이 포함됨.
      • Data Layer 
        • 외부 인터페이스 및 데이터베이스와 통신하여 Data 를 가지고옴.
  • 예시
public class BusinessThubServiceImpl {

  public long sumThub(ThubUser tuser) {
    DataThubServiceImpl dataThubService = new DataThubServiceImpl();
    long ret = 0;
    for (ThubData tdata : dataThubService.queryData(tuser)) {
      ret += tdata.getValue();
    }

    return ret;
  }
}
  • 클래스 설명
    • BusinessThubServiceImpl 클래스
      • 데이터 서비스 ​​​​​인 ​DataThubServiceImpl과 데이터를 주고 받고 비즈니스 로직을 추가하는 전형적인 비즈니스 서비스임.
      • 비즈니스 로직
        • dataThubService 가 반환하는 데이터의 합계를 계산하여 반환함.
    • DataThubServiceImpl 클래스
      • 사용자와 관련된 데이터를 데이터베이스에서 가져옴.
  • 의존성
    • BusinessThubServiceImpl 은 DataThubServiceImpl 에 의존함.
      • 즉, DataThubServiceImpl은 BusinessThubServiceImpl의 의존성임.
  • 이 예시는 아래와 같이, BusinessThubServiceImpl은 DataThubServiceImpl의 인스턴스를 생성하는 방법에 중점을 두고 있음.
DataThubServiceImpl dataThubService = new DataThubServiceImpl();
  • 설명
    • BusinessThubServiceImpl 은 인스턴스 자체를 만들게 되는데 이것은 단단한 결합을 의미한다.
    • 이것은 단위테스트를 할 수 없는 단점이 있음.
    • 여기서 고민 한가지가 생성된다 (ㅡ_ㅡ);;; 
      • 즉, BusinessThubServiceImpl 와 DataThubServiceImpl 간 단단한 결합을 줄일 수 있는 방법을 생각해봐야한다. ^^;
    • 해결책
      • 하나, DataThubServiceImpl의 인터페이스를 작성하는 방법
        • 즉, 위의 예시 처럼 직접 클래스를 사용하는 대신 BusinessThubServiceImpl 에서 DataThubServiceImpl 가 새로 만든 인터페이스를 사용할 수 있음.
        • 아래예시는 인터페이스를 만드는 방법이다.
public interface DataThubService {
    List<ThubData> queryData(ThubUser tuser);
}

인터페이스를 사용하기 위해 BusinessThubServiceImpl 코드를 업데이트해보자.

public class BusinessThubServiceImpl {

  public long sumThub(ThubUser tuser) {
    DataThubService dataThubService = new DataThubServiceImpl();
    long ret = 0;
    for (ThubData tdata : dataThubService.queryData(tuser)) {
      ret += tdata.getValue();
    }

    return ret;
  }
}
  • 즉, 위와 같이 인터페이스를 사용하면, 느슨하게 결합된 코드를 만들 수 있음.
    • 인터페이스를 구현하는 모든 와이어링을 잘 정의된 의존성으로 교체할 수 있음.
    • 예를들면, 암호화가 필요한 비즈니스를 생각해 볼 수 있다.
      • 첫번째, 옵션은 직접 AES 암호화 알고리즘을 사용하는 것.
        • but, AES 암호화 알고리즘을 DES 암호화 알고리즘으로 변경하는 문제가 발생 시 코드를 직접 변경해야하는 단점이 발생함. 
          • 즉, 유연하게 대처하지 못함.
      • 두번째, 옵션은 방금 변경한거와 같이 암호화 알고리즘에 대한 인터페이스를 만든 후 인터페이스를 사용하는 것.
        • 즉, 특정 암호화 알고리즘은 나중에 유연하게 와이어링될 수 있음.
          • 즉 코드가 아니라 추후 와이어링만 변경하면 됨.
  • but, 위의 코드는 DataThubService 인터페이스는 사용하고 있으나, 여전히 BusinessThubServiceImpl 클래스는 DataThubServiceImpl의 인스턴스 생성과 밀접하게 결합되어 있는 구조이다.
    • 고민한가지 또 발생 -_-;;
      • 그럼 이 문제를 어떻게 해결해야하나?
      • 해결책은 바로 인스턴스를 다른곳에서 만들어 주는 방법이있다.
      • 그럼, 코드를 다시한번 업데이트 해보자 ^^;
public class BusinessThubServiceImpl {

  private DataThubService dataThubService;

  public long sumThub(ThubUser tuser) {
    
    long ret = 0;
    for (ThubData tdata : dataThubService.queryData(tuser)) {
      ret += tdata.getValue();
    }

    return ret;
  }

  public void setDataThubService(DataThubService dataThubService) {
      this.dataThubService = dataThubService;
  }
}
  • 설명 
    • 첫번째로, setter를 만들어 인스턴스를 주입하는 방법이 있음.
    • 그럼, 다른 방법은 없을까? ^^
    • 두번째로, 생성자 주입 방식이 있음.
      • 즉, 데이터 서비스를 인수로 받아들이는 BusinessThubServiceImpl 생성자를 만드는 방법.
    • 결과
      • BusinessThubServiceImpl 클래스는 이제 DataThubService의 모든 구현체와 함께 작동할 수 있는 유연한 구조를 가지게 됨. ^^
        • 즉, BusinessThubServiceImpl 같은 구현체와 밀접하게 결합되지 않는 구조가 되었음.

결론을 내려고 하는 찰라, 여기서 한가지 의문점.

  •  DataThubServiceImple 클래스의 인스턴스를 생성하고, BusinessThubServiceImpl 클래스를 연결 해 줄 것인가?
    • 결론부터 말씀드리면,
      • 스프링 IoC 컨테이너가 존재하는 이유이다.^^; 
        • 다음 블로깅에서 스프링 IoC 컨테이너에 대해서 공부하고 블로깅을 남겨봐야겠다. ^^

결론

  • 대부분의 자바 클래스는 다른 클래스에 의존함.
  • 의존성에 대해서 잘못 코드를 짜게되면, 유연하지 못한 구조와 수정사항이 한가지 발생하면, 모든 소스코드를 수정해야 하는 개발자의 잘못된 숙명(?)에 빠지게 됨.
  • 의존성을 해결하기 위한 해결책은 인터페이스를 사용하고, 유연한 구조를 위해 인스턴스를 주입받는 방식을 차용해야함.
    • 아래 2가지 방법이 있음
      • 생성자 주입
      • setter 주입
  • 다음 블로깅에서 스프링 IoC 컨테이너에 대해서 공부하고 블로깅을 남겨봐야겠음.
  • 오늘도 자바의 의존성 관련 지식에 대한 마술(?)한가지 획득완료. 감사합니다. ^^
300x250