LazarusCompleteGuide:7.1
라자루스 컴포넌트의 아키텍처
LCL은 매우 다른 기능들을 가진 많은 컴포넌트들을 제공한다. 이를 이용해 애플리케이션을 빠르고 쉽게 개발할 수 있다. 라자루스와 함께 제공되는 컴포넌트 외에도 기본 라자루스 컴포넌트의 기능을 확장시키는 데 이용 가능한 제3자 컴포넌트 또한 많이 존재한다. 본장에서는 LCL의 기본 빌딩 블록(building block)을 설명하고, 커스텀 컴포넌트의 전 개발 과정에 대한 개관을 제공하고자 한다. 이러한 지식은 제3자 컴포넌트를 성공적으로 라자루스에 포팅하는 방법을 이해하도록 도와줄 될 것이다.
LCL은 서로 밀접하게 연결된 두 개의 계층적 클래스 트리 집합을 기반으로 한다. 두 클래스 집합 중 하나는 플랫폼 독립적인 부분(platform-independent part)이다-컴포넌트 팔레트에 나타나는 컴포넌트들은 (TButton과 같은) 해당 집합에 속하며, 이 집합은 델파이와 호환이 가능하다. 두 번째 클래스 집합은 소위 위젯 셋(widget-set)이라고도 불리는데, 좀 더 정확히 말하자면, 네이티브 위젯 셋에 대한 라자루스 인터페이스라고 볼 수 있다. 위젯 셋은 네이티브 라이브러리로, Linux에서 GTK, Windows에서 WinAPI가 있다. 플랫폼 독립 LCL과 (예: TButton) GTK 네이티브 버튼 (gtk_button이라 불림) 간 인터페이스를 LCL 인터페이스라고 부른다.
각 LCL 인터페이스의 이름은 그것이 결합된 기본 위젯 셋의 이름을 따른다. 이러한 이유 때문에 위젯 셋과 LCL 인터페이스의 개념은 종종 동의어로 사용되기도 한다.
모든 LCL 컴포넌트가 위젯 셋에 대응 대상을 가지는 것은 아니며, 때로는 커스텀 컴포넌트가 단순히 LCL의 플랫폼 독립적 부분 위에 빌드되기도 한다.
따라서 플랫폼 독립적 부분을 먼저 설명하고자 한다.
다양한 클래스들 간 관계를 그림 7.1에 표시하였다.
라자루스 애플리케이션의 다양한 부분들이 표시되어 있다. 화살표는 각 부분이 통신하는 다른 부분들을 의미한다. 그림에서 볼 수 있듯이 LCL 클래스만 위젯 셋 클래스와 통신한다. 이는 위젯 셋 클래스는 다른 위젯 셋으로만 대체할 수 있으며, 어떠한 코드도 변경하지 않고 애플리케이션이 기능할 수 있음을 의미한다.
LCL의 플랫폼 독립 계층
모든 LCL 컴포넌트는 TComponent 클래스로부터 내려온 TLCLComponent 클래스의 자손들이다. TComponent 는 FCL (프리 파스칼 컴포넌트 라이브러리)에서 정의되며, 두 가지 주요 기능을 제공한다. 그 기능 첫 번째는, TComponent 는 TPersistent 로부터 published 프로퍼티를 정의하는 기능을 상속받아 컴파일러로 하여금 프로퍼티의 읽기 및 런타임 시 일반적인 방식으로 설정을 허용하기 위한 코드를 생성하도록 해준다. 이것은 오브젝트 인스펙터(Object Inspector)가 컴포넌트의 published 프로퍼티를 표시 및 조작하도록 허용하는 기능이다.
TComponent 가 제공하는 두 번째 기능은 published 프로퍼티를 스트림(stream)으로 스트리밍하는 기능으로, .lfm 파일을 예로 들 수 있겠다. 이것이 작동하는 이유는 모든 컴포넌트가 Name 프로퍼티를 가지며, 중첩될 수 있기 때문이다; 또한 폼 디자이너에서 TButton 을 TForm 인스턴스로 드롭할 수 있게 해준다. TComponent 의 스트리밍 기능은 매우 강력하여 상속과 중첩(nesting)ㅡ라자루스 IDE에서 시각적으로 지원ㅡ을 모두 허용한다. TComponent 에 대해 빌드된 이러한 이중 기능은 제3자 컴포넌트가 TComponent 자손이어야 하는 이유이기도 한다.
컴포넌트와 그 모든 자식들을 라자루스 소스 트리 내 한 스트림에 저장하는 것이 얼마나 쉬운지를 (그리고 스트림으로부터 어떻게 다시 읽는지를) 보여주는 예제는 디렉터리 examples/componentstreaming 에서 찾을 수 있다.
LCL 은 LCLRefCount 프로퍼티와 WidgetSetClass 프로퍼티를 도입한 TLCLComponent 클래스에서 시작된다. WidgetSetClass 프로퍼티는 컴포넌트와 위젯 셋 인터페이스 간 링크를 형성한다ㅡ관련 내용은 추후 다루겠다. 컴포넌트 팔레트에 표시되는 모든 LCL 컴포넌트는 TLCLComponent 의 자손으로, 세 가지 그룹으로 나뉜다.
첫 번째 그룹은 대화창 컴포넌트로, 파일 선택 시 이용할 수 있는 TOpenDialog 를 예로 들 수 있다. 대화창 컴포넌트는 TCommonDialog 에서 계승된다. TOpenDialog 는 기본 위젯 셋에 의해 완전히 구현되므로, TOpenDialog 는 운영체제에 따라 다른데, 가령 Windows에서는 사용자가 즉시 윈도우 스타일로 된 아이콘과 버튼을 인식할 것이다. 반면 Mac OS X에선 Mac 사용자에게 익숙한 Mac 대화창이 표시될 것이다. 둘 모두 정확히 같은 TOpenDialog 컴포넌트를 이용하고 있다. 운영체제의 새 버전이 출시되면 프로그램을 재컴파일할 필요가 없다. 대화창은 운영체제에 의해 생성되기 때문에 현재 버전의 표준을 따를 것이다. 반면 TFindDialogㅡ텍스트 검색에 사용되는ㅡ는 간단한 대화창이다. TFindDialog 는 모든 운영체제에서 같은 모양을 한다.
새 대화창을 작성해야 하는 이라면 TFindDialog 코드를 확인해야 한다. 한편 TOpenDialog 는 위젯 셋과 그 성능에 따라 좌우되기 때문에 변경할 수 없다.
두 번째 컴포넌트 그룹은 메뉴로 구성된다: TMenu, TPopupMenu, TMenuItem. 이들 또한 위젯 셋에 의해 구현된다.
세 번째이자 가장 큰 그룹은 TControl 클래스로부터 내려온다. TControl 은 많은 프로퍼티들을 정의하며, 컨트롤의 위치와 크기를 결정하는 Top, Left, Width, Height 프로퍼티도 포함된다. 마우스나 키 이벤트, 그리기나 포커스 처리(focus handling)와 같은 기본 이벤트들도 마찬가지로 TControl 에서 정의된다. 이러한 프로퍼티 및 이벤트에도 불구하고 TControl 에서 직접 파생되는 소수의 컨트롤이 있다. TControl 에서 직접 파생되는 클래스는 두 가지 있는데, 고유의 가족(family)의 조상이 되므로 매우 중요하다: TGraphicControl 과 TWinControl.
가장 먼저 TGraphicControl 클래스는 OnPaint 이벤트를 이용해 TControl 를 향상시키지만 고유의 창 핸들(window handle)을 갖고 있지 않다. 즉, 그에 상응하는 위젯 셋 컨트롤이 없으며, TGraphicControl 은 전적으로 LCL에 의해 그려진다.
예를 들어, TSpeedButton 은 플랫폼 독립적인 방식으로 그려진다. 핸들이 없다는 것은 다른 어떤 컨트롤도 TGraphicControl 에 드롭할 수 없음을 의미한다. TGraphicControl 은 포커스가 필요 없는 클릭 가능한 심볼, 이미지 또는 시각적 구분자(visual separator), 또는 키보드 입력 등을 비롯해 단순한 컴포넌트와 관련해 가장 좋은 시작지점이다. TWinControl 은 TControl 의 강력한 자손이다. TWinControl 은 자식 컨트롤을 가질 수 있으며, 이러한 목적으로 핸들이 함께 제공된다: 핸들은 위젯 셋과의 직접 통신에 필요하다. TWinControl 에는 많은 특수화된 자손들이 있는데, 모두 LCL에서 공공연하게 이용할 수 있다. TWinControl 에서 직접 파생되는 컨트롤은 매우 소수에 불과하지만 가장 눈에 띄는 예외로 3D 컨텍스트를 구현하는 TOpenGLControl 을 들 수 있겠다.
복잡한 컨트롤을 구현 시, 그것을 파생하기 가장 적합한 클래스는 TWinControl 자손, TCustomControl 클래스이다. 이 클래스는 스크롤바를 표시할 수 있으며, 그림을 그리는 캔버스와 OnPaint 이벤트를 가진다.
TCustomControls 는 스스로 그리기 때문에 모든 플랫폼에 동일하게 나타난다. 예로 TTreeView, TSynEdit (라자루스 소스 에디터에서 사용되는 컨트롤), TOIPropertyGrid (오브젝트 인스펙터 그리드)가 있다.
그들의 스크롤바는 위젯 셋이 그리며, 그 위치와 크기는 TCustomControl 이 제어한다.
TScrollingWinControl 은 HorzScrollbar 와 VertScrollbar 를 자동으로 관리함으로써 TCustomControl 을 향상시킨다. 이것은 컴포넌트에 기반 역할을 하여 그 위에 자식 컨트롤들의 드롭을 허용한다-그 외에는 TCustomControl 과 동일한 기능을 제공한다.
위에서 논한 모든 컴포넌트들은 TWinControl-적합한 포함 컨트롤의 인스턴스는 TControl 의 Parent 프로퍼티를 통해 이용 가능-에 내장(embed)되어야 한다는 조건을 공유한다. 자유롭게 이동 가능한 창들인 컨트롤들은 (부모 컨트롤이 필요 없는) 그러한 컴포넌트 모두에게 기반 클래스의 역할을 하는 TCustomForm 기반 클래스로서 (TScrollingWinControl 자손) 간주할 필요가 있다. 일부 프로퍼티들은 이 클래스에서 그들의 의미를 변경한다: 창은 단순히 위치뿐만 아니라 Monitor, BorderIcons, WindowState를 가지기도 하며, 최대화 및 최소화도 가능하다. 많은 위젯 셋들은 (WinCE도 그 중 하나) 이러한 프로퍼티에 추가적으로 제한을 둔다.
마지막으로 TForm 은 IDE가 모든 창에 대한 기반 클래스로 사용하는 TCustonForm 자손이다. IDE가 생성하는 모든 창은 TForm 의 자손이다-따라서 자체가 새 컴포넌트 클래스가 된다 (예: 새로운 폼들이 TForms 가 아닌 TForm 자손들로 생성된다).
LCL 컴포넌트 계층구조를 살펴보면 컴포넌트 팔레트의 거의 모든 클래스가 TCustom 이름으로 시작되는 클래스의 자손들임을 발견할 것이다. 예를 들어, TTreeView 의 선언은 published 블록으로만 구성된다: 실제 구현은 TCustomTreeView 에 있다. 그 이유는 프로퍼티의 가시성(visibility)이 자손에서 증가될 수는 있지만 절대로 감소되진 않기 때문이다.
프로퍼티(예: Left)가 Public으로 정의되는 경우, 더 이상 자손 클래스에서 Protected 또는 Private로 만들 수 없으며, 의미가 통하지 않더라도 지원해야만 한다. 이러한 이유로 대부분 모든 LCL 클래스들이 TCustom 클래스로 구현되며, Public이나 Published 프로퍼티의 수는 극히 소수에 불과하다: TTreeView 와 같은 end 클래스는 더 높은 가시성을 요하는 프로퍼티를 단순히 Public 또는 Published로 만든다. 이용 불가한(unavailable) 프로퍼티로 트리뷰를 생성하고자 하는 경우, TCustomTreeView 에서 계승시켜, 어떤 이용 불가 프로퍼티도 Public 또는 Published로 만들지 않으면 된다.
플랫폼 독립 계층
앞서 설명하였듯, LCL은 일반 LCL 클래스에 일치하는 클래스의 계층구조를 포함한다. TCustomButton 클래스뿐만 아니라 그와 동등한 TWSButton 도 있다. 이것은 LCL 인터페이스에 대한 추상 기반 클래스로서 추상 클래스 메소드만 포함한다.
TCustomButton 이 TButtonControl 에서 계승되듯 TWSButton은 TWSButtonControl 에서 계승된다. WS-클래스들은 각 LCL 인터페이스에서 구현된다. 예를 들어, LCL Win32 인터페이스는 TWin32WSButton 클래스를 정의함과 동시 MS-Windows 플랫폼을 위한 TWSButton 기능을 구현한다. TCustomButton 은 WidgetSetClass 를 가지며, 윈도우 플랫폼에서 TWin32WSButton 값을 갖는다. 복잡하게 들리지만 TCustomButton 이 쉽게 LCL 인터페이스를 호출하도록 해준다:
procedure TCustomButton.WSSetDefault;
begin
// Default only tells us if button was set to Default at design time
// At runtime, Active actually shows us if the button is the default button.
If HandleAllocated then
TWSButtonClass(WidgetSetClass).SetDefault(Self,FActive)
end;
HandleAllocated 함수는 핸들을 (위젯 셋으로 접근에 사용되는) 이용 가능한지 테스트하고, LCL 인터페이스를 호출한다. 프로그램을 컴파일 시 어떤 위젯 셋이 사용되었는지는 중요하지 않다.
커스텀 위젯 셋 클래스를 작성하는 것은 LCL 개발자들이 직면하는 가장 큰 도전과제 중 하나다. 예를 들어, TOpenGLControl 을 보면 클래스 계층구조 두 개를 모두 확장하고 네 가지 위젯 셋에 대한 구현부를 포함하고 있다. 컨트롤은 패키지 components/openl/lazopenglcontext.lpk 에서 구현된다.