GUI는 머리 속에서 지우자.

[1] Undo/Redo 기능을 넣어보시오.

거창하게 연재식으로 쓰자니 쑥스럽다. 이 전 글에서 대표적으로 undo/redo를 예로 들어, 덩치 큰 프로그램을 어떻게 설계하는 것이 합리적인지에 대해 이야기를 꺼내보았다. 요약을 하면, 결국 "어떻게 표현할 것인가"라는 전형적인 일반화 문제로 귀결된다. 이제 보다 구체적인 방법론을 살짝 이야기 해보자.

이전 글의 답글에도 썼고, Console Interface라는 글에서도 이미 언급이 되었듯이 많은 프로그래머들이, 특히 윈도우 프로그래머들이 가지고 있는 선입관에 대해서 이야기를 하려고 한다.

예를 들어, 당신은 지금까지 Win32 API와 MFC를 공부한 윈도우 프로그래머이고 이제 파워 포인트와 같은 제품을 만들어야 한다고 하자. 머리 속에 떠오르는 것은 화려한 그래픽 인터페이스이다. 아마 (나를 포함해서) 대부분의 개발자들은 이렇게 시작을 할 것이다.

MFC 어플리케이션 프로젝트를 열고, 이제 CMainFrame::OnCreate에서부터 프로그램을 짜기 시작한다. 그리고 '새 문서' 기능을 넣기 위해서 리소스 편집기에서 메뉴 항목을 추가하고 거기에 맞는 이벤트 핸들러 CMainFrame::OnFileNewDocument를 삽입하고 이제 살을 붙이기 시작한다.

C# 프로그래머라면 Windows Forms 응용프로그램을 선택하고 버튼 만들고 더블 클릭해서 자동으로 만들어주는 이벤트 핸들러에서 코딩을 시작할 것이다.

이 방법이 잘못되었다는 것이 결코 아니다. 간단한 프로그램은 당연히 이렇게 짜야 한다. 그러나 파워 포인트와 같이 아주 큰 프로그램은 이렇게 짜면 나중에 고생을 많이 하게 된다. 특히, undo/redo를 넣어보려고 한다면 앞이 캄캄할 것이다. 그럼 어떤 방식으로 접근하는 것이 좋은가?

이미 답을 말 했다. 콘솔을 떠올리는 것이다. 즉, 프로그램에서 화려하고 복잡한 그래픽 인터페이스를 완전히 제외하라는 것이다.

파워 포인트 및 각종 편집을 할 수 있는 프로그램들을 아주 일반화를 시키면 무엇일까? "일반적인 컴포넌트들을 조작하는 것"으로 추상화 시킬 수 있다. 컴포넌트를 만들고 지우며 그리고 그 속성을 편집하는 것이다. 그리고 이 과정은 파일로 저장할 수 있어야 하며 다시 읽어올 수 있어야 한다. 물론, undo/redo도 당연히 포함이 되어야 한다. 지금까지 말한 기능은 복잡한 GUI를 전혀 필요로 하지 않는다. 아주 기초적인 콘솔 입력 창만 있어도 만들 수 있다.

이런 부분을 이제 '엔진'이라고 부르자. 엔진이라는 단어가 너무 추상적이고 포괄적이어서 모호한 부분도 있지만 어쨌든 GUI를 제외한 순수 커맨드라인 기반의 핵심 프로그램을 엔진이라 부르자. 그리고 GUI는 여기에 덧붙는 껍데기, 말 그대로 shell일 뿐이다.

리눅스 툴체인에 익숙한 분이라면 지금까지 한 말이 너무나 당연한 말일 것이다. Eclipse도 커맨드 라인 기반의 툴체인을 감싼 껍데기에 불과하다. Visual Studio는 그렇지 않을 것 같은가? MSVC도 얼마든지 커맨드 라인에서 컴파일할 수 있다 (다만 gdb에 해당하는 커맨드라인 디버거는 보지 못했음). 그리고 여기에 VS라는 거대한 GUI 인터페이스를 결합한 것이다. 파워 포인트? 마찬가지다. 파워 포인트도 놀랍게도 프로그래밍으로 문서를 만들 수 있다. 이것은 곧, GUI는 단순히 마우스라는 하나의 입력 방법을 전달해주는 도구, 그리고 그 문서의 내용을 시각적으로 표현해주는 껍데기에 지나지 않음을 뜻한다.

