개발/Spring
[Spring] 스프링 빈 주입 방법
함수형 인간
2025. 3. 13. 08:55
Spring에서 빈을 주입하는 방법은 필드 주입, Setter 주입, 생성자 주입 세가지가 있다.
1. 필드 주입 (Field Injection)
- 방법: @Autowired 어노테이션을 필드(멤버 변수)에 직접 붙인다.
Java
@Component
public class MyService {
@Autowired
private MyRepository myRepository; // 필드 주입
// ...
}
장점:
- 코드가 가장 간결.
단점:
- 강한 결합: MyService 클래스는 MyRepository 없이는 테스트하기 어렵다. (리플렉션을 사용하지 않고는 외부에서 myRepository 필드에 다른 객체를 주입할 수 없음)
- 순환 참조 (Circular Dependency) 문제 발생 가능성: 필드 주입은 생성자 주입에 비해 순환 참조 문제를 발견하기 어렵다. 순환 참조는 애플리케이션 시작 시 오류를 발생시킬 수 있다.
- 불변성(Immutability) 보장 불가: 필드는 final로 선언할 수 없으므로, 주입된 빈이 변경될 가능성이 있다.
- DI 컨테이너 외부에서 사용 불가: @Autowired는 스프링 컨테이너 내부에서만 동작하기 때문에, 스프링 없이 단위 테스트를 할 때는 수동으로 의존성을 주입해야 한다.
- 언제 사용?
- 과거에는 많이 사용되었지만, 최근에는 권장되지 않는 방식.
- 빠르게 프로토타입을 만들 때, 또는 테스트 코드에서 간편하게 의존성을 주입할 때 사용할 수 있다.
2. Setter 주입 (Setter Injection)
- 방법: @Autowired 어노테이션을 Setter 메서드에 붙인다.
Java
@Component
public class MyService {
private MyRepository myRepository;
@Autowired // Setter 주입
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository;
}
// ...
}
Lombok 사용예시 :
- @Setter(onMethod_ = {@Autowired}): Lombok이 @Autowired 어노테이션이 붙은 Setter 메서드를 생성.
- onMethod_는 Lombok 1.18.16 이상에서 사용 가능. 이전 버전에서는 @Setter @Autowired를 필드에 함께 사용.
Java
@Autowired
public void setMyRepository(MyRepository myRepository){
this.myRepository = myRepository;
}
Java
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyService {
@Setter(onMethod_ = {@Autowired}) // Lombok 사용
private MyRepository myRepository;
}
장점:
- 선택적 의존성(Optional Dependency)을 표현하기 좋다. (Setter 메서드가 없어도 객체 생성 가능)
- 주입받을 빈이 변경될 수 있는 경우(runtime에 동적으로 변경)에 유용.
단점:
- 필드 주입과 마찬가지로 강한 결합, 순환 참조 문제 가능성, 불변성 보장 불가.
- Setter 메서드가 public으로 열려 있어야 하므로, 외부에서 의도치 않게 의존성을 변경할 수 있다.
- 의존성이 선택적이거나, 런타임에 변경될 가능성이 있는 경우에 제한적으로 사용.
3. 생성자 주입 (Constructor Injection)
- 방법: 생성자의 파라미터로 의존성을 주입받고, @Autowired 어노테이션을 생성자에 붙인다. (Spring 4.3부터는 생성자가 하나만 있는 경우 @Autowired 생략 가능)
Java
@Component
public class MyService {
private final MyRepository myRepository; // final로 선언
// @Autowired (생성자가 하나뿐이므로 생략 가능)
public MyService(MyRepository myRepository) { // 생성자 주입
this.myRepository = myRepository;
}
// ...
}
- Lombok 사용 예시: @RequiredArgsConstructor를 사용하면 final 필드에 대한 생성자를 자동으로 생성하여 코드를 매우 간결하게 만들 수 있다.
- @RequiredArgsConstructor: Lombok이 final 필드(myRepository)에 대한 생성자를 자동으로 생성.
- 생성자가 하나만 있는 경우에는 @Autowired를 생략해도 되므로, 코드가 더욱 깔끔.
Java
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor // Lombok 사용 (권장)
public class MyService {
private final MyRepository myRepository; // final 필드
// 생성자 코드를 직접 작성할 필요 없음!
}
장점:
- 테스트 용이성: 생성자를 통해 의존성을 주입받으므로, Mock 객체나 다른 구현체를 쉽게 주입하여 단위 테스트를 수행할 수 있다.
- 순환 참조 방지: 생성자 주입 시 순환 참조가 발생하면 애플리케이션 시작 시점에 명확한 에러가 발생하여 문제를 빠르게 파악할 수 있다.
- 불변성 보장: 주입받을 필드를 final로 선언하여 불변성을 보장할 수 있다. (객체 생성 시점에 의존성이 설정되고, 이후 변경 불가)
- 필수 의존성 명시: 생성자 파라미터를 통해 어떤 의존성이 필요한지 명확하게 알 수 있다.
- NullPointerException 방지: 생성자에서 null 체크를 할 수 있어 NPE 방지에 유리.
단점:
- 의존성이 많아지면 생성자 파라미터가 길어질 수 있다.
4. 정리 및 결론
필드 주입 | 코드 간결 | 강한 결합, 테스트 어려움, 순환 참조 문제, 불변성 보장 불가 | X |
Setter 주입 | 선택적 의존성, 런타임 의존성 변경 가능 | 강한 결합, 테스트 어려움, 순환 참조 문제, 불변성 보장 불가, 외부에서 의존성 변경 가능 | △ |
생성자 주입 | 테스트 용이, 순환 참조 방지, 불변성 보장, 필수 의존성 명시, NullPointerException 방지 |
의존성이 많아지면 생성자 파라미터가 길어짐 (하지만 이는 클래스 설계 문제의 신호일 수 있음) | O (권장) |
가능하면 항상 생성자 주입을 사용하고, 선택적 의존성이나 런타임에 의존성을 변경해야 하는 특별한 경우에만 Setter 주입을 고려.
필드 주입은 최대한 피하는 것이 좋다.