« Previous : 1 : 2 : 3 : 4 : 5 : Next »

오늘날, 어떤 데이터(개념상 Document라고 불리는)를 메모리에 완전히 읽어들여서 사용자가 그 데이터를 편집할 수 있는 업무용 프로그램에는... 거의 필수로 undo 기능이 있다.
이건 우리가 잘 실감을 못 해서 그렇지 사용자에게 심리적으로 굉장한 안정감을 주는 편리한 기능이다. 뭘 잘못해서 망쳐 놓더라도, '미리 저장 -> 지금 문서를 저장 안 하고 예전 문서를 다시 불러오기' 같은 뻘짓을 안 해도 Ctrl+Z만 누르면 만사 OK.

소프트웨어 GUI 가이드라인 교과서를 보면, 소프트웨어는 사용자에게 '용서'(forgiveness)라는 덕목을 발휘해야 한다는 말이 나온다. 사용자가 아무리 바보짓을 하더라도 이를 최대한 추스리고 수습하고 원상복귀 할 수 있어야 한다는 뜻.
컴퓨터 프로그램은 기독교의 하나님 같은 존재가 아니다. “자유 의지가 있으니, 하늘나라든 지옥이든 선택에 따른 책임도 전적으로 네 몫이다” 주의가 아니다. 그러니 인간의 삶에도 Undo가 있으면 참 좋을 텐데 현실은 그렇지 못하니 참으로 안습이다. 오히려 '말은 하고 못 줍는다', '엎질러진 물', '소 잃고 외양간 고친다' 같은 냉정한 속담만이 있을 뿐이다.

잡설이 길어졌다만,
역사적으로 Undo라는 개념이 허접하게나마 가장 일찍부터 존재한 분야는 그래픽 프로그램이었다.
걔네들은 원래부터 문화가 좀 독특해서 마우스의 비중이 매우 높으며, 우클릭이 Context menu의 의미로 쓰이지도 않을 정도이다.
그리고 근본적으로 마우스라는 게 키보드보다 실수를 훨씬 더 하기 쉬운 입력 장비이고, 한 번의 동작으로 수백· 수천 개의 픽셀이 한꺼번이 바뀔 수 있기 때문에 Undo가 없으면 안 된다.

문득 든 생각: 그래픽 프로그램을 이용해서 마우스로 Freehand drawing을 하는 도중에 bksp 키를 누르면.. 직전의 수 픽셀 단위로 곡선을 철회하는 기능이 있으면 좋을 것 같다. 정교한 도트 노가다 할 때 유용할 것 같다. 보통 Ctrl+Z 누르면 그렸던 선 전체가 한 방에 날아가 버리잖아?

물론 Undo라는 건 그렇게 쉽게 구현할 수 있는 기능이 아니며 메모리 오버헤드가 크다.
더구나, 과거에 Undo 기능이 있던 프로그램은 딱 한 단계밖에 취소가 되지 않았었다. Ctrl+Z를 누르면 직전 작업을 취소했다가, 다시 살렸다가 하기만을 반복할 뿐이었다. (메모장.. 정확히 말하면 윈도우 운영체제의 에디트 컨트롤도 딱 그 수준의 1단계 Undo를 지원한다.)
수십, 수백 단계의 작업을 고스란히 취소하고 취소 내역을 다시 철회(redo)까지 하는 command history 수준의 multi-level undo 기능은 1990년대까지만 해도 MS 워드 같은 소수의 상업용 대형 프로그램에서나 볼 수 있었다.

이런 프로그램은 Document의 내용을 변형하는 모든 명령들이 체계적으로 분류가 돼 있다. 그래서 편집 메뉴를 열어 보면 단순히 '실행 취소 / 재실행'이라고만 돼 있는 게 아니라 '삭제 취소 / 취소된 자동 완성 재실행'처럼, 무슨 명령이 직전에 취소되었고 무엇을 재실행할지 명령의 이름까지 메뉴에 친절하게 표시돼 있기도 한다.
그 반면, Undo/redo를 염두에 두지 않고 Document를 고치는 코드가 제멋대로 섞여 있던 프로그램에다가 어느덧 갑자기 multi-level Undo/redo 기능을 최초로 추가할 일이 생겼다면 아마 십중팔구 코드를 다 갈아엎는 대공사를 해야 할 것이다.

컴퓨터의 성능이 열악하던 도스 시절엔, Undo와 Redo가 모두 존재하는 프로그램은 매우 드물었다.
아래아한글 1.x는 좀 특이한 경우인데, 줄 끝까지 지우기· 단어 지우기 같은 몇몇 지우기 명령으로 인해 지워진 텍스트만을 1회에 한해 되살리는 Undo 기능이 있었다.
그 후 아래아한글 2.0에서 97은 그 Undo의 단계가 3회로 늘었을 뿐이었다. 내 기억이 맞다면, 이 기능은 최근 3회의 주요 지우기 단축키(메뉴에 등재되지 않은)에 의해 지워졌던 텍스트 중 하나를 골라서 커서가 있는 곳에다 삽입해 주는 기능에 불과했다. 원래 있던 위치에 되돌려 놓는 것도 아니고... -_-

Ctrl+X(오려두기)야 본문이 클립보드에 고스란히 들어가 있으니 별도의 Undo 버퍼에다 저장할 필요가 없고,
Ctrl+E(지우기)로 지워진 텍스트는 의도적으로 되살리기가 전혀 되지 않는다고 도움말에 친절하게 안내까지 돼 있었다. ^^;;
그것 말고 문서나 표 레이아웃을 잘못 건드려서 망쳤다거나 하는 더 중요한 기능에 Undo 따위는.. “Undo 뭥미? 그거 먹는겅미? 우걱우걱...”이었다. 그냥 이전 문서를 새로 불러오는 수밖에..
이런 불편한 프로그램을 옛날 사람들은 어떻게 쓰고 지냈을까?

그래서 아래아한글 2002는 256단계 undo가 지원된다고 잔뜩 광고를 하고 다녔었다. MS 제품들은 진작부터 지원한 기능인데도 말이다. 하긴, 그것 말고도 글자 크기 제한이 드디어 없어지고 글자색 제한이 없어진 것도 개인적으로 무척 마음에 들긴 했다. better late than never이다.

<날개셋> 한글 입력기의 편집기 프로그램은 1.x와 2.x 시절에는 Undo 비슷한 기능도 전혀 없었다. 3.0에 가서야 32단계의 multi-level undo가 추가되기는 했으나... 글자 하나, 한글 낱자 하나 입력되는 모든 단계가 histroy에 기록된지라 실용성이 시원찮았다.
그것이 지금의 형태로 개선되 것이 4.2 버전부터이다. 연속된 에디팅 동작뿐만이 아니라 불연속적인 에디팅 동작을 한 history로 통합하는 기능까지 추가되어, 여러 블록을 동시에 삭제한 것이나 Replace All 명령을 내린 것도 한 번에 취소가 가능해졌다.

사실 <날개셋> 편집기의 에디팅 엔진은 아직 좀 효율적이지 못한 구석이 있다. Undo/redo 명령을 내리면 그 부분이 아무리 사소하더라도 문서 전체의 레이아웃을 싹 다시 하고, 화면 전체를 새로 그린다. 그렇기 때문에 수~수십 MB짜리 텍스트를 연 뒤에 Ctrl+Z를 꾹 누르고 있기가 겁난다. 본인은 이 프로그램을 만든 사람이고 프로그램의 내부 디테일이 어떤지를 잘 알기 때문에 그러기가 더욱 꺼려진다.

2004년에 만든 3.0 이래로 그냥 brute-force 알고리즘을 그 부분만은 아직까지 최적화를 못 했다. 한글 입력 부분과 직접적인 관계가 없고, 딱히 크게 티가 나는 부분이 아니다 보니 지금까지 방치되어 온 것이다. <날개셋> 한글 입력기 6.0의 다음 버전은 이 부분을 개선하여, 이제 안심하고 Ctrl+Z를 꾹 누를 수 있는 프로그램이 될 것이다.

Undo 기능과 관련된 얘깃거리를 두 가지만 더 꺼내고 글을 맺겠다.

첫째, 예전에 한번 언급한 적이 있듯이, 프로그램들이 Undo는 거의 예외 없이 Ctrl+Z로 정착해 있는 반면 Redo는 단축키가 프로그램마다 일치하지 않는 경우가 있다. MS의 관행은 Ctrl+Y인 듯하지만 Ctrl+Shift+Z인 프로그램도 있다.
아래아한글은 도스 시절에 Ctrl+Y가 지우기 명령의 일종이기 때문에 주의해야 한다. Redo를 생각하고 눌렀다가는 Redo는커녕 텍스트를 지우면서 후폭풍으로 기존 Undo history까지 모두 날려 버리기 때문이다!

둘째, Multi-level undo를 잘 구현한 프로그램이라면, 문서의 modified 플래그 처리도 잘 되어야 한다. 무슨 말이냐 하면, 문서를 저장했다가 어딘가를 건드린 후(= modified 플래그 켜짐), Ctrl+Z를 눌러 그 작업을 철회한다면 문서는 당연히 다시 unmodified 상태로 바뀌어야 한다.
그리고 반대로, 저장한 문서에 대해서 Undo를 해서 modified 상태가 됐더라도, 그걸 다시 Redo로 철회했다면 문서는 unmodified로 되돌아가야 한다. 논리적으로 당연한 얘기이다. <날개셋> 편집기는 multi-level undo가 처음으로 지원되기 시작한 3.0 때 이거 하나는 아주 철저하게 잘 구현해 뒀다.

비주얼 C++ 에디터, 그리고 국산 에디터인 EditPlus는 이 플래그 처리가 잘 된다. MS 오피스 제품도 마찬가지.
그러나 AcroEdit는 이게 되지 않아서 불편하며, 아래아한글도 2007은 처리가 되지 않는다. WordPad 역시 지원 안 함.

