티스토리 뷰

DI(=Dependency Injection)

DI란 의존 관계 주입 또는 의존성 주입이라 불립니다. Spring의 3가지 핵심 프로그래밍 모델(AOP, DI, IoC) 중 하나 입니다.

 

DI는 외부에서 객체 간의 관계(의존성)을 결정해 주는데 즉, 객체를 직접 생성하는 것이 아니라 외부에서 생성 후 주입시켜 주는 방식이라고 할 수 있습니다.

 

이를 통해서 객체 간의 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있습니다. 

그림으로 보는 DI


Spring에서의 DI 방법

Spring에서 의존 관계를 주입(DI)하는 방법에는 3가지가 있습니다.

Spring DI 3가지 방법

  • Construct Injection (생성자 주입)
  • Field Injection (필드 주입)
  • Setter Injection (Setter 주입)

Spring 3.x 버전까지만 해도 Setter Injection을 권장하였으나, 최근에는 순환 참조 등의 문제로 인하여 Spring 4.3 이후 버전부터는 Constructor Injection 방법을 권장하고 있습니다.

 

 

Constructor Injection (생성자 주입)

  • 현재 가장 권장되는 의존 관계 주입 방식
    • Spring 공식 문서에서도 해당 방식을 권장하고 있습니다.
    • 하나의 생성자가 존재할 때 필드 주입의 단점 대부분을 극복합니다.
    • Spring 4.3 부터 @Autowired가 생략 가능해져 최근에는 생성자를 딱 1개 두고, @Autowired를 생략하는 방법을 주로 사용합니다.
    • 추가로 Lombok 라이브러리의 @RequiredArgsConstructor를 함께 사용하면 생성자를 생략할 수 있어 코드가 깔끔해집니다.
  • 오직 생성자 주입만이 final 키워드를 사용할 수 있고, 생성자를 통해 주입되는 방식입니다.
    • final 키워드를 사용하기 때문에 생성자로 인해 인스턴스가 생성될 때 1번만 할당됩니다.
  • final 키워드를 사용하여 값이 한번만 할당되기 때문에 객체의 불변성이 보장됩니다.
  • 초기에 할당되기 때문에 NPE(Null Pointer Exception)이 절대 발생하지 않습니다.
public class Injection {
    private InjectionService injectionService;
    
    // 생성자 DI
    // @Autowired -> Spring4.3부터는 @Autowired 생략 가능
    public Injection(InjectionService injectionService) {
    	this.injectionService = injectionService;
    }
}

 

위와 같은 코드를 Lombok 라이브러리를 사용하면 생성자 역시 어노테이션으로 생략할 수 있습니다.

@RequiredArgsConstructor
public class Injection {
    private InjectionService injectionService;
    
}

 

@RequiredArgsConstructor는 Lombok의 어노테이션 중 하나로 final 키워드가 붙은 주입에만 필드에만 생성자를 만들어줍니다. 만약 final 키워드를 사용하지 않을 시 @AllArgsConstructor 어노테이션을 사용하면 됩니다.

 

 

 

 

Field Injection (필드 주입)

  • 생성자 주입 방법 중 가장 간단한 방법으로 Bean으로 등록된 객체를 사용하고자 하는 클래스에 Field로 선언한 뒤 @Autowired를 붙여주면 자동으로 의존 관계가 주입됩니다.
  • Spring에서 @Autowired 어노테이션을 통해 객체 내 필드에 선언하여 주입하는 방법
  • 간편하게 의존 관계 주입이 가능하지만, 참조 관계를 눈으로 확인하기 어려우며, 순환 참조를 막을 수 없습니다.
    • 예를 들어 A가 B를 가지고 있고, B가 A를 가지고 있어(순환 참조) 실행 전까지 Error를 잡을 수 없습니다.
  • Constructor Injection 이외에는 모두 생성자 이후에 호출되므로, 필드에 final 키워드를 사용할 수 없습니다.
public class Injection {
	@Autowired
    private InjectionService injectionService;
}

 

필드 주입의 장점

  • 코드가 간결해집니다.

필드 주입의 단점

  • 단일 책임 원칙을 위배합니다.
  • Unit Test(단위 테스트) 가 어렵습니다.
  • final 키워드를 사용할 수 없습니다.
    • 불변성이 보장되지 않기 때문에, 객체가 변할 수 있습니다.

 

 

 

 

Setter Injection (Setter 주입)

  • Spring에서 @Autowired 어노테이션을 사용해서 Setter 메서드를 통해 주입하는 방법
    • @Autowired는 Field, Setter Method, Constructor에 사용 가능합니다.
  • NPE가 발생할 수 있습니다.
  • Constructor Injection 이외에는 모두 생성자 이후에 호출되므로, 필드에 final 키워드를 사용할 수 없습니다.
public class Injection {
    private InjectionService injectionService;
    
    @Autowired
    public void setInjectionService( InjectionService injectionService) {
    	this.injectionService = injectionService;
    }
}

생성자 주입을 지향합시다.

위에서도 언급 하였듯이 Spring 3.x 버전까지는 권장하는 DI 방식이 Setter 주입, 혹은 필드 주입을 사용하였습니다.

하지만, 스프링 공식 문서에서도 언급하듯이 Spring 4.3부터는 생성자 주입 방식을 사용하는 것이 좋습니다.

 

생성자 주입의 장점은 여러 가지가 있지만, 프레임 워크에 의존하지 않고 순수 Java 언어의 특징을 잘 살리는 방법이기도 하다.

 

가끔 옵션이 필요한 경우 Setter 주입을 사용하고, 절대 필드 주입은 사용하지 않는 것이 좋습니다.

공지사항
최근에 올라온 글
Total
Today
Yesterday