. 컨트롤화의 필요성

지금까지 만들어 온 ApiEdit 예제는 오버랩드 윈도우로 작성되어 있다. 그래서 타이틀바를 가지고 있으며 최대, 최소화가 가능하고 스스로 위치를 옮길 수 있다. 이런 구조에서는 ApiEdit의 편집기능을 재활용하기가 무척 어렵다. ApiEdit가 다른 윈도우의 차일드가 될 수 없기 때문에 대화상자 안에서 편집을 하도록 한다거나 한꺼번에 두 개의 문서를 편집하도록 할 수가 없는 것이다.

ApiEdit의 편집기능을 다방면으로 활용하고 싶다면 버튼, 에디트, 리스트박스와 같은 독립된 컨트롤로 만들어야 한다. 그래야 필요한 곳에 자유롭게 배치할 수 있으며 복수 개의 인스턴스를 생성할 수 있다. 컨트롤은 다른 윈도우의 차일드로만 생성되며 절대 메인 윈도우가 되지 않는다. 버튼이나 스크롤바 따위가 어떻게 메인 윈도우가 될 수 있겠는가?

그래서 ApiEdit는 애초에 텍스트 편집기가 될 수 없으며 텍스트 편집 컨트롤이 되어야 하는 것이다. 텍스트 편집기는 따로 만들고 ApiEdit는 이 편집기의 차일드 컨트롤로 배치되어 실제 텍스트 편집을 하는 것이 구조적으로 옳다. 메모장을 보면 이런 구조를 확실하게 목격할 수 있는데 메모장의 작업영역에 에디트 컨트롤이 배치되어 텍스트를 편집한다. 에디트 컨트롤 자체가 메모장이 되는 것이 아니다.

워드프로세서나 그래픽 편집 프로그램들도 실제 편집기능을 제공하는 것은 컨트롤이지 응용 프로그램 자체가 아니다. 응용 프로그램은 컨트롤과 사용자를 인터페이스 하는 중간자 역할을 할 뿐이다. 이런 컨트롤 중심의 가장 대표적인 예로 웹브라우저인 인터넷 익스플로러(IE)와 동영상 편집기인 미디어 플레이어를 들 수 있는데 프로그램의 기능 90%가 컨트롤에 의해 구현되며 응용 프로그램은 별로 하는 일이 없다. IE의 웹페이지 해석 능력은 웹브라우저 컨트롤에 의해 구현되어 있으며 그래서 탐색기나 비주얼 스튜디오도 이 컨트롤만 사용하면 웹 서핑이 가능한 것이다.

이 장에서는 ApiEdit의 활용성을 높이기 위해 오버랩드 윈도우로 되어 있는 ApiEdit 윈도우를 차일드 윈도우로 바꾸어 컨트롤로 변환할 것이다. 결국 컨트롤로 만들 예정이었다면 처음부터 컨트롤 형태로 시작해서 만들어 올 수도 있었다. 그러나 그렇게 하지 않은 이유는 컨트롤은 혼자서 동작할 수 없기 때문에 반드시 테스트 예제(호스트)를 같이 만들어야 제대로 돌아가는지 점검해 볼 수 있기 때문이다. 두 개의 모듈을 따로 유지하는 것은 무척이나 번거로운 일이라 실습용으로는 적합하지 않다.

중간에 컨트롤로 전환한다고 해서 특별히 작업이 많아지는 것은 아니며 모든 논리는 그대로 유지되고 코드도 대부분 변경없이 재사용된다. 다만 컨트롤화를 하는 방법에 따라 약간씩의 추가 코드가 필요하기는 하다. 이미 만든 윈도우를 컨트롤화하는 것은 기존의 코드를 재사용이 쉬운 형태로 변환하는 기법이라 할 수 있는데 잘 알아 두면 실무에 유용하게 사용될 것이다.

컨트롤은 화면에 보이며 사용자와 직접 통신할 수 있다. 사용자는 컨트롤로부터 원하는 정보를 얻고 또 컨트롤을 통해 원하는 값을 입력한다. 컨트롤은 자신이 제공하는 기능을 사용자가 자유롭게 사용할 수 있도록 키보드나 마우스, 팝업메뉴 등의 다양한 인터페이스를 제공해야 한다. 에디트 컨트롤을 예로 들면 키보드를 통해 문자열을 입력받고 마우스로 선택할 수 있으며 팝업메뉴로 클립보드 동작을 한다.

또 컨트롤은 다른 윈도우의 차일드로 존재하기 때문에 응용 프로그램의 통제를 받아야 하며 응용 프로그램은 컨트롤이 제 기능을 발휘하도록 위치 지정, 크기 설정, 다른 컨트롤과의 관계 등 환경을 만들어야 한다. 컨트롤이 이런 통제를 받으려면 응용 프로그램이 컨트롤을 마음대로 조작할 수 있는 프로그래밍 인터페이스를 제공해야 한다. 에디트 컨트롤은 EM_GETSEL 같은 메시지를 제공하여 응용 프로그램이 선택영역을 조사할 수 있도록 하며 텍스트가 변경될 때 EN_CHANGE 통지 메시지를 보내준다.