Undo나 Redo 같은 Command history 기능은 문서의 modified 상태까지 예전으로 되돌리는 명령이기 때문에 문서를 건드리는(modified 플래그를 언제나 켜는) 동작보다 상위에서 돌아가야 할 텐데, 이 점을 미처 고려를 못 한 것 같다. Undo나 Redo나 문서를 고치는 기능인 건 매한가지이기 때문에 무조건 modified 상태로. ^^

Posted by 사무엘

2011/06/26 08:23 2011/06/26 08:23
, , ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/531

칠레처럼 아주 길쭉한 국가가 있다고 치자. 이 국가에는 지형을 따라 거대한 간선 철도가 놓여 있고 n개의 역이 있으며, n개의 역에 모두 정차하는 완행 열차가 일정 간격으로 다닌다.

이 설정을 좀 극단적으로 확장하여 역 수가 수백, 수천, 수만-_-개에 달한다고 가정하자. 그렇다면 급행 열차를 운행할 필요가 응당 생긴다. 2000개역쯤 떨어진 지역에 가려고 하는데 전역정차 열차를 탈 수는 없는 노릇 아닌가. 그렇게 여행 거리가 길어지면, 급행 열차가 서는 곳까지 가서 환승하는 불편 정도는 급행의 빠른 속도가 충분히 보상하고도 남게 된다.

자, 이를 일반화하면.. 급행도 등급이 필요해서 특급, 쾌특 등 n차원의 급행을 생각할 수 있게 된다. 등급이 올라갈수록, 서는 역 수가 무척 적어서 타기 힘든 대신에 일단 타기만 하면 엄청난 이동성이 보장된다. 급행과 완행은 배차 간격은 모두 동일하다고 치자.

여기서 문제가 생긴다.
각 등급의 급행 열차들은 정차역 수를 얼마로 설정하는 게 좋을까?
또한, 철도역 수 n에 대해서, 최대 몇 등급의 급행이 존재하는 게 적당할까?

n개의 역이 모두 똑같이 중요하고 이용객 수가 균일하다고 가정할 때,
어떻게 급행을 운영하는 게 승객의 평균 표정속도를 최대화하고 반대로 평균 환승 대기 시간을 최소화할 수 있을까?
선로 수는 충분하기 때문에, 완급 결합으로 인한 대피 대기 오버헤드라든가 선로 용량 걱정은 할 필요 없다고 가정하겠다. ^^

역 수가 10개 남짓이라면 급행이 있을 필요가 없겠지만, 역 수가 100개쯤 된다면 3~4개역을 건너뛰는 1차 급행에 이어서 한 10~12개쯤 역을 쉬엄쉬엄 건너뛰는 2차 급행이 있어도 좋을 것 같다.

전산학을 전공한 친구라면, 이런 부류의 문제를 생각하면서 비슷한 형태의 아주 유명한 알고리즘을 하나 떠올리게 될 것이다.
바로 '쉘 정렬'이다!

쉘 정렬은 삽입 정렬을 원소별로 띄엄띄엄 적용하되 나중에 그 간격을 촘촘히 좁히는 방식이다.
삽입 정렬은 시간 복잡도가 O(n^2)이지만, n의 크기가 작아서 띄엄띄엄일 때는 오버헤드가 크지 않으며, 또 편차가 커서 리스트가 상당수 정렬되어 있을 때는 매우 빠르게 수행되기 때문에 그 특성을 이용한 것이다.
쉘 정렬은 알고리즘의 특성상 실제로 코딩해 보면 루프가 3중, 4중으로 들어가기 때문에 무거울 것 같지만 돌려 보면 성능이 매우 좋다. 프로그래밍 언어라고는 아직 어셈블리밖에 없던 1950년대에 고안된 알고리즘이다.

여타 정렬 알고리즘들이 O(n^2), O(n log n) 아니면 심지어 O(n) 같은 식으로 시간 복잡도가 딱 파악되는 반면, 이 쉘 정렬은 비록 O(n^2)보다야 훨씬 빠르긴 하지만 시간 복잡도가 제대로 분석되어 있지 않다.
삽입 간격을 설정해서 좁히는 방식을 어떻게 설정하냐에 따라서 성능이 크게 달라지기 때문이다.

완행 다음으로 급행을 겨우 1역 균일 통과, 특급을 2역 균일 통과처럼 정말 무식하기 짝이 없게 운행하지는 않는다. 급행 등급이 하나 올라갈 때마다 급행은 최소한 기하급수적으로 통과역 수가 늘어야 이치에 맞다.
쉘 정렬도 그와 마찬가지이다. 23, 10, 4, 1 같은 급으로 큼직하게 수가 바뀌고, 이 수들이 가능한 한 서로소가 되게 하는 게 정렬 효율에 좋다고 알려져 있다. 16, 8, 4, 2, 1처럼 정확하게 컴퓨터스럽게 배수· 약수 관계로 포개지는 간격은 매우 비효율적이며, 그런 나쁜 수열을 쓰면 쉘 정렬의 시간 복잡도가 최악의 경우 도로 O(n^2)로 치솟는다고 한다.

우리나라의 급행 전철이 정차역 수가 여전히 너무 많다는 지적이 있긴 하지만, 이것은 환승을 싫어하는 국민 정서 내지 환승이 불편한 구조, 급행도 어차피 최대 속도는 동일하고 완행보다 그렇게 많이 빠르지 않은 것, 역마다 weight가 현실적으로 차이가 나는 것, 급행의 소극적인 운행(긴 배차 간격) 같은 다른 환경적인 요인 때문에 그런 것이다. (현실에서는 환승역이냐 그렇지 않느냐의 여부 하나만으로도 역별 weight가 크게 벌어질 수밖에 없다)

이상, 철도와 전산학을 융합한 뻘글이었다.
쉘 정렬의 수열 설정 방식이 철도 운영에서도 이론상 효율적이라 말할 수 있을까? ^^;; (급행은 4역씩 건너뛰고 특급은 10개역, 쾌특은 23개역.. ㄲㄲ)
참고로 쉘 정렬은 수열을 제일 잘 설정했을 때 시간 복잡도가 O(n (log n)^2) 까지는 떨어진다고 한다.


* 덧붙이는 말:
어제는 KTX 열차가 개통 사상 처음으로 탈선 사고를 일으켰다고 한다.
여기에 대해 철도 덕후 사무엘 님의 공식 입장을 말하자면, 이건 두말 할 나위도 없이 선로 시설 문제이지 차량 문제가 아니라는 것이다.
차량이 떼제베가 아닌 산천이었다고 하는데, 지금까지 늘 말썽을 일으켜 온 것처럼 차량이 고장을 일으킨 거라면, 그대로 차가 멈춰서는 걸로 끝나지 지가 무슨 능력으로 탈선까지 하겠는가?

더구나 이 차는 보기 드문 광명 시종착 KTX였다. 광명이 단순 경유역이 아닌 종착역이기 때문에 여타 열차와는 다른 선로로 건너가야만 했다. 그래서 선로 분기기가 열차를 새 선로로 유도하고 있었는데, 열차가 다 건너기 전에 선로 분기기가 전산 착오 내지 추위로 인해 오작동한 것 같다. 그래서 뒷부분 객차의 진로를 막았고, 이것 때문에 찌이이이익 소음+타는 냄새+탈선이 야기된 것으로 추정된다.

열차가 고속으로 쌩쌩 달리다가 교량이 붕괴했다거나 차량이 자폭이라도 한 것과는 전혀 다른 부류의 사고이다. 오히려 열차는 종착역 진입을 앞두고 위와 같은 이유로 인해서 아주 천천히 달리면서 신호를 기다리고 있는 상태였을 것이다. 그리고 그 사고도 그런 특수한 상황에서나 발생한 사고이다. 이 사고가 KTX 차량 시스템 전반에 대한 불신풍조로 이어지기는 않기를 본인은 바란다.

이 사고로 인해 이 열차를 바로 뒤따라오던 상행 KTX는 평택쯤에서 다시 천안아산-_- 역으로 역주행하여 돌아가야만 했고, 대전-서울 구간의 고속선이 폐쇄되는 바람에 다른 KTX들은 아예 경부선 기존선으로 우회해서 다녀야 했다. 주말 임시 열차는 아예 선로 용량 부족으로 인해 운행 중단. 코레일의 입장에서는 손해가 그야말로 막심했을 것이다. 이로 인해 수원-천안 구간에서 KTX 산천이 보이는 진풍경이 연출되기도 했다.

Posted by 사무엘

2011/02/12 17:49 2011/02/12 17:49
, , , , ,
Response
No Trackback , 5 Comments
RSS :
http://moogi.new21.org/tc/rss/response/464

컴퓨터는 배열로 표현된 직사각형 형태의 데이터를 처리하는 걸 좋아하며, 이는 그래픽에서도 예외가 아니다.
그러나 사람이 생각하는 개념을 그래픽 개체의 형태로 표현하다 보면 직사각형이 아닌 임의의 모양의 그래픽을 찍어야 할 일이 생긴다.
게임에서는 스프라이트가 좋은 예이고, 굳이 게임이 아니더라도 GUI 환경에서는 아이콘이라든가 심지어 customized 마우스 포인터도 그런 부류에 속하는 그래픽이다.

이런 그래픽은 결국 큰 직사각형 안에서 투명색을 제외한 나머지 색상을 찍는 방법으로 처리하는데, 그 구체적인 테크닉은 역사적으로 아래와 같은 세 양상을 거치며 바뀌어 왔다.

1. 모노크롬이나 그에 준하는 저색상: 비트 연산

그림을 두 장 준비한다. 그리고 그 두 장을 화면에다 그냥 copy만 하는 게 아니라, 화면에 이미 있는 픽셀과 비트 연산을 하여 그 결과를 찍는다. 이것을 raster operation이라고 하는데, 비트 연산은 CPU-friendly한 작업이기 때문에 컴퓨터가 나름 빠르게 수행할 수 있다.

