백엔드 면접 용어 사전
Spring·JPA의존성 주입

DI

필요한 협력 객체를 외부에서 넣어 주는 방식. 생성자 주입이 권장된다(불변·순환 조기 발견).

객체가 필요로 하는 의존 대상을 외부에서 넣어 주는 것(Dependency Injection).

세 가지 방법

① 생성자 주입 — 권장

@Service
public class OrderService {
    private final PaymentClient client;      // final 가능

    public OrderService(PaymentClient client) {   // @Autowired 생략 가능
        this.client = client;
    }
}

② 필드 주입 — 지양

@Autowired private PaymentClient client;

③ 수정자(setter) 주입 — 선택적 의존에만

@Autowired public void setClient(PaymentClient c) { this.client = c; }

왜 생성자 주입인가 — 이유가 네 개

① 불변으로 만들 수 있다

private final PaymentClient client;   // 생성자 주입에서만 final 가능

주입 후 바뀌지 않음이 보장된다. 필드 주입은 final을 못 붙인다.

② 순환 참조를 기동 시점에 잡는다

  • A가 B를 필요로 하고, B가 A를 필요로 한다
  • 생성자 주입: 애플리케이션이 아예 기동하지 않는다 (즉시 발견)
  • 필드 주입: 기동은 되고, 나중에 런타임에 이상 동작 ← 훨씬 나쁘다 "빨리 실패하는 것이 낫다" 는 원칙이 적용된 예다.

③ 테스트가 쉽다

// 생성자 주입 — 그냥 new 하면 된다
var service = new OrderService(new FakePaymentClient());

// 필드 주입 — 스프링 컨테이너를 띄우거나 리플렉션을 써야 한다
ReflectionTestUtils.setField(service, "client", fake);   // 지저분하다

④ 의존성이 많아지면 눈에 보인다

public OrderService(A a, B b, C c, D d, E e, F f, G g) { }
// 생성자가 흉해진다 → "이 클래스가 너무 많은 일을 하는구나" 라는 신호

필드 주입은 어노테이션을 하나 더 붙이면 그만이라 비대해지는 것을 눈치채기 어렵다. 생성자의 불편함이 설계 냄새를 드러내는 장치로 작동한다.

스프링 4.3+ — @Autowired 생략

생성자가 하나뿐이면 @Autowired를 붙이지 않아도 자동 주입된다. Lombok과 함께 쓰면 더 간결해진다.

@Service
@RequiredArgsConstructor          // final 필드로 생성자를 만들어 준다
public class OrderService {
    private final PaymentClient client;
    private final OrderRepository repository;
}

같은 타입의 빈이 여럿일 때

public interface PaymentClient { }
@Component class TossClient implements PaymentClient { }
@Component class KakaoClient implements PaymentClient { }

// → NoUniqueBeanDefinitionException

해결 방법

@Primary                          // ① 기본으로 쓸 것을 지정
@Component class TossClient@Qualifier("kakaoClient")         // ② 이름으로 지정
OrderService(PaymentClient client)

// ③ 전부 주입받아 직접 고른다 — 전략 패턴에 유용
OrderService(Map<String, PaymentClient> clients)
OrderService(List<PaymentClient> clients)

③은 결제 수단이 늘어날 때 기존 코드를 안 고쳐도 되는 구조를 만들 수 있어 실무에서 유용하다.

순환 참조가 생겼다면

스프링 2.6부터는 기본적으로 기동이 실패한다. spring.main.allow-circular-references=true 로 켤 수 있지만, 그것은 문제를 미루는 것이다.

대개는 설계 문제의 신호다.

  • A ⇄ B
  • 공통 로직을 C로 추출해 A→C, B→C 로 바꾼다
  • 또는 이벤트(ApplicationEventPublisher)로 방향을 끊는다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — Spring·JPA — IoC/DI·AOP·영속성 컨텍스트·N+1