, 컨트롤이 구현해야 하는 기능은 사용자가 직접 사용하는 기능과 응용 프로그램을 위한 기능으로 분류할 수 있다. 전자를 유저 인터페이스라고 하며 후자를 프로그래밍 인터페이스라고 한다. 키보드 입력 기능이나 마우스 선택 기능은 당연히 사용자를 위한 기능들이므로 컨트롤이 직접 제공해야 하지만 다음 기능들은 직접 제공할 필요가 없으며 프로그래밍 인터페이스 형태로 제공하는 것이 좋다.

 

파일 입출력 기능 : 파일은 메모리 외부에 있으며 어떤 경우는 네트워크를 통해 가져올 수도 있는데 이 작업은 응용 프로그램이 해야 한다. 컨트롤은 파일의 내용을 버퍼에 넣어주면 편집할 수 있도록 하고 편집 결과를 요구하면 버퍼의 내용을 다시 돌려주기만 하면 된다. 에디트 컨트롤을 보면 알겠지만 직접 파일 입출력을 하는 기능은 없고 대신 Get(Set)WindowText 함수로 버퍼 내용을 읽고 쓰도록만 한다.

설정 변경 기능 : 정렬 상태나 제어문자 보기, 현재 행 보기, 폰트 설정 등의 기능은 사용자가 컨트롤을 통해 직접 조정하는 것이 아니다. 컨트롤은 프로그래밍 인터페이스만 제공하고 설정 변경은 응용 프로그램이 필요하다고 판단될 때, 그리고 해당 설정을 변경할 수 있을 때 직접 하게 된다. EnableWindow 함수로 컨트롤의 사용금지 여부를 결정하는 것은 응용 프로그램이 할 일이지 사용자가 직접 컨트롤을 사용 금지 시키는 것이 아니다.

설정 저장 기능 : 컨트롤은 레지스트리를 직접 액세스하지 않는다. 설정상태나 최근 읽은 파일 목록 같은 정보들은 응용 프로그램이 저장했다가 다음 번 실행할 때 컨트롤에게 넘겨 주는 것이다. 컨트롤은 호스트가 제공하는 환경 안에서만 제대로 동작하면 된다.

동작에 필요한 정보 획득 : 컨트롤은 사용자가 직접 사용하지만 동작에 필요한 대부분의 정보는 호스트가 사용자에게 입력받아 컨트롤에게 전달한다. 예를 들어 ApiEdit가 검색 기능을 제공한다고 해보자. 검색 문자열이나 옵션 등을 입력받는 대화상자를 ApiEdit가 가지는 것이 아니며 호스트가 대신 이 옵션을 입력받아 ApiEdit에게 검색 명령을 내리게 된다.

 

컨트롤은 프로그램을 구성하는 단위 부품이 되는데 부품은 너무 많은 일을 하는 것보다 맡겨진 임무에만 충실하도록 작고 가볍게 만드는 것이 더 좋다. 그래야 이런 부품을 조립하는 호스트가 부담없이 컨트롤을 활용할 수 있다. 사용자에게 직접 제공해야 할 기능과 숨겨야 할 기능을 구분하는 절대적인 기준은 없으며 그때그때 상황에 따라 기능의 포함여부를 결정해야 한다. 다만 기능을 포함시킬 때 재사용성과 경제성을 염두에 두어야 한다.

재사용성이란 어떤 환경에서도 잘 동작하는 특성이다. 특정 응용 프로그램을 위한 전용 컨트롤이라면 재사용성을 고려할 필요가 없지만 임의의 응용 프로그램에 사용될 컨트롤이라면 어떤 상황에서라도 잘 조화될 수 있는 범용성을 가지고 있어야 한다. 컨트롤이 제대로 동작하기 위해서 특정한 설정을 먼저 해야 한다거나 파일 시스템이 반드시 NTFS여야 한다는 등의 제약 사항이 있어서는 안되며 불가피할 경우는 최소화해야 한다. 재사용에 불리한 기능은 호스트가 구현하도록 하는 것이 더 좋다.

경제성이란 컨트롤의 목적에 맞는 꼭 필요한 기능만을 제공하는 것이다. 고급 기능은 그만큼 많은 메모리와 CPU를 요구하므로 기능이 많다고 해서 무조건 좋은 것은 아니다. 버튼 컨트롤은 사용자로부터 명령을 입력받을 때 사용하므로 이 이상의 기능을 굳이 제공할 필요는 없다. 예를 들어 버튼이 클릭될 때 소리를 낸다거나 그래픽 파일을 읽어 버튼 표면에 보여 준다거나 할 필요까지는 없다. 이런 기능을 구현하는 것은 호스트의 몫이지 컨트롤의 몫이 아니다.