준비해야 하는 그림은,
찍어야 할 내용이 그려져 있고 배경은 '검은색'(0)으로 처리되어 있는 '원래 비트맵'과,
원래 비트맵하고는 정반대로 배경은 무조건 '흰색'(1)이고 내가 차지하는 스프라이트 영역은 '검은색'(0)으로 처리되어 있는 '마스크 비트맵' 이렇게 둘이다. 마스크 비트맵은 1 아니면 0만 있는 모노크롬이다.
(따라서 '원래 비트맵'만으로는 검은색이 배경인지 아니면 스프라이트가 실제로 차지하는 검은색인지 알 수 없다.)

화면에다가는 먼저 마스크 비트맵을 AND 연산으로 그린다. 원래 화면에 있던 픽셀이 X라면, 마스크에서 배경으로 처리된 픽셀은 X AND 1이므로 X가 그대로 남고, 0이면 0이 되어 검은색이 된다.
즉, 마스크 비트맵에 대한 AND 연산은, 스프라이트가 칠해져야 할 영역만 시꺼멓게 만드는 효과를 낸다.

그리고 다음으로 이 자리에다가 원래 비트맵을 XOR 연산으로 그린다.
0 XOR X = X이므로, 이 연산을 수행해 주면 화면이 0으로(특히 마스크 비트맵 AND 연산으로 인해 0이 된) 시꺼먼 곳은 원래 비트맵이 그대로 그려지고, 원래 비트맵이 0인 배경은 아무 변화가 생기지 않는다.

사용자 삽입 이미지

그림의 출처는 위키백과.
이로써 스프라이트가 멋있게 그려졌다.
도스용 게임 중에 <위험한 데이브>는 이런 초보적인 XOR 방식으로 스프라이트를 찍었기 때문에, 검은 배경이 아니라 두 스프라이트가 겹치면 화면에 잔상이 남곤 했다.

옛날 윈도우 9x 시절에.. 컴퓨터 메모리가 많이 부족해서 하드디스크 스와핑/thrashing이 일어나고 프로그램의 각종 아이콘들이 그려지는 게 눈에 보일 때는... 아이콘이 차지하는 영역이 먼저 시꺼매지거나 반대로 잠깐 하얗게 번쩍이는 걸 볼 수 있었다. 흠, 프로토스 건물도 소환이 끝났을 때 실루엣이 허옇게 번쩍이다가 원래 형태가 드러나는데...;; raster 연산을 더블버퍼링 없이 화면에다 바로 그리다 보니, 컴퓨터 속도가 느려졌을 때 그 중간 과정이 눈에 띄는 것이다.

검정에다가 원래 비트맵의 색을 합성할 때는 이론적으로 OR을 써도 되는데 XOR이 의도적으로 쓰이고 있다.
이는 XOR이 유용하기 때문이다. XOR 1은 비트를 반전시켜 준다는 특성상, XOR 연산으로 그린 그림은 거기에다 XOR을 한번 더 해 주면, 다른 곳에 영향을 주지 않고 자기가 차지하고 있던 영역에서만 완전히 지워진다.

XOR 연산은 컴퓨터의 입장에서는 매우 부담이 가볍기 때문에, 마우스 선택 영역을 나타내는 점선 사각형이라든가 창 크기를 조절하는 작대기처럼 수시로 업데이트를 해 줘야 하는 비주얼 효과를 나타낼 때 즐겨 쓰인다.
아니, 텍스트 블록이라든가 깜빡이는 커서(캐럿)조차도 반전 사각형이니까 XOR이다.

마우스 포인터도 XOR 연산이다. 텍스트 입력란을 뜻하는 I자(beam) 모양의 마우스 포인터는 검은색이 아니라 배경색에 대한 반전색이다. 마스크 비트맵 값을 0이 아닌 1로 둬서 배경을 지우지 않은 상태에서 XOR 비트맵도 1로 해 주면 배경색이 반전되는 효과가 난다. ^^;;

XOR 연산은 디지털 컴퓨터가 존재하는 한 그래픽에서 언제까지나 없어지지 않고 쓰일 방식이긴 하지만... 오늘날은 다소 촌스러운(?) 것으로 간주되고 있기도 한다. GPU님이 계시니 화면 비주얼을 굳이 CPU 친화적인 방법만 고집할 필요는 없는 듯. 그래서 요즘은 뭔가 선택 영역을 나타낼 때 알파 블렌딩을 동원하여 다 옅은 파란 배경 + 더블버퍼링으로 대체되는 추세이다. 화면 전체의 DC를 얻어와서 XOR 연산을 시키는 건 Aero 환경에서는 오히려 성능을 더욱 떨어뜨리는 짓이기도 하니 말이다.

2. 모노크롬 이상 16~256색 사이: 컬러 키(color key)

그 후 컴퓨터의 그래픽 카드의 성능이 향상되면서, 256색 시대가 열렸다. 256색은 팔레트 조작이라는 과도기적인 괴악한 개념을 도입한 걸로도 유명하다.
색깔이 적당히 많아졌기 때문에, 비트맵에서 256색 중 하나만 투명색으로 예약하여 쓰지 않고 나머지 색은 그대로 찍게 하는 방식이 유리하다. 마스크 비트맵 따위를 번거롭게 구비할 필요가 없다. 또한 256색은 RGB 값이 아니라 인덱스 기반 컬러를 쓰기 때문에, xor 반전 연산이 어차피 그렇게 큰 의미를 지니지도 않는다. (실제 색깔값이 반전되는 게 아니라 팔레트 인덱스 번호가 반전되기 때문)

256색 전용으로 유명한 gif 그래픽 파일이 이런 컬러 키를 지정하여 투명색을 지정할 수 있다.
윈도우 API에도 비트맵이나 아이콘의 (0, 0) 위치 픽셀을 투명색으로 간주하고 그려 주는 함수가 있으며, SetLayeredWindowAttributes 함수는 컬러 키를 지정하여 해당색을 투명하게 처리함으로써 non-rectangular 윈도우를 만드는 효과를 내어 준다. region을 만들지 않고도 동일한 일을 할 수 있다는 뜻이다.

3. 트루컬러: 알파 채널

투명색 처리의 최종 완전체는 바로 알파 채널이다. 이건 과거의 픽셀 raster operation과는 차원이 다르며, 컴퓨터가 빨라진 정도를 넘어 그래픽 가속을 위한 별도의 GPU까지 등장하면서 가능해진 궁극의 기술이다.
매 픽셀에다가 이분법적인 투명 여부가 아니라, 이 픽셀이 배경과 얼마나 짙게 오버랩될지 반투명 등급 자체가 추가로 들어간다. RGB에 이어 A까지, 가히 색깔의 4차원화인데, 기계 입장에서는 한 픽셀당 딱 정확히 32비트이니 처리하기에는 다행히 좋다.

256색을 초월한 천연색 그래픽에는 워낙 많은 개수의 색상이 쓰이기 때문에.. 그 중 딱 한 색깔에다가만 컬러 키를 부여하는 게 무의미하다. 그리고 마치 글꼴에도 안티앨리어싱을 하듯, 스프라이트도 경계가 배경색과 부드럽게 융합해야 트루컬러의 진정한 의미가 살아난다. 그래서 알파 채널이 필요한 것이다.

윈도우 98에서 알파 채널을 적용한 비트맵 찍기라든가 그러데이션을 한번에 처리하는 API가 처음으로 추가됐다. 프로그램의 제목 표시줄에 그러데이션 효과가 윈도우 98에서 처음 추가되었는데, 바로 이 API를 쓴 것이다.
그리고 윈도우 XP에서는 알파 채널이 적용된 확장 아이콘이 처음으로 도입되었고, GDI+는 그리기 기능에 전반적으로 알파 채널을 염두에 두고 설계되었다. 하지만 GDI의 기본적인 벡터 드로잉 함수는 그런 새로운 기술로부터 소외되어 있으니 안타까울 뿐.

윈도우 비스타는 48*48도 모자라서 아예 256*256 크기의 아이콘을 지원한다. XP 때부터 이제 아이콘 하나가 2~3만 바이트에 달하는 시대가 됐는데(윈도우 3.1 시절에는 1~2천 바이트.. -_-), 전통적인 ico는 bmp와 같은 '무압축 포맷'인지라 256*256 크기의 32비트 픽셀을 저장했다간 크기를 감당할 수가 없기 때문에, ico 포맷은 내부적으로 png 파일도 포함할 수 있게 구조가 확장되었다.
gif를 대체하는 새로운 이미지 포맷인 png는 알파 채널을 지원한다. 그 자그마한 아이콘 하나도 전문 그래픽 디자이너가 포토샵으로 만들어야 하는 시대가 도래한 지 오래이다.

윈도우 내부적으로는 아이콘과 마우스 포인터 파일은 거의 동일한 포맷으로 간주된다. 아이콘은 이미지 이미지 비트맵과 마스크 비트맵 이렇게 둘 들어있는 형태이며, 마우스 커서는 거기에다 센터 위치가 추가되고.. 애니메이션 포인터는 gif스럽게 프레임이 더 추가되겠구나.
알파 채널이 등장하면서 마스크 비트맵은 존재 가치가 상당수 퇴색하긴 했으나, 오늘날에도 고전 테마(XP의 Luna, 비스타의 Aero 따위가 없는)에서 아이콘을 찍을 때라든가 disabled 상태 같은 변형 상태를 찍을 때 참고 정보로 쓰이기 때문에, 완전히 필요가 없어진 것은 아니다.

요컨대 오늘날은 기술 발전의 정도에 따라 최소한 세 가지 형태의 투명색 표현 기법이 쓰이고 있는 셈이다. 흥미로운 사실이다.

Posted by 사무엘

2011/01/24 07:35 2011/01/24 07:35
, , ,
Response
No Trackback , 7 Comments
RSS :
http://moogi.new21.org/tc/rss/response/454

