« Previous : 1 : ... 147 : 148 : 149 : 150 : 151 : 152 : 153 : 154 : 155 : ... 221 : Next »

1. 라틴 알파벳의 위엄

오늘날 세계 문자들 중에 라틴 알파벳은 인지도와 실용성 면에서 단연 절대적인 '갑'임을 부정할 수 없다. 라틴 알파벳은 다음과 같은 여러 장점과 유리한 점이 있다.

  • 어느 한 국가나 민족만의 문자가 아니다. 물론 나라의 정서법마다 알파벳을 읽는 방식에 대동소이한 차이가 있어서 혼동스러운 면모도 있지만, 어쨌든 가장 국제적이다.
  • 음소문자여서 나름 다양한 언어의 말소리에 대응하기 유리하다. 또한 풀어쓰기를 하는 구조여서 언어의 다양한 음절 구조에 대응하기에도 유리하다.
  • 글자 수가 적어서 간편하며 활용하기도 쉬운 구조이다. 가령, 세계의 문자들 중에 기계식 타자기로도 만들고 또 겨우 8*8 크기의 비트맵에 각 글자의 모양을 다 담을 수 있는 문자는 얼마 되지 않을 게다.
  • 각각의 글자들이 들쭉날쭉하고 개성이 뚜렷해서 한데 뭉쳤을 때 시각성이 뛰어나며, 기호로서의 역할도 하기 좋다.
  • 타이포그래피 관점에서는 문자 차원에서 대소문자 구분이 존재하고, 또 이탤릭체 같은 같은 방식으로도 호소력을 높일 수 있다.

물론 라틴 알파벳은 표음· 음소문자라는 취지와는 달리 현실은 시궁창으로 언문일치가 개떡이며, 구조적으로 모음 글자가 너무 부족하다는 한계도 있긴 하다.

그럼에도 불구하고 라틴 알파벳은 신경 써야 하는 글자 수가 겨우 수십 개에 불과하기 때문에, 개개의 글자가 그야말로 왕이며, 서체 디자이너는 각 글자에 대해 완전 올인 몰빵 최적화가 가능하다. 이게 개인적으로 가장 부럽게 느끼는 점이다.
가령, A~Z까지 아주 정교하게 테스트된 수제 힌팅 프로그램을 집어넣는 여유를 부리는 건 이미 20여 년 전에 트루타입 글꼴이 처음으로 도입됐을 때부터 있었던 일이다.

그리고 요즘 글꼴이라는 건, 그저 코드값별로 고정된 글자의 폭과 벡터 이미지만을 기술하는 static한 데이터의 집합이 아니다. OpenType 기술 규격 덕분에 자기 옆에 무슨 글자가 오느냐에 따라서 다른 모양을 제공할 수 있고(GSUB), 폭을 미세하게 달리할 수도 있다(커닝.. 이게 개념적으로 더 확장되어 GPOS).

알파벳 정도야 문자 개수를 제곱해 봐야 몇백~몇천 정도까지의 조합밖에 안 나오니, 그 정도는 감당 가능하다. 한글이 '가' 할 때 ㄱ과 '고'나 '강' 할 때 ㄱ의 모양이 서로 달라지는 게, 알파벳으로 치면 W 다음에 A가 올 때와 W 다음에 P가 올 때의 간격을 미세하게 달리 설정하는 것과 같다는 뜻이다. 무슨 뜻인지 아시겠는가?

이런 기술은 이런 게 반드시 있어야만 정서법 차원에서 제대로 표시가 가능한, 복잡한 외국어 문자(complex script. 태국어나 아랍어 문자 같은)를 위해서 개발된 것이다. 그런데 라틴 알파벳은 원래 그런 기술의 도움을 받아야 할 정도로 복잡한 문자가 아님에도 불구하고, 더 정교하고 아름다운 타이포그래피를 위해 서체 디자이너가 그런 최신 기술까지 적용하여 시쳇말로 가히 잉여짓을 하고 있는 것이다. 가령, 라틴 알파벳도 아주 정교하고 미려한 필기체 중에는 그렇게 다이나믹한 표시 조건이 필요한 것도 있으니 말이다.

2. 반대편 극단에 있는 한자와 가나

이렇게 소수의 유한한 글자들이 왕인 라틴 알파벳 문화권에 비해, 글자 수가 엄청 많은 CJK는 상황이 굉장히 다르다.
한자는 전세계의 문자들 중 유일하게 '열린-_- 집합'인 무지막지한 문자 시스템이다. 덕분에 가변폭-_-이라든가 글자간의 다이나믹한 상호작용 같은 개념은 전혀 존재하지 않는다. 그래도 각각의 글자 자체가 워낙 그림처럼 생겼기 때문에 그 특성을 살린 타이포그래피가 존재한다.

거기에다 일본어 정서법은 한자에다가 자기네만의 간단한 표음문자가 더해져서 상황이 또 달라진다.
일본어는 입력하기가 굉장히 복잡하고 어려운 축에 든다. 그리고 '가나'라고 불리는 고유 문자는 구조적으로 불완전하며 한글에 비해 스케일이 작고 표음 능력에 한계가 있는 게 사실이다. 일본어 정서법은 가나 전용이 현실적으로 무리이다. 한자를 쓰지 않으면 안 되는 상황이며, 한글하고는 분명 상황이 다르다.

그러나 이런 형태가 단점만 있는 건 아니다. 굵직한 의미를 강조하는 한자와, 비교적 단순한 모양인 가나가 어우러지면 그것도 또 개성이 있으며 시각성이 살아나서 읽기도 꽤 좋다. 유식한 용어를 동원하자면 function word와 content word가 딱 잘 구분돼 보인다.
또한 입력이 어려운 대신, 한번 입력된 일본어 문장은 문자 차원에서 한자, 히라가나, 가타카나 같은 적지 않은 양의 NLP 정보가 담기기 때문에, 형태소 분석이나 번역을 의외로 유리하게 만들기도 한다. 히라가나-가타카나 구분이 라틴 알파벳으로 치면 대소문자에 얼추 비슷하게 대응한다고 볼 수도 있다(완전히 같지는 않지만).

3. 한글은 멀티 패러다임

자, 이렇게 라틴 알파벳 쪽의 정황과 한자 및 일본어 정서법의 정황을 살펴보았다.
그렇다면 세종대왕이 창제한 그 우수하고 독창적인 문자라는 '한글'을 사용하는 우리는 상황이 어떨까?

한글과 관련해서 우리가 절대로 간과해서는 안 되는 점은, 한글은 단일 패러다임만으로 설명이 되지 않는 문자라는 점이다. 단적인 예로, 단일 패러다임으로 설명이 되지 않기 때문에 한글이 총 몇 자냐는 질문에 24자부터 시작해 67자, 11172자, 심지어 160여만 자 같은 사람마다 뒤죽박죽인 대답이 나오는 것이다. 각각의 숫자가 한글의 규모를 어떤 관점에서 측정한 것이겠는지는 독자 여러분이 알아서 생각해 보시라.

차라리 한자는 총 몇 자냐는 질문에 대해 “아무도 모른다”라는 대답이라도 곧바로 나오지, 한글은 대단히 특이한 경우가 아닐 수 없다. 그래서 유니코드에는 고육지책으로 한글이 자모와 글자마디가 모두 등록되어 있으며 이것은 내가 보기에 나쁜 선택이 아니다. 그러나 한글을 바라보는 패러다임의 대립은 심지어 컴퓨터 전문가 사이에서도 거의 종교적인 신념의 대립에 가깝다. 이 말이 무슨 뜻인지 잘 모르겠으면, 본인에게 개별적으로 물으면 대답해 주겠다.

한글이 이런 유별난 위치에 있는 이유는 두 말할 나위도 없이 초-중-종성을 모아서 한 글자를 이루는 구조이기 때문이다. 만약에 한글이 IPA의 지위를 노리는 범용 음성 부호를 염두에 두고 만들어졌다면, 굳이 모아쓰기를 할 필요가 없다. 로마자의 예에서 볼 수 있듯, 풀어 쓰는 게 자음이나 모음의 중첩 같은 각종 외국어들의 변화무쌍한 음운 구조의 대처에 더 유리하다. 지금의 모아쓰기 체계는 모음의 발음이 분명하고 받침이 자주 등장하는 한국어의 음운 구조에 좀 더 최적화/로컬라이즈 전략을 선택한 귀결이다.

그래서 한글은 한국어의 음절 구분이 분명하며 시각성과 개성이 더 뛰어난 문자가 되었으며, 구조적으로는 알파벳 같은 계열보다 한자 계열과 좀 더 비슷한 형태가 되었다. 이 선택은 매우 큰 장점과 개성과 자부심을 가져온 것이 사실이다. 그러나 한편으로는 우리가 기술적으로 풀어야 할 과제도 많이 만들었다. 로마자처럼 소수의 글자를 최적화하고 품질을 다듬는 데 투자될 수 있는 기술과 노력이, 한글 조합 그 자체의 문제만을 푸는 데 다 소비되어 버리게 되었다. 입력이든 출력이든 모두에서 말이다.

다시 말해, 1만 개, 1백만 개, 심지어 1억 개의 글자를 조합해서 생성할 수 있다고 하는 한글의 그런 잠재성은, 알파벳처럼 개개의 글자마다 완전 정교한 힌팅, 커닝, 이탤릭, 필기체 따위로 최적화가 가능한 면모와 기술적으로 공존할 수가 없거나, 최소한 상호 조화시키기가 대단히 매우 어렵다는 뜻이다. 그렇다면 모아쓰기라는 걸 현실에서 반드시 절대적으로 일방적인 장점이라고만 간주해도 되는 것일까?

그래서 옛날 한글 선각자 중에는 로마자의 직관적인 기계화 기술과 먼치킨 급의 타이포그래피 최적화에 너무 한이 맺힌 나머지, “우린 안 될 거야 아마”라는 비관적인 결론으로 빠져서 아예 <글자의 혁명> 급으로 한글을 풀어쓰기 형태로 마개조할 생각을 한 분까지 있었다. 다중 패러다임이 독이라고 생각했던 것 같다.

물론 그 의도는 이해하지만 그건 너무 과격하고 비현실적인 주장이다. 개개의 한글 낱자는 마치 일본어 가나만큼이나 완성도가 부족하다. 가나가 한자와 섞어 쓰라고 만들어진 문자라면, 한글 낱자는 모아 쓰라고 만들어진 문자인 것이다. 한글 낱자만으로 풀어 쓰려면 진짜로 모음 ㅡ라면 U처럼 바꾸는 식으로, 글자 자형을 좀 더 변별성 있게 고쳐야 하고 맞춤법까지 대대적으로 손을 봐야 한다. 위험 비용이 너무 클 뿐만 아니라, 풀어쓰기를 실제로 오랫동안 시행해 본 분의 증언에 따르면, 한글 풀어쓰기의 시각적 능률은 모아 쓴 한글 음절을 도저히 따라갈 수 없었다고 한다.

4. 이제는 한글 자체의 고유한 특성을 살려서 입출력 기술을 발전시켜야

결국 이 모아쓰기에 따른 다중 패러다임은 우리가 한글을 고유 문자로 사용하는 한, 우리가 언제까지나 지고 가야 하는 숙제이다. 예전에 컴퓨터의 성능이 열악하던 시절에는 한글 같은 복잡한 문자를 심을 길이 안 보여서 풀어쓰기라도 생각해야 할 것처럼 상황이 암울했다. 그보다 상황이 약간 나아졌을 때에도 겨우 일본의 자국 문자 로컬라이즈 방식을 모방한 2바이트 한글 코드에다 전/반각 문자 따위밖에 선택의 여지가 없었다.

그러나 지금은 그런 기술적인 제약이 없다. 유니코드에, 화려한 OpenType 기술까지 완비되어 있는데 뭘 더 바라겠는가? 한글은 기계화가 유리한 문자인 건 명백한 사실이다. 그러나 딱 그거 하나만 믿고, 기계화 수준이라는 게 과거의 타자기 내지 2바이트 한글 코드 시절의 기술적 수준에서 멈춘 채 발전이 정지해 있다. 그런 낙후된 기술 수준은 한글에서 제한된 일부 패러다임밖에 수용을 할 수 없다.

일례로 입력부터 먼저 살펴보면, 15년도 더 전의 윈도우 95의 마소 한글 IME의 설정 대화상자와, 지금 윈도우 8의 한글 IME의 설정 대화상자는 제공하는 기능이 차이가 거의 없다. 이거 좀 문제가 있다.
어차피 조합이 필요하고 IME 같은 계층이 필요하다면 그걸 살려서 IME 계층이 있어야만 할 수 있는 일을 추가로 많이 지원해 줘야 한다.. 이것은 내가 <날개셋> 한글 입력기를 통해 어느 정도 기술적으로 실현시켰으며, 이 분야의 발전에 대한 필요성은 내 석사 논문에다가도 충분히 언급해 놨다.

출력 쪽도 마찬가지이다. 앞서 언급했듯이 라틴 알파벳은 적당히 꼬불꼬불하고 스스로 들쭉날쭉해서 보기가 좋으며, 일본어는 한자와 가나가 어우러져서 보기 좋다. 그렇다면 한글은 단일 종류의 문자가 빽빽하게 모였을 때 어떤 미를 추구해야만 할까?
이제는 한글도 한자 중심의 획일적인 정사각형 타이포그래피를 벗어나서 커닝도 생각하고, OpenType 기술을 적극 활용한 다이나믹 글꼴이 더 많이 나와야 한다. 한글은 굳이 한자가 섞일 필요가 없는 stand-alone, self-contained 문자이기 때문이다.

