DI : Dependency Injection

김하나·2023년 5월 2일

Spring Boot

목록 보기
2/16

DI : Dependency Injection

의존성 주입 - DI

클래스간 의존성을 클래스 외부에서 주입하는것


의존성이란 무엇인가?

클래스 간에 의존 관계가 있다는 것은 한 클래스가 바뀔 때 다른 클래스가 영향을 받는다는것!

만약 의존 관계인 두 클래스가 있는데, 요구사항 변경 등의 이슈로 한 클래스를 변경해야 하는 상황이 오면 한 클래스의 변경으로 인해 해당 클래스와 의존관계인 또 다른 클래스에서 오류가 발생

⇒ 이러한 점을 방지하기 위해 인터페이스를 이용하여 클래스로 부터 의존성을 없앰

 

주입이란?

클래스 외부에서 객체를 생성하여 해당 객체를 클래스 내부에 주입하는 것

 

왜 필요한가?

만약 클래스 내부에서 인스턴스화 된다면 클래스마다 객체를 생성해서 가지고 있어야함 → 수정 할때 클래스 모두에게서 수정해야함

외부에서 객체를 만들어 필요한 각 클래스에 주입하면 문제 해결!

외부에서 객체를 저장해놓는 공간이 바로 컨테이너 → 이제 객체에 대한 제어권한은 각 클래스가 아닌, 컨테이너에 있음!

⇒이걸 바로 IoC(제어 역전)라고 합니다..!


스프링에서 의존성을 주입 받는 방법

  1. 생성자를 통한 의존성 주입
    ->생성자 주입

  2. 필드 객체 선언을 통한 의존성 주입
    ->필드주입

  3. setter 매서드를 통한 의존성 주입
    ->수정자주입

  4. 일반 메서드 주입

* 의존 관계가 변경되지 않을 경우: 생성자 주입

* 의존 관계가 선택적이거나 변경 가능한 경우: 수정자 주입

 

생성자 주입

  • 생성자에 @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에 필요한 빈들을 주입해줌
@Autowired
   public MemberService(MemberRepository memberRepository){
    this.memberRepository = memberRepository;
 }

MemberService클래스에서 생성자를 이용해 MemberRepository의 의존성을 주입함

  • 특징
    1. 생성자가 1개만 존재하는 경우 @Autowired 생략 가능
    2. NullPointerException 방지 가능
    3. 주입받을 필드를 final로 선언 가능

 

필드 주입

  • 필드에 @Autowired를 붙여서 주입하는 방법
@Autowired 
private MemberRepository memberRepository
  • 특징
    1. 코드가 간결해짐
    2. 외부에서 변경이 불가능, 테스트 하기 어려움
    3. 애플리케이션의 실제 코드와 상관없는 특정 테스트를 하고 싶을 때 사용
    4. 의존관게를 필수적으로 넣고 싶지 않으면 @Autowired(required=false)를 이용해 필수가 아님을 명시
    5. DI 컨테이너 안에서만 작동하기 때문에 순수 자바 코드로 테스트 하기 어려움
    6. DI 프레임워크가 있어야 사용 가능
    7. final 키워드를 통한 선언 불가능

 

Setter 주입

  • Setter(수정자)를 통해 의존관계를 주입하는 방법, @Autowired가 있는 수정자들을 자동으로 의존관계 주입
private MemberRepository memberRepository;
      @Autowired
      public void setMemberRepository(MemberService memberService){
      this.memberService = memberService;
  • 특징
    1. 선택과 변경 가능성이 있는 의존관계에서 사용
    2. 자바 빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법
    3. @Autowired를 입력하지 않으면 실행되지 않음

 

생성자 주입을 사용해야 하는 이유

  1. 필드 주입 사용시 외부에서 수정이 불가능 하기 때문에 테스트 코드를 작성할 때 객체 수정이 어려움 + 자바 코드로 단위 테스트를 하게 되면 의존 관계 주입이 정상적으로 되지 않아 NullPointError가 발생

  2. Setter 주입의 경우 public으로 구현하기 때문에 객체 변경 가능성을 열어둠→ 에러 발생 위험 높음 (Setter의 경우 immutable 속성보장이 되지 않기 때문에 DTO에서도 사용을 지양)

  3. 객체의 불변성을 확보하기 위해 → 객체 생성시 1회만 호출되는게 보장되는 특징이 있기 때문에 불변성 확보

  4. 순환 참조 에러 방지 → 생성자 주입 방식을 사용하면 순환 참조 문제를 애플리케이션 실행 시점에 순환 참조 문제가 있다며 예외가 발생하기 때문에 애플리케이션이 실행 되지 않음

    (만약 필드주입을 사용해 서로 메서드를 호출하는 순환 참조가 일어나면 StackOverFlow에러가 나서 애플리케이션이 다운되는 문제점 발생)


IoC

  • 객체의 생명주기 관리를 외부(스프링 컨테이너, IoC 컨테이너)에 위임하는 것

    ⇒ 객체의 관리를 컨테이너에 맡겨 제어권이 넘어감

    ⇒ 제어역전! *개발자는 비지니스 로직을 작성하는데 더 집중할 수 있음

*제어 역전을 통해 의존성 주입 (DI: Dependency Injection)

*관점 지향 프로그래밍 (AOP: Aspect-Oriented Prokgeamming)

 

기존의 객체 생성-실행 순서

  1. 객체 생성
  2. 의존성 객체 생성 (클래스 내부에서 생성)
  3. 의존성 객체 메소드 호출

 

스프링 객체-실행 순서

  1. 객체 생성
  2. 의존성 객체 주입 (스스로 객체 생성 x 제어권을 스프링에 위임해 스프링이 만들어 놓은 객체를 주입)
  3. 의존성 객체 메소드 호출

 

스프링이 모든 의존성 객체를 스프링이 실행될때 다 만들어주고 필요한 곳에 주입시켜줌으로써 Bean들은 싱글톤 패턴의 특징을 가짐

*싱글톤 패턴이란?

객체의 인스턴스가 1개만 생성되는 패턴

0개의 댓글