Extremely Agile/UML2007. 10. 29. 11:01
I. 컴포넌트와 클래스 라이브러리

컴포넌트가 인터페이스를 갖도록 다이어그램을 그리는 것은 간단합니다만, 그것을 실제로 구현하는 것은 조금 어렵습니다. 컴포넌트는 잘 구현하면 그야말로 컴포넌트가 되지만, 제대로 구현하지 못하면 컴포넌트인지 클래스 라이브러리인지 알수가 없는 지경이 되거든요.

짐작하시겠습니다만 컴포넌트와 클래스 라이브러리는 많이 다릅니다. 클래스 라이브러리는 그 라이브러리를 쓰는 쪽에서 deploy의 방법을 결정하는 반면, 컴포넌트는 이미 고정되어 있는 상태라는 것이죠. 클래스 라이브러리의 경우에는 쓰는 쪽에서 어떻게 쓰느냐에 따라서 그 인스턴스들이 어떻게 만들어지고 이용되는지가 굉장히 달라질 가능성이 있습니다만, 컴포넌트의 경우에는 그 컴포넌트 단위로 배치가 이루어지기 때문에 컴포넌트 안에 속한 객체들이 만들어지고 이용되는 과정이 정형적입니다. 그리고 대부분의 경우, 컴포넌트 내부에서 무슨 일이 벌어지고 있는지는 클라이언트 코드 쪽에서는 전혀 알 필요가 없지요.


II. 인터페이스의 구현

그런데 어떤 코드 집합이 컴포넌트가 될 것인지 아니면 클래스 라이브러리가 될 것인지를 결정짓는 가장 핵심적인 요소는 무엇일까요? 제 생각에, 그것은 그 인터페이스를 어떻게 구현할 것이냐, 하는 문제인 것 같습니다. 다음과 같이 설계된 컴포넌트가 있다고 칩시다.

 component.JPG

이 컴포넌트가 외부에 제공해야 하는 인터페이스는 Request Admission Interface 하나뿐입니다. (앞선 글에서도 언급했었습니다만, 이 인터페이스는 반드시 하나의 Java Interface로 구현될 필요는 없습니다. 필요하다면 여러 개의 인터페이스로 구성될 수 있지요.) 예제 구성을 편하게 하기 위해, 우선은 이 인터페이스가 단 하나의 Java Interface 'RequestAdmissionInterface'로 구현된다고 치겠습니다. 그리고 이 인터페이스를  RequestrAdmissionInterfaceImpl 클래스로 구현했다고 해 보죠. 이렇게 구현된 인터페이스를 클라이언트 코드 안에서 다음과 같이 사용해야 한다면 어떨까요?

RquestAdmissionInterface o = new RequestAdmissionInterfaceImpl(); 
o.foo(); 

클라이언트 코드 안에서 RequesutAdmissionInterface의 구현에 사용된 클래스의 이름에 대해서 알아야 하는데, 그렇게 되면 클라이언트 코드 안에 불필요한 종속성이 발생하게 됩니다. 이런 종류의 종속성은 클래스 라이브러리를 사용할 때에는 흔히 발생하는 일입니다. (List 인터페이스를 구현하는 Concrete List 클래스들을 사용하는 경우들을 생각해보시기 바랍니다.) 위의 코드는 RequestAdmissionInterfaceImpl 이외의 다른 Implementation 클래스를 사용해야 하는 경우 반드시 변경되어야 합니다. (물론 new 를 해 주는 부분의 코드만 변경되면 충분하겠지만 말입니다.) 그러니, 저린 식의 사용방법을 강제하는 컴포넌트는 '그다지 컴포넌트 스럽지 않은' 컴포넌트라고 할 수도 있겠습니다. 컴포넌트를 사용하는 사람은 컴포넌트의 내부구조가 어떻게 변경되느냐에 무관하게 클라이언트 코드를 작성할 수 있어야 하기 때문이죠.

그렇다면 '단순히 인터페이스 클래스들을 만든 다음 그 인터페이스들을 구현하는 것 만으로는' 컴포넌트가 가져야 하는 요건을 충족하는 코드를 작성할 수 없겠군요. 그렇다면 이 문제를 해결하기 위해 참고할만한 기존의 사례는 없나요?

있습니다. RMI쪽이 그 대표적인 사례이죠. RMI는 외부에서 참조할 인터페이스를 구현하는 클래스에 의해 생성된 객체를 모종의 Repository에 등록하게 한 다음, 필요한 순간에 그 객체에 대한 레퍼런스를 가져와서 쓰도록 강제합니다. 이런 구현 방법의 장점은 '아무도 어떤 인터페이스의 실제 구현에 대해서는 알 필요가 없다'는 것이고, 단점은 '해당 인터페이스를 쓰려면 언제나 중앙의 repository를 한 번은 거쳐야 한다'는 것입니다. 물론 이 단점은 단점이라고 보기에는 좀 사소하죠. repository 개념 덕분에, RMI는 그 단순성을 네트워크-wide 하게 확장해 나갈 수 있었으니까요.

그렇다면, 우리도 RMI와 비슷하게 인터페이스 repository 객체를 만든 다음에 나중에 특정한 컴포넌트의 인터페이스가 필요할 때는 거기서 레퍼런스를 가져와서 쓰도록 하면 될 것 같군요. 어차피 컴포넌트는 deploy가 되는 순간에 그 컴포넌트를 대표하는 객체가 생성되어 있다고 봐도 좋을테니, 레퍼런스를 가져오는 것에 큰 문제는 없어 보입니다.

물론 중앙에 repository를 두는 것이 맘에 안 들 수도 있습니다. 그럴 경우에는 어떻게 해야 하나요? 컴포넌트의 외피를 적절한 클래스에 의해 모델링 한 다음, 그 클래스에 인터페이스 객체의 레퍼런스를 가져오기 위한 메소드를 구현하는 것을 생각할 수 있습니다.  

class DecisionSupportComponent {
   ...
   RequestAdmissionInterface getRequestAdmissionInterface() {
      ... 
   }
   ...
}

이렇게 하면 DecisionSupportComponent 객체를 사용하는 쪽에서는 인터페이스의 구현이 어떤 클래스에 의해 만들어지는지는 알 수가 없겠습니다. 그 코드는 getRequestAdmissionInterface() 함수 안에만 있을 테니까요. 모든 컴포넌트들이 이런 표준적인 메커니즘에 의해 인터페이스 레퍼런스를 제공하도록 만들고 싶은 경우도 있을텐데, 그렇게 할 경우 모든 인터페이스 클래스들이 또 다른 인터페이스 클래스를 상위 클래스로 가져야 한다는 문제가 있어 솔루션이 지나치게 복잡해 질 수 있습니다. 그러니 그건 그냥 패스하도록 하죠.


이 글은 스프링노트에서 작성되었습니다.

Posted by 이병준

소중한 의견, 감사합니다. ^^