Eternity's Chit-Chat

aeternum.egloos.com



Framework - 1부 Concept & Principle

재사용과 프레임워크

가장 이상적인 재사용 방법은 추가적인 프로그래밍 작업 없이 이미 존재하는 컴포넌트(component)를 조립하여 시스템을 구축하는 것이다. 그러나 이와 같은 컴포넌트 기반의 레고 블록(LEGO block, 또는 집적 회로IC) 접근 방법에는 분명한 한계가 있다.

다양한 컨텍스트 내에서 컴포넌트를 재사용하기 위해서는 조립 시 다양한 매개변수와 옵션을 설정할 수 있어야 한다. 설정 가능한 파라미터와 옵션의 종류가 적을수록 컴포넌트를 재사용할 수 있는 컨텍스트의 범위가 줄어든다. 반대로 설정 가능한 파라미터와 옵션의 종류가 많을수록 재사용을 위해 기억해야 하는 정보의 양이 늘어나기 때문에 프레임워크를 사용하기가 복잡해진다. 컴포넌트와 관련된 역설은 컴포넌트를 재사용 가능하게 만들려고 하는 모든 노력은 컴포넌트의 재사용 가능성을 감소시킨다는 것이다.

보다 본질적인 문제는 비즈니스 컴포넌트와 같은 대규모의 재사용 가능한 컴포넌트를 개발하는 것이 거의 불가능하다는 사실이다. 설정 가능한 파라미터와 옵션이 다양해야 한다는 것은 소프트웨어가 수용해야 하는 요구사항의 스펙트럼이 다양해야 한다는 것을 의미한다. 여러 애플리케이션에 대해 코드를 수정하지 않고도 재사용 가능할 정도로 유사한 요구사항을 발견하는 것은 예외에 가깝다.

이 논쟁의 핵심은 소프트웨어 다양성이라 불리는 주제에 있을 것이다. 만약 여러 프로젝트나 애플리케이션 도메인 사이에 비슷한 문제가 충분히 많이 존재한다면 컴포넌트 기반의 접근방법은 결국 효과가 있을 것이다. 그러나 많은 사람들이 의심하는 바와 같이, 애플리케이션과 도메인의 다양성으로 인해 두 가지 문제가 아주 비슷한 경우가 거의 없다면, 가장 기본이 되는 공통적 작업만이 일반화될 수 있을 것이고, 이것이 프로그램 코드에서 차지하는 비율 또한 매우 작을 것이다.

- Robert L. Glass, 우리가 미처 알지 못한 소프트웨어 공학의 사실과 오해 

컴포넌트 재사용이 비현실적인 접근방법이라면 컴포넌트를 개발하는 동안 축적된 경험과 지식을 재사용하는 방법을 생각해 볼 수 있다. 컴포넌트 자체를 재사용하는 것은 코드 재사용(code reuse)을 의미한다. 이에 비해 경험과 지식의 재사용은 설계의 재사용(design reuse)을 의미한다. 언어나 플랫폼과 같은 다양한 제약으로 인해 적용 범위가 제한되는 코드 재사용에 비해 설계를 재사용할 수 있는 범위는 무한대에 가깝다. 또한 설계의 재사용은 프로젝트 초반부터 적용 가능하기 때문에 코드 재사용보다 투자 대비 효과가 더 크다.

설계를 재사용하는데 있어 가장 큰 어려움은 재사용 가능하도록 설계를 표현하고 커뮤니케이션할 수 있는 효과적인 방법을 찾는 것이다. UML을 중심으로 한 그래픽적인 표기법과 패턴을 통해 일정 수준의 의사소통은 가능할 지 몰라도 설계에 담긴 모든 휴리스틱과 근거, 대안을 모호하지 않도록 전달하는 것은 쉽지 않은 일이다.

패턴의 목적은 설계 의도나 근거를 설명하는 것이지 구현 결과를 제공하는 것이 아니다. 따라서 패턴이라는 형식을 통해 표현된 설계를 재사용하기 위해서는 패턴을 구성하는 각 부분에 대응되는 코드를 재작성하는 반복적인 작업이 요구된다. 또한 패턴은 어떤 애플리케이션에라도 적용 가능해야 하므로 언어나 구현 방법에 독립적이며 추상적이어야 한다. 디자인 패턴을 효과적으로 적용하기 위해서는 트레이드오프를 통해 상황에 적합한 패턴을 선택하고 가공해야 한다. 우리는 항상 TEMPLATE METHOD와 STRATEGY 패턴의 갈림길에서 고민해야 한다.

