우선, 아무리 뛰어난 사람도, 처음 맡는 도메인의 문제를 해결해야 할 때는, 삽질이라는 절차를 반드시 거쳐야 합니다. 이 때는 정보의 추상적 깊이, 내포 관계등을 구분할만한 단서가 없으므로, 질문 하기 보다는, 그 분야의 개요를 빨리 익혀야 합니다. 매크로 뷰가 없는 상태에서 질문과 답변을 반복하면, 부분적으로 얕은 지식이 쌓이게 됩니다. 이는 전체를 오해하는 결과를 초래 해, 일반적으로 오히려 해가 됩니다. 가능한한 짧고 간략한 오버뷰 문서를 구하여, 정독합니다.

검색

이클립스는 세상에서 가장 커다란 오픈 프로젝트이며, 따라서 방대한 양의 좋은 문서들이 모두 공개되어 있습니다. 좋은 검색 습관을 갖는 것 만으로도, 당신의 생산성을 10배이상 높일 수 있습니다.

  • 만약 겪고 있는 문제가 SWT / JFace 관련이라면 스니펫을 검색한다. - 모든 링크는 아래에 있습니다.
  • 이클립스에서 널리 사용되고 있는 유형의 문제라면 이클립스 공식 FAQ를 검색한다.
  • 일반적인 큰 개요 유형의 문제라면, 이클립스 위키 문서 또는 리소스를 검색 해 본다.
  • 다른 사람도 겪고 있는 문제라는 생각이 들면, 구글을 검색 해 본다.
  • 구글에 없으면 이클립스 뉴스 그룹에 들어간다. 대개의 질문은 RCP그룹에서 답을 얻을 수 있다.
  • 그곳에서도 답이 없다면 이클립스 뉴스 그룹에 질문한다. 거기엔 월급을 받고 *헌신*적으로 대답해 주는 사람들이 있다. 이곳에서 2~3일 이내(주말 제외)에 답변을 얻지 못하면 다른 돌파구를 마련해야 합니다.

좋은 대답을 가려내는 것은 매우 어려운 일입니다. 알려진 유명인이 대답한 경우에는 (예를 들어 폴 웹스터나 에드먹은 관련 카테고리 내에서 신의 대접을 받고 있습니다) 문제가 없지만 그렇지 않은 경우에는, 가려운 점을 정확히 긁어 주지 못하거나, 국소적인 질문에 대해 너무 큰 개요의 대답을 받는 경우도 종종있습니다. 이럴 때는 상대방이 왜 그런 대답을 했는지 곰곰히 생각 해 보는 것이 중요합니다.

질문을 할 때에는 자신의 의도와 하려는 일을 명확히 기술 하십시오. 대부분의 삽질은 *방법*이나 *시도*자체가 잘못되었을 가능성이 높습니다. 단지 API만 질문하는 것보다 훨씬 폭 넓은 조언을 얻을 수 있습니다.

영어에 대한 두려움을 버리십시오. 이클립스 커뮤니티 포럼에 작성되는 글의 50%가량은 엉터리 영어입니다. (물론 나머지는 그냥 영어죠) 이클립스는 글로벌이니까요. ㅎㅎ. 게다가 컴퓨터 영어는 생각보다 쉽습니다. 단순한 단어를 짧게 조합해서 궁금한 것만 확실히 전달해도 다들 친절하게 대답해 줍니다. 이클립스에 관한 유용하고 훌륭한 정보는 거의 전부라고 해도 좋을 정도로 영어로 작성되 있습니다. 이클립스 5년차 한국 개발자 보다, Java밖에 모르지만 모르지만 영어를 아는 사람이 훨씬 더 유리할 정도로 말이죠.

ps. SWT는 의외로 같은 연구소 내의 MFC/win32개발자들에게 질문하면 간단히 납득할 수 있는 대답을 얻는 경우가 많았습니다.

역공학

