컴퓨터 소프트웨어의 GUI 요소 중에는 잘 알다시피 체크 박스와 라디오 박스가 있다.
전자는 n개의 항목을 제각각 복수 선택할 수 있기 때문에 선택의 가짓수가 2^n개가 가능하다.
그 반면 후자는 n개의 항목 중 하나만 선택할 수 있기 때문에 선택의 가짓수가 딱 n이 된다.

그리고 이런 개념은 사실 메뉴에도 존재한다.
메뉴 항목은 사용 가능 여부(enabled)와 더불어 체크 여부(checked)라는 상태가 존재하여, 자신이 체크된 것처럼 보이는 시각적 피드백을 줄 수 있다.

Windows는 초창기엔(=16비트 시절) 말 그대로 √ 1종류만이 존재했다. 이를 제어하는 함수는 CheckMenuItem이다.
그러다가 Windows 95/NT4에서부터는 ● 모양의 체크를 표시해 주는 CheckMenuRadioItem 함수도 추가되었다. 이로써 각각의 항목들을 따로 체크할 수 있는 메뉴와, 여러 개 중 한 모드만 선택할 수 있는 메뉴의 구분이 가능해졌다.
CheckMenuRadioItem는 특정 메뉴 항목 하나의 속성을 바꾸는 여타 함수들과는 달리, 메뉴 항목들을 여러 개 한꺼번에 지정한 뒤 하나만 체크를 하고 나머지는 체크를 모두 자동으로 해제하는 형태로 동작한다.

그런데 재미있는 것은, MFC는 95/NT4 이전의 16비트 시절에서부터 메뉴에다 custom 비트맵을 지정하는 독자적인 방식으로 라디오 박스를 자체 지원해 왔다는 점이다.
운영체제에 CheckMenuRadioItem가 추가된 뒤에도 내부적으로 그 함수를 쓰지 않는다. 이것은 비주얼 C++ 2012의 최신 MFC도 변함이 없다.

MFC는 동일한 명령 ID에 대해서 메뉴, 도구모음줄 등 여러 GUI 요소에 대해 일관되게 checkd/enabled 상태를 관리할 수 있게 이 계층만을 CCmdUI라는 클래스로 따로 뽑아 냈다. 그리고 윈도우 메시지의 처리가 끝난 idle 시점 때 모든 GUI 상태들을 업데이트한다.
MFC 소스를 보면, CCmdUI::SetCheck는 CheckMenuItem 함수를 호출하는 형태이다. 그러나 CCmdUI::SetRadio는 운영체제의 API를 쓰는 게 아니라 자체 생성한 bullet 모양 비트맵을 SetMenuItemBitmaps로 지정하는 좀 더 힘든 방법을 쓴다.

고전 테마를 포함해 심지어 Windows XP의 Luna에서도 운영체제가 그려 주는 radio 그림과 MFC가 그려 주는 radio 그림은 차이가 거의 없었다. 둘 다 그냥 글자와 동일한 모양으로 동그란 bullet을 그리는 게 전부였다. 그렇기 때문에 두 구현이 따로 노는 건 그리 문제될 게 없었다.

그러나 문제는 Vista 이후에서부터이다. 운영체제가 그리는 radio 그림은 더 알록달록해지고 배경까지 가미되어 화려해진 반면, MFC가 그리는 radio 그림은 아직까지 단색의 단조로운 bullet이 전부이다. 그래서 시각적으로 이질감이 커졌다. 그것도 일반 체크(√) 항목은 괜찮은데 라디오(●) 그림만 차이가 생긴 것이다.

사용자 삽입 이미지사용자 삽입 이미지

이해를 돕기 위해 그림을 첨부한다. Windows Vista 이후에 운영체제가 메뉴에다 그려 주는 라디오 체크는 배경에 은은한 무늬가 생겨 있다(왼쪽). 그러나 MFC가 그리는 라디오 체크는 여전히 옛날 스타일대로 단색 동그라미밖에 없으며, 일반 체크와도 형태가 다르다(오른쪽). 오른쪽의 프로그램은 본인이 예전에 MFC 기반으로 개발했던 오목 게임이다. ㅋㅋ

