스프링 빈 주입과 필드 주입에 대한 정리

박의진·2025년 11월 27일

필드 주입 (@Value, @AutoWired)

스프링이 객체 생성 후 리플렉션으로 필드에 값을 넣는 방식

빈 주입(생성자 주입 @Autowired / @RequiredArgsConstructor)

객체를 만들기 전에 생성자 파라미터로 의존성을 넣어주는 방식

🔍 1. "빈 주입" = 생성자 주입


@Component
@RequiredArgsConstructor
public class MyService {

    private final UserRepository userRepository;
}

/*lombok이 만든 생성자 */
public MyService(UserRepository userRepository) {
    this.userRepository = userRepository;
}

스프링은 해당 코드를 보고 아래의 step으로 생각한다.
-> Myservice를 생성하려면 UserRepository가 필요함
-> 그럼 UserRepository 빈을 찾아서 생성자 파라미터에 넣어야함

즉, 스프링이 객체 생성 시점에 빈을 넣어주는 방식
-> 그래서 final 필드도 가능하고
-> 의존성 없으면 그자리에서 에러가 발생
-> 타입이 일치하는 빈을 스프링 컨테이너에서 찾음

🔍 2. "필드 주입" = 인스턴스 생성 후 필드에 값 채워넣기

@Component
public class MyUtil {

    @Value("${my.prop}")
    private String prop;
}

해당 코드는 생성자가 아니라 객체 생성 후에 리플렉션으로 값을 넣는 방식이다.

여기서 스프링 컨테이너 흐름 정리를 하자면
1. 객체 인스턴스 생성(기본 생성자를 호출)
2. 스프링이 리플렉션으로 필드를 찾음
3. 그 위에 @Value가 붙어있으면 yml값을 꽂아 넣어줌

즉, 생성 -> 주입 순서이다.

두 방식에서 중요한 차이는 ?

  1. 필드주입은 final필드에 값 넣을 수가 없다. (이미 객체가 생성된 후이기 떄문에 final은 변경이 불가함)
  2. @RequiredArgsConstructor가 final필드를 생성자 파라미터로 만들어버리면
    1) 스프링은 string 빈을 주입해야한다고 판단
    2) 하지만 String 빈이 없어서 에러 발생

여기서! 헷갈리는 실수

🧨 왜 @Value + final + @RequiredArgsConstructor 조합이 터지는가?

@Component
@RequiredArgsConstructor
public class MyClass {

    @Value("${my.prop}")
    private final String prop;
}

/*그러면 롬복은 이렇게 생성자를 만든다.*/
public MyClass(String prop) {
    this.prop = prop;
}

그런데 여기서 @Value로 yml의 값을 넣고자 한다면?
스프링은 @Value가 붙어 있는 것을 신경쓰지 않음 -> 즉, 스프링 입장에서는 클래스 객체 생성하려면 String type의 빈이 필요한데 스프링 컨테이너에 String 빈이 없네? -> 에러 터짐

정리하자면 @Value 가 적용되기 전에 @RequiredArgsConstructor가 먼저 적용되어서 필드가 생성자 주입 대상이 되어버린 것이다.

🎯 최종 정리

주입 방식시점final 가능?스프링이 뭘 찾나?
생성자 주입(빈 주입)객체 생성 ✔ 가능스프링 빈
필드 주입(@Value, field @Autowired)객체 생성 ❌ 불가능@Value → yml 값
@Autowired → 빈

🎯 완전 요약

@RequiredArgsConstructor
→ final 필드를 생성자 주입 대상으로 만듦
→ 스프링은 “해당 타입의 빈이 필요하다”고 생각함
@Value
→ 생성자 호출 후 리플렉션으로 주입
→ final 필드에는 값 못 넣음
두 개를 같이 쓰면
→ 스프링이 yml 값이 아니라 “String 빈”을 요구
→ 당연히 없음 → 에러!

profile
주니어 백엔드 개발자의 개발 log💻

0개의 댓글