개발/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 주입을 고려.

필드 주입은 최대한 피하는 것이 좋다.