TheSpecUIframework:Chapter 04
- Spec 의 기본 사항
Spec 의 기본 사항
이 장에서는 Spec 의 주요 측면 및 구축(building) 과정의 중요한 맞춤 요소등을 다시 살펴보도록 하겠습니다.
사용자 인터페이스 구축: 구성하기
Spec 의 주요 측면은 모든 사용자 인터페이스가 기존 사용자 인터페이스의 작성 및 재사용을 통한 생성 등으로 구성된다는 점입니다. 이러한 구조를 허용하기 위해 사용자 인터페이스 정의는 사용자 인터페이스의 model 을 정의하고 화면에 표시 될 사용자 인터페이스 요소를 not 로 구성합니다. 이러한 UI 요소는 기본 UI 프레임 워크를 고려해서 Spec 에 의해 인스턴스화됩니다.
결국, 그것은 모델의 구성 요소이며, 결과적으로 보여지는 최종 사용자 인터페이스를 구성하는 UI 요소가 됩니다. 또한 Spec 의 뿌리(root/루트) 구성 요소 이름도 설명합니다: 모든 UI 는 다른 UI 의 구성(composition) 을 통해 구성되며 UI 를 정의하기 위해 model 을 정의하는 것으로 충분하므로 모든 UI 의 루트 클래스 이름은 ComposableModel 입니다. 결과적으로 새 사용자 인터페이스를 정의하려면 ComposableModel 의 하위 클래스를 만들어야 합니다.
사용자 인터페이스에서 다른 위젯의 구성 및 조정을 고려했을 때, Spec 은 Model-View-Presenter 패턴에서 영감을 얻었습니다.
Spec 에 정의 된 모델은 MVP 3각관계에서 발표자(presenter)에 해당합니다. 이 때문에, Spec 의 향후 버젼에서는, 이름의 일관성을 위해서, ComposableModel 을 ComposablePresenter 로 이름을 변경할 가능성이 있습니다.
기본적으로, ComposableModel: 에서 다음의 세가지 메서드로 그들 스스로를 구체화하는 세가지 관심사로 구성되었습니다:
- initializeWidgets 위젯 자체를 다루기
- initializePresenter 위젯 사이의 상호 작용을 처리하기
- defaultSpec 위젯의 레이아웃을 처리
따라서 이러한 방법은 각 사용자 인터페이스의 모델에서 흔히 발견할 수 있습니다. 이 장에서는 각 메서드의 세부 사항과, 이러한 세가지 방법을 통해 전반적인 UI를 구축하는 방법에 대해 설명합니다.
initializeWidgets 메서드
initializeWidgets 메소드는 인스턴스 변수를 인스턴스화 하고 저장하며, UI 의 일부가 될 다른 위젯을 부분적으로 구성합니다. 모델을 인스턴스화하면 다른 하위 수준의 사용자 인터페이스 구성 요소가 인스턴스화되고 초기화되어 사용자에게 표시되는 UI가 생성됩니다. 각 위젯 구성의 첫 번째 부분이 여기서도 지정되므로 이 메소드를 initializeWidgets 라고 부릅니다. 이 메소드의 요점은 위젯의 모양과 자체 포함(self-contained) 행동방식을 명시하는 것입니다. 모델의 상태를 업데이트하는 동작은(예 : Save 버튼을 누를 때) 이 메서드에서도 기술(described)됩니다. 위젯 간의 상호 작용(between) 을 정의하는 것은, 이 메소드의 책임이 아니라 명시적이지 않은 것입니다. 명시적이지는 않지만 위젯 사이에서 상호 작용을 정의하는 것은 이 메서드의 책임입니다.
일반적으로 initializeWidgets 메서드는 다음의 양식을 따르게 됩니다:
- 위젯 인스턴스화
- 위젯 설정 명세
- 초점(focus) 순서 명세
마지막 단계는 필수는 아니지만 적극적으로 권장됩니다. 실제로 이 마지막 단계가 없으면 키보드를 이용하는 탐색은 안정적으로 작동하지 않습니다.
initializeWidgets 메서드를 지정하지 않는다면, UI 가 위젯을 가질 수 없기 때문에 반드시 지정해야 합니다. |
위젯 인스턴스화
위젯에 대한 모델의 인스턴스화는 생성 메소드(creation method)의 사용 또는 instantiate: 메소드의 사용의 두 가지 방법으로 수행 할 수 있습니다.
- 첫 번째 선택을 고려했을때, 프레임워크는 모든 기본 위젯을 생성하기 위한 단항 메시지를 제공합니다. 이러한 메시지의 형식은 new[Widget] 입니다 (예 : newButton 은 버튼 위젯을 만들고 newList 는 목록 위젯을 만듭니다). 사용할 수 있는 위젯 생성 메서드의 전체 목록은 widget 프로토콜의 ComposableModel 클래스에서 찾을 수 있습니다.
- 두 번째 선택지는 보다 일반적입니다. ComposableModel 의 하위 클래스(첫 번째 옵션에 의해 처리 된 것 외의)를 재사용하려면 instantiate: 메소드를 사용해서 위젯을 인스턴스화 해야 합니다. 예를 들어 MessageBrowser 위젯을 다시 사용하려는 경우에, 코드는 self instantiate: MessageBrowser 가 됩니다.
UI 레이아웃 정의
위젯 레이아웃은 UI를 구성하는 다른 위젯이 배치되는 방식을 지정하는 메소드를 지정하여 정의됩니다. 또한 창 크기를 조정할 때 위젯이 어떻게 반응해야 할지를 지정합니다. 중에 살펴 보겠지만, 이 메서드는 다른 이름을 가질 수도 있으며 특정 조회(lookup) 프로세스의 적용을 받습니다.
레이아웃 메서드는 일반적으로 모든 인스턴스에 대해 동일한 값을 반환하기 때문에 클래스 측면에 배치됩니다.
다르게 말해서, 일반적으로 동일한 사용자 인터페이스의 모든 인스턴스는 동일한 레이아웃을 가지게 되기 때문에 클래스 변수에 대한 클래스 측면의 접근자로 간주 될 수 있습니다.
UI 가 없으면 UI 에 위젯이 표시되지 않기 때문에, 레이아웃 메서드는 반드시 지정해야 합니다. |
위젯을 위한 여러개의 레이아웃 보유하기
동일한 UI 에 대해 여러가지의 레이아웃을 작성할 수 있으며, UI 를 구축한 뒤에 특정 레이아웃을 사용할 수 있습니다. 이런 작동을 위해 지금까지처럼 openWithSpec 을 호출하는 대신에, openWithSpec: 메시지를 사용해서 레이아웃 메소드의 이름을 인수로 사용하면 됩니다. 예를 들어, 두개의 서로 다른 레이아웃이 있는 두개의 버튼 UI 에 대해, 조금 부자연스럽지만, 다음과 같은 예제를 고려해 보십시오.
ComposableModel subclass: #TwoButtons
instanceVariableNames: 'button1 button2'
classVariableNames: ''
package: 'Spec-BuildUIWithSpec'
TwoButtons >> initializeWidgets
button1 := self newButton.
button2 := self newButton.
button1 label: '1'.
button2 label: '2'.
self focusOrder
add: button1;
add: button2
TwoButtons class >> buttonRow
<spec: #default>
^SpecRowLayout composed
add: #button1; add: #button2;
yourself
TwoButtons class >> buttonCol
^SpecColumnLayout composed
add: #button1; add: #button2;
yourself
이 UI는 여러 가지 방법으로 열 수 있습니다:
- TwoButtons new openWithSpec: #buttonRow 버튼을 연속으로 배치
- TwoButtons new openWithSpec: #buttonCol 세로로 배치
buttonRow 레이아웃 메서드에는 <spec: #default> 프라그마(전처리-pragma)가 있습니다. 결과적으로 buttonRow 레이아웃은 TwoButtons new openWithSpec 을 실행할 때 사용됩니다. 이제부터 이 내용을 설명하겠습니다.
openWithSpec 메서드는 다음의 조회 메커니즘을 사용해서 레이아웃 메소드를 얻습니다:
- 클래스 측면을 대상으로, 전체 클래스 계층 구조 전체에서 <spec: #default> 프라그마를 사용하는 메서드를 검색합니다.
- 검색 결과에서 이러한 메서드가 여러개 있는 경우라면, 발견된 것중에 첫 번째 메서드를 사용합니다.
- 해당되는 메서드가 존재하지 않으며 <spec> 프라그마에 정확히 하나의 메서드만 있다면 그 메서드를 사용합니다.
- 그렇지 않으면, 클래스 측면의 defaultSpec 메서드가 호출되기 때문에, 오버라이드를 진행하는 것이 아니라 subclassResponsibility 를 작동시키게 됩니다.
작성중인 UI의 수퍼 클래스에 <spec: #default> 또는 <spec> 프라그마가 있는 메서드가 있다면, 클래스에 구현된 defaultSpec 메서드 대신에 프라그마가 있는 클래스가 사용됩니다. |
예를 들어, TwoButtons 의 하위 클래스는 <spec: #default> 프라그마를 가진 메서드가 있기 때문에, defaultSpec 메소드를 사용할 수 없습니다.
위젯을 재사용하는 경우에 대한 레이아웃 지정
위젯이 여러가지 레이아웃을 가진다는 것은, 위젯이 재사용 될 때 사용할 레이아웃을 지정하는 방법이 있음을 의미합니다.
지금까지는 add: 메시지를 사용해서 레이아웃에 위젯을 추가했습니다. 이 방법은 openWithSpec 메커니즘을 사용하여 재사용되고 있는 위젯의 레이아웃을 결정합니다. 재사용되는 위젯에 대한 대체 레이아웃을 사용하려면 add:withSpec: 메시지를 사용하면 됩니다. 쓰려고 하는 레이아웃 메서드의 이름을 추가 인수로 사용해야 합니다.
ComposableModel subclass: #TBAndListH
instanceVariableNames: 'buttons list'
classVariableNames: ''
package: 'Spec-BuildUIWithSpec'
TBAndListH >> initializeWidgets
buttons := self instantiate: TwoButtons.
list := self newList.
list items: (1 to: 10).
self focusOrder add: buttons; add: list.
TBAndListH >> title
^'SuperWidget'
TBAndListH class >> defaultSpec
^ SpecRowLayout composed
add: #buttons; add: #list;
yourself
위 코드의 TBAndListH 클래스는, 그림 4-1 처험 SuperWidget 창을 생성합니다. TwoButton 위젯은, TwoButton 위젯의 buttonRow 레이아웃 메서드를 사용하기 때문에 세개의 위젯을 전부 수평으로 배치합니다.
또는 TBAndListV 클래스를 TBAndListH 의 하위 클래스로 만들고, 아래의 defaultSpec 메서드만 변경할 수도 있습니다. 재사용된 buttons 위젯이 buttonCol 레이아웃 메서드를 사용해야한다는 것을 지정하기 때문에, 그림 4-2와 같은 창이 나타납니다.
TBAndListH subclass: #TBAndListV
instanceVariableNames: ''
classVariableNames: ''
package: 'Spec-BuildUIWithSpec'
TBAndListV class >> defaultSpec
^ SpecRowLayout composed
add: #buttons withSpec: #buttonCol; add: #list;
yourself
initializePresenter 메서드
initializePresenter 메소드는 서로 다른 위젯 간의 상호 작용을 정의합니다. 서로 다른 위젯의 동작을 연결하여 전체 프리젠테이션, 즉 전체 UI 가 사용자에 의한 상호 작용에 어떻게 반응 하는지를 지정합니다. 대개의 경우, 이 메서드는 특정 이벤트가 위젯에 의해 수신될 때 수행할 동작의 명세로 구성됩니다. 이렇게 되면, UI의 전체 상호 작용 흐름이 해당 이벤트의 전달로서 나타납니다.
initializePresenter 메서드는, Spec UI 의 유일한 옵션 메소드입니다. |
Spec 에서는 다양한 UI 모델이 값 소유자(value holders)에 포함되어 있으며, 이벤트 메커니즘은 위젯 간의 상호 작용을 관리하기 위해 이러한 값 소유자의 발표(announcements)에 의존됩니다. 값 소유자는 변경시 수행 할 블록을 등록하는 데 사용되는 whenChangedDo: 메소드와 주어진 객체에 메시지를 전송하는 whenChangedSend: aSelector to: aReceiver 메소드를 제공합니다. 예를 들자면 이러한 원시(primitive) 메서드 외에도, 기본 위젯은 목록의 항목을 선택할 때보다 구체적인 훅(hook)을 제공합니다.
결론
이 장에서는 Spec 의 세가지 핵심 메서드에 대해 자세하게 알아보았습니다: initializeWidgets, defaultSpec 및 initializePresenter 는 각각 사용자 인터페이스 작성(building) 프로세스의 각각 다른 측면을 담당합니다. 또한 다른 레이아웃 메서드를 사용하는 능력과 레이아웃 메서드의 색인이 어떻게 수행되는지에 대해 상세히 논의했습니다.
Spec 의 핵심은 재사용이지만, 이 장에서 명시 적으로 다루지는 않았습니다. 대신 앞의 내용에서 자세한 내용을 참조해 주시기 바랍니다. |