Skip to content

Latest commit

 

History

History
97 lines (77 loc) · 5.02 KB

File metadata and controls

97 lines (77 loc) · 5.02 KB

Spring DI에 대해서 설명해주세요.

의존성이란?

  • 의존성(Dependency)이란 코드에서의 두 모듈 간의 연결을 말한다.
    • 파라미터나 리턴값 또는 지역변수 등으로 다른 객체를 참조하는 것을 말한다.
  • 객체지향에서는 두 클래스 간의 관계라고도 표현할 수 있다.
  • 두 클래스 사이에 의존 관계에서는 항상 의존 관계의 방향성을 부여해야 한다.

컴파일 의존성과 런타임 의존성

  • 컴파일 의존성이란?

    • 컴파일타임 의존성이란 코드를 컴파일하는 시점에 결정되는 의존성이며, 클래스 사이의 의존성에 해당한다.
    • 일반적으로 추상화된 클래스나 인터페이스가 아닌 구체 클래스에 의존하면 컴파일타임 의존성을 갖게된다.
  • 런타임 의존성이란?

    • 런타임 의존성이란 애플리케이션을 실행하는 시점에 결정되는 의존성이며, 객체 사이의 의존성에 해당한다.
    • 일반적으로 추상화된 클래스나 인터페이스에 의존 할 때 런타임 의존성을 갖게 된다.

의존성이 높다면?

  • 모듈간의 의존성이 높다면 변경의 파급효과가 커질 수 있다.
  • 특정 모듈을 독립적으로 떼어 내기 어려우므로, 유닛 테스트가 어렵다.

DI란?

  • DI란 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴으로, 의존성을 외부에서 생성 후 동적으로 주입하는 방법을 말한다.
  • 인터페이스를 사이에 둬서 클래스 레벨에서는 의존관계가 고정되지 않도록 한다.(클래스가 아닌 객체에 의존하도록 한다.)
    • 구체(Concrete) 클래스에 의존하는 경우 DIP를 위반하고, 결과적으로 OCP를 위반하게 되기 때문이다.
  • DI를 활용하면 런타임 의존성을 갖게 되어 결과적으로 결합도를 낮추고 유연한 코드 설계를 가능하게 한다.
  • 또한 코드 내에서 생성과 구현의 책임을 분리할 수 있다는 이점도 있다.

스프링의 DI

  • 스프링은 특정 위치부터 클래스를 탐색하고, 객체를 만들며 객체들의 관계를 설정 해준다.
  • 이러한 이유로 스프링은 DI 컨테이너라고도 불린다.

DI의 3가지 방법 - 1. setter 주입

다음 코드는 두 수를 더하는 Adder클래스로 예시를 들고자 합니다.

public class Adder {

    private int number1;
    private int number2;

    public void setNumber1(int value){
        this.number1 = value;
    }

    public void setNumber2(int value){
        this.number2 = value;
    }
}
  • setter 주입이란, 말 그대로 setter를 통해 값을 변경하는 방법이다.
  • setter 메서드의 파라미터로 변경할 값을 주입하여, 이를 업데이트한다.
  • 하지만 setter 주입을 사용할 경우 외부에서 손쉽게 내부 필드에 접근하여 값을 변경할 수 있게 되므로 권장하지 않는다.

DI의 3가지 방법 - 2. field 주입(@Autowired)

public class Adder {

    @Autowired
    private int number1;
    
    @Autowired
    private int number2;
}
  • Spring에서 필드 주입은 @Autowired 어노테이션을 통해 이루어진다.
  • @Autowired를 이용하면, 해당 어노테이션이 붙은 필드에 스프링 빈 객체를 매핑하여 자동으로 의존성 주입을 해 준다.
  • 의존 관계가 외부로 명확하게 드러나지 않는다는 단점이 있다. 즉, 스프링 컨테이너와 강하게 결합할 수 있는 소지가 생긴다.
  • final로 선언된 필드에는 @Autowired 사용이 불가능합니다.
  • 의존성 주입을 위한 DI 컨테이너가 반드시 존재해야 합니다.

DI의 3가지 방법 - 3. 생성자 주입

public class Adder {

    private int number1;
    private int number2;

    public Adder(int value1, int value2) {
	this.number1 = value1;
        this.number2 = value2;
    }
}
  • 생성자 파라미터를 통해 의존성을 주입받는 방법이며, 스프링에서 보편적으로 권장하는 방법이다.
  • 생성자 주입을 이용하면 다음과 같은 이점을 누릴 수 있다.
    • 의존관계 설정이 단 한번만 일어남을 보장하는 것이다.
      • setter 주입의 경우, 항상 변경 가능성을 내포하고 있다.
    • final로 선언된 필드에도 의존성 주입이 가능하다는 장점이 있다.
      • final 키워드는 상수 필드를 선언할 때 사용하며, 상수 필드는 선언과 함께 반드시 초기화가 함께 이루어져야 한다.
      • 만일 개발자의 실수로, 외부에서 의존성을 주입하는 코드가 누락되었다면 final로 선언된 필드의 경우 컴파일 타임에 해당 실수를 잡아낼 수 있다.
    • 순환 참조를 방지할 수 있다.
      • 필드 주입과 수정자 주입은 빈 생성 이후에 참조를 하기 때문에, 실제 호출되기 전까지는 순환 참조 여부를 알 수 없다.
      • 하지만 생성자 주입을 이용하면 BeanCurrentlyInCreationException이 발생하므로 미리 파악할 수 있게 된다.