본문 바로가기

IT 살이/04. 기술 - 프로그래밍

개발 프레임워크 만들기 대장정 10- Dependency Injection 패턴

이번 포스트에서는 Unity Application Block에서 사용하고 있는 Dependency Injection 패턴에 대해서 알아본다. UAB에서 구현하고 있는 이 패턴에 대해서 잘 이해하면 프레임워크를 만들때 개발자가 해야 할 일을 프레임워크단에서 공통적으로 해결해 줄 수 있는 유연한 방법을 많이 찾을 수있을 것으로 보인다. 프로젝트가 한참 진행되는 도중이라면 새로운 고객의 요구사항을 수용하기가 참 힘들다. 고객은 단순히 메소드에 파라미터 하나만 더 추가해서 빌드하면 되지 않겠는냐는 식으로 간단히 말한다. 그러나 그 간단한 요구사항이 공통팀과 업무 개발자들에게는 쓰나미로 다가오는 경우가 있다. 이런 경우 Dependency Injection 패턴을 잘 활용하면 업무 개발자가 기존 코드의 수정을 하지 않고도 공통팀에서 프레임워크단을 수정하는 것만으로 쓰나미를 피해갈 수 있는 가능성이 많아질 수 있다는 것이다. 그렇다면 이 패턴이 뭔지 알아보자.

Dependency Injection? 종속되는 것을 injection? 삽입하다? 끼워넣다? 그렇다. 말 그대로하면 종속되는 것을 끼워넣는 것이다. 종속되는 것? 끼워넣어줘? 대체 뭔 말이여! 대체 누가 뭘 끼워넣어준다는 것이여!

미리 말하면, "어떤 객체에 필요한(종속되는) 객체를 컨테이너!가 개발자 대신에 생성해서 필요한 자리에 건네준다"는 것으로 표현할 수 있다. 즉 개발자는 객체(종속된 객체)를 대상 객체에 건네주지 않아도 컨테이너가 알아서 생성해서 대상 객체에 대신 건네준다는 것이다.

  이전 그림

  수정된 그림

잘못 설명한 부분 [대상 객체(TargetObject)의 어떤 "메소드"를 호출할때는 인자로 특정 타입의 인스턴스가 필요하다고 하자. 개발자는 이 대상 객체에 대한 참조를 new를 생성해서 얻는 것이 아니라 Unity 컨테이너에 요청해서 얻는다. 컨테이너는 이미 생성되어 있는 인스턴스를 또는 필요한 경우에는 새로 생성해서 개발자에게 전달해준다. 개발자는 대상 객체가 특정 타입의 인스턴스를 필요로 함에도 불구하고 그 객체를 넘겨주지 않고 그냥 호출한다.

그 호출은 바로 대상 객체로 넘어가는 것이 아니라 일단 Unity 컨테이너가 중간에 인터셉트한다. 그래서 대상 객체에서 특정 타입의 객체가 필요하다는 것을 확인하고나서는 그 객체를 개발자 대신에 생성해서 대상 객체의 "메소드"를 호출한다. 반환값이 있다면 일단 컨테이가 받고 나서 다시 개발자에게로 건네준다. 이것이 dependency injection(DI)이 일어나는 메커니즘이다. ]

이탤릭체로 된 부분은 이전에 설명한 부분이지만 잘못된 설명이다. 내가 뭔 소리를 해 놓은 건지 모르것다. .NET에서 지원하는 프락시를 이용하는 AOP 패턴과 혼동한 듯 싶다.

Unity 컨테이너에서는 객체를 생성할때 미리 메소드 호출에 필요한 파라미터 객체를 생성해둔다. 이어지는 포스트에서 설명하지만 dependency injection란 것이 파라미터를 필요로 하는 메소드를 코드상에서 파라미터를 건네주지 않고 호출할 수 있다는 것은 아니다.  dependency injection이 일어나는 것은 객체를 생성할때이다. 객체를 생성할때 필요한 해당 클래스의 외부 인스턴스를 미리 생성해서 객체를 초기화시켜주는 작업을 말한다. 뒤의 포스트에서 자세히 설명한다. 특히 ObjectBuilder 아키텍쳐를 설명하는 부분을 유용하게 참조할 수 있을 것이다. - 2008.06.15 일요일 오후 수정

컨테이너에게 이런 dependency injection 메커니즘이 작동해야 한다는 것을 알려주기 위해서 대상 객체의 메소드에 특별한 어트리뷰트를 이용해서 표시를 해줘야 한다. 이 어트리뷰트에 대해서는 뒤에서 설명한다. 앞에서는 "메소드"라고 했지만 사실 메소드는 우리가 알고 있는 일반 메소드뿐만 아니라 특수한 메소드 즉 생성자 그리고 속성 그중에서도 setter 속성도 여기에 해당된다. 즉 생성자, setter 속성, 일반 메소드에 특정 표시를 해 주면 컨테이너는 이 메소드들을 호출할때는 DI 메커니즘이 작동해야 한다는 것을 인식한다. 그래서 이 메소드들의 호출에 필요한 인스턴스를 개발자가 건네주지 않아도 자기가 스스로 생성해서 호출해주는 것이다.

그럼 그 표시 즉 어트리뷰트는 어떤 모습인가. 생성자, setter 속성, 일반 메소드 모양은 다르다.

다음 포스트에서는 세 종류의 injection에 대해서 예제를 통해서 알아보도록 하겠다. 너무 많은 것을 한꺼번에 하면 지친다. 필자도 지치고 읽는 사람도 지치고.