문제가 너무 특수하여 대답을 받을 수 없는 경우, 이클립스 내에서 비슷한 기능을 찾아 본다.

  • 해당 기능을 실행하는 다이얼로그등에서 Alt + Shift + F3을 누르면 어떤 플러그인이 그 UI를 기여했는지 파악이 가능하다.
  • 그 상태에서 다시 Alt + Shift + F1을 누르면 그 다이얼로그나 현재 보이는 UI의 활성 클래스 및, UI 컨텍스트, 셀렉션 모델등에 대해 확인이 가능하다. 적당한 클래스를 열어 브레이크포인트를 찍고 관찰하자.
  • Alt + Shift + F2를 누른 상태에서 메뉴나 툴바 아이템을 누르면, 그 메뉴의 메뉴 컨트리뷰션 주소를 알 수 있다.
  • ICommandService 로부터 커매드가 제출 될 때 마다, 어떤 핸들러가 선택되는지 확인 하는 간단한 플러그인을 만들어, 무엇이 실제로 문제를 해결하고 있는지 디버깅해 볼 수 있다.
  • 모든 익스텐젼 포인트는 우클릭을 하여 Find Reference를 선택하면 이 익스텐젼 포인트를 어떻게 사용하는지 알 수 있다. (우선 용의자 플러그인들을 디펜던시에 넣어둘 것)

관련 링크들

'Eclipse Core' 카테고리의 다른 글

P2의 주요 용어 정리  (0) 2010.11.29
Extension Point and Extension  (0) 2010.07.08
어댑터 디커플링  (2) 2009.08.10
Job과 Schedule Rule  (0) 2009.08.08
Posted by 지이이이율
,

사실 에디터란, 편집되는 대상에 따라 평가 방식이 달라지기 때문에, 그 전체에 대해 품질 점검표를 만들 수는 없습니다. 본 기사에서는 이클립스로 만들어진 에디터로서 지켜야 할 기본 품질 속성에 대해 알아봅니다. 100점 부터 시작하여, 자신이 만든 에디터가 감점 요소에 해당되면 해당 점수만 큼 감점하시면 됩니다.

ps. 이클립스를 배우면 배울 수록 기존의 이클립스에 있는 에디터들이 얼마나 잘 만들어졌나에 심한 충격을 받게 됩니다. 그래도 힘 냅시다.

1. File 선택 동기화 : 5점

  1. 에디터를 두 세개 정도 엽니다.
  2. 리소스 네비게이터나, 패키지 익스플로러 뷰는 좌우화살표 모양의 Link with Editor 버튼이 있습니다. 이 버튼을 누릅니다.
  3. 활성 에디터와 뷰의 파일 선택이 동기화 되어야 합니다.

만약 에디터의 입력이 복수의 파일등으로 구성되는 경우라면, 아마도 여러분은 직접 IEditorInput을 만드셨을 겁니다. 이 경우에는 에디터와 IEditorInput이 IFile.class에 대한 어댑터 질의에 응답하여야지만 그러한 기능이 정상작동합니다.

2. 에디터 복원 : 5점

  1. 본인이 만든 에디터를 두 세개 정도 엽니다.
  2. 이클립스를 종료하고 다시 실행합니다.
  3. 열어 두었던 모든 에디터가 되살아나 있어야 합니다.

IFileEditorInput 등과 같은 경우만 입력으로 고려한 경우, 이러한 내용은 모두 저절로 처리 되어 잘 알기 어렵지만, 실제로 에디터의 입력은 뷰에 표시되는 한 모델 객체 일 수도 있습니다. 예를 들면 MyLyn의 태스크 엘리먼트들은 파일이 아닙니다. 이러한 경우엔, 입력을 보존하고, 복원하는 작업을 직접 수행해야 합니다.

보존과정

워크벤치 -> IEditorInput : getPersistable
IEditorInput --> 워크벤치: IPersistable
워크벤치 -> IPersistable: getFactoryId
워크벤치 -> 워크벤치: id를 이용하여 메멘토 노드 생성
워크벤치 -> IPersistable: saveState
IPersistable-> IPersistable: 입력 상태 저장

복원 과정

워크벤치 -> 워크벤치: id로 팩토리 탐색
워크벤치 -> 엘레멘트 팩토리: 메멘토 노드 전달
엘레멘트 팩토리 -> 엘레멘트 팩토리 : IEditorInput  복원
엘레멘트 팩토리 -> 워크벤치: IEditorInput
워크벤치 -> 워크벤치 : 에디터 복원

