DI의 본질 – 다이나믹 (타입) 언어는 Dependency Injection이 필요없는가?

루비나 파이썬 같은 다이나믹 언어 또는 다이나믹 타입 언어의 특성을 가진 언어에서는 왜 DI가 인기가 없는가, 또는 왜 DI가 사용되지 않는가 하는 질문을 본 적이 있다. 그리고 그 대답으로 그런 언어들을 잘 안다고 하는 사람들이 이런 식의 답을 달았던 것이 생각난다.

DI는 디자인패턴과 마찬가지로 자바와 같은 스태틱 타입 언어의 단점 또는 한계 때문에 필요한 것일 뿐이고, 루비나 파이썬 같은 다이나믹 타입 언어에서는 언어차원에서 DI기능을 손쉽게 해결해주기 때문에 DI가 필요없어서 사용하지 않는다

 

과연 그런가?

짧게 대답하자면 "DI을 알게 된 이후로 들었던 수없이 많은 황당한 이야기의 하나"라고 하겠다.

벌써 10월이 된데다 RC1이 나온 탓에 할 일이 산더미지만, 트위터나 미투도 안하는 주제에 블로그도 잘 안쓰는 요즘의 게으른 삶에 대한 반성으로 좀 더 길게 얘기해보자.

 

DI의 기원과 본질

많은 사람들이 가진 오해는 DI라는 프로그래밍 모델과 DI 프레임워크(또는 컨테이너)의 혼동에서 출발한다고 생각한다. DI는 IoC의 일종으로 생각하면 DI도 당연히 프레임워크를 전제로 하는 개념이라고 생각할 수도 있다. 하지만 IoC가 프레임워크의 동작원리이긴 하지만 프레임워크가 없이도 IoC를 적용할 수 있는 것처럼, DI역시 프레임워크 또는 컨테이너를 사용하므로 그 가치가 더 드러나긴 하지만 기본적으로는 객체지향(어떤 이들의 주장을 따르자면 자바와 같이 꾸진 특정 객체지향) 프로그래밍의 한가지 기법에 불과한 것이다.

DI라는 개념을 유행시키고 널리 알린 것은 누구나 동의할 수 있듯이 스프링이다. 스프링의 등장 이후로 DI는 매우 빠르게 인정받고 확산되어서 적어도 자바에서는 DI는 누구나 알아야 할 표준 프로그래밍 모델이자 패턴이되었다. 스프링이 DI를 바탕에 깔긴 했지만 종합적인 애플리케이션 프레임워크를 지향하고 덩치가 커져가는 것에 반해서, 구글주스와 같은 최신 자바의 언어적 장점을 잘 살려서 DI에 최적화된 기능을 제공하는 것도 등장했다. 구글내부에서는 일반적인 JEE 모델은 아니지만 수백만 라인이상되는 복잡한 서비스 또는 내부 애플리케이션 개발에 이 구글주스를 활용하고 있다고 한다. 씸은 나름대로 In/Out 개념으로 확장해서 DI를 bijection이라는 용어를 만들어서 적용하기도 했으며, 스프링과 주스에 발목잡히긴 했지만 표준 DI 스펙으로 만들어 제출하기도 했다. 또 자체 DI 기능을 제공하는 제품이나 오픈소스 프로젝트들도 적지 않다. Maven이 사용하는 DI프레임워크인 Plexus도 그 동네에서는 제법 유명한가보다.

 

스프링을 DI의 기원으로 보자면, 스프링의 코드가 만들어진 동기이자 배경인 로드 존슨의 One-on-One J2EE Design & Development라는 책을 살펴봐야 한다. 그 책에서 내가 가장 흥미롭게 생각하는 것은 4장인가에 나오는 개첵지향 디자인 테크닉에 관한 부분이었다. 로드 존슨이 한 유명한 얘기인 "J2EE보다는 자바가 더 중요하고, 자바 보다는 객체지향이 더 중요하다"라는 말을 생각해보면 그가 객체지향설계와 프로그래밍 기술에 얼마나 중요한 가치를 두었는지 이해할 수 있을 것이다. 내가 처음 그가 쓴 OO디자인 테크닉에 관한 내용을 읽어보니 이건 마치 GoF의 Design Pattern의 축소판을 보는 듯 했다. GoF의 DP책의 서론인 1장과 오브젝트 합성(object composition)을 통한 확장기법을 다룬 Object scope를 가지는 주요 패턴에 관한 이야기를 압축에서 정리한 것이다. programming to interface나 favour composition over inheritance와 같은 GoF DP의 1장에 나온 패턴에 적용된 핵심 원리도 그렇고, 프레임워크의 동작원리인 IoC에 대한 설명도 마찬가지이다.  나중에 비교해보니 어떤 문장은 GoF책의 내용을 단어 하나 바꾸지 않고 그대로 옮긴 것도 있고 표현은 조금 바꿨으나 내용은 그대로 가져다 쓴 것도 적지 않게 발견된다.

스프링은 자바 엔터프라이즈 개발에서 POJO(스프링 쪽 용어로는 simple objects) 프로그래밍을 제대로 적용할 수 있도록 돕는 것이 그 핵심철학이자 목표이라고 2006년 SpringOne에서 소개된 적이 있다. 나도 그 이후로 그 철학과 목표를 자주 언급하곤 하는데, POJO가 목표다라고만 하면 뭔가 빠진 느낌이다. POJO개발을 하는 이유가 무엇인가? 왜 스프링이 DI를 극단적일 정도로 적용해서 각종 기술적인 문제들과 환경에 종속된 코드가 아닌 순수한 자신의 로직만을 담고 있는 POJO라 불리는 단순한 자바 오브젝트를 만들려고 할까? 단지 코드를 간단하게 하려고? 그건 아니다.

스프링이 결국 추구하는 것은 자바의 언어적인 특성, 즉 객체지향 언어의 장점을 극대화 하고, 객체지향기술이 주는 최고의 장점인 유연성(또는 그 때문에 얻어지는 코드의 수정 없는 재사용성)을 극대화하려는 것이다. 그리고 그 적용기법으로 다양한 객제지향설계 테크닉, 패턴들을 추구한다.  환경과 기술에 종속적이며 침투적인 기술로는 그런 유연성을 얻을 수 없기 때문이다.

 

로드 존슨이 DI를 생각한 이유는 기본적으로 그것이 유연한 객체지향설계의 가장 대표적인 기법이었기 때문이고, DI를 지원하는 프레임워크를 만든 이유는 그 기법을 제대로 적용하기 위해서 그 단점을 극복하려고 했기 때문이다.

 

나는 DI의 기원을 GoF패턴의 전략패턴(strategy pattern)에서 찾는다. 물론 DI는 패턴을 스코프로 분류했을 경우에 object scope에 해당하는 모든 패턴에 다 들어맞는다. 상속을 확장을 위해서 사용하는 class scope구조에는 DI가 적용되지 않는다(DI를 적용하다보니 가끔 필요한 경우가 있긴 하다. method injection은 상속을 통한 확장기법의 특별한 사례이다).

