노트

의존성 주입 (DI)

Dependency Injection

백엔드#spring · 연결된 개념 16개

쉽게 말하면

의존성 주입은 배우가 소품을 직접 만들지 않고, 무대 스태프가 필요한 소품을 손에 쥐여 주는 방식이에요. 배우는 연기에만 집중하고, 리허설 땐 가짜 소품으로 바꿔 쥐여 주기도 쉽죠.

비유가 깨지는 곳 스태프는 소품을 이름이 아니라 종류(타입)로 찾아 줘서, 같은 타입 빈이 둘이면 @Qualifier나 @Primary로 골라 줘야 해요. 서로를 먼저 달라는 순환 참조면 시작부터 실패해요.

객체가 필요한 협력 객체를 직접 new로 만들지 않고, 바깥(스프링 컨테이너)에서 넣어 받는 방식. 무엇을 언제 만들고 어떻게 연결할지의 제어가 내 코드에서 컨테이너로 넘어가므로 제어의 역전(Inversion of Control, IoC)이라고도 부른다.

  • 컨테이너가 만들고 관리하는 객체가 빈이다
  • 주입 방법은 생성자·세터·필드가 있고, 생성자 주입을 권장한다. 필드를 final로 둘 수 있고 테스트에서 직접 넣기 쉽다
  • 컨테이너는 원본 대신 프록시를 주입하기도 한다. @Transactional이 동작하는 이유다
  • 프런트 비유: React Context로 의존성을 내려받는 것과 닮았지만, 연결을 컨테이너가 타입으로 찾아 준다는 점이 다르다

주입 방식

@Service
public class OrderService {
    private final PaymentClient payments;          // final 가능
    public OrderService(PaymentClient payments) {  // 생성자가 하나면 @Autowired 생략 가능
        this.payments = payments;
    }
}
  • 생성자 주입: 의존성이 빠지면 객체를 만들 수조차 없어 누락이 바로 드러난다. 불변으로 둘 수 있고, 테스트에서 new로 가짜를 넣기 쉽다. @RequiredArgsConstructor와 함께 쓰면 코드가 짧다
  • 세터 주입: 선택적 의존성에만 쓴다
  • 필드 주입(@Autowired private): 짧지만 final을 쓸 수 없고, 컨테이너 없이는 테스트에서 값을 넣기 어렵다

같은 타입의 빈이 여럿일 때

주입할 후보가 둘 이상이면 스프링은 다음 순서로 고른다.

  1. 주입 지점의 @Qualifier("이름")가 가리키는 빈
  2. 후보 중 @Primary가 붙은 빈
  3. 파라미터·필드 이름과 같은 이름의 빈

셋 다 없으면 NoUniqueBeanDefinitionException으로 시작에 실패한다. 이름 일치에 기대는 방식은 변수명을 바꾸면 조용히 깨지므로, 의도가 있다면 @Qualifier나 @Primary로 드러낸다. 전부 필요하면 List<PaymentClient>로 받는다(전략 패턴).

순환 참조

A가 B를, B가 A를 생성자로 요구하면 어느 쪽도 먼저 만들 수 없어 시작 시 실패한다. 필드·세터 주입이면 예전에는 우회됐지만, 스프링 부트 2.6부터는 이런 순환 참조도 기본으로 금지된다. 대개 책임이 잘못 나뉜 신호라, 공통 부분을 세 번째 빈으로 빼거나 이벤트로 끊는다(결합도, 의존성 역전 원칙).

책: 4장. 마법 걷어내기 — DI와 IoC

출처: Spring Framework 문서: Dependency Injection · @Qualifier로 미세 조정

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • JDBC와 JdbcTemplate

    JDBC(Java Database Connectivity)는 자바가 여러 DB와 통신하기 위한 표준 API이고, Hibernate·MyBatis·스프링 JDBC 모두 그 위에 있다. JdbcTemplate은 스프링이 JDBC의 반복 작업(연결 열고 닫기, 예외 변환, 결과 순회)을 대신해 주는 클래스다.

  • Bean Validation

    Bean Validation은 객체 필드에 제약 어노테이션(constraint annotation)을 붙여 입력을 검증하는 자바 표준이다(Jakarta Validation, 구현체는 Hibernate Validator). 스프링은 컨트롤러 파라미터에 @Valid가 붙으면 바인딩 직후 검증을 실행한다.

  • 스프링 AOP

    AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)는 로깅·트랜잭션·보안·실행 시간 측정처럼 여러 클래스에 흩어져 반복되는 횡단 관심사(cross-cutting concern)를 한 곳에 모아, 비즈니스 코드를 건드리지 않고 메서드 호출 앞뒤에 끼워 넣는 방법이다. 스프링 AOP는 이를 프록시로 구현한다.

  • 스프링 부트 자동 설정

    스프링 부트는 스프링 앱을 만들 때 반복되던 설정(의존성 버전 맞추기, 서블릿 컨테이너 연결, 배포)을 관례(convention over configuration)로 대신해 주는 도구다. 클래스패스(classpath)에 무엇이 있는지 보고 필요한 빈을 자동으로 등록하고, 웹 서버를 내장해 java -jar 하나로 실행된다.

  • 스프링 REST 컨트롤러

    @RestController는 메서드의 반환값을 뷰 이름이 아니라 HTTP 응답 본문(보통 JSON)으로 쓰는 컨트롤러다. @Controller에 @ResponseBody를 합친 것으로, 직렬화(serialization)는 Jackson이 맡는다.

보기 옵션