|
거창하게 연재식으로 쓰자니 쑥스럽다. 이 전 글에서 대표적으로 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 부분을 나눴을 때 좋은 점을 정리해보자.
반면, 단점도 존재할 것이다. 바로 귀찮음. 그리고 왜 자꾸 그냥 OnFileNewDocument()에서 코드 넣고 싶은데 복잡하게 하라는 소리야? 그냥 GUI 함수를 호출하면 되지 왜 귀찮게 이벤트라는 것으로 인터페이스를 추상화 해야 하는 거야? 라고 질문하는 초보 프로그래머들 혹은 팀 동료들을 설득하는 일이다. 또, 누군가 이렇게 짜면 코드 양이 늘어나지 않느냐라고 반문할 수 있다. 그렇다. 모든 것이 그렇지만 한번 더 추상화 과정을 거치는 것은 성능이라는 측면에서 결코 바람직하지 않다. 무수한 virtual 함수와 많은 예외(try-catch)는 임베디드 시스템 같은 곳에서는 치명적이다. 그러나 지금 우리가 생각하고 있는 파워포인트는 이런 걱정 할 필요가 없다. 프로그램의 성능은 다소 손해 보더라도 보다 깔끔하고 일반화된 컴포넌트 표현 방식이 훨씬 높은 생산성을 가져다 준다. 새로운 사용자의 요구가 들어왔을 때도 유연하게 대처할 수 있다. 결국 디자인 패턴에서 말하는 loosely coupled components를 위하는 방법이기도 하다. 정말 쉽다. 다 아는 이야기 떠드니까 사기치는 것 같다. 지금까지 말한 것을 바탕으로 만든 어떤 프로그램의 작동 모습: ![]() ps. 여러분이 짠 MFC 프로그램이 얼마나 loosely coupled 되어있는가를 확인하려면 한번 CMainFrame의 헤더파일 (MainFrm.h)를 수정하고 컴파일 하였을 때, 얼마나 많은 파일들이 재 컴파일 되는지 살펴보는 것이 하나의 방법.
최근 등록된 덧글
zzzzzzzzzzzzzzzzz..
by xxx at 23:10 저도 논문을 위해 작문위주의.. by Gerald at 10/11 opaque type은 내부를 알 .. by 몽몽이 at 10/09 샘이님 말씀에 한 표~ ^^;; by 엽우 at 10/09 Globefish 라는 Firefox .. by nurigis at 10/09 커스텀 사전에서 명사로 등.. by 게드 at 10/09 그렇네요. 제가 가지고 있는.. by object at 10/09 전산용어가 되면 뭐든지 셀 .. by 샘이 at 10/09 최근 등록된 트랙백
|