그 중에서 전략패턴이 가장 대표적인 이유는 GoF패턴 책에 DI가 구체적으로 설명되어있기 때문이다.  전략패턴에 관한 내용에 보면 오브젝트의 협력관계에 관한 부분이 있고 거기에 이런 표현이 나온다.

A context forwards requests from its clients to its strategy. Clients usually create and pass a ConcreteStrategy object to the context; thereafter, clients interact with the context exclusively. There is often a family of ConcreteStrategy classes for a client to choose from.

나는 이 내용만큼 DI를 잘 설명해주는 것이 없다고 생각한다.

전략패턴의 구조를 생각하면 보통 다음과 같이 두 오브젝트의 구조로만 생각하기 쉽다.

Context –> Strategy(ConcreteStrategyA, ConcreteStrategyB)

Context가 사용(의존)하는 알고리즘(전략)을 자유롭게 교체해서 사용하기 위해서 이를 오브젝트 합성 구조로 분리하고 Strategy라는 인터페이스를 두고 Context는 이에 대해서만 프로로그래밍 하도록 만든 것이다(programming to interface). 그 덕분에 Strategy를 상속한 구체적인 클래스를 자유롭게 교환하거나, 새롭게 만들어서 이를 Context가 사용하게 만들 수 있다. 여기서 핵심은 구체적인 Strategy를 바꾸더라도 Context는 수정할 필요가 없다는 점이다. 이를 통해서 기능 확장(strategy)에는 열려있고 변경(context)에는 닫혀있는 OCP를 충실히 지키며, 코드의 수정 없이 재사용이 가능한 Context가 만들어지는 것이다. 유연성(확장가능)과 재사용(변경없이)이라는 OO의 장점을 잘 살린 구조를 만들 수가 있다.

여기서 관건은 이 두 오브젝트 사이의 협력구조로만은 사실 이것을 이룰 수 없다는 점이다. 모델링타임시 코드에서는 인터페이스 의존관계를 가진 다는 점은 장점이지만, 실전(런타임시)에서는 구체적인 클래스의 오브젝트와 연결(링크)이 필요하다. 그래서 제3의 존재가 등장하는 것이다.

위의 설명을 보자면 context와 strategy외에 제 3의 존재인 client가 필요함을 설명하고 있다. 사실은 다음과 같은 구조가 되는 것이다.

Client –> Context –> Strategy(ConcreteStrategy…)

Context 스스로 구체적인 strategy클래스 정보를 가지고 있으면 이 모든 패턴과 원칙을 다 깨뜨릴 수 밖에 없다. 따라서 실전에서 구체적인 오브젝트에 대한 정보는 client가 context에게 pass(또는 주입, 삽입, 제공 머라고 해도 좋다)해줘야 한다고 명시하고 있는 것이다. 그 후에는 client는 그 뒤에 어떤 strategy가 있는지 상관없이 client는 context만을 보고 사용하면 그만인 것이다.

이게 바로 DI이다.

DI가 뭐 거창하고 대단한 것이라고 오해하기 쉬운데 가장 원시적이며, 핵심적인 DI란 오브젝트 합성 구조를 통한 확장성을 보장받기 위해서 그 둘(context, strategy) 말고 제3의 존재(클라이언트든 뭐든)가 필요하고 그 제3의 존재가 런타임 의존관계(링크)를 제공해줘야 한다는 뜻이다. 그리고 그 방법과 장점에 대해서는 이미 GoF의 전략패턴에서 잘 설명하고 있다.

 

GoF는 이런 방법의 consequence의 하나로 다음과 같은 문제를 언급하고 있다.

A choice of implementations. Strategies can provide different implementations of the same behavior. The client can choose among strategies with different time and space trade-offs.

Clients must be aware of different Strategies. The pattern has a potential drawback in that a client must understand how Strategies differ before it can select the appropriate one. Clients might be exposed to implementation issues. Therefore you should use the Strategy pattern only when the variation in behavior is relevant to clients.

DI방식으로 전략패턴을 적용하는 것의 단점은 클라이언트에 막중한 짐이 주어진다는 점이다. 또한 구체적인 구현에 노출되어버린다는 점이다.

단지 C->C->S구조가 전부라면 뭐 상관없다. 하지만 client도 역시 context와 전략패턴 또는 오브젝트 합성구조로 연결되어있다면 어쩔 것인가? client자체가 단지 context가 유연한 방식으로 strategy와 연결되게 하고 실행시키는 것 이상의 자신만의 책임을 가진 클래스라면 어쩔 것인가? 그런 경우 클라이언트는 자신의 책임 외에 과중하게 strategy에 대한 정보를 알고 이를 만들고 context에게 주입(전달)하는 등의 이중 책임을 떠맡는 것이다. 따라서 GoF는 클라이언트 관점에서 이를 주의해서 사용하도록 권고하고 있다.

여기까지가 원시적인 DI의 모습이다. 어쨌든 모든 제3의 오브젝트가 다른 두 개의 오브젝트 사이의 유연성을 보장하기 위한 목적으로 한쪽(strategy, dependent object)에 대한 정보(dependency)를 다른 오브젝트(context)에게 전달한다면 이는 모두 DI이다. 이를 통해서 두 가지 핵심 오브젝트 사이에서는 OCP를 충실하게 지키는, 유연하고 재사용성이 높은 구조와 코드가 만들어지는 것이다.

따라서 오브젝트 사이의 관계를 통해서 프로그램이 만들어지는 모든 객체지향언어와 기술에는 DI가 적용가능하며, 실제로 다들 사용하고 있다.

 

GoF패턴 책에는 나타나지 않지만 자바언어의 특징(또는 한계)을 잘 살려서 로드 존슨이 적극 사용한 패턴이 템플릿-콜백 패턴이다. 이 패턴은 전략패턴의 특별한 케이스라고 로드 존슨이 그 빨간 책에서 잘 설명해주고 있다. 자세히 살펴보면 템플릿-콜백 패턴은 이 client-context-strategy라는 전략패턴과 DI의 구조가 선명하게 적용된 케이스이다. 다만 자바에는 전달가능한 메소드(펑셔널 오브젝트, 코드블록, 프로시져 등등 머라고 부르던 간에)라는 개념이 없으므로, 단 하나의 메소드만을 가진 인터페이스를 구현한 오브젝트를 일종의 메소드 오브젝트 개념으로 사용하는 것이다. 이 패턴에서는 클라이언트의 역할이 아주 명확하게 잘 드러난다. 클라이언트는 매번 템플릿(context)가 사용할 코드블록인 콜백(strategy의 구현)을 전달한다. 이를 통해서 템플릿은 전혀 수정하지 않고도 매번 템플릿이 사용하는 주요기능을 바꿔서 사용할 수 있게 된다. 역시 템플릿을 호출하는 클라이언트가 콜백(전략)을 만들고 메소드 파라메터로 주입(DI)하는 방식으로 동작한다. 전략패턴-DI 구조의 특별한 케이스일 뿐이다.

 