MFC는 운영체제의 새로운 함수를 왜 쓰지 않는 걸까?
그냥 이런 사소한 데에까지 신경을 안 써서 그런 것일 수도 있고, 또 CCmdUI는 각각의 메뉴 항목에 대해 개별적으로 호출되는 반면 CheckMenuRadioItem는 그 자체가 여러 메뉴 항목의 상태를 한꺼번에 바꾸는 함수이기 때문에 기능의 구현 형태가 서로 맞지 않아서 도입하지 않은 것일 수도 있다.

물론, SetMenuItemInfo라는 만능 함수를 쓰면, 개별적으로 라디오 체크 상태를 바꾸는 것도 불가능하지는 않다. 다만, 구조체를 준비해야 하는 데다, 상태(state)만 옵션으로 간단히 바꾸면 되는 게 아니라 메뉴의 유형(type)까지 바꿔야 하니 일이 좀 번거로운 건 사실이다.

다만, 요즘은 MFC에도 잘 알다시피 MS Office나 Visual Studio의 모양대로 GUI 외형을 싹 바꿔 주는 툴킷이 도입되었고, 이런 상태에서는 어차피 메뉴의 요소들이 무조건 모조리 자체적으로 그려진다. 그러니 저런 SetRadio와 SetCheck의 동작 방식의 차이 같은 것도 존재하지 않으며, 그런 걸 논하는 게 아무 의미가 없다. 저건 오로지 운영체제 표준 GUI를 쓸 때만 발생하는 이슈이기 때문이다. ^^

* 글을 맺으며..

WinMain 함수를 포함해 윈도우 클래스 등록, 프로시저 구현을 전부 직접 하면서 Windows용 응용 프로그램을 밑바닥부터 만들어 본 사람이라면, MFC가 내부적으로 프로그래머에게 몰래 해 주는 일이 얼마나 많은지를 어렴풋이 짐작할 수 있다.

  • 대화상자를 창의 가운데에다 배치해 주는 것,
  • 프레임 윈도우와 뷰 윈도우 사이의 경계에 깔끔한 입체 모양 테두리 넣는 것,
  • 고대비 모드일 때 도구 아이콘의 검은색을 흰색으로 바꾸는 것,
  • 심지어 콤보 박스 내부에 디폴트 데이터(리소스 에디터에서 만들어 넣었던)들을 집어넣는 것,
  • 프레임 윈도우가 키보드 포커스를 얻었을 때 그 아래의 view 윈도우로 포커스를 옮기는 것,
  • 프로퍼티 시트의 내부에 들어가는 프로퍼티 페이지들의 글꼴을 운영체제 시스템 글꼴로 바꾸는 것 등..

이런 사소한 것들도 공짜가 아니라 죄다 MFC가 내부에서 해 주는 일들이다.
Windows API만 써서 프로그램을 만드는 방식은 최고의 작고 가볍고 성능 좋은 프로그램을 만들 수 있지만 생산성도 미칠 듯한 저질이기 때문에, 인제 와서 이런 불편한 방식으로 프로그램을 만들 프로그래머는 거의 없을 것이다. 요즘 세상에 C++도 아닌 C는 사실상 어셈블리나 마찬가지다.

Posted by 사무엘

2013/04/29 08:34 2013/04/29 08:34
, ,
Response
No Trackback , 8 Comments
RSS :
http://moogi.new21.org/tc/rss/response/824

Trackback URL : http://moogi.new21.org/tc/trackback/824

