« Previous : 1 : ... 122 : 123 : 124 : 125 : 126 : 127 : 128 : 129 : 130 : ... 221 : Next »

익사 사고 관련 정보

물에 빠져 죽는 건 비록 당장 눈에 띄는 상처나 핏자국 같은 건 없지만, 폐에 물이 찬 채로 숨을 못 쉬면서 굉장히 고통스럽게 죽는 죽음이다. 질식사의 일종이다.
허나, 여름에 강과 바다로 놀러 갔다가 거기서 살아서 못 돌아온 불운한 사람이 매년 전국에 수십~백수십 명가량은 된다.

익사 사고에 대해서 우리가 주의해야 하는 점이 몇 가지 있다.
먼저, (1) 물에 빠져서 허우적대는 사람은 주위를 향해 자기를 구해 달라는 티를 적극적으로 못 낸다는 것이다.
무슨 "불이야!" / "도둑이야!" 외치듯이, 혹은 육지에서 강도에게 쫓기는 것처럼 우렁차게 "사람살려!" 소리를 지르며 구조요청을 할 수가 없다.

익수자는 숨 차고 힘들어 죽을 것 같은 상태이다. 입을 조금이라도 벌렸다가는 물이 당장 입으로 흘러들어올 판이고, 말을 하면서 숨을 내뱉었다가는 몸의 밀도가 올라가 물 속으로 더욱 가라앉을 게 뻔한데 어떻게 그런 소리를 지를 수 있겠는가? 절대 불가능하다. 기껏해야 '헉~ 헉~ 꺼억' 같은 죽는 신음 소리밖에 못 내다가 조용히 꼬로록 가라앉고 익사할 뿐이다. 당연히 멀리서 들릴 리가 없고. 물에 빠져 죽어 가는 건, 물리적인 양상만 다를 뿐 차라리 목이 밧줄로 졸리고 있는 상황과 비슷하다고 봐야 한다.

그러니 물에 들어간 채로 뭔가 큰 소리로 외칠 여유가 있다는 건 그 자체가 이미 얼굴이 당장 물에 잠길 위험은 없으며 따라서 생명에도 사실상 지장이 없음을 의미한다. 이 정도면 드라마, 영화가 현실과는 다른 왜곡 중 하나에 해당되겠다. 마치 유언 장면처럼 말이다. (현실에서의 죽음, 특히 병사는 절대로 그렇게 낭만적이지 않다.)

현실에서 물에 빠져서 생명의 위협을 느끼고 허우적거리는 사람이, 그저 평범하게 수영을 하거나 물장난만 치는 사람과 다른 점은 대체로 다음과 같다. (위가 아니라 아래와 더 비슷하다는 뜻) 해수욕장에 비치된 안전 요원들은 멀리서 수면을 관찰하다가 "아, 저 사람이 지금 위급한 상황이구나"를 먼저 분간하는 훈련을 받는다. 즉, 청각이 아니라 시각을 이용한다.

사용자 삽입 이미지

  • 물이 입까지 간당간당 차 있으며 고개는 뒤로 젖혀져 있다.
  • 정상적인 수영을 하는 상태가 아니므로 물에서도 다리를 아래로 하고 꼿꼿하게 수직으로 서 있다. 물론 물 밖에서 익수자의 다리를 보기는 어렵겠지만, 물에 빠진 사람은 뭔가 "사다리"를 잡고 오르려는 듯한 손짓을 필사적으로 하므로 손을 봐도 된다.
  • 해수욕장에서 물에 빠진 사람이 시선을 해변이 아닌 심해 쪽으로 향하고 있을 리는 없다. 땅 또는 더 얕은 곳을 향하여 필사적으로 이동하려는 듯하지만 나아가질 못한다.
  • 앞머리가 이마나 눈을 가리고 있지만 수습을 못 한다. 힘이 더 빠지면 눈에 초점이 없거나 아예 눈을 감고 있다.

수영을 할 줄 모르고 물에 뜨지 못하는데 발이 바닥에 닿지를 않는다면 익수자는 얼마나 놀랄까? 그야말로 패닉에 빠진다. 그리고 소중한 친구나 가족, 친지가 물에 빠졌다는 걸 알게 되면 주변 사람들도 덩달아 만만찮게 패닉에 빠진다.
그런데 여기서 절대 유의해야 할 점이 하나 더 추가된다. (2) 자기가 수영깨나 할 줄 안다고 해서 섣불리 맨몸으로 구조하러 나서서는 안 된다. 최대한 침착해야 한다.

물 속에서 생명의 위협을 느낀 익수자는 최후의 발악을 하는 과정에서 힘이 평소보다 훨씬 더 세어지며, 구조자가 다가가면 튜브마냥 다짜고짜 붙잡고 매달린다. 익수자가 물귀신처럼 되는 셈. 이러다 일이 틀어지면 둘 다 물에 나란히 가라앉아서 익사하고 만다.
구조자의 입장에서는 하다못해 익수자가 물 좀 먹고 힘 빠져서 축 늘어진 뒤에 나서는 게 더 안전할 정도이다.

사람이 따로 나서는 게 아니라, 고정된 물건과 밧줄로 연결된 도구를 던져 주는 게 일단은 제일 좋다. 불가피하게 구조자가 직접 물에 뛰어들더라도 그런 것과 몸을 줄로 연결한 상태로 나서는 게 안전하다. 디즈니 포카혼타스에서도 초반부에 토머스가 배에서 떨어져 바닷물에 빠졌을 때, 존 스미스는 그렇게 자기를 배와 밧줄로 연결하고 나서 물에 뛰어들었다. 물놀이를 갈 때 긴 밧줄이 굉장히 요긴하게 쓰일 것 같다.

결국 사람을 물에서 구하는 건 감전된 사람을 구하는 것과도 비슷해 보인다. 어지간히 심하게 감전된 사람은 신경이 마비되어 자기 힘으로 사지를 전원으로부터 떼어낼 수가 없게 된다. 그런데 주변 사람이 어설프게 나섰다가는 구조자까지 같이 감전되기 쉬우니, 부도체 도구를 최대한 사용해서 구출해야 한다.

익사 사고는 더운 여름에 많이 발생하지만, 겨울에도 얼음 낚시 같은 걸 하러 갔는데 약한 살얼음을 잘못 밟는 바람에 물에 빠져서 발생하기도 한다. 이때는 마지막으로 유의해야 하는 점이 있다. (3) 물에서 빠져나온 뒤에 얼음판 위에서 절대로 두 발로 서지 말아야 한다는 것. 발바닥만 한 좁은 면적에 체중이 다 쏠리면, 옆의 얼음도 연달아 깨지기 쉽다. 그래서 기껏 물 밖으로 나왔는데 또 물에 빠지는 최악의 사태가 벌어진다.

그때는 누운 채로 옆으로 데굴데굴 구르면서 체중을 넓은 면적에 최대한 분산하면서 육지로 가야 한다. 옛날에 무슨 서바이벌 가이드 TV 프로에서도 다뤘던 내용이다.

물에 한번 빠져서 곤혹을 치른 적이 있는 사람이라면 부력의 소중함을 체험함과 동시에 비행기나 배를 탈 일이 있을 때 거기에 비치되어 있는 구명조끼도 다시 보게 될 듯하다. 구명조끼는 승객이 입수하더라도 얼굴과 코가 물에 잠기지 않게 정말 최소한의 부력을 만들어 주는 물건이다. 물에 떴다고 해도 구명조끼가 차가운 물로 인한 저체온증까지 예방해 주지는 않으니, 비상 상황에서 수면에 내던져진 승객은 최대한 어서 구조되거나 다른 부유물의 위로 올라가서 전신이 물 밖으로 나갈 수 있어야 한다.

끝으로, 본인이 개인적으로 굉장히 안타깝게 생각하는 익사 사고를 언급하며 글을 맺겠다.
바로 작년(2014) 8월에 경북 청도의 어느 계곡에서 승용차가 통째로 급류에 휩쓸려서 안에 있던 일가족 7명이 모조리 사망한 사고이다. 물놀이 익사는 아니고 일종의 자연재해인 폭우로 인한 사고 되겠다.

자동차가 지나가는 경로와 수직으로 물이 졸졸 흐르고 있었는데 그 날은 물이 불어서 물살이 꽤 강했던 모양이다. 차가 지나가는 게 좀 불안해 보여서 가장인 운전자가 혼자서 시범삼아 차를 몰고는 길을 건넜다가 되돌아와 보기까지 했다고 한다. 그런데 정작 가족들을 다 태우고 다시 건너는 순간 차는 그대로 휩쓸려서 옆으로 떠내려가 버렸다. 뒷차 운전자들이 보는 앞에서 말이다.
차는 2km가 넘게 떠내려 갔고, 탑승자들은 차에서 탈출할 엄두도 못 내고 안에서 모조리 익사했다.

사용자 삽입 이미지

하긴 작년 여름엔 물폭탄 폭우가 쏟아졌던 창원에서 시내버스가 불어난 물에 떠내려 가는 바람에 운전사와 승객이 모두 사망하기도 했다. 이때는 차체를 탈출한 사람들도 여럿 있었지만 그래도 생존하지는 못했으며, 차량보다 더 멀리 떨어진 하류에서 시신이 발견됐다.

이런 강한 흙탕물 앞에서는 아무리 수영 실력이 뛰어나도 아무 소용이 없었겠다..;;
도시에 사는 사람들은 물에 빠지는 것에 대한 대비보다는 당장 심폐소생술이나 소화기 및 제세동기 같은 물건의 사용법을 익혀 놓는 게 비상 응급 상황에서 더 유용히 쓰일 가능성이 높을 것이다.
기왕 이런 얘기가 나온 김에 우리 주변에 있는 그런 것들에 관심을 갖고 살펴보는 것도 나쁘지 않을 듯하다.

Posted by 사무엘

2015/05/01 08:38 2015/05/01 08:38
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1089

DllMain 이야기

Windows 운영체제에 실행 바이너리로는 잘 알다시피 EXE와 DLL이 있다.
EXE에 시작 지점이 WinMain이 있듯, DLL에는 DllMain이라는 시작 지점이 있어서 간단한 자신의 시작과 종료 같은 이벤트 통지를 받아서 초기화/마무리 작업을 할 수 있다. 이건 static library에는 없는 개념이다.

이 함수는 export table의 이름이나 ordinal로 탐색하는 게 아니라, PE 실행 파일 헤더 차원에서 주어져 있는 entry point의 주소로 진입하는 것이기 때문에 이름이 딱히 export되어 있지 않아도 된다.
물론 요즘 DLL은 코드 셔틀뿐만 아니라 리소스 셔틀의 역할도 많이 하기 때문에(특히 다국어 지원용!) 데이터만 추출한다는 플래그를 주고 LoadLibraryEx를 호출했거나 DLL 자체가 리소스 전용으로만 만들어졌다면 그런 DLL은 DllMain 함수가 없거나, 있더라도  실행되지 않는다.

실행 파일은 자신이 말 그대로 실행 주체이기 때문에 WinMain 함수의 리턴이 곧 해당 프로세스의 종료를 뜻한다. 그러나 DLL은 어떤 함수 호출이 있을 때에만 그때 그때 실행되는 형태이기 때문에 DllMain도 이벤트만 받고는 금세 리턴된다는 차이가 있다. 즉, 이런 점에서 DllMain은 WinMain보다는 윈도우 프로시저와 더 비슷한 형태이다.

DLL이라는 단어 자체가 대문자 이니셜이긴 하지만, 일단 C/C++ 컴파일러가 링크 때 참조하는 명칭은 DLLMain이 아니라 DllMain이다. L은 소문자로 쓰니 스펠링을 혼동하지 않도록 주의하자.
함수의 인자로는 자기 자신의 인스턴스(모듈) 핸들, 그리고 호출 이벤트(DWORD fdwReason)와 부가 인자(PVOID)가 들어오며, 리턴값은 BOOL이다.

이벤트는 4가지 종류가 있다.
일단, DLL이 처음 로딩되어 어떤 프로세스에 붙었을 때(DLL_PROCESS_ATTACH), 그리고 반대로 종료될 때(*_DETACH) 두 가지 경우가 당연히 포함된다. 이것은 LoadLibrary 내지 FreeLibrary 같은 런타임 동작으로 인한 결과일 수도 있고, 아니면 실행 파일의 import 테이블 차원에서 로딩되거나 해당 프로세스가 종료되어 딸린 DLL들이 모두 일괄 종료되는 경우일 수도 있다.

그 다음으로 DLL들은 특별히 그 프로세스 안에서 스레드가 새로 생성되거나 종료되었을 때도 통지를 꼬박꼬박 받는다. DLL_THREAD_ATTACH와 *_DETACH. 이건 응당 16비트 시절에는 없다가 나중에 새로 생긴 정보일 것이다. 그리고 당연한 말이지만 그 생성되거나 소멸되는 스레드의 실행 문맥으로 함수가 호출되므로 GetCurrentThreadId 같은 함수로 지금 스레드 ID 따위를 쉽게 조회해 볼 수 있다.