DI vs DI 프레임워크

스프링 개발자라면 당연히 알고 있겠지만 스프링의 DI를 담당하는 컨테이너 오브젝트가 구현해야 하는 핵심 인터페이스의 이름은 BeanFactory이다. 사실상 DI프레임워크란 Bean, 즉 오브젝트 팩토리이다. 구지 빈이라는 이름을 붙인 이유는 그것이 컨테이너에 등재되어서 관리되어지는 오브젝트(managed object)이기 때문에 붙인 별명일 뿐이다.

템플릿-콜백과 같은 특별한 케이스를 제외하자면 전략패턴-DI는 GoF가 말한 consequence를 가진다. 즉 클라이언트의 책임이 크고, 너무 구체적인 정보에 노출되어있을 뿐더러, 다중 오브젝트 합성 구조에서는 client를 더 이상 확장할 수 없게 만든다.

따라서 원래 클라이언트가 하던 두개의 오브젝트 사이의 런타임 링크를 만들어주는 DI역할을 전담할 별도의 오브젝트를 두는 것이다. 그것이 마틴 파울러 표현으로 보면 Assembler이고, 내가 좋아하는 용어로 보자면 Object Factory 또는 스프링 표현으로는 BeanFactory인 것이다. 클라이언트의 책임이었던 DI작업을 별도의 팩토리로 빼놓으면 단지 두 계층의 오브젝트 구조가 아니라 이를 더 확장한 복잡하고 많은 의존관계를 가지는 복잡 오브젝트-합성 구조에도 이 개념을 일반화해서 적용가능해진다. 그러기 위해선 매번 오브젝트 팩토리에 DI된 오브젝트를 요청해서 가져오는 것이 아니라, 아예 애플리케이션 전체를 DI로 구성해놓는 것이 훨씬 편하다는 개념을 가지게 된 것이고 이를 편리한 프레임워크로 만든 것이 결국 스프링이었다.

스프링의 특징은 프로그래밍 모델과 설정(configuration) 모델을 구분한 것인데, 프로그래밍 모델은 기본적으로 오브젝트 합성구조를 가지며 생성자,설정자,메소드 심지어 필드 같은 DI 받을 수 있는 메카니즘을 제공하면서 인터페이스 프로그래밍을 적용한 것이고, 설정 모델은 XML, 애노테이션 또는 자바코드 자체를 전략패턴의 클라이언트가 그랬듯이 DI해주는 제3의 존재로 손쉽게 만들 수 있도록 해주는 것이다.

 

정말 다이나믹 (타이핑) 언어는 DI가 필요없을까?

객체 사이의 유연성을 필요로 한다면 모든 객체지향언어는 DI가 가장 좋은 방법이고 다이나믹 타이핑 언어 또한 예외는 아니다. 단지 그 메카니즘 면에서 유연하고, 자동화 면에서는 불편할 뿐이다.

내가 그나마 초큼 아는 다이나믹 타이핑 언어는 루비이니 루비를 가지고 생각해보자.

루비라고 해서 Context가 Strategy를 직접 알고 스스로 만든다면 역시 Strategy에 종속된다. 따라서 생성자를  이용하든 메소드를 이용하든, 제3의 존재가 실제 Context가 사용할 구체적인 객체(그것이 비록 런타임시에 다이나믹하게 바뀌었다고 할지라고 아무튼 사용할 그 놈)와 연결해줘야 한다.

물론 루비는 자바와 달리 타입제한이 없으며 클래스 개념이 매우 유연하다는 장점이 있다. 자바와 같은 매우 엄격한 타입을 체크하는 언어는 인터페이스를 사용해야지만 오브젝트 합성구조가 가능하다. 하지만 루비는 덕 타이핑을 통해서 실제 호출가능한 메소드를 가지고 있기만 하면 언제든지 대체가 가능하다. 심지어 메소드가 없어도 처리할 수 있는 기법도 있다. 따라 루비와 같은 언어는 인터페이스가 필요없다는 것은 맞는 말이다.

또한 아예 클래스 자체를 바꾸거나 변경하는 것도 가능하다. 루비의 클래스는 자바처럼 클래서로더당 한번 읽으면 그만인 읽기전용 정보가 아닌, 유연한 오브젝트이고 클래스 이름은 그 오브젝트에 대한 이름일 뿐이다. 소위 오픈 클래스라고 하는 개념은 기존의 클래스의 구현 내용을 확장하거나 변경할 수 있으며, 인스턴스 단위로도 가능하다. 따라서 목 오브젝트와 같은 각종 테스팅 기법에서 막강한 위력을 발휘한다.

이렇게 다이나믹 타이핑 언어 또는 다이나믹 언어이기 때문에 가지능 장점은 DI의 메카니즘인 인젝션이 훨씬 유연하고 간단한 코드로 가능하다는 것이다. 하지만 그렇다고 DI가 필요없다는 말은 틀렸다. 지금 설명한 그런 모든 작업자체가 DI이다. 테스트 할 때마다 사용할 클래스를 MyObject에서 MyMockObject라고 코드를 뜯어고치지 않을 수 있는 이유는 무엇인가? 바로 이런 저런 방법을 통해서 런타임 의존관계를 제3자가 설정 또는 변경해주기 때문이다. 바로 전략패턴의 클라이언트가 하는 그 역할 그대로이다.

따라서 루비는 코드를 통해서 수동으로 DI하기에 더 할나위 없이 좋은 언어이다. 나도 그런 점이 매우 매력적이라고 생각한다. 하지만 잠깐 코드를 통해서 클래스를 바꿔치기 하거나, 덕 타이핑을 이용해서 유사한 메소드를 정의해서 사용하는 것과 같은 일시적인 DI방법 또는 블록과 같이 코드블록 오브젝트를 전달해서 템플릿성의 코드가 사용하게 하는 기법등에 적용된 DI등만이 전부가 아니다. 스프링과 같이 애플리케이션 전체의 구성자체를, 또는 모든 기술적이거나 프레임워크 의존적인 코드를 개발자 코드에서 제거하는 것과 같은 포괄적인 DI를 위해서라면 별도의 툴이나 프레임워크를 사용하는 것이 필요하다. 몰랐는데 루비에도 DI 프레임워크라고 이름을 달고 나온 것들이 제법 많이 있고, 사용하기도 하는 것 같다. 파이썬용으로 Spring.Python인가 하는 것도 만들어진 것도 있다고 한다.

 

그럼 루비에서는 DI 프레임워크를 왜 많이 사용하지 않을까?

그건 나도 모른다. 사실은 자바에서도 DI 도구없이 이미 10년 이상을 개발해왔고 지금도 역시 그 없이도 잘만 개발하는 곳이 넘처난다. 왜 그럴까? 구지 필요성을 못느껴서일까? 무지해서?