한글은 각 낱자는 직선 아니면 원밖에 없어서 기하학적으로 의외로 무척 추상적이고 단순하다. 그러면서 초중종성의 개성이 아주 분명하니, 한자나 로마자와는 달리 각종 로고타입이나 픽토그래픽으로 형상화하기가 좀 까다로운 면모가 있다(여전히 그림이나 아이콘 같아 보이지 않고, 글자처럼 보임). 그러나 그런 식으로 모아 쓰는 한글의 특성 때문에 오로지 한글에만 적용할 수 있는 글꼴이 나올 수도 있다. 모아쓰기를 그저 거추장스럽게 한글 자모를 정사각형에다가 예쁘게 끼워 넣는 overhead, burden으로만 받아들이지 말고 좀 더 창조적으로 활용할 수는 없을까? 이것이 내가 현재 고민하고 있는 분야이다.

요컨대,

  • 한글을 굳이 여타 문자와 더 비슷하게 만들어서 단점을 상쇄하려 하기보다는, 한글의 개성과 장점을 더 살린다. “넌 그걸 할 수 있냐? 난 그걸 못 하는 대신이 완전히 새로운 이걸 할 수 있다. 놀랍지?”를 지향한다는 뜻이다.
  • 한국어의 특성은 배제하고 오로지 한글의 형태 자체만을 생각한다.

이것이 내가 전통적으로 생각해 온 연구 방향이다. 입력기도 그렇고 글꼴도 그렇다. 더 이상의 자세한 설명은 생략한다. 연구 결과물이 다 나온 뒤에 빵! 터뜨려야 하는 것이기 때문에 당분간은 비밀이다. 그렇다고 해서 그렇게 심하게 거창한 건 아니고.. <날개셋> 한글 입력기도 겨우 1.x~2.x 시절에는 아이디어만 새로웠지, 기술적으로는 그다지 거창하지 않았으며 허접함 그 자체였었다.

이런 식으로 입력과 출력 모두에서 한글의 기술적 가능성을 한 걸음 확장하는 시도를 골고루 달성하고 나면, 그것이 대중적으로 성공하든, 아니면 너무 과격하거나 시장성이 없어서 실패하든 난 대한민국이라는 나라에서 태어난 보람을 느낄 것이고 죽어도 여한이 없을 것 같다.

Posted by 사무엘

2013/03/27 19:38 2013/03/27 19:38
,
Response
No Trackback , 13 Comments
RSS :
http://moogi.new21.org/tc/rss/response/811

자유에 대한 생각

우리나라 내지 이에 준하는 여타 자유 민주주의 국가들이 국민에게 보장하는 자유는 여러 종류가 있다. 우리나라의 경우 헌법 제2장에서 신체의 자유, 거주와 이전의 자유, 직업 선택의 자유, 사생활과 비밀의 자유, 양심의 자유, 종교의 자유, 집회와 결사의 자유 등등이 명시되어 있다. 그리고 모든 자유에는 책임이 뒤따르며, 남의 자유를 침해하거나 범죄· 이적 행위· 반역을 조장하거나, 다른 자유 이념과 모순을 일으키는 자유는 자유로 인정받을 수 없다고 우리는 아주 어릴 적부터 사회· 도덕 시간에 배운다.

이런 자유들은 당연한 것 같지만 당연한 게 아니다. 선조들이 피흘려 쟁취한 소중하고 고귀한 이념이다(대표적으로 6· 25!). 또한 이것은 충분한 경제력과 심지어 과학 기술이 뒷받침 되어야만 실현 가능하다. 옛날에 먹고 살기가 어렵던 시절에는 사회 보장 제도가 제대로 돌아갈 수 없었으며, 뭐 하나 잘못 사고 쳤다간 집안이 쫄딱 망하고 처자식이나 자기 자신을 노예로 팔아야만 수습할 수 있었다. 거기에다 누가 무슨 짓을 할지 상대방을 믿을 수도 없는 긴급한 상황이라면 어떻게 모든 자유가 보장될 수 있겠는가?

그래서 우리나라는 명목상 미국의 사회· 정치 제도를 벤치마킹한 자유 민주주의 국가로 시작했으나, 휴전선 너머 북한의 집요한 방해 공작과 비열한 해코지로 인해, 완전히 이상적인 자유를 실현하는 데는 애로사항이 있었다. 완전 폐쇄적인 선군정치 최적화 병영 국가와, 사회 시스템이 다 개방되어 있는 자유로운 국가가, 서로 인구와 경제력이 비슷하고 다른 변수가 없다면 어디에서 어디로 간첩을 침투시키고 유언비어 퍼뜨리고 심리전을 전개하기가 쉬울 것이며, 무력 충돌이 벌어졌을 때 누가 이길 가능성이 더 높을까?

이 때문에 우리나라는 전통적으로 북한과 관련해서는 불가피하게 기본권을 법으로 크게 제한할 수밖에 없었다. 우리나라에 21세기에까지 국가 보안법 같은 구시대 악법(?)이 존재하는 이유는, 21세기에까지 우리나라 체제를 부정하고 김씨 부자에게 이로운 짓을 하는 구시대 악인이 아직도 존재하고 있으며 법에 저촉되어 잡히고 있기도 하기 때문이다.

이것은 독일이 아무리 자유 민주주의 국가라 해도 나치 및 히틀러와 관련된 매체 표현은 무조건적으로 금지하며, 누가 길거리에서 팔 뻗쳐서 “하일 히틀러!”만 외쳐도 잡아 가는 것과 동일한 맥락이다. 정상적인 독일 국민 중에 그걸 보고 무슨 국민 기본권 침해라고 징징대는 사람이 과연 있을까?

자, 여기서 표현이라는 말이 나왔다. 우리나라 헌법에는 '표현의 자유'라는 말이 문자적으로 등장하지는 않지만, 18조와 21, 22조는 개념적으로 표현의 자유의 범주에 든다고 여겨진다.

본인은 크리스천으로서 표현의 자유라는 게, 사상과 종교의 자유에 동급으로 매우 중요한 자유라고 생각한다. 표현의 자유는 크리스천에게 거리 설교 같은 복음 전파를 법적으로 보장하는 자유이기 때문이다. 기독교 신앙은 남에게 영향을 끼치고 역사하는 신앙이다. 표현의 자유를 배제하고서 사상과 종교의 자유만으로 크리스천 신앙을 다 논하는 것은 불가능하며, 둘을 서로 떼어서 생각할 수 없다.

그리고 본인은, 우리나라는 표현의 자유를 포함한 기본권의 보장이 이미 넘치도록 충분히 아주 잘 돼 있다고 생각한다. 그래서 우리나라의 체제에 대해 고맙게 여긴다. 지금까지 온라인이나 오프라인으로 내가 믿는 프로파간다를 주변에 알리거나 교회에서 거리 설교를 하면서 공권력으로부터 구금· 체포의 위협을 느낀 적이 없다. 심지어 정치인들 비판도 아무 문제 없이 했다.

이 명박 정권 때부터 분야별로 온라인 검열이 강화된다면서 사람들이 굉장히 불안해했다. 그러나 난 별로 동요하지 않았다. 난 일단 신앙과 관련된 자유가 침해받지 않는 것만으로도 굉장한 자유감을 느끼며, 그것만 보장해 준다면 심지어 독재 정권이라 해도 별로 거부하지 않는다. 오히려 자유가 너무 많아서 그걸 오· 남용 하는 부류들이 더 문제라고 난 생각한다.

북한 관련 표현이야 나라에서 하지 말라면 좀 참고 안 하면 되고, 음란물· 성인물이야 어차피 나하고는 아무 상관 없는 일이니.. 나라에서 그런 규제를 가하는 사정을 오히려 이해한다. 그런 규제가 무슨 북한 같은 수준의 검열로 이어질 거라고 생각하지도 않는다. 언론들이 너무 오버를 하고 네티즌들을 막 선동했다.

글쎄, 난 이런 점에서는 “나만 괜찮으면 된다”는 식의 어찌 보면 좀 이기적인 생각을 하고 있기도 하다. 그런데 난 그게 성경적으로 나쁘다고 생각하지 않는다.

예를 들어, 계몽주의 사상가 볼테르는 이런 유명한 말을 남겼다고 한다.

“난 당신의 사상에 동의하지 않습니다. 그러나 당신이 그 사상 때문에 박해를 받는다면 나도 당신의 말할 권리를 위해 끝까지 같이 싸우겠습니다.”


그래서 특히 진보 성향이 강한 사람들이 이 말을 자기 블로그에다가도 많이 걸어 놓고, 많이 떠받드는 것 같다.

그런데, 난 양심적으로 저렇게는 못 하겠다.
나더러 그저 소수이고 박해받는다는(?) 이유만으로 병역 거부하는 인간들, 동성애자들, 낙태 합법화, 사형 폐지론자들의 말할 권리를 위해 같이 싸우자고? 못 한다. 물론 그들은 어차피 요즘 대한민국에서 박해를 받고 있는 것도 아니고.

크리스천으로서 그런 사람들을 괴롭히고 왕따 시키고 해코지하는 것에야 물론 동참하지 않는다.
하지만 혼자 죄의 가시밭길을 가다가 혼자 망하든지 말든지 마음대로 해라.
그들과 얘기를 나눌 기회가 있으면 그들에게 복음을 전하고 그들의 오류를 지적하면 지적했지,
그들의 표현의 자유의 보장을 위해 싸운다거나 기도해 준다거나(?) 하는 일은 난 할 수 없다. 난 볼테르 같은 대인배가 아니다. 아니, 난 하나님보다 더 대인배여야 할 필요를 느끼지 않는다.

저렇게 '행동하는 양심'의 감성을 자극하는 작품으로 <나치가 그들을 덮쳤을 때>라는 시도 유명하다.
물론, 정치적 무관심을 풍자하는 건전한 메시지가 핵심이며, 평소에 선을 많이 행해 놔야 내가 위급할 때 나도 도움을 받을 수 있다는 교훈 정도는 나도 잘 안다.

그러나..
공산주의자, 사회민주당원, 노조, 유대인?
그 무엇이 됐든 아무튼 내가 신념적으로 동조하지 않는 그룹이 먼저 쓸려 나갈 때, 내가 굳이 먼저 그들을 위해 같이 나서야 할 필요는.. 솔직히 난 못 느끼겠다.
그들이 남아 있다고 해서 내가 위기에 처했을 때 나의 '신앙'을 방어해 주는 데 기여할 거라고도 생각하지 않고 말이다. 오히려 크리스천은 그런 상황에서 마 10:17-20 같은 말씀을 더 떠올려야 하지 않겠는가?

글쎄, 배가 침몰한다거나 화재가 났다거나 해서 사상이고 종교고 나발이고 없이 다같이 죽게 됐을 때야 인간으로서 서로 도와야 한다. 그거야 맞는 말이다. 그러나 그 사상과 종교 자체만이 사람을 가르는 요인이라면 얘기가 달라진다는 뜻이다.

자라면서 이런 내 생각이 앞으로 또 바뀌게 될지는 모르겠다. 크리스천 중에서도 교리에 명백히 어긋나지 않는 범위 하에서 세상사의 참여에 굉장히 호의적이며, 각종 사회 운동이나 심지어 파업· 데모에 대해서도 더 열린(?) 생각을 갖고 있는 사람도 있다. 사실 집회와 결사의 자유 자체가 헌법에 있기도 하니 말이다. 이에 대한 이견과 논쟁은 이 세상이 어차피 제도적으로 성경대로 손쉽게 살 수 있는 형태로 되어 있지 않은 이상, 언제까지나 존재할 것 같다.

끝으로, 자유 얘기가 나왔으니 말인데 미국 얘기를 좀 하고 글을 맺겠다.
한쪽에서는, 미국이 점점 법이 바뀌어 개인의 자유가 제한되고 반기독교 성향으로 가고 있다고 엄청 걱정한다.
거리설교를 했다간 잡혀 가고, 학교에 성경의 개인적인 반입이 금지되고, 교회나 신학교계에서도 이제 동성애를 당당히 반대했다간 사회적으로 매장당하게 생겼다고 걱정한다. 특히 흑인 대통령이 부임하면서 그런 추세가 더욱 심해졌다고 그런다.

그러나 크리스천 중에서도 좌성향이 더 강한 다른 한쪽에서는, 저런 것보다는, 여전히 미국의 메이저 교회들이 저지르는 병크와 비리, 그리고 특히 정치 종교 결탁 행위만을 비판하느라 바쁘다. 그런 것들이 나라에서의 크리스천들의 자유를 억압하는 빌미를 주고 있다고 말이다.

양 극단 중에서 진실은 어디에 있는지 모르겠다. 미국은 모든 면모를 균형 있게 이해하기가 힘든 나라인 한편으로, 대형 교회들의 폐단은 한국이나 미국이나 별 차이 없다는 걸 느낀다.

Posted by 사무엘

2013/03/25 08:38 2013/03/25 08:38
, , ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/810

C/C++의 const 이야기

1.

C/C++에서 const라는 키워드는 어떤 변수를 선언할 때 타입과 함께 지정해 줄 수 있는 modifier 속성이다. 이와 비슷한 위상인 키워드로 volatile도 있다.

이 const의 큰 의미와 용도는 C와 C++에서 모두 동일하다. 바로, 한번 값이 정해지고 나면 그 뒤로 값이 또 바뀔 수 없다는 걸 뜻한다. 비슷한 용도로 쓰이는 매크로 상수나 enum과는 달리, const 개체는 엄연히 상수 역할을 하는 '변수'이기 때문에 L-value의 특성도 껍데기나마 지니며, 자기 주소를 & 연산자로 얻을 수 있다는 특징이 있다. (자기 주소가 있는데 왜 대입을 못 하니 ㄲㄲㄲ)