다만, 이 통지는 이 DLL이 로딩되기 전부터 이미 존재하던 스레드에 대해서는 오지 않으며, 스레드가 하나씩 차례대로 종료되는 게 아니라 메인 스레드가 실행이 끝나서 프로세스의 스레드들이 죄다 한꺼번에 종료될 때는 스레드별로 detach 통지가 오지 않는다. 그러므로 실행 중인 모든 스레드를 한데 조회하는 건 다른 API를 써서 해야 하며, 스레드별로 만들어 뒀던 리소스를 한꺼번에 해제하는 건--가령, 스레드별로 TLS 슬롯들이 가리키는 추가 할당 메모리-- *_PROCESS_DETACH 에다가도 마련해 둬야 한다.

DllMain의 추가 인자는 사실상 *_PROCESS_DETACH일 때에만 쓰인다.
이 DLL이 FreeLibrary로 인해서 레퍼런스 카운트가 0으로 떨어져서 동적으로 해제되는 것이면 추가 인자에는 0이 들어오고, 그렇지 않고 호스트 프로세스가 종료되면서 자명한 이유로 인해 같이 해제되는 것이면 1이 들어온다.
그렇기 때문에 *_PROECSS_DETACH일 때 어떤 값이 들어오느냐에 따라 혹시 모듈의 reference count leak가 있지는 않았는지를 체크할 수 있다.

운영체제가 기본 제공하는 시스템 DLL을 쓰는 게 아닌 이상, 내 EXE에서 쓰는 내 custom DLL은 대부분 동적으로 읽어들이고 해제하는 게 일반적이다. (delay-load 포함) 그런데 한번 읽었던 동일 DLL에 대해 자꾸 LoadLibrary를 반복 호출하면 해당 DLL의 레퍼런스 카운트가 증가하기 때문에 나중에 FreeLibrary를 한 번만 했을 땐 메모리에서 해제가 되지 않게 된다. 그 즉시 DLL_PROCESS_DETACH에 0이 들어오면서 DLL이 해제가 되지 않는다면 어딘가에 버그가 있다는 뜻이다.
결국 프로세스가 종료될 때가 돼서야 궁극적으로 해제가 되긴 하지만(이때는 1이 들어옴), 어쨌든 이런 것도 넓은 의미에서는 memory leak이 되는 셈이다. COM 오브젝트의 레퍼런스 카운트 관리와 완전히 똑같은 문제다.

*_PROCESS_ATTACH일 때에도 추가 인자에는 LoadLibrary에 의한 동적 로드일 때는 0, 그렇지 않고 import 테이블에 의한 정적 로드일 때는 1이나 이에 준하는 nonzero 값이 온다고는 하는데 본인은 그건 확인을 못 해 봤다.
그리고 DLL의 입장에서는 자신이 어떤 방식으로 로드되느냐에 따라 딱히 달리 동작하거나 자신의 로딩 방식을 굳이 알아야 할 일은 거의 없다.
이것 말고 단순히 스레드 생성/소멸 통지 때는 추가 인자는 쓰이지 않고 그냥 0만 온다.

한편, DllMain 함수의 리턴값은 일반적으로는 쓰이지 않고 1을 되돌리든 0을 되돌리든 무시된다.
이게 쓰이는 단 한 가지 상황은 *_PROCESS_ATTACH 때로, 이때는 함수가 TRUE를 되돌려야 DLL의 로딩이 성공한 것으로 간주된다. 안 그러면 이 DLL의 로딩은 거부되며 LoadLibrary의 리턴값은 NULL이 된다.

과거에 Visual C++이 2005와 2008 시절에 CRT와 MFC 라이브러리에 대해서 side-by-side assembly 방식을 강제 적용해서 이 방식으로 DLL이 로딩되지 않으면 로딩을 거부하곤 했는데, 그것 판단을 DllMain 함수에서 했다. 즉, 자신이 단순히 EXE와 같은 디렉터리나 운영체제 시스템 디렉터리에 있어서 로딩이 된 것이라면 DllMain이 고의로 FALSE를 되돌렸던 것이다.

그리고 중요한 점으로는.. DllMain의 *_PROCESS_* 실행 시점은 C++ 프로그램으로 비유하자면 main 함수가 실행되기도 전에 전역 클래스 객체의 생성자 내지 소멸자 함수가 실행된 것과 같다.
범용적인 DLL는 정말 기상천외한 EXE에 붙을 수도 있으며 DllMain의 호출 시점은 주변의 다른 DLL들이 모두 제대로 로딩 됐다고 장담할 수가 없는 때이다. 그렇기 때문에 이때는 다른 복잡한 초기화를 하지 말고 가능한 한 우리의 영원한 친구인 kernel32에 있는 함수만 호출해야 한다.

DllMain에서 할 만한 좋은 작업의 예로는 TLS 슬롯 할당, heap, 뮤텍스, 크리티컬 섹션, memory mapped file 같은 커널 오브젝트의 간단한 초기화 정도이다. 그 외에 user32나 gdi32까지 가는 작업은 권장되지 않으며 하물며 레지스트리나 COM/OLE 같은 시스템을 건드리는 정도만 되면 절대 금지이다.

이 함수 안에서 또 다른 DLL을 연쇄적으로 불러들이거나 해제하는 작업도 금물이다. 그게 가능하다면 참 편할 것 같지만 운영체제의 입장에서는 예측할 수 없는 엔트로피를 키우는 일이며 데드락을 야기할 수 있다. (특히 상호간에 LoadLibrary를 하는 경우는..?? =_=) 완전히 같은 예는 아니지만 생성자 안에서 가상 함수를 호출하는 게 왜 금지되어 있는지를 생각해 보자.

그러니 더 복잡하고 정교한 초기화나 마무리 작업은 DllMain에서 하지 말고, 함수를 따로 만든 뒤에 이 DLL의 사용자로 하여금 그걸 별도로 호출하게 해야 한다.
윈도우 컨트롤을 제공하는 DLL이라면 그냥 LoadLibrary를 하는 순간에 컨트롤의 윈도우 클래스들이 자동으로 등록돼 버리면 좋겠지만, 일단은 윈도우 클래스도 user 계층 관할이기 때문에 초기화를 별도의 함수에서 하는 게 바람직하다.

CWinApp 개체가 존재하는 MFC 확장 DLL의 경우, InitInstance와 ExitInstance가 호출되는 타이밍이 역시 DllMain이다. 그 때밖에 기회가 없으니 어찌 보면 당연한 얘기이다. 그러니 그때에도 사용하는 함수에 동일한 제약을 적용하여 주의해야 한다. 이건 Lyn 님의 블로그에서 발견한 정보임을 밝힌다. ^^

끝으로, 스레드와 관련하여 하나만 더 첨언하고 글을 맺겠다.
*_PROCESS_* 메시지야 어느 DLL에게나 자신의 생명 주기를 알리는 필수불가결한 메시지이겠지만, *_THREAD_*의 경우는 그렇지 않다. 모든 DLL들이 스레드 생성이나 소멸을 일일이 통보 받아야 할 필요는 없다.
그렇기 때문에 스레드 통보를 받을 필요가 없는 DLL은 DisableThreadLibraryCalls라는 함수를 호출함으로써 *_THREAD_*를 받지 않겠다고 운영체제에다 알려 주는 게 조금이나마 성능 향상에 도움이 된다.

얘는 물론 DllMain에서 곧장 호출해도 안전한 kernel32 함수이다. 수시로 켰다 껐다 할 필요가 있는 옵션이 아니어서 그런지, 한번 지정만 하고는 끝이다.
개인적으로는 이런 간단한 정보는 DllMain + DLL_PROCESS_ATTACH의 리턴값 플래그로 접수하는 게 좋지, 굳이 별도의 함수로 만들 필요가 있었나 싶은 생각이 든다. 리턴값이 0이면 로드 거부, 1이면 로드 허용, 2이면 로드 허용하되 앞으로 이 DLL은 스레드 통지는 안 함 정도로. 하지만 함수가 저렇게 만들어져 버렸으니 프로그래머의 입장에서는 그걸 적절히 사용만 하면 되겠다.

다만 C 라이브러리를 static 링크하는 DLL은 저 함수를 사용하지 않는 게 좋다. 자기는 스레드 통지를 사용하지 않더라도 배후의 CRT가 스레드 통지를 내부적으로 활용하기 때문이다. 물론 CRT를 DLL 링크하는 DLL에 대해서는 이런 제약이 적용되지 않는다.
그렇기 때문에 한 소스에 대해 CRT를 static/DLL 여러 방식으로 빌드하는 DLL이라면 #ifdef _DLL에 따라서 DisableThreadLibraryCalls 또는 __noop으로 대응하는 매크로 함수를 만들어 사용하는 게 바람직하다.

Posted by 사무엘

2015/04/28 08:20 2015/04/28 08:20
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1088

컴퓨터의 역사뿐만 아니라 자전거, 자동차 같은 교통수단의 역사를 추적하는 것도 흥미진진한 일이다.

1. 자전거

사용자 삽입 이미지

자전거는 두 말할 나위 없이 정말 위대한 발명품이다. 엔진 같은 게 전혀 없고 구조도 간단하지만, 단순 가마나 수레와 비교했을 때 여전히 기계적으로 마냥 쉽게만 만들 수 있는 물건이 아니다. 이런 이유로 인해 자전거의 역사는 자동차의 역사와 별 차이 없을 정도로 짧은 편이다. 아무리 일찍 잡아도 since 19세기이다.

자전거도 처음엔 발로 땅을 차면서 나아가다가 페달이 등장하고, 핸들과 브레이크가 등장하는 등 점진적으로 발전을 했다. 체인으로 뒷바퀴를 구동하고 고무 타이어까지 달린 현대 스타일의 자전거는 무려 1890년대는 돼서야 등장했다.

그런데 옛날 자전거 중에 꽤 주목할 만한 건 위의 사진에 등장하는 물건이다.
1870년대에 유럽에서 리즈 시절을 구가했으며, 옛날 자전거라 하면 곧장 떠오르는 '자전거의 상징'은 바로..
앞바퀴가 겁나게 큼직한 일명 'penny-farthing, 하이휠' 자전거이다. 그때는 지름이 거의 1.5m에 달하는 물건도 있었다고 한다. 검고 큼직한 마술사 모자를 쓴 19세기 영국 신사가 딱 타고 있어야 어울릴 것만 같은 바로 그 자전거.
자전거에 체인이나 변속기 같은 게 아직 없던 시절에 오로지 속도를 올리기 위해서 앞바퀴가 커졌다.

무슨 헬리콥터의 메인 로터와 테일 로터 같은 관계도 아니고..
저건 뒷바퀴에 자전거가 옆으로 쓰러지지 않게 고정하는 기능이라도 있지 않으면, 구조상 거의 외발자전거나 다름없다.
딱 보기에도 타고 내리기가 어렵고 위험하며, 탄 채로 정지해 있을 수가 없다. 당연히 젊은 성인 남자 정도의 전유물이었다.
진짜 외발보다 좋은 점은 딱 하나, 앞뒤로 자빠지지는 않겠다는 것뿐으로 보인다.

동력 전달의 측면에서 보면 저 자전거는 일종의 고단 고정이다.
정지 상태에서 첫 출발을 할 때나 오르막 오르는 건 정말 고역이었을 것 같다.
게다가 자명한 이유로 인해, 그 큰 바퀴를 상대적으로 짧은 크랭크암(= 같은 힘으로 밟아도 작은 토크)과 연결된 페달로 열나게 밟아야 한다.

그래도 이런 자전거가 자전거 경주 대회에서 다른 정상적인(?) 형태의 자전거들을 제치고 연전연승을 해서 성능을 입증받았고 10~20년간 유행을 탔다고는 한다. 속도를 위해 다른 편의성을 희생한 게 꽤 많았지만..;;

예전에 이색적인 하이브리드 교통수단이라든가 휴대용 교통수단에 대해 글을 쓴 적이 있었는데, 엔진이 없는 자전거도 생각보다 기상천외한 게 많다. 외발자전거인데 바퀴 위에 올라타는 게 아니라 커다란 바퀴 안에 들어가는 형태인 놈도 있고, 앉아서 운전하는 게 아니라 누워서 운전하는 자전거도 있다. 이런 것들에 대해서도 언젠가 글을 쓸 일이 있으면 좋겠다.

안 그래도 며칠 전이 장애인의 날이기도 했는데, 당장 휠체어만 보더라도 사람이 팔로 열나게 바퀴를 돌려야 하는 수동 휠체어는 뒷바퀴가 겁나게 큼직한 반면, 전동 휠체어는 바퀴가 아주 작다. 수동과 전동의 외형상의 가장 큰 차이가 이거라고 해도 과언이 아닌데.. 왜 이런 차이가 존재하는지는 각자 한번 생각해 보자.

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

2. 삼륜차

엔진이 달려서 단순히 차가 아니라 '자동차'라고 불릴 수 있는 물건이 최초로 개발된 것은 우리 주변에서 흔히 볼 수 있는 사륜차 형태가 아니었다. 그럼 오토바이 같은 이륜차냐 하면 그것도 아닌 것이, 엔진을 그 정도로 작고 균형 잡기 쉽게 만드는 것이 고역이었다.

