1️⃣ 의존성주입 (DI : Dependency Injection) 방법
의존성 주입에는 3가지 방법이 존재한다.
- 필드 주입
- 수정자 주입 (setter 주입)
- 생성자 주입
💚 필드 주입
필드에 @Autowired를 붙여서 바로 주입하는 방법이다.
필드 주입의 특징
- 코드가 간결해진다.
- 단, 외부에서 변경이 불가능하여 테스트하기 어려운 단점이 있다.
@Component
public class CoffeeService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private CoffeeRepository coffeeRepository;
}
필드 주입을 사용하지 않는 이유
- DI 프레임워크가 없으면 아무것도 할 수 없다.
- 테스트 코드의 중요성이 부각됨에 따라 필드의 객체를 수정할 수 없는 필드 주입은 거의 사용되지 않게 되었다.
- 애플리케이션의 실제 코드와 무관한 테스트 코드나 설정을 위해 불가피한 경우에만 이용
💚 수정자 주입 (setter 주입)
setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존 관계를 주입하는 방법
실행되는 클래스를 스프링 빈으로 등록하고 의존관계를 주입하게 된다.
@Autowired가 있는 수정자들을 자동으로 의존관계를 주입한다.
수정자 주입 (setter 주입) 의 특징
- 선택과 변경 가능성이 있는 의존 관계에 사용한다.
- 자바 빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다.
- set필드명 메서드를 생성하여 의존 관계를 주입한다.
- @Autowired를 입력하지 않으면 실행이 되지 않는다.
@Component
public class CoffeeService {
private MemberRepository memberRepository;
private CoffeeRepository coffeeRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setCoffeeRepository(CoffeeRepository coffeeRepository) {
this.coffeeRepository = coffeeRepository;
}
}
수정자 주입을 사용하지 않는 이유
- 수정자 주입을 통해서 Service의 구현체를 주입해주지 않아도 Controller 객체는 생성이 가능
- Controller 객체가 생성가능하다는 것은 내부에 있는 Service의 method 호출이 가능하다는 것인데, set을 통해 Service의 구현체를 주입해주지 않았으므로, NullPointerException 이 발생
- 주입이 필요한 객체가 주입이 되지 않아도 얼마든지 객체를 생성할 수 있다는 것이 문제
💚 생성자 주입
현재 스프링 프레임워크에서 가장 권장되는 방식임
생성자 주입은 생성자를 통해 의존 관계를 주입받음
생성자 주입의 특징
- 생성자 호출 시점에 1번만 호출되는 것을 보장한다.
- 불변과 필수 의존 관계에 사용한다.
- 생성자가 1개만 존재하는 경우 @Autowired를 생략해도 자동 주입된다.
- NPE(NullPointerException)를 방지할 수 있다.
- 주입받을 필드를 final로 선언 가능하다.
@RequiredArgsConstructor 어노테이션
- 생성자 주입의 단점은 생성자를 만들기 번거롭다는 것!
→ lombok(롬복)의 @RequiredArgsConstructor 어노테이션을 사용하면 간단한 방법으로 생성자 주입을 해줄 수 있다. - @RequiredArgsConstructor은 final 혹은 @NotNull이 붙은 필드의 생성자를 자동으로 만들어준다
- 이를 통해 새로운 필드를 추가할 때 다시 생성자를 만들거나 하는 등의 번거로움을 없앨 수 있음
@RequiredArgsConstructor 를 사용하지 않은 생성자 주입
@Component
public class CoffeeService {
private final MemberRepository memberRepository;
private final CoffeeRepository coffeeRepository;
@Autowired
public CoffeeServiceImpl(MemberRepository memberRepository, CoffeeRepository coffeeRepository) {
this.memberRepository = memberRepository;
this.coffeeRepository = coffeeRepository;
}
}
@RequiredArgsConstructor 를 사용한 생성자 주입
@Component
@RequiredArgsConstructor
public class CoffeeService {
private final MemberRepository memberRepository;
private final CoffeeRepository coffeeRepository;
}
생성자를 따로 작성하지 않아도 괜찮음!
💚 생성자 주입을 사용해야 하는 이유
객체 불변성 확보
- final 키워드 사용 가능, 따라서 생성자에서 값이 설정되지 않으면 컴파일 시점에서 오류 확인 가능
(생성자 주입외에 나머지 방법은 final 키워드 사용이 불가능함) - 의존 관계 주입은 처음 애플리케이션이 실행될 때 대부분 정해짐 + 종료 전까지 변경되면 안됨.
애초에 변경 가능성이 있으면 좋지 않음. 변경하면 안 되는 메서드가 변경할 수 있게 설계하는 것 자체가 위험 - 따라서 수정자 주입(setter 주입)의 경우 메서드를 public으로 열어두어 변경이 가능하기 때문에 적합하지 않음
- 생성자 주입은 객체를 생성할 때 최초로 1번만 호출된 이후 호출될 일이 없기 때문에 불변하게 설계가능
테스트 용이
- 생성자 주입을 사용하면 단독으로 실행할 때도 주입 데이터 누락 시 컴파일 오류가 발생
- 의존 관계 주입이 누락(null)되면 호출했을 때 NPE(NullPointException)가 발생
- 따라서 누락이 발생하더라도 실행되는 일 없음
순환 참조 방지
- 순환참조란 A객체는 B객체를 참조하고, B객체는 A객체를 서로 동시에 참조하고 있을때 발생
- 개발을 하다 보면 여러 컴포넌트 간에 의존성이 생기게 된다.
- 필드 주입과 수정자 주입(setter 주입)은 빈이 생성된 후에 참조함 -> 서로 메서드를 계속해서 호출 -> 시스템 다운
→ 이게 컴파일 시에는 아무런 에러가 없다가 메서드 호출시에 발생함 - 하지만, 생성자를 통해 의존관계를 주입하게 되면 BeanCurrentlyInCreationException이 발생하게 되어 문제를 알 수 있다.
💻 reference
'Server > Spring Boot' 카테고리의 다른 글
[Spring] DAO, Entity, DTO, VO의 개념과 비교 (0) | 2023.09.25 |
---|---|
[Spring Boot] 빌더(Builder) 패턴의 정의, 생성자 패턴 빌더 패턴 비교 (0) | 2023.09.25 |
[Chapter 03] 스프링 부트에서 JPA로 데이터베이스 다뤄보자 (1) - 등록 (0) | 2023.09.24 |
[Chapter 02] 스프링 부트에서 테스트 코드를 작성하자 (0) | 2023.09.16 |
[Chapter 01] 인텔리제이로 스프링 부트 시작하기 (0) | 2023.09.15 |