메멘토 노드는 일종의 XML 노드입니다. 워크벤치는 종료될때 XML로 종료직전의 상황을 기록합니다. 팩토리는 Element Factory 확장점을 이용하여 등록합니다.

3. 파일 동기화: 총 배점 20

  1. 에디터를 엽니다.
  2. 텍스트 에디터등 다른 에디터로도 그 파일을 엽니다.
  3. 다른 에디터에서 수정한 뒤 저장합니다.
  4. 원래 에디터를 활성화 합니다.
  5. 더티 마크가 없던 경우, 파일을 즉시 다시 읽어 들여 표시해야 하고, 더티마크가 있는 경우, 파일의 내용이 바뀌었으니 다시 읽겠냐고 질의해야 합니다. : 10점
  6. 에디터가 열린 채로 해당 파일을 삭제하거나, 부모 폴더를 삭제하거나, 프로젝트를 삭제하거나 Close합니다. 이 때 에디터는 사라져야 합니다. : 5점
  7. 에디터가 열린 상태로 파일을 리네임 하거나 다른 곳으로 이동시킵니다. 에디터에서 조금 작업을 한 뒤 저장하면, 바뀐 파일로 안전하게 저장되야 하며, Save as... 기능도 작동해야 합니다. : 5점

이 기능은 SVN, CVS등 업데이트, get contents등과 같은 기능이 제대로 동작하는데에도 매우 중요합니다. 입력이 IFileEditorInput이었던 경우, 다음과 같이 워크스페이스를 리스닝 해야 합니다. 왜 해당 파일이 아니라, 전체 워크스페이스를 감시해야 하는지는 이어서 설명합니다.

if (input instanceof IFileEditorInput) {
	ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
}

해당 리소스에 영향을 미치는 방법은 아주 다양합니다. 프로젝트 닫기, 프로젝트 리네임, 부모 폴더 이동, 부모폴더 삭제, 부모폴더 리네임등등 경우의 수가 매우 다양합니다. 따라서, 워크스페이스 전체의 이벤트를 감시한다음, 그것을 해당 리소스를 기준으로 의미를 번역하여 사용합니다.

public void resourceChanged(IResourceChangeEvent e) {
	IFileEditorInput input = (IFileEditorInput) getEditorInput();
	IFile file = input.getFile();
	IPath fullPath = file.getFullPath();

	// 프로젝트가 닫긴경우. bug 8593
	if (e.getDelta() == null) {
		return;
	}

	IResourceDelta relatedDelta = e.getDelta().findMember(fullPath);
	if (relatedDelta == null) {
		return;
	}

	int kind = relatedDelta.getKind();

	if (kind == IResourceDelta.REMOVED) {
		handleResourceRemoved(relatedDelta);
	}

	if (kind == IResourceDelta.CHANGED && !this.isSaving) {
		handleResourceChanged(relatedDelta);
	}
}

11 번째 라인이 리소스 변경 이벤트를, 입력 파일을 기준으로 상대적 이벤트로 변환하는 코드입니다.

4. 문서 동기화 : 10점

  • 에디터를 엽니다.
  • 메뉴의 Window / New Window 를 선택하여, 워크벤치 윈도우를 하나 더 엽니다.
  • 새 윈도우에서도 같은 파일에 대한 같은 에디터를 엽니다.
  • 어느 한쪽을 수정하면 즉시 동일한 컨텐츠를 보여 주어야 합니다.
  • 어느 한쪽에서 저장하면, 나머지 한쪽에서도 더티마크가 사라져야 합니다.

이클립스 텍스트 기반 에디터는 DocuemntProvider를 통해서 문서를 공급받습니다. 따라서, JavaEditor와 Text Editor를 동시에 열어 둔 경우, 둘은 같은 도큐먼트를 공급받게 되어 수정 즉시, 저장하지 않도라도 동일한 내용을 표시하게 됩니다. 에디터가 파일로부터 모델을 직접만드는 방식으로 구현해서는 안됩니다.

여러개의 파일로부터 나온 모델(문서)들이 서로 참조를 갖는 경우, 한 파일로 부터 하나의 모델만 공급되는 중간 체계가 있어야 하는것은 매우 중요합니다. 그렇지 않다면, 모델단위로 쉽게 구현될 수 있었던 리팩토링이, 파일 변경이벤트로만 처리되어야 하므로 매우 어렵고 복잡한 문제로 바뀔 것입니다.