사륜차도 아니고 이륜차도 아니면 남는 것은 바로 삼륜차이다. 프랑스의 퀴뇨가 1770년에 고안한 시속 4km짜리 증기 자동차는 삼륜차였고, 세계 최초의 가솔린 엔진 자동차인 벤츠 모터바겐도 삼륜차였다.
우리나라도 한때는 기아 산업에서 삼륜차를 생산한 적이 있으며, 지금도 동남아 개발도상국에서는 서민들이 온전한 형태의 4륜 승용차를 지를 구매력이 안 되기 때문에 오토바이 내지 툭툭이라고 불리는 삼륜차가 널리 굴러다니고 있다.

사용자 삽입 이미지

퀴뇨의 삼륜차를 보면 물탱크를 앞바퀴보다도 앞에다 배치한 게 무게 배치가 영 불안해 보인다. 물이 가득 차 있으면 차체가 앞으로 들려 올라갈 것만 같다.

사용자 삽입 이미지

벤츠 모터바겐은 1886년에 첫 생산되었는데, 지금도 재현품이 남아 있다. 차의 후방에서 뭘 힘을 줘서 빙글빙글 돌려 주면 시동이 걸려서 엔진이 '툭툭툭툭~!' 소리를 내며 돌아간다. 그 뒤 사람이 잽싸게 올라타서 기어를 중립에서 전진으로 바꾸면 엔진 회전이 바퀴에 전달되어 차가 달리기 시작한다. '툭' 소리는 실린더에서 미량의 가솔린이 폭발하면서 나는 소리일 테고. (☞ 관련 동영상)

4행정 엔진은 폭발이 크랭크축의 2회전마다 한 번 발생하니, 저 차의 엔진의 실제 회전수는 단위 시간당 '툭툭툭툭' 소리가 나는 횟수의 두 배일 것이다.
차가 가기 시작하면 아무래도 엔진에 걸리는 부하가 커지니 엔진 회전수가 순간적으로 약간 감소한다.

영문 위키백과의 설명에 따르면, 최초의 모터바겐은 954cc 단(1)기통 4행정 가솔린 엔진을 얹어서 최대 출력이 대략 2/3마력이고 최대 속도가 16km/h 정도였다고 한다. 요즘 저 모터바겐보다도 배기량이 작은 경차조차 50~70마력대의 출력이 나오니(얼추 거의 12cc당 1마력??) 자동차의 기술 발전도 컴퓨터의 기술 발전만큼이나 드라마틱하다는 걸 알 수 있다.

그리고 요즘 자동차는 실린더 하나의 부피를 저렇게 우악스럽게 크게 잡지 않는다. 경차는 3기통, 소형~중형차는 4기통, 대형차 이상은 6~8기통을 쓴다. (1) 한 실린더에서 한 번에 지나치게 많은 연료를 폭발시키지 않게 하고, 또 (2) 4행정 엔진은 폭발이 일어나는 회전과 폭발이 없는 회전 때에 산출되는 토크가 동일하지는 않기 때문에 서로 다른 연소 상태인 실린더를 여럿 두는 것이다. 그래야 시동이 걸린 엔진의 소음과 진동이 줄어들고 승차감도 더 부드러워진다.

게다가 그걸로도 충분치 않기 때문에 자동차나 오토바이에는 머플러가 장착되어서 엔진 소음을 추가로 상쇄시킨다. 이런 메커니즘 덕분에 툭툭툭툭 소리가 상대적으로 부드러운 부르릉(?) 소리로 바뀌는 것이다. 가솔린 엔진보다 진동이 더 심한 디젤 엔진은 털털털 정도로나 바뀌지만, 그래도 받침이 무성음에서 유성음으로 바뀌었다.

그리고 여담이지만, 디젤도 아닌 가솔린 엔진이 시동 직후부터 내부에서 너무 심한 떨림이 느껴지고 '들들들~ 두두두두 / 따다다다'거린다면 그건 아마 노킹 현상을 의심해야 할 것이다.
연료가 어떤 이유로 인해 실린더 안에서 정확하게 폭발을 해야 할 타이밍보다 먼저 폭발하는 바람에 이런 일이 벌어지는데, 엔진의 내구성에는 굉장한 악영향을 초래한다. 유연이니 무연 휘발유니 하는 것도 이 노킹 현상을 줄이려고 연료의 화학적 성질을 튜닝하는 첨가제를 나타내는 명칭이다.

우리나라 현대 자동차의 경우, '쏘나타'나 '그랜저'라는 승용차 브랜드명은 1980대 이래로 지금까지 쭉 잘 우려먹고 있다. 그러나 그보다 먼저 만들었던 차량인 포니, 엑셀, 스텔라 같은 부류는 그저 구형 싸구려 이미지로만 치부하면서 자기들이 옛날에 만들었던 차량에 대해서 뭔가 정통성을 존중하려는 노력을 너무 하지 않는 것 같다. 이에 대해서는 이미 국내의 몇몇 자동차 매니아들이 아쉬움을 표현한 적이 있다.

월트 디즈니 <미녀와 야수>에 나오는 모리스(벨의 아버지, 발명가)도 증기 기관 삼륜차를 발명한 듯하다. =_=; 비행기도 엔진이 2개도 4개도 아닌 삼발 엔진기는 뭔가 과도기스러운 물건으로 인식되듯, 삼륜차 역시 그런 위상을 차지하고 있다.

사용자 삽입 이미지

3. 타이어와 공기 주입구

다음으로 좀 다른 얘기를 꺼내 보겠다.
요즘 자전거와 자동차, 그리고 심지어 비행기의 랜딩기어에 이르기까지 단단한 땅 위를 굴러가는 바퀴의 테두리엔 거의 다 고무 타이어가 장착돼 있다.
똑같이 시꺼먼 합성고무인 것 같아도 타이어도 역사적으로 속에 튜브가 따로 없어도 공기가 새지 않는 튜브리스 타이어, 그리고 접지력과 주행 연비가 더 우수한 래디얼 타이어 같은 더 좋은 물건이 역사적으로 꾸준히 개발되어 왔다.

여기서 중요한 것은 타이어 내부에 충분한 공기(압)를 유지해야 한다는 것이다. 공기가 부족하면 타이어의 아랫부분이 차체의 무게 때문에 점점 짓눌리게 되는데, 그러면 바퀴가 점점 잘 굴러가지 않기 시작한다. 힘이 많이 든다. 자전거만 운전해 봐도 타이어에 공기가 충분할 때와 그렇지 않을 때 힘이 드는 정도는 천차만별로 달라진다.

또한 타이어에 공기가 충분치 못하면 타이어는 주행 중에 열도 더 많이 받으며, 이 때문에 더운 여름에는 고속 주행 중에 갑자기 타이어가 펑크까지 날 수 있다. 그러면 차가 한데 쏠리고 제동력과 조향력을 상실하여 큰 사고를 초래할 수 있다.
타이어의 공기압에 따라서 주행 중인 차량에 왜 그런 상태 차이가 발생하는지 단순한 직관 이상으로 물리적으로(아마도 유체역학적으로) 숫자와 공식을 이용해서 정량적으로 설명해 보라고 하면 난 잘 모르겠다. 그저 접지 면적에 차이가 생겨서 그러는지?

그리고 내가 타이어의 물리적인 특성에 대해서 아직도 제대로 이해를 못 하고 있는 건 타이어의 공기 저장 능력이다.
타이어는 구멍이 났다고 해서 무슨 수영 튜브나 풍선에서 바람이 빠지듯이 즉시 쪼그라들지는 않는다. 그런데 한편으로는 완전히 밀폐된 물건도 아니다. 아주 천천히 바람이 새긴 하는 것 같다. 자전거 타이어의 경우 수시로 바람을 보충해 줘야 하기 때문이다. 저질 싸구려 타이어여서 그런지는 잘 모르겠다.

타이어의 재질 자체뿐만 아니라 공기를 주입하는 단자도 사실 한 종류만 있는 게 아니다.
자전거에서 흔히 많이 쓰이는 가장 저렴하고 단순한 단자는 '던롭' 방식이다. 검은 고무 마개(밸브 캡)로 입구를 봉인할 수 있지만 마개를 제거하는 것도 굉장히 쉽게 할 수 있으며 마개가 없다고 해서 당장 타이어가 바람이 술술 빠지지는 않는다.
그렇다고 해서 마개 없이 자전거를 달리기도 꺼림칙하고.. 마개의 정확한 역할이 무엇인지가 궁금하다.

이것 말고 '슈레이더' 방식 단자는 던롭보다는 더 고급형이다. 단자의 중앙에는 작은 핀이 꽂혀 있으며, 이 핀을 누르고 있는 동안은 밀폐 상태가 풀려서 공기가 빠지고 반대로 공기 보충도 가능한 상태가 된다. 마개는 이 핀이 외부 환경에 의해 손상되지 않게 하는 역할을 하며, 마개 자체가 타이어 내부를 개방하지는 않는다.

얘는 '던롭' 방식보다 폐쇄 상태와 개방 상태가 더 확실히 구분되며 더 고압의 공기 주입이 가능하기 때문에 일단 자동차 타이어에 이 단자가 쓰인다. 그리고 자전거에도 일부 고급 모델의 타이어에 쓰이고 있다고 한다. 단, 던롭 방식만치 아무 펌프로나 쉽게 공기 보충을 할 수는 없는 듯하다.

그리고 또 고급 산악 자전거에는 '프레스타' 방식 단자도 쓰이는데, 이것은 슈레이더에서 핀 역할을 하는 게 별도로 돌출되어 있는 작은 나사이다. 단자 위에 또 나사가 들어있기 때문에 마개가 던롭 단자의 마개보다 더 길쭉한 편이다.

공기 주입구도 이런 차이가 있는 게 마치 컴퓨터에서 신호의 입출력용으로 쓰이는 각종 아날로그/디지털 단자들 규격을 보는 듯한 느낌이다.
타이어와 공기 주입구 역시 자동차와 자전거 자체와 역사를 함께 하며 발전해 왔을 것이다.

Posted by 사무엘

2015/04/25 08:23 2015/04/25 08:23
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1087

※ 자동· 수동 공통으로 변속기에 적용되는 주의 사항

(1) 차가 완전히 정차하기 전에 차의 진행 방향을 반대쪽으로 바꾸는 변속을 하지 말 것. (전진 ↔ 후진)
주차 중에 성질이 급해지면 이런 행동을 하기 쉬운데, 절대 금물이다. 변속기를 망가뜨리는 지름길이다.

(2) 오르막(혹은 이와 비슷하게 차를 전진하지 못하게 막는 외부 저항)과 엔진 동력이 상쇄 평형을 이루는 상태로 차를 정지시키지 말 것.
자동은 D 상태이고 수동의 경우 반클러치 상태를 말한다. 이 역시 변속기에 굉장한 무리를 가하는 행위이기 때문에 하지 말아야 한다. 정석대로 중립+브레이크 상태로 정지해야 한다.

그리고 (2)의 연장선상에서 생각했을 때, 자동 변속기의 경우 신호 대기 때문에 차를 수십 초 이상 세울 일이 있으면 좀 귀찮더라도 변속기를 N으로 꼬박꼬박 바꿔 주는 게 좋은 걸로 본인은 안다.
시동 유지를 위한 엔진 공회전만 해도 성인 남자 5명이 탄 1~2톤짜리 쇳덩이를 슬금슬금 기어가게 할 수 있는 강한 힘이며, 사람 한 명이 이를 막을 수가 없다. 그런데 그걸 브레이크가 막고 있고 그 부하를 변속기의 토크 컨버터가 받고 있는 건 결코 좋은 상태라고 할 수 없기 때문이다.

똑같이 정차하고 있어도 D가 아닌 N으로 하는 게 연비까지 미세하게 더 좋다는 걸, 무슨 케이블 TV에서 자동차 박사 김 필수 교수가 실험 결과까지 제시하며 입증하는 걸 본 기억이 있다. 다만, 요즘 자동차들은 ECU가 더 똑똑해졌기 때문에 외력이 아니라 브레이크를 밟아서 서고 있는 정도라면 자동으로 '중립'과 동일한 상태로 동작시켜 주기까지 한다는 반론도 있다.

※ 락업 클러치

수동 변속기 차량에 '반클러치'라는 (자동차 회사에서는 비추하는) 꼼수가 있다면, 요즘 자동 변속기 차량에는 '락업 클러치'라는 공인 상태 내지 테크닉이 있다.

아무래도 주행 연비를 올리려면 액셀이나 브레이크를 가능한 한 밟지 말고 최대한 관성만으로 슬금슬금 부드럽게 가게 하는 게 좋다. 이건 뭐 자전거만 타 봐도 경험적으로 체득할 수 있는 물리 법칙이다.
그런데 가감속을 할 일이 없는 구간에서 액셀을 1/3 정도만 살짝 밟은 상태에서 순항 상태를 수 초간 유지하고 있으면, 엔진 rpm이 살짝 내려가고 순간연비도 액셀을 밟고 있는 것치고는 올라가면서 자동차의 ECU가 나름 최적화 상태를 구축해 준다고 한다.