그런데 const라는 의미를 언어 차원에서 실현하는 방식이 C는 다소 느슨한 편이다.
C 언어도 const 변수에다가 대놓고 대입 연산자를 들이대는 시도 정도는 컴파일러가 에러로 대응하며 막아 준다. 그러나 강제로 const 속성을 없애는 형변환+포인터 연산 같은 것까지 저지하지는 못한다.

이는 마치, C/C++ 코드에서 변수를 초기화하지 않고 사용하는 걸 간단한 지역 변수 정도는 컴파일러가 알아서 발견하여 경고로 처리해 주지만, 복잡한 배열이나 포인터, 구조체의 경우를 일일이 체크하지는 못하는 것과 비슷한 맥락. 그래서

const int MARK = 100;
const int *p = &MARK;

printf("%d %d\n", MARK, *p);
*const_cast<int *>(&MARK) = 50;
printf("%d %d\n", MARK, *p); //이것이 문제.

이런 코드를 돌려 주면 C에서는 MARK가 처음에는 100이다가 나중에는 50이 되어 버린다! 이런 이유로 인해 C에서 const int는 껍데기만 const이지 case 문의 상수로 쓰이지도 못한다. 아, C 언어는 const_cast라는 연산자가 없으니, 그냥 *((int *)&MARK) = 50; 이라고 해야겠지만 말이다.

허나, C++은 이 정책이 바뀌어서 const를 다루는 방식이 좀 더 엄밀해졌다. 사실, 객체 지향 언어이다 보니 상수값을 취급하는 방식이 더 정확하고 엄밀해져야만 하는 게 마땅하다. 무작정 C 같은 '고수준 어셈블리' 패러다임만 추구해서는 곤란할 터이다.

C++은 MARK 변수가 차지하는 메모리에 들어있는 값과 상관없이 소스 코드에서 MARK가 그대로 쓰인 곳은 언제나 100을 대응시켜 준다. 다시 말해 위의 경우 100과 50이 출력된다. MARK와 *(&MARK)의 값이 달라지는 한이 있더라도 MARK는 언어 차원에서 처음 선언해 준 값이 그대로 유지되며, 진짜 매크로 상수처럼 쓰일 수 있다는 뜻이다. 신기하지 않은가? C와 C++ 사이의 교묘한 차이 중 하나이다. C/C++ 프로그래머라면 이 정도는 이미 아는 분이 많을 것이다.

2.

C/C++은 잘 알다시피 '선언 따로, 정의 따로'라는 좀 원시적이라면 원시적인 디자인 철학을 따르는 언어이다. 그래서 헤더에 들어간 선언은 그 선언을 사용하는 모든 번역 단위들이 include를 “매번” 해 줘야 하고, 그 선언에 대한 정의는 아무 번역 단위에다가 “한 번만” 써 주면 링크 때 알아서 말 그대로 '연결'이 된다. 그렇다, 걔네들은 원래 그런 언어이다.

자바나 C#은 클래스의 선언과 정의가 일심동체이고 그 클래스가 곧 번역 단위이다. 뭐, C++도 클래스를 선언하면서 멤버 함수의 몸체까지 헤더 파일 안에다 같이 써 주는 게 불가능하지는 않지만, 그건 간단한 인라인 함수를 만들 때에나 제한적으로 쓰이는 관행이다. 아니면 어차피 모든 클래스의 몸체가 헤더에 들어가야만 하는 템플릿일 때 정도.

자, 이런 이중적인 구조로 인해 C++은 static 멤버 변수의 정의조차도 클래스의 선언과 동시에 할 수가 없다.
여러 번역 단위에서 매번 인클루드되는 '선언부'에다가 한 번만 등장해야 하는 '정의부'가 동시에 들어갈 수는 없기 때문이다.
자바나 C#은 클래스 안에다가 static int MAX = 100; 같은 문장을 아무렇지도 않게 넣을 수 있으나, C++은 굳이 static int MAX;int CFoo::MAX = 100; 을 분리해서 써 줘야 한다.