아마도 DI 프레임워크 없이도 코드 레벨에서 DI를 잘 사용했기 때문일 수도 있고, 객체지향언어의 유연성에 그다지 목매지 않아서 일 수도 있다. 의존관계가 바뀌면 그냥 코드를 뜯어고치거나, 애플리케이션 로직과 기술 코드를 짬뽕해서 쓰거나 그래도 그럭저럭 개발하고 사용할 수 있으니까 그럴 것이다. DI의 설정을 만드는 것보다 코드로 필요할 때(아마 테스트?) 적절한 변경만 해서 사용하는게 낫다고 생각하면 그러면 될 것이다.

그런면에서 나는 DI 프레임워크를 꼭 사용해야 한다고 주장하고 싶지는 않다. 자바를 가져다 객체지향언어가 아닌 C처럼 개발하든, 루비를 마치 스태틱 타입언어처럼 사용한들 뭐 어쩌겠는가. 안돌아가는 것도 아니고 말이다. 나야 DI가 편하고, 그게 없으면 답답해서 개발을 못하겠으니 쓰는 것이고, 수 많은 자바개발자들과 DI에 매료된 사람들 또는 DI를 스스로 먼저 나서서 지원하는 각종 프레임워크 개발자들도 각자 뭔가 맘에 드는게 있으니 쓰는 것일 뿐이겠지.

DI원리를 적용하거나 말거나, DI를 프레임워크를 이용해서 개발하거나 말거나 상관하고 싶지는 않은데, 그래도 "우리는 꾸진 자바와 달리 DI를 언어가 해결해주니까 필요없다"는 얘기는 제발이지 안했으면 좋겠다. 내가 아는 한 DI를 언어차원에서 지원해주는 것은 NOOP 밖에 없다.

다이나믹 타이핑 언어에서의 DI에 관해서 나름 균형있게 잘 소개한 http://onestepback.org/articles/depinj/ 이라는 글도 흥미롭다. 물론 여기도 DI라는 용어를 DI프레임워크라는 것과 별 구분없이 사용하는 듯 하다. 재밌는 내용은 Ruby에서 DI가 많이 사용되지는 않는다고 하면서 그 이유로 다음 두가지를 들고 있는 것인데,

  • The dynamic nature of Ruby reduces the payoff
  • DI really becomes useful in really large projects
    • There aren’t that many really large Ruby projects.

언어적인 특성 어쩌고 하는 것들은 사실 대부분 테스팅(주로 mock)에 관한 것이라는 점도 좀 아쉽긴 하지만 아무튼 편하긴 하니가 그렇다 치고, 두번째 내용이 흥미롭다. DI는 대규모 프로젝트에서 유용해진다는 점이다. 그런데 루비 프로젝트는 그런 큰 규모의 개발이 별로 없다고. 대규모의, 그래서 많은 팀과 개발자들이 협력해야 하고, 그래서 어쩌면 평균적인 개발자들의 수준은 하향되는 구조에서 개발자들의 고급 코딩 능력에 의존될 가능성이 높은 루비와 같은 언어는 그다지 매력적이지 않은가보다. 자바라고 크게 다를 바는 없다고 생각되지만.

 

자바에서 DI가 더 인기있는 이유에 대해서 최신 DI경향을 가지고 생각해볼 필요가 있다고 생각한다. 원래 스프링의 DI설계모델은 XML이었다. XML에 모든 DI정보가 담겨 있었다. 그런 것이 이제는 구글주스의 등장과 스프링의 발전, 애노테이션 기반의 표준 모델에 의존적인 씸의 등장 등에 따라서 점차로 autowiring이라 불리는 자동DI쪽으로 설정방법이 많이 기울어져 가는 것 같다.

스프링의 @Autowired는 이전 XML에서 지원하던 byType방식의 autowiring과 달리 fine-grained된 타입 방식으로 DI후보를 자동으로 찾아준다. 매우 매력적이고 편하며 간단하다. XML조차 아예 필요없을 수도 있다. 과도한 설정파일 관리에서 해방되면서도 지능적으로 DI가 가능하다는 점 덕분에 최신 스프링버전의 적용 프로젝트들은 거의 대부분 애플리케이션 코드 레벨은 @Autowired를 비롯한 애노테이션 방식의 자동-와이어링 기법을 이용한다.

그런데 바로 이 @Autowired와 같은 자동 DI 후보를 찾는 기술은 스태틱 타입 언어이기 때문에 가능한 것이다. 자바와 같은 스태틱 타입언어는 툴링(tooling)에서 큰 장점이 있어서 다이나믹 타입 언어보다 우위에 있을 수 있다는 로드 존슨의 InfoQ 패널토의 시간의 발언이 생각난다. 다이나믹 타입언어는 DI후보를 자동으로 찾기가 매우 어렵다. 너무 유연해서 다이나믹하게 만들어지는, 또는 필요한 메소드 호출만 가능하면 주입이 가능한 언어이니 프레임워크가 자동으로 DI후보 하나를 찾아서 대입해준다는 것이 근본적으로 불가능하다. 루비 지원 IDE들이 리팩토링에 취약한 것도 마찬가지 이유이다.

그래서 다이나믹 타입 언어는 코드에 의한 adhoc DI에는 강력하지만, 반면에 애플리케이션 전반에 걸친 DI설정을 손쉽게 적용하기 위한 autowiring에는 취약한 것이다. 장점과 단점을 다 가지고 있는데, 본격적인 DI 프레임워크 적용으로 보자면 단점이 더 크다. 그래서 XML과 같은 명시적인 DI설정을 가지는 구조가 그나마 유일한 방법이고, 그런 부가적인 설계정보를 가진 프로그래밍 모델을 굳이 필요로 할 만큼 DI에 푹 빠지지 않는 이상 많이 사용하지 않는 것이 당연할지도 모르겠다.

 

그렇다면 과연 자바는 DI를 잘 사용하고 있을까? DI와 DI프레임워크(도구)를 구분해서 생각해야 한다는 점은 사실은 이런 위험성이 있다고 볼 수도 있다. DI프레임워크를 사용하지만 DI는 하지 않는 그런 문제도 있다는 말이다. DI는 객체지향설계의 유연성을 극대화 하기 위한 기법이다. 하지만 DI프레임워크를 사용하면서도 여전히 제대로된 객체지향설계는 없고, 기계적인 3-tier 컴포넌트에 DI 적용이 전부라면, 사실은 DI를 제대로 하고 있다고 말할 수 없다. 스프링을 백날 써봤자, 제대로된 클래스 설계와 적절한 분리, 유연한 합성구조 설계 등이 뒤따르지 않는다면 스프링의 철학과 목적에 부합하는 사용을 하고 있지 않다는 얘기다.

고객관리 서비스 클래스면 오로지 그 하나의 클래스에 자주 변하는 정책과 각종 인벤트시 처리와 잘 변하지 않는 고객관리의 기본 로직을 한데 뭉텅이로 넣어도 된다고 생각하고, SpringMVC의 컨트롤러는 스프링이 제공해주는 확장포인트 외에는 아무리 반복적이고 모듈화가 가능하며, 자주 변경되는 코드가 들어가도 그냥 복사해서 사용하고 마는 것이라면 그게 무슨 스프링이고 DI적용이란 말인가. 그런식으로 만들어 놓고 스프링 컨설팅을 한다고 다니는 사람들을 보면 정말 씁쓸하다.

 