동력 손실이 있는 비효율적인 토크 컨버터를 거치지 않고, 엔진과 크랭크축이 그 기어비로 직결이 되는데 그걸 락업 클러치 상태라고 한다. 나도 경험적으로 그런 상태가 있다는 건 알고 있었는데 그게 그렇게 된 상태였구나.
심지어는 관성 주행을 하다가 서서히 감소한 속도를 회복하기 위해 주기적으로 액셀을 밟는 것보다, 차라리 락업 클러치 상태로 등속을 유지하고 있는 게 연비가 더 좋을 정도라고...;; 응?

아까 그 D/N 문제도 그렇고 락업 클러치도, 프로그래밍으로 치면, 컴파일러나 CPU가 더 좋아진 덕분에 어설프게 온갖 포인터 테크닉으로 사람이 골치아프게 최적화하는 것보다 차라리 서로 다른 변수를 성큼성큼 불러다 쓰고 그걸 병렬화나 잘 시키는 게 성능이 더 좋아지는 것과 같은 그런 발상의 전환인 듯하다. 마치 멀티코어 아키텍처 하에서는 xor 꼼수가 더 병렬화가 안 되고 오히려 더 불리해진 것처럼 말이다.

※ 속도계에서 시속 30km에 찍힌 빨간 눈금의 정체

자동차 계기판에서 엔진 회전수 타코미터에 red zone이 있는 것이야 누구나 그 이유를 수긍할 것이다.
최대출력이 나오는 RPM을 넘어서도록 너무 세게 밟으면 엔진이 과열 등 여러 무리를 받기 쉽다. 레드 존은 당장 회전수는 높아도 토크가 이미 크게 떨어져 비실비실해진 상태이기도 하다. 그리고 사실은, 액셀을 밟은 게 아니라 단순히 기어비가 너무 낮아서 바퀴 회전 속도를 따라 덩달아 야기된 과회전(내리막에서 엔진 브레이크)도 마찬가지로 해롭다.

그런데 현기차는 타코미터뿐만 아니라 속도계를 보면 시속 30km에 빨간 눈금이 콕 찍혀 있다.
타코미터의 red zone은 '영역'인 반면, 이건 '점'이다.
성경에서 창세기 1장을 읽으면서 왜 둘째 날에만 "보기 좋았더라"가 없는지 의문을 품을 정도의 눈썰미라면..
자동차를 운전하면서도 이런 궁금한 점을 찾아낼 수 있다.

인터넷 검색만 해 보면 해답을 바로 알 수 있듯, 이 30km/h는 자동차의 주행 성능이나 연비 같은 것과는 아무 관계가 없는 표시이다.
단지 스쿨존에서 이 속도를 초과해서 밟지 말라고 운전자에게 주의를 주는 차원에서 계기판에다 넣어 둔 애드립이라고 한다.

※ 난폭운전

내 경험상, 스케줄과 회전율에 쫓기는 버스나 트럭의 직업 운전 기사 말고 일반 자가용 운전자가 운전 습관이 점점 난폭해지는 이유는
(1) 똑같이 직진하는데 왜 내 차선만 차가 안 가고 막혀?
(2) 왜 하필 내가 갈 때만 자꾸 신호에 걸려?

에 대한 피해의식과 보상심리 때문으로 보인다. 딴 거 없다.
저 의문 제기가 정말 사실이고 합리적일 수도 있는 반면, 정말로 아무 치우침 없는 복불복일 뿐이고 남들도 다 별 차이 없이 겪는 현상인데 자기만 그렇게 망상을 하는 것일 수도 있다.
자주 다니는 길이라면 운전 경험에 대해서 통계에 기반한 데이터와 좀 더 똑똑한 신호 패턴 분석이 필요해 보인다.

※ 자동차 내비

내 경험상, 내비를 켜고도 길을 잘못 드는 상황은 다음과 같다.

  1. 좌우 중 어느 한쪽으로 가긴 해야 하는데 그 쪽으로도 길이 여러 갈래여서 더 안쪽으로 도는 길을 잘못 선택함
  2. 어느 한쪽으로 가는 길이 일정 간격을 두고 여러 개 등장하는데 그걸 잘못 선택함. (주로, 가야 하는 길보다 먼저 등장하는 분기로 진입)
  3. 복잡한 분기가 계속되는데, 분기 후에 다음 분기에 맞춰 차를 어느 차선에다 둬야 할지를 알지 못해서 다음 분기를 실패함

내비가 어떤 목적지의 근처까지 가는 길을 안내는 하는데 목적지의 반대편 차선으로 가는 길을 안내해서 실제로는 유턴이 필요한 등 불편한 경우가 종종 있었다. 이에 대한 보정을 하는 방법이 있으면 좋겠다.

또한, 유료 도로를 최소화하는 옵션으로 경로를 검색했더라도 경로가 유료 도로보다 가성비가 현저히 떨어지고 톨비 절약보다 기름값 손실이 더 큰 지경이라면 이런 제약은 적용하지 않는 게 좋다고 사용자에게 권하는 기능이 있어야겠다.
이런 게 내비게이션을 더 똑똑하게 만드는 방법일 것이다.

※ 기타

20여 년 전, 부모님이 1500cc짜리 소형차를 굴리시던 시절에 본인은 수동 3~5단이 각각 시속 35, 45, 60km 이상부터 권장되는 기어비라고 취급 설명서에서 봤었다. 그리고 시속 80 정도로 달리면 엔진 회전수가 2000rpm을 넘어가고, 100 이상은 3000rpm 근처까지 갔던 걸로 기억한다.

하지만 요즘 차는 확실히 옛날보다 더 낮은 속도에서 고단으로 달릴 수 있고, 더 낮은 rpm에서 높은 속도가 나온다.
차가 힘이 얼마나 좋은지를 따질 때 흔히 정지 상태에서 시속 100까지 도달하는 데 걸리는 '제로백'을 거론하곤 하는데, 본인은 그것보다는 지구력에 가까운 잣대에 더 관심이 있다. 평지에서 시속 100으로 달릴 때의 엔진 rpm이 얼마 정도 되느냐 하는 것.

내 차는 2000+알파 rpm 정도 되는 듯하다. 경제 속도에 도달할 때까지는 엔진 회전수가 1000~2000대에서 왔다 갔다 하면서 저단에서 고단으로 변속이 되고, 그보다 속도가 더 올라가면 이제는 더 고단으로 변속이 되지 않고 차의 속도에 비례하여 엔진 rpm도 쭉쭉 올라간다.

하지만 힘 좋은 디젤 차량은 당연히 더 낮은 rpm에서 시속 100이 거뜬히 나오고, 에쿠스 같은 워낙 고배기량 고성능 고급 차량도 1000rpm대 중후반에서 바로 시속 100을 찍는다고 한다. 내가 직접 본 적은 없다. 배기량 짬밥이 어딜 가는 게 아니긴 하다. 똑같이 5명이 타는 승용차이더라도 오르막이나 고속 주행에서 경차하고는 차이가 확연히 나게 돼 있다.

또한 요즘 자동차들은 그렇게 강한 힘이 필요할 때는 연료를 마구 태워서라도 강한 힘을 뿜어 내지만, 반대로 신호 대기 같은 정차 공회전 중에는 시동 유지만 가능한 수준으로 연료를 극미량만 뿌리면서 연비를 최대화하게 만들어진다. 컴퓨터가 아이들링 중일 때 전력 소모를 최소화하는 것과 같은 맥락이다.

연비를 최대화했다는 말은 엔진의 출력도 최소라는 뜻이다. 그렇기 때문에 자동 변속기라 하더라도 오르막 정지 상태에서 브레이크에서 발을 떼면 순간 차가 뒤로 밀릴 수 있다. 사람이 자전거를 모는 상황을 생각해 보더라도 처음 출발하는 게 어려우며, 오르막은 조금만 있어도 왕창 힘든 게 느껴진다. 사람에게 힘든 건 자동차에게도 똑같이 힘들다.

또한, 신호 대기 때문에 정차와 출발이 잦으면, 정지 상태에서 차가 처음 출발할 때 연비가 정말 안습하기 때문에(큰 힘+저속) 아무리 아이들링 타임의 연료 소모를 줄인다 하더라도 평균 연비가 저하되는 건 어쩔 수 없다. 시내가 막힐 때는 직선 최단 거리보다 심지어 2배가 넘게 우회하더라도 안 막히는 자동차 전용 도로를 이용하는 게 답이다.

Posted by 사무엘

2015/04/22 08:22 2015/04/22 08:22
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1086

※ 컴퓨터 & 프로그래밍

1.
예전에 본인은 시스템 종료 중에라도 사용자가 무슨 동작을 취하면, 컴을 아주 꺼 버리는 시스템 종료가 아니라 그 뒤 '재시작'으로 종료 모드를 바꾸는 기능이 있으면 좋겠다는 제안을 한 적이 있다. 그것과 비슷한 제안인지도 모르겠는데, 또 하나 아이디어를 내자면 이렇다. 사용자가 한동안 컴퓨터를 건드리지 않아서 모니터가 꺼지거나 컴퓨터가 절전· 최대 절전· 종료 등으로 바뀌게 되면, 그 모드로 진입하기 전에 화면에 10초나 5초 정도 카운트다운을 좀 띄웠으면 좋겠다.

프레젠테이션을 할 때처럼 화면을 빤히 보고 있으면서 키보드· 마우스만 안 건드리고 있는데 화면이 갑자기 꺼져 버려서 당황한 적이 여러 번 있었다. 화면 보호기 정도는 카운트다운 없이 바로 진입해도 상관 없겠지만 아예 하드웨어적인 변동이 생기는 저런 모드는 예고가 있으면 좋겠다.

2.
동영상 엔진인 '코덱'과 과거의 컴퓨터 통신 장비인 '모뎀'이 정확히 같은 조어법에 의해 거의 같은 구조의 이니셜을 가진 단어이구나.

3.
식당에서 주문을 한 뒤에야 "아 손님, 죄송하지만 재료가 떨어져서 그 메뉴는 지금 제공이 안 됩니다" 이런 메시지를 받으면 허탈하잖아. 애초에 메뉴판에 그런 메뉴는 disable된 상태로 시각 피드백이 있으면 좋겠다.

4.
공동 작업을 하는 코드의 명칭에 영어 스펠링이 틀린 게 많아서 작업에 지장을 적지 않게 받은 적이 있었다. 검색이 안 되기 때문이다. 이쯤에서 분명 availableItem이런 단어가 있는 걸 봤었는데 나중에 보니 avalible이라고 돼 있는 식.
이건 당장 버그나 성능 같은 동작과 직접적인 관련이 있지는 않지만, 그래도 또 다른 형태의 민폐이다. 도서관으로 치면, 책을 보고 나서는 자기 분류 코드상으로 있어야 할 곳이 아닌 엉뚱한 곳에다 책을 꽂은 것과 같다. "잘못 꽂힌 책은 없는 책과 같습니다. 정리는 사서가 알아서 할 테니까 열람하신 책은 그냥 여기에 놔 두세요" ;;;;

5.
관광 가이드를 매뉴얼과 스케줄 대로 승객들을 안내하는 컴퓨터 프로그램에다가 비유한다면, 이 사람이 수행하는 프로그램의 소스 코드는 정말 그야말로 try ... catch문으로 빽빽이 무장하고 있어야겠구나 하는 생각이 들었다.
누군가가 갑자기 아플 때, 뭔 물건을 놔 두고 왔을 때, 여권을 잃어버렸을 때, 긴급한 사고가 발생했을 때, 일행 중 일부가 없어져서 못 찾을 때 등등.. 그 어떤 예외 상황에서도 패닉과 스케줄 펑크를 최소화하는 방향으로 의연히 대처가 가능해야겠다.

6.
Windows 환경에서 응용 프로그램이 자기 영역으로 사용할 수 있는 메모리 주소는 64KB 이상부터이다. NULL 포인터인 0자체뿐만이 아니라 첫 64KB는 가상 메모리 영역 설계 차원에서 봉인되어 있으며, 이 주소에 메모리를 읽거나 쓰는 건 무조건 에러가 난다. 사실, 0 자체뿐만 아니라 64KB 정도까지는 막혀 있어야 NULL포인터 자체뿐만 아니라 NULL로부터 구조체 멤버를 참조한 포인터도 에러로 처리될 수 있을 것이다. ((POINT *)NULL)->y처럼.

아울러, 과거의 Windows 9x는 이보다 제약이 더 커서 64KB가 아니라 상위 4MB까지가 추가로 막혀 있었다. 64K부터 4M까지의 영역은 16비트 프로그램(도스용 & Windows용 모두)이 사용한다. (☞ 이에 대한 더 자세한 설명)

이런 이유로 인해 전통적으로 32비트 Windows 프로그램들은 시작 주소(preferred base)가 딱 4MB로 맞춰지곤 했다. NT 계열에서는 꼭 4MB가 아니라 64KB 이상 아무 지점이어도 상관이 없지만, 4MB 이상이어야 윈도 9x와 NT계열에서 모두 실행 가능하기 때문이다.