오랜만에 알고리즘 얘기.
정보 올림피아드 공부를 한 적이 있는 분이라면, 제목에 등장한 용어가 아주 친숙할 것이다. 앞으로 LIS라고 줄여 일컫겠다.

어떤 수열이 왼쪽에서 오른쪽으로 나열돼 있으면, 그 배열 순서를 유지하면서 크기가 점진적으로 커지는 가장 긴 부분수열을 추출하는 것이 목표이다.
가령, {3, 2, 1, 4, 5, 2, 3, 5, 3, 6, 4} 같은 수열이 있으면
1, 2, 3, 5, 6이 가장 긴 solution이 된다. {3, 2, 1, 4, 5, 2, 3, 5, 3, 64} OK?
정렬만큼이나 알고리즘 기초를 다지는 데 도움이 되는 흥미로운 문제이다.

이 문제는 간단하게 생각하면 다이나믹 프로그래밍(동적 계획법)을 적용한 O(n^2)의 시간 복잡도로 풀 수 있다. 작은 set에 대한 답을 구한 뒤 그 결과를 저장해 놓고, 그 set의 크기를 차츰 키우면서 작은 solution들을 종합하여 최종 solution을 구하는 방식.

매 원소에 대해서 자기까지 왔을 때 존재 가능한 subsequence의 최대 길이와, 그 subsequence 상에서 자기 앞 원소의 위치를 적어 놓는다. 그러면 다음 원소 차례가 됐을 때는 자기 앞 원소들을 일일이 탐색하여, 자기보다 값이 작으면서 잠재적 subsequence 길이가 최장으로 설정되어 있는 원소에다 자기를 연결해 놓는다. 물론 자기의 subsequence 길이는 1 증가시켜 놓고 말이다.

오프셋 0 1 2 3 4 5 6 7 8 9 10
n 3 2 1 4 5 2 3 5 3 6 4
LIS길이 1 1 1 2 3 2 3 4 3 5 4
이전오프셋 -1 -1 -1 0 3 2 5 6 5 7 6

위와 같은 표가 완성되고 나면, 그 후 개수가 5로 가장 큰 9번 오프셋부터 시작하여 이전 참고 위치를 따라 역추적을 하면 LIS가 구해진다.

그런데 이걸 구하기 위해서 꼭 O(n^2)이나 되는 계산량이 필요할까? 더 효율적인 알고리즘은 없을까?
답은 ‘있다’이다. 물론 메모리 복잡도도 아까처럼 O(n)으로 완전히 동일하고 말이다.
이 새로운 알고리즘은 역시 길이가 n인 버퍼에다가 작업을 하는데, 버퍼의 용도가 아까와는 살짝 다르다.

이 버퍼 A[i](1<=i<=n)의 의미는, 길이가 i인 LIS를 구한다고 쳤을 때 존재 가능한 가장 작은 LIS 마지막 원소(와 그 원소의 위치)이다. 즉, 이 버퍼는 구해진 LIS의 길이만큼만 사용된다.

위의 예제 수열에서 매 원소가 들어올 때마다 버퍼는 다음과 같이 바뀌게 된다. 뒤에 새로운 원소가 추가되거나 이미 있는 값의 업데이트만 발생하지(O(1)), 배열 원소들을 전부 하나씩 밀어야 하는 삽입이나 삭제(O(n))가 발생하지는 않음을 염두에 두기 바란다.
3: 3
2: 2
1: 1
4: 1 4
5: 1 4 5
2: 1 2 5
3: 1 2 3
5: 1 2 3 5
3: 변화 없음
6: 1 2 3 5 6
4: 1 2 3 4 6

즉, 버퍼가 가리키고 있는 것은 각 길이별로 가장 작은 수일 뿐이다. 그러나 버퍼가 가리키는 순서대로 배열을 참조하면 수열이 언제나 오름차순, 즉 정렬이 돼 있다는 게 보장된다.
최소값을 갱신할 위치를 찾는 것은 이분 검색(binary search)으로 할 수 있다. 이 덕분에 작업이 O(n^2)에서 O(n log n)으로 줄어들 수 있게 된다. 정확하게 말하면 O(n log k)(k는 LIS 길이)이니 더욱 빠르다. worst case로 증가 수열을 만들 수가 없는 내림차순 수열을 던져 주면, 거의 O(n)이나 다름없는 속도로 금방 실행이 끝난다는 뜻이다.

물론, 이 버퍼에는 각 길이별로 가장 작은 증가 수열을 구하는 힌트만 들어있을 뿐, 가장 긴 LIS를 추적하는 정보는 전혀 들어있지 않다. 그렇기 때문에 추적 순서는 역시 별도의 배열에다 따로 보관해 놔야 하며 이 역시 그리 어렵지 않게 구현할 수 있다. 심심하신 분은 이 알고리즘을 직접 코딩해 보기 바란다.

정보 올림피아드를 공부하던 시절엔 이런 유형의 문제도 재미있었다. 뭐, 본인은 머리싸움에 쥐약인 타입인지라 경시 부문에서는 별 재미를 못 보고, 대박은 공모 부문에서 다 냈지만 말이다.

- 양수와 음수가 뒤섞인 n개의 수열이 있을 때 합이 가장 큰 구간을 O(n) 시간 만에 구하기
- 위와 비슷한 예로, 0.x와 n.x가 뒤섞인 n개의 수열이 있을 때 곱이 가장 큰 구간을 역시 O(n) 시간 만에 구하기
- x*y 2차원 배열이 있을 때, 이런 조건을 만족하는 가장 넓은 면적을 구하기 (1999년도 IOI의 공항 건설 부지 찾기 같은)

알고리즘이라는 게 OR(operations research)과 밀접한 관계가 있는 것 같다. 선형 계획법, 동적 계획법 같은 개념도 원래는 그 분야에서 유래되었기 때문에 용어에서 그다지 전산학적인 어원은 찾을 수 없다.
덧. algorithm인데 왜 다들 알고리듬이라고 적지 않고 알고리즘(=algorism?)이 보편화해 있는 걸까?

Posted by 사무엘

2010/11/30 09:00 2010/11/30 09:00
, , ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/421

동일한 기능을 하는 프로그램이 여러 CPU 아키텍처로 포팅된 실행 파일을 살펴보면...
우리에게 아주 친숙한 x86 아키텍처용 EXE는 크기가 가장 작다고 단정적으로 말할 수는 없지만 상당히 작은 편이다.
이보다 코드 사이즈가 더 작게 컴파일되는 아키텍처는 본인이 보기엔 찾기가 어렵다.
EXE 파일의 기계어 코드 부분을 헥사 에디터로 들여다봐도 코드 바이트가 좀 조밀조밀 있는 편이다. 그 반면 IA64나 MIPS 같은 아키텍처 EXE의 기계어 코드를 들여다보면, 4~8바이트 단위로 패턴이 느껴진다.

물론 인텔 아키텍처도 그나마 32비트와 64비트로 가면서 인스트럭션의 평균적인 크기가 길어져 오긴 했다. 그런데 초기의 16비트 명령 체계는 정말 아담했으며, 어셈블리 튜닝을 쓰면 오늘날의 컴퓨터 아키텍처로는 상상도 할 수 없는 작은 크기의 프로그램으로 온갖 큼직한 기능을 넣을 수 있었다. ^^;;
인텔 아키텍처는 하위 호환성을 최대한 존중하고 있으니, 옛날에 정한 1바이트짜리 코드 바이트가 다 선점되었으면 32비트나 64비트 때 추가된 명령은 탈출문자 접두사를 붙여서 5바이트~6바이트... 이런 식으로... 근본적으로 길어질 수밖에 없는 셈이다.

컴퓨터 아키텍처에 대해서 배운 전산 전공자라면, CISC 방식과 RISC 방식의 차이에 대해서는 익히 들어 봤을 것이며, 오늘날엔 이런 식의 단편적인 구분이 별 의미가 없어졌다는 것까지도 알고 있을 것이다.

옛날은 메모리가 아주 귀한 자원이던 시절이었다. 그래서 인텔 x86은 코드를 적재하는 데 필요한 기억장소의 크기를 최대한 아끼는 방향으로 설계되었다. 기계어 코드의 크기가 1바이트부터 시작해 아주 가변적이고, 각 명령 하나하나에 꽤 함축적으로 많은 뜻을 포함시킬 수 있다.

많은 뜻이라 함은, 명령을 내릴 때 상대 주소라든가 절대 주소라든가 실제 상수 같은 개념을 한 인스트럭션에다가 바로 표현할 수 있음을 의미한다. 다른 아키텍처라면 임시값을 또 레지스터에다 먼저 저장하고 전처리를 해서 여러 인스트럭션을 거쳐 표현해야 할 명령을, 한 인스트럭션만으로 표현할 수 있다는 뜻이다. 이것을 CISC 방식이라고 하며, 앞의 C는 complex를 뜻한다. 인텔 x86은 CISC 방식 아키텍처의 대표적인 예이다.

CISC 방식는 상술했듯이 코드 길이가 짧아진다는 장점이 있지만 이를 하드웨어적으로 해석하는 회로가 필연적으로 복잡해지고 만들기 어려워진다는 단점이 있다. 이는 전력 소모의 증가로까지 이어진다. CPU에서 실제로 명령을 실행하는 부분이 아니라 이게 무슨 명령인지 파악하는 부분부터가 오버헤드가 커진다는 뜻이다.

그래서 CISC 방식은 근본적으로 임베디드나 모바일 환경에는 적합하지 않다. 이런 이유로 인해, 오늘날 전세계적인 각광을 받고 있는 스마트폰에서는 ARM이라는 RISC (Reduced) 방식 아키텍처가 쓰이고 있다. ARM이 스마트폰 세계에서 거의 인텔 x86 같은 본좌 인지도를 차지한 것이다.