그럼, C++의 클래스에서 멤버를 선언할 때 대입 연산자가 들어갈 일이란 오로지 순수 가상 함수를 선언할 때 쓰이는 = 0밖에 없는 걸까? (자바와 C#은 순수 가상 함수는 오히려 pure이나 abstract 같은 키워드를 따로 써서 표현함!)

놀랍게도 그렇지는 않다.
딱 하나 예외적으로, static const라는 속성을 지닌 간단한 '정수 계열'의 멤버는 클래스 안에다 선언과 함께 초기화를 하는 게 가능하다. 즉, 클래스 안에다가 static const int MAX = 100; 정도는 C++도 허용해 준다는 뜻이다.

물론 제약이 몹시 심하다.
static과 const 중 속성이 하나라도 빠져서는 안 된다. 그리고 배열이나 구조체의 초기화는 어림도 없다. static const WCHAR NAME[] = L"foo"; 같은 거 안 된다.

쉽게 말해 정수 정도면, 심벌이 있는 곳의 메모리 주소를 참고하는 게 아니라 심벌의 값 자체를 매번 집어넣어 주는 게 어차피 이득이니까 예외적으로 클래스 내부에서의 정의와 초기화가 허용되는 셈이다. 그러니 static const 정수는 그냥 메모리 주소를 얻는 게 가능한 enum 수준에 불과하다.

정수 계열은 심지어 __int64도 허용되지만 포인터는 허용되지 않는다. 그리고 부동소수점도 안 된다. static const double PI = 3.141592; 는 안 된다는 뜻이다. 이건 현재 GNU 계열 컴파일러에서만 지원하는 extension일 뿐, 표준은 아니다.

3.

한 소스 파일에다가 const 속성을 가진 커다란 정수 테이블 배열을 전역변수 형태로 만들었다. 그건 난수표가 될 수도 있고 time-critical한 실시간 계산 프로그램(게임이라든가)에서는 삼각함수나 로그값 테이블이 될 수도 있고 문자 코드 변환 테이블이 될 수도 있다.

그런데 다른 번역 단위에서는 그 테이블의 명칭을 extern으로 선언해 놓고 참고하여 사용했는데, 링크할 때는 그 명칭을 찾을 수 없다고 에러가 나는 것이었다. 본인은 그 이유를 알 수 없었다. 경험적으로 const 속성을 제거하면 문제를 피해 갈 수 있긴 했으나, 값을 변경하지 않는 상수 테이블을 일반 배열로 취급할 수도 없는 노릇이었다.

링크가 되지 않던 이유를 난 한참 뒤에야 알게 됐다.
C가 아닌 C++에서는 static이나 extern 명시가 없이 const로 선언된 전역변수는 기본적으로 extern이 아니라 static 속성이 부여된다. 그러니 그 번역 단위 내부에서만 쓸 수 있지, 외부로 명칭이 노출되지 않으며, 따라서 링크 에러가 난다.

왜 그렇게 정책이 바뀌었냐 하면 const 개체에 대해서는 이 글의 1번 항목에서 명시한 것과 같은 무결성을 보장하기 위해서인 듯하다.
심벌이 가리키는 메모리 주소는 값이 언제 바뀌어 있을지 모르니, const 개체의 값은 매 번역 단위마다 컴파일러가 소스 코드로부터 읽어들여서 확인하기 위해서이다.

이 조치를 무시하고 const 개체의 값을 다른 번역 단위에서도 사용하려면 extern을 명시적으로 지정해 줘야 한다.

extern const TYPE TABLE = ... 라고 바로 써 줘도 되고, external const TYPE TABLE; 이라고 먼저 선언만 한 뒤에 나중에 const TYPE TABLE = ... 을 쓰면 TABLE은 여느 전역변수와 마찬가지로 다른 번역 단위에서 참조가 가능한 extern 변수가 된다.

4.

Windows 환경에서 개발을 하다 보면 지금 설치되어 있는 운영체제의 SDK에 기본 내장되어 있지 않은 GUID를 수동으로 추가해서 사용해야 할 때가 있다.

GUID는 코드가 아니라 128비트짜리 난수가 들어있는 구조체에 불과하지만, 엄연히 const 전역변수들의 집합이기 때문에 선언부와 정의부가 따로 있다. 그리고 주요 GUID의 실제 값들은 플랫폼 SDK의 라이브러리 디렉터리에 있는 uuid.lib에 들어있다. kernel32, user32, gdi32만큼이나 딱히 우리가 지정을 안 해도 자동으로 링크되는 기본 라이브러리이기 때문에, 파일의 존재감을 모르는 분도 많을 것이다.

그런데 이놈의 GUID 하나 좀 쓰자고 헤더 파일과 소스/라이브러리 파일을 다 구비해 줘야 하는 걸까? 여간 번거로운 일이 아닐 수 없다. 귀찮다고 헤더 파일에다가 GUID 값을 몸체(정의)를 다 써 주면, 이론적으로는 그 헤더를 인클루드하여 사용하는 모든 번역 단위에 동일한 GUID의 몸체들이 obj 파일 내부에 중복 기재될 위험이 있기 때문이다.

결국 이 문제는 MS 컴파일러의 경우 자기만의 언어 확장을 만듦으로써 우격다짐으로 해결했다. DLL 심벌을 만들거나 사용할 때 __declspec(dllexport/dllimport)를 사용하는 것처럼 __declspec(selectany)라는 속성도 있다. 이것이 지정된 전역 변수는 여러 object에서 중첩 기재된 심벌이라도 링크 때 딱 한 몸체만 임의 선택된다.

여러 소스 코드에서 공통으로 쓰이는 GUID를 새로 추가하고 싶으면 #include <initguid.h>를 해 준 뒤, DEFINE_GUID 매크로로 새 GUID의 명칭과 값을 써 주면 된다. 이 매크로는 내부적으로 selectany 지정자를 사용한다.

결국 이것은 전역 변수 선언계의 #pragma once나 마찬가지이다. 중복 인클루드 방지에 이어 심벌 몸체의 중복 링크 방지 마크이다. 이게 다 C/C++에는 간편히 끌어다 쓰는 패키지 개념이 없이, 원시적인 헤더/라이브러리에만 의존하느라 컴파일러 제조사가 부득이 추가한 꼼수인 셈이다.

내가 늘 느끼는 거지만..
C++ 님 좀 짱이다. 10년이 넘게 파 왔지만 아직도 지금까지 몰랐던 사실들이 계속 발견된다.

Posted by 사무엘

2013/03/22 08:29 2013/03/22 08:29
, ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/809

철도/지하철역과 주차장

지하철은 역에 접근하는 여러 교통수단들과 어떤 방식으로 연계를 하고 있을까?

가장 먼저 도보는 trivial, self-explanatory이다. 설명이 더 필요하지 않다.
버스는 교통 카드를 이용할 경우 잘 알다시피 환승 할인이 된다(30분 이내에 환승시. 그리고 최대 5회까지). 외국에서도 서울의 대중교통 요금 시스템을 배우러 올 정도로 시스템이 합리적으로 잘 바뀌었다.

그리고 자전거가 있다. 저탄소 녹색 성장이라는 구호 아래 나라에서 나름 권장은 많이 한다. 역 주변에 자전거 주차대를 많이 설치해 놓았으며, 일부 역은 전동차에다 자전거 휴대도 가능하게 계단에 경사로를 설치하기도 했다.
하지만 우리나라는 도로에서 자전거를 몰기는 위험한 곳이 많고, 자전거를 휴대하여 승차하는 것도 마냥 쉽지만은 않다는 한계가 있다. 자전거는 스마트폰만큼이나 도난에도 취약한 편이고 말이다. (공공 자전거 주차대에는 CCTV 정도는 장착해 둬야 할 듯.)

허나, 지하철이 대중교통으로서 진짜로 자가용의 수요를 흡수하려면 버스나 자전거 같은 것뿐만 아니라 역설적으로 승용차와의 연계도 고려해야 하지 않을까 하는 생각이 든다. 딴 게 필요한 게 아니라 주차 시설 말이다. 버스 이용자가 지하철로 이동하는 것보다는 자가용 이용자가 지하철로 전향하는 게 훨씬 더 성공적인 현상이지 않은가? 교회로 치면 불신자가 교회로 새로 유입되어야지, 한 교회의 기존 신자를 다른 교회로 옮기기만 하는 제로썸 게임은 성장에 한계가 있단 말이다.

서울 중심부보다는 변두리 외곽의 역들이 이런 식으로 승용차를 맞이할 채비를 더욱 갖출 필요가 있다. 대중교통이 열악한 경기도 외곽에서 서울 근교의 전철역까지는 승용차를 타고, 거기서 서울 도심까지는 지하철을 타는 식의 통근 패턴이 지금보다 더 활발하게 정착되어야 한다.

그 주차장은 기본적으로 유료이지만 지하철 환승객에게는 주차료를 아주 크게 깎아 주는 식으로 운영되어야 할 것이다. 굳이 좁아 터진 서울 시내까지 스트레스 받으면서 차를 직접 끌고 가느니, 그냥 여기에 세워 놓고 주차료+지하철비가 시간과 비용면에서 훨씬 더 수지가 맞게끔 장점이 와 닿을 수 있어야 한다.

복정 역은 외곽+2개 노선 환승+주차장이라는 세 변수를 두루 갖춘 좋은 사례이다. 주변이 허허벌판이다 보니 주차장은 그냥 평지에서 운영되고 있다.
그러나 건물의 주차장은 보통 지하에 있는 편이고 지하철은 승강장도 지하에 있는데, 두 시설 다 지하에다 넣는 게 승객의 동선면에서 더욱 유리할 것이다. 이미 주차장 없이 완공되어 버린 기존역들은 어쩔 수 없고.

그러니 주차장이 갖춰진 지하철역은 사람이 드나드는 출입구뿐만 아니라 자동차가 드나드는 출입구도 어딘가에 생기게 된다. 개화산(5호선), 잠실(8호선. 2호선 말고), 동묘앞(1호선)처럼 인근에 지하철 관련 건물이 따로 있는 역들은 바로 그 건물의 지하에 주차장을 갖추는 것을 적극 검토할 필요가 있다.

이렇게 지하 승강장 + 지하 주차장이 갖춰진 역은 내가 알기로 공항 철도 서울 역, 그리고 신분당선의 판교 역 정도이다. 특히 판교의 경우 아직 지어지지 않은 건물의 내부에 있는 역으로 형태가 변모할 예정이니 지하 주차장이 미리 건설되는 게 당연한 이치이다. 상업 시설이 같이 갖춰져 있는 민자 역사들 역시 비슷한 이유로 인해 주차장이 있어야만 할 것이다.

내 경험상 성남의 8호선 수진 역은 출입구 바로 옆에 노상 공영 주차장이 있어서 본인 역시 몇 번 편리하게 이용한 적이 있다. 관리 요원이 퇴근한 저녁과 심야 시간대에는 무료 개방이기도 해서 더욱 좋았다.
다만, 도심 지하철이 아닌 광역전철들은 사정이 좀 낫다. 특히 지상 고가역들은 고가 아래가 주차장으로 개방되어 있는 경우가 많다. 경춘선 갈매 역이 좋은 예. 물론 무료이다.

중앙선 전철역들도 대체로 역 주변에 무료 주차장이 있다. 최근에는 서울 강북 최동단에 있는 양원 역의 주차장을 유용하게 이용했다. 장애인 차량 주차 구역만 안 건드리면 된다. 물론, 무료이기 때문에 자리가 언제나 있다는 보장은 못 한다. 운빨이 작용해야 된다.

공항 철도도 역마다 주차 시설을 갖추고 있다. 유료이더라도 요금이 공항 주차장보다 무척 저렴하기 때문에, 영종도를 내 기름값과 톨비를 들여서 자가용으로 직접 힘들게 건너느니, 차라리 차를 역에다 두고 공항까지는 열차로 가는 게 낫겠다는 생각을 절로 들게 만든다. 이 전략에 대해서는 교통 평론가 겸 철덕인 한 우진 님께서 정리해 놓은 게 있으니 참고하면 되겠다.

영종도 공항 화물 터미널 역의 근처에는 소규모 '무료 주차장'도 있다고 한다. 하지만 무료인 만큼 관리인도 없고, 차량 파손 및 도난 우려도 감수해야 한다고.

이렇듯, 현재 전철역들의 주차 편의는 역마다 케바케인 편이다. 그러나 서로 다른 교통수단들이 힘을 합쳐서 파이의 크기를 키우고 시너지 효과를 내려면 철도역들도 주차에 대한 배려를 좀 더 해야 할 것이다. 특히 서울 2기 지하철들은 내부가 깊어서 공간이 많아서 사업 아이템이랍시고 물품 보관 서비스 같은 것까지 한다는데, 자전거나 자동차 주차에 대한 배려도 생각해야 하지 않을까?

Posted by 사무엘

2013/03/19 19:36 2013/03/19 19:36
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/808

윤곽선 글꼴과 아이콘 이야기

1. 윤곽선 글꼴의 기술 디테일

옛날에 인쇄가 물리적인 활자로 행해졌고 컴퓨터에서도 비트맵 글꼴이 대세이던 시절에는, '폰트 한 벌'이라 하면 여기에는 서체뿐만 아니라 고정된 크기라는 개념까지 포함되어 범위가 더욱 제한적이었다.
진짜 말 그대로 활자 한 벌이다. 그 폰트가 제공하는 서너 종류의 크기로만 글자를 쓸 수 있는 것이다. 더욱이 컴퓨터용 서체의 경우 화면용과 인쇄용이 따로 있기도 했고 말이다. 한 서체를 나타내는 그런 각 크기별 폰트들을 모두 통틀어서 자족(font family)이라고 불렀다.

오늘날처럼 트루타입, 오픈타입 같은 베지어 곡선 기반의 윤곽선 폰트 기술이 보편화된 관점에서 보면, 다양한 크기를 얻는 건 너무 당연하고 하나도 특별할 게 없는 특성이지 않은가. 과거의 관행은 상상조차 하기 어려운 것 같다.
허나 과거에는 오히려 한 폰트가 혼자서 다양한 크기의 font family의 역할을 다하는 게 보통일이 아니었다. 그래서 글꼴 대화상자를 보면 트루타입 글꼴을 선택했을 경우 “이 글꼴은 트루타입 글꼴로, 화면과 프린터에서 동일한 글꼴이 사용됩니다”가 떴었다.

윤곽선 기술을 통해 크기 문제가 해결되고서야 영문 서체의 경우, font family는 다양한 크기의 집합이 아니라 bold나 italic 같은 변형의 총칭을 일컫는 개념으로 변모했다. 트루타입 글꼴이 도입되기 전엔 bold/italic은 그냥 원글꼴을 산술 연산으로 변형해서 구현했을 뿐, 별도의 글꼴로 만든다는 걸 생각하기 어려웠다. 조합 가짓수가 감당을 못 할 정도로 너무 많아지니 말이다.

인간이 쓰는 글자는 글자 하나하나가 생각보다 꽤 정교한 벡터 드로잉이다. 수많은 윤곽선 글꼴 자형을 화면에다 래스터라이즈 하려면 비트맵 글꼴만 상대하면 될 때보다 훨씬 더 많은 계산량과 메모리가 필요하다. 쉽게 말해 게임에서 2D 스프라이트가 3D 폴리곤으로 바뀌는 것과 비슷하다.

(지금이 아무리 컴퓨터의 성능이 좋아져도 3D 폴리곤으로 옛날의 스타크래프트 같은 수십· 수백 마리의 저글링 개떼 블러드를 구현하는 건 좀 찰진 맛이 안 난다. ㄲㄲㄲㄲㄲㄲ 또한 둠 2에서 둠 3으로 넘어가면서 가장 먼저 사라진 게, 예전 같은 광활한 개방된 맵에서 쏟아져 나오던 몬스터 개떼들이지 않던가. 뭐 어쨌든..)

그래서 자동차에 변속기가 필수인 것처럼 윤곽선 글꼴을 찍는 시스템은 운영체제든 무슨 프로그램이든간에 글꼴 캐시(font cache)가 반드시 있어야 한다. 쉽게 말해 자주 쓰이는 윤곽선 글꼴은 래스터라이즈된 비트맵 결과를 미리 저장해 놓고 재사용하라는 소리다.

제아무리 강한 엔진이라도 3~4단 기어에서 바로 출발 가능한 자동차는 없듯, 제아무리 날고 기는 고성능 폰트 엔진이라도 글꼴 캐시 없이 윤곽선 글꼴을 비트맵과 별 차이 없는 속도로 찍을 수는 없다.
폰트 캐시는 각종 운영체제나 소프트웨어가 잡아먹는 메모리에서 생각보다 많은 비중을 차지하고 있다. PC의 성능이 시원찮던 1990년대 중반에는 운영체제의 한글판과 영문판의 요구 시스템 사양의 차이를 만들 정도였다.

2. 힌팅

윤곽선 글꼴을 찍는 시스템은 이것만으로도 굉장히 복잡해지는데 또 하나 간과할 수 없는 것은, 바로 저해상도에서 심각하게 보기 안 좋아지는 품질 문제였다.
수백~수천 픽셀의 EM 크기에서 만들어진 매끄러운 윤곽선 패스를 수~수십 픽셀대로 축소하여 래스터라이즈하다 보면 획이 빠지거나 뭉개지거나 굵기가 뒤죽박죽이 되어 버린다. 컴퓨터의 래스터 디스플레이는 연속적인 실수가 아니라 유한한 정수 개의 픽셀로 구성되어 있으니 말이다.

요즘 유행하는 서브픽셀(ClearType)이라든가 그레이스케일은 한 픽셀에 담을 수 있는 정보량 자체를 흑백보다 더 늘려서 글자를 좀 더 부드럽게 보이게 하는 anti-aliasing 방법이다. 그러나 그 전에는 monochrome 디스플레이에서도 최대한 글자가 예쁘게 래스터라이즈 되게 하려면...

일단, 싱거운 결론이지만 이것은 원론적으로 100% 완전한 해결이 불가능한 문제이다.
래스터라이저를 아무리 귀신같이 잘 만든다 해도 이 획과 저 획이 어느 크기로 scale했을 때 간격이 같고 굵기가 같아야 할 기준을 스스로 찾을 수는 없다. 그 기준 자체가 아주 모호하고 인위적이기 때문이다.

결국, 서양에서 만들어 낸 것은 '힌팅'이라고 불리는 기술이다.
트루타입 폰트의 경우 이 힌팅이 특허로 등록되어 있었을 정도로 고급 핵심 기술이었다.
폰트 래스터라이저의 동작 알고리즘을 다 안다고 가정하고, 특정 크기에서 특정 글자는 윤곽점을 빼거나 추가하거나 위치를 옮겨서 인위적으로 이런 식으로 래스터라이즈되게끔(= 사람 눈에 보기 좋게) 윤곽선을 변조한다.

쉽게 말해 부가 정보를 덧붙인다는 뜻이다. 그래서 명칭도 힌트, 힌팅이다. 이건 100% 자동화를 할 수 없으며 장인의 정교한 수작업이 동원해야만 넣을 수 있다.
Times New Roman 같은 서체를 6~11포인트 크기로 anti-aliasing이 없이 보면 정말 하나하나 수작업으로 비트맵을 만든 게 아닌가 하는 생각이 들 정도로 모양이 예쁜 걸 볼 수 있는데, 그건 내장 비트맵이 아니라 힌팅만으로 주어진 윤곽선을 변형하여 만들어 낸 결과물이다. 래스터라이저의 범용적인 알고리즘만으로 만들 수 있는 결과물이 결코 아니다!

오늘날은 픽셀 자체를 anti-alias하는 기술이 발달하여 예전보다는 힌팅의 필요성이 줄어들었지만, 그래도 힌팅을 해 주면 윤곽선의 제어점이 래스터라이즈 기준 지점에 더 가까이 옮겨지기 때문에 뿌옇게 찍힐 것이 더 깔끔하고 배경과 글자 사이가 더 높은 채도로 찍히는 긍정적인 효과를 얻을 수 있다.

다만, 화면의 해상도까지 예전의 도트 프린터 수준으로까지 올라간다면 힌팅은 정말로 할 필요가 없고 그냥 anti-aliasing만으로 충분한 지경이 될 수 있다. 힌팅은 마치 옛날의 256색 팔레트 제어 기술만큼이나 legacy로 전락하는 날이 올지도 모른다.

그리고 한글이나 한자처럼 문자 집합의 크기가 커서 일일이 수제 힌팅을 도저히 줄 수 없는 문자는.. 애시당초 크기별로 내장 비트맵을 일일이 만들어 넣는 게 속 편하다. 뭐 요즘은 그 관행도 '맑은 고딕'을 시작으로 서서히 변하고 있긴 하지만 말이다.

3. 아이콘과 아이콘 패밀리

글꼴이 비트맵에서 윤곽선으로 넘어가면서 겪은 변화와 비슷한 맥락의 변화를 겪고 있는 곳이 또 있으니, 그건 바로 아이콘이 아닌가 싶다.

원래 응용 프로그램의 아이콘은 32*32 16컬러 크기만 있었다. 그러던 것이 Windows 95 이래로 16*16이 활발히 쓰이기 시작해서 메뉴나 작업 표시줄 같은 데서 좋은 인상을 주려면 오히려 16*16을 심혈을 기울여 잘 만들어야 하는 지경이 되었다. 아이콘 하나 때문에 Windows API에는 윈도우 클래스 등록용으로 WNDCLASSEX라는 구조체가 새로 만들어졌고 RegisterClassEx 함수가 도입되었다.

그러다 아이콘의 색깔은 256색 이상으로 늘고, 윈도우 XP에서부터는 트루컬러 정도가 아니라 알파 채널이 들어간 32비트 색상 아이콘이 등장했다. 덕분에 아이콘 하나도 크기가 수만 바이트로 늘고 프로그래머가 대충 발로 만들 수 없는 물건이 되어 버렸다. Visual Studio IDE는 최신 2012버전까지도 32비트 아이콘은 내용을 볼 수만 있지 고칠 수는 없다. 전용 그래픽 에디터가 필요해졌다.

그리고 윈도우 비스타부터는 32*32보다도 더 큰 48*48이 표준 크기로 또 추가되고, 아예 크기가 세 자리 수로 진입한 png 이미지가 아이콘 안에 들어가는 경지가 되었다. 이젠 아이콘 이미지도 예전 관행처럼 압축 없이 저장했다간 크기가 너무 커지기 때문이다.
XP까지만 해도 수만 바이트에 불과하던 간단한 메모장 프로그램이(notepad.exe) 별로 기능이 추가된 것도 없는데 비스타 이후부터 크기가 세 배로 뻥튀기 된 건 전적으로 아이콘 이미지 때문이다.

이렇게 아이콘의 크기가 커졌음에도 불구하고 아이콘은 역시나 본문 글자만큼이나 16*16의 작은 크기에서도 자신의 본분에 충실해야 한다. 이 점에서 아이콘은 글꼴과도 비슷한 구석이 있다. 단지, 크기뿐만 아니라 색상까지 고려해야 한다는 차이가 있을 뿐.

요즘은 굳이 16색까지 갖출 필요는 거의 없어졌지만, 프로그램의 한 아이콘은 똑같은 컨셉이더라도 자고로 최소한 256색과 32비트 트루컬러, 그리고 16, 32, 48과 심지어 경우에 따라서는 24나 40 같은 그 중간 크기까지 따로따로 갖추고 있어야 한다. 옛날에 크기별로 비트맵 글꼴을 따로 만들던 거랑 정확히 같은 맥락이니, 그야말로 font family에 착안하여 icon family라는 말을 만들어야 할 판이다.

그리고 작은 크기일 때는 수제 도트 노가다 말고는 정말 답이 없다. 큰 이미지를 무식하게 축소시켰다가는 품질이 그야말로 개판이 되기 때문. 아이콘에 무슨 힌팅 같은 게 있는 것도 아니니 말이다.
오늘날 아이콘은 점점 벡터 이미지처럼 되고 있는 면모가 있지만, 그렇다고 도트 노가다가 필요하지 않은 것도 아니니 참 오묘한 존재가 되어 간다는 생각이 든다.

language bar에 표시되는 각종 IME 아이콘들의 경우, 담겨 있는 정보는 단색이더라도 무조건 32비트 알파 채널 이미지로 만들지 않으면 운영체제가 아이콘을 화면에 제대로 표시해 주지를 않는다(Vista 이상 기준). 아이콘 출력을 재래식 GDI가 아니라 GDI+ 같은 다른 계층으로 하는 것 같다.

Posted by 사무엘

2013/03/16 19:31 2013/03/16 19:31
, ,
Response
No Trackback , 3 Comments
RSS :
http://moogi.new21.org/tc/rss/response/807

MFC와 View 오브젝트 이야기

1. 들어가는 말: MFC에 대한 큰 그림

MFC는 Windows API를 단순히 C++ 클래스 형태로 재포장만 한 게 아닌 독창적인 기능이 다음과 같이 최소한 세 가지 정도는 있다.

  • 가상 함수가 아니라 멤버 함수 포인터 테이블을 이용하여 메시지 핸들러를 연결시킨 메시지 맵. MFC 프로그래머 치고 BEGIN/END_MESSAGE_MAP()을 본 사람이 없다면 간첩일 것이다.
  • 운영체제가 제공하는 핸들 자료형들과 C++ 개체를 딱 일대일로 연결시키고, 특히 MFC가 자체적으로 생성하지 않은 핸들이라도 임시로 C++ 개체를 생성해서 연결했다가 나중에 idle time 때 자동으로 소멸을 시켜 주는 각종 handle map 관리자들. 절묘하다.
  • 20년도 더 전의 MFC 1.0 시절부터 있었던 특유의 document-view 아키텍처. 상당히 잘 만든 디자인이다.

양념으로 CPoint, CRect, CString 같은 클래스들도 편리한 물건이긴 하지만, 그건 너무 간단한 거니까 패스.

사실, MFC는 Windows API를 객체지향적으로 재해석하고 포장한 수준은 그리 높지 않다. 본디 API가 prototype이 구리게 설계되었으면, MFC도 해당 클래스의 멤버 함수도 똑같이 구린 prototype을 답습하고 내부 디테일을 그대로 노출했다.

이와 관련하여 내가 늘 드는 예가 하나 있다. 당시 경쟁작 라이브러리이던 볼랜드의 OWL은 radio button과 check button을 별도의 클래스로 분리했다. 그러나 MFC는 그렇게 하지 않았다. 운영체제 내부에서 둘은 똑같은 버튼 윈도우이고 스타일값만 다를 뿐이기 때문이다. 그러니 MFC로는 동일한 CButton이다. 그리고 CStatic도 마찬가지.
아마 기존 응용 프로그램의 포팅을 용이하게 하려고 의도적으로 이런 식으로 설계한 것 같긴 하지만, 이것 때문에 MFC를 비판하는 프로그래머도 물론 적지 않았던 게 사실이다.

그러나 인간이 하루 하루 숨만 쉬고 똥만 만드는 기계가 아니듯, MFC는 단순한 API 포장 껍데기가 아니라 다른 곳에서 더 수준 높은 존재감을 보여준다. 오늘 이 글에서는 document-view 아키텍처 쪽으로 얘기를 좀 해 보겠다.

2. view가 일반적인 윈도우와 다른 점

MFC는 뭔가 문서를 생성하여 작업하고 불러오거나 저장하는 일을 하는 업무용 프로그램을 만드는 일에 딱 최적화되어 있다. 그렇기 때문에 MFC AppWizard가 FM대로 생성해 주는 기본 코드는 아주 간단한 화면 데모 프로그램만 만들기에는 구조가 필요 이상으로 복잡하고 거추장스러워 보인다.
그냥 프레임 윈도우의 클라이언트 영역에다 바로 그림을 그려도 충분할 텐데 굳이 그 내부에 View라는 윈도우를 또 만들었다. 그리고 View는 Document 계층과 분리돼 있기 때문에, 화면에 그릴 컨텐츠는 따로 얻어 와야 한다.

이런 계층 구분은 소스 코드가 몇십~몇백만 줄에 달하는 전문적인 대형 소프트웨어를 개발할 걸 염두에 두고 장기적인 안목에서 해 놓은 것이다.
먼저, View와 Document를 구분해 놓은 덕분에, 동일한 Document를 여러 View가 자신만의 다양한 설정과 방법으로 화면에 동시에 표시하는 게 가능하다. 텍스트 에디터의 경우, 한 문서의 여러 지점을 여러 창에다 늘어놓고 수시로 왔다 갔다 하면서 편집할 수 있다. 한 창에서 텍스트를 고치면 수정분이 다른 창에도 다같이 반영되는 것이 백미.

일례로, MS 워드는 기본, 웹, 읽기, 인쇄, 개요 등 같은 문서를 완전히 다른 방식으로 렌더링하는 모드가 존재하지 않던가(물론, MS 워드가 MFC를 써서 개발됐다는 얘기는 아님). 게다가 이 중에 실제로 위지윅이 지원되고 장치 독립적인 레이아웃이 사용되는 모드는 인쇄 모드뿐이다. 인쇄를 제외한 다른 모드들은 인쇄 모드보다 문서를 훨씬 덜 정교하게 대충 렌더링하는 셈이다.

이렇듯, view는 그 자체만으로 독립성이 충분한 특성을 가진 계층임을 알 수 있다. view는 프레임 윈도우와도 분리되어 있는 덕분에, 한 프레임 윈도우 내부에 splitter를 통해 하위 view 윈도우가 여러 개 생성될 수도 있다.
CWnd의 파생 클래스인 CView는 윈도우 중에서도 바로 저런 용도로 쓰이는 윈도우를 나타내는 클래스이며, 부모 클래스보다 더 특화된 것은 크게 두 가지이다. 하나는 CDocument와의 연계이고 다른 하나는 화면 출력뿐만 아니라 인쇄와 관련된 기능이다.

SDI형 프로그램에서는 view 윈도우 자체는 계속 생성되어 있고 딸린 document만 수시로 바뀌기 때문에, document를 처음 출력할 때 view가 추가적인 초기화를 하라고 OnInitalUpdate라는 유용한 가상 함수가 호출된다. 그리고 화면 표시와 프린터 출력을 한꺼번에 하라고 WM_PAINT (OnPaint) 대신 OnDraw라는 가상 함수가 호출된다. 하지만 프린터 출력이 화면 출력과 기능면에서 같을 수는 없으니 CDC::IsPrinting이라든가 OnPrepareDC 같은 추가적인 함수도 갖고 있다.

그러고 보니 MFC의 view 클래스는 운영체제에 진짜 존재하는 '유사품' 메시지인 WM_PRINT 및 WM_PRINTCLIENT와는 어떻게 연계하여 동작하는지 모르겠다. 화면의 invalidate 영역과 긴밀하게 얽혀서 BeginPaint와 EndPaint 함수 호출을 동반해야 하는 WM_PAINT와는 달리, PRINT 메시지는 invalidate 영역과는 무관하게 그냥 창 내용 전체를 주어진 DC에다가 그리면 된다는 차이가 존재한다. 거의 쓰일 일이 없을 것 같은 메시지이지만, AnimateWindow 함수가 창 전환 효과를 위해 창 내용 이미지를 미리 내부 버퍼에다 저장해 놓을 때 꽤 유용하게 쓰인다.

3. CView의 파생 클래스들

MFC에는 CView에서 파생된 또 다른 클래스들이 있다. 유명한 파생 클래스 중 하나인 CCtrlView는 MFC가 자체 등록하는 클래스 말고 임의의 클래스에 속하는 윈도우를 그대로 view로 쓰게 해 준다.
그래서 운영체제의 시스템 컨트롤을 view로 사용하는 CTreeView, CListView, CEditView, CRichEditView 등등은 다 CCtrlView의 자식들이다.

  • 프로그램의 클라이언트 영역에다 CTreeView와 CListView를 splitter로 나란히 배열하면 '탐색기' 내지 레지스트리 편집기 같은 외형의 프로그램을 금세 만들 수 있다.
  • <날개셋> 편집기가 MFC를 써서 개발되던 버전 2.x 시절에는 문서 창을 CCtrlView로부터 상속받아 만들었다.

CCtrlView 말고 CView의 또 다른 메이저 파생 클래스로는 CScrollView가 있다. 얘는 이름에서 유추할 수 있듯, view에다가 스크롤과 관련된 기본 구현들이 들어있다. 텍스트 에디터 같은 줄 단위 묶음 스크롤 말고, 픽셀 단위로 컨텐츠의 스크롤이 필요한 일반 워드 프로세서, 그래픽 에디터 같은 프로그램의 view를 만들 때 매우 유용하다. 마우스 휠과 자동 스크롤 모드(휠 클릭) 처리도 다 기본 구현돼 있다.

인쇄 미리 보기 기능은 온몸으로 scroll view를 써 달라고 외치는 기능이나 다름없으며, 실제로 MFC가 내부적으로 구현해 놓은 '인쇄 미리 보기' view인 CPreviewView 클래스도 CScrollView의 자식이다.
단, 요즘은 Ctrl+휠을 굴렸을 때 확대/축소 기능도 구현하는 게 대세인데 배율까지 관리하는 건 이 클래스의 관할이 아닌 듯하다. 그건 사용자가 직접 구현해야 한다.

그럼 스크롤 가능한 view로는 오로지 자체 윈도우만 설정할 수 있느냐 하면 그렇지는 않다. CFormView는 대화상자를 view 형태로 집어넣은 클래스인데 그냥 CView가 아니라 CScrollView의 파생 클래스이다. 워낙 설정할 게 많아서 환경설정 대화상자 자체가 세로로 쭈욱 스크롤되는 프로그램은 여러분의 기억에 낯설지 않을 것이다.

옛날에 윈도우 3.x 시절의 PIF 편집기처럼 클라이언트 영역에 대화상자 스타일로 각종 설정을 입력 받는 게 많은 프로그램을 만들 때 CFormView는 대단히 편리하다. 대화상자는 여느 윈도우들과는 달리, 자식으로 추가된 컨트롤들에 대해 tab 키 순환과 Alt+단축키 처리가 메시지 처리 차원에서 추가되어 있다.

4. CScrollView 다루기

처음에는 CView로부터 상속받은 view를 만들어서 프로그램을 열심히 만들고 있다가, 뒤늦게 view에다가 스크롤 기능을 추가해야 할 필요가 생기는 경우가 종종 있다.
이미 수많은 프로그래밍 블로그에 해당 테크닉이 올라와 있듯, 이것은 대부분의 경우 base class를 CView에서 CScrollView로 문자적으로 일괄 치환하고 몇몇 추가적인 코드만 작성하면 금세 구현할 수 있다.

클래스 이름을 치환한 뒤 가장 먼저 해야 할 일은 스크롤의 기준이 될 이 view의 실제 크기를 SetScrollSizes 함수로 지정해 주는 것이다. OnInitialUpdate 타이밍 때 하면 된다. 안 해 주면 디버그 버전의 경우 아예 assertion failure가 난다.

여기까지만 하면 반은 먹고 들어간다. OnDraw 함수의 경우, 전달되는 pDC가 아예 스크롤 기준대로 좌표 이동이 되어 있다! 즉, 내부적으로 (30, 50) 위치에다가 점을 찍는 경우, 현재 스크롤 시작점이 (10, 20)으로 잡혀 있으면 화면상으로 이 위치만치 뺀 (20, 30)에 점이 찍힌다는 뜻이다. 내가 수동으로 스크롤 좌표 보정을 할 필요가 없다. 아, 이 얼마나 편리한가! invalid 영역의 좌표도 화면 기준이 아닌 내부 기준으로 다 이동된 채로 전달된다.

그러니 CView 시절에 짜 놓은 그리기 코드를 어지간하면 수정 없이 CScrollView에다 곧바로 써먹을 수 있다. 다만, 최적화만 좀 신경 써 주면 된다. 당장 화면에 표시되는 영역은 수백 픽셀에 불과한데 수천 픽셀짜리의 전체 그림을 몽땅 불필요하게 계산해서 그리는 루틴을 OnDraw에다 때려박지 않도록 주의해야 한다.
이때 유용한 함수는 RectVisible이다. 이 영역이 invalidate되었기 때문에 반드시 그려 줘야 하는지의 여부를 알 수 있다.

그 다음으로 신경을 좀 써야 하는 부분은 마우스 클릭이다.
마우스 좌표는 화면 기준으로 오지 내부 기준으로 오지는 않으므로, 내부 개체에 대한 hit test를 하려면 마우스 좌표에다가 GetScrollPosition(현재 스크롤 위치) 함수의 값을 더하면 된다.
그리고 화살표 키로 무슨 아이템을 골랐다면, 그 아이템의 영역이 지금의 화면 범위를 벗어났을 경우 스크롤을 시켜 줘야 한다. 수동 스크롤은 ScrollToPosition 함수로 하면 된다.

화면의 일부 영역을 다시 그리도록 invalidate하는 것도 스크롤 위치 반영이 아닌 그냥 지금 화면 기준의 좌표를 지정하면 된다. 그러면 OnDraw 함수에서는 스크롤 위치가 반영된 내부 좌표 기준으로 refresh 위치가 전달된다.

끝으로, 마우스로 어떤 개체나 텍스트를 눌러서 끌든, 혹은 단순 selection rectangle을 만들든 그 상태로 포인터가 화면 밖으로 나갔을 때, 타이머를 이용한 자동 스크롤도 구현해야 할 것이다. 이 역시 자동화하기에는 customization의 폭이 너무 넓기 때문에 MFC가 알아서 해 주는 건 없다. 알아서 구현할 것. 이 정도면 이제 스크롤 기능을 그럭저럭 넣었다고 볼 수 있을 것이다.

이 정도면 어지간한 개발 이슈들은 다 나온 것 같다.
참, 혹시 재래식 GDI API가 아니라 GDI+를 쓰고 있는 프로젝트라면 CScrollView로 갈아타는 걸 신중히 해야 할 것 같다. GDI+는 MFC가 맞춰 놓은 GDI 방식의 기본 스크롤 좌표를 무시하고 DC의 상태를 난장판으로 만들어 버리기 때문이다. GDI+는 재래식 GDI보다 느리지만 곡선의 안티앨리어싱과 알파 블렌딩이 뛰어나니 아무래도 종종 사용되게 마련인데..

간단한 해결책 중 하나는, GDI+ 그래픽은 CreateCompatibleDC / CreateCompatibleBitmap을 이용한 메모리 DC에다가 따로 그리고, 본디 화면에다가는 그 결과를 Bitblt로 뿌리기만 하는 것이다. 그렇게 하면 아무 문제가 발생하지 않고, 심지어는 속도도 내 체감상으로는 더 빨라지는 것 같다.

Posted by 사무엘

2013/03/13 19:34 2013/03/13 19:34
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/806

1. 메뉴 -- 긴 역사를 자랑하는 GUI 구성요소

'메뉴'(menu)라는 단어는 순우리말로는 흔히 차림표라고 하고, 식당의 음식 메뉴 아니면 컴퓨터 소프트웨어의 GUI 요소라는 꽤 이질적인 두 심상이 결합해 있는 독특한 단어이다. 이런 점에서 '메뉴'는 '마우스'하고도 비슷한 구석이 있는 것 같다.

메뉴는 GUI라는 개념이 컴퓨터에 도입된 이래로 굉장히 오랜 시간을 인간과 함께해 왔다. 워낙 중요하고 필수적인 기능이기 때문에 Windows 운영체제는 아예 API 차원에서 창을 하나 만들 때 메뉴 핸들을 같이 넘겨 줄 수 있게 돼 있다. (CreateWindowEx 함수) Windows는 그래도 보급 메뉴(?) 지원을 무시하고 GUI 툴킷이 자체 구현한 싸제 메뉴를 붙일 여지라도 있지만, Mac OS는 메뉴 bar가 무조건 화면 위에 붙박이로 고정이고 게다가 운영체제의 시스템 메뉴와 일심동체로 통합되어 있기 때문에 싸제 메뉴 같은 건 있을 수 없다.

물론, 너무 무난하고 밋밋한 관계로 요즘 만들어지는 응용 프로그램에서는 메뉴가 천덕꾸러기처럼 취급되는 면모가 없지는 않다. 메뉴+툴바가 리본 UI로 대체된 것은 물론이고, 메뉴가 있더라도 메뉴 bar를 평소에는 감춰 버리고 Alt키를 눌러야만 마지못해 보여 준다. 글쎄, 이러다가 나중에 또 복고풍으로 메뉴로 돌아가지는 않을지?
그리고 어떤 경우든 사각형 안에서 선택막대로 기능을 선택하는 전통적인 메뉴 개념 자체가 없어지는 일은 없을 것이다.

난 닷넷 프레임워크는 그냥 운영체제의 보급 메뉴를 자기 고유 API로 감쌌는줄 알았는데, 그렇지 않다는 걸 알게 되어 개인적으로 놀란 적이 있다. 닷넷 기반 GUI 프로그램은 기본적으로 Office XP 스타일을 적당히 따라 한 싸제 메뉴가 나온다.

보급이든 싸제든, 어쨌든 GUI에서 전통적인 메뉴는 F10을 눌렀을 때 화면 상단에 나타나는 가로줄 메뉴, 혹은 main 메뉴를 가리키는 경우가 많다.
그러나 이것 외에 어떤 개체를 마우스로 우클릭했을 때 나타나는 Context 메뉴, 혹은 팝업 메뉴는 좀 더 나중에, 1990년대 중반에 도입되었다. 윈도우 95 이전에 3.x 시절에는 그림판으로 두 색깔을 번갈아가며 쓸 때 말고는 마우스를 우클릭할 일 자체가 거의 없었던 것 같다. 팝업 메뉴를 띄우는 기능 자체는 3.x 시절에도 있었을 텐데도 불구하고 말이다.

2. HMENU

자, 그럼 Windows 플랫폼 프로그래밍의 관점에서 운영체제의 메뉴 개체에 대해서 좀 더 살펴보자.

이 메뉴라는 놈을 관리하는 개체는 바로 HMENU이다. 얘는 메뉴에 표시시킬 각종 아이템들과 그것들의 상태들을 보관하고 있는 일종의 연결 리스트의 포인터라고 생각하면 된다. 어떤 메뉴 항목에는 또 부메뉴가 딸려 있을 수 있으므로 메뉴는 일종의 재귀성까지 갖추고 있다.

메뉴는 잘 알다시피 리소스의 형태로 쉽게 만들어 내장시킬 수도 있다. 그러나 HMENU 값은 아이콘이나 액셀러레이터, 마우스 포인터 같은 여타 리소스들과는 달리, read-only 리소스가 아니다. 이게 무슨 말인지 배경을 좀 설명하자면 이렇다.

16비트 Windows 시절에는 EXE/DLL에 있는 리소스 데이터를 얻기 위해서 별도로 파일을 열고 메모리를 할당하고 고정하는 등의 절차가 필요했다. 그러나 운영체제가 32비트 환경으로 바뀌면서 실행 파일의 로딩 방식이 memory mapping 방식으로 바뀌었기 때문에, 모듈에 내장된 리소스를 찾는 건 그냥 이미 로딩된 메모리의 주소만 되돌리는 형태로 아주 간단해졌다.

그래서 예전과는 달리, 이제는 한번 fetch해 온 리소스 데이터에 대해서 FreeResource 같은 함수를 호출할 필요가 없어졌다. 그 리소스를 제공하는 EXE의 실행이 종료되거나 DLL이 Unload될 때 어차피 자동으로 한꺼번에 해제되기 때문이다.

일반적인 읽기 전용 리소스는 그런 간소화의 혜택을 입게 되었다.
그러나 메뉴의 경우는 모듈에 내장된 메뉴 데이터의 포인터만 얻어 오는 걸로 끝이 아니라, 그 데이터를 토대로 메뉴 연결 리스트를 별도로 재구성한다. 사용자는 그 연결 리스트의 데이터를 변경함으로써 메뉴에 별도의 항목을 추가하거나 삭제하고, 체크 표시나 disable 처리를 할 수 있다.

그렇기 때문에 LoadIcon, LoadCursor 등의 리턴값은 Free를 할 필요가 없지만, LoadMenu 핸들의 리턴값은 반드시 DestroyMenu를 해 줘야 한다. (물론, 아이콘 같은 리소스라 해도 모듈 내장이 아니라 직접 동적으로 생성한 놈이라면 Destroy*함수를 호출해서 수동으로 소멸해야 하는 건 변함없음.)

HMENU는 내부적으로 딱히 reference counting을 하지는 않는 단순한 구조이다.
윈도우와 연결되어 있는 메뉴는 윈도우가 소멸될 때 같이 자동으로 소멸되며(물론 부메뉴들도 재귀적으로 다 같이), 한 메뉴 인스턴스가 여러 윈도우에서 공유되지는 않는다. '이동', '닫기' 같은 명령이 있는 시스템 메뉴가 있는데, 필요하다면 사용자가 이 메뉴 역시customize할 수 있다.

3. API 디자인

(1) Windows API의 설계 관점에서 흥미로운 것은, 정수로 식별하는 ID를 받는 곳에다가 필요에 따라 메뉴 핸들도 같이 집어넣게 한 게 종종 보인다는 점이다.
CreateWindowEx 함수의 경우, HMENU는 생성하려는 윈도우가 팝업 같은 메이저 윈도우이면 메뉴 핸들이고, 메뉴를 갖는 게 의미가 없는 자그마한 마이너 자식 윈도우이면 정수 ID를 의미한다.

물론 메뉴 핸들과 ID가 동시에 쓰일 일은 없는 건 사실이다. 윈도우의 ID는 대화상자의 차일드 컨트롤들을 식별할 때에나 쓰는 것이니 말이다.
하지만 어째 이 둘을 실제로 공유시킬 생각을 했는지 궁금하다. 어지간하면 그냥 내부 구조체에다 별도의 멤버를 따로 둘 법도 한데, Windows 1.x 시절의 헝그리 정신을 살려, 메모리 절약을 위해 공용체를 썼는가 보다.

또한 메뉴 API도 AppendMenu나 InsertMenu를 보면, 일반 메뉴 아이템에 대해서는 명령 ID를 전달하는 항목에, MF_POPUP이 지정된 하위 메뉴 아이템에 대해서는 또 HMENU를 typecast하여 전달하게 되어 있다.

(2) CreateMenu와 CreatePopupMenu 함수를 왜 따로 만들어 놨는지 영 이해가 안 된다. HINSTANCE와 HMODULE만큼이나 사실상 의미 없는 구분이 돼 있다.
응용 프로그램의 main 메뉴나 우클릭 팝업 메뉴는 화면에 보이는 형태만 다를 뿐, 부메뉴를 가질 수 있는 재귀적인 형태인 것도 똑같고 내부 자료 구조가 달라야 할 것은 없다.
하긴, 그러고 보니 HCURSOR도 HICON하고 내부적으론 거의 같은 자료구조라고 하지. (핫스팟 위치만 추가됐을 뿐)

(3) 메뉴의 상태를 나타낼 때 MF_GRAYED와 MF_DISABLED를 따로 만들어 놓은 건 개인적으로 무척 기괴하게 여겨진다.
MF_GRAYED는 우리가 흔히 보는 '사용할 수 없는' 메뉴 아이템이다. 흐리게 표시되고 선택도 되지 않는다. 그러나 MF_DISABLED는 선택만 안 될 뿐 흐린 표시는 아니다.
이건 솔직히 말해서 잉여력이 넘치는 구분이다.

그래서 심지어는 MS 내부의 개발자들조차도 이를 혼동해 있다.
고전 테마를 쓰고 있을 때는 MF_DISABLED를 설정한 메뉴가 '일반 글자'로 표시된다.
그러나 Luna나 Aero 같은 테마가 적용되어 있을 때는 이게 MF_GRAYED와 동일하게 '흐린 글자'로 표시된다! 문서화된 바와도 다르고 일관성 없게 동작한다는 뜻이다. 내 말이 믿어지지 않으면 당장 프로그램을 짜서 확인해 보기 바란다.
일상생활에서는 MF_DISABLED는 전혀 신경 쓸 필요 없고 MF_GRAYED만 쓰면 될 것 같다.

(4) RemoveMenu, DeleteMenu, DestroyMenu의 차이가 뭘까?
먼저 DestroyMenu는 HMENU 자체를 완전히 소멸시키는 함수이다. 메뉴와 부메뉴들이 모두 다 사라지고 해당 핸들은 사용할 수 없게 된다.
RemoveMenu와 DeleteMenu는 메뉴 안에 있는 한 항목을 제거한다. 제거할 항목을 순서 인덱스 또는 명령 ID로 지정할 수 있다. 부메뉴를 가진 항목이나 항목 구분용 separator는 명령 ID를 갖고 있지 않으므로 반드시 순서 인덱스만 지정 가능할 것이다.

둘의 차이는 딱 하나. 부메뉴를 가진 항목을 지울 때 부메뉴 핸들을 재귀적으로 destroy하느냐(Delete) 안 하느냐(Remove)이다. 마치 '프로젝트 목록에서 파일 제거'와, '파일 제거 + 실제로 디스크 상에서도 삭제'의 차이와 비슷한 맥락이다.

(5) 사실, Windows의 메뉴 API가 좀 더 객체지향적으로 설계되었다면, HMENU뿐만 아니라 각각의 메뉴 아이템을 나타내는 HMENUITEM 같은 자료형도 또 만들었을 것이다.
지금은 그렇지 않기 때문에 메뉴 아이템을 식별할 때마다 매번 HMENU와 UINT nID, 그리고 nID가 명령 ID인지, 순서 인덱스인지를 나타내는 플래그를 넘겨줘야 한다. 메뉴 항목을 편집하거나, 어디 뒤에 삽입하거나 삭제하는 함수들이 전부 저 인자들을 일일이 받는다. 내가 보기엔 무척 지저분하다.

또한 동일한 기능을 하는 API가 구 API, 그리고 좀 더 기능이 확장되고 구조체를 인자로 받는 신 API가 섞여서 중구난방스러운 것도 어쩔 수 없는 일이다. 가령, 예전에는 CheckMenuItem 같은 함수가 있었지만 지금은 SetMenuItemInfo가 있는 식. 새로운 함수는 범용적이긴 하지만 매번 구조체를 만들어서 초기화해 주는 작업이 몹시 성가신 것도 사실이다.

32비트 Windows부터는 각각의 메뉴 아이템에 대해서 명령 ID와는 별개로 임의의 UINT_PTR 데이터 값을 갖는 게 가능해졌다. 마치 리스트박스에서 item data와 비슷한 맥락이다. 이 값을 읽고 쓰는 함수로 지저분하게 SetMenuItemData 같은 함수를 또 추가하느니, 차라리 메뉴와 관련된 모든 속성을 읽고 쓸 수 있는 SetMenuItemInfo라는 종결자 함수를 만들게 됐을 것이다.

Posted by 사무엘

2013/03/10 19:15 2013/03/10 19:15
, ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/805

커티스 르메이(1906-1990).
20세기 중반에 활동한 미국의 군 장성이다. 군사, 세계사, 현대 전쟁사 쪽에 관심이 있는 사람이라면 아마 이름을 들어서 알 것이다.

사용자 삽입 이미지

이 양반 정말 골때리면서도 재미있는 사람이다.
그는 정말 뼛속까지 군인 타입으로, 닥치고 폭격기 화력 덕후였으며 그의 주특기는 쑥밭 만들기였다.
하긴, 그 당시 미국은 워낙 물자가 풍족하게 넘쳐나는 부자 나라였으니 그의 전투 이념은 나름 적절했다.

게다가 그는 '석기 시대'를 굉장히 좋아한 매니아였다. ㅋㅋㅋㅋㅋ
미국 앞에서 깝치는 적국들은 본진을 폭격으로 다 쑥밭으로 만드는 것도 아니고, 모조리 '죽탕치는(?)' 것도 아니고, '석기 시대'로 되돌려 놓겠다는 으름장을 공석에서 입버릇처럼 뇌까렸다. 영어로는 Stone Age.
호전적이고 입이 험악한 걸로 악명 높은 북한도 공식 석상에서 석기 시대 공갈을 친 적은 없는 것 같다.

오죽했으면 르메이 장군에 대해서 이런 패러디짤이 나돌 정도이다.

사용자 삽입 이미지

그의 지휘 하에 일본 도쿄는 2차 세계 대전 말기에 그 이름도 유명한 '도쿄 대공습'을 당했다. 미군 폭격기가 우박처럼 떨어뜨리는 소이탄에 시내 전체가 말 그대로 시뻘건 불바다가 되어 버렸다. 사진에서 보듯, 목조 건물은 형체도 없이 그냥 주저앉아 없어졌고, 일부 석조/콘크리트 건물도 새까맣게 탄 흉측한 몰골만 남았다.

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

이 폭격으로 인해 죽은 사람은 10만여 명에 달해서 사실은 히로시마 원자 폭탄 투하로 죽은 사람보다도 수가 더 많았다고 한다. 도쿄를 그런 석기 시대로 되돌리는 데는 겨우 3시간 남짓밖에 걸리지 않았다.

그러나 이게 무슨 원폭을 성층권 고도에서 투하하는 식이 아니라 상당한 저공에서 위험한 자세로 소이탄을 떨어뜨리다 보니, 미군도 폭격기가 총 12기나 일본의 대공포로부터 반격을 받아 격추되고, 42기는 피탄 당하는 손해를 입었다. 이에 몇몇 미군 파일럿들은 권총을 들고 르메이를 직접 찾아가서 이렇게 따졌다.

“왜 이런 무모한 저공 비행 폭격 명령을 내렸는가? 귀관 때문에 우리가 전우를 얼마나 많이 잃었는지 아는가?”

하지만 르메이는 그 말은 들은 체도 하지 않고 이렇게 응수했다고 한다.

“제군들은 단 하루 만에 일본 제국의 수도를 잿더미로 만들고 놈들을 최소 10만 명이나 없앴다! (사실, 미군 전사자는 많아 봤자 수십~수백 명에 지나지 않을 것이다) 그러니 오늘 작전은 대성공이다. 이런 식으로 내일은 나고야, 모레는 오사카, 그 다음은 고베.. 1주일 동안 일본 전체를 잿더미로 만들 것이다. 모두들 오늘의 성공을 자축하도록!

전쟁을 치르면서 전사자가 나오는 건 어쩔 수 없는 일이고 작전 자체는 르메이의 말대로 미군의 승리이긴 하지만... 저건 좀.. ^^;; 정말 그의 머리에 든 건 오로지 폭격밖에 없었다.
일본이 원폭을 맞고 나서 일찌감치 항복을 하지 않았다면, 그래서 르메이가 생각한 작전들이 모두 시행되었다면 일본이라는 나라는 진짜로 지도에서 없어지고 일본 열도는 석기 시대로 퇴화했을지도 모른다.

이 양반의 무자비한 작전은 훗날 6·25 때도 계속되었다. 북한 중에서도 평양 시내는 그야말로 형체가 남은 건물이 손에 꼽을 정도였을 정도로 그냥 말 그대로 모조리 잿더미가 되었다. 오로지 미국이니까 가능한 돈지랄로 폭탄을 그냥 때려 박았다고 생각하면 된다.

이 때문에 그 당시 북한의 내부에서는, 평양을 재건할 게 아니라 아예 이 기회에 수도를 다른 데로 옮기는 게 낫지 않겠냐는 논의도 오갔다고 한다. 비록 그렇게 되지는 않았지만 말이다.
그리고 이때의 공습과 폭격의 악몽 때문에 지금 평양 시내는 이에 대비하느라 지하 방공망이 굉장히 깊고 정교하게 구축되어 있다. 평양 지하철이 무지막지하게 깊게 건설된 것도 이런 맥락에서이다.

그 뒤에도 르메이의 버릇은 어디 가지 않았다. 케네디 대통령 시절에 있었던 월남전 땐 베트남도, 그리고 쿠바 사태가 벌어졌을 때도, “베트남이건 쿠바건 다 폭격해서 석기 시대로 되돌려 놓겠다. 대통령 각하는 명령만 내려 달라”는 식으로 일관되게 나섰다.
마치 게임 해설자 김 태형 씨가 캐리어를 좋아하듯 그는 석기 시대가 자기 상징이 되어 버렸다.

그래서 나중에는 미국의 정치인들도 그의 말투를 따라하는 지경에 이르렀다. 미국은 걸프전 때 이라크를 석기 시대로 되돌리겠다고 공갈을 쳤고, 나중에 9· 11이 터졌을 때는 파키스탄을 상대로도 대테러전에 협조하지 않으면 너네 나라를 석기 시대로 되돌려 놓겠다고 그랬다. 뭐, 반미 성향이 있는 사람이라면 이런 협박 멘트에 심기가 불편함을 느낄 것이다.

르메이 같은 사람도 미군에 있는데 맥아더 장군은 차라리 양반이라는 생각이 문득 들었다.
맥아더가 훌륭하고 존경스러운 군인이라지만 그도 인간이고 신은 아니기에, 맨날 인천 상륙 작전 같은 성공만 한 게 아니며 실수도 저질렀다. 처음엔 북한과 중공군을 얕잡아보다가 1· 4 후퇴를 당하고 호되게 데인 뒤에야, “이 자식들 안 되겠어.”라고 하면서 정말 상상을 초월하는 강경책을 쓰려 했다.

어떻게든 빨갱이들을 없애 버리겠다는 의도 자체는 좋지만, 그렇다고 한반도에다 핵을 또 터뜨린다거나, 전쟁을 아예 3차 세계 대전 급의 대규모 장기전으로 키우는 것도 불사하겠다는 식이었으니... 이런 강경한 생각이 화근이 되어 맥아더가 트루먼 대통령과의 사이가 틀어진 건 유명한 사실이다.

그런데 르메이도 그 당시 “이 좋은 핵무기를 왜 안 써?” 급의 생각을 하고 있던 건 마찬가지였다. 맥아더보다 더하면 더한 꼴통이지 못하지는 않았다.
미국 대통령이 이런 호전적인 군 장성 양반들의 근성을 이성적으로 잘 통제하지 않았다면, 과거에 소련과의 냉전이 냉전으로 끝나지 않았을 수도 있었다. 굳이 한반도가 아니어도 어디에서 핵이 한두 발 터지는 바람에 특정 국가가 지도에서 사라지거나 진짜 석기 시대로 돌아갈지도 몰랐다.

오죽했으면 아인슈타인도 “3차 세계 대전 때 인간이 무슨 신무기를 쓰고 있을지는 난 잘 모르겠습니다. 하지만 그 다음 세계 대전이 일어난다면, 그때 인간은 새총(slingshot) 같은 냉병기를 쓰고 있겠죠?”라고 얘기했겠는가. 성문 종합 영어에도 등장하는 유명한 지문이다. 아인슈타인 역시 영락없이 석기 시대 회귀-_-를 염두에 두고 있었다는 뜻이다.

석기 시대 드립 말고 르메이 장군이 자신의 호전성을 또 드러낸 유명한 어록으로는 “세상에 무고한 민간인이란 없다”(There are no INNOCENT civilians)이다.
사실, 도쿄 대공습 같은 경우 미국이 연합국이고 전쟁에서 이겼으니 망정이지, 저렇게 대놓고 시내를 폭격하여 비전투 민간인들을 무차별 학살하는 건 전쟁 범죄로 간주될 수 있는 짓이었다.
대놓고 말해, 나치 독일이 영국이나 미국의 본토 도심지를 소이탄 폭격으로 다 불태워서 민간인을 대량 학살했다면 그 후폭풍이 어찌 됐겠는가? 그럼에도 불구하고 르메이는 작전을 강행했다.

일례로, 히로시마에 원자 폭탄을 투하하는 작전에 참여했던 폴 티베트 대령은 훗날 다음과 같은 요지로 회고한 바 있다.
“난 그 당시 그저 명령에 따랐을 뿐이다. 그리고 원폭은 전쟁을 더 일찍 종결시키고 더 많은 인명의 희생을 막은 차선이며 필요악이었다고 생각한다. 군인으로서 나의 행동에 대해 양심의 가책이나 후회는 없다.”

허나, 르메이는 한 술 더 떠서 민간인의 죽음에 대해 아예 그 정도의 책임이나 죄책감마저도 가질 필요가 없다는 식이었으니, 더 할 말이 없다. ^^
“사실 저 밑에 곤도네는 군용 볼트를, 옆집 스즈키네는 군용 너트를 만들고 있을 뿐이다. (무고해 보인다고 저 민간인들을 안 죽이면, 그게 다 우리를 죽이는 병력이 되어 돌아온다. 그러니 마음껏 폭격하고 닥치는 대로 죽이고 불태워 버려라.)”

그리고 결정적으로는 르메이의 말이 어느 정도 사실이었다! 일본은 도시 구조가 민간인 거주지와 군수 업체 영역의 구분이 그다지 분명하지 않았기 때문이다.

그의 생전 어록으로는 이 외에도
“전쟁이란 총알 많은 쪽이 많이 죽이면 이기는 것이다.”
“충분히 많이 죽이면 다시는 덤벼들지 못할 것이다.”

처럼, 틀린 말은 아니지만 좀 우악스럽고 꼴통 같은 방면으로 주로 전해 내려오고 있다.

그래도 아무려면 어때, 천조국 미국 소속이지 않은가. 그리고 그는 최소한

“보급이란 원래 적에게서 취하는 법이다.”
“포탄은 자동차 대신 소나 말에 싣고 가고, 그러다 포탄을 다 쓰면 필요 없어진 소나 말을 먹으면 된다.”
“정글에서 비행기를 어디에다가 쓰냐?”
“일본인은 원래 초식동물이니 가다가 길가에 난 풀을 뜯어먹으며 진격하라.

같은 이런 진짜 미친 개소리를 하지는 않았다. 저건 잘 알다시피 '무다구치 렌야'라고 일본 역사상 최악의 무능한 장군이 남긴 훈시.. -_-
그도 그럴 것이 물량이 풍족한 곳에서 그냥 물량으로 밀면 된다는 교리가, 없는 여건 속에서 닥치고 근성과 정신력만으로 돌격하라는 교리보다는 훨씬 나은 게 자명하지 않은가?
그는 스타크래프트를 했으면 무한 맵에서 저그 가디언 굴리는 걸 좋아했을 것 같다. (대공 유닛으로 대지상 폭격 -_-)

뭐, 르메이 장군은 맥아더 장군과 마찬가지로 우리 같은 사람으로서는 미워할 구석이 없는 인물이다.
우방국의 장군답게 일본, 북한 등 대한민국의 적들하고만 싸웠으니 말이다.
(아 하긴, 무다구치 렌야도 자기 군대를 말아먹은 행적이 가히 대한 독립 유공자 급이니, 우리가 딱히 미워할 구석이 없는 건 마찬가지이고.. -_-;;;)

그는 그런 호전적인 기질답지 않게 미군의 전술 체계 수립에 큰 공을 세운 바 있으며, 심지어 적국인 일본으로부터도 훗날 자위대의 재건에 기여한 공로를 인정받아 훈장을 받기도 했다. 괜히 장성까지 진급한 게 아니다.

다만, 그 막강한 화력으로 미국이 전투는 이겼을지 몰라도 한국전과 베트남전은 적군을 완전히 몰아내는 깔끔한 '전쟁 승리'를 이루지 못한 것은 아쉬운 점이다.
그리고 도쿄 대공습 때는 재일 동포도 많이 희생된 게 사실이다. 비록 뭐 어쩔 수 없는 일이지만 말이다.

그리고 글을 맺으면서 마지막 한 마디.
이 사람의 상징은 잘 알다시피 '석기 시대'이다. 허나 아담 이래로 6천 년 인류 역사를 믿는 크리스천은 인류에게 딱히 석기 시대라 불릴 만한 긴 원시 시대가 존재했다고 믿지 않는다.
문명의 이기가 존재하지 않는 시절로의 퇴보를 언급하려 한다면, 차라리 노아의 홍수 직후 상태로 되돌리겠다고 하는 게 말이 될 텐데.. 아무래도 '석기 시대'보다는 우악스러움이 덜하다. ^^

Posted by 사무엘

2013/03/07 19:26 2013/03/07 19:26
, , , , , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/804

1.

본인은 비주얼 C++ 2012로 갈아탄 뒤부터 예전에는 본 적이 없는 이상한 현상을 겪곤 했다. 내가 만들고 있는 프로그램을 IDE에서 곧장 실행하자(Ctrl+F5 또는 F5) 프로세스는 분명히 실행되어 있는데 창이 화면에도, 작업 표시줄에도 전혀 나타나 보이지 않았다.

Spy++를 돌려 보니 프로그램 창이 생기긴 생겼는데 어찌 된 일인지 WS_VISIBLE 스타일이 없이 숨겨져 있다는 걸 알게 되었고, 문제의 원인은 생각보다 금방 발견할 수 있었다.
프로세스에 전달되는 STARTUPINFO 구조체의 wShowWindow 멤버 값은, dwFlags에 STARTF_USESHOWWINDOW 플래그가 있을 때에만 유효하다는 걸 깜빡 잊고 있었던 것이다.

일반적으로 프로그램을 실행할 때 운영체제가 그 구조체에다 ShowWindow 플래그를 안 넣는 적은 사실상 없기 때문에 지금까지 그 로직이 별로 문제가 되지 않았었다. 하지만 비주얼 C++ 2012는 이례적으로 그 구조체의 거의 모든 멤버들을 그냥 0으로만 집어넣은 채 프로세스를 생성하고, 0은 SW_HIDE와 같기에 창이 화면에 나타나지 않았다.

2.

<날개셋> 한글 입력기 외부 모듈을 debug 형태로 빌드한 뒤 디버거를 붙여서 실행해 보면, 때에 따라서는 호스트 프로세스가 종료될 때 memory leak 로그가 뜨는 경우가 종종 있었다. 하지만 이것이 항상 나타나는 건 아니고 leak의 양이 심각하게 많은 건 아니었기 때문에, 본인은 크게 신경 쓰지는 않았다.

그런데 우연히 추가 디버깅을 한 결과, 응용 프로그램에 따라서 아예 COM 개체들의 reference count가 달라지고 TSF 모듈의 소멸자 함수의 실행 여부가 달라지는 걸 발견하였고, 이에 본인은 이 현상에 대해 좀 더 심혈을 기울여 디버깅을 실시하게 되었다.

이건 꽤 특이한 현상이었다. <날개셋> 편집기에서도 leak이 발생했기 때문에 가장 먼저 'TSF A급 지원' 옵션을 꺼 봤다. 그리고 외부 모듈은 아예 날개셋 커널을 로딩하지 않고 아무 기능도 사용할 수 없는 panic 상태로 구동했다. 그렇게 프로그램의 주요 기능들을 다 끄고 절름발이로 만들었는데도 <날개셋> 외부 모듈을 한 번이라도 로딩을 하고 나면 leak이 없어지지 않았다.

이런 식으로 COM 오브젝트의 reference count가 꼬이는 버그는 여간 골치 아픈 문제가 아니기에 각오 단단히 하고 디버깅을 계속할 수밖에 없었다. 그 결과 무척 신기한 점을 발견했다. MFC를 사용하는 GUI 프로그램과, MFC든 무엇이든 대화상자(DialogBox)를 사용하는 프로그램에서는 leak이 안 생기는데, Windows API로 message loop을 직접 돌리면서 윈도우를 구동하는 프로그램에서는 memory leak이 발생한다는 것이었다.

오히려 방대하고 복잡한 MFC를 쓰는 프로그램에서 메모리가 새면 샜지, 왜 더 간단한 프로그램에서 문제가 발견되는 걸까?
이 정도까지 밝혀지니 궁금해 미칠 지경이 됐다. leak이 있는 프로그램과 없는 프로그램을 종료할 때 외부 모듈 개체의 Release 함수가 어떻게 호출되고 reference count가 어떻게 변하는지를 검토했다.

그리고 드디어 leak이 있는 프로그램과 없는 프로그램의 차이가 밝혀졌다.
MFC는 프로그램 창이 WM_CLOSE 메시지를 받아서 창의 소멸 단계로 들어서기 전에, 프로그램 창을 강제로 한번 감춰 주고 있었다( ShowWindow(SW_HIDE) ). CFrameWnd::OnClose()에서 CWinApp::HideApplication을 호출함. 이걸 함으로써 운영체제의 TSF 시스템 내부는 객체에 대한 Release가 일어나고 메모리 해제가 완전히 이뤄졌다. 소스가 없는 대화상자도(DialogBox 함수) 잘은 모르지만 종료될 때 비슷한 call stack을 갖는 Release 호출이 있었다.

그 반면 창이 없어질 때 따로 별다른 처리를 하지 않는 프로그램에서는 외부 모듈 개체의 reference count가 1 남게 되었고, 이것이 memory leak으로 이어졌다. MS에서 직접 만든 다른 입력 프로그램들도 마찬가지다. 도대체 왜 그럴까?.

MFC가 WM_CLOSE에서 자기 창을 감추는 이유는 그냥 자식 윈도우들이 순서대로 닫히는 모습이 사용자에게 티가 나 보이지 않게 하고, 겉보기로 창이 당장 없어져 버렸으니 프로그램 종료에 대한 사용자 반응성을 향상시키려는 목적으로 보인다. 그게 반드시 필수는 아니다. 내가 보기에 그렇게 하지 않는 게 잘못이라 볼 수는 없다.

OS별로 살펴보니, 이런 leak은 윈도우 XP와 비스타에서는 없었다가 그 후대인 7과 8에서 생겼다. 즉, XP/Vista에서는 hide를 안 해 줘도 원래 leak이 없는데 7부터는 hide를 해 줘야 한다는 뜻. 아무튼 난 여러 모로 윈7의 문자 입력 체계가 별로 마음에 안 든다. 이쪽 부분 담당자가 갑자기 바뀌었는지, 혹은 대대적인 리팩터링을 한 후유증이기라도 한지 자잘한 버그들이 너무 많이 들어갔기 때문이다.

결국 이것은 IME 문제가 아니라 운영체제 내지 응용 프로그램의 문제라는 결론을 내리고 편집기의 소스를 고쳤다. 문제를 피해 가는 법을 발견하긴 했으나 뒷맛이 개운하지 못하다.

* Windows 환경에서의 4대 디버깅 도구와 테크닉

  • 문자열을 printf 스타일로 포맷하여 OutputDebugString 함수로 전달하는 TRACE 함수 (디버거 로그)
  • 별도의 디버거 로그가 아니라 그냥 화면 desktop DC에다가 로그를 찍는 깜짝 함수
  • 프로그램이 특이한 환경에서 뻗을 때 call stack을 확인할 수 있는 miniDumpWriteDump와 SetUnhandledExceptionFilter 함수
  • memory allocation number에다가 breakpoint를 거는 _crtBreakAlloc 변수. 정체불명의 memory leak 잡을 때 필수

Posted by 사무엘

2013/03/02 19:24 2013/03/02 19:24
, , ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/802

새마을호 디젤 동차의 퇴역

1987년 7월 6일.
민주화를 갈망하며 전국적으로 벌어진 6월 항쟁이 마무리되어 가던 이 날, 한국 철도계에는 일대 혁신이 시작되었다.

“철도청은 오늘부터 서울 부산간의 전후동력형 새마을호 열차 운영을 시작했습니다.
이 열차는 승차감이 좋으며 안전감이 높고 예비동력이 확보돼 고장과 사고를 줄일 수 있습니다.” (MBC 뉴스 보도)


바로, 전통적인 기관차+객차 형태를 탈피하여 전후동력형 새마을호 디젤 동차가 첫 운행을 개시했기 때문이다.
그리고 이 열차를 최초로 제작하여 납품한 업체는 그 이름도 유명한 대우 중공업이었다. 대우 중공업은 이 사실에 대해 무척 자부심을 갖고 있는 듯하다.

사용자 삽입 이미지

승차감이 더욱 좋아졌습니다.
대우 중공업이 자체 개발한 최신 유선형의 전후 동력 새마을 열차가 뛰어난 승차감으로 더욱 쾌적하고 안락한 철도 여행을 약속합니다.


사용자 삽입 이미지
그런데 7월 1일이라니? 지금도 이렇게 전시되어 있는지는 모르겠으나, 철도 박물관에 적혀 있는 저 날짜는 잘못되었다. 6일로 바로잡혀야 한다.

이때 새마을호는 지금의 KTX를 능가하는 초호화 귀족 열차였다.
열차의 동력차부터가 아주 특이하고 심상찮게 생겼는데 서울에서 부산까지 가면서 도중에 대전과 동대구밖에 정차를 안 한다니!
나도 이 새마을호 디젤 동차를 안 봤으면 철덕이 됐을 가능성이 아주 낮아졌을 것이다.

그 당시 전후동력형 디젤 동차가 내세웠던 장점은 기관차보다 엔진이 작고 가볍고 조용하면서도 성능이 탁월했다는 점이다. 디젤-전기 형태가 아니라 유압 변속기로 동력비를 조절했다. 엔진이 앞뒤로 두 개 달린 대칭형이기 때문에 전차대 없이도 회차가 용이하며, 두 엔진 중 하나가 퍼지더라도 속도가 느려질지언정 그럭저럭 운행을 할 수 있어서 더욱 좋다. '예비 동력'이라는 게 그런 의미이다.

하늘을 날아가는 비행기나 로켓이야 닥치고 무조건 가벼운 게 진리이겠지만, 구름 마찰력에 의존하는 육상 교통수단은 너무 가볍기만 해서는 바퀴가 헛돌기 쉽다. 이 때문에 동차형 새마을호는 빨리 잘 달릴 수 있는 평지에서는 성능이 탁월한 반면, 지형이 험준한 곳에서는 투입이 곤란했다는 특성도 존재한다.

이 계열의 차량은 1994년까지 대우, 현대, 한진 중공업 생산분이 총 세 차례에 걸쳐 도입되었다.
동력차의 말단 부분에 저렇게 날렵한 45도 경사면을 처음으로 만드는 것부터가 1980년대 후반에는 어려운 일이었다고 한다. 비록 그때 고속철이 다닌 건 아니었지만 새마을호의 디자인에서부터 이미 프랑스 떼제베의 외형을 벌써 참고한 거라는 썰이 나돈다.

초기 도입분은 6량 1편성이었으나, 나중에는 8량으로 늘고 엔진의 출력도 향상되었다. 차량 말고 엔진은 독일제이며, 우리나라 해군이 운용하는 잠수함에서도 동일한 엔진이 쓰이는 기체가 있다고 한다.

이 열차는 분명 매우 획기적인 열차였으나, 이 열차가 새마을호의 최초 원조는 아니다. 가령,

  • 서울-부산 4시간 10분은 이 열차보다 먼저 특대형 디젤 기관차가 견인하는 새마을호가 1985년 11월에 달성했다. 디젤 동차가 최초가 아니다.
  • 새마을호 특유의 동그란 창문 모양의 객차는 1986년 제작된 '유선형 새마을호 객차'의 디자인을 물려받은 것이다. 디젤 동차가 최초가 아니다.
  • 새마을호도 처음부터 지금과 같은 완전 으리으리한 좌석이었던 건 아니다. 1991년에 우등 고속이 생긴 뒤인 1992~1994년의 후기형 객차에서야 드디어 종아리 받침대까지 갖춰진 좌석이 도입되었다.

기존의 기관차형 새마을호 객차와, 새로 개발된 동차형 새마을호의 객차는 객실 인테리어는 거의 차이가 없지만, 전기 배선 규격이 호환되지 않아서 서로 혼합 편성을 할 수는 없다고 한다.

승승장구하던 새마을호는 1990년대 중반부터 정차역이 급격히 늘고 위상이 하락하기 시작했으며, KTX가 개통한 뒤부터는 진짜 처참하게 몰락하고 말았다. KTX를 보조하는 한 수 아래 열차로 굴리기엔 내장재가 너무 좋아서 뭘 어찌 할 수가 없다. 게다가 오래 된 엔진은 종종 퍼지고 고장을 일으켰다. KTX에 올인을 해야 하는 철도 경영자의 입장에서는 진짜 계륵 그 이상도 그 이하도 아닌 열차로 전락했다.

1987년 초기 도입분은 20년이 지난 2007년부터 슬슬 퇴역하기 시작했다. 그 신호탄이 바로 2006년부터 잠시 다녔던 임진강 라이너 새마을호. 그리고 지난 2013년 1월 5일의 마지막 운행을 끝으로, 전후동력형 새마을호 동차가 1월 6일부로 모두 퇴역하여 자취를 감췄다. 아아..! 내 너를 어찌 잊으리!

새마을호라는 열차 등급은 2014년 말까지 유지되다 폐지될 예정이다. 고로 지금 다니는 새마을호는 모두 기관차+객차형 새마을호이다. 비둘기호, 통일호에 이어 새마을호까지 없어지면 1980년대부터 시행되었던 '-호' 열차 등급 체계는 사실상 다 무너지는 셈이다. 사실, '-호'라는 체계는 배에서 모티브를 딴 옛날 스타일 작명법에 가깝다.

* 아, 그러고 보니 여담 하나. 1987년 7월 6일은 연세대에 재학 중이던 고 이 한열 열사가 최루탄을 맞은 뒤 치료를 받다가 끝내 사망한 바로 다음날이기도 하다. (7월 5일)

Posted by 사무엘

2013/02/28 08:40 2013/02/28 08:40
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/801

« Previous : 1 : ... 147 : 148 : 149 : 150 : 151 : 152 : 153 : 154 : 155 : ... 221 : Next »

블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2024/12   »
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:
3049628
Today:
648
Yesterday:
2142