GEF 에디터의 기본구현에 포함된 EditDomain은 EditorPart를 하나만 갖으면서 CommandStack을 내장하고 있기 때문에, 이 사항을 충족하는데는 상당히 까다롭고 높은 비용이 듭니다. ㅠㅠ

5. 사용자 단축키 지정: 10점

  • 메뉴에서 Windows / Preference 를 엽니다.
  • 필터에서 key라고 입력하고, key 노드를 선탣합니다.
  • 우측 필터에 에디터의 이름을 입력합니다.
  • 에디터와 연관된 모든 단축키가 즉시 알아볼 수 있는 형태로 나타나야 하며 변경할 수 있어야 합니다. 예를 들어 '삭제', '추가'와 같은 형태로 나타난다면 사용자는 "뭘 삭제하고 추가한단 말이지?" 라고 생각할 것입니다.
  • 변경한 단축키는 노출된 모든 경로에 즉시 표시되고 적용되야 합니다. (팝업 메뉴 등등)
  • When 필드에서 "in My Editor" 와 같은 여러분이 만든 에디터를 사용중일때를 의미하는 컨텍스트가 공급되야 합니다.
  • 여러분의 에디터를 사용하는 도중 Ctrl+3을 누르고, "삭제", "~추가" 등과 같은 명령어를 직접 타이핑 하여 해당 명령을 실행 시킬 수 있어야 합니다.: 예를 들어 자바 에디터에서 필드를 선택한 뒤 Ctrl+3을 누르고 generate라고 입력해 보십시오.

이클립스 3.2의 주요한 변화는 액션이 가벼워 졌다는 점입니다. IAction객체는 UI생성과 비즈니스 로직-run()이 함께 있기 때문에 매우 무겁습니다. 단지 툴바에 버튼을 하나 그리기 위해서 클래스가 로드되고, 그로 인해 플러그인까지 시작되어 버리기 때문에, 이클립스의 시작시간 아주 느리게 만듭니다. 이클립스는 어마어마하게 많은 플러그인들이 전사적으로 뒤섞여 동작하기 때문에, 이 룰을 따르지 않으면 치명적인 결과가 나타납니다. 이러한 명령들은 모두 Command라고 부르는 확장점으로 선언적으로 설계되어야 합니다. 그러면 이클립스는 XML파일을 로드하는 것 만으로도 UI를 만들 수 있고, 플러그인은 시작되지 않아도 됩니다. 또한 이렇게 선언적으로 만들어진 명령들은, 자동적으로 단축키 설정등과 같은 플랫폼의 지원을 받을 수 있게 됩니다. 이클립스 위키에서 Command Framework, Core Expression, Menu Contribution, UIContext를 참조하십시오.

6. Team Support: 10점

  • 새파일을 만들고 에디터를 엽니다.
  • SVN등에 이 프로젝트를 셰어하고 커밋합니다.
  • 문서 변경, 저장 커밋을 몇 차례 반복합니다.
  • history 뷰를 열어, 과거버전을 더블 클릭하면 제대로 열려야 하며, *읽기 전용*으로 열려야 합니다.
  • 읽기 전용으로 열린 과거 버전의 파일을 Save as... 로 저장하면, 편집이 가능해져야 합니다.
  • 만약 이전 버전과의 비교 에디터를 공급했다면 당신은 이 평가표를 읽을 필요도 없는 구루이십니다. 부끄럽게 왜 그럽시니까. 원츄-3-b

7. 클립보드, 프린팅: 10점

  • 당신의 에디터에서 일부 모델을 Copy 합니다.
  • 메모장에 붙여 넣으면 텍스트로, 워드에 붙여 넣으면 스타일 텍스트로, 그림판에 붙여 넣으면 그림으로 붙여 넣어져야 합니다.
  • 인쇄가 되어야 합니다.

'PDE' 카테고리의 다른 글

빌드 자동화 하기 #1 - Hello Ant  (2) 2011.03.07
이클립스 플러그인에 DLL 포함시키기  (0) 2010.12.10
제품 빌드시 자주 발생하는 문제들  (0) 2010.11.26
Bundle과 Resource  (0) 2010.10.28
네이쳐와 빌더  (0) 2010.10.15
Posted by 지이이이율
,