RISC는 설계 철학이 CISC와는 반대이다. 인스트럭션의 크기는 기계가 한 번에 인식하는 machine word 크기와 일대일 대응하거나 최소한 배수 단위이다. 인스트럭션을 fetch, decode하고 의미를 파악하는 절차가 아주 간편하다. 최소 의미 단위가 작은 대신에 임시 작업용으로 범용 레지스터를 CISC 방식 CPU보다 꽤 많이 준다.

이런 CPU는 심지어 구조체 멤버를 인식하는 단위에도 제약이 있다. 포인터의 오프셋이 machine word의 배수 단위로 딱 떨어지지 않고 사이에 걸쳐 있는데 그 주소를 참조시키면 CPU가 에러를 일으킨다. 성능과 효율을 위해, 이런 복잡한 상황은 과감하게 처리를 거부하는 방법을 택한 것이다.

인텔 x86은 그렇지 않다. 명령어의 실행부터가 워낙 지저분한 바이트 단위 fetching에 익숙한지라, 오프셋이 저런 경우에도 한 사이클만에 참조를 못 하더라도 여러 사이클로 나눠서 사용자가 원하는 데이터를 얻어 준다.

RISC 아키텍처에서 돌아가는 실행 파일을 들여다보면 기계어 코드가 진짜로 4바이트나 8바이트 패턴으로 쫘르륵 나열되어 있는 걸 볼 수 있다. 그리고 동일한 코드를 컴파일한 실행 파일 크기가 x86 같은 아키텍처의 것보다 더 크고, 실행 파일의 압축률도 더욱 높다(듬성듬성하다는 뜻). 다만 한 기능을 수행하는 데 드는 CPU 명령 수가 증가하고 그럴 수록 side effect라든가 복잡도도 더 증가하는 만큼, RISC 아키텍처 코드를 컴파일러가 최적화하기는 더욱 어려울 것이다.

이렇듯, 컴퓨터 아키텍처에서 CISC냐 RISC냐 하는 케케묵은 논쟁은 자동차로 치면 전륜 구동이냐 후륜 구동이냐, 철도 차량으로 치면 동력 분산식이냐 동력 집중식이냐 하는 차원의 재미있는 주제이다. 요즘은 x86 같은 CISC 방식 CPU도 내부적으로는 복잡하고 함축적인 명령을 다시 RISC 식 명령으로 나눠서 파이프라인을 수행함으로써 RISC의 장점을 취하려고 하고 있다.

그리고 문득 든 생각:
어느 기계에서나 이식 가능한 평범한 C/C++ 코드는 자연어로 치면 "나는 철수입니다" 같은 평이한 문장에다 대응시킬 수 있다.
그렇다면 도저히 포팅이 불가능한 인라인 어셈블리 코드는, 자연어로 치면 특정 언어의 음운 특성이나 특정 문화 배경에 대한 이해 없이는 도저히 이해할 수도, 문자 그대로 번역할 수도 없는 함축적인 유머나 언어 유희에다 비유할 수 있겠다.

Posted by 사무엘

2010/09/20 09:16 2010/09/20 09:16
, , , ,
Response
No Trackback , 6 Comments
RSS :
http://moogi.new21.org/tc/rss/response/376

본인은 예전에 쓴 이 글에서 언급된 노트북을 개인용 컴퓨터로 아주 만족스럽게 잘 쓰고 있다. 이제 2년이 경과했지만, 예전 노트북과는 달리 이번 4대 노트북은 잔고장이 발생한 적이 전혀 없고 심지어 아직까지 운영체제를 재설치한 적도 없다.

http://moogi.new21.org/tc/141

그런데 지금으로부터 두세 주 남짓 전부터는 컴퓨터에 뭔가 이상 징후가 느껴졌다.
바로 컴퓨터의 속도가 체감상으로는 평소의 절반 이하 수준으로 곤두박질친 것이다.

창의 크기를 바꾸거나 인터넷 화면을 스크롤할 때의 반응이 매우 둔해지고, 컴파일도 엄청 느려졌다.
Aero를 꺼도 화면이 버벅대는 게 변함없었기 때문에 이건 그래픽 카드 문제가 아니며, 디스크 I/O 쪽의 병목 현상 역시 아니었다. 전적으로 CPU가 느려진 것이 확실했다. 심하게 버벅댈 때는 마우스 포인터까지 버벅대며 움직였다.
더구나 AC 전원을 연결해도 속도가 여전히 느렸기 때문에 전원 절약과 관련된 CPU 감속 역시 아니었다.

본인은 평소에 컴퓨터 관리를 얼마나 결벽증에 가깝게 하고 지내는데 메모리 부족이나 악성 코드 때문도 아니고..
이 노트북은 물론 예전 노트북에서도 이런 식의 이상 증세를 경험한 적은 없었기 때문에 처음엔 좀 당황했다.

이 문제는 의외로 굉장히 쉽게 해결됐다. 그것 때문일 수도 있다고 예상은 했지만 정말로 전적으로 그것 때문일 줄은 몰랐다.

바로... 냉각팬에 잔뜩 낀 먼지 때문이었다. 그것만 제거해 주자 컴퓨터는 거짓말처럼 본디 속도로 되돌아왔다!
팬이 잘 안 돌아가고 열이 못 빠져나가고 있으면 컴퓨터가 안전을 위해 스스로 알아서 감속 운행(?)을 해 왔던 것이다. 굳이 절전을 위한 감속뿐만 아니라 말이다. 먼지를 제거하기 전이나 후나 밖에서 느껴지는 컴퓨터 소음 내지 팬 바람은 별 차이가 없는데, 컴퓨터의 성능이 이렇게 확 달라지다니 참 뜻밖이고 의미 있는 경험을 했다. (날씨가 너무 덥고 레일이 너무 뜨거우면 알아서 감속을 하는 KTX처럼?? ㅋ)

10년이 넘게 노트북을 써 왔지만 이런 일은 처음이었다. 하긴, 2년이 넘도록 A/S 센터를 단 한 번도 찾지 않을 정도로 컴퓨터가 건강했기 때문에, 내부 청소를 할 일도 지금까지 없었다. 이렇게 한번 먼지를 제거해 줬으니 앞으로 또 이 노트북이 몇 년간 쌩쌩하게 돌아가 주기를 기대한다.

하긴, 다른 가전제품에서도 비슷한 경험을 한 적이 있다. 멀쩡하던 에어컨이 갑자기 전혀 동작하지 않고, 아무리 온도를 낮춰도 더운 바람밖에 나오지 않는 것이었다. “올여름엔 지내는 데 당분간 애로사항이 잔뜩 꽃피겠구나! A/S 센터 연락처가 어디더라?” 이러고 있었는데 문제의 원인은 정말 어처구니없는 곳에서 찾아낼 수 있었다.

에어컨으로부터 나오는 더운 바람이 빠져나갈 통로가 막혀 있었던 것이다. ㅜ.ㅜ
겨울에는 보온을 위해 닫아 두지만 한여름에는 응당 개방해 놓아야 한다.
컴퓨터든 에어컨이든 열이 잘 빠져나가게 해 줘야 한다는 교훈을 얻었다. 이건 자동차에도 동일하게 적용되는 원칙일 것이다.

Posted by 사무엘

2010/08/05 09:09 2010/08/05 09:09
Response
No Trackback , 6 Comments
RSS :
http://moogi.new21.org/tc/rss/response/339

컴퓨터 구조를 공부하면서 배우는 기본 개념 중 하나는, ‘컴퓨터 내부에서 숫자가 표현되는 원리’이다.
부호 있는 정수는 소위 말하는 ‘2의 보수’ 형태로 표현되는데, 이것은 모든 비트가 1인 숫자는 -1이 되는 형태이다. 이런 방식을 쓰는 이유는 연산 회로를 설계할 때, 뺄셈을 덧셈의 변형만으로 매우 손쉽게 구현할 수 있는 체계이기 때문이다.

베이직 언어는 다른 언어들과는 달리 TRUE의 값이 -1인데, 그 이유가 바로 이런 컴퓨터의 구조와 관련이 있다. 베이직은 비트 연산자와 논리 연산자의 구분이 없기 때문이다.
0 아니면 1밖에 모르는 디지털 컴퓨터는 연속이나 무한 같은 개념을 표현할 수 없다. 숫자도 정수만 다루는 데 익숙하다. 그러나 현실 세계에서 발생하는 문제를 해결하려면 소숫점이 동반된 실수를 다뤄야 할 일도 매우 자주 발생한다.

소수를 표현하는 가장 간단한 방법은 소위 ‘고정소수점’이다. 가령 32비트 고정소수점의 경우, 정수와 완전히 똑같은 방법으로 숫자를 표현하되 실제로는 그 수의 의미를 정수를 16비트 크기만큼(65536) 나눈 것으로 인식하는 것이다. 즉, 수의 정밀도만 1이 아닌 1/65536으로 기계적으로 높아지는 셈.

고정소수점은 일단 덧셈· 뺄셈· 비교 연산을 정수와 완전히 동일한 방식으로 할 수 있어서 처리 속도가 매우 빠르며, 곱셈과 나눗셈을 할 때만 약간 주의해서 자릿수 정돈을 하면 되니 편하다. 실제로 일부 벡터 그래픽이나 글꼴 쪽 분야에서는 이런 고정소수점 방식이 잘 쓰이고 있다. 그러나 표현 가능한 수의 범위가 매우 심각하게 제한을 받기 때문에 범용성은 떨어진다.

자리수의 제약을 받지 않고 소수점을 좀더 자유롭게 표현하려면, 유효숫자와 자릿수를 따로 둘 필요가 있다. 그게 훨씬 더 실용적이다. 부동(floating)이라는 개념이 여기에서서 나왔다. 제일 큰 자리수의 숫자가 무엇이며 그 뒤에 0이 몇 개 붙느냐가 중요한 것이다.