Comments List

  1. 김재주 2013/04/29 19:03 # M/D Reply Permalink

    이제 사실상 윈도 API를 직접 사용해서 C로 코딩해야 하는 상황은 커널에 붙어서 동작해야 할 드라이버 말고는 없다고 봐야겠죠. 아직까지는 Native Code가 performance 측면에서 장점을 가지고 있기 때문에 MFC와 C++이 여전히 쓰이고 있습니다만 C#과 JAVA로 대세가 넘어가는 것은 시간 문제가 아닐까 저는 생각하고 있습니다.

    이미 JIT 컴파일러가 실시간으로 profile을 통해 생성해내는 바이너리 코드가 static하게 생성된 프로그램을 뛰어넘는 경우가 보고되고 있으니까요. 특히 입력의 특성에 따라서 그때그때 분기의 흐름이 아주 다양하게 변화할 수 있는 프로그램의 경우엔 static profile guided optimization의 성능이 dynamic 버전을 따라가기 어려우니까요. 물론 이 방법에도 단점은 존재하지만...

  2. Lyn 2013/04/29 19:40 # M/D Reply Permalink

    jit은 이론상 스태틱한 컴파일러의 성능을 뛰어 넘고 실제로 최근들어 그런 강력한 jit 컴파일러들이 속속 등장하고 있지만 GC가 속도 다 까먹고있죠 (...)

    뭐 요즘은 드라이버나 펌웨어도 C++로 짜니...

  3. Lyn 2013/04/29 19:45 # M/D Reply Permalink

    그나저나 UI 프로그램을 전혀 안하다보니 저런것들이 다른지는 몰랏네요

  4. 사무엘 2013/04/29 23:20 # M/D Reply Permalink

    김재주, Lyn: 요즘은 MFC를 비롯해 C/C++의 라이브러리들 오버헤드도 너무 커져 보인다는 게 함정.
    그러니 차라리 그런 공통된 오버헤드를 운영체제가 미리 다 담당하고 있는 더 상위 계층의 프레임워크의 존재 명분도 섭니다.
    하지만 IME 쪽은 만년 네이티브 코드 강세 지대랍니다. ^^;;

    그리고 아무리 성능 크리티컬한 환경이라도 요즘 어지간해서는 C++조차도 없이 C로만 코딩을 해야 하는 곳은 진짜 거의 없어지지 않았을까 싶네요.

    라디오 체크 모양의 차이는 저도 최근에 발견하고는 흥미로워서 블로그 글로 정리했습니다.
    IME 개발하려면 운영체제의 기본 UI하고는 쫙 밀착을 해야 한답니다. =_=

  5. 김재주 2013/05/01 11:42 # M/D Reply Permalink

    임베디드 쪽에선 아직 C로 짜야 하는 일이 흔합니다. 예를 들어 SD나 CF카드 내부의 FTL같은 곳이 그렇죠. 이쪽은 RAM도 SRAM이고 1메가에도 턱없이 모자라는 메모리 위에서 동작하는데다 턱없이 적은 소비전력만으로 동작해야 하니까요..

    PC에서라면 말씀하신 것처럼 이제 C만으로 코딩할 일은 정말 없겠죠.

  6. Lyn 2013/05/02 09:04 # M/D Reply Permalink

    PC가 아니라 어지간한데도 다 ...

    이미 예전에 임베디드라고 불리던 급의 기계들도 다 범용 OS 깔리게된지 한참이니 ...

  7. 사무엘 2013/05/02 09:44 # M/D Reply Permalink

    음, 그런데 자원이 너무 열악해서 C++ 컴파일러조차 돌릴 수 없는 모바일이라 해도 크로스 컴파일은 가능하지 않을까요? 그것조차도 안 돼서 C만 써야 하면 정말 암울할 듯.

  8. 김재주 2013/05/03 13:11 # M/D Reply Permalink

    ARM을 쓰는 환경이라도 정말 천차만별이라.. 플래시 메모리 카드 같이 칩을 엄청 소형화해야 하는 경우에는 메모리와 CPU마저도 원칩에 통합시켜야 할 경우가 있습니다. 모바일 DRAM으로는 커버가 안 될 정도로요. 이 경우 SRAM을 사용해야 하는데 보통 96KB(!) 정도를 사용합니다.

    C++을 크로스 컴파일해서 코드를 생성하는 것 자체는 가능한데, 이런 환경에서는 가상 생성자라든지 아무튼 C++에서 지원하는 고급 프로그래밍 언어 기술을 사용하기 위해서 구조체에 추가적으로 붙게 되는 그 약간의 공간마저도 상당히 아깝다는 거죠.

    그리고 이런 칩에 들어가는 프로그램들은 말 그대로 어떤 특수한 목적을 위한 전용 코드에 가깝기 때문에 OOP를 사용해도 그다지 이점이 없다는 것도 있습니다. 기껏해야 수천에서 수만줄밖에 안되는 프로그램이니까요.

Leave a comment
« Previous : 1 : ... 1410 : 1411 : 1412 : 1413 : 1414 : 1415 : 1416 : 1417 : 1418 : ... 2131 : Next »

블로그 이미지

그런즉 이제 애호박, 단호박, 늙은호박 이 셋은 항상 있으나, 그 중에 제일은 늙은호박이니라.

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2024/03   »
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

Site Stats

Total hits:
2633636
Today:
434
Yesterday:
1754