가장 이상적인 형태의 재사용 방법은 설계 재사용과 코드 재사용을 적절한 수준으로 조합하는 것이다. 코드 재사용만을 강조하는 컴포넌트는 실패했다. 추상적인 수준에서의 설계 재사용을 강조하는 패턴은 설계를 재사용하기 위해 매번 유사한 코드를 작성해야만 한다. 패턴과 유사하게 기존 설계를 재사용할 수 있으면서도 패턴을 반복적으로 구현하는 문제를 피하기 위해 이미 존재하는 코드를 재사용할 수 있는 방법은 없을까?

프레임워크에서 해답을 찾을 수 있다. 프레임워크란 “추상 클래스 집합과 추상 클래스 인스턴스 간의 상호작용을 통해 시스템 전체나 일부를 표현하는 재사용 가능한 설계”, 또는 “애플리케이션 개발자가 현재의 요구사항에 맞게 커스터마이즈할 수 있는 애플리케이션의 골격(skeleton)”을 의미한다. 첫 번째 정의가 프레임워크의 구조적인 측면에 초점을 맞추고 있는 반면 두 번째 정의는 프레임워크를 사용하는 목적인 코드와 설계의 재사용에 초점을 맞추고 있다.

프레임워크는 애플리케이션의 아키텍처를 제공하며 문제 해결에 필요한 설계 결정과 이에 필요한 기반 코드 모두를 포함하고 있다. 프레임워크는 애플리케이션이 확장할 수 있도록 부분적으로 구현된 추상 클래스와 추가적인 작업 없이도 재사용 가능한 다양한 종류의 컴포넌트 라이브러리를 제공한다. 설계 아이디어를 재사용하기 위한 수단으로 프레임워크를 이용할 경우 패턴에서 살펴본 설계의 전달 방안에 대해 고민할 필요가 없다. 프레임워크에서는 코드가 곧 설계 재사용을 위한 표기법이다.

프레임워크에서 설계를 재사용 가능하도록 지원해 주는 핵심 구성요소는 추상 클래스(abstract class)다. 추상 클래스의 경우 인스턴스를 생성할 수 없기 때문에 서브 클래스를 생성하기 위한 템플릿의 용도로 사용된다. 프레임워크를 사용해서 애플리케이션을 구축할 경우 추상 클래스를 상속 받은 서브 클래스를 추가하여 애플리케이션에 특화된 기능을 구현하게 된다. 따라서 프레임워크에 속한 추상 클래스는 애플리케이션에 속한 서브 클래스의 인터페이스를 정의한다.

인터페이스의 정의가 프레임워크에 속하기 때문에 자연스럽게 애플리케이션의 제어 흐름 역시 프레임워크에 의해 결정된다. 프레임워크의 핵심은 추상 클래스가 아니라 추상 클래스의 인터페이스에 의해 정의되는 상호작용 방식이다. 서브 클래스는 추상 클래스가 사용되는 문맥 내에서 추상 클래스와 동일한 방식으로 상호작용할 수 있어야 하며 리스코프 치환 원리(LSP, Liskov Substitution Principle)에 따라 대체 가능해야 한다. 

프레임워크는 애플리케이션에 대한 아키텍처를 제공한다. 즉, 프레임워크는 클래스와 객체들의 분할, 전체 구조, 클래스와 객체들 간의 상호작용, 객체와 클래스 조합 방법, 제어 흐름에 대해 미리 정의한다. 프레임워크는 설계의 가변성을 미리 정의해 두었기 때문에 애플리케이션 설계자나 구현자는 애플리케이션에 종속된 부분에 대해서만 설계하면 된다. 프레임워크는 애플리케이션 영역에 걸쳐 공통의 클래스들을 정의하여 일반적인 설계 결정을 미리 내려 둔다. 비록 프레임워크가 즉시 업무에 투입할 수 있는 구체적인 서브 클래스를 포함하고 있기는 하지만 프레임워크는 코드의 재사용보다는 설계 자체의 재사용을 중요시 여긴다.

- GOF, 디자인 패턴