이전 편에서는 다양한 방식으로 의존성을 주입하는 법에 대해 살펴봤다. 이번 편부터는 스프링이 제공하는 의존관계 자동 주입의 모든 방식을 정리하고, 실제 현업에서는 어떤 방식이 가장 효과적인지 비교 분석해본다.
생성자 주입 - 가장 권장되는 방식
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
- 생성 시점에 한 번만 호출되므로 불변성을 보장할 수 있다.
- final 키워드와 함께 사용하면 필수 의존성임을 명확히 표현할 수 있다.
- 생성자가 하나라면 @Autowired는 생략 가능하다.
- 테스트 코드에서 명시적으로 주입하기 용이하다.
수정자(setter) 주입 - 선택적 의존성에 사용
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
- 선택적 의존성, 혹은 변경 가능한 의존성 주입에 사용한다.
- @Autowired(required = false) 옵션을 사용하면 빈이 없어도 예외가 발생하지 않음.
- 단점: 객체가 완전히 초기화되지 않은 상태로 사용될 수 있으며, 불변성이 깨짐.
필드 주입 - 테스트 불가능, 사용 금지
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
- 가장 간단해 보이지만 절대 권장되지 않는 방식
- 외부에서 의존성을 변경하거나 주입할 수 없어 테스트 작성이 거의 불가능
- DI 프레임워크에 강하게 결합됨 → 스프링이 아니면 작동하지 않음
- 결국 테스트하려면 setter 만들어야 해서 본말전도
일반 메서드 주입
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
- 일반 메서드에 @Autowired를 붙이면 의존성이 주입됨
- setter 주입과 거의 유사한 방식
- 거의 사용되지 않음, 필요 시 한 번에 여러 의존성을 넣고 싶은 경우만
자동 주입이 되기 위한 조건
- @Autowired는 스프링 컨테이너가 관리하는 빈이어야만 동작함
- new 키워드로 생성한 객체에는 아무런 주입이 일어나지 않음
- 외부 객체나 빈이 아닌 객체에는 작동하지 않음
옵션 처리 - 빈이 없을 수도 있을 때
@Autowired(required = false)
public void setNoBean1(Member noBean1) {
System.out.println("noBean1 = " + noBean1);
}
@Autowired
public void setNoBean2(@Nullable Member noBean2) {
System.out.println("noBean2 = " + noBean2);
}
@Autowired
public void setNoBean3(Optional<Member> noBean3) {
System.out.println("noBean3 = " + noBean3);
}
- required = false: 아예 메서드 호출 안 됨
- @Nullable: null 값으로 호출
- Optional: Optional.empty로 주입됨
설계 원칙 요약
주입 방식 | 특징 | 사용 시점 |
생성자 주입 | 불변 + 필수 | 가장 권장, 기본값 |
수정자 주입 | 선택적 + 변경 가능 | 조건부 의존성 |
필드 주입 | 외부 주입 불가, 테스트 불가 | 사용 금지 |
일반 메서드 주입 | 한 번에 여러 의존성 주입 가능 | 거의 사용하지 않음 |
'Spring' 카테고리의 다른 글
[Spring 완전 정복 시리즈] 20편 - 빈 생명주기 콜백과 초기화 전략 정리 (1) | 2025.07.31 |
---|---|
[Spring 완전 정복 시리즈] 19편 - 자동 주입 고급 기능과 전략 패턴 적용법 (3) | 2025.07.30 |
[Spring 완전 정복 시리즈] 17편 - 다양한 의존관계 자동 주입 방식 (1) | 2025.07.29 |
[Spring 완전 정복 시리즈] 16편 - 컴포넌트 스캔 심화 (0) | 2025.07.29 |
[Spring 완전 정복 시리즈] 15편 - 컴포넌트 스캔의 시작 (0) | 2025.07.29 |