DI의 특별한 적용사례의 한가지인 AOP(AOP를 포함해서 스프링의 모든 기술은 다 DI가 그 원리이다)에 대해서도 할 얘기가 있지만 너무 길어져서 여기서 끝.

22 Comments

찬욱October 1st, 2009 at 2:41 pm

이런 장문을 써주시니, 집에 가서 읽어야겠어요!

cavinOctober 1st, 2009 at 2:51 pm

아 너무도 훌륭한 식견과 글입니다.
덕분에 몰랐던 부분 많이 배우고 갑니다. :)

- 덧붙여 –
까만배경 흰폰트가 눈이 아파서 계속 rss로 보게 되더군요. OTL

TobyOctober 1st, 2009 at 4:26 pm

cavin/ 안그래도 스킨이 여러모로 맘에 안들어서 다시 바꾸려고 했습니다. 이제 좀 보기 편하실거에요.

영회October 1st, 2009 at 4:40 pm

커휘샵에서 했던 이야기를 장문으로 옮겨뒀구만.. 잘했어.. ‘제3의 존재’까지 잊지 않고 말이야.
근데 이 긴 글에 무슨 답변을 달란거야?

서두에 제기했던 오해가 우리나라 개발자 사이에서 흔히 나오는지에 대해서는 할 말이 있긴 한데…
쩝.. 재미 없는 이야기라 말이지

박성철October 1st, 2009 at 7:21 pm

아니 한국에서 영지 버섯 달인 물에 밥 말아 먹고 산삼 뿌리 무침을 반찬이라도 드셨나? 이렇게 멋지고 아름다운 글을…

이글 읽기 시작한게 점심 먹고인데 지금 오후 6:14… 중간에 회의도 있었고 일도 했지만 나중에는 집중이 안 될 정도로 힘드는데 말이지…

대단하삼

아! 긴 원론 성격의 서설과 결론에 대체로 동의하고 특히 결론은 내 생각과 거의 일치하는 것 같아. 이렇게 다른 사람이 같은 생각을 해주니 뭔가 확신 같은게 드네. 땡스.

TobyOctober 1st, 2009 at 7:27 pm

박성철/ KSUG 회장님이 친히 방문해주셨네요. 오늘 인수인계 작업을 했다는 전 회장 Y군에 이어서 글이 달리니 비교가 되어 재밌군요.

KevinOctober 2nd, 2009 at 1:08 am

저 발언에 대해 이렇게 장문의 글을 쓰시다니,
Toby님은 정말 친절하신거 같습니다. :)

조슈아 블록이 얘기한것중에,
자신은 다이나믹 언어에 대해 반대하지 않는다고 하면서
“언어를 도구로 보자면, 망치나 렌치의 사용에 반대할 이유가 없다.
하지만, 렌치를 망치처럼 쓰는것은 손을 다치거나,
못을 나무에 제대로 박지 못할수도 있기 때문에
반대한다”는 얘기가 있었죠.
만드는 어플의 규모가 커지면, statically typed language가
유용할수 밖에 없다는 얘기와 함께요.

뭐든 필요에 따라 쓰면 된다는게 제 생각입니다만,
자신이 망치의 존재를 모르거나 망치에 대해 잘 모른다고 해서
못 박는데, 렌치를 사용한다면……

자신이 좋아서 쓴다면, 그것에 대해 뭐라고 할수는 없겠지만요.

static typing의 장점을 고작 철자 오류 정도 찾는걸로 생각하는
사람들도 있는거 같던데요. :D

dynamic typing에 기초한 테스팅에서의 장점은…
글쎄요. 뭐가 중요한걸까요?
우리가 사용하기 위한 프로그램 제작을 위해서 프로그래밍을 하는걸까요?
아니면 제작한 프로그램을 테스트 하려고 프로그래밍을 하는걸까요?
테스트가 중요하긴 하지만, 이게 근본 목표가 되면,
뭔가 주객이 전도된거 아닌가 하는 생각도 듭니다.

역시 조슈아 블록이 얘기한적이 있는 내용입니다만,
“테스트로는 프로그램이 제대로 돌아간다는걸 증명할수 있는게 아니고,
프로그램이 제대로 돌아가지 않는다는것을 증명할수 있다”고 했죠.

물론 조슈아 블록이 테스트가 중요하지 않다는 얘길 한것은 아닙니다.
자신의 트위터에 “테스트 없이 코드를 작성하는것은
손도 안 씻고 수술을 하는것과 같다.” 는 얘길 했을 정도로
테스트가 중요하다고 얘길 했습니다.
다만, 테스트가 중요하지만, 이걸로 찾기 쉬운게 있고,
아닌것도 있다는 얘길 하면서 이것에만 의존 할수는 없다는 얘길 한거죠.

제가 볼때 다이나믹 언어의 장점은 어플 규모가 커질수록
단점으로 변하는거 같습니다.

static typing의 경우 초기에 type정의를 위한 프로그래밍에
시간이 더들겠지만, 문제를 디자인 할때 발견하기 쉽겠죠.
프로그램상의 문제는 늦게 발견하면 발견할수록 처리 비용이 더 커지구요.
dynamic typing의 경우 프로그래머가 프로그램시
자신이 무슨 프로그램을 처리하는지 static typing을 쓸때만큼 쉽게
생각하기 힘들다는 문제가 있겠죠.
프로그램 규모가 작으면 모르겠지만…
역시 커지면…

PHP를 처음 배우고 옥션을 하나 만든적이 있는데, 이틀 걸리더군요.
일반회원 기능 하루, 관리자 기능 하루 이렇게 걸리더군요.
물론 아주 세세한 기능까지는 아니지만, 옥션이 기본적으로 갖춰야할
상품 검색, 정보보기, 비딩 등등의 기능은 다 갖췄습니다.
진짜 빨리 제작이 가능하길래 놀라긴 했는데,
시간을 두고 보니까… 역시 규모가 커지면, static typing만큼
전체와 각 부분의 디자인을 제대로 신경 쓰면서 만들기 힘들고,
처음 만들고 전체 프로그램에 대한 이해를
완벽하게 한 상태에서는 괜찮지만,
시간이 지난후 유지 보수를 하려고 했을때,
static typing을 이용했을때 처럼 쉽게 유지보수가 가능하다거나
만든 프로그램을 다시 이해 하려고 했을때 쉽지 않다는걸 느꼈습니다.

원래 사람이란게 뭔가 정의 하고 연관된 것들끼리 묶어 놓고 분류해 놨을때
더 쉽게 제대로 일을 처리 할수 있어서…
어차피 프로그램, 혹은 객체에 대해 알긴 알아야 하는데,
이걸 언어의 type system에 맡겨서 좀더 편하게
문제 없이 다루는것과, 프로그래머가 다 이해하고 있어야 하는것과…
이미 언급한데로 프로그래머가 다 머리속에 넣고 있는것도
프로그램 규모에 따라 한계가 있죠.