그런데 이건 오늘날까지도 하드디스크가 C로 시작하는 디스크 드라이브 관행과도 정확히 일치하는 것 같다.
플로피 디스크가 완전히 없어졌음에도 불구하고 A, B 드라이브는 사실상 결번으로 남아 있으니 말이다. 요즘은 하다못해 USB 메모리 드라이브를 거기에다 할당해도 될 것 같은데!

※ 알고리즘

7.
longest common subsequence를 구하는 문제와 longest increasing subsequence를 구하는 문제는 서로 관련이 있는 무척 흥미로운 문제인 것 같다.
가만히 생각해 보니, 후자는 임의의 sequence와, 그 입력을 오름차순으로 정렬한 sequence와의 longest common subsequence를 구하는 것과 같다. 그러므로 후자는 전자 문제로 다항 시간 만에 변환 가능한 special case이다.

두 문제는 일단 다이나믹 프로그래밍으로 O(n^2)의 복잡도로 풀 수 있지만, 더 작고 특수한 케이스인 후자는 O(n log n)의 해법도 있다.
전자 문제는 문장의 정확도를 구하는 알고리즘, 소스 코드의 diff 툴 등 활용되는 분야가 굉장히 많다. 지금은 어떤가 모르겠는데 내 때에는 국제 정보 올림피아드의 첫째 날 1번 문제가 해법이 이 형태로 귀착되는 경우도 종종 있었다. 1999년도의 꽃병 문제는 대놓고 저런 타입이었고, 2000년도의 palindrome 문제도 자신과 자신을 역순으로 뒤집은 단어와의 longest common subsequence를 구하는 것과 동일하다.

8.
엑셀에서 파이 모양 차트를 그리면 아이템별로 파랑, 빨강, 주황 등 알록달록한 색깔이 배당되어 차트가 그려진다.
그런데 최초의 색깔인 파랑부터 아이템 N에 이르기까지, 색깔을 선별하는 방식이 과연 무엇일까?
Office 2003까지는 뭔가 보라색 위주의 우중충하고 칙칙한 색깔 위주였는데 2007부터는 그래도 예전보다 훨씬 더 세련되게 바뀌었다.

이건 뭔가 RGB나 hue 같은 색공간에서 최대한 균등하게, 마치 흑에서 백으로 디더링 픽셀을 하나씩 채워 나가듯이 색깔을 뽑아낸 것 같다(관련 링크). 그 구체적인 알고리즘이 궁금하다.
그리고, 이런 픽셀 채우기 문제의 domain을 2차원 평면이 아니라 3차원 공간으로 확장하면 문제의 난이도가 어찌 되는지도 궁금하다.

※ 자동차

9.
자동차 차량 취급 설명서의 각종 선택사양에만 적용되는 설명들은 C/C++ 코드에서 #if #endif 전처리기에 대한 아주 좋은 예시라 여겨진다.

10.
오늘날 "일찍 나는 새가 벌레를 잡는다"보다 훨씬 더 현실적으로 와 닿는 말은 "일찍 움직이는 차가 주차 자리를 차지한다"라고 해도 과언이 아닐 것이다.

※ 기타 미분류

11.
공항 안에 개인 물품 보관함 같은 게 있으면 단독 여행 시에 유용하겠다는 생각이 든다. 이곳과 계절이 크게 다른 지역을 여행 갈 때 지금 입은 옷을 보관해 놓는다거나, 반입 금지 내지 무게 제한에 걸린 물건을 귀국 때까지 임시로 보관할 수 있게 말이다. 물론 후자의 경우는 당사자가 보관함까지 갔다가 돌아오는 게 곤란하니, 추가 비용을 부담해서 보관 대행을 맡길 수 있어야 하겠다.

12.
비행기와 열차의 큰 차이:
열차는 출발 15분 전부터 승강장으로 입장이 가능한 반면, 비행기는 출발 15분 전에 탑승이 종료된다는 것이다.
그리고 여담인데, 내 경험상 인천 공항을 출발한 비행기는 견인차에 끌려 터미널을 떠난 순간부터 활주로에 진입하여 이륙을 시작할 때까지도 거의 정확히 15분이 소요된다.

13.
"바탕체 레귤러"라는 서체 이름을 보고는 바탕체 볼드가 아니라
"바탕체 라지"가 순간적으로 먼저 떠올랐다.
요즘 커피를 너무 많이 마셨나 보다....? =_=;;
하긴, 아메리카노가 생각이 안 나서 순간 "아프리카노요"라고 주문을 했다는 사람 얘기도 있으니..;;

14.
몇 년 전부터 우리나라에서는 우측통행, 도로명 주소 등 일상생활과 직접적인 관계가 있는 여러 규범이 바뀌었으며, 이런 차원에서 단위도 비표준 단위가 통상적으로 쓰이던 곳까지 SI 단위가 강제 추진되었다.
고기의 무게는 오래 전부터 '근'이 거의 전멸하고 100그램 단위로 다 정착을 한 것 같지만 여전히 오락가락하는 곳은 부동산에서 다루는 건물이나 땅의 면적이다.

그런데 내가 보기에도 '1평'을 '3.3제곱미터'로 바꿔서 실생활에서 유리한 게 없다. 부자연스러울 뿐만 아니라 음절수도 너무 많아서 발음하기가 불편하다. 바꿀 거면 사람이 실제로 생각하는 넓이의 덩어리도 1제곱미터나 10제곱미터 단위로 업데이트가 돼야 할 텐데.
참, 그나저나 화면의 크기를 표기할 때 으레 쓰이는 '인치'는 센티미터로 바뀌기라도 했는지 궁금하다. 여기도 평이나 근 만만찮게 좀 이상한 단위가 관습적으로 쓰여 온 곳이니까 말이다.

Posted by 사무엘

2015/04/19 08:36 2015/04/19 08:36
, , , , ,
Response
No Trackback , 7 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1084

C/C++ 같은 지극히 static한 컴파일 지향 언어에서는 상상도 못 할 일이겠지만, 아주 다이나믹하고 인터프리터를 지향하는 프로그래밍 언어들은 문자열에 담긴 자기 언어 코드를 지금의 변수/함수 context를 기준으로 실행해 주는... 엄청난 기능을 제공하기도 한다.

PHP와 자바스크립트 모두 eval이라는 함수가 이 일을 한다. 루비던가 펄이던가 문자열의 동적 처리가 강한 다른 언어에도 응당 동일 기능이 있다.
문자열 변수에 들어있는 값(가령 "abc")을 통해서 해당 이름의 변수에 접근하는 것(abc)도 가능하다. C/C++에서는 지역 변수는 그냥 함수 스택 프레임으로부터 고정된 오프셋 숫자를 나타내겠지만, 저런 언어에서 지역 변수는 힙 기반의 해시나 트리를 참조하는 유동적인 이름-값 key 명칭에 해당할 것이다.

그리고 또 다르게 비유하자면, static type 언어는 테이블의 각 필드별로 타입과 정보량이 매우 엄격하게 정해져야 하는 데이터베이스와 같고(액세스),
dynamic type 언어는 셀에 들어가는 값과 타입이 자유자재로 바뀌어도 되는 스프레드 시트와 같다(엑셀).
어마어마한 대용량의 데이터를 처리하는 성능은 까다롭고 전문적인 DB가 훨씬 더 뛰어나지만, 그래도 엑셀 정도만 돼도 성능이 굉장히 좋아지고 그러면서 일상생활에서는 더 편리하다. 세상엔 그 정도 tradeoff는 어디에나 있는 듯하다.

그나저나, 문자열 코드를 실행하는 기능은 편리한 건 그렇다 치더라도 보안을 매우 취약하게 만들 수 있다는 점을 감안하고 사용해야 한다. C 언어의 %d, %s 같은 포맷 문자열만 해도 이미 위험하기 때문에 입출력 포맷 문자열에다가는 사용자로부터 실시간으로 입력받은 문자열을 절대 넣지 말아야 한다는 것이 불문율이다. 그런데 하물며 저건 문자열에 명시된 대로 아예 프로그램이 실행되니 C의 포맷 문자열과는 차원이 다른 방식으로 위험할 수밖에 없다.

본인이 경험해 본 프로그래밍 언어 중에 코드를 실시간으로 생성하고 자기 자신을 변형할 수 있는 물건의 원조는 역시 GWBASIC이었다.
비록 얘는 문자열을 코드로 그대로 해석하고 실행하는 기능은 없지만, 그래도 RUN, CHAIN, MERGE처럼 파일 차원에서 임의의 코드를 즉석에서 실행하는 기능은 있었기 때문이다.

다음은 스택 기반의 복잡한 파싱 없이 15*(2+5), 256^2, 1/5 등의 수식을 쓱쓱 계산해 내는 계산기 프로그램이다. 내가 이걸 생각한 게 20여 년 전의 일이다. ㅎㅎ

10 INPUT "Expression? ", A$
20 IF A$="" THEN END
30 OPEN "TMP.BAS" FOR OUTPUT AS #1
40 PRINT #1, "10 ON ERROR GOTO 30"
50 PRINT #1, "20 PRINT "+A$+": GOTO 40"
60 PRINT #1, "30 PRINT "+CHR$(34)+"Error"+CHR$(34)
70 PRINT #1, "40 RUN "+CHR$(34)+"CALC.BAS"+CHR$(34)
80 CLOSE #1
90 RUN "TMP.BAS"

프로그램의 동작 원리를 알면 피식 웃음이 나올 것이다. 이 프로그램이 하는 일이라고는 입력받은 수식을 그대로 PRINT하는 프로그램을 생성한 뒤, 그걸 실행하는 것이 전부이기 때문이다. 즉, 실질적인 계산을 하는 부분은 내가 짠 코드가 아니라 GWBASIC 인터프리터 자체인 것이다.
그나마 수식에 오류가 있어도 뻗지 않게 하려고 ON ERROR GOTO를 쓰는 일말의 치밀함(?)을 보였다.

사실, RUN은 지금 내 프로그램을 지우고 실행 제어를 다른 프로그램으로 완전히 옮기는 명령이다. MERGE나 CHAIN을 쓰면 내 프로그램의 컨텍스트를 유지하면서 타 프로그램을 그대로 병합을 할 수가 있으며 이게 내가 의도한 형태에 더 가깝다. 하지만 본인은 저 두 명령은 어떻게 사용하는지 잘 모른다.

MERGE 같은 경우 QuickBasic이나 QBasic으로 넘어가면서 후대의 베이직 언어들도 지원하지 않게 되었으며, 후대의 언어들도 차라리 문자열 코드를 실행하는 eval은 지원해도 저런 무지막지한 명령을 지원하지는 않는다. 먼 옛날에 컴퓨터와 함께 제공되었던 두툼한 MS-DOS 겸 GWBASIC 매뉴얼에서나 그런 명령에 대한 자세한 설명을 볼 수 있었던 걸로 기억한다.

자, 그럼 다시 자바스크립트 얘기로 돌아온다. 얘는 HTML, CSS와 더불어 웹을 구성하는 한 축이며, HTML 문서가 MS 오피스의 Word, Excel 같은 일반 문서라면 자바스크립트는 그런 문서에 첨부된 매크로나 마찬가지이다. 다른 모든 언어들은 사용하기 위해서 해당 언어 구현체들 런타임이나 엔진을 설치해야 하지만 자바스크립트는 웹브라우저만 있는 운영체제라면 바로 돌려볼 수 있다는 차이가 존재한다.

자바스크립트에는 딱히 컴파일· 링크 같은 건 없지만 난독화 겸 간소화(compaction)라는 리팩터링 후처리를 거쳐서 서버에 올라가곤 한다. 굳이 IOCCC 같은 대회를 노려서는 아니다. 핵심 알고리즘의 누출을 막고(분석을 완전히 막지 못하더라도, 어렵게 만들거나 지연시키기 위해) 크기도 줄이기 위해서이다.

난독화 내지 간소화를 자동으로 해 주는 도구가 당연히 존재한다. 모든 주석이나 공란은 제거되며 지역 변수는 전부 a~z, aa ... 처럼 식별만 가능하지 최대한 짤막하고 암호 같은 명칭으로 바뀐다. 상수 명칭의 조합으로 좀 더 알아보기 쉽게 숫자를 표현하던 것도 당연히 다 실제 숫자로 치환된다.

그런데 자바스크립트를 리팩터링해 주는 툴 중에는 그런 명칭 치환 수준을 넘어서 코드를 암호화에 가까운 완전히 다른 형태로 바꿔 버리는 것도 있다. 그런 덩어리를 보면 예전 코드는 다 이상한 문자열로 바뀌고 코드의 가장 바깥은 eval을 호출하는 형태가 돼 있다. 그 문자열을 원래의 자바스크립트 코드로 해독하는 건 다른 치환과 디코딩 함수들이고 말이다.

