스프링 DI: 생존자 주입을 권장하는 이유

2025. 5. 5. 23:58·SpringBoot

의존성 주입 방식 4가지 — 어떤 차이가 있을까?

스프링에서 객체 간 의존성을 주입하는 방법은 크게 4가지가 있다.

구분 설명
생성자 주입 생성자를 통해 필요한 객체를 주입
필드 주입 클래스 내부 필드에 직접 주입
수정자(Setter) 주입 세터 메서드를 통해 주입
일반 메서드 주입 일반 메서드를 통해 주입

1. 생성자 주입 — 가장 권장되는 방식

@Component
public class OrderService {
    private final MemberService memberService;

    public OrderService(MemberService memberService) {
        this.memberService = memberService;
    }
}
  • 객체를 생성할 때 필수 의존성을 한 번에 받아서 주입
  • 필드를 final로 선언할 수 있어 불변 객체로 만들 수 있다
  • 생성자가 1개일 경우 @Autowired 생략 가능

장점

  • 불변성 보장
  • 테스트 용이 (스프링 없이도 생성 가능)
  • 컴파일 또는 실행 시점에 의존성 누락 감지
  • 순환 참조 발생 시 즉시 오류 발생 (fail-fast)

2. 필드 주입 — 사용은 가능하지만 권장되지 않음

@Component
public class OrderService {
    @Autowired
    private MemberService memberService;
}
  • @Autowired를 필드에 붙여 객체를 직접 주입
  • 코드가 짧아보이지만, 스프링 없이는 사용할 수 없다

단점

  • 테스트에서 직접 주입 불가 → 테스트 코드 작성 어려움
  • 의존성 누락 여부를 코드만 보고 파악하기 힘듦
  • 순환 참조가 감춰져 실행 중 문제가 생길 수 있음

실무에선 거의 사용하지 않거나, 아주 제한적으로만 사용


3. 수정자(Setter) 주입 — 선택적 의존성에 적합

@Component
public class OrderService {
    private MemberService memberService;

    @Autowired
    public void setMemberService(MemberService memberService) {
        this.memberService = memberService;
    }
}
  • 객체 생성 이후, 세터 메서드를 통해 의존성 주입
  • 선택적으로 필요한 경우에 유용 (있어도 되고 없어도 되는 의존성)

장점

  • 의존성 변경이 가능 (동적으로 바꿀 수 있음)
  • 테스트 코드에서 일부만 주입 가능

단점

  • 불변성 보장 불가
  • 누가 언제 의존성을 넣었는지 알기 어려움
  • 순환 참조 시 필드 주입과 동일하게 지연되어 늦게 에러 발생

4. 일반 메서드 주입 — 거의 쓰지 않음

@Component
public class OrderService {
    private MemberService memberService;

    @Autowired
    public void init(MemberService memberService) {
        this.memberService = memberService;
    }
}
  • 세터 주입과 유사하지만, 이름이 꼭 setXxx()일 필요는 없음
  • 특정 라이프사이클 타이밍에만 쓰이는 예외적 주입 방식

사용 예

  • 여러 의존성을 한 번에 주입해야 할 때
  • 명확하게 "초기화 단계"임을 드러내고 싶을 때

일반 개발에서는 거의 사용되지 않음. 특수한 상황에만 사용


왜 생성자 주입을 써야 할까?

생성자 주입은 위 4가지 방식 중에서도 다음 이유로 가장 우수하다.


1. 불변성 보장

  • 주입된 객체는 final로 선언할 수 있다.
  • 실행 도중 실수로 의존성이 바뀌는 일을 방지한다.

2. 의존성 누락 방지

  • 생성자에 없으면 객체 생성이 불가능
  • 필수 의존성이 빠지면 컴파일 또는 실행 시 바로 오류가 발생

3. 테스트 용이

OrderService service = new OrderService(mockMemberService);
  • 스프링 없이도 객체를 생성해 테스트할 수 있다.
  • Mockito와 함께 단위 테스트를 쉽게 작성 가능

4. 순환 참조 탐지 가능

순환 참조란?

@Component
public class A {
    public A(B b) {}
}

@Component
public class B {
    public B(A a) {}
}
  • A는 B가 필요하고, B는 다시 A가 필요
  • 이 구조는 무한 루프처럼 작동해 앱 실행을 막는다

생성자 주입 vs 필드/세터 주입

주입 방식 순환 참조 발생 시 결과
생성자 주입 즉시 에러 발생 빠르게 감지 가능 (fail-fast)
필드/세터 주입 실행됨 → 나중에 오류 문제 원인 파악 어려움, 위험

5. 명확한 설계

  • 생성자를 보면 이 클래스가 무엇을 필요로 하는지 한눈에 보인다
  • 유지보수 및 협업 시 구조 이해가 쉬워진다

생성자 주입은 스프링 개발에서의 표준이라고 봐도 무방하다.
처음부터 생성자 주입을 쓰면 구조가 단단해지고, 나중에 유지보수도 훨씬 쉬워진다.

'SpringBoot' 카테고리의 다른 글

웹 애플리케이션의 이해  (0) 2025.05.13
RequestScope로 요청별 로그 분리하기  (0) 2025.05.07
스프링 빈 자동 등록과 의존성 주입  (1) 2025.05.01
싱글톤 패턴과 스프링의 싱글톤 관리  (0) 2025.04.30
Spring Boot: 데이터 검증(Validation)  (1) 2024.11.22
'SpringBoot' 카테고리의 다른 글
  • 웹 애플리케이션의 이해
  • RequestScope로 요청별 로그 분리하기
  • 스프링 빈 자동 등록과 의존성 주입
  • 싱글톤 패턴과 스프링의 싱글톤 관리
moodone
moodone
  • moodone
    무던하게
    moodone
  • 전체
    오늘
    어제
    • 분류 전체보기 (36)
      • Java (7)
      • SpringBoot (24)
      • JavaScript (0)
      • Database (1)
      • Python (0)
      • Git (1)
      • IDE (0)
      • 기타 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    티스토리챌린지
    git
    git bash
    오블완
    Repository
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
moodone
스프링 DI: 생존자 주입을 권장하는 이유
상단으로

티스토리툴바