제가 혼자 만든 프로그램의 경우 파일이 600개 좀 넘는 정도로 작은 규모입니다만,
이정도도 몇달 만에 보면, 600개 파일 하나 하나가 뭘 하는지 다 기억하고 있기 힘들죠.
다행인것은 Java로 작성한거라서 Eclipse같은 도구의 도움으로
쉽게 전체 디자인을 다시 머리 속에 그려내거나
각각의 기능 추적이 상당히 수월하다는 점입니다.

반면, 작년말쯤 수십개의 파일로 구성된 PHP어플의 유지보수를
부탁받은적이 있는데, 물론 프로그램 자체가 잘 디자인 되지 않은점도 있지만,
각 부분부분에 대해 기능 추적도 용의하지 않을뿐더러,
이부분을 바꾸면 어디어디서 영향을 받는지도 찾기가 쉽지 않더라구요.

그래서 아주 심각하다고 판단된 부분은 수정을 꺼리게 되고,
주어진 시간이 얼마 없었으므로, 어쩔수 없이…
기능을 새로 만들어서 추가 하는식으로 굉장히 지저분해 지더군요.

뭐, 이게 좋다, 저게 좋다 그런 얘기는 아니고,
필요할때 필요한거 쓰면 되는거겠죠. :)

DI의 경우는, 명칭이 늦게 정해졌지 사실은 우리 곁 (?) 에 꽤 오래전부터
있어온게 아닌가 싶습니다. 가령, 도입된지 꽤 오래된 JDBC만 봐도…
Sun에서 Specifications 만 정하고 (지금은 JCP?), interface들만 내놓은 상태에서
각 database vendor별로 이 interface에 기초한 implementation을
제작해서 내놓는 형태라서, 프로그래머는 interface에 맞춰 프로그램을
짜면 되고, 각 vendor별 implementation details에는 크게
신경을 쓰지 않아도 되죠 (각, DB별 SQL등의 차이는 제외).

처음 Java를 배울당시 알게 된거라서, 나중에 DI에 대해 들었을때는,
별로 신선한건 없었습니다만, Spring이 DI를 손쉽게 사용할수 있는
light-weight container를 가진걸 보고 참 맘에 들었습니다.
저는 reflection 을 이용하는 factory를 만들어서 썼었는데,
지금의 DI container에 비하면 아주 초보적인 수준이었는데
스프링에서 이걸 깔끔하게 해결한걸 보고 자연스럽게
이걸 안 쓸수가 없더라구요.
암튼 스프링을 필요에 의해서 쓰게 된거고, 쓰고 있고,
단순히 인기가 있다고 쓰는건 아닙니다. :)

“다만 자바에는 전달가능한 메소드(펑셔널 오브젝트, 코드블록, 프로시져 등등 머라고 부르던 간에)라는 개념이 없으므로”
이부분은,
“자바는 first-class functions를 지원하지 않으므로,”
혹은
“자바에서 method는 first-class object (혹은 first-class citizen) 이 아니므로,”
라고 해도 될것 같군요. :)

이건 Java가 Closure를 지원하면 anonymous class 같은 nested class없이
더 쉽게 해결 가능 할것 같은데… Java 7에서는 제외된 모양입니다.
아무래도 조슈아 블록이 우려하는데로 기존 Java type system과의 균형도 생각해 봐야겠죠.

TobyOctober 2nd, 2009 at 5:49 am

Kevin/ Closure까지는 아니더라도 anonymous inner class를 작성하는 표기법의 단순화 정도는 지원해줬으면 좋겠는데, 그것도 채택이 안됐나보군요.

KevinOctober 2nd, 2009 at 6:48 pm

일단 JDK 7에 들어가기로 Project Coin에서 최종 채택된거 중에 기억나는게
저도 굉장히 기다려온 겁니다만,
automatic resource management가 있구요.
diamond 와 Strings in switch 그리고 varargs method 에 대한거랑…
다이나믹 언어에 대한게 있었던 걸로 기억이 되는군요.

automatic resource management는 조슈아 블록이 제안한대로 될것 같은데요.
이미 아시겠지만,
C#에서 IDisposable을 구현한 class를 using statement와 사용하면
해당블록이 끝날때 자동으로 Dispose method를 호출해서 자원해제를 하는것과
비슷한거 같더군요.
Java에서는 Disposable interface와 close method,
그리고 이미 가지고 있는 try block를 쓸것같고,
C#보다 나은점은 한번에 여러 resource를 동시에 열수 있다는거겠죠.
try (ResourceOne resourceOne = new ResourceOne(file);
ResourceTwo resourceTwo = new ResourceTwo(anotherFile))
{
}
//블록 끝에서 자동으로 close method 호출
이런식으로요.
diamond는 대충
Map<String, List> idToObjectMap = new Map<String, List>();
이거를
Map<String, List> idToObjectMap = new Map();
이렇게 쓸수 있게 해주는것 같구요.

Strings in switch는…
이건 IBM에서 일했던 software engineer 와 Ph.D 과정에 있는 호주 친구 한명에게
얘길 해봤는데, 재밌게도 서로 상반되는 반응을 보이더군요.
처음분은 그게 왜 진작에 안 들어갔는지 모르겠다는 얘길 하셨고,
나중 친구는 별로 안 좋아하더라구요. :)
이친구는 학생들의 Java프로그래밍 과제 채점을
바이트코드 분석을 통해 프로그램의 디자인이 제대로 됐는지 아닌지를
평가하는 스크립트를 작성해서 자동으로 처리하고,
Google의 pagerank algorithm을 이용해서
한꺼번에 많은 양의 블록을 없애면서 테트리스 게임을 자동으로 플레이하는
일종의 인공지능을 만든 친굽니다. (자바로요)
http://www.ryanheise.com/tetris/tetris_artificial_intelligence.html
이거 보시면 플레이 하는 데모를 보실수 있습니다.
테트리스 게임 자체도 이친구가 만든거구요 (역시 자바).

암튼 String을 switch에서 사용할수 있게 됐다는 말에
이친구가 가장 먼저 던진 질문이
switch에서 String검사를 할때
이게 identity 체크인지 equality 체크인지였습니다.
저야 관련 제안서를 들여다 보지 않아서 모르는데다가,
strings in switch에 대해
크게 신경을 쓰지 않았던 터라…
identity 체크면 별로 쓸모가 없겠다는 생각에
equality가 아니겠냐는 대답을 했구요.

이친구 불만이
일단, 사용자가 이게 identity인지 equality인지
제대로 모르고 잘못 사용할수도 있다는점과,
(근데 이건 프로그래머가 언어에 대해 제대로 알지 못하고
쓰는건데 if문을 써도 똑같은 문제가 발생할수 있겠죠).