이건 개념적으로 실행 파일 압축과 동일한 기법이다. 거기도 압축을 푸는 데 필요한 최소한의 코드만 들어있고 나머지는 데이터를 압축을 풀어서 코드를 생성함으로써 실행되니까 말이다. 그게 기계어 코드가 아닌 인터프리터 스크립트형 언어에도 저런 식으로 존재할 수 있다는 게 신기하게 느껴진다. 자바스크립트의 JIT는 저런 것까지 다 감안해서 동작해야 할 테니 런타임이 안 그래도 거의 VM 수준의 스케일로 만들어져야 할 듯하다.

이념적으로 C/C++의 완전 정반대편에 있는 동적 언어를 익혀서 나쁠 건 없는 것 같다. JS는 그렇다 치더라도 파이썬은 슬라이싱 기능이 완전 마음에 든다. 늘 하는 생각이지만, 메모리 관리가 자동으로 되고 마음대로 new를 남발해도 되는 언어로 코딩을 하는 기분은 운전으로 치면 정말 클러치 걱정을 할 필요 없는 자동변속기 차를 모는 것과도 같아 보인다.

그리고 저런 동적 언어들은 아무래도 문자열 처리가 강세이며, 언어의 설계자가 확실히 정규 표현식 덕후라는 생각이 들었다.
메모리 관리를 할 필요가 없는 건 편하지만 C/C++의 포인터처럼 아주 저수준 직관적으로 문자열에 접근을 할 수 없는 건 좀 불편하다. 새로운 언어를 익히면 그 언어의 String 클래스가 제공하는 멤버 함수(메소드)부터 새로 익혀야 하니까.

또한 자바스크립트, 정규 표현식, URL, 그리고 HTML 내부에 제각각으로 돌아가는 탈출문자들 때문에 신물이 났다.
문자열을 검색· 치환하는 함수들이 다 있는 그대로 찾아 바꾸는 게 아니라 기본적으로 정규 표현식 기반이다. 역슬래시나 따옴표 같은 크리티컬한 문자 그 자체를 찾거나 그걸로 바꿔야 할 때 프로그램이 곧이곧대로 동작하지 않아서 그것도 좀 애로사항이었다.

Posted by 사무엘

2015/04/16 19:36 2015/04/16 19:36
,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1085

네트워크나 파일 같은 외부 입출력을 열고 닫는 작업은 실패할 가능성이 워낙 높기 때문에 프로그램 작성에서 에러 처리가 거의 무조건적인 필수이다.

하지만 메모리 할당은 그렇지 않다. 수~수십 MB 정도 공간을 요청하거나 재할당하는 것쯤은 요즘 컴에서는 실패할 가능성이 0에 수렴한다. malloc의 결과값이 NULL인지 일일이 체크하는 코드는 요즘 거의 찾을 수 없다. C++의 new 연산자도 예전에는 실패 시 NULL 리턴이었지만 지금은 예외를 던지는 형태로 디자인이 바뀐 지 오래다. 그거 일일이 NULL 체크를 하는 건 너무 남사스럽고 성가시고 민망하기 때문이다.

메모리 할당을 위해 어지간해서는 C++의 깔끔한 new와 delete를 쓴다지만, C++ 연산자에는 메모리의 재할당 기능이 없기 때문에 이를 위해서는 여전히 malloc/realloc/free 쌍이 쓰인다. 그리고 좀 원시적인 테크닉이긴 하지만 가변 길이 구조체의 메모리 할당을 위해서도 크기 지정이 자유로운 C 스타일의 메모리 함수가 필요하다. 아니면 operator new 함수를 직접 호출하든가 말이다.

그런데 realloc은 실행이 실패했을 때의 상태가 꽤 복잡하다. 보통 ptr=realloc(ptr, newsize) 같은 형태로 활용을 하는데, 재할당이 실패했다고 생각해 보자. 이때는 realloc은 재할당을 할 수 없어서 NULL을 되돌린다. 이는 분명 비정상적인 오류 상황이고 프로그램이 그에 대한 별도의 대비를 하긴 해야 하지만, 그렇더라도 ptr이 원래 가리키던 메모리는 아무 이상이 없다. 그런데 ptr에다가 무턱대고 마치 malloc의 리턴값처럼 NULL을 대입해 버리면 ptr은 소실되고 메모리 leak이 발생하게 된다.

그러니 실행이 실패하더라도 메모리 leak은 발생하지 않게 하려면

ptr_tmp=realloc(ptr, newsize);
if(ptr_tmp) ptr=ptr_tmp; //성공
else { } //실패

번거롭지만 이렇게 임시 포인터 변수를 하나 추가로 둬서 실행이 성공했을 때에만 포인터의 실제값을 반영하게 해야 안전하다. 본인은 이 점을 한 번도 생각을 안 하고 있었는데 비주얼 C++ 2012에서부터 추가된 코드 정적 분석기가 지적을 해 주는 걸 보고서야 “아하!”하고 무릎을 쳤다.

이런 것을 생각하면 realloc의 실패야말로 리턴값보다는 예외 처리로 알려 주는 게 더 편리하겠다는 생각이 든다.
절차형으로 실행되는 컴퓨터 프로그램에서는, 당연한 말이지만 n+1단계 명령은 그 앞의 1~n단계의 모든 명령들이 성공적으로 차곡차곡 잘 실행됐다는 전제하에서만 실행 가능하다. 중간에 뭔가 탈이 났다면 더 진행을 할 수 없으며 어디까지 앞뒤로 되돌아가면 되는지를 컴퓨터가 스스로 판단할 수 없다. 컴퓨터에게는 인간 같은 유도리가 존재하지 않는다. 그렇기 때문에 그런 정보가 없다면 그 프로그램은 전체가 강제 종료되는 것밖에 답이 없다.

자동차 운전을 하는 사람이라면 단순히 핸들과 페달과 변속기를 조작하는 것 말고도 사고가 났을 때의 대처 요령과 보험사 연락처 같은 것도 숙지하고 있어야 하듯, 컴퓨터 프로그램도 마찬가지이다. 중간에 탈이 나도 최대한 부드럽게 수습하고, 피치 못할 상황에서  프로그램이 죽더라도 최소한 지금 작성 중인 문서를 저장이라도 한 뒤에 죽는 그 로직 자체도 프로그래밍이 돼 있어야 한다. 그것이 바로 예외 처리라는 분기 제어에 해당한다.
아울러, 숙달된 프로그래머라면 예외 처리를 구현하는 데 드는 추가 오버헤드와 비용을 숙지해 둘 필요도 있다. 수많은 객체들의 생명 주기를 관리하면서 여러 함수들을 한꺼번에 이탈하는 것도 그냥 될 리는 없으니 말이다.

C/C++은 애초에 운영체제/하드웨어 차원에서의 crash는 있어도 언어 차원에서의 예외 처리라는 게 아예 존재하지 않던 언어이다 보니 이쪽의 지원이 다른 언어들보다 상대적으로 미비하다. C++에 try/catch 키워드는 한참 뒤에 등장했으며 언어 자체는 이 예외 구문을 전혀 사용하지 않는다. 이걸 사용하는 건 라이브러리 계층에서이다. 그리고 예외 처리용 객체를 날려 줄 때조차도 new로 메모리를 할당했다면 해제를 수동으로 해 줘야 하니 불편한 점이 아닐 수 없다.

다시 본론인 realloc 얘기로 돌아온다.
저런 예외 처리도 오버헤드가 크니 싫고 리턴값만으로 모든 책임을 회피하고 싶다면, realloc 함수의 프로토타입을 차라리 이렇게 설계했으면 더 편했을지도 모른다.

bool realloc(void **pptr, size_t newsize);

void **라니 참 COM스러워 보이지만(CoCreateInstance, IUnknown::QueryInterface ㅋㅋ), C++이라면 템플릿 함수로 이걸 감싸서 지저분함을 한결 예방할 수 있을 것이다.

if(realloc((void **)&ptr, newsize)) { /* 성공 */ }
else { /* 실패 */ }
free(ptr);

내가 무엇을 의도하는지는 딱 보면 알 수 있을 것이다. 기존 메모리를 가리키고 있는 포인터의 주소를 받아서, 재할당이 성공하면 그 포인터가 가리키는 값을 그대로 바꿔 버리고 true를 되돌리는 것이다. 어차피 지금 realloc 함수는 ptr=realloc(ptr, newsize)라고 ptr이 함수 인자(input) 겸 리턴값(output) 형태로 동시에 쓰이고 있으며, 재할당이 성공했다면 예전 주소는 보관하고 있을 필요가 전혀 없으니 말이다.

실패했다면 ptr은 *ptr이든 **ptr이든 아무 변화가 없고 리턴값만 false가 된다. free(ptr)을 해 주는 한 어떤 경우든 메모리 leak 걱정은 안 해도 된다. realloc 함수가 이렇게 만들어지는 게 더 낫지 않았나 싶은 생각이 든다.
뭐, realloc이 결코 실패하지 않는다고 가정하고 프로그램이 막무가내로 동작한다면, 차라리 NULL 포인터 일대를 액세스하다가 확실하게 죽는 게 기존 메모리를 범위를 초과하여 건드리다가 죽는 것보다는 더 안전할지도 모르겠지만 말이다.

끝으로 하나 더. fopen에서 접근 모드와(read/write 등) 데이터 처리 모드(바이너리/텍스트) 인자는 들어올 수 있는 조합이 뻔하고 상수 명칭 조합으로 처리해도 하등 이상할 게 없을 텐데, 왜 하필 더 파싱도 어렵게 문자열을 쓰고 있는지도 이유를 모르겠다. 딱히 확장의 여지가 있어 보이지도 않는데 굳이 _open 같은 저수준 함수와 형태를 달리할 이유가 없다. 이런 것들이 C 라이브러리에 대해서 궁금한 점이다.

Posted by 사무엘

2015/04/14 08:25 2015/04/14 08:25
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1083

이건 예전에 썼던 disabled 윈도우 관련 글에서 추가로 다뤘어야 했는데 그때 빠뜨렸던 내용이다.
그 글에서 설명했듯, Windows는 '고전 테마'에서 메뉴, 버튼, static 컨트롤이 disable된 형태는 그냥 회색으로만 표시하는 게 아니라 흰색 윤곽 위에다가 회색 윤곽을 덧씌워서 일종의 '엠보싱' 효과를 줘서 표시한다.

사용자 삽입 이미지

이것들은 은색(밝은 회색, 일명 3D 배경색)이 배경이라는 공통점이 있다. 시스템 컬러 번호는 COLOR_3DFACE. 사용자가 딱히 변경을 할 수 없는 고정된 문자들이다.

그 반면, 리스트 박스나 에디트 컨트롤처럼 흰 배경(COLOR_WINDOW)에 표시되는 문자들은 사용자의 선택이나 입력에 의해 내용이 바뀔 수가 있다. 이런 것은 disable됐을 때 배경이 회색으로 변하고 문자는 딱히 엠보싱 처리가 되지 않는다. 각각의 경우에 비주얼이 나름 원칙이 있게 설계된 셈이다.

그런데 먼 옛날에 Windows 9x 시절에 안전 모드 부팅을 해 본 분들은 뭔가 이상한 낌새를 느끼지 않으셨나 모르겠다.
안전 모드에서는 일체의 외부 디바이스 드라이버를 불러들이지 않는 관계로 그래픽조차도 완전 미개한 VGA 640*480 16색으로로 돌아가 버린다.

회색과 파랑의 기본색 팔레트가 다른 톤으로 바뀌는데, 이건 그렇다 치더라도
안전 모드에서는 disable UI가 엠보싱이 아니라 그냥 짙은 회색 단색으로 간소화되어 출력된다. 그래서 느낌이 더욱 달라진다.

사용자 삽입 이미지

엠보싱을 표현하는 데는 흰색과 회색 단 두 색만 있으면 된다. 색깔 표현의 한계 때문에 엠보싱을 포기한 건 결코 아니다.
결정적으로 NT 커널 기반인 Windows 2000은 똑같이 VGA 16색인 안전 모드에서도 동일하게 엠보싱 처리를 해 준다. 어찌 된 일일까?

사용자 삽입 이미지

the old new thing 블로그에 관련 설명이 있다.
시스템 정보를 얻는 API 함수 중 하나인 GetSystemMetrics를 보면 SM_SLOWMACHINE이라는 아이템이 있는데,
얘의 리턴값이 true일 정도로 열악한 환경에서는 운영체제 셸은 disable UI에 엠보싱을 포기하고 그냥 회색 단색을 출력한다고 한다.

이 플래그는 컴이 486이 안 되고 램이 6MB도 안 되는.. 그야말로 윈도 3.1만 간신히 돌리던 극도의 똥컴에서나 켜지는 정도였다. 윈도 95가 당시 사용자의 컴퓨터 환경을 감안하여 명목상으로는 386+램 4MB 이상을 기준으로 설계되었다는 걸 생각해 보자. 물론 한글판은 한글 입출력 오버헤드 때문에 그런 사양에서는 어림도 없으며, 최하 486에 램 8MB 이상은 기본으로 갖춰져야 했다.