이런 체계에서는 10.5에서 0.5는 제대로 표현할 수 있지만, 10000000.5에서 0.5는 손실될 가능성이 커진다. 그리고 한 숫자를 결국 두 수의 조합으로 표현해야 하므로, 각종 연산이 단순 정수보다 훨씬 더 느리고 힘들어진다.

옛날에는 이런 부동소수점 계산을 하드웨어 회로 차원에서 바로 해 주는 코(보조)프로세서가 별도로 존재했다. fdiv, fmul 같은 인스트럭션. 심지어는 제곱근이나 삼각함수 값까지 바로 구하는 명령이 있다!
하지만 시중에는 그런 게 없는 컴퓨터도 있다는 얘기이기 때문에, 1990년대에 상업용으로 쓰이던 어지간한 컴파일러들을 보면 소프트웨어적으로 부동소수점 계산을 흉내 내는(엄청 느리지만-_-) 코드를 추가할지를 지정하는 옵션도 있었다.

그런데 부동소수점을 표현하는 방식 자체가 통일돼 있어야 그 기준에 따라 코프로세서를 만들든지 말든지 할 수 있을 것이다. 그 표준 규격이 바로 IEEE754이다. 1985년에 제정되었다.

이 규격은 좀 정밀도가 떨어지지만 처리 속도가 더 빠른 32비트와, 용량이 넉넉하지만 역시 속도의 압박이 있는 64비트로 나뉜다. CPU 단위가 16비트이고 부동소수점을 소프트웨어적으로 처리하는 게 보편적이던 옛날에는, 아예 컴파일러 재량으로 소프트웨어적으로 구현한 48비트(6바이트-_-.. 파스칼에서 Real) 실수와 80비트(C/C++에서 long double) 실수도 존재하였으나 지금은 완전히 흑역사가 되었다. 요즘은 화면 픽셀 크기도 24비트는 존재하지 않으며 32비트이다. 죄다 컴퓨터가 처리하기 편한 단위로 그냥 확장된 듯하다.

제일 간단하게 말하자면, 32비트 실수에서는 1비트는 이 수의 부호를, 다음 8비트는 지수를, 다음 나머지 23비트는 유효숫자를 나타낸다. 64비트 실수에서는 그 비율이 1:11:52이다.
컴퓨터가 사용하는 부동소수점의 지수의 밑은 너무 당연한 말이지만 10이 아니라 2이다. 따라서 2의 거듭제곱의 역수가 아닌 모든 소수들은 끝자리가 잘린 ‘순환소수’가 된다! 0.25, 0.125, 0.5 같은 수가 아닌 다른 모든 수들.. 0.1, 0.2, 0.3 이런 것들은 화면에서는 적당하게 근사되어 표현되었다 할지라도 실제로는 그 수의 100% 정확한 형태로 저장되지 않은 셈이다. 부동소수점의 한계를 분명히 알고 있어야 한다.

어쨌거나.. 2^23과 2^52에다 base가 10인 로그를 씌워 보면 32비트 실수의 유효숫자 정밀도는 약 7자리, 그리고 64비트 실수의 정밀도는 약 15자리라는 걸 알 수 있다. 그리고 표현할 수 있는 자리수의 범위는 32비트는 약 38자리, 그리고 후자는 약 308자리이다(비트가 3개 더 늘어서 2^3인 8배가 더 늘었으므로). 소수점 밑으로도 그만치 내려갈 수 있다.

까놓고 말해 이런 공용체를 통해 부동소수점을 쉽게 해부할 수 있다.

union REAL {
   float fValue;
   struct {
      unsigned sign: 1; //부호
      unsigned expo: 8; //지수부
      unsigned mantissa: 23; //가수부
   };
};

공용체와 비트 필드가 존재하는 C/C++ 만만세. ㄲㄲ
단, 우리가 쓰는 인텔 CPU는 little endian이므로 sign, expo, mantissa 순이 아니라 반대로 mantissa, expo, sign으로 멤버 순서만 바꿔 주면 된다. 쉽죠?

그런데 지수부와 가수부 사이에 아무런 제약을 가하지 않을 경우 동일한 숫자를 여러 방법으로 표현할 수 있게 되어 문제가 생긴다. 4를 2의 2승, 4의 1승 이런 식으로 표현하듯이 말이다. 그래서 IEEE754는 가수부는 가장 큰 자리수의 비트값이 무조건 1인 것만 인정하게 하고 그 1은 명시적인 표기를 생략했다. 이를 ‘정규화’된 형태라고 한다.

거두절미하고, 32비트 부동소수점에서 구조체의 각 멤버로부터 부동소수점 값을 다시 얻어 오는 식은 다음과 같으므로 참고하자. 식에서 23과 24는 가수부의 자릿수 23비트와 관계가 있으며, 126은 지수부의 크기 8과 관계가 있다. 2의 8-1승보다 2 더 작은 값이다. 1<<23이 바로 생략된 최대 자리수인 셈이다.
그저 의미를 알 수 없는 블랙박스 같던 부동소수점이 좀더 친근하게 와 닿을 것이다.

pow(2.0, (double)( (int)a.expo-24-126))* ((1<<23)|a.mantissa) * (a.sign ? -1:1)

IEEE754에는 이외에도 무한대를 표현하거나 불능을 뜻하는 규격이 있다. 정수를 0으로 나누면 CPU 차원에서 바로 exception이 일어나지만 부동소수점은 에러는 안 나고 저런 특수한 숫자가 돌아온다.
또한 정규화를 해서 자리수를 강제로 늘리는 바람에 표현할 수 없게 된 일부 극히 작은 수를 별도로 표현하기 위해 내부적으로 심지어 ‘고정소수점’ 방식을 임시로 사용하는 규정조차 갖추고 있다. 마치 퀵 정렬이 작은 구간에서는 삽입 정렬을 사용하기도 하는 것처럼 말이다. 이에 대한 더 이상의 자세한 설명은 생략하겠다.

참고로,
1. IEEE754 부동소수점에는 구조적인 한계로 인해 0이 +0과 -0 이렇게 두 개 존재한다.
2. 역사상 최초의 전자식 컴퓨터로 일컬어지는(논란의 여지는 좀 있지만) 에니악은 10진법을 사용했다! 전자 신호 4비트(=16)로 10진법 숫자 하나를 표현했을 것이고 매우 비효율적으로 동작했을 것이다. 하지만 그 당시엔 이 기계로 탄도 계산도 하고 풍동 실험도 하고 심지어 일기 예보도 했었다.

Posted by 사무엘

2010/04/22 22:53 2010/04/22 22:53
, ,
Response
No Trackback , 7 Comments
RSS :
http://moogi.new21.org/tc/rss/response/251

자기가 어느 분야에서 완전 덕후이고 전문가이면..

가령 영상 처리, 필기 인식, 음성/동영상 압축, 디바이스 드라이버, 폰트 엔진, 게임 3D 엔진, 자연어 처리, 컴파일러, 파일 압축, 데이터베이스 엔진 ....
뭐 하이튼 그런 쪽으로 회사나 연구소 하나 먹여살릴 정도의 기술이 있으면..

그 기술 분야 자체가 수요가 없어지고 사장되지 않는 한, 딱히 외공이 없어도 먹고 사는 데 지장이 없다.
컴퓨터 조립할 줄 몰라도 되고-_-, 모바일 쪽 개발 하나도 몰라도 된다.
아직까지도 윈도우 XP + 비주얼 C++ 6으로 개발한다 하더라도 기술 이사로 대접 받을 수 있다.

외공이 필요하면 외공을 갖춘 다른 개발자를 고용해서 일 시키면 된다.
사실 컴퓨터 관련 이공계 대학원은 '내공'을 쌓으라고 있는 것이다. 굳이 컴퓨터 자체만 골수로 파고들지 않아도 되며, 사실은 다른 분야와 학제간의 연구가 분야가 더욱 넓기도 하다.
단순 비트 아카데미, 게임 스쿨 같은 사설 교육기관과 근본적으로 다른 점이 바로 이것이다.

물론 전자라고 해서 외공이 전혀 필요 없다거나, 후자라고 해서 내공을 아예 등한시한다는 것은 아니지만, 추세가 그렇다는 뜻이다.

외공은 내공과는 반대이다. 한 분야에 대한 세부적인 깊이는 그리 없더라도 정말 다양한 분야를 섭렵해야 한다. 깊이 대신 넓이이다.
늘 새로운 기술과 플랫폼을 익혀야 하고 최신 IT 동향을 익히고, 처음 보는 환경에서도 기술 문서를 척 보면 바로 이해하고 잘 적응해야 한다. 그 바닥의 숲을 척 꿰뚫고 있어야 한다.

여기에다 사업 수완과 사회성, 경제 관념까지 갖춰지면, 처음엔 개발자로 시작했다가도 금세 개발자 딱지 떼고 관리자 내지 심지어 경영자의 길로 갈 수 있다. 굳이 내 손으로 개발 안 해도 된다. 앞으로 무엇을 개발해야 할지, 이 일을 누구에게 시키면 되는지 그 일만 잘 해도 내 역할 다 한 것이기 때문이다.

내공, 외공 어느 것도 시원찮으면 정말로
그냥 노가다 코딩만 하는 3D 업종 개발자의 굴레를 벗어나지 못한다.
나는 내공형인가, 외공형인가?

Posted by 사무엘

2010/01/15 14:43 2010/01/15 14:43
Response
No Trackback , a comment
RSS :
http://moogi.new21.org/tc/rss/response/144

윈도우 환경에서야, 실행 가능한 기계어 코드가 들어있는 다른 모듈을 동적으로 로드하는 것이 일도 아니다. LoadLibrary, GetProcAddress라는 마법 같은 API가 있기 때문이다. 플러그 인 같은 것도 프로토콜만 하나 잘 짜 놓으면 얼마든지 만들 수 있다.