엔진은 자료구조를 조작하는 프로그램의 핵심 요소라고 할 수 있다. 그리고 GUI는 이런 자료구조의 상태를 아름답게 표현한다. 그리고 콘솔로 제한되어있는 (보다 구체적으로는 stdin, stdout) 인터페이스를 보다 다양한 방법으로 확장시키는 일을 하는 것이다.

그리고, 여기서 GUI와 엔진은 끊임없이 서로 연락을 주고 받아야 한다. 그리고 그 중의 핵심은 '이벤트'이다. 너무 쉽다. 윈도우 프로그래밍을 시작하면서 듣는 '이벤트 프로그래밍'의 그 이벤트이다. 말이 길다. 이 모든 것을 그림으로 설명하자.

엔진은 우리가 목표로 하는 것들을 표현하는 내부 자료구조를 가지고 있다. 그리고 이제 이 내부 자료구조를 수정할 수 있어야 한다. GUI는 엔진이 제공하는 가장 기초적인 커맨드 라인 외에, 복잡한 키보드, 마우스 메시지 등을 해석하여 '명령 (Command)'라는 것으로 추상화를 하여 엔진으로 보낸다. 그러면 엔진은 이 명령에 맞게 자료구조를 변경한다. 그리고 변경 사항이 있으면 '이벤트'를 통해 바깥 세계로 알린다. GUI는 단순히 이 이벤트를 잡아서 각종 GUI를 변경하면 된다.

여기까지 거창한 디자인 패턴 따위는 전혀 필요 없다. 이벤트 프로그래밍은 누구나 다 아는 것 아닌가! 거대한 프로그램을 만들 때, GUI와 엔진 부분을 분리하자라는 생각만 가져도 모든 것이 쉽게 바뀔 수 있다. 거짓말 아니고 이렇게만 짜면 모든 것이 쉽다.

그렇다 해서 아주 엄격하게 GUI를 분리하라는 것이 아니다. 비록 파워 포인트의 문서를 프로그래밍으로 만들 수 있도록 엔진이 분리가 되어있지만 아무도 그런 방식으로 만들지 않는다. 그러나 실제 사용자들이 콘솔 기반으로 작업하지 않는다고 하더라도, 개발 도중에는 항상 이런 생각을 가지고 개발을 해야 한다.

이렇게 엔진과 GUI 부분을 나눴을 때 좋은 점을 정리해보자.

  1. 생각을 단순히 할 수 있다. 머리 속에서 복잡한 GUI는 싹 잊어 버릴 수 있다.
  2. 엔진의 무결성을 검증하기도 편하며, 버그를 찾기도 한결 손 쉽다.
  3. 커맨드 라인 기반은 스크립트화 하기 쉽기 때문에 각종 테스트에서도 유용하다.
  4. 엔진은 플랫폼 독립적으로 작성하기가 훨씬 용이하다.

반면, 단점도 존재할 것이다. 바로 귀찮음. 그리고 왜 자꾸 그냥 OnFileNewDocument()에서 코드 넣고 싶은데 복잡하게 하라는 소리야? 그냥 GUI 함수를 호출하면 되지 왜 귀찮게 이벤트라는 것으로 인터페이스를 추상화 해야 하는 거야? 라고 질문하는 초보 프로그래머들 혹은 팀 동료들을 설득하는 일이다.

또, 누군가 이렇게 짜면 코드 양이 늘어나지 않느냐라고 반문할 수 있다. 그렇다. 모든 것이 그렇지만 한번 더 추상화 과정을 거치는 것은 성능이라는 측면에서 결코 바람직하지 않다. 무수한 virtual 함수와 많은 예외(try-catch)는 임베디드 시스템 같은 곳에서는 치명적이다. 그러나 지금 우리가 생각하고 있는 파워포인트는 이런 걱정 할 필요가 없다. 프로그램의 성능은 다소 손해 보더라도 보다 깔끔하고 일반화된 컴포넌트 표현 방식이 훨씬 높은 생산성을 가져다 준다. 새로운 사용자의 요구가 들어왔을 때도 유연하게 대처할 수 있다. 결국 디자인 패턴에서 말하는 loosely coupled components를 위하는 방법이기도 하다.

