제어의역전(IoC)/ 의존관계 주입(DI)
IoC/DI는 스프링의 핵심개발 원칙이다.
스프링의 3대 기술인 AOP,PSA도 IoC/DI에 바탕을 두고있다.
왜 두 개의 오브젝트를 분리해서 만들고 인터페이스를 두고 느슨하게 연결한 뒤 실제 사용할 대상은 DI를 통해 외부에서 지정하나?
이렇게 DI방식으로 하는것이 new 키워드로 생성해서 사용하는 강한 결합을 쓰는 방법보다 나은가?
간단한 답변은 '유연한 확장이 가능하게 위해서' 이다.
DI는 개방 폐쇄 원칙(OCP)이라는 객체지향 설계원칙으로 설명된다.
유연한 확장은 OCP의 '확장에는 열려있다(개방)'에 해당하고,
'변경에는 닫혀있다(폐쇄)에도 해당한다. 폐쇄 관점에서의 장점은 '재사용이 가능하다' 로 볼 수 있다.
A -> B라는 의존관계를 갖는 오브젝트 구조에서 확장B는 자유롭게 변경될 수 있음을 의미한다.
B가 변경되어도 A는 아무런 영향을 받지 않고 그대로 유지됨을 의미한다.
B의 관점에서는 유연한 확장이고 A의 관점에서는 변경없이 재사용 한다는 의미이다.
B는 B1, B2, B3이 될 수 있고 A는 그대로 재사용이 가능하다고 볼 수 있다.
예를 들어 서비스 오브젝트가 사용하는 DAO가 있을때 DAO의 구현을 JDBC, JPA, 하이버네이트, JDO, iBatis 등으로 변경한다.
구현 방식을 통째로 바꾸는 것이다.
만일 사용자 관리 서비스라고 한다면 사용자의 등급을 결정하는 정책을 담은 코드를 DI로 분리한다.
만약 비즈니스 로직이 변경돼서 새로운 정책을 적용한다면 DI를 이용해서 새로운 정책을 담은 클래스로
통째로 변경해주면 된다.
이런 기능 외에도 핵심기능의 동적인 변경도 가능하다.
예를 들어 사용자의 등급에 따라 다른 DataSource를 사용하게 할 수도 있다.
DAO는 DataSource에 의존한다.(DAO -> DataSource)
그런데 DAO하나가 여러 개의 DataSource에 의존하게 할 수도 있다.
그리고 사용자의 등급에 따라 그때그때 다른 DataSource를 DAO가 사용하게 할 수도 있다.
VIP사용자는 좀 더 속도가 빠른 DB를 이용하게 해서 빠른 처리 속도를 보장해주기 위해 적용할 수 있는 기법이다.
심지어 DAO를 따로 만들 필요도 없다.
대신 매우 지능적인 방식으로 동작하는 DI덕분에 선택적으로 사용할 DataSource를 바꿔주는 기법이 가능하다.
이렇게 동적인 방식으로 핵심기능을 변경하는 것은 기술적인 관점에서는 다이나믹 라우팅 프록시나
프록시 오브젝트 기법을 활용한 것이다.
이러한 기법 적용이 가능한것은 DI가 있기 때문이다.
만일 DI가 없었다면 불가능하다.
세번째 활용 방법으론 핵심기능은 그대로 두고 부가기능을 추가할 수 있다.
트랜잭션 기능을 부여한것이 대표적인 예다.
전달 파라미터를 조작하거나 파라미터나 리턴 결과를 활용하여 로깅이나 보안처리같은 부가적인 작업 수행이 가능하다.
부가기능의 추가 방식을 특정 오브젝트가 아닌 좀더 많은 대상으로 일반화 해서 적용하면 AOP가 된다.
싱글톤과 오브젝트 스코프
DI가 필요한 중요한 이유 중 한 가지는 DI 할 오브젝트의 생명주기를 제어할 수 있다는 것이다.
DI를 프레임워크로 이용한 다는 건 DI 대상 오브젝트를 컨테이너가 관리한다는 의미다.
오브젝트의 생성부터 관계설정, 이용, 소멸에 이르기까지 모든 과정을 DI컨테이너가 주관하기때문에 그 오브젝트의 스코프를 자유롭게 제어할 수 있다.
가장 기본이 되는 스코프는 싱글톤이다.
스프링의 DI는 기본적으로 싱글톤으로 오브젝트를 만들어서 사용한다.
컨테이너가 알아서 싱글톤으로 만들어서 관리하기 때문에 클래스 자체는 싱글톤을 고려하지 않고 자유롭게 설계해도 되는 장점이 있다.
하지만 떄로 당일 싱글톤이 아니라 임의의 생명주기를 갖는 오브젝트가 필요할 때가 있다.
스프링에서 싱글톤 이외에 다양한 스코프를 갖는 오브젝트를 만들어서 DI에 사용할 수도 있다.
HTTP요청당 하나의 오브젝트를 만들거나 HTTP세션당 하나씩 오브젝트가 만들어지게 할 수도 있다.
DI의 중요한 용도는 바로 테스트다.
다른 오브젝트와 협력하여 동작하는 오브젝트를 효과적으로 테스트하는방법은 가능한 고립시키는 것이다.
자칫 다른 오브젝트와 협력을 통해 동작하는 기능을 다 허용하고 테스트하다가는 한번에 수십 개의 오브젝트와 DB, 환경까지
모두 테스트하는 부담을 안을수도 있다.
그래서 테스트할 대상이 의존하는 오브젝트를 테스트를 목적으로 만들어진 목 오브젝트로 대처하면 유용하다.
갈수록 테스트의 중요성이 커져가고 있으니 DI의 활용방법에서 테스트가 차지하는 비중도 커질것이다.
이외에 템플릿과 콜백,인터페이스 변경, 프록시 등 다양한 활용방법이 있다.
런타임시 유연하게 구현을 바꿀 수 있는 DI라는 개념이 실전에서 얼마나 활용도가 높은지 알 수 있다.
이러한 활용방법은 한 번에 한 가지만 선택적으로 사용해야만 하는것이 아니다.
여러가지 활용 방법을 한 번에 적용할 수도 있다.
예로 하나의 DI 대상에 대해 핵심기능도 업무 변화에 따라 바꾸고, 부가기능도 추가하고, 테스트에서 활용 하는 방법도 가능하다.
출처 토비의 스프링3.1