Spring

IoC와 DI: 맨날봐도 헷갈리는 스프링의 기반

noh.noah 2022. 9. 16. 21:00

오늘은 비슷한듯 개념이 다른 IoC와 DI에 대해서 정리해보고자 한다.

(맨날 헷갈린다... 누가 물어보면 갸가 갸여... 이러고 마는 느낌ㅠ)


스프링의 대 삼각형: POJO(순수객체) 기준

  • IoC/DI: 의존관계 주입
  • AOP: 관점 중심 프로그래밍
  • PSA: 이식 가능한 서비스 추상화

참고: https://velog.io/@minkeun428/IoC%EC%99%80-DI%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90

 

IoC / DI: by 토비의 스프링

  • 스프링 프레임워크의 근간
  • 오브젝트의 생명주기와 의존관계에 대한 프로그래밍 모델
  • 유연하고 확장성이 뛰어난 코드를 만들 있게 해주는 프로그래밍 모델

그래서 먼저, "의존관계 주입"을 포스팅 하고자한다.


IoC: Inversion of Control (제어의 역전)

직접 객체를 생성하여 코드를 “제어”

  • 객체 생명주기나 메서드의 호출을 직접 제어한다 = 객체 생명주기나 메서드의 호출을 직접 관리한다.

제어의역전

  • 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것

 

그래서 IoC 필요한가?

1. 객체 내에서 제어하는 경우

2. 재료에 대한 제어권을 이탈리안 비엠티였지만 역전이 되고나서 내가 되어짐 (재료 커스터마징)

 

객체지향 원칙을 지키기 위해!

  • 역할과 관심을 분리해 응집도를 높이고 결합도를 낮추며,
  • 이에 따라 변경에 유연한 코드를 작성할 있는 구조가 있기 때문

할리우드 법칙 (Don’t call us, we’ll call you)

  • “우리를 더 이상 부르지 마시오. 우리가 필요하면 당신을 부르겠소.” (Don't call us, we will call you)

DIP(Depnedency Inversion Princple): 의존 역전원칙

상위 레벨 모듈은 절대 하위레벨 모듈에 의존하지 않고 추상화에 의존해야한다.

 

서브웨이 예시

    • 1.고수준 모듈: 이탈리안 비엠티 / 저수준 모듈: 화이트 브레드 -> 만약, 빵이 바뀐다면? ㄷㄷ
    • 2.고수준 모듈: 이탈리안 비엠티 / Bread Interface: 아무튼 빵 / 저수준 모듈: 화이트 브레드 / 위트 브레드 / …

클래스 간 결합을 느슨히 하기 위함

  • 한 클래스의 변경에 따른 다른 클래스들의 영향을 최소화 -> 애플리케이션을 지속가능하고 확장성 있게 만든다

원칙을 예시로 설명

  • 기존: 난 이탈리안 비엠티야 그냥 먹어! -> 으앙 싫어
  • IoC: 으앙 싫어! 랜치소스로 바꿀래 -> 으악! 소스 바뀐다.

DI: 의존성 주입

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

말그대로, 의존성을 다른데서 주입받는것을 의미한다.

 

1.생성자 주입

  • 필요한 의존성을 모두 포함하는 생성자를 만들고 생성자를 통해서 의존성을 주입

2.세터 주입

  • 의존성을 입력받는 세터메서드를 만들고 메소드들을 호출해서 주입 받음

3.인터페이스 주입

  • 의존성을 주입하는 메서드를 포함한 인터페이스를 작성하고 인터페이스를 구현하도록 함으로써 
  • 실행 시 이를 통하여 의존성을 주입합니다
  • 세터주입처럼 메서드를 외부에서 호출해줘야 하는 것은 비슷하지만 의존성 주입 메소드를
  • 빠뜨릴 있는 세터와 다르게 오버라이드를 통해 메서드 구현을 강제할 있다는 차이가 있다.

 

의존성 분리: DIP 이용해 의존관계를 분리시킨다

상위계층이 하위계층에 의존하는 상황을 인터페이스를 이용해 반전시켜 하위계층의 구현으로부터 독립시킵니다.


중간정리!

1.IoC와 DIP는 모두 Principle(원칙)이다

  • IoC: 제어의 역전
  • DIP: 의존방향의 역전

2.DI 디자인 패턴의 종류이다.


스프링 빈으로 등록되면 스프링이 자동으로 생성한다. 필요한 의존성도 주입해준다

 

Spring DI: @Autowired 사용법

1.필드주입

  • 어노테이션 붙이기 (그러나, 비추: 의존성이 프레임워크게 강하게 종속)

2.세터주입

  • 세터 메서드에 어노테이션을 붙이기 (빈 생성자또는 빈 정적 팩토리 메서드가 필요: 불변보장X -> 선택적 의존성)

3.생성자 주입

  • 객체생성 시점에 주입 (스프링에서 추천하는 방법) final / 의존성 주입이 생성자 호출 시 최초 1회만 이뤄짐
    • final 가능해서 NPE X / 순환 참조문제 방지

 

만약 생성자가 여러 개라면?

  • 의존성을 자동으로 주입하는데 사용할 생성자에 @Autowired
  • @Autowired가 여러 개 있을 경우, 가장 많은 의존성을 주입할 수 있는 생성자를 사용
  • @Autowired가 붙은 모든 생성자가 사용 불가능한 경우 또는 어떤 생성자에도 @Autowired가 없을경우 기본 생성자 호출
  • 기본 생성자조차 없는 경우 컴파일에러

 

의존성 주입 순서 (다같이 쓰인다는 가정하에)

  • 1.생성자 -> 2.필드 -> 3.setter

 

주입대상이 여러개

  • 하나의 인터페이스로 여러개의 서비스가 생성되어 여러 Bean이 검색되었다면 
  • 스프링은 그 다음으로 Bean의 이름을 기준으로 의존성을 주입합니다.
  • 이 떄 주입하는 데 사용하는 메서드의 매개변수명과 등록된 Bean의 이름이 일치하는지 체크합니다.
  • 그래서 생성 매개변수명을 다르게하면 다르게 인지 -> 계속 변경하게 되면 놓칠 수 있음
  • 그래서 스프링인 @Qualifier로 별도로 이름을 지정할 수 있다. @Primary를 하면 기본 빈 등록을 할 수 있다.
    • 타입 ->  @Qualifier -> @Primary -> 변수명

결론

  • 원칙: IoC / DIP
  • 디자인패턴: DI
  • 프레임워크: Spring DI / IoC

References