정말 쉽다. 다 아는 이야기 떠드니까 사기치는 것 같다. 다음에는 실제 이렇게 엔진과 GUI 부분을 이벤트로 연결해서 만들 때 어떠한 이점이 있는지 구체적인 예를 통해 살펴보자. 이 이야기는 좀 더 나중에 할 예정.

지금까지 말한 것을 바탕으로 만든 어떤 프로그램의 작동 모습:

ps. 여러분이 짠 MFC 프로그램이 얼마나 loosely coupled 되어있는가를 확인하려면 한번 CMainFrame의 헤더파일 (MainFrm.h)를 수정하고 컴파일 하였을 때, 얼마나 많은 파일들이 재 컴파일 되는지 살펴보는 것이 하나의 방법.

by object | 2007/08/05 07:16 | 컴퓨터 | 트랙백 | 핑백(2) | 덧글(10)
트랙백 주소 : http://minjang.egloos.com/tb/1385829
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Linked at art.oriented : 컴.. at 2007/08/24 18:01

... [1] Undo/Redo 기능을 넣어보시오. [2] GUI는 머리 속에서 지우자. 지금까지 한 이야기를 간단히 요약을 해보자. 지금 파워포인트와 같은 편집 프로그램을 만들려고 한다. GUI는 머리 속에서 지우고 모든 것을 커맨드 라인 기반 ... more

Linked at art.oriented : 구.. at 2008/09/07 14:19

... C로 해당 프로세스로 보내면 되고, 반대로 프로세스 내부에서 어떤 이벤트가 발생하면 다시 IPC로 전달해 최종적으로 UI를 바꾸도록 하면 될 것이다. 복잡한 프로그램을 짤 때 GUI와 엔진을 분리하는 전형적인 방식일 것이다. 직접 소스나 관련 문서를 찾아보기 전에 한 번 Spy++와 Process Explorer로 조사 해보자. 각각 두 탭씩 두 개의 브라우저 ... more

Commented by 오스카 at 2007/08/05 07:43
좋은 글 잘 읽었습니다. 다음 글도 기대되네요. ^^
Commented by Ego君 at 2007/08/05 07:44
MFC는 대충 아는 거라서, 좋은 글 잘 읽었습니다 :D
Commented by uriel at 2007/08/05 09:58
훌륭한 글이네요. 예전에 MFC/ATL 응용프로그래머로 살았던 때가 있어서 가슴에 와닫습니다.
Commented by 하얀사자 at 2007/08/05 10:19
공부 많이 됬습니다. 좋은 글 감사합니다.
Commented by louis at 2007/08/05 10:45
전혀 다른 이야기지만, 저는 저 위에 art.orient 글꼴이 좋아요. 이름이 뭐죠? -_-
Commented by object at 2007/08/05 11:02
Bitstream Vera Sans Mono라는 폰트입니다. 이 폰트가 없으면 일반적인 Verdana가 표현이 됩니다. Bitstream은 일반적으로 컴퓨터에 깔려있지 않으니까 Verdana로 표현이 될 것 같은데, Verdana는 흔한 폰트입니다. 한번 확인해보세요. Bitstream Vera 폰트는 http://www.gnome.org/fonts/ 에서 받을 수 있습니다.

조만간 후속편으로 찾아뵙겠습니다 ㅎㅎ
Commented by havien at 2007/08/05 13:51
이건 정말 경험에서 나오는 글인데, 좋은글 감사합니다.
Commented by 백승우 at 2007/08/05 19:19
잘읽었습니다.^^
GUI부터 만들기 시작하면 사용자의 요구사항이 늘어나면 제대로 고생하겠죠..^^
Commented by wookay_noh at 2007/08/06 12:13
웹 개발도 저런식으로 하고 싶어요.
Commented by object at 2007/08/06 20:04
감사합니다. 생각의 전환이 때론 참 많은 것을 편하게 할 수 있다는 것을 지난 삽질로부터 뼈저리게 느낄 수 있었습니다.

:         :

:

비공개 덧글

<< 이전 페이지 다음 페이지 >>





by object 여기는 공사중....
최근 등록된 덧글
최근 등록된 트랙백
VisualStudio 2005에서 Gui..
by 셈말짓기
SSD와 WD의 벨로시랩터
by 정보와 휴식...그리고 미래

한RSS 구독자수 website counter

한RSS에 추가

Add to Google

rss

skin by 이글루스