하지만 도스 시절에 그런 일종의 DLL이라는 것을 직접 구현을 어떻게 했을까?
본인은 도스 환경에서 하드웨어를 직접 제어한다거나 시스템 프로그래밍 경험이 전혀 없다. PC 통신 시절에 올라오던 랄프 브라운의 "인터럽트 릴리즈" 이런 것들도 내게는 완전 외계인 문서였다.
32비트 윈도우에서 곧바로 C/C++을 공부한 경우이다 보니, 지금 다시 생각해 보면 그게 무척 신기하다. 그래서 점점 다시 저수준 시스템 쪽으로 회귀 중이다.

도스용 아래아한글은 2.5에서 덧실행이라는 기능이 추가되었다. 그래서 과거 1.2 시절에 잠깐 있었던 테트리스 게임도 덧실행으로 부활하고, 아래아한글 안에서 숫제 한네트라는 통신 에뮬레이터까지 구동할 수 있었다. (개인적으로 그래픽 에디터를 그렇게 덧실행으로 내장했으면 무척 좋았을 것 같다.)

3.0에서는 덧실행의 활용의 폭이 더욱 커져서 계산기, 지뢰 찾기, CD 플레이어도 제공했으며 심지어 화면 보호기까지 덧실행 프로그램으로 독립했다. 2.5 확장팩에서부터 제공되던 영한 사전도 GUI는 덧실행 애플릿 형태였다. 이런 덧실행 프로그램들은 내부적으로 32비트 네이티브 코드였으며, 응당 32비트 에디션(HWP386)에서만 지원되었다.

도스용 이야기 역시 이름은 덧실행이 아니지만, 이런 덧실행에 해당하는 여러 기능이 있었다. 그림 파일 뷰어도 있고, AI가 상당히 똑똑했던 걸로 기억하는 오목 게임도 있었다. 16컬러에서밖에 실행되지 않았던 아래아한글과는 달리 이야기는 256색/트루컬러 그래픽까지 지원했지만, 내부 코드는 여전히 볼랜드 C++로 빌드된 16비트 프로그램이었으며, 덧실행 역시 16비트 코드라는 게 아래아한글과는 달랐다.

물론 덧실행에 앞서 이들 프로그램은 그래픽/프린터 드라이버가 이미 일종의 DLL이며 아래아한글의 경우 폰트 드라이버라는 계층이 있었지만, 이들보다 화면에 보이는 GUI가 있는 덧실행 프로그램이 사용자에게 더욱 존재감 있게 다가온 것 역시 부인할 수 없는 사실이다.

이런 덧실행들은 어떻게 만들었을까? 아래아한글 덧실행의 경우, SDK가 있었다.
아래아한글 32비트 에디션과 덧실행 프로그램들은 왓콤 C/C++로 빌드되었는데, SDK가 지정한 각종 함수/구조체 프로토콜에 맞게 덧실행 프로그램을 작성한 후, 역시 SDK가 제공하는 특수한 라이브러리를 써서 링크하고 바이너리에다 고유한 post processing을 마치면 간단히 덧실행 프로그램이 만들어졌다. 얘네들은 헤더만 다를 뿐, 내부적으로는 인텔 32비트 어셈블리 기계어 코드가 들어있는 exe 그대로였다.

아래아한글은 자기 밑에 실행되는 덧실행 프로그램에다가 지금 돌아가는 비디오 환경 같은 걸 알려 주고, 하드웨어 독립적인 각종 그래픽 루틴, 그리고 요즘 GUI 운영체제들이 다 그렇듯이 막강한 한글 입출력 엔진을 이용하여 글자를 찍고 GUI를 출력하는 API를 덧실행에다가 제공해 주었다.

그때 화면 보호기 중에는 아주 고전적인 "우주 여행"도 있었고 점 찍기, 선 그리기, 생명 게임, 퍼즐, 벌떼 같은 것들이 기본 제공되었다. 우주 여행은 단순히 전진만 하는 게 아니라 방향을 꺾는 것도 있고 무척 박진감 넘치고 원근감이 무척 멋지게 잘 표현돼 있었다.

그리고 벌떼도 나름대로 2차원 공간에서의 boid 시뮬레이션인데 움직임이 굉장히 사실적이어서 저런 걸 어떻게 짰을지가 무척 궁금했다.

이것 말고 선이나 점을 그리는 것은 while 루프 안에서 랜덤하게 화면에 낙서를 하는 녀석이었다. 특히 점 찍기는 그냥 화면 가득히 검은 1픽셀 점을 채워넣는 것으로, 프로그램 파일의 크기가 400바이트도 채 되지 않았다.

그래서 문득 궁금해졌다. 크기도 꽤 작고 프로그램을 어떻게 짰을지 감도 오고 하니,
코드 부분을 긁어 와서 디스어셈블을 해 봤다.

본인은 어셈블러 쪽 지식이 거의 없다. 뭐 복잡한 레지스터 이름만 나와도 머리가 지끈거린다.
본인보다 더 고수이신 분이 있으면 아래의 해설에 오류 교정이나 보충할 만한 설명 있으면 얼마든지 환영한다.

; 레지스터에 주어져 있는 어떤 값들을 스택에다 push함.
; 화면 보호기들은 다들 이런 명령으로 시작하더라.
0012FDF8 53               push        ebx
0012FDF9 51               push        ecx
0012FDFA 52               push        edx

; EAX 레지스터의 값을 0으로 초기화. x xor x는 언제나 0이므로, mov 0을 하는 것보다 코드 길이가 짧아서 좋다.
0012FDFB 31 C0            xor         eax,eax

; 뭔가 초기화 함수를 호출한다.
; 아래아한글 덧실행 SDK가 제공해 주는 라이브러리 함수에다 static link를 한 것이다.
0012FDFD FF 15 88 02 00 00 call        dword ptr ds:[288h]
0012FE03 FF 15 50 02 00 00 call        dword ptr ds:[250h]

; 고급 언어로 치면 while문의 시작인 듯. while(CanContinue()); 로 딱 번역 가능한 문장이다.
; 함수 리턴값을 테스트하여 조건이 만족하는 경우 뺑뺑이 바깥으로 빠져나간다. (12FE54)
; 화면 보호기의 종료 조건을 테스트하는 것이므로, 아마 키보드나 마우스 입력을 감지하는 함수일 것이다.
0012FE09 FF 15 14 02 00 00 call        dword ptr ds:[214h]
0012FE0F 85 C0            test        eax,eax
0012FE11 75 41            jne         0012FE54

; 뭔가 포인터 참조를 하는 듯하다. x = ptr->member 정도? 먼저 ptr의 위치를 임시로 EDX에다 저장 후,
0012FE13 8B 15 24 03 00 00 mov         edx,dword ptr ds:[324h]

; 함수 호출을 염두에 두고, 아마 EAX의 값인 0을 얹어 놓는 것 같다. 이렇게 EAX를 자주 써먹는 이유는 아까 xor EAX,EAX와 마찬가지로 명령어 길이가 짧기 때문.
0012FE19 50               push        eax

; EBX에다가 화면 세로 해상도를 저장하는 것 같다.
0012FE1A 8B 5A 14         mov         ebx,dword ptr [edx+14h]
; 아마도 이 0x24C 오프셋이 난수를 되돌리는 함수인 듯하다.
0012FE1D FF 15 4C 02 00 00 call        dword ptr ds:[24Ch]

; 정확한 의미 잘 모름.;;
0012FE23 89 C2            mov         edx,eax
0012FE25 43               inc         ebx

; 32비트 숫자를 31만치 오른쪽으로 비트 shift 한다는 것은 결국 그 숫자의 부호만 남기고 싹 없앤다는 뜻인데.. 특별한 의미는 모르겠다. 나눗셈 연산 전에 부호와 관련된 무슨 플래그 설정인 듯.
0012FE26 C1 FA 1F         sar         edx,1Fh

; 이제 난수가 화면 세로 해상도의 범위 안에 있도록, 난수 값을 화면 해상도로 나눈 나머지를 구한다.
; idiv 명령은 한번에 몫과 나머지를 모두 구하는데, 나머지의 값을 EDX에다 저장해 준다. 그렇기 때문에 EDX를 push하는 것을 알 수 있다.
; 아까 inc ebx 명령은, 화면 해상도에 0이 들어오더라도 나눗셈 에러가 나지 않게 안전 조치를 취한 것 같다.
0012FE29 F7 FB            idiv        eax,ebx
0012FE2B 52               push        edx

; 세로에 이어 가로 위치 argument를 얹을 준비를 한다. 다시 EDX에다가 324h 포인터 위치를 저장하고,
0012FE2C 8B 15 24 03 00 00 mov         edx,dword ptr ds:[324h]

; 아까 0x14가 화면의 세로 해상도이고 이거는 10h이니 가로 해상도? ㅋㅋ
0012FE32 8B 5A 10         mov         ebx,dword ptr [edx+10h]
0012FE35 FF 15 4C 02 00 00 call        dword ptr ds:[24Ch]

; 아까와 거의 동일하다. 이번엔 inc 명령은 들어가 있지 않다.
0012FE3B 89 C2            mov         edx,eax
0012FE3D C1 FA 1F         sar         edx,1Fh
0012FE40 F7 FB            idiv        eax,ebx
0012FE42 52               push        edx

; 이제는 324h 오프셋 자체를 push한다. 이 값은 역시 덧실행 프로그램이 호스트(아래아한글) 프로그램으로부터 받은 핸들 같다.
; 이게 제일 나중에 push된다는 말은, C++ 코드 상으로 제일 첫째 argumet라는 뜻이다. 즉,
; SetPixel(hDC, x, y, 0); 에서 hDC뻘 된다는 뜻이다. 0은 아까 EAX의 값으로 대체했던 점의 색깔 정도? (이 화면 보호기는 화면을 온통 검은 점으로 채운다)
0012FE43 FF 35 24 03 00 00 push        dword ptr ds:[324h]

; 점 찍는 함수 드디어 호출!
0012FE49 FF 15 E0 01 00 00 call        dword ptr ds:[1E0h]