그리고 컴퓨터가 아니면 그래픽 카드가 완전 구릴 때에도 이 플래그가 설정되었다.
이를테면 마우스 포인터 깜빡임 보정조차 안 될 정도로 안습일 때 말이다. 기본 VGA는 하드웨어 가속이고 뭐고 아무것도 없는 느려터진 모드이다. 당장 시스템 종료를 위해 Alt+F4를 눌러 보면, 화면 배경 전체에 검은 도트가 반씩 씌워지는 것조차도 단번에 안 되어 점이 내려오는 게 보일 지경이다.

안전 모드에서 disable UI가 엠보싱 없이 출력되었던 것은 바로 그래픽 모드 때문이었다.
엠보싱은 지금 컴퓨터의 관점에서야 그야말로 껌값인 처리이지만, 나름 더블 버퍼링이라는 오버헤드가 필요한 연산이었으니 말이다.

이런 9x와는 달리, NT 계열은 1993년에 출시된 첫 버전 3.1부터가, 그것도 한글· 한자 오버헤드 따위도 없는 영문 원판이 램이 최하 12MB 이상 필요한 왕창 무거운 물건이었다. 범용성과 안정성, 이식성을 위해서 컴의 성능을 쫙쫙 빼다 쓰는 형태로 설계되었기 때문이다. 기본 문자 집합부터가 1바이트가 아닌 2바이트 크기였으니 그것도 메모리를 추가로 잡아먹었을 테고. (그러니 그 시절에 NT를 돌릴 수 있는 컴을 가진 사람이 도대체 얼마나 됐겠는가.)

블로그 글에 따르면, NT는 그야말로 “All machines are fast.”라고 가정하고 태생적으로 SM_SLOWMACHINE 플래그를 사용하지 않는다고 한다. 즉, 어떤 컴퓨터에서나 언제나 false를 되돌린다.
그러니 Windows 2000에서는 VGA 16색 안전 모드에서도 UI에 엠보싱이 적용되는 이유가 논리적으로 바로 설명이 된다.

단, 신기한 것은 2000은 VGA 16색 안전 모드에서도 그래픽이 그렇게 느리게 느껴지지 않는다는 점이다.
게다가 그 모드에서도 그래픽이 마구 바뀌는 곳에 마우스 포인터를 가져갔을 때 포인터가 깜빡거리지 않는다! 하드웨어 제어를 어떻게 했는지 굉장히 궁금해지는 대목이다. 뭔가 굉장히 탄탄하고 안정적이라는 느낌이 든다.

Windows XP부터는 더 나아가 이제 안전 모드에서도 VBE인지 뭔지 슈퍼 VGA 규격을 사용한다. 비록 하드웨어 가속이 없을지언정 일단 트루컬러는 무조건 보장된다. 그래서 초라한 16색, 256색 따위는 정말로 볼 일이 없어졌으니 참 격세지감이다. 그냥 16비트 컬러냐 32비트 컬러냐의 양자 선택만이 있을 뿐이다.

그리고 고전 테마 말고 새로운 테마에서는 disabled UI에 엠보싱 자체를 하지 않는다. 오히려 SM_SLOWMACHINE 스타일과 같은 맥락인 회색 단색으로 회귀했다. 고전 테마가 아예 없어진 Windows 8부터는 엠보싱은 아련한 과거 추억이 됐다.

그래픽 모드가 아예 단색일 때는 diabled UI는 글자에다가 배경색 도트를 반반씩 뿌려서 흐리게 했었는데 그게 16컬러 시절에 엠보싱으로 바뀌었고, 이것이 궁극적으로는 알파 채널로 변모하는 듯하다. 사실 엠보싱은 트루컬러+알파채널+ClearType에 친화적인 방식이 아니긴 하니까 말이다. (맑은 고딕을 엠보싱해서 흐리게 출력하면 보기가 대략 좋지 않다)

참고로 MS Office 제품 중에 Word와 Excel은 운영체제의 대화상자 API가 아니라 자체 개발한 대화상자와 GUI 라이브러리를 사용한다. 얘들은 고전 테마 기준으로 push 버튼은 엠보싱으로 출력하지만, 라디오나 체크 박스는 단순 회색으로 disabled 상태를 출력한다. 즉, 비주얼이 짬뽕이며, 운영체제 GUI와 외형이 완전히 같지는 않다.

사용자 삽입 이미지

끝으로..
Windows에 사용자의 컴퓨터 성능을 체크하는 듯한 기능 몇 군데를 좀 살펴보고 글을 맺도록 하겠다.
하나는 비스타와 7 시절에 있던 그 유명한 Windows 체험지수인데, 이건 8부터는 없어졌다.
다른 하나는 '내 컴퓨터' 속성의 "고급 - 성능 옵션"에서 각종 시각 효과를 설정하는 곳이다.

이건 Windows XP에서 처음 도입된 걸로 기억하는데, 윈2000이나 돌릴 법한 좀 간당간당한 컴에서 "최적 성능으로 조정"을 켜면 그림자 효과나 창의 애니메이션 등 몇몇 효과가 알아서 제외된다.
하지만 이것은 SM_SLOWMACHINE 플래그와는 별개로 구현된 기능이라고 생각하면 되겠다. 또한, 요즘 컴퓨터에서는 그런 거 성능이 문제되는 경우는 전혀 없다고 봐도 무관하고.

아무튼 흥미로운 사실을 하나 알게 됐다.

Posted by 사무엘

2015/04/11 08:30 2015/04/11 08:30
,
Response
No Trackback , 3 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1082

본인이 인터넷에서 굉장히 고맙게, 유용하게 잘 열람하는 정보 중 하나는 지도이다.
참 대단하지 않은가? 항공 사진, 길거리 사진, 길 찾기, 실시간 대중교통 연계와 도로 상황 안내 등... 정말 혀를 내두르는 수준이다. 이젠 도대체 얼마나 더 똑똑해질 거리가 남아 있는 걸까?

아울러, 지도의 일종인 차량용 내비게이션 소프트웨어도 도대체 어떤 천재가 만들었나 싶은 생각이 든다. 도로 상황을 감안해서 길을 찾는 건 당연한 소리이고, 그걸로도 모자라서 길 가는 중에 실시간으로 “해당 경로에 사고가 발생했습니다. 우회 경로를 재탐색할까요?”까지도 튀어나온다.

2013년엔 구글 회장이 한번 방북을 하고 났더니 구글어스가 평양을 중심으로 북한의 세부 지리 정보(단순 항공 사진은 예전부터 제공했음)를 제공하기 시작했다. 얘를 시작으로 2014년 하반기부터는 국내 지도 사이트들도 북한 정보를 제공하기 시작했다.
고무적인 현상이다. 물론 구글어스도 그 자체는 처음부터 구글이 개발한 게 아니라 타 업체 솔루션을 인수한 것이긴 하지만 말이다.

본인이 예전에 인터넷 지도에 대해 썼던 글은 이 모든 기능이 별도의 응용 프로그램이 아니라 웹에서 웹 표준 기술만으로 바로 구현 가능해진 것이 신기하다는 요지였다.
이번에는 다른 분야에서 대단히 신기하게 느껴지는 것에 대해 이야기를 늘어놓아 보겠다. 바로 이미지 가공 기술이다.

지도 사이트들이 제공하는 평면 항공 사진은 (1) 넓디넓은 영역을 한결같이 위에서 아래를 내려다보는 단일 각도로 본 이미지이다. 그런데 이거 정말 가공을 많이 했겠다는 생각이 들지 않는가?
이미지에서 원근감이라는 걸 완전히 제거하고 건물들이 마치 스타크래프트 맵처럼 보이게 해야 한다. 중심에서 먼 곳의 건물일수록 모양이 왜곡되어 보이는 카메라 렌즈의 오차를 보정해야 한다.

물론 엄청 높은 곳에서 촬영을 하면 건물 자체의 높이로 인해 발생하는 원근감은 상당수 없어지지만 이번엔 반대로 고층 건물도 높이가 전혀 표현되지 않게 되며, 또 사진의 화질이나 해상도, 그리고 구름으로 인한 시야 가려짐 같은 기술적인 문제도 커진다. 게다가 지구 자체도 근본적으로 평면이 아니라 둥근 구이니, 이로 인한 평면의 왜곡은 카메라의 위치가 높아질수록 더욱 부각되어 보일 것이다.

이런 항공 사진은 전세계의 것을 동시에 촬영하기란 불가능할 테니 여러 사진, 혹은 연속적으로 촬영된 사진을 파노라마 사진 만들듯이 연결해야 할 것이고 이 사진들은 촬영 시간대도 최대한 일치해야 할 것이다(광량의 차이). 또한, 주행 중이어서 시시각각 위치가 변하는 자그마한 자동차나 열차의 모습은 어떻게 보정을 하면 좋을까?
이런 것들을 다 극복하고 전국· 전세계의 항공 사진을 최대한 일관성 있는 색조와 각도로 엮는 것은.. 그 어려움과 복잡함이 정말 말도 못 할 것 같다. 비행기에서 아래를 내려다보고 사진만 팡팡 찍는다고 해서 구현 가능한 게 아니다.

사용자 삽입 이미지
(사진으로 나타난 63 빌딩의 높이와, 그림자의 길이를 비교해 보자.;; 각도가 뭔가 자연스러운 것 같지는 않다. 보정을 한 게 아닐까..)

그 보정이 자동화가 가능한지 아니면 일일이 수작업으로 행해지는지가 궁금하다.
마치 요런 영화 촬영 기법을 떠올리게 한다. 피사체는 시간이 정지한 듯 꼼짝 않고 있는데 카메라가 뱅그르르~ 돌아가면서 다른 위치와 각도에서 피사체를 응시하며 촬영하는 것 말이다. 심지어 사람이 하늘에 붕 떠 있는 채로 그런 장면이 나오기도 하니 더욱 신기한 일이다.

그리고 다음으로 생각할 것은 로드뷰이다.
이것은 앞의 항공 평면 사진과는 반대로, (2) 단일 시점에서의 view를 모든 각도로 제공하는 것이다. 이것은 어쨌든 연속으로 촬영할 수는 없기 때문에 로드뷰의 시점은 수~십수 미터 간격으로 띄엄띄엄 제공된다.

사용자 삽입 이미지

이런 시점 view는 지금이야 지도 사이트에서 쉽게 열람할 수 있는 기능이 됐지만, 옛날에 2000년대 초엔 철도청 홈페이지에서 자바 애플릿 형태로 비슷한 기능을 제공한 게 있었다.
바로 새마을· 무궁화· 통일호 내지 전동차의 객실 내부를 저런 로드뷰처럼 상하좌우 둘러보는 기능이었다.

이 기능은 내부적으로 2차원 평면 형태의 파노라마 사진을 한 장 저장하고, 그 그림의 일부에다 원근법을 적용하여 변형한 것을 표시하는 형태로 구현되어 있다. 내가 아는 건 이게 전부이고 구체적으로 어떤 계산을 하는지, 그리고 이런 용도로 사용하는 사진은 어떤 형태이고 어떻게 촬영하는지에 대해서는 잘 모른다. 그야말로 상하좌우 시야각이 다 열려 있는 특수한 카메라를 써야 할 텐데..

내가 10여 년 전에 이미 3차원 그래픽 시연 프로그램이라는 것도 만들어 봤지만, 비트맵 이미지로부터 3차원 시야를 어떻게 구현하는지는 여전히 감이 안 온다.
2차원 이미지에서 원근감을 넣거나 없애고, 평면과 공간 사이를 오고 가게 하는 기술이 참 대단하게 느껴진다. 그 기술이 인터넷 지도, 더 나아가 증강현실 같은 것도 가능하게 한 셈이다.

Posted by 사무엘

2015/04/08 08:32 2015/04/08 08:32
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1081

Windows API 메모

1.
Windows API에서 DrawText는 gdi도 아니고 user 계층에 있는 고급 함수인 주제에 여러 줄(DT_SINGLELINE 플래그 없는 기본 모드)을 찍을 때에도 세로 정렬(DT_VCENTER, DT_BOTTOM)을 좀 지원해 주면 어디 덧나나 싶다. 오래 전부터 개인적으로 매우 대단히 아쉽다고 생각해 온 점이다.

얘는 gdi 계층에 있는 다른 글자 출력 함수들과는 달리, 글자수를 -1 (null-terminate string을 가정하고 알아서 길이를 계산하게)로 줄 수가 있으며, 긴 파일/디렉터리 이름의 중간을 생략하여 찍거나 액셀러레이터 &를 다음 글자의 밑줄로 바꿔 출력하는 기능, 심지어 밑줄만 출력하는 기능도 있다.
& 전처리의 경우, 하는 게 아니라 “끄는 게” 별도의 플래그로 주어져 있을 정도로 기본 기능이다.

그러니 이건 천상 운영체제 내부에서 자기네 GUI 출력용으로 쓰는 함수인데 제3자도 사용할 수 있게 공용 API로 열어 놨다는 뜻이다.
안 그래도 텍스트를 처음부터 끝까지 쭉 읽어 봐야만 할 수 있는 처리들이 즐비한데, 그에 비해 멀티라인 텍스트의 세로 정렬은 텍스트 전체에서 \n 개수를 세어서 줄 수만 파악하고 나면 아주 손쉽게 구현 가능한 처리이다.
그러니 왜 지원을 안 하는지가 몹시 의문이다.