다른 하나는, switch의 장점인, case 값의 범위가 적당히 정해진 경우
expression 값을 index처럼 이용해서 해당 값으로 바로 점프를 하는
tableswitch를 사용하도록 컴파일 되기 때문에
if 문을 사용해서 값을 하나 하나 비교하는거보다
훨씬 빠르다는점을 String의 경우 제대로 살릴수 없을것 같다는 거더군요.
(tableswitch뿐 아니라 lookupswitch도 if 문 여러번 돌리는거 보다는 빠르겠죠).

그말 듣고 생각해 보니, switch의 장점은 입력값이 전부 int인데서
가능한것이 떠올라서, (char나 byte, short 도 switch 에서 전부 int로 변환되니까요)
제안서를 찾아보니, 아니나 다를까 단순히 equals 메소드만 사용하는건 아니더라구요.
tableswitch는 안되도 lookupswitch를 쓰는것처럼,
일단 String을 int로 바꿔주는 방법이 몇개 (예. hashCode(), (long)s.hashCode<<32
) + s.length()) 제안되어 있고,
이게 같은 값일 경우를 고려해서 최종적으로 equals()를 이용하는 방법을
제안했더군요. 이러면 일단 처음에는 int를 이용해서 바로 점프하고 나서
equals를 이용해서 한번만 더 비교하면 되니까 if-then-else를 여러번 거치는거보다
속도는 더 빠르게 나올것 같습니다.

암튼 이거 보며, 명색이 프로그래머라면서 자기가 쓰는 언어에 어떤
변화가 생기는지 알고 있으면서도 제대로 알아보지 않고,
문법은 좀더 편하게 변해도 내부적으로는
대충 equals 비교를 하겠거니 생각한 제 자신을 반성하게 되었습니다.
거기에 역시 지식은 나눌때 더 많이 돌아온다는걸 다시한번 느끼게 됐고,
그래서 Java 7 말 꺼낸김에 Toby님 블로그 방문하는 분들과
공유하고자 이렇게 주절 거려봅니다…@_@;

TobyOctober 2nd, 2009 at 8:38 pm

Kevin/ API레벨에서는 살펴보지 않았지만 Java7은 그다지 매력적으로 느껴지는 변화는 없는 것 같군요. 그나마 관심이 가는 것은 바이트코드 레벨에서 다이나믹 타입 언어를 직접 지원할 수 있는 방법이 추가될 거라는 점 정도.

String case는 이제서야 지원된다는 사실이 부끄러울 뿐입니다.

ologistOctober 3rd, 2009 at 10:20 pm

오랫만에 좋은 아티클과 코멘트들을 읽고 갑니다. 역시 Toby님의 글은 사람을 땡기는 뭔가가 있네요~^^
코멘트 중에 diamond도 좋군요!

디키썬October 5th, 2009 at 12:34 am

잘 읽었습니다 ^^ DI에 대해서 개념정립이 확~ 되네요.
그런데 다이나믹 타입은 DI처럼 이용할 수 있지만, 그렇다고 DI라고 부르는 것은 조금 다릅니다.

흡사 C에서 C++의 class를 보며 ‘class가 structure이므로 C++에서는 structure가 필요없다’ 라는 느낌과 비슷합니다. 실제 필요없을 수 있습니다 :-) 하지만, 그렇다고 class가 structure는 아니듯이

다이나믹 타입이 런타임 relation을 맺어주는 면에서 DI이기도 하지만, Context차원에서가 아니라 언어차원이라는 면에서 같다고 말하기는 좀 어렵습니다. (그렇다고 ^^ DI를 이용해서 다이나믹 타입을 구현한 것도 아니고)

그러므로 전 ^^ 글의 명제인

‘DI는 디자인패턴과 마찬가지로 자바와 같은 스태틱 타입 언어의 단점 또는 한계 때문에 필요한 것일 뿐이고, 루비나 파이썬 같은 다이나믹 타입 언어에서는 언어차원에서 DI기능을 손쉽게 해결해주기 때문에 DI가 필요없어서 사용하지 않는다’

는 참이라고 생각합니다~

박성철October 5th, 2009 at 12:25 pm

디키썬 / 비슷한 말은 많이 들었습니다. 그런데 구체적으로 어떻게 DI를 언어적으로 지원 하는지 (또는 DI의 필요성을 없애는지) 그 얘를 아무리 찾아도 못 찾겠습니다. 그러니까 class나 structure의 유사성 정도라도 비슷한 뭔가가 있어야 할 것 같은데 나름 고민하고 여러 사람에게 물어보았는데 “정적 언어에서나 해당하는 걸 동적 언어에 강요하지 말라”는 정도의 답만 들었습니다. 전 강요하려는 게 아니라 그냥 궁금한 것 뿐인데…

October 5th, 2009 at 2:23 pm

저는 동적언어를 잘 아는게 없어서 본 글의 주제에 대해서는 생각이 별로 없고..그저 후속편(DI로 풀어헤치는 AOP)이 기대되네요.

“스프링을 백날 써봤자, 제대로된 클래스 설계와 적절한 분리, 유연한 합성구조 설계 등이 뒤따르지 않는다면 스프링의 철학과 목적에 부합하는 사용을 하고 있지 않다는 얘기다. ”

이 말씀 명언이십니다!!! 노력하겠습니다~

디키썬October 5th, 2009 at 2:45 pm

박성철 / 제가 첨에 들었던 예가 부적절했던 것 같습니다.
흔히 C에서도 객체지향이 가능하다며, structure에 함수포인트를 사용하신 것을 보신적이 있으실 겁니다. 하지만 이를 객체라 부르지는 않습니다.(불러도 상관은 없지만 엄밀히 말하면 이는 C에 structure일뿐이지요.)
다이나믹 언어에서의 타입변경은 동적타입일뿐 DI라고 부르지 않습니다.(물론 이것을 DI로 바라보아도 틀린 것은 아닙니다.)

즉, 저는 ‘다이나믹 언어에서 DI가 언어적 차원에서 지원되므로 DI가 필요하지 않다.’ 이 명제에 대해 참이라고 생각하는 것입니다
물론 정밀하게 이야기하면 ‘다이나믹 언어에서는 DI가 언어적 차원에서 지원되므로 별도의 DI기능(DI프레임워크일수도 있고)은 필요하지 않다’
가 되겠지요^^

다이나믹 언어에서의 동적타입을 DI라 부르는 것 자체가 FALSE다 라는 이야기는 아닙니다^^.

박성철October 6th, 2009 at 2:21 pm

디키썬/ 저랑 DI에 대한 정의가 좀 다른 것 같습니다.

전 DI가 동적 타입과 별 상관이 없다고 생각합니다. 스트롱 타입이건 덕 타입이건 어떤 객체가 다른 객체에 의존하고 있을때 그 의존이 되는 객체(의 참조)를 외부에서 누군가가 알려주느냐 아니면 의존하는 객체가 그것을 직접 생성하거나 factory 등을 사용해 끄집어 오느냐의 문제라고 생각합니다.

그리고 시스템 전체적으로 (이런 저런 S/W 컴포넌트를 연결해) 시스템을 구성하는 역할을 맡은 객체가 있다면 DI Framework라고 할 수 있고요…

