JuBin's personal study blog

[Spring] Dependency Injection 본문

Spring

[Spring] Dependency Injection

JuBin 2024. 4. 28. 18:28
반응형

의존성 주입(Dependency Injection)이란?

SpringFramework에서 지원하는 3대 핵심 프로그래밍 모델 요소(AOP, DI, IOC) 중 하나로, 객체 간의 관계(의존성)를 직접 생성해서 사용하는것이 아니라, 외부(Spring Container(DI Container))에서 관리되는 Bean을 런타임에 동적으로 주입 시켜주는 방식 입니다.


의존성 주입이 필요한 이유

public class Store {
    private Pencil pencil;

    public Store() {
        this.pencil = new Pencil();
    }
}

문제점

  1. Store Class와 Pencil Class의 관계가 강결합 되어있다.(Pencil이 Store에 의존적이다.)
    두 Class가 강하게 결합되어 있다 보니 Pencil이 아닌 다른 Eraser로 상품을 바꾸고자 한다면 Store Class 생성자에서 변경이 필요합니다. 즉 유연성이 떨어지고, 확장성이 떨어지므로 피하는것이 좋습니다.

  2. 객체간의 관계가 아닌 클래스 관계로 엮여있다.
    Store Class와 Pencil Class는 객체간 관계가 아닌 Class간 관계로 엮여있는 문제가 있습니다. 객체지향적 설계에서는 객체끼리 관계가 맺어져야 합니다. 이를 위해 실제 구체클래스를 알지 못하도록 Interface를 통한 타입으로 객체간의 관계를 맺을 수 있습니다.

의존성 주입시 장점

  1. 객체간의 관계에서 유연성을 높임
  2. 유연성이 높다라는 말은 곧 객체간의 결합도가 낮아졌다 라는말
  3. 확장성 있는 코드 개발이 가능
  4. 테스트 용이

의존성 주입 3가지 방법

1. 생성자 주입

@Service
public class BookingService {

    private BookingRepository bookingRepository;
    private UserService userService;

    @Autowired
    public BookingService(BookingRepository bookingRepository, UserService userService) {
        this.bookingRepository = bookingRepository;
        this.userService = userService;
    }
}

생성자 주입 방식은 생성자의 호출이 1회 인것을 보장 합니다. 즉 주입받은 객체가 변하지 않거나 반드시 객체의 주입이 필요한 경우를 강제할 수 있습니다. Spring에서 권장하는 방식이기 때문에 특별한 이유가 없다면 생성자 주입으로 객체 관계를 맺어 주어야 합니다. 생성자 주입으로 DI를 해야 하는 이유에 대해서는 아래 작성 하였습니다.

 

2. Setter 주입

@Service
public class BookingService {

    private BookingRepository bookingRepository;
    private UserService userService;
    
    @Autowired
    public void setBookingRepository(BookingRepository bookingRepository) {
        this.bookingRepository = bookingRepository;
    }

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

Setter 주입은 말그대로 Setter를 통해 의존 관계를 주입하는 방법입니다. 생성자 주입과 차이점은 주입받는 객체가 변경될 가능성이 있는경우에 해당 방식으로 의존성 주입을 합니다.
@Autowired 어노테이션으로 객체 주입시, 주입할 대상이 없는 경우에는 오류가 발생합니다.

 

3. 필드 주입

@Service
public class BookingService {

    @Autowired
    private BookingRepository bookingRepository;
    
    @Autowired
    private UserService userService;

}

필드 주입 방식은 필드에 바로 의존 관계를 주입하는 방법입니다. 코드가 간결해서 과거에는 많이 사용하였지만, 테스트 코드의 중요성이 높아짐으로서 외부에서 접근이 불가능하다 라는점 때문에 사용을 지양해야 합니다. 실제 서비스코드와 무관한 테스트 코드에서, 불가피한 경우에만 사용합니다.

 

생성자 주입으로 DI를 해야하는 이유

SpringFramework 에서는 DI 방식으로 생성자 주입을 권장하고 있습니다.

 

1. 객체의 불변성 확보

실제 개발시, 의존 관계의 변경이 필요한 상황은 거의 없습니다. Setter주입을 통해 불필요하게 수정에 대한 가능성을 열어 두어 오류를 발생시킬 여지를 만드는것 보다 객체 생성시 1회에만 동작한다라는 방식에서 변경의 가능성을 만들지 않고 불변성을 보장하는것이 좋습니다.

 

2. Lombok 사용시 final 키워드와의 결합

@Service
@RequiredArgsConstructor
public class BookingService {
    private final BookingRepository bookingRepository;
    
    private final UserService userService;

}

Lombok 어노테이션인 @RequiredArgsConstructor 사용시 final 변수에 대해 생성자를 대신 생성해 줍니다.(컴파일 시점에 생성자 코드가 추가됨) 이로 인해 간편하게 생성자 주입을 할 수 있고, 컴파일 시점에 누락된 의존성을 확인할 수 있다라는 장점이 있습니다.

 

3. 테스트코드 작성에 용이

필드주입 방식과, Setter주입 방식은 순수 자바 테스트 코드 작성시 의존관계 주입이 어렵습니다. 반면 생성자 주입 방식으로 적용된 class는 컴파일 시점에 객체를 주입 받아 테스트 코드를 작성할 수 있으며, 주입하는 객체가 누락된 경우 컴파일 시점에 미리 오류를 확인할 수 있다라는 장점이 있습니다.

 

4. 순환참조 에러 방지

순환참조 에러는 각각 인스턴스가 서로를 참조할때 발송하는 오류로, 서로를 무한으로 계속 호출하여 StackOverFlow 에러를 발생시킵니다. 생성자 주입 방식은 이러한 오류를 컴파일 시점에 미리 확인이 가능하여 런타임 시점에 순환참조가 발생하는것을 방지할 수 있습니다.


의존성 주입 동작 원리

작성중(04.29)

 

 

반응형