공교롭게도 운영체제의 컨트롤들 중에 static text는 DrawText의 기능을 사용해서 그런지 multiline 상태에서 세로로 중앙이나 아래 정렬을 하는 옵션이 없다.
그러나 버튼(push, radio, check 모두)들은 그런 옵션이 있다.

2.
아마 이건 예전에 의견을 한번 피력한 적이 있는데 다시 적자면..
본인은 선에 안티앨리어싱을 해서 그리는 기능 정도는 그냥 Pen 관련 GDI 함수/구조체에다가도 스타일로 추가해서 지원을 좀 해 줬으면 하는 생각을 한다. PS_SMOOTH 정도로..;;
마치 Cleartype이 적용된 글자를 찍기 위해 생소한 API를 굳이 사용할 필요가 없는 것처럼 말이다. 그냥기존 LOGFONT 구조체의 lfQuality에 새로운 값이 추가되는 걸로 훌륭하게 잘 구현되지 않았던가.

21세기 초에 야심차게 도입됐던 GDI+는 하드웨어 가속 버프도 없이 거의 버림받은 신세가 됐고, Direct2D는 COM을 사용하는 등 API 패러다임이 너무 다르다.
하지만 GDI는 유구한 역사를 자랑하는 Windows의 창립 멤버 API이고 이제 와서 도저히 버릴 수가 없는 압도적인 짬밥을 보유하고 있으니.. 그냥 유지보수 차원에서만 지원되는 legacy가 돼 버렸고 GDI API에 근본적인 확장은 없을 것으로 생각된다.

3.
유니코드 UTF16 문자열과 여타 8바이트 기반 인코딩(UTF8 포함) 사이를 변환하는 API 함수는 잘 알다시피 WideCharToMultiByte와 MultiByteToWideChar이다.
얘는 Windows NT가 유니코드+2바이트 wide char 기반으로 통 크게 설계되었을 때부터 역사를 함께 해 왔다. 옛날에 Windows 3.x에다가 Win32s를 설치하면 단순히 32비트 커널+썽킹 코드뿐만 아니라 코드 페이지 변환 테이블도 잔뜩 설치되었다. 32비트 EXE/DLL은 리소스의 내부 포맷부터가 유니코드인 관계로, 이들을 당장 변환할 수 있어야 하기 때문이다.

유니코드에서 여타 인코딩으로 변환하는 것은 마치 double에서 short로의 형변환처럼 큰 집합에서 작은 집합으로 이동하는 변환이다. 그러니 인코딩에 존재하지 않는 문자는 ? 같은 default 문자로 치환된다.
그런데, 별도의 플래그가 없다면 WideChar... 함수는 약간의 '유도리'를 발휘하여 동작한다. 여러 유니코드 문자가 한 여타 인코딩으로 변환될 수 있다는 뜻이다.

예를 들어, 유니코드를 KS X 1001로 변환한다고 치면, 원래 거기에 있던 호환용 한글 자모 ㄱ(U+3131)만 0xA4, 0xA1로 바꾸는 게 아니라 표준 한글 자모 영역에 있는 U+1100(초성 ㄱ)과 U+11A8(종성 ㄱ)까지 다 호환용 한글 자모 ㄱ으로 바꾼다는 뜻이다. ?로 바꾸지 않는다.
이런 예가 호환용 한글 자모나 일부 유럽 문자에 대해서 더 존재한다. 유럽 문자라 함은, 대문자 버전이 존재하지 않을 경우 그냥 소문자 버전으로 바꾸는 식이다.

이런 동작을 원하지 않고 엄밀하게 변환을 하고 싶다면 WC_NO_BEST_FIT_CHARS라는 플래그를 반드시 줘야 한다. 얘는 변환된 타 인코딩을 유니코드로 역변환했을 때 원래의 유니코드로 정보가 유지되지 않는다면 무조건 ?로 바꾼다. 즉, U+11??대의 표준 한글 자모는 호환용 한글 자모로 바뀌지 않는다. 이 옵션은 Windows NT4에도 존재하지 않으며, 98/2000부터 새로 추가된 얼마 안 되는 기능이다.

어느 방식을 사용할지는 그야말로 상황에 따라 다르다. 문자열을 복사하는 함수만 해도 버퍼 크기가 초과되었을 때 그냥 뒷부분을 융통성 있게 잘라 버려도 괜찮은 경우가 있는가 하면, 반드시 정확도가 보장되어야 해서 차라리 예외가 발생해야 하는 경우도 있을 수 있으니 말이다.

한편, 여타 인코딩에서 유니코드로 바꾸는 경우는 작은 집합에서 큰 집합으로 가는 것이니 일단은 유니코드에 대응하지 못하는 문자 걱정은 없다.
하지만 아무래도 여러 바이트가 한 글자를 구성하다 보니 정규화가 잘못되어서 해당 인코딩에 해당하지 않고 유니코드로 변환 자체가 될 수 없는 바이트 나열이 들어있을 수 있다. 이 경우는 유니코드로 변환했다가 다시 그 인코딩으로 역변환을 했을 때 바이트 나열이 원래대로 돌아올 수가 없게 된다.

이런 일이 발생했는지를 엄격하게 체크하려면 Multi... 함수에다 MB_ERR_INVALID_CHARS 플래그를 주면 된다.
<날개셋> 편집기는 이 두 경우를 모두 체크하여 불러오기가 제대로 되지 않았을 때, 혹은 저장과 함께 정보가 소실될 우려가 있을 때 경고 메시지가 나온다.
저장이야 UTF8 내지 UTF16 같은 유니코드 계열 인코딩만 골라 주면 문제가 없지만, 불러오기 자체가 문제가 있었다면 그 어떤 인코딩을 쓰더라도 다시 저장하는 순간 정보 소실이 생기기 때문이다.

4.
다음으로, 우클릭 메뉴를 구현할 때 즐겨 쓰이는 TrackPopupMenu(Ex) 함수에 대해서도 좀 한 마디 하겠다.
사실 얘는 굳이 임의의 지점을 우클릭했을 때 외에도, 어떤 버튼을 눌렀을 때 메뉴가 튀어나오게 하는 용도로도 많이 쓰인다. 그래서 Ex 버전에서는, 메뉴가 상하좌우 좀 치우친 곳에서 튀어나와서 위치 보정이 필요하더라도, 그 버튼 영역은 메뉴에 의해 가려지지 않게 하는 유용한 옵션이 추가되었다.

윈도 Vista 이상에서부터는 버튼의 오른쪽 끝에 ▼라는 split 버튼을 넣는 옵션이 추가된 관계로, 팝업 메뉴는 이 UI와 연동되어 즐겨 사용된다. 본인이 개발하는 <날개셋> 한글 입력기의 제어판 UI에도 물론 적극 활용되었다.

그런데 그건 그렇고.. 본인이 이 함수에 대해서 좀 이해가 안 되는 면모는 크게 두 가지이다.
얘는 HWND를 하나 인자로 받는다. 사용자가 메뉴를 ESC로 취소하지 않고 뭔가 항목을 선택하면 그 명령 ID가 부모 윈도우에다가 WM_COMMAND의 형태로 전달된다. 이것은 일단은 팝업 메뉴 말고도 단축키 내지 프로그램 창에 기본으로 딸린 메뉴를 선택했을 때와 동작의 일관성을 맞추기 위한 조치이다.

그러나 그렇게 하지 말고 사용자가 선택한 명령 ID가 그냥 함수의 리턴값으로 바로 오게 할 수도 있다. DLL 같은 걸 만들기 때문에 응용 프로그램의 기본 메뉴 연계 따위를 생각 안 하는 환경에서는 이런 디자인이 훨씬 더 유용하다. 그래서 이때는 flag에다가 TPM_RETURNCMD를 주면 된다.

사소해 보이는 팝업 메뉴의 디자인도 이렇게 두 양상으로 나누어 생각할 수가 있는 것이다.
마우스의 드래그 드롭 동작을 각 WM_LBUTTONDOWN, WM_MOUSE, WM_LBUTTONUP 핸들러 함수에다 제각기 따로 처리할지, 아니면 WM_LBUTTONDOWN 안에다가 또 message loop을 만들어서 한 함수 안에다가 다 집어넣을지의 차이와 비슷한 맥락이다.

아무튼, 메뉴에서 TPM_RETURNCMD에 대해, MSDN에는 "determine the user selection without having to set up a parent window for the menu."라는 문장까지 버젓이 있는데..
그럼에도 불구하고 TPM_RETURNCMD가 있더라도 HWND hParent의 값은 어떤 경우에도 NULL이어서는 안 된다. 심지어 자신이 만들지 않은 다른 윈도우(데스크톱 전체 윈도우 같은)를 줘도 안 되고 동작이 실패한다.

WM_COMMAND를 안 받으면 이 윈도우는 정말 레알 천하에 필요하지 않은데도 말이다. 애초에 메뉴가 튀어나오는 좌표도 언제나 화면 좌표이지 부모 윈도우 같은 걸 받지도 않는다. 그래도 이 윈도우는 없으면 안 된다.
그래서 <날개셋> 한글 입력기는 부득이하게 화면에 표시도 안 되는 message-only 윈도우를 간단히 만들어서 이걸 셔틀로 삼아 메뉴를 띄운 뒤, 메뉴가 사라지자마자 그 윈도우를 메시지 펌핑 하나 안 하고 파괴해 버리는 꼼수를 불가피하게 쓴 부분도 있다. 순전히 삽질이다.

이게 한 가지이고, 다른 하나는.. TPM_NONOTIFY라는 플래그는 왜 있느냐는 것이다. TPM_RETURNCMD 플래그가 있으면 명령 ID는 리턴값으로 오고 WM_COMMAND가 가지 않아서 이미 no notify의 효과가 나는데 저 플래그가 또 하는 일이 무엇인지 MSDN만 봐서는, 또 내 직관과 경험만으로는 모르겠다. 알 수 없는 노릇이다.

5.
인터넷에서 갓 다운로드한 파일은 운영체제가 뭔가 좀 다르게 취급한다는 걸 컴퓨터(일단은 Windows 기준으로) 사용자라면 경험적으로 다들 아실 것이다.
Word나 Excel 같은 프로그램에서 문서를 열면 "이 문서는 인터넷에서 가져온 것이기 때문에 위험할 수 있다. 매크로를 기본적으로 꺼 놨다" 이런 꼬리표가 붙는다. msi나 exe는 잠재적인 범죄자로 취급되며, 특히 디지털 서명 같은 게 없으면 다루기가 정말 까다로워져 있다.

먼 옛날 2000년대 중반엔 Windows XP에 보안 업데이트가 행해져서 이렇게 '인터넷 다운로드'로 분류돼 있는 CHM(컴파일된 HTML)은 아예 화면에 표시가 되지 않게 됐다. 파일 속성을 들어가서 '차단 해제'를 해 줘야만 이들 파일도 일반 파일들과 동등하게 다룰 수 있게 된다.
(XP도 초창기엔 읽기 전용 매체인 CD도 아니고 USB 메모리가 autorun.inf 실행이 됐을 정도로 UI 차원에서의 보안이 굉장히 막장이긴 했다. 이것도 다 훗날 보안 업데이트를 통해 막혔음.)

그나저나 저런 '다운로드 파일' 보안 속성은 운영체제 내부에서는 어떻게 구현되어 있을까?
가장 간단하게 생각할 수 있는 방법은 도스 시설부터 존재했던 파일 속성이다. 일명 ARHS(기록, 읽기 전용, 숨김, 시스템)의 형태로 존재하던 것 말이다. 실제로 Windows에는 이것 말고도 압축/암호화 등 내부적으로 쓰이는 속성이 더 있다.

하지만 다운로드 속성은 그런 비트 형태의 속성으로 구현되어 있지는 않다.
바로 파일 시스템 차원에서 제공되는 대체 데이터 스트림이 해당 파일에 꼬리표처럼 붙는데 거기에 있는 zone identifier가 이 파일이 인터넷에서 왔음을 나타낸다.

대체 데이터 스트림은 당장 내 컴퓨터의 다운로드 디렉터리에서 DIR /r을 하면 정체를 확인할 수 있다. 내 기억이 맞다면 CreateFile 함수로 저 대체 스트림의 내용을 바로 확인할 수도 있으며, IZoneIdentifier 인터페이스 등을 얻어서 이것을 조작할 수도 있다. 물론 저 꼬리표를 제거하는 것도 포함해서 말이다. 자세한 방법 소개는 The Old New Thing 블로그 내용을 링크하는 것으로 대체하겠다.

이런 기능이 과거의 FAT 계열 파일 시스템에서 가능했을 것 같지는 않고.. 언제 도입되었는지는 잘 모르겠다. 최소한 Windows 9x 시절의 IE 6 미만에는 없었던 것 같다.

Posted by 사무엘

2015/04/05 08:35 2015/04/05 08:35
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1080

« Previous : 1 : ... 122 : 123 : 124 : 125 : 126 : 127 : 128 : 129 : 130 : ... 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:
3053706
Today:
2013
Yesterday:
2713