서상현October 14th, 2009 at 7:10 pm

DI Framework들은 결국 크게 보면 Java reflection을 사용하는 것인데, Java에서는 reflection을 사용하기 힘들지만 동적 언어에서는 모든 호출이 reflection으로 이루어지지요. “다이나믹 언어에서는 DI가 언어적 차원에서 지원된다”는 몰라도 “다이나믹 언어에서는 reflection이 언어적 차원에서 지원된다”가 참이라는데는 이견이 없으실 줄로 압니다. 그런데 reflection이 언어적 차원에서 지원될 때 DI를 위한 framework가 필요한지는 개인적으로 의심스럽습니다.

TobyOctober 14th, 2009 at 8:33 pm

서상현/ 저는 DI 프로그래밍 모델을 본격적으로 추구한다면 여전히 필요할 것이라고 생각합니다.

그리고 DI 프레임워크는 자바가 언어차원에서 리플렉션 지원을 안해주기 때문에 그것을 땜빵해주는 기술이 아닙니다. DI가 다이나믹 언어의 리플렉션 기능이나 다이나믹 타이핑, 메타 프로그래밍 등을 흉내내기 위해서 만들어진 기술이 아닌데 왜 자꾸 자바의 언어적인 특징과 DI를 자꾸 결부시키는지 이해하기 힘드네요.

다이나믹 언어가 리플렉션이 간단히 된다고 하는 것과 DI라는프로그래밍 모델을 본격적으로 사용하려고 할 때 프레임워크가 필요하다는 것 사이에 무슨 관련이 있는지 저는 잘 모르겠습니다.

서상현October 14th, 2009 at 10:55 pm

이해를 잘 못하시는 것 같으니 간단히 다시 설명해 드릴게요. 라이브러리를 쓰는 것이 항상 좋은 것이 아니고, 라이브러리는 라이브러리를 배우는 데 드는 비용보다 라이브러리를 써서 얻는 이득이 커야 쓰는 것이지요? 저는 리플렉션이 DI라는 이야기를 하는 것이 아니구요, 리플렉션이 쉬우면 DI를 ad hoc하게 하는 것이 쉽기 때문에 DI를 하는 라이브러리를 쓸 동기가 감소한다는 것입니다. 직접 쓰신 글에 있는 “dynamic nature reduces the payoff”가 무슨 뜻인지 고민해 보시죠.

서상현October 14th, 2009 at 11:13 pm

Martin Folwer의 용어를 쓰자면, service와 service의 configuration을 분리한다는 것인데 (프로그래밍 모델과 설정 모델, 같은 이야기입니다) 제가 보기에 configuration은 scripting과 통하는 것입니다. 저는 데이터로 이루어지는 것을 configuration, 코드로 이루어지는 것을 scripting이라고 부르지만 DI쪽 용어를 보면 configuration과 scripting을 뭉뚱그려 configuration이라고 말하더군요.

그런데 scripting은 스크립트로 해야지요. 코드 재사용을 증가시킨다고 할 때 코드 수정 없는 재사용은 service에 해당하는 것이지 service의 configuration에 해당하는 것은 아닌데, 예컨대 service도 파이썬으로 짜고 service configuration도 파이썬으로 한다고 후자를 수정하는 것을 “그냥 코드를 뜯어고친다”고 표현하는 것은 잘못된 것입니다. configuration이 scripting이라면 이를 해주는 assembler는 다름아닌 script interpreter인데, 동적 언어가 DI를 내장하고 있다는 것은 바로 이러한 의미입니다.

TobyOctober 15th, 2009 at 12:05 am

서상현/ 제가 언제 “service configuration도 파이썬으로 한다고 후자를 수정하는 것을 ‘그냥 코드를 뜯어고친다’고 표현” 했나요? 뭔가 제 글을 오해하신 것 같군요. 자바의 경우도 DI의 설정모델을 평범한 자바코드로 만들기도 합니다. 그걸 고쳤다고 유연성 없이 코드를 뜯어고친다고 하지 않습니다. 마치 제가 그런 식으로 얘기한 것처럼 말씀하시는 것 같은데… 억울해요~

자바도 configuration을 자바코드 만들고 이를 JVM이 동작시키게 해서 설정과 프로그램을 분리하는 DI모델로 만들 수 있습니다. 그렇다면 역시 자바언어도 “DI를 내장하고 있다는 것”이라고 해도 되겠네요? 물론 자바는 인터페이스 미리 정의하고 써야 합니다. 상대적으로 불편하죠. 그래도 언어가 지원 안하는 건 아니자나요?

다이나믹 언어가 DI방식의 프로그래밍 모델을 (이름이 맘에 안들면 뭐 DI라고 안해도 그만이지만) 쉽게 적용할 수 있다는 것 잘 압니다. 복잡한 시스템 구성을 위해서가 아니라면 XML설정파일을 이용하는 프레임워크 따위 없어도 된다는 것도 잘 압니다. 근데, 그건 자바도 마찬가지 거든요? DI 프레임워크 따위 없어도 자바로 DI가 추구하는 개념을 잘 활용해서 얼마든지 많은 애플리케이션을 개발해왔습니다.

다만 보다 쉽고 폭넓은 활용을 위해서, 또는 코드로 설정부분을 만드는 것의 번거로움 내지는 친숙하지 않음 때문에 XML이나 애노테이션 데이터를 사용하는 DI프레임워크가 발전한 것 뿐이고요. AOP와 같이 보다 복잡한 DI활용이 실전에서 많이 인기를 끌었던 것도 한가지 이유일테고요.

제가 불만인 것은 자바언어가 뭔가 후져서 (처음 글에서 쓰신 것처럼 리플렉션이 어려워서?) 복잡한 DI 프레임워크를 사용해야만 하는 것처럼 말하는 것이죠. 저는 여전히 DI와 DI프레임워크는 프로그래밍 방법의 문제이지 언어의 문제는 아니라고 봅니다. 그 자체를 언어차원의 우월성을 가지고 비교한다는 것은 이해가 되지 않습니다.

다이나믹 언어는 DI프레임워크를 훨씬 간단하게 구현할 수 있다는 점, 또는 구지 프레임워크화 하지 않고 코드 레벨에서 DI가 추구하는 유연성 있는 오브젝트 설계와 설정(스크립팅?)을 잘 분리할 수 있다는 점에는 물론 동의합니다. 다만 타입추적을 통한 자동 설정정보 생성기능과 같은 스태틱 언어의 장점을 잘 살린 최신 DI프레임워크 기법으로 보자면 불리한 면도 있어서 DI기능 구현이라는 면에서 “장단점이 있다”고 생각합니다. 그래도 근본적인 장점이 많다고 인정합니다.

영회October 29th, 2009 at 9:45 am

misko.hevery 가 관련 글 썼네.

misko.hevery.com/2009/10/25/automatic-dependency-injection-in-the-land-of-dynamic-languages/

Leave a comment

Your comment