마우스 클릭 시나리오

마우스 -> Tool: 클릭
Tool -> EditPart: 드래그 트래커 요청
EditPart --> Tool: 드래그 트래커
Tool -> DragTracker : 마우스 이벤트
DragTracker -> DragTracker : 리퀘스트 생성
DragTracker ->EditPart: 리퀘스트
EditPart -> EditPolicy*: 리퀘스트 전달
EditPolicy* -> EditPart: Command
EditPart -> Tool: Command
Tool -> Tool: 커맨드 수행
Tool -> 마우스: 복귀

개체의 역할 설명

  • Tool: 대게의 그래픽 편집이 그러하듯, UI이벤트는 일차적으로 툴에게 전달됩니다. 툴은 상태기반 머신의 일종으로, 대게는 마우스 아래에 있는 에디트파트나, 현재 선택된 에디트 파트와 연동하게 됩니다. 이 때, 그 연동의 역할을 수행하는 것이 DragTracker입니다. Selection Tool은 EditPart에게 드래그 트래커를 요청하고, 이벤트를 전달합니다. 예를 들어 선택된 에디트파트의 드래그 트래커에게 마우스가 눌렸다는 사실을 통보하고, 이 드래그 트래커는 선택 요청(SelectionRequest) 객체를 만들게 됩니다.
  • DragTracker: 드래그 트래커는 Selection Tool을 도와주는 객체입니다. 다른 툴들은 보통 드래그 트래커를 사용하지 않습니다. 대부분의 작업은 Selection Tool에 의해 이루어지므로(선택, 이동, 크기 조절, 입양, 직접 편집 삭제 등등), 드래그 트래커는 매우 중요한 역할을 하며, 각 파트의 동작을 개인화 하는 주요 진입점이 됩니다. 드래그 트래커는 셀렉션 툴이 전달한 UI이벤트를 바탕으로, Request 객체를 만들어 EditPart에 전달합니다.
  • 에디트 파트는 최상위 CEO급 컨트롤러이므로, 이 시퀀스 다이어그램에서 나타난 롤만을 설명합니다. 에디트 파트는 Request를 받으면, 자기 자신이 가진 편집 정책(EditPolicy)들에게 이 요구를 전달하고, 편집 정책들이 특정 행동을 하게 하거나, 정책들이 반환 해준 Command를 다시 툴에게 전달합니다.
  • EditPolicy: 기본적으로는 요구를 받으면, 해당 요구를 분석하여, 모델을 수정하는 커맨드를 만들어 리턴합니다. 독자적으로 호스트를 감시하면서 동작을 개인화 하기도 합니다.
  • 사족

    Posted by 지이이이율
    ,

    어댑터 패턴

    어댑터 패턴의 기본 전략 중 하나는, 한 객체가 특정한 영역에서만 의미를 갖는 기능집합을 요구 받을 때, 원래의 객체로 부터 분리된 어댑터 객체에게 그 임무들을 위임하는 것입니다. 이렇게 함으로써, 객체는 본래의 간결함을 유지 할 수 있으며, 개발자는 본인이 개발하지 않은 컴포넌트에 대해서도, 다른 영역에 참가시키는 것이 가능합니다.

    기능영역 -> 컴포넌트: getAdapter();
    컴포넌트 -> 컴포넌트: 어댑터 생성
    컴포넌트 --> 기능영역: adapter
    기능영역 -> 기능영역: 쓰임새 수행
    
    어댑터블한 컴포넌트가 기능영역에 참여하는 기본 원리

    일반적으로 어댑터 질의는 그림처럼 IAdaptable 인터페이스의 getAdapter(Class) 메소드를 통하여 이루어집니다. 하지만, 이 경우에, 어댑터 질의에 응답하는 주체가 객체 그 자체가 되기 때문에, 어댑터가 추가되거나 변경될 때 마다, 객체의 코드가 변경되어야 합니다. 이 경우엔 유연한 협력이나, 블라인드 확장(원래의 컴포넌트가 어떻게 구현되었는지 전혀 모르는 상태에서 기능을 확장시키는 것)이 불가능 합니다. 그러나 서로 모르는 사람들이 가장 많이 참여하는 프로젝트인 이클립스에서는, 이러한 상황이 매우 빈번하게 일어납니다. 이 문서는 모델과 어댑터간의 디커플링을 달성 하는 방법과 원리를 설명합니다.

    어댑터 질의를 플랫폼에 위임

    이런 문제를 해결하기 위해서 이클립스에서는 어댑터 질의를 플랫폼에게 위임하는 전략을 사용합니다.

    기능영역 -> 컴포넌트: getAdapter()
    컴포넌트 -> 어댑터매니저: 위임
    어댑터매니저 -> 확장점: 팩토리 찾기
    확장점 -> 확장점: 확장 탐색
    확장점 -> 어댑터팩토리 : 생성
    어댑터팩토리 -> 어댑터팩토리: 어댑터 생성
    어댑터팩토리 --> 컴포넌트: adapter
    컴포넌트 --> 기능영역: 쿼리 응답
    
    어댑터블한 컴포넌트가 기능영역에 참여하는 기본 원리

    이클립스에서 어댑터블한 객체들은 다음과 같은 기본 구현을 가지고 있습니다.

    public Object getAdapter(Class adapterType){
    	...
    	return Platform.getAdapterManager().getAdpater(this, adapterType);
    }
    

    두 번 째 줄에서 보이는 바와 같이 이 객체는 어댑터 질의를 Platform에게 위임시킵니다. 따라서, 개발자는 플랫폼에 어댑터 공급 방법을 알려줌으로써, 원래 객체를 수정하지 않고도 어댑터를 추가 공급할 수 있습니다. 이렇게 어댑터를 추가적으로 공급하는데 쓰이는 확장점이 바로 런타임 어댑터 (확장점ID: org.eclipse.core.runtime.adapters) 입니다. 이 확장점은 특정한 타입의 객체에 대해, IAdapterFactory를 등록할 수 있게 해 줍니다. 마찬가지로 여러분이 만든 컴포넌트도 런타임 어댑터를 지원하려면, getAdapter()의 마지막 부분에 마찬가지의 코드를 삽입해야 합니다.

    어댑터 팩토리

    public interface IAdapterFactory {
    	public Object getAdapter(Object adaptableObject, Class adapterType);
    	public Class[] getAdapterList();
    }
    

    어댑터 팩토리는 위와 같은 메소드 구성을 가집니다. getAdapterList()는 이 어댑터 팩토리가 어떠한 종류의 어댑터 질의를 처리 할 수 있는지를 나타내고, getAdapter()는 실제로 어댑터를 만들어 플랫폼에 제출하는 역할을 합니다. 확장점 정의에보면, 이미 가능한 어댑터 종류에 대한 노드들이 있는데도 불구하고 구현 클래스도 질의를 받는 인터페이스가 있는 이유는, 확장점 뿐만 아니라, 프로그래밍으로도 어댑터를 등록할 수 있게 하기 위해서 입니다.

    정리

    직접 작성한 객체나 컴포넌트가 아니라고 하더라도, org.eclipse.core.runtime.adapters 확장점을 이용하면 특정 도메인에 대한 어댑터를 별도로 공급할 수 있습니다.

    하는 김에 같이 줏어먹기

    Platform.getAdatper(...) 는 현재 활성화된 플러그인들 중에서만 런타임 어댑터 팩토리를 찾아내어 작동합니다. 만약 모든 플러그인과 연동되게 하려면, loadAdpater(...)를 사용해야 합니다. 이 경우, 어댑터 질의 과정중에 다른 플러그인이 활성화 될 수도 있다는 점을 염두에 둬야합니다.

    Core Expression

    만약 코어 익스프레션에서 adapter 노드를 사용한 경우에, 런타임 어댑터로 등록된 어댑터들만 리턴값이 넘어옵니다. 주의하세요.

    'Eclipse Core' 카테고리의 다른 글

    P2의 주요 용어 정리  (0) 2010.11.29
    아아 좋은 이클립스 삽질이다  (0) 2010.11.01
    Extension Point and Extension  (0) 2010.07.08
    Job과 Schedule Rule  (0) 2009.08.08
    Posted by 지이이이율
    ,