본문 바로가기

Spring

DI(Dependency Injection)란?

GOAL

  •  객체지향에서 의존성을 이해한다
  •  DI의 개념과 도입 이유를 이해한다.
  •  Spring에서 어떻게 사용하고 있는지 이해한다.

객체지향에서의 의존성이란?

객체지향 프로그래밍의 장점 중 하나는 모듈화를 통한 유연성 있는 프로그램을 설계하고 코드를 재사용하여 유지보수를 용이하게 만들 수 있다는 것이다.

객체지향에서는 의존성 있는 코드를 싫어한다. 의존성이 필요 없다면 제거해야 하며, 클래스 간의 양방향 의존성을 가지고 있다면 문제점이 발생할 수도 있다.

왜 그럴까? 아래 코드의 문제점은 무엇일까?

room 객체는 Bed 객체에 의존하고 있다. -> new 연산자를 통해 Simons() 객체를 주입받아 의존성을 가지고 있다. 역할에 의존해야 되는데 구현에 의존하고 있다. 

아니 뭐 가질수도 있지.. 참.. 어려운 것 같다.. 의존성이 위험한 이유는 유지보수의 어려움이다. 하나의 모듈이 변경되면 의존관계를 갖고 있는 모듈도 변경을 해야 하기 때문이다. 만약 이러한 의존 관계가 뒤죽박죽으로 수 없이 얽혀있다면 유지 보수할 생각은 꿈도 꾸지 못할 것이다. 또한, 테스트 코드가 필수적이 된 상황에서 의존관계를 갖고 있다면 Unit Test 작성에서 어려움을 겪을수도 있다.

의존성 주입으로 해결 하자

그럼 위의 문제를 해결할 방법은 없을까? 있다. 위와 같은 의존성을 해결하고자 나온 개념이 바로 의존성 주입이다! 의존성 주입이란 위의 예시처럼 객체 내부에서 new 연산자를 통해 객체를 생성하는 것이 아니라 외부에서 주입받겠다는 것이다.

그래서 IoC라는 개념이 생긴 것이다. Inversion Of Control(제어의 역전)으로 기존의 프로그래머가 new 연산자를 통해 직접 의존성을 주입하고 제어했다면, 이러한 제어권을 스프링과 같은 프레임워크에게 넘기는 것이다. 그래서 DI와 IoC는 떼려야 뗄 수 없는 관계가 된 것이다.

아래의 코드는 MovieService의 MovieRepository에 대한 객체 의존성을 DI를 통해 제거한 것이다. 만약 DI의 개념이 없었다면 new MovieRepository()가 될 것이다.

DI를 도입함으로서 OCP와 DIP를 지키는 데 성공했다.

스프링 프레임워크에서는 어떻게 쓰는데?

스프링 프레임워크에서는 BeanFactory라는 IoC Container에 Bean이라는 의존 객체를 등록하고 객체의 제어권을 넘겨 의존성을 주입하게 된다.

실행하게 되면 Component Scan과 AutoConfiguration 어노테이션에 의해 bean이 등록되고 의존관계가 주입된다. 스프링에서 의존성을 주입하는 방법은 크게 3가지가 있다. 

필드 인젝션, 세터 인젝션, 생성자 인젝션으로 가장 지향해야 할 것은 생성자를 통한 의존성 주입이다.

Field Injection -> 인텔리 제이에서 추천하지 않는다고 알림이 뜬다.

코드도 간결하고 깔끔해보이는데 왜 좋지 않을까? -> 외부에서 변경이 불가능하다. 테스트 코드 작성이 매우 어렵다(스프링이 아닌 순수 자바 테스트 코드를 작성이 불가능, 결국 setter 매서드를 만들어 객체를 주입해줘야 한다.)

Setter Injection ( 빈 생성이 먼저 일어난 후에 의존 관계 설정)

선택이나 변경의 가능성이 있는 경우 사용

Constructor Injection (빈을 등록하면서 의존관계 주입도 같이 일어난다. ->)

생성자를 호출하는 시점에 딱 1번의 호출만이 보장이 된다.(final 키워드를 통해 불변하게 설정)