2019년 7월 29일 월요일

DI와 스프링

DI는 외부 인스턴스 혹은 특정 container가 두 object 간의 종속성을 (대신) 주입해주어 서로에 대한 종속성을 낮추게 해준다. DI를 사용함으로써 어떤 클래스가 특정 클래스에 의존성을 갖을 때 구체적인 구현에 대해 모르게 할 수 있다. (구현에 대해 갖는 종속성을 제거.) 이렇게 하면 모듈화에도 좋고, DI를 적용하지 않았을 때보다 독립적인 단위 테스트를 하기 쉬워진다. 독립적인 단위 테스트를 하기 쉬워진다는 말은 결합도(coupling)가 낮아지는 것을 의미한다. 알다시피 낮은 결합도(=모듈의 높은 응집도)는 좋은 소프트웨어 설계의 목표 지점 중 하나다.

DI가 구현에 대한 의존성을 제거하는 유일한 방법은 아니다. 이 작업을 수행하는 데 사용할 수 있는 또 다른 패턴으로 Service Locator가 있다. Service Locator 패턴을 따르는 경우엔 애플리케이션이 service locator에게 필요한 서비스를 메시지로 직접 요청하는 형태를 띄게 된다. 반면 DI의 경우엔 명시적인 요청 없이 서비스가 주입된다. 이런 형태를 일반적으로 'Inversion of Control' 이라 한다.

아래는 마틴 파울러가 15년전 작성한 글인데 정말 좋다.
https://martinfowler.com/articles/injection.html

DI 구현방식

DI엔 constructor injection, setter injection, field injection의 세가지 방식이 있다. 그리고 이 중에선 일반적으로 constructor injection 방식이 가장 권장된다. Constructor injection 방식을 쓰면 생성자만 보고도 의존 관계와 복잡성을 쉽게 알 수 있게 되기 때문에 작성하는 코드가 올바른 방향으로 가고 있는지 알 수 있게 된다. 그리고 final 키워드를 이용해 객체의 상태를 변하지 않게 만들 수 있다. 이는 setter, field injection 방식에서는 가질 수 없는 형태다.
일반적으로 객체를 생성할 땐 가능하면 하나의 방식을 갖는 것이 좋고, 의존 관계가 있다면 setter method 보다 매개 변수가 있는 생성자를 통해 드러내는 것이 좋다고 알려져 있다. (객체를 구성하는 방법이 여러개라면 Factory Methods를 쓰고..) 이와 비슷한 맥락에서 DI도 constructor injection 방식을 권장하는게 아닌가 한다.

반대로 setter injection의 경우엔 특정 의존 관계를 인스턴스의 생성 시점과 구분해서 지정하거나 또는 지정하지 않거나, 교체할 수 있는 형식을 가질 수 있기 때문에 setter injection 방식도 쓰임새가 있다.

[코드 예시: https://multifrontgarden.tistory.com/214]


DI와 스프링

잘 알려진 DI 구현체를 꼽으면 스프링을 예로 들 수 있다. 스프링에선 스프링 DI 컨테이너가 관리하는 객체를 빈(bean)이라고 표현한다. 여기서 빈이 특별한 개발 사양을 갖는 것은 아니고 빈은 단지 스프링 컨테이너에 의해 관리를 받는다는 의미로 쓰인다. 그리고 스프링 컨테이너는 이 빈들을 관리한다는 의미로 빈 팩토리(bean factory)라 불린다. 빈 팩토리는 단어 그대로(factory=공장) 빈을 생성(instantiating)하고, 설정(configuring)하고, 관계를 갖는 빈들을 묶어주는(assembling) 역할을 맡는다. 여기에 비즈니스 애플리케이션을 지원하는데 필요한 기능 몇가지가 추가되면 바로 그(?) ApplicationContext가 된다.

댓글 없음:

댓글 쓰기