; 함수 호출 후 argument를 얹었던 스택 위치를 재정리하는 작업. 16바이트를 더하는 것을 보면, 위의 함수가 총 4개의 인자를 받았다는 것을 알 수 있다.
0012FE4F 83 C4 10         add         esp,10h

; 다시 while의 시작으로 빠꾸
0012FE52 EB B5            jmp         0012FE09

; 루프 끗.
0012FE54 5A               pop         edx
0012FE55 59               pop         ecx
0012FE56 5B               pop         ebx
0012FE57 C3               ret

따라서 위의 코드에서 loop 부분을 슈도코드로 재구성하면 대략 이런 형태가 되겠다.

HANDLE hHWPHost;
while( CanContinue() )
        PutPixel( hHWPHost, rand() % hHWPHost->nScreenX, rand() % (hHWPHost->nScreenY+1), 0 );

저 간단하기 그지없는 코드를 C언어로 짜도, 컴파일을 하면 저 정도 수준의 기계어 코드로 번역된다는 것이 놀랍다.
역시 전산학의 총아는 컴파일러이다. 컴파일러 제작자가 존경스럽다.

프로그램 논리를 다 알고 있는 초간단 코드도 겨우 인텔 CPU 인스트럭션 세트 매뉴얼을 뒤지고,
간단히 내가 생각하는 코드를 VC++로 최적화 없이 컴파일하여 살펴보면서 낑낑대면서 디스어셈블을 해 봤는데,
이보다 훨씬 더 복잡한 기계어 프로그램은, 암호 같은 숫자와 명령 코드 속에서 본디 의미와 논리를 찾는다는 게 거의 불가능에 가깝다.

커다란 wav 파일 하나 던져 주고서, 이걸 재생하지 않고서 무슨 소리인지 알아 맞히는 거나 다름없다.
wav는 그래도 손실 압축이라도 되지, 기계어 exe는 같은 크기이더라도 정보량이 훨씬 더 많으며 손실 압축을 할 수가 없다.

점 찍기와는 달리, 선 그리기만 해도 코드 길이가 저것보다 더 길고, 특히 난수를 다섯 개나 요청한다.
x1, y1, x2, y2, 그리고 색깔 이렇게 다섯 개인 것을 어셈블리 코드 상으로 확인을 할 수 있었다.

비록 도스에서 돌아가는 일개 덧실행 프로그램이지만
돌아가는 기계 환경이 동일하고 똑같은 32비트 기계어이기 때문에 비주얼 C++의 디버깅 기능으로 이렇게 간단히 디스어셈블리 결과를 들여다볼 수 있다는 게 신기했다 (실행은 당연히 못 하지만).

단지 운영체제에 따라 EXE 파일의 포맷이 다르며, 입출력이라든가 운영체제가 제공하는 기능을 요청할 때, 도스 프로그램은 인터럽트에 의존하는 반면, 윈도우는 커널 영역에 자리잡은 함수 호출 기법을 사용하는 식의 차이가 존재하는 것이다.

Posted by 사무엘

2010/01/12 11:40 2010/01/12 11:40
,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/118

 http://www.dilascia.com/ruint.htm

본인이 이 사람 이름을 본 것은 비주얼 C++ 6.0을 쓰던 시절부터이다.
MSDN을 보면 각종 함수 레퍼런스, 툴 설명서뿐만이 아니라 고맙게도 일부 책이나 간행물 내용까지 수록돼 있었는데, 어느 프로그래밍 잡지의 C++ Q&A란을 애독하기 시작했다. 그리고 그 코너를 집필하는 사람이 바로 저 전설의 프로그래머 Paul DiLascia였다.

특히 비주얼 C++ 6.0 MSDN에는 bmp 파일 뷰어를 밑바닥부터 만드는 과정을 설명해 놓은 게 있었는데
친절한 설명도 설명이거니와 이 아저씨는 글빨 입담이 정말 구수하다는 것을, 생소한 영어를 읽으면서도 느끼지 않을 수가 없었다.

윈도우+MFC 프로그래밍의 달인인 건 의심의 여지가 없고, 나중에 알고 보니 이 사람은 원래 수학 전공에다 컴퓨터 예술 쪽에도 심취해 있는 다재다능 엄친아였다. 이름이 좀 유럽풍인 것 같아 보이나, 실제로는 뉴욕에서 태어나서 자란 골수 미국인이라고 한다. 조상이 이민자?

링크를 건 곳은 저 사람의 2003년 시절 인터뷰이다.
고수 프로그래머로서의 조언도 여럿 담겨 있는데, 그 내용이 무척 공감이 간다.

- 최신 기술 동향은 놓치지 않되, 남들이 좋다고 하는 데에 소신 없이 절대 우루루 휩쓸려 따라가지 말라. 가령 클라이언트처럼 C/C++가 독보적인 분야가 있고, .NET 같은 곳이 더 유리한 분야가 따로 있을 뿐이다. 자신의 문제 해결에 가장 적합한 툴이나 기술을 잘 고르는 요령이 무엇보다도 중요하다. 그런 것들은 도구일 뿐이며 절대적인 우열이 존재하는 게 아니다.
- Win32 API가 존재하는 한.. 윈도우즈 운영체제가 밑바닥부터 새로 뒤바뀌지 않는 한, 너무나 클래식(?)한 C/C++이나 MFC 같은 것은.. 결코 그렇게 호락호락 없어지지 않는다. 더 업데이트가 안 되고 있다는 말은 그만큼 API가 성숙하고 안정화됐다는 뜻으로 오히려 다행스러운 현상인 것이다.
- 늘 목표를 명확히 하고 내가 무슨 문제를 해결해야 하고 그 목표 달성을 위해 무슨 도구를 쓰는 게 가장 최적일까를 고민하라. 디자인 과정을 소홀히 하지 말라.

민장(minjang.egloos.com) 님 블로그에서도 비슷한 요지의 말을 봤던 것 같다.

그리고.....

  "워드, 엑셀 같은 유명 소프트웨어에 들어있는 GUI 베껴서 따라 만드느라 시간 낭비 절대 하지 말라!" (그 시간에 실제 기능 구현에 필요한 자료구조/알고리즘 연구나 더 해라)

란 주문도 들어있다. ^^;;
아마 C++ Q&A 운영하면서 "나도 저기에 들어있는 그 기능, 그 UI 만들고 싶다. 어떡하면 좋은가?" 류의 뱁새가 황새 따라가려는 급의 문의를 엄청 많이 받았지 싶다.

* * * * *
  Too many programmers spend all their energy implementing some cutesy UI feature like docking windows or pink scrollbars because they saw it somewhere else. Microsoft has 5000 programmers to create animated paper-clips. You don't. Don't fall into the code envy trap!

  Don't get side-tracked implementing the latest GUI feature you saw in Word or Excel.
(그런 공룡 대기업들이나 부리는 '가진 자의 여유'를 당신이 따라할 여건은 안 된다는 걸 알아야 한다)
* * * * *

저건 우리나라의 유명한 비주얼 C++ 서적의 저자인 이 상엽 씨도 똑같은 말을 했다.

* * * * *
  그래도 예술적 가치가 있는 프로그램 제작에 열을 올린다면 좋은 이야기다. 그것도 아닌 것을 예술인냥 착각하고 움직이지는 절대 말라는 것이다. 예술적 가치가 없는 부분이 어떤것인가를 물어 볼것이다. 거 있지 않은가? MS 사에서 도움말 강아지 이리저리 왔다 갔다 한다고 자신의 프로그램에 강아지 만들어 넣는거...Visual C++의 워크 스페이스 창이 도킹 되었다가 떨어졌다 하는데 나두 이거 만들구 싶다 라는거...
예를 간단하게 들어서 MP3 에 있는 압축기술이나 음성인식 또는 지문인식 등의 기능이 예술이라고 볼수 있고 그냥 강아지 이리저리뛰어 다니는 것은 처음 만들어 내지 않는다면 것은 잡다구리 테크닉이다.
* * * * *

그래서 <날개셋> 한글 입력기의 편집기 프로그램은... 9년이 넘게 개발되고 버전이 5.5가 넘어선 지금까지도 완전 윈도우 95의 기본 컨트롤과 UI 요소만 사용하여 만들어져 있다. ^^;;; 편집기의 경우 과거 3.41 버전에서 MFC를 떼어내는 과정에서, 이제 도구모음줄이 도킹을 할 수 없게 바뀌었다. 그게 원래 MFC가 구현해 주던 일이었기 때문이다.

사실, 편집기를 실행해 보면 도구모음줄 아이콘들이 좀 중앙에 안 있고 메뉴, 즉 위쪽에 너무 바싹 붙었다는 인상을 받는데 이것도 딱히 바꿀 방법이 없다.
아이콘 사이에 임의의 크기로 여백을 내는 것도 MFC가 윈도우 프로시저를 다 서브클래싱해서 굉장히 지저분한 작업을 한 끝에 구현한 것이다. 이런 점에서 MFC는 단순히 윈도우 API wrapper 역할만 하는 것은 아님을 알 수 있다. 하지만 그런 거 따라하는 일에 너무 심취하지 말라는 얘기이다.

아쉽게도 이 사람은 작년(2008) 9월, 40대 후반의 나이로 세상을 떠났다. 사인은 밝혀져 있지 않다. 비주얼 C++ 2008의 내장 MSDN에는 2006년자로 작성된 그의 글을 볼 수 있는데, 이제 더는 그런 글을 접할 수 없으니 안타깝다.

Posted by 사무엘

2010/01/11 10:40 2010/01/11 10:40
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/104

« Previous : 1 : 2 : 3 : 4 : 5 : Next »

블로그 이미지

철도를 명절 때에나 떠오르는 4대 교통수단 중 하나로만 아는 것은, 예수님을 사대성인· 성인군자 중 하나로만 아는 것과 같다.

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2021/10   »
          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:
1674404
Today:
66
Yesterday:
622