티스토리 뷰

728x90
반응형

DI(Dependency Injection, 의존성 주입)는 스프링 프레임워크의 네 가지 특징 중 하나이다.

 

이전 글에서 다룬 적이 있지만, 간략하게 요약하면 다음과 같다.

 

2022.08.09 - [개발/Spring] - [Spring]Spring Framework, Spring Triangle

 

[Spring]Spring Framework, Spring Triangle

Spring Framework, 혹은 Spring은 Java/Kotlin을 기반으로 한 오픈소스 웹 프레임워크이다. 특히나 엔터프라이즈급 애플리케이션 개발에 필요한 기능이 종합적으로 포함되어 있는데, 대한민국 전자정부

gnidinger.tistory.com

DI는 객체 간의 의존관계가 소스코드 외부에서 설정에 의해 정해지는 방식을 뜻한다.

 

여기서 의존이란 한 객체가 다른 객체의 메서드를 호출하는 식으로 클래스의 기능을 사용하는 것이고

 

어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는 게 아니라, 주입받아 사용하는 방법이 DI이다.

 

예를 들자면, 배터리 분리형 장난감을 만들어 배터리를 사용하는 장난감에게 배터리를 넣어주는 거라고 볼 수 있다.

 

출처: https://esoongan.tistory.com/90

 

[Spring] 의존성주입(DI)이란?

Spring프레임워크의 3가지 핵심 프로그래밍 모델중 하나로, 외부에서 두 객체간의 관계를 결정해주는 디자인패턴으로 인터페이스를 사이에 두고 클래스 레벨에서는 의존관계가 고정되지 않도록

esoongan.tistory.com

이번 글에선 의존성 주입의 종류에 대해 조금 더 세세하게 알아본다.

 

스프링에는 총 네 가지의 의존성 주입 방법이 있다.

 

  • 생성자 주입
  • 수정자 주입(setter Injection)
  • 필드 주입
  • 일반 메서드 주입

하나씩 천천히 살펴보자.

 

  • 생성자 주입

말 그대로 생성자를 통해서 의존성을 주입받는 방법이며, 가장 권장되는 방법이기도 하다.

 

생성자에 @Autowired를 붙여 컨테이너안에 @Component로 등록된 빈 중 생성자에 필요한 빈들을 주입한다.

 

만약 클래스의 생성자가 하나이고, 그 생성자로 주입받을 객체가 빈으로 등록되어있다면 @Autowired를 생략할 수 있다.

@Component
public class OrderServiceImpl implements OrderService {

    private final UserRepository userRepository;
    private final DiscountInfo discountInfo;

    @Autowired // 생략 가능
    public OrderServiceImpl(UserRepository userRepository, DiscountInfo discountInfo) {
    this.userRepository = userRepository;
    this.discountInfo = discountInfo;
  }
}

생성자 주입은 생성자의 호출 시점에 1회 호출되는 것이 보장된다(이후로 호출되는 일이 없다).

 

때문에 주입받은 객체가 변하지 않거나(final 선언 가능), 반드시 객체의 주입이 필요한 경우에 강제하기 위해 사용된다.

 

또한 프레임워크에 의존적이지 않아 POJO생성에 유리하며 테스트도 용이하다는 장점을 가지고 있다.

 

  • 수정자 주입(setter Injection)

수정자 주입은 필드 값을 변경하는 수정자(setter 메서드)를 통해 의존성을 주입하는 방법이다.

 

setter 주입은 생성자 주입과는 달리 주입받는 객체가 변경될 가능성이 있는 경우에 사용된다.

@Component
public class OrderServiceImpl implements OrderService {

    private UserRepository userRepository;
    private DiscountInfo discountInfo;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Autowired
    public void setDiscountInfo(DiscountInfo discountInfo) {
        this.discountInfo = discountInfo;
    }
}

생성자를 대신해 setter메서드를 사용한 것을 확인할 수 있다.

 

또한 setter주입은 생성자 주입과 다르게 @Autowired를 생략하는 것이 불가능하다.

 

@Autowired로 주입할 대상이 없는 경우에도 오류가 발생하는데, 

 

주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false)를 입력하면 된다.

 

  • 필드 주입

필드에 바로 @Autowired를 붙여 의존 관계를 주입하는 방식이다.

 

IntelliJ에선 필드 주입을 사용하면 Field injection is not recommended이라는 경고 문구가 발생한다.

 

코드가 간결해서 예전에 많이 사용되던 방식이지만, 외부에서 접근 및 변경이 불가능하여 테스트하기 힘들다는 단점이 있다.

 

또한 필드 주입은 반드시 DI 프레임워크가 있어야 하기 때문에, 프레임워크에 의존적이라 좋지 않다.

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private UserRepository userRepository;
    @Autowired
    private DiscountInfo discountInfo;

}

주로 실제 코드와 상관 없는 특정 테스트를 하고 싶을 때 사용한다.

 

  • 일반 메서드 주입

일반 메서드를 사용해 주입하는 방법이며, 한 번에 여러 필드를 주입받을 수 있다는 특징이 있으나 거의 사용되지 않는다.

 

  • 생성자 주입을 권장하는 이유

스프링에선 의존성 주입방법 중 생성자 주입을 권장하는데, 그 이유를 짧게 살피면 아래와 같다.

 

  1. 객체의 불변성 확보 - 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장
  2. NPE(Null Point Exception) 방지 - 주입 데이터 누락 시 컴파일 오류 발생
  3. 테스트 편의성 - 순수한 자바 코드로 단위 테스트를 작성하는 것이 용이
  4. final 키워드 사용 및 Lombok과의 결합
    final 키워드를 사용함으로써 컴파일 시점에 누락된 의존성 확인가능
    final 키워드를 사용함으로써 Lombok과 결합해 코드를 아래와 같이 간결하게 작성 가능
  5. 순환 참조 에러 방지 - 앱 구동 시점(객체의 생성 시점)에 아래와 같은 에러 발생, 순환 참조 예방 가능
@Component
@RequiredArgsConstructor // final 필드를 위한 생성자를 대신 생성
public class OrderServiceImpl implements OrderService {

    private final UserRepository userRepository;
    private final DiscountInfo discountInfo;

    public void order(String name) {
        userRepository.add(name);
    }
}

<위의 생성자 주입을 Lombok과 결합>

 

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  memberService defined in file [C:\Users\Mang\IdeaProjects\build\classes\java\main\com\mang\example\user\MemberService.class]
↑     ↓
|  userService defined in file [C:\Users\Mang\IdeaProjects\build\classes\java\main\com\mang\example\user\UserService.class]
└─────┘

<순환 참조 시 발생하는 에러>

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
글 보관함