« Previous : 1 : ... 81 : 82 : 83 : 84 : 85 : 86 : 87 : 88 : 89 : ... 230 : Next »

Windows의 Region

Windows API에는 region(영역)이라는 일종의 자료구조 라이브러리가 있다. 얘는 2차원 래스터(픽셀/비트맵) 그래픽 공간에서 각 픽셀별로 "영역에 포함되냐 안 되냐"라는 일종의 '집합'을 표현한다.
그리고 운영체제는 이 자료구조를 이용하여 각종 그래픽이 그려지는 영역을 정한다. 즉, region은 클리핑(clipping) 영역을 표현하는 데 쓰인다는 것이다.

도스 시절의 여느 그래픽 라이브러리에도 간단한 사각형 영역에만 그림이 그려지게 하는 초보적인 수준의 클리핑 기능은 있었다. 하지만 Windows의 region은 여러 사각형이 겹친 것, 임의의 다각형, 원 등 아무 모양이나 표현하고, 그 영역 안에만 그림이 그려지게 만들 수 있다.

그도 그럴 것이 Windows 같은 GUI 운영체제라면 창들의 Z-order 같은 걸 구현하는 과정에서 밥 먹고 맨날 하는 짓이 정교한 클리핑일 수밖에 없게 된다. 뒤쪽에 있는 창 내용은 앞쪽에 있는 창의 영역을 침범하지 않고 그려져야 하기 때문이다. 그러니 그런 기능을 사용자들에게도 쓰라고 제공해 주는 게 결코 이상한 일이 아니다.

region은 가장 먼저, (1) 직사각형, 타원, 모서리가 둥근 직사각형, 다각형(CreatePolygonRgn, CreatePolyPolygonRgn)처럼.. 속이 닫힌 도형을 그리는 다양한 API를 통해 생성할 수 있다. 당연히 그 도형의 모양이 영역의 모양이 된다.

다음으로, GDI가 제공하는 기능인 (2) path로부터 region을 생성할 수 있다(PathToRegion). path란 마치 윤곽선 글꼴 글립처럼 직선(MoveTo, LineTo)과 곡선(PolyBezirTo)을 임의로 조합하여 어떤 궤적이나 경계선을 기술하는 자료구조이다. region과 달리 벡터 기반이며, 별도의 자료구조로 존재하는 게 아니라 DC의 내부 상태에 종속인 형태로 보관된다는 차이가 있다.

path를 사용하면 경계선이 베지어 곡선인 region을 만들 수 있으며, TextOut 같은 글자 출력 함수를 path에다 넣으면 임의의 글자의 윤곽선도 따서 고스란히 region으로 만들 수 있다. 커다란 두 글자를 포개 놓은 뒤, 겹치는 영역만 다른 색깔로 칠하는 게 region으로는 가능하다. 그 비결은 바로...

사용자 삽입 이미지

(3) CombineRgn이라는 함수를 통해 region 간에 일종의 집합 연산을 할 수 있기 때문이다. 두 region의 교집합, 합집합, 차집합을 구함으로써 더 복잡한 형태의 region을 만들 수 있다.

위의 그림을 보아라. 운영체제나 하드웨어 차원에서 제공되는 layer이나 alpha channel 합성 같은 걸 쓴 게 아니다. 옅은 회색(B), 짙은 회색(S), 검정(겹침)이 차지하는 영역을 2차원적으로 완전히 따로 떼어내서 서로 완전히 다른 색깔과 무늬로 칠할 수 있다. 뚝 떨어진 영역도 당연히 같이 감안해서 말이다. 이런 게 평범한 글자 찍기 API로는 가능하지 않을 것이다.
다만, region은 anti-aliasing을 지원하지 않는 boolean 흑백 자료구조이다 보니, 글자 경계가 거친 것은 아쉬운 점이며 요즘 그래픽 기술의 트렌드와 맞지 않다.

그리고 끝으로.. region은 내부 자료구조를 어느 정도 노출해 주고 있기까지 하다. 그래서 그걸 직통으로 저장하고 불러오는 식으로 생성할 수도 있다. 데이터를 얻는 함수는 GetRegionData이고, (4) 그걸로부터 region을 다시 생성하는 함수는 ExtCreateRegion이다.

어떤 방식으로 region을 생성했건, 얘는 내부적으로 크게 세 부류로 나뉜다. region을 생성하거나 받아들이는 함수들이 그 region의 유형을 리턴값을 통해 알려주기도 한다.

  • 아무 영역도 없는 공집합인 NULLREGION
  • 직교좌표 직사각형 하나로만 구성된 SIMPLEREGION
  • 그 외의 다른 모든 모양을 표현하는 COMPLEXREGION.. 얘는 내부적으로 2개 이상의 사각형, 아니 scan line들로 구성된다.

자연에서 관찰되는 힘이라는 것들이 중력이나 원자력과 관계 있는 게 아니면 나머지는 출처가 몽땅 전자기력이듯이(폭발력, 마찰력, 탄성, 자석, 정전기, 표면장력, 생물 근육 등등등..),
그리고 사람이 생성(...)되는 방식이 흙을 빚어서 직통, 여자의 씨 같은 극소수 예외를 제외하면 나머지 수십~수백 억의 인간들은 몽땅 남자의 씨 기반이듯이.. 그런 것처럼 region도 일상생활에서 보는 단순하지 않은 물건들은 몽땅 complex라고 생각하면 되겠다.

region이 내부적으로 구현된 방식의 특성상(벡터/오브젝트 기반이 아닌 비트맵/픽셀 스캔라인 기반) 무한을 구현할 수 있지는 않으니, 집합 연산에서도 not 연산인 여집합이 지원되지는 않는 걸 볼 수 있다. 차라리 이미 있는 집합끼리 차집합이 대신 지원되고 말이다.

region을 식별하는 핸들 내지 포인터 자료형은 HRGN이다. 그런데 Create...처럼 HRGN을 리턴값으로 주는 함수 말고, Get...Rgn, CombineRgn 이런 이름이면서 HRGN을 인자로 받는 함수들은... 이미 있는 HRGN에다가 값만 바꿔서 넣어 준다. 그런 함수를 쓰려면 null region 하나라도 미리 미리 생성해서 전해 줘야 한다.

그런데 Windows API에는 많고 많은 region 생성 함수들 중에 null region만을 달랑 생성하는 함수는 의외로 없다. 좌표가 모두 0인 직사각형.. CreateRectRgn(0,0,0,0) 이게 그냥 텅 빈 region을 생성하는 역할을 한다. 좀 교묘한 점이다.

그럼 이 region는 어떤 용도로 쓰이며, 할 수 있는 일이 무엇일까? 본인이 생각하기에 다음과 같다.

1. 단독

그냥 저거 자체만으로 뭔가 2차원 공간 상의 기하/집합 알고리즘 구현체로.. 다른 GDI API와의 연계 없이, 심지어 명령 프롬프트용 프로그램에서도 쓰일 수 있다. 펜, 브러시, 글꼴, 비트맵 같은 타 오브젝트들이 DC와의 연계 없이는 거의 쓸모없는 것과 굉장히 대조적이다.
하지만 region이 그렇게 단독으로 쓰이는 경우는 그리 많지 않아 보인다.

2. 창의 invalid 영역 표현

어떤 창의 앞을 가리던 다른 창이 없어지고 내 창의 내용이 다시 그려지게 됐을 때.. 그 이름도 유명한 WM_PAINT 메시지가 날아온다.
BeginPaint와 함께 제공되는 DC는 창 전체가 아니라 정확하게 다시 그려져야 하는 영역에만 그림이 그려지도록 클리핑 처리가 돼 있는데, 이 영역이 말 그대로 region으로 표현되며 GetUpdateRgn 함수를 통해 얻어 올 수 있다.

WM_PAINT 때 이 영역에 대해서 PtInRegion이나 RectInRegion을 적절히 호출하면서 그림을 그리면, 무식하게 화면 전체를 그리는 것보다 프로그램 성능과 반응성을 향상시킬 수 있다.
물론 DC 차원에서 클리핑 처리가 되는 것만으로도 화면 전체를 그리는 것보다는 속도가 향상되지만, 애초에 그리기 요청을 안 하고 CPU 계산을 안 하는 게 더 낫기 때문이다.

3. 내부 클리핑

운영체제뿐만 아니라 사용자 역시 임의의 region을 생성해서 클리핑 용도로 쓸 수 있다. 비트맵이 사각형 모양이 아니라 원 모양으로만 뿌려진다거나, 특정 글자 모양으로만 뿌려지게 할 수 있다는 것이다.
그렇게 하려면 HRGN을 DC에다가 지정하면 된다. 이것은 SelectObject로 해도 되고 SelectClipRgn으로 해도 된다. 완전히 동일하다.
단지, 클리핑을 해제하는 것은 SelectClipRgn로만 가능하다. HRGN 값으로 NULL을 전해야 하기 때문이다. null region은 그림이 전혀 그려지지 않게 하는 효과를 낼 테니까..

HRGN은 기술적으로는 HPEN, HBRUSH, HBITMAP, HFONT와 마찬가지로 여러 GDI 오브젝트 중 하나로 취급된다. 상술한 바와 같이 DC에 SelectObject 될 수 있으며, 소멸 함수가 DeleteObject인 것까지도 동일하다.
하지만 얘는 다른 오브젝트들과 달리 select나 get 될 때 내부 메모리가 복사될 뿐, 핸들값 자체를 주고 받지는 않는다. 즉, HRGN은

HRGN oldRgn = (HRGN)SelectObject(dc, newRgn);
(.....)
SelectObject(dc, oldRgn);

이런 식으로 운용되지 않는다는 것이다. 옛날 핸들값을 보관하고 되돌리는 식의 절차가 필요하지 않다.
DC와 region은 서로 따로 논다. 이렇게 설정한 뒤에 원래 있던 HRGN 핸들은 곧장 삭제해 버려도 된다.

본인은 MFC의 CGdiObject처럼 GDI 객체 핸들만 한데 뭉뚱그린 템플릿 클래스를 만들어서 쓰고 있다. (소멸자에는 DeleteObject가 있고..)
그런데 다른 오브젝트들은 template<T> Handle(T v=NULL) 이런 식으로 NULL이 default 인자인 생성자를 만들어서 초기화할 수 있는 반면,
HRGN에 대해서는 인자가 없는 경우에 대해 specialize된 생성자를 따로 만들어서 이때는 null region을 생성해 놓게 했다. 그래야 이놈을 region을 얻어 오는 다른 함수에다가 인자로 줄 수 있기 때문이다.

4. 칠하고 그리는 공간

그리고 region 자체가 도형을 나타내니 그 모양대로 클리핑이 아니라 내부를 칠하는 용도로 응당 활용할 수 있다. 내부를 칠하는 FillRgn과, 내부의 경계선을 그려 주는 FrameRgn이라는 함수가 제공된다.
흥미로운 것은 경계선을 그릴 때도 pen 대신 brush가 쓰인다는 것이다. region은 path와 달리 벡터 드로잉이 아니기 때문이다. 경계선은 그냥 픽셀 차원에서 색깔이 변하는 곳을 얼추 감지해서 표시해 주는 것일 뿐이다.

5. 창 자체의 외형

끝으로, region은 윈도우의 모양을 지정하는 용도로도 쓰인다. 통상적인 사각형 모양이 아니라 리모콘 같은 다른 물건처럼 생긴 프로그램 창, 현란한 스플래시 윈도우, 내부에 구멍까지 있는 윈도우.. 전부 SetWindowRgn 함수의 산물이다.
한번 SetWindowRgn에다 전해 준 HRGN은 이제 운영체제가 관리하기 때문에 사용자가 DeleteObject 하지 말아야 한다고 문서에 거듭 명시되어 있다.

SetWindowRgn를 지정하는 순간부터 그 창은 운영체제가 non-client 영역과 테두리 경계에 기본 제공하는 각종 테마와 반투명 프레임, 둥그런 테두리, 그림자 효과들로부터 완전히 열외된다. 그 대신 고전 테마의 완전 무미건조한 테두리만이 그려진다. 일반적으로는 당연히 아래처럼 그려질 프로그램 창이 위처럼 그려지게 된다는 뜻이다.

사용자 삽입 이미지

그런 창은 어차피 non-client 영역이 전혀 없이 외형을 사용자가 완전히 customize하는 형태로 쓰일 테니 말이다. 제목 표시줄과 테두리의 외형은 보급품 그대로이면서 중앙에만 region을 지정해서 총알 구멍 같은 게 숭숭 뚫린 프로그램 창 같은 건 만들 수 없다는 뜻이다.

보통은.. 공용 컨트롤 6.0 매니페스트가 없는 프로그램의 경우, 버튼 같은 컨트롤들이 구닥다리 고전 스타일로 그려지고 non-client 영역은 운영체제가 자동으로 쌈빡하게 그려 준다. (그림에서 아래 오른쪽) 그런데 SetWindowRgn을 지정하면 반대로 컨트롤들은 정상적으로 그려지는데 non-client 영역이 고전 스타일로 돌아간다는 게 흥미롭다.

단, 지난 Windows 2000부터는 SetWindowRgn가 아닌 다른 방법으로도 사각형 모양이 아닌 윈도우를 표현할 수 있게 되었다. 바로 layered window이다. WS_EX_LAYERED 스타일을 준 윈도우에다가 SetLayeredWindowAttributes 함수를 호출하면 (1) 이 창에 대해서 투명도를 지정할 수 있고, (2) 특정 RGB 값을 color key로 지정해서 그 색깔은 투명으로 처리할 수 있다. 배경색을 칠하는 것만으로 그 부위가 투명해지니, 번거로운 region보다 훨씬 더 편리해지기까지 했다.

과거 MS Office 97~2000 시절의 흑역사 중에는 'Office 길잡이'라는 "취지만 좋았다" 급의 물건이 있었다. 애니메이션 캐릭터가 화면에 나타나서 돌아다니는데.. 첫 도입되었던 97은 캐릭터가 사각형 창 안에 갇힌 형태였던 반면, 2000부터는 배경 없이 창이 시시각각 애니메이션 캐릭터 모양으로 변했다.

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

이게 Windows 2000에서는 하드웨어빨을 탄 layered window로 비교적 간편하게 구현됐던 반면.. 9x에서는 일일이 window region을 바꿔 가면서 동작했다. 그 밑의 창은 매번 WM_PAINT가 발생하면서 성능 페널티가 장난이 아니었을 것이다.

본인은 먼 옛날에 이런 프로그램도 구경한 적이 있었다. 실행하면 만화영화 그림체로 그려진 병아리 한 마리가 마치 저 Office 길잡이처럼 튀어나왔는데, 그냥 가만히 있는 게 아니라 각종 프로그램 창들 위를 돌아다녔다. 나름 중력도 구현했던지라, 마우스로 집어다가 공중에다 옮겨 놓으면 아래의 근처에 있는 창으로 떨어지기까지 했다. 내 기억이 맞다면 꽤 재미있는 프로그램이었는데.. 지금 인터넷으로 다시 검색하고 구할 길이 없다.

이 프로그램에 대해서 본인이 현재 기억하고 있는 건 아마 일본에서 만들어진 걸로 추정된다는 것, 그리고 무려 Windows 3.x용 16비트 프로그램이었다는 것이다. 그 열악한 Windows 3.x에서도 타이머를 걸어서 최소화 아이콘에다가 애니메이션을 구현하고, 창의 경계가 시시각각 곡선 윤곽으로 바뀌는 저런 액세서리 프로그램이 만들어지기도 했다.

이렇듯, SetWindowRgn을 이용해서 이런 재미있는 활용을 할 수 있는데.. 날개셋 한글 입력기에서 사각형이 아닌 모양의 창이 나타나는 곳은 마우스 휠을 눌렀을 때 나타나는 동그란 자동 스크롤 앵커가 유일하다.

사용자 삽입 이미지

에디트 컨트롤은 자동 스크롤 모드가 없고, MS 오피스 제품들은 동그란 테두리 없이 그냥 배경에다가 회색 화살표가 나타나는 듯하지만.. 마소에서 만든 웹 브라우저(IE, Edge)에서는 앵커 윈도우가 나타난다. 날개셋의 앵커 윈도우도 먼 옛날에 얘를 참고해서 만들어진 것이다. 맨 처음에는 region만 쓰다가 이내 layered window도 사용하도록 형태가 바뀌었다.

여담: 좌표계 관련 문제

아, region의 경계면과 관련해서 주의해야 할 점이 있다.
같은 좌표를 줬을 때, 직사각형은 pen으로 그려지는 테두리와 region의 영역이 픽셀 단위로 정확하게 일치한다. 다시 말해 같은 RECT rc에 대해서 CreateRectRgn + SetClipRgn을 한 뒤에Rectangle을 호출한 결과는 클리핑을 안 했을 때와도 동일하다.

하지만 타원(Ellipse vs CreateEllipticRgn)이나 폴리곤(Polygon vs CreatePolygonRgn) 같은 다른 도형에 대해서는 이것이 성립하지 않는다. region은 오른쪽과 아래 끝의 1픽셀이 미묘하게 잘린다.

//직사각형
CRgn rg; CRect rc(10, 10, 80, 80);
rg.CreateRectRgnIndirect(rc);
dc.SelectClipRgn(&rg); dc.Rectangle(rc);
rc.OffsetRect(40, 40); dc.Rectangle(rc);

//원
CRgn rg; CRect rc(110, 10, 180, 80);
rg.CreateEllipticRgnIndirect(rc);
dc.SelectClipRgn(&rg); dc.Ellipse(rc);
rc.OffsetRect(40, 40); dc.Ellipse(rc);

//폴리곤으로 표현한 직사각형
CRgn rg; POINT pt[4] = {
{10, 100}, {80, 100}, {80, 170}, {10, 170} };
rg.CreatePolygonRgn(pt, 4, ALTERNATE);
dc.SelectClipRgn(&rg); dc.Polygon(pt, 4);

이 코드를 실행한 결과는 다음과 같이 차이가 난다.

사용자 삽입 이미지

폴리곤으로 직사각형 좌표를 지정해 줘도, 아예 직사각형 전용 생성 함수를 줬을 때와 달리, region은 영역이 살짝 덜 생긴다. 그래서 그 region 안에서 동일 좌표로 도형을 직접 그려 보면 테두리의 오른쪽과 아래쪽이 잘린다.

이를 감안해서 원형 region을 생성할 때는 그리기 함수일 때보다 1픽셀 정도 더 크게 원을 그리면 잘리는 현상은 막을 수 있다. 하지만 그래도 그리기 함수와 region 함수는 경계 계산 결과가 서로 미묘하게 달라서 직사각형일 때처럼 깔끔하게 일치하는 모양이 나오지 않는다.
그러니 region 자체의 경계를 그려 주는 FrameRgn 함수를 대신 쓸 수밖에 없다. 허나, 얘가 그려 주는 테두리는 전문적인 원 그리기 함수에 비해 표면이 거칠며 별로 예쁘지 않다.

본인은 처음에 이런 특성을 몰라서 한동안 삽질을 했었다. 이럴 때도 region 대신 layered window는 순수하게 그리기 결과에 따라서 투명색을 자동으로 처리해 주니 더욱 유용하다.

Posted by 사무엘

2019/04/12 08:34 2019/04/12 08:34
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1607

1. 재부팅 이벤트

본인은 지금까지 Windows가 시스템 종료/재시작/로그아웃 될 때.. 메시지에 즉각 응답하면서 정상적으로 돌아가는 프로그램이라면 종료 처리도 당연히 정상적으로 되는 줄로 알고 있었다.
사용자가 [X] 버튼이나 Alt+F4를 눌러서 종료할 때와 완전히 동일하게 말이다. WM_CLOSE에 이어 WM_DESTROY, WM_NCDESTROY가 날아오고, WM_QUIT이 도달하고, message loop이 종료되고, MFC 프로그램으로 치면 ExitInstance와 CWinApp 소멸자가 호출되고 말이다.

왜냐하면 시스템이 종료될 때 발생하는 현상이 언뜻 보기에 정상적인 종료 과정과 동일했기 때문이다. 저장되지 않은 문서가 있는 프로그램에서는 문서를 저장할지 확인 질문이 뜨고, 운영체제는 그 프로그램 때문에 시스템 종료를 못 하고 있다고 통보를 한다. 좋게 WM_CLOSE 메시지를 보내는 걸로 반응을 안 하는 프로그램에 대해서만 운영체제가 TerminateProcess 같은 강제 종료 메커니즘을 동원할 것이다.

그런데 알고 보니 그게 아니었다.
정상적인 종료라면 ExitInstance 부분이 실행되어야 할 것이고 프로그램 설정들을 레지스트리에다 저장하는 부분도(본인이 구현한..) 실행돼야 할 텐데, 내 프로그램이 실행돼 있는 채로 시스템 종료를 하고 나니까 이전 설정이 저장되어 있지 않았다.

꼭 필요하다면 WM_ENDSESSION 메시지에서 사실상 WM_DESTROY 내지 ExitInstance와 다를 바 없는 cleanup 작업을 해야 할 듯했다. 뭐, 날개셋 한글 입력기에서 이 부분이 반영되어 수정돼야 할 건 딱히 없었지만 아무튼 이건 내 직관과는 다른 부분이었다.

시스템이 종료될 때 발생하는 일은 딱히 디버거를 붙여서 테스트 가능하지 않다. =_=;; 로그 파일만 기록하게 해야 하고, 매번 운영체제를 재시작해야 하니 가상 머신을 동원한다 해도 몹시 불편하다. 꽤 오래 전 일이 됐다만, 날개셋 외부 모듈이 특정 운영체제와 특정 상황에서 시스템 종료 중에 운영체제 시스템 프로세스 안에서 죽는 문제가 발생해서 디버깅을 한 적이 있었다. 몹시 골치 아팠었던 걸로 기억한다.

2. 파일의 동등성 비교

파일 이름을 나타내는 어떤 문자열이 주어졌을 때,

  • 상대 경로(현재의 current directory를 기준) vs 절대 경로
  • 과거 Windows 9x 시절이라면 ~1 같은 게 붙은 8.3 짧은 이름 vs 원래의 긴 이름
  • 대소문자 (Windows는 대소문자 구분이 없으므로. 그런데 이것도 A~Z 26자만 기계적으로 되나? 언어별로 다른 문자의 대소문자는?)
  • 그리고 옵션으로는 정규 DLL search path와 환경 변수

이런 것들을 몽땅 다~ 감안해서 다음과 비스무리한 일을 하는 가벼운 API가 좀 있으면 좋겠다. 보다시피 동일한 파일을 표현하는 방법이 굉장히 다양하기 때문이다.

  • 두 개의 파일 명칭이 서로 동일한 파일인지 여부를 판단. 당연히 파일을 직접 open/load하지 않고 말이다.
  • 그 파일의 대소문자 원형을 유지한 절대 경로. 일명 '정규화된' 이름을 되돌린다. 이게 동일한 파일은 물리적으로, 절대적으로 동일한 파일임이 물론 보장된다.
    참고로 Windows API 중에서 얼추 비슷한 일을 한다고 여겨지는 GetFullPathName이나 GetModuleFileName 함수는 절대 경로 말고 파일의 대소문자 원형 복원이 제대로 되지 않는다.
  • 혹은 그 파일의 시작 지점이 디스크에서 물리적으로 어디에 있는지를 나타내는 64비트 정수 식별자를 구한다. 포인터가 아니지만 파일의 동등성 여부 판단은 가능한 값이다. 정수를 넘어 UUID급이어도 무방함.

본인은 비슷한 목적을 수행하기 위해, 그냥 두 파일을 CreateFileMapping으로 열어 보고 리턴된 주소가 동일하면 동일한 파일로 간주하게 한 적이 있었다. 핸들 말고 MapViewOfFile 말이다. 본질적으로 동일한 파일이라면 운영체제가 알아서 같은 주소에다 매핑을 하고 레퍼런스 카운트만 증가시킬 테니까..
Windows 9x에서는 주소가 시스템 전체 차원에서 동일하겠지만 NT 계열에서는 한 프로세스 내부에서만 동일할 것이다.

하지만 내가 필요한 건 파일의 내용이 아니라 그냥 두 파일의 동등성뿐인데 이렇게 매핑을 하는 건 overkill 삽질 같다는 인상을 지울 수 없었다.
비슷한 예로, LoadLibrary도 같은 파일에 대해서는 같은 리턴값이 돌아온다. HMODULE도 오늘날은 핸들이 아니라 메모리 map된 주소이니까.. 다만, 오버헤드를 줄인답시고 LoadLibraryEx + LOAD_LIBRARY_AS_DATAFILE 이렇게 열어서는 안 된다. 그러면 로딩 방식이 크게 달라지더라.

3. JNI에서 문자열 처리하기

Java 언어는 JNI라고 해서 자기네 바이트코드 가상 머신이 아닌 C/C++ 네이티브 코드를 호출하는 통로를 제공한다.
프로그램 자체를 C/C++로 짜던 시절에는 극한의 성능을 짜내야 하는 부분에 어셈블리어를 집어넣는 게 관행이었는데.. 이제는 일반적인 코딩은 garbage collector까지 있는 상위 계층에서 수행하고, 극한의 성능을 짜내야 하는 부분에서만 C/C++ 코드를 호출한다는 게 흥미롭다.

JNI는 그냥 언어 스펙에 가까운 광범위한 물건이다. Windows 환경에서는 그냥 Visual C++로 빌드한 DLL이 export하는 함수를 그대로 연결할 수도 있다. 물론 그 DLL을 빌드하기 위해서는 Java SDK에서 제공하는 jni 인터페이스 헤더와 static 라이브러리를 사용해야 한다.
한편, 안드로이드 앱 개발에서 쓰이는 NDK는 JNI 스펙을 기반으로 자체적인 C++ 컴파일러까지 갖춘 네이티브 코드 빌드 도구이다.

Java의 문자열은 JNI에서는 jstring이라고 내부 구조를 알 수 없는 자료형의 포인터 형태로 전달된다. C++에서는 UTF-8과 UTF-16 중 편한 형태로 바꿔서 참조 가능하다.
UTF-8로 열람하려면 JNIEnv::GetStringUTFChars를 호출하면 된다. 길이를 알아 오려면 GetStringUTFLength부터 호출한다. 전해받은 문자열 포인터는 ReleaseStringUTFChars로 해제한다.

그 반면, UTF-16 형태로 열람하려면 위의 함수 명칭에서 UTF를 빼면 된다. GetStringChars, GetStringLength, ReleaseStringChars의 순이다. Java가 내부적으로 문자를 2바이트 단위로 처리하기 때문에 이들이 주로 취급하는 자료형은 jchar*이다. 그러니 얘는 char16_t 자료형과 호환된다고 간주해도 좋다. 참고로 wchar_t는 NDK 컴파일러의 경우 4바이트로 처리되더라.

UTF-16이나 UTF-8이나 다 UTF이긴 마찬가지인데, Java는 변별 요소인 8을 생략하고 함수 이름을 왜 저렇게 지었나 개인적으로 의구심이 든다. 물론 GetStringChars는 Java가 내부적으로 문자열을 원래부터 2바이트 단위로 처리하다 보니 우연히 UTF-16과 대응하게 됐을 뿐, 대놓고 UTF-16을 표방했던 건 아닐 것이다. 뭐, 이제 와서 그 체계를 바꾸는 건 불가능하고 "자바 문자열 = 2바이트 단위"는 완전히 고정되고 정착했지만 말이다.

또한 GetStringChars는 GetStringUTFChars와 달리 굉장히 치명적으로 불편한 단점이 하나 있다. 바로.. 변환된 문자열이 NULL-terminated라는 보장이 없다는 것이다!
그래서 본인은 이 포인터를 사용할 때 메모리를 n+1글자만치 또 할당해서 null문자를 추가해 주는 매우 번거로운 두벌일을 하고, 아예 클래스를 이렇게 따로 만들어야 했다. 좀 개선의 여지가 없으려나 모르겠다.

class CJstrToString16 {
    JNIEnv *_ev;
    jstring _jstr;
    const jchar *_ret;
    char16_t *_arr;
public:
    CJstrToString16(JNIEnv *ev, jstring js): _ev(ev), _jstr(js) {
        jsize n = ev->GetStringLength(js);
        _ret = ev->GetStringChars(js, NULL);
        _arr = new char16_t[n+1];
        memcpy(_arr, _ret, n*sizeof(char16_t));
        _arr[n] = 0; //고작 요거 하나 때문에..
    }
    ~CJstrToString16() {
        ev->ReleaseStringChars(_jstr, _ret);
        delete[] _arr;
    }
    operator const char16_t*() const { return _arr; }
};

4. Visual C++의 STL

C++은 타 프로그래밍 언어들과 달리, 심지어 전신인 C와도 달리, 언어가 개발되고 나서 자신의 특성을 잘 살린 라이브러리가 언어 차원에서 붙박이로 곧장 제정되지 않았던 모양이다. 그래서 각 컴파일러들이 중구난방으로 파편화된 형태로 라이브러리를 제공해 오다가.. 표준화라는 게 1990년대 말이 돼서야 논의되기 시작했다.

템플릿이 추가되어 C++에서도 제네릭, 메타프로그래밍이라는 게 가능해진 뒤부터 말이다. 처음에는 자료구조 컨테이너 위주로 STL이라는 이름이 붙었다가 나중에는 그냥 C++ library가 된 걸로 본인은 알고 있다.

Windows용으로 가장 대중적인 C++ 컴파일러야 두 말할 나위 없이 MS Visual C++이다. 얘는 거의 20여 년 전 6.0 시절부터 P.J. Plauger라는 사람이 구현한 C++ 라이브러리를 제공해 왔다. C 라이브러리와 달리 C++ 라이브러리는 소스가 비교도 안 될 정도로 복잡하고 난해하다는 것(암호 같은 템플릿 인자들..=_=), 그리고 저렇게 마소 직원이 아닌 개인 이름이 붙어 있다는 게 인상적이었다. 2000년대 초까지만 해도 휴렛-패커드라는 회사명도 주석에 기재돼 있었다.

P.J. Plauger는 현재는 Dinkumware라고 C++ 라이브러리만 전문적으로 관리하고 라이선스 하는 회사를 설립해 있다. 나이도 생각보다 지긋한 듯..
그런데 이런 세계적인 제품에 들어가는 라이브러리가.. 성능이 의외로 시원찮은가 보다. Visual C++이 제공하는 컨테이너 클래스가 유난히도 느리다고 까이는 걸 여러 사이트에서 봐 왔다.

최근에는 본인 직장의 상사마저도 같은 말씀을 하시기에 "헐~!" 했다. 업무상 필요해서 string, set, map 등을 써서 수십, 수백 MB에 달하는 문자열을 분석하는 프로그램을 돌렸는데, 자료 용량이 커질수록 속도가 급격히 느려져서 자료구조를 직접 새로 짜야 할 판이라고 한다.

난 개인적으로는 C++ 라이브러리를 거의 사용하지 않고, 더구나 그걸로 그 정도까지 대용량 작업도 해 보지 않아서 잘 모르겠다. 그 날고 기는 전문가가 만든 코드에 설마 그런 결함이 있으려나? 아니면 컴파일러의 최적화 문제인지?

글쎄.. 이런 게 있을 수는 있다. MFC의 CString은 그냥 포인터와 크기가 동일하며 값으로 전할 때의 reference counting도 처리한다. 그러나 std::string은 자주 쓰이는 짧은 문자열을 번거로운 heap 메모리 할당 없이 빠르게 취급하기 위한 배열까지 내부에 포함하고 있다. 이런 특성을 모르고 std::string도 함수에다 매번 value로 전달하면 성능에 악영향을 줄 수밖에 없다.

그런 식으로 임시 객체가 쓸데없이 생겼다가 사라지는 구조적인 비효율이 C++ 라이브러리에 좀 있는 걸로 들었다. R-value 참조자 &&가 도입된 것도 vector의 내부 처리에서 그런 삽질을 예방하는 근거를 언어 차원에서 마련하기 위해서라지 않는가? 그리고 Visual C++이 그런 비효율을 보정하는 성능이 좀 시원찮다거나 한 것 같다. 전부 다 그냥 추측일 뿐이다.

그러고 보니 cout<<"Hello world"가 printf("Hello world")보다 코드 오버헤드가 작아지는 날이 과연 올지 모르겠다. 이것도 그냥 떡밥인 건지..?? =_=;;

Posted by 사무엘

2019/04/09 08:32 2019/04/09 08:32
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1606

요즘 컴퓨터 프로그램들은 상업용이라 해도 과거처럼 디스켓이나 씨디가 담긴 패키지 박스의 형태로 배포되는 경우가 거의 없어졌다. 바이너리의 배포 자체는 인터넷으로 하며, 패키지가 있다 해도 그 안에는 시리얼 번호 쪽지 정도나 달랑 들어있다. 그 뒤, 사용자가 구매한 카피 개수만큼만 프로그램을 동시에 구동하고 있는지, 사용 기한이 경과하지 않았는지 같은 인증은 인터넷으로 진행한다.

정식 사용자가 확인되지 않은 프로그램은 일단 평가판 모드로 동작한다. 정품 인증이 안 됐고 평가판 기간도 경과했다면 그 다음부터는 기능 제한 모드로 동작한다. 문서나 데이터를 취급하는 업무용 프로그램이라면 이제 문서를 편집할 수 없고 일종의 viewer 형태로만 동작하게 된다.

과거에는 소프트웨어 개발사들이 사용자에게 자기 제품의 기능을 자랑하는 한편으로 정품 구매에 대한 동기를 부여하기 위해 셰어웨어, 평가판, 데모 같은 것을 따로 배포하곤 했다.
하지만 지금은 인터넷이 워낙 발달한 덕분에.. subset 구분 없이 전체 제품을 통째로 뿌린다. 그 뒤 제품을 구입하고 해금 비밀번호/일련번호를 받은 사용자에게만 전체 기능이 제공되도록 한다. 아래아한글, MS Office 같은 거대한 프로그램들도 이제는 다 이런 식으로 동작하고 있다.

과거에는 그런 일련번호를 수학 공식을 기반으로 생성하곤 했다. 하지만 오프라인 환경에서 소프트웨어적인 알고리즘에만 의존하는 copy-protection은 역공학을 통해 뚫릴 수 있다. 마치 주민 등록 번호 자동 생성기처럼 말이다. 어둠의 경로를 개척하는 사람들이 이만저만 똘똘한 게 아니기 때문이다.

하지만 인터넷이 소프트웨어의 배포와 불법 복제만 편하게 만든 건 아니며, 까놓고 말해 개발사가 사용자의 사용 패턴을 미주알고주알 파악하는 것도 더 용이하게 만들었다. (뭐, 서버 유지 비용은 부담해야 하지만..) 제아무리 클라이언트 프로그램을 복제하고 뿌려도 온라인 게임을 돈 안 내고 즐기는 건 불가능하며, 스타크래프트 불법 복제 립버전으로 배틀넷 접속은 할 수 없다. 또한, 주민 등록 번호는 생성하더라도 방대한 DB에 일일이 접속하는 실명 인증은 수학 공식만으로 뚫을 수 없지 않던가? 그런 식으로 창과 방패는 발전하는 것 같다.

물론 업무용 프로그램들은 온라인 게임과 달리 업데이트 체크나 인증을 할 때만 인터넷에 접속하지, 나머지 동작은 전부 어차피 오프라인에서 행해지는 게 대부분이다. 그런 부류라면 실행 파일을 분석해서 인증을 시도하는 부분만 변조하고 크랙해 버릴 수도 있다. 파일이 전부 암호화되지 않고 헤더에만 암호가 기록돼 있다면 그 부분만 건너뛰어 버리면 되듯이 말이다. 그렇기 때문에 오프라인에서의 소프트웨어 보안도 예나 지금이나 여전히 필요하다.

이렇게 소프트웨어의 정품 사용 여부를 파악하기 위해서 반드시 필요한 절차가 있다. 바로 프로그램이 돌아가고 있는 기기 내지 제품의 사용자를 중복 없이 유일하게 식별하는 것이다!
매번 변하는 난수 씨앗이야 그냥 현재 시각을 기반으로 생성한다고 하지만, 한 컴퓨터에만 고유하게 적용되는 시리얼 키 같은 걸 생성하려면 전세계에서 유일하고 불변하고 전무후무한 식별 번호가 하드웨어 차원에서 부여되어 있어야 한다.

하다못해 예전에 도스용 게임 중에서도 저장 기능이 없는 대신, 각 레벨별로 암호 코드가 부여되는 게 있었다. 그 코드값을 알면 나중에 상위 레벨에서부터 게임을 시작할 수 있다.
그런데 문제는 그 코드값이 컴퓨터마다 제각각으로 생성된다는 것.. 그러니 매 컴퓨터에서 게임을 새로 시작해서 상위 레벨에 직접 진입을 해야만 코드값을 알 수 있었다.

그리고 어떤 소프트웨어가 정품 인증을 받았다거나, 셰어웨어의 경우 등록판이 생성되었는데.. 그 인증 정보는 레지스트리나 파일 형태로 저장되곤 한다. 물론 꼼꼼하게 암호화해서 말이다.
그 인증 정보에는 당연히 특정 컴퓨터의 식별자도 들어있어야 할 것이다. 그래야 그 인증 정보만 다른 컴퓨터에다가 슬쩍 복사해서 집어넣더라도 명의가 도용되지 않을 테니 말이다.
이런 식으로 컴퓨터의 고유 식별자는 프로그램 개발자의 입장에서는 매우 다양하게 활용될 수 있다.

자동차에는 외부에 노출된 번호판과 별개로, 자동차 껍데기 자체를 식별하는 차대 번호라는 게 있어서 엔진룸이나 도어 한구석에서 확인할 수 있다.
노트북 PC는 시리얼 번호가 밑바닥에 적혀 있다. 그리고 스마트폰도 기기를 식별하는 고유 번호가 있는 건 마찬가지이다. 사용자를 식별하는 USIM과는 당연히 별개로 말이다.

그런데 PC는 컴퓨터를 유일하게 식별하는 깔끔한 단일 통합 메커니즘이 의외로 존재하지 않는다.
먼 옛날에 펜티엄 3~4 시절에는 CPU의 일련번호를 얻어 오는 명령이 있었던 듯하나.. 예제 코드가 어셈블리어여서 이식성이 전혀 없으며, 요즘 CPU에서는 통하지도 않는다고 한다.

컴퓨터를 식별하기 위해서 지금까지 제일 만만하게 쓰여 온 방법은 맥 어드레스(mac address)라는 48비트짜리 숫자이다. 요즘은 휴대전화 번호가 사람을 식별하는 준 주민 등록 번호나 마찬가지이지 않는가? 그것처럼 통신망에서의 주소는 기기 식별 용도로 나쁘지 않은 선택이다.
하지만 얘를 쓰기에는 요즘 컴퓨터 네트워크는 계층과 종류가 너무 다양해졌으며, 사용자가 값을 변조도 그리 어렵지 않게 할 수 있기 때문에 여러 모로 약발이 다했다.

하드웨어적인 방법에만 너무 의존하면.. 사용자가 램을 더 달거나 하드디스크를 교체한 것만으로 프로그램 정품 인증이 실패하는 불상사가 벌어진다. 도대체 한 컴퓨터의 정체성을 결정하는 것이 무엇인가 하는 본질적인 고민에 부딪히게 된다.
소프트웨어적인 방법으로는 HKEY_LOCAL_MACHINE 상에 있는 Windows의 product ID라든가 Machine GUID가 있는데.. 이것은 변조하기 쉽고 운영체제를 다시 설치하는 것만으로도 무력화될 수 있는 게 약점이다.

최근엔 본인도 이런 쪽으로 고민할 일이 좀 있었다. 그러다가 WMI(Windows Management Instrumentation)라는 DB인지 API인지.. 정체를 알 수 없는 방대한 물건을 최근에야 난생 처음으로 접했다. 여기에 시스템 정보와 관련된 것들이 다 있었다. 하드디스크의 시리얼 번호, 마더보드의 시리얼 번호, 뭐 별별 것까지 다.. 그야말로 끝판왕이었다.

그 중 Win32_ComputerSystemProduct라는 클래스에 있는 uuid 값이.. SMBIOS, 즉 펌웨어 레벨에서 새겨져 있는 불변 유일한 컴퓨터 식별자 역할을 얼추 하겠다는 결론을 내리게 됐다. 최소한 맥 어드레스보다는 더 믿을 만하지 않을까? 명령 프롬프트에서는 wmic csproduct get uuid라고 하면 얻을 수 있다.

WMI에 접근하는 건 C#에서는 꽤 간단하고 쉽게 할 수 있어 보이던데 C++에서는 COM을 초기화하고 온갖 복잡한 인터페이스를 몇 단계씩 생성해야만 가능했다. DB 아니랄까봐, COM으로도 모자라서 팔자에 없는 SQL 쿼리까지 날려야 되더라! SELECT * from Win32_ComputerSystemProduct 같은 식으로 말이다.
누가 클래스 라이브러리 하나 만들어 놓은 것조차 없는 듯... 저 GUID 하나만 달랑 얻어 오는 용도로 쓰기에는 낭비가 꽤 심해 보인다.

Windows와 달리 mac 계열은 하드웨어/소프트웨어가 워낙 딱딱 들어맞는 일체형이니 저 정도의 복잡한 고민은 필요 없을 듯하다. 시스템 정보를 보면 나오는 시리얼 번호와 하드웨어 UUID만으로 식별과 관련된 모든 고민이 끝이지 않을까?
게다가 gethostuuid라는 함수 한 방으로 그 값을 바로 구할 수 있었다.

이미 유비쿼터스니 사물 인터넷 IoT니 뭐니 하면서 운영체제 불문하고 수많은 기기들이 인터넷에 접속하고 있으며, 그에 맞춰서 주소 공간이 월등히 넓어진 ipv6도 서서히 보급되고 있다. ipv6는 주소 공간의 크기가 UUID의 그것과 동일한 128비트이다.

그러니 Windows/mac, 그리고 안드로이드/iOS를 불문하고 전세계 전무후무 유일불변이 보장되는 기기 식별자 같은 것도 제정되지 않을까 싶다. 이미 제정돼 있는데 본인이 아직 모르는 것일 수도 있겠지만, PC 한정으로는 내가 알기로 딱 떨어지는 답은 아직까지 없다. 심증에 속하는 여러 정황상의 단서들을 모아서 물증인 것처럼 편의상 활용하고 있을 뿐이다.

Posted by 사무엘

2019/04/06 08:35 2019/04/06 08:35
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1605

1. 옛날 언어의 간결함

예전에도 한번 언급한 적이 있었지만.. 세상에 생명의 기원보다도 더 알 수 없는 게 바로 언어의 기원이다.
먼 옛날, 최초의 언어가 어떠했는지는 문헌 기록도 음성 녹음도 없고, 아예 문자조차 없으니 제대로 알 길이 없다. 이런 게 저절로 우연히 생겨날 수는 없다고 믿어 버리면 증명도 반증도 할 수 없고 그냥 개인 신념의 영역이 된다.

언어별로 차이가 있긴 하겠지만, 대체로 고대 언어는 현대의 언어보다 문법이 더 복잡하고 불규칙도 더 많고, 사용되는 음운도 더 다양했으리라고 추측된다. 그런데 한편으로는 반대로 옛날 언어가 어휘나 표현이 더 간결한 것도 있었다.

(1) 예를 들어.. 영어 고어에는 before보다 더 짧은 ere(air, heir와 같은 '에어')가 있고.. enemy보다 더 짧은 foe가 있다. 그리고 뻔한 문맥에서 목적어 같은 걸 생략도 많이 했다. 아래의 KJV 구절을 살펴보자.

  • Abimelech king of Gerar sent, and took Sarah. (창 20:2)
  • Bring these men home, and slay, and make ready. (창 43:16)

'사람을 보내어'인데 그냥 sent를 자동사인 듯이 썼으며
'가축을 잡아서'인데 그냥 slay 한 단어로만 씨크하게 표현했다.

(2) 그런데 영어 이전의 성경의 언어였던 히브리어· 그리스어 레벨에서는 이런 자비심 없는 축약이 더 많았던 모양이다.
그래서 KJV 영어 본문에서 이탤릭체 처리된 단어들을 찾아보면.. 없으면 단순히 문법적으로만 어색하거나, 아까 사람 내지 가축처럼 어렴풋이 유추 가능한 정도를 넘어서 뜻이 완전히 달라지는 것들도 있다.

시 12:5 끝부분의 "I will set [him] in safety [from him that] puffeth at him."에서 []로 둘러싸인 부분이 전부 이탤릭이다. 도대체 히브리어 원어 원문은 얼마나 암호처럼 짧게 기록됐길래 영어에서 저런 목적어와 수식어를 창작해서 집어넣어 줘야 했는지가 궁금해진다. (물론 KJV 외의 타 성경들도 동일하게 저렇게 번역했음)

(3) KJV는 they라는 짤막한 단어를 3인칭 복수 대명사로도 쓰고, people 같은 '일반적인 사람' 용도로도 쓴다. 이것 자체는 현대 영어에서도 존재하는 관행이지만, KJV는 중의성· 모호성이 느껴지기도 할 정도로 they를 즐겨 쓰는 감이 있다.
왕하 19:35를 보면.. "they가 아침에 일어나 보니 they는 모두 죽은 송장이 되어 있었더라"처럼.. 서로 다른 대상에 대해서도 똑같은 they가 쓰인다.

출 20:13 "살인하지 말라"도 타 성경들은 murder 정도를 넣었지만 KJV만은 그냥 짧은 kill이다.

(4) 영어를 비롯한 성경 언어 쪽 얘기가 길어졌는데, 반대편의 한국어도 마찬가지이다. 훈민정음 서문이라든가 이 윤탁 한글 영비 같은 걸 보면.. "영한 비라. 거운 사람은 재화를 입으리라" 말이 굉장히 짧다는 생각이 들지 않는가?
요즘 같으면 못해도 "이것은 신령한 비입니다. 이 비를 무너뜨리는 사람은 재앙을 당할 것입니다" 정도로는 풀어서 쓸 텐데? 띄어쓰기가 없는 고어체를 감안하더라도 주어 생략에다 '거우다?'라는 짤막한 단어.. 뭔가 현대 한국어의 화자는 알지 못하는 옛 한국어의 면모인 것 같다.

2. 의존명사

한국어에는 의존명사라는 게 있다. '리', '수' 같은 건 분명 고유한 뜻과 뉘앙스가 있긴 한데, 그게 구체적으로 뭔지를 설명해 보라고 하면 난감하다. 얘들은 '-ㄹ' 꼴로 활용된 용언으로부터 수식을 받은 바로 다음에만 등장하며(그럴 리, 할 수..), 특히 '리' 다음에는 '없다'만 쓰인다.

어처구니, 어이 같은 단어는 그런 통상적인 의존명사에 속하지는 않지만.. 뒤에 붙는 용언이 답정너 너무 뻔하고 다른 형태로 쓰이는 일이 없다시피하다. 그래서 '어이없다, 어처구니없다'가 그냥 한 단어로 인정될 지경이다. '쓸데없다'처럼 말이다.

한국어 맞춤법과 띄어쓰기가 너무 어렵고 일관성이 부족하다고 원성이 자자하지만.. 한국어가 단어와 형태소의 경계를 구분하는 게 그만치 어렵다. 이것 때문에 사전 편찬자와 국어 문법학자들도 고충이 많다. 용언에 극도로 제한된 뻔한 형태로만 활용되는 불완전동사가 있는 것만큼이나(더불다, 가로다, 달다) 체언에는 아주 제한된 형태로만 쓰이는 의존명사 같은 물건이 있는 셈이다.

그런데.. 이렇게 제한된 형태, 관용구 형태로만 쓰이는 명사가 영어에도 있는 것 같다. 유익· 편의라는 뜻인 sake 내지 behalf 같은 단어 말이다. 얘들은 오로지 one's sake, ?n behalf of .., for the sake of 형태로만 쓰이고 단독 내지 다른 형태로 쓰이는 일이 없다. 이것 말고 다른 예도 있지 싶다.

3. 일관성을 찾을 수 없는 혼돈의 카오스

(1) 외래어 표기법의 노답 문제

  • 자음 음절 경계: 플룻 플루트 로보트 백 태그..;; 답이 없다. ㅡ는 음가가 참 불분명한 모음이다.
  • 장모음: 윈도우 보우 리모트 보트 스노우.. [ou]라는 영어 장모음은 u를 무시하고 '오'로만 표기하는 게 원칙이지만 snow나 window 같은 단어는 여전히 '우'까지 표기한 형태가 더 익숙하다.
  • 모음 경계: washer는 와셔일까, 워셔일까? ㅏ~ㅓ, ㅗ~ㅓ 경계가 의외로 헷갈린다.

(2) 한글 맞춤법 및 발음에서 노답 문제

  • 사잇소리와 사이시옷: 비빔밥/볶음밥 중에서 왜 전자만 밥이 '빱'으로 바뀔까? 물고기/불고기 중에서 왜 전자만 '꼬' 소리가 날까? '김밥'의 발음은 밥과 빱 중에 무엇이 더 바람직할까?
  • 띄어쓰기: 한자어 복합명사들의 띄어쓰기부터가 아주 모호하다. 또한, 순우리말 중에도 '잘 만 듯 못' 요런 어절들은 단어도 되고 접사도 되기 때문에, 뒤에 '+하다' 같은 게 이어질 때 띄어쓰기 여부가 아주 구리다.

4. ㅐ와 ㅔ의 발음

이건 한국어 음운 체계에 남아 있는 희대의 미스터리이다.
원래 '아 다르고 어 다르다'만큼이나, I와 you를 구분할 정도로 완전히 다른 소리였는데 어쩌다가 요즘 사람들이 절대로 구분하지 못하는 소리의 쌍으로 전락했나 모르겠다. 서로 다르게 발음도 못 하고, 알아듣지도 못한다. 장음· 단음 구분이 망가진 것처럼 말이다.

요즘 통용되는 그 소리는 원래의 ㅐ도, ㅔ도 아닌 중간의 소리라고들 한다. 그런데 그 소리가 영어나 일본어에서도 쓰이는 보편적인(?) 소리인 건지도 모르겠다.
ㅐ/ㅔ뿐만 아니라 ㅒ와 ㅖ의 구분도 완전히 사라졌으며, ㅙ/ㅞ/ㅚ도 서로 변별력을 완전히 상실했다. 덕분에 재련과 제련, 결재와 결제, 제제와 제재 같은 한자어의 표기도 굉장히 헷갈리게 되었다.

한글이 처음 창제되던 당시에는 저 모음들이 어떻게 구분되어 발음되었는지.. 몇백 년 전 사람을 만나서 물어 보고 싶기라도 한 심정이다.

5. 사라져 가는 순우리말

까닭(이유), 달걀(계란), 뭍(육지, 땅) 같은 순우리말 단어는 한 1990년대 초반까지만 해도 방송과 도서에서 종종 접할 수 있는 단어였는데 갈수록 용례가 줄어들고 있는 게 보인다. 특히 '뭍'의 경우, 옛날에 전래동화 책에서 봤던 기억이 남아 있기도 하지만 이제는 사전에서나 찾을 수 있는 단어로 전락했다.

이런 식으로 '미덥다, 미쁘다, 미련하다' 같은 단어도 사라지는 것 같다. 어째 다 '미'짜로 시작한다는 공통점이 있다.
콩팥은 신장에 밀려서, 허파도 폐에 밀려서 앞날을 장담하기 어려워 보인다.
지금 세대는 알아듣기라도 하지만 다음 세대 애들한테는 완전히 듣보잡이 되겠지?

Posted by 사무엘

2019/04/03 08:35 2019/04/03 08:35
,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1604

본인은 박사 과정에 진학해서 수업을 다 듣고 종합 시험도 통과한 뒤, 지난 2년이 넘는 기간 동안 휴학을 했다. 그리고 그 기간 동안 날개셋 한글 입력기는 8.6에서 9.7까지 올라갔다.
입력기 연구의 연장선에다가 글꼴 연구도 새로 접목하는 것을 목표로 진학했었는데, 입력기 연구가 당초 계획보다 다소 오래 걸렸다.

이제 입력기는 파일 포맷과 엔진 구조를 다 뜯어고칠 정도로 너무 비현실적인 추상화(재설계나 리팩터링), 아니면 너무 이상적인 수준의 기능을 제외하고 어지간히 규칙 기반 한글 입력과 관련된 것들은 다 통달했으며 잘 실현됐다. 9.7도 특별히 심각한 문제 없이 아주 잘 만들어졌다.

다만, 날개셋은 TSF 기반의 한글 IME일 뿐만 아니라 반대로 타 IME들을 구동해 주는 텍스트 에디터이기도 하니.. 요즘은 편집기에서 타 IME를 구동하는 동작과 관련된 이슈들을 좀 살펴보고 있다.

1. 내 프로그램에서 9.7 이후로 개선된 사항

(1) 외부 모듈의 옛한글 조합을 여느 블록(selection)과 달리 취급

날개셋 편집기에서 입력 항목을 '빈 입력 스키마'로 고르면 앞서 언급한 바와 같이 자체 입력기가 아니라 다른 외부 IME들을 사용할 수 있다.
그런데 한글 IME로 현대 한글을 조합할 때는 깜빡이는 네모 cursor가 나타나는 반면, 옛한글을 조합할 때는 조합이 그냥 블록 형태로 잡힌다. 그래서 자체 입력기로 옛한글을 입력할 때와는 달리 이질적이고 아마추어스러운 느낌이 난다.

이건 일차적으로는 운영체제에서 옛한글처럼 내부적으로 2개 이상의 코드값으로 표시되는 한글에 대한 배려를 안 해서 그렇다. 조합 문자열이 한글로만 이뤄져 있을 때 응용 프로그램이 강제로 보정을 해서 깜빡이는 네모 cursor를 구현하라면 할 수도 있다.

내 프로그램에서는 그렇게까지는 안 하는 대신, 비록 블록처럼 보이더라도 진짜 블록이 잡힌 것처럼 복사/잘라내기 버튼이 켜지지도 않게 프로그램의 동작을 깨알같이 개선했다. 그건 블록이 아니라 조합을 표시하는 용도일 뿐이기 때문이다..

사용자 삽입 이미지

(2) 붙여넣기를 할 때 외부 모듈의 조합이 덧나지 않게

또한, 자체 입력기가 아닌 외부 IME로 한글을 조합하고 있던 중에 도구모음줄의 '붙여넣기' 버튼을 마우스로 누르면..
자체 입력기를 사용할 때와 마찬가지로 조합이 종료된 뒤에 클립보드 내용이 삽입되는게 정상이다.

하지만 지금까지는 그렇지 않았다. 조합이 중단되고 그 문자열이 사라지고서 클립보드 내용이 삽입되었다. 이 버그를 발견하여 고쳤다.

사용자 삽입 이미지

이 두 가지 사항은 언제쯤 다음 버전에 반영되어 나올지 모르겠다.

2. 내 프로그램과 무관한 운영체제의 버그

(1) IME 도구모음줄이 두 종류 모두 표시됨

Windows 10 1803 버전 기준으로..
IME의 구형 재래식 도구모음줄과 Windows 8 스타일의 간소화 도구모음줄이 다같이 동시에 뜨는 경우가 있다. 정확한 재연 조건은 잘 모르겠지만 컴퓨터를 절전 상태로 껐다가 다시 켰을 때 가끔, 그러나 확실하게 이런 현상이 발생한다.

사용자 삽입 이미지

"고급 키보드 설정"에서 "사용 가능한 경우 바탕 화면 입력 도구 모음 사용" 옵션을 건드려 주면 다시 둘 중 하나만 나타나게 개선되긴 한다. 하지만 이건 운영체제의 버그이니 나중에 업데이트를 통해 해결되어야 할 것이다. Windows 8은 물론이고 10도 초창기에는 이런 현상이 발생한 적이 없었다.

(2) 일본어 IME의 조합 관리 버그

날개셋 편집기 또는 IE/Edge 브라우저의 텍스트 입력 폼에서 Microsoft 일본어 IME를 구동하고, 히라가나 모드에서 일본어를 몇 자 입력한다.
space를 눌러서 그 일본어 문자를 변환은 하지 말고, 좌우 화살표 키를 눌러서 조합 영역을 빠져나간다. 그러면 조합을 나타내는 밑줄이 일시적으로 사라진다.

그 뒤에 caret이 기존 조합 영역으로 돌아오면 기존 조합이 다시 생겨야 되는데 그리 되지 않는다.
그 상태에서 다른 곳에서 Shift+화살표를 눌러서 블록을 만들어 보면 아까 조합하던 일본어 문자가 덧나서 잘못 삽입된다.

사용자 삽입 이미지

이 버그는 Windows 10의 16xx대 이전 버전에서는 발생하지 않다가 후대 버전에서 나타났다. 1803의 후대 버전에서는 어찌 되었나 모르겠다. 날개셋 편집기뿐만 아니라 MS에서 만든 TSF A급 웹브라우저에서 모두 동일하게 발생하니 내 프로그램만의 문제도 아니다.

단, 워드패드에서는 동일 운영체제와 동일 IME에서 저런 오동작이 발생하지 않는다. 서식을 지원하기도 하니 에디팅 엔진 차원에서 무슨 차이가 있어서 그런 것 같다.

(3) 옛한글 IME의 조합 영역 처리 버그

이건 Windows 8 이래로 계속 동일한 것 같은데..
마소에서 제공하는 옛한글 입력기는 초성이나 중성에 옛한글이 들어간 상태에서 종성의 첫 타를 입력하면.. caret 위치가 좀 이상하게 찍힌다. 내부적으로 표현되는 글자 수가 3자가 되었으니 0~3까지 모두 조합 영역으로 설정해야 하는데 종성이 입력되기 전처럼 0~2까지만 설정한다.
그래서 날개셋 편집기에서는 화면이 일시적으로 이렇게 표시된다. 종성 둘째 타 이후부터는 다시 괜찮아진다.

사용자 삽입 이미지

시각적으로 좀 이상한 것 말고 다른 오동작은 없다. 하지만 날개셋, 한컴 입력기 등 옛한글 입력을 지원하는 다른 모든  IME에는 이런 현상이 없고 MS IME만 저러니.. 이건 저 프로그램만이 단독으로 해결해야 할 문제로 보인다.

3. 단순 차이점 -- 옛한글 filler 글쇠

두벌식 옛한글 글자판에는 중성이 빠진 미완성 한글 내지 종성 단독 낱자를 입력하기 위해서 일명 filler라는 글쇠가 있다. 위치는 관례적으로 Shift+J로, 날개셋, 아래아한글, MS 옛한글 입력기가 모두 동일하다.

날개셋과 아래아한글에서는 이 filler라는 게 언제나 '중성 filler'를 의미한다. 이것만 있어도 초성이나 종성이 없는 글자는 입력 가능하기 때문이다.
하지만 MS의 경우, filler도 뭔가 두벌식스럽게 글자를 완전 처음 입력할 때는 빈 자리에다가 '초성 filler'를 흉내 내어 주는 것 같다. 굳이 그럴 필요가 없지만 말이다.

그래서 초기 상태에서 종성을 단독으로 입력하려면 filler를 한 번이 아닌 두 번 눌러야 한다. 본인은 처음엔 이런 차이를 몰라서 마소 옛한글 입력기로는 종성 단독 입력이 불가능한 줄 알았다.
초성 filler도 지원해 주는 게 사람에 따라서는 더 직관적으로 보일 수도 있다. 하지만 한글을 연속으로 입력하기 시작하면 filler는 어차피 사실상 중성으로만 동작해야 하기 때문에 굳이 저럴 예외를 둘 필요가 있나 싶다.

중요한 건 이런 동작조차도 표준으로 딱 정해진 게 없어서 프로그램마다 차이가 있을 수 있다는 점이다. 날개셋에서는 글쇠배열의 수식을 바꿔 주면 지금 동작(중성 고정)뿐만 아니라 MS IME의 동작도 물론 구현할 수 있다.

4. 원인을 알 수 없는 문제

다음은 본인의 개발 환경에서 아주 드물게 발생하는 것을 확인하긴 했지만 재연 조건을 전혀 몰라서 좀 난감한 지경에 있는 버그 아이템들이다. 이것들이 문제의 원인이 전적으로 내 프로그램의 귀책사유로 판명되어 해결된다면.. 위의 1번의 개선 사항까지 포함해서 다음 버전인 9.71이 지금이라도 당장 나오게 된다.

(1) 여전히 발생하는 랙

이번 9.7에서는 안 그래도 편집기의 에디팅 엔진과 관련된 몇몇 버그들이 잡히고 내부 동작 방식이 최적화 됐다. 그런데 편집기를 한번 띄워 놓고 며칠 이상 오래--특히 중간에 컴의 절전 모드와 복귀를 수차례 반복할 정도로-- 쓰다 보면, 어느 샌가 글자가 화면에 나타나는 속도가 내 타자 속도를 못 따라갈 정도로 랙이 걸리는 경우가 여전히 발생한다.

게다가 이게 참 악랄한 게.. 랙의 발생하던 당시에 발생 조건이 다음과 같이 가변적이었다는 것이다.

  • 오로지 날개셋 외부 모듈로 한글을 입력할 때만 느려짐 (MS IME, 자체 입력기 등등은 괜찮음)
  • 외부 모듈로 한글을 입력할 때만 느려짐 (날개셋, MS IME에서 랙. 자체 입력기는 괜찮음)
  • 아무 방식으로나 글자를 입력할 때 몽땅 느려짐

마지막으로 이 문제가 발생했을 때엔.. 처음엔 외부 모듈에서만 발생하는 것 같더니 이내 상황이 최악으로 바뀌었다.
특정 문서의 맨 마지막 줄에서 글자를 입력할 때만 극심한 랙이 걸리고, 그렇지 않을 때는 괜찮았다(타 문서 or 다른 줄). 심지어 그 문서에서 편집하고 있던 텍스트를 몽땅 지우고 새로 입력을 시작해도 랙이 사라지지 않았다.

이 랙이 발생하는 동안 내 프로그램의 내부에서는 무슨 일이 벌어지고 있고 도대체 어느 계층에서 뺑뺑이를 도는 건지 도무지 알 길이 없다. 그냥 평범하게 프로그램을 띄워서는 절대로 발생하지 않는다. 그나마 유력한 단서가 될 만한 현상은.. 이때 날개셋 편집기가 다음과 같이 memory leak이 발생해 있었다는 것이다.

(2) MS IME를 사용할 때 발생하는 괴이한 memory leak

날개셋 편집기와 작업 관리자를 같이 실행한다. 다음으로, 편집기에서 TSF 지원 옵션을 켠 상태에서 '빈 입력 스키마'를 고른다.
Microsoft 기본 한글 IME로, "세벌식 390/최종"(두벌식 말고)으로 "ㅇ.ㅇ.ㅇ.ㅇ." 처럼.. 한글 + 비한글 문자를 수십 회 쭈룩쭈룩 교대로 입력해 보라.

그러면 초기에 2~3MB대 안팎이던 프로세스 메모리 사용량이 계속해서 증가하는 게 관찰된다. 명백하게 memory leak이다.
COM 오브젝트 간의 reference count 같은 게 꼬인 것 같은데.. 이건 도대체 누구 잘못이라고 봐야 할까?

당연히, 디버그 빌드에서 단순 memory leak detector로는 문제가 전혀 감지되지 않는다. 내 프로그램은 10수 년에 달하는 짬밥을 자랑하며 얼마나 오랫동안 안정화가 돼 왔는데.. 소스 코드 상으로 무식한 결함이 있지는 않다.

더구나 날개셋, 한컴 입력기 등 "타 IME에서는 이런 현상이 없다." MS IME도 세벌식을 쓰고 있을 때만 저렇고 두벌식일 때는 문제 없다.
그리고 Windows Vista, 7, 10에서 이 현상을 확인했다. 구닥다리 XP에서는 MS IME+세벌식에서도 문제가 없다.

그럼 내 과실 0, 마소 과실 100을 입증하려면 날개셋 편집기 말고 다른 프로그램에서도 MS IME + 세벌식으로 저렇게 쳤을 때 동일한 memory leak이 발생한다는 걸 입증해야 하는데 그건 또 그렇지 않아 보인다~! 워드패드, MS Word, IE, Edge 등 TSF를 지원하는 프로그램들은 또 희한하게도 저런 현상이 발생하지 않는다.

다음으로 지푸라기를 잡는 심정으로 날개셋 구버전까지도 구해서 써 봤다.
날개셋 8.0까지는 이 문제가 없고, 8.4부터 leak이 발생하더라. 8.2는 불명.. 그러니 이 버그는 대략 2016년부터 있어 왔다는 것이다.
이때 도대체 어떤 변화가 있었는지.. 본인은 프로그램 소스를 자주 백업하는 편이지만, 컴퓨터가 바뀌는 과정에서 지금으로부터 3년이 넘게 너무 오래된 소스는 갖고 있지 않아서 이 방법으로도 문제의 원인을 파악할 수 없었다. ㅠㅠ

난 Windows용 IME라는 물건을 개발하느라 지난 10여 년 동안 온갖 희한한 버그 신고들을 받고 기상천외한 지저분한 환경에서 디버깅을 했다. 그러면서 마치 교통사고 과실 비율 따지는 것 같은 현상들을 많이 경험했었다.
둘 다 스펙대로 100% 무결하게 구현된 건 아니었고(혹은 스펙 자체가 모호해서..) 둘 다 조금만 조심하면 됐는데 둘 다 무데뽀로 동작해서 운 나쁘게 문제가 발생하는 것.. 말이다. Windows용 IME라는 바닥은 무척 "구리다."

아무튼 현재로서는 저 memory leak의 원인과 해결 방법이 오리무중이다. 해결만 된다면 9.7 다음으로 9.71이 당장 나와야 할 것이다.

(3) 제어판 닫았을 때 프로그램 뻗나?

정말 민망하고 황당한 버그인데.. 올해 들어 두세 번인가 겪었다.
날개셋 편집기에서 제어판을 꺼내서 설정을 바꾼 뒤, 확인을 눌러서 닫았더니 편집기 프로그램이 그냥 대짜로 뻗어 버렸다.
한 번 발생했을 때는 그냥 재수없는 우연인가 싶었는데, 몇 주 전에 마지막으로 동일 문제를 겪었을 때는 '미저장 확인'을 누른 것만으로도 뻗었다.

물론 그 뒤로는 편집기에서 날개셋 외부 모듈을 또 얹고, 제어판에서 온갖 설정을 바꾸고 빠른설정을 띄우면서 지지고 볶아 봐도.. 동일 문제가 다시는 재발하지 않고 있다. 위의 랙도 몹시 드물게 발생하지만 이 crash는 그것보다 더 드물게 발생했다. 그래서 난감하다.

Posted by 사무엘

2019/03/31 08:30 2019/03/31 08:30
, , ,
Response
No Trackback , 6 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1603

우리나라에는 자동차 기반의 장거리 대중 교통수단으로 고속버스와 시외버스라는 이원화된 시스템이 존재한다.
사실, 법적으로는 이들은 일반형 시외버스, 직행형 시외버스, 고속형 시외버스라는 세 부류로 나뉘는데, 고속형 시외버스가 고속버스라고 여러 모로 특별 취급을 받는 구도이다. 그리고 나머지 시외버스들도 시대의 흐름에 따라 갈수록 직행화하고 고속버스와 형태가 비슷해지고 있다.

본인은 옛날 대학 시절에 경주에서 울진으로 가는 시외버스를 이용한 적이 있었다. 고속도로가 없이 국도만 타느라 울진까지 가는 데 4시간이 넘게 걸렸던 걸로 본인은 기억한다. 더구나 이런 지방 왕래 수요가 많을 리가 없으니, 버스도 무슨 완행 열차가 정차하듯이 온갖 시골 정류장들을 들쑤시고 다녔다. 그래야 수지가 맞을 것이다.

지방에서 시외버스는 원래 이런 식으로 운행되고 있다. 울진 같은 경북의 오지뿐만 아니라 당장 강원도의 전방에서 차가 없는 사람들의 이동을 책임지는 것도 시외버스가 유일하다. 화천· 양구 같은 곳에 철도가 있나, 공항이 있나? 동서울 터미널에 괜히 군인들이 우글거리는 게 아니다.

다만, 요즘은 집집마다 승용차를 굴리는 세상이며, 중소 규모 이상의 도시에는 철도 같은 대체제도 있다. 전국에 고속도로도 워낙 촘촘하게 많이 건설되고 있지 않은가?
그러니 버스도 경쟁력을 갖추기 위해서는 촘촘한 단거리 이동보다는 장거리를 어설픈 중간 경유 없이 승용차보다 더 빠르고 편하게 가는 것에 집중하게 되었다. 일반형 완행 시외버스는 저런 시골 지방 말고는 없어지는 추세이다. 철도로 치면 간이역들이 갈수록 없어지는 것과 비슷한 이치이다.

고속버스는 시외버스의 고급 특화 케이스이기 때문에 시외버스보다 노선이 훨씬 적다. 그 덕분인지 승차권 발매· 예매를 위한 단일 통합 전산망도 2000년대 초부터 시외버스보다 훨씬 더 잘 갖춰져 있었다. 과거 1980년대에 철도 승차권 전산 발매도 새마을호에 제일 먼저 적용되었던 것처럼 말이다.

현행법에 따르면 길이가 100km 이상이고, 전체 구간의 60% 이상을 고속도로로 달리는 노선에만 고속버스가 투입될 수 있다. 고속도로는 그 정의상 최대 속도가 100km/h 이상으로 정해진 곳이니 고속버스의 정의에는 속도와 거리에 모두 100이라는 숫자가 존재하는 셈이다.

경주-대구 사이에는 고속버스와 시외버스 노선이 모두 존재해서 서로 경쟁 중이다. 저기는 80km가 안 되는 짧은 거리이지만, 100km 이상 규정이 생기기 전부터 있었기 때문에 고속버스 노선이 여전히 존재하는 중이다. 마치 이화여대 초등교육과가 전국에서 유일하게 초등학교 교사를 양성하는 사립 기관이듯이 말이다. (국립 교육대들보다 먼저 존재했음)
뭐, 신경주 역에서 KTX를 타면 동대구 역까지 20분이 채 걸리지 않지만.. 운임과 역 접근성 때문에 버스도 여전히 경쟁력이 있다. (역이 시내에서 너무 멀므로..)

그리고 고속버스는 태생적으로 중간 정차가 거의 허용되지 않는다. 시점과 종점만 있는 시외버스라는 게 원래는 고속버스의 전유물이었다. 고속버스는 고속도로 휴게소에 두세 시간 간격으로 정차하고, 아니면 시점 또는 종점과 동일한 지역에 소재한 정류장에 딱 한 번만 추가 정차할 수 있다.

대표적인 예는 대구-서울 고속버스이다. 상행은 동대구 역 근처에 있는 터미널을 출발한 뒤에 서대구 정류장을 들렀다가 서울로 가며, 하행은 반대로 서대구 정류장을 들렀다가 터미널로 간다. 대구 시내 안에서 터미널과 정류장 사이만 왕복하는 용도로는 고속버스를 이용할 수 없다.

즉, 쟤들은 터미널을 출발하자마자 곧장 고속도로로 진입하지 않는다. 굳이 대구 시내를 횡단해서 서대구 정류장을 경유하느라 고속버스의 표정속도가 하락하는 건 개인적으로 아쉽게 느끼는 점이다.
이 정도만이 고속버스에게 허용된다. 이런 고속버스와는 달리, 동서울을 출발한 직행 시외버스는 경주에서 승객을 하차시킨 뒤 포항까지도 간다.

다음으로 운임의 측면에서 살펴보면, 고속버스는 시외버스보다 고급스럽고 사치스러운(?) 교통수단으로 간주되어 운임에다 부가가치세가 붙는다. 즉, 원가 대비 차삯이 더 비싸다. 하지만 겨우 고속버스가 사치품인 것은 무슨 1970년대에 경부 고속도로라는 게 처음 생겼고 버스 안에 안내양까지 탑승하던 시절의 사고방식에 지나지 않는다. 법리적으로 볼 때 부가세를 폐지하는 것이 옳다는 지적이 있어 왔다.

고속버스에는 1991년인가 92년부터 우등이라는 등급이 생겨서 좌석 수가 더 적고 넓고 큼직한 대신, 더 비싼 버스가 등장했다. 열차는 상위 등급이 정차역 수가 적고 더 빨리 가는 반면, 고속버스는 처음부터 중간 무정차를 표방했기 때문에 우등이라고 해서 더 빠른 건 아니다. 그 대신 버스는 그 구조상 한 차량 안에서 특실/일등석(!) 같은 구분은 없다.

새마을호에 종아리 받침대가 달린 한국 철도 역사상 최고급 좌석이 등장한 것도 비슷하게 1990년대 초인 것으로 본인은 기억하는데.. 우등 고속을 의식한 것인지, 아니면 반대로 우등 고속이 더 나중인지는 정확한 시기와 역학관계를 잘 모르겠다. 승차감과 좌석 앞뒤 간격은 철도인 새마을호가 더 낫고(특히 특실은!), 좌석과 팔걸이의 폭은 아예 2-1 배열인 우등 고속이 더 컸던 것으로 본인은 기억한다.

직행형 시외버스에도 우등형 좌석 차량이 있다. 단, 얘들은 앞뒤 간격이 우등 고속보다 약간 더 좁아서 28인승이 아닌 33인승이다.

고속버스 업계에서는 우등 고속이 생긴 지 25년 가까이 지난 2017년부터 우등보다도 더 고급스러운 21인승 프리미엄 우등이라는 것도 등장했다. 하지만 이건 서울-부산 같은 소수 장거리 노선 말고는 막 보급되기 어려울 듯하다. 우리나라가 무슨 1000km짜리 노선이 있는 것도 아닌데.. 속도의 향상 없이 내장재만 잔뜩 고급화해서 비싼 운임을 받는 건 타 교통수단 대비 경쟁력을 확보하기 곤란하다. 다만, 좌석에 콘센트나 폰 충전 단자가 있는 버스라면 개인적으로 귀가 약간 솔깃해지긴 한다!

지금이야 시대가 어느 시대인데 시외버스도 고속버스 못지않은 승차권 전산 발매 인프라를 갖췄고, 심지어 시내/광역버스처럼 교통카드 결제가 가능해지기도 했다. 줄 서서 창구에서 표 사는 번거로움을 덜기 위해 맨 처음에는 무인 예매 발권기라는 게 생겼고 2000년대 초반에는 홈티켓이 유행했는데, 이제는 그냥 모바일 승차권을 써도 된다.
그리고 고속버스도 무조건 한 차량으로 시점-종점만 고집하는 게 아니라, 휴게소 환승이라는 것도 이미 10여 년 전에 생겼다.

그 전에 옛날에는 고속버스의 승차권 전산 발매 시스템이 통합돼 있지 않아서 일부 지역은 kobus, 일부 지역은 이지티켓(easyticket)으로 별도의 사이트를 이용해야 했다. 그 시절에 동영상 코덱들이 난립하고 휴대전화 충전 단자들이 통일돼 있지 않았던 것처럼 말이다.

또한 대구는 대도시에 걸맞지 않게 고속버스 터미널이 회사별로 찢어져 있던 것으로 악명 높았다. 동부/서부 이렇게 정말 지리적으로 서로 멀리 떨어진 게 아니라, 똑같이 동대구이고 그냥 한 블록 간격인데 회사가 관할하는 행선지별로 터미널이 찢어진 것이다. 더구나 이게 전산 시스템에도 반영돼서 '대구 한진', '대구 동양' 같은 식으로 찢어졌으니 병크가 따로 없었다.

그러다가 대구에 드디어 동대구 역과 연계되고 기존 동부 시외버스 정류장과 백화점까지 통합한 동대구 통합 고속버스 터미널이 완공됐으니.. 참 오래 살고 볼 일이다. 진작에 그렇게 됐어야 했다.
이 추세라면 시외버스와 고속버스라는 두 체계는 궁극적으로 하나로 통합해도 되지 않나 싶다. 육군이 서부와 동부 전선 야전군(제1, 제3)을 통합해서 그냥 전방 담당 사령부를 만든 것처럼 말이다.

이제는 고속도로를 달리는 게 별다른 특권이 아니며 직행 시외버스와 고속형 시외버스의 경계가 굉장히 모호해졌기 때문이다. 고속버스만 타 시외버스와 다르게 취급할 이유가 별로 없다. 단일 시외버스 체계에서 시골 지방을 위한 완행 아니면 직행 구분만 하면 될 것 같다. 차량과 전산망 말고 터미널 건물은 요즘 모든 지역들이 고속과 시외 구분 없이 통합해서 만드는 게 대세가 된 지 오래다.

한반도에 철도와 시내버스까지는 일제 시대에도 있었던 교통수단이다. 그러나 고속철은 말할 것도 없고 그 전에 고속도로와 고속버스라는 것은 그 시절을 넘어 할배 슬하의 1공화국 시절에도 없었다. 1970년대 박통 때에 와서야 등장했다.
사실, 일제 시대 경성 시내 사진을 봐도 길거리에 자동차와 노면전차까지는 다니지만, 길에 차선이 그어지고 신호등이 설치된 걸 본 적은 없을 것이다. 미국 LA는 1940년대 모습이 이미 우리나라의 1970년대 이상 같고 자동차의 모양만이 옛날 디자인 같은데.. 참 대조적이다.

그렇게 길거리에 교통 시설이 아무것도 없다시피했기 때문에 미군정은 1946년에 자기 재량으로 한반도에서 자동차의 통행 방향을 좌측에서 우측으로 곧장 변경할 수 있었다. 자동차가 극히 드물던 시절, 고속버스를 타는 것만으로도 지금으로 치면 비행기를 타는 것 같은 희소한 경험이던 시절에 사람들의 삶이 어떠했을지가 궁금하다.

또한 저것보다는 비교적 가까운 과거에 운전석 옆에 안내양이 앉는 작은 의자가 있던 시절, 그리고 우등 고속의 오른쪽 맨 앞자리(3번)에 냉장고와 이동식 공중전화가 비치되어 있던 시절, 현대도 대우도 아닌 아시아 자동차 버스가 있던 시절도 개인적으로 문득 그리워진다.

Posted by 사무엘

2019/03/28 19:33 2019/03/28 19:33
, , , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1602

스텐실 폰트

* 굉장히 오랜만에 폰트 분야에 글을 하나 추가하게 됐다.

지금은 실생활에서 좀 보기가 힘들다만.. 30대 이상의 나이가 좀 있으신 분들 중에는 요런 투박한 모양의 글자 내지 숫자를 각종 표지판이나 벽면, 차량의 외부에서 본 적이 있을 것이다. 양각· 음각 형태로 새겨지거나 오려 붙여진 게 아니라, 물감· 페인트로 칠해진 형태로 말이다.

사용자 삽입 이미지

이 글꼴의 컨셉은 글자가 가독성을 해치지 않는 한도 내에서 획의 일부가 규칙적으로 끊어지고 단절돼 있다는 것이다. 특히 O 대신 ()이고, ㅁ 대신 [] 모양이다.

미술· 디자인 쪽으로 조예가 있는 분이라면 이미 다 눈치 채고 아시겠지만, 이건 '스텐실'이라고 불리는 인쇄 내지 칠하기 기법에 맞춰진 글꼴이다.
투명한 필름지 같은 것에다가 도안을 그려서 선이나 면을 잘라내고, 그걸 종이 위에다 올린다. 그 뒤 필름에다가 칠을 하면 필름이 없어서 종이가 노출된 영역에만 색이 칠해진다. 이 필름지를 이용하면 동일한 그림도 여러 번 쉽게 찍어낼 수 있다.

판화와는 접근 방식이 다르다. 판화는 개념적으로 도장과 비슷하며, 찍는 과정에서 좌우가 바뀐다. 하지만 스텐실은 칠하는 방식이 다르니 그런 mirroring이 발생하지 않는다. 판화와 달리 칠을 더 다채롭고 다이나믹하게 할 수 있다.
그러나 스텐실은 판화에는 해당사항이 없는 한계도 존재하는데, 내부의 구멍을 구조적으로 표현할 수 없다. 그렇기 때문에 내부의 구멍이 존재하는 글자는 부득이하게 획의 일부를 끊어서 구멍 내부도 외부와 연결되게 해야 한다.

기왕 획의 일부를 끊었으니 이걸 일관된 개성과 컨셉으로 삼아서 스텐실용 글꼴을 만들어 보자는 발상은 서체 디자이너들이 누구나 할 수 있었던 생각이다.
저 영문 Stencil 글꼴에서 보듯, 획 굵기의 기복이 있는 세리프 계열 글꼴이 단절감이 덜하고 좀 더 어울린다. 어차피 획이 최대한 가늘어지는 곳에다가 단절을 시키면 되기 때문이다.

하지만 산세리프 계열에도 스텐실 글꼴이 얼마든지 존재한다.

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

디지털 글꼴이 없던 옛날에 이런 글자들은 스텐실 기법으로 만들어지고 복제된 도안들이라고 생각하면 된다.

사용자 삽입 이미지

버스의 경우, 동일한 노선을 달리는 버스가 수십 대 이상 있었을 테니 이렇게 행선지를 인쇄하는 게 합리적이었을 것이다.

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

옛날에 군부대 근처에 있던 "접근금지", "위험" 표지판도 다 이런 식이었던 것 같다.
지금이야 옛날 같은 투박함과 엉성함은 사라지고 스텐실 컨셉을 일부러 흉내 낸 깔끔한 글꼴을 골라 쓰는 시대가 됐지만 말이다.

일반적인 책과 문서에서는 Times 같은 평범한 본문용 서체를 쓰는 반면,

  • 타자기나 코딩용으로는 딱딱한 불변폭 서체를 쓰고,
  • 기계가 인식하기 편하라고 타자기와 비슷하면서 획을 간소화시킨 그 특유의 OCR용 서체도 만들고,
  • 멋을 내기 위해서는 기울이고 날리고 최대한 한붓그리기를 추구한 필기용 서체를 쓰고,
  • 열악한 디지털 기계에서는 8픽셀도 채 안 되는 높이 내지 7-segment 같은 극도로 단순한 형태로 문자 외형을 간소화도 하고,
  • 스텐실용으로는 이렇게 구멍이 없는 서체를 쓰는 등..
문자를 용도에 따라 다채롭게 활용 가능하게 하는 것이 타이포그래피의 묘미임이 틀림없다.

그나저나 오늘날 같은 디지털 인쇄와 복사 기술이 발달하기 전에 등사처럼 아날로그 판화· 인쇄술이 어떠했는지에 대한 의문과 관심이 문득 생긴다.

Posted by 사무엘

2019/03/26 08:36 2019/03/26 08:36
, , , , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1601

1. 32비트 컴파일러: 16비트 메모리 접근의 한계를 극복하기

예전에도 언급한 적이 있지만.. 1993년 말에 발매되었던 Doom 게임은 그야말로 충격적인 3차원 그래픽 덕분에 게임 업계에 큰 충격을 선사했다. 업계 종사자들은 기술 수준 자체뿐만 아니라 "얘는 어셈블리어를 거의 사용하지 않고 순수 C만으로 개발되었습니다"라는 존 카맥의 말에 더 큰 충격을 받게 됐다.

훗날(1997년) Doom의 소스 코드가 공개되면서 이 말은 사실임이 밝혀졌다.
Doom은 무슨 16비트 Windows 같은 쑤제 어셈블리어 튜닝 위주로 개발된 게 아니라, Windows NT처럼 굉장히 이식성 있게 개발되었다. 그러니 Doom 엔진 기반의 수많은 게임과 mod들이 온갖 플랫폼으로 이식되어 만들어질 수 있었다.

단지, 오리지널 도스용의 경우, 컴파일러를 그 당시에 흔하던 볼랜드나 MS 같은 16비트용을 쓴 게 아니라 Watcom이라는 다소 생소한 32비트 고성능 제품을 썼을 뿐이다.
그리고 어셈블리어를 안 쓰더라도 고정소수점이라든가, IEEE754의 특성을 이용해서 3차원 그래픽용 실수 연산(삼각함수, 제곱근, 벡터 정규화...)을 왕창 빠르게 수행하는 각종 tweak들은 응당 최대한 구사해서 성능을 끌어올렸다.

그러니 Doom은 아직 상대적으로 생소하던 32비트 컴파일러라든가 DOS/4G 도스 익스텐더 같은 물건의 인지도를 끌어올려 줬다. 이렇게 Doom을 통해 Watcom 컴파일러까지 알렸던 id 소프트웨어에서는 훗날 퀘이크를 만들어서 이번에는 오픈소스 진영의 걸출한 도스용 32비트 컴파일러이던 djgpp를 알리게 되었다.

운영체제 자체를 OS/2나 Windows NT처럼 통째로 32비트로 쓰기에는 아직 기계값이 너무 비싸고 특히 메모리가 부족했다. 그러니 도스에서 돌아가는 일부 대형/고사양 프로그램이 자체적으로 도스의 한계를 극복하고 보호 모드로 진입하는 솔루션을 내장했던 것이다.

생각해 보니 국내에서도 아래아한글 2.1이 전문용은 Watcom C/C++을 이용한 32비트 전용으로 만들어졌다. 얘는 발매 시기가 심지어 Doom보다도 3개월 남짓 더 앞섰다(1993년 9월 vs 12월). 그러니, 터보 C, 볼랜드 C++ 하던 그 시절에도 32비트 컴파일러에 대해서 알 사람은 이미 다 알기는 했던 모양이다.

다만, 아직도 286 똥컴이 많이 굴러다니고 서민용 운영체제들은 아직도 16비트 도스와 Windows가 주류인데, 내 프로그램을 386 전용으로 개발하는 것에 대한 득과 실을 신중하게 따져야 했다. 오죽했으면 아래아한글도 후속 버전인 2.5와 3.0에서는 일반용/전문용 구분이 없어지고 그냥 hwp86.exe와 hwp386.exe 두 에디션을 모두 내장하는 것으로 형태가 바뀌었다. 추가 글꼴과 사전 컨텐츠는 '확장팩'으로 분리되고 말이다.

아래아한글은 Phar Lap 도스 익스텐더를 사용했다. 아래아한글이 그 시절의 도스용 게임처럼 DOS/4G(W) 로고를 띄우면서 실행되었다면 무척 볼 만했을 것이다.
86과 386 에디션은 성능 말고는 덧실행 프로그램이 지원되는지의 여부가 가장 큰 차이점이었다. 덧실행은 16/32비트용이 따로 나오지 않고 32비트 전용이었기 때문이다.

화면 보호기들, 그리고 확장팩에서 제공되었던 프라임 영한사전도 다 덧실행 프로그램이었다.
먼 옛날 1.2 시절에는 별도의 액세서리로 테트리스 게임이 있었는데 나중에 그게 덧실행으로 컴백한 걸 보니 개인적으로 감회가 새로웠었다.

이렇게 1990년 중반에 도스용 프로그램들의 32비트화 추세와 달리, 마소는 진작부터 PC에서 도스를 Windows로 대체하려는 큰 그림을 갖고 있어서 그런지.. 도스용으로 32비트 컴파일러를 결코 내놓지 않았다. 정작 자기들은 그 기술을 내부적으로 보유하고 사용했으면서 말이다.
Visual C++ 1.5x는 16비트 도스/Windows 바이너리들을 빌드할 수 있었는데, 명령 프롬프트에서 돌아가는 컴파일러와 링커 같은 툴들은 그냥 32비트 프로그램이 아니라 32비트 PE 기반의 콘솔 프로그램이었다.

Windows NT 같은 데서는 직통으로 실행 가능하고, 도스에서 실행되면 stub으로 embed된 도스 익스텐더가 컴을 보호 모드로 진입시키고 CreateFile/GlobalAlloc 같은 Win32 API를 제공해서 프로그램을 실행했다.
스레드를 만들지는 못했겠지만 컴파일러· 링커가 사용하는 Win32 API야 뭐 파일이나 메모리 I/O 정도밖에 없었을 것이고, 이건 도스 익스텐더가 감당 가능했다. 결국 한 바이너리만으로 도스와 Windows에서 모두 사용 가능.

이건 뭐 콘솔 프로그램계의 Win32s나 마찬가지인 엄청난 기술인데.. 마소의 Visual C++에서 이런 이중 바이너리를 만드는 걸 end-user에게 지원한 적은 내가 알기로 없다.
마치 C# 네이티브 코드 컴파일러만큼이나 대외적으로 공개되지 않고 마소 내부에 봉인된 기술인 것 같다.

2. 슈퍼 VGA 라이브러리: 표준 VGA의 한계를 극복하기

IBM 호환 PC라고 불리는 물건에서 IBM이 주도하는 PC의 단일/표준 규격이라는 건 286 AT 이후로 없어졌다. 그러니 286 이후로 최초의 386 PC는 IBM이 아닌 컴팩에서 출시되기까지 했다.
그리고 그래픽 카드도 절대불변 단일 표준은 1987년의 구닥다리 VGA가 마지막이다. 표준 VGA는 800*600 해상도조차 지원하지 않았으며, 그나마 색깔이 아쉬운 대로 다양해진 256색은 겨우 320*200에서밖에 지원되지 않아서 업무라기보다는 그냥 게임 전용 모드로만 쓰였다.

그 뒤로 VGA보다 더 높은 해상도와 더 많은 색상을 지원하는 규격은 그야말로 온갖 싸제 SVGA 제조사들이 난립하면서 파편화 천국이 됐다. VESA 같은 규격이 괜히 필요해진 게 아니다.

이게 불과 1990년대 초반의 일이니, 앞에서 언급한 보호 모드가 어떻고 DPMI가 제정되던 때와 시기적으로 비슷하다. 하긴, 1990년에 나온 그 옛날 프로그램인 Deluxe Paint조차도 처음 실행될 때 맨 아래에 1024*768 256색 SVGA 모드가 있긴 했다. 물론 당대에 그걸 선뜻 고를 수 있을 정도의 금수저 컴퓨터를 소유한 사용자는 매우 소수였을 것이다.

마소의 베이직 컴파일러야 SCREEN 명령으로 SVGA 지원은 전무했다. API 구조가 완전히 다른 3rd-party 라이브러리를 구해서 써야 했다.
볼랜드의 경우는 상황이 약간 낫다. 비록 자체적으로는 VGA까지밖에 지원하지 않았지만, 일종의 그래픽 드라이버인 bgi 파일이 내부 스펙이 공개돼 있고 확장 가능했기 때문에 이걸 기반으로 SVGA 라이브러리를 만든 곳이 있긴 했다.

검색을 해 보니 Jordan Hargraphix 소프트웨어가 이 업계의 독자적인 큰손이었던 모양이다. 이미 1991년 무렵부터 유명했다.
바이오스를 거치지 않고 일명 VGA mode X라고 불리는 320*240, 400*300 같은 변형 모드까지 다 지원했다.
그때는 소프트웨어가 잘못된 명령을 내려서 컴퓨터만 뻗게 하는 게 아니라 모니터를 손상시키는 것도 가능했던 시절이다. (주사율 변조..) 옛날에 CGA도 160*100 같은 tweak mode가 있었다고 하는데 그것만큼이나 신기한 일이 아닐 수 없다.

다만, BGI라는 그래픽 API는 무려 1980년대 후반에 개발된 것이며, 아무리 bgi 드라이버를 새 하드웨어에 맞게 확장한다 해도 256색 이상의 색을 지원하는 것은 구조적으로 불가능했다고 한다. 트루컬러 SVGA를 지원하려면 완전히 새로운 독자 라이브러리를 써야 했다.
BGI는 색상을 관리하는 게 RGB값 기반이 아니라 팔레트 인덱스 기반으로 고정돼 있었던 모양인데, 16비트 시절에 이는 충분히 수긍이 간다. 쟤가 무슨 Windows GDI 급으로 하드웨어 통합과 추상화를 표방한 물건은 아니었으니 말이다.

도스용 아래아한글은 16비트 바이너리의 경우 Turbo/Borland 컴파일러로 개발되었다. 하지만 아주 초창기인 1.x 시절부터 그래픽 라이브러리를 독자 구현했는지, 볼랜드의 보급 BGI 라이브러리를 사용한 흔적이 전혀 없는 것이 매우 흥미롭다.
이건 비슷한 시기에 도스용 한메 타자 교사도 마찬가지다. 얘도 MS C로 개발되었지, 의외로 볼랜드 출신이 아니다.

Posted by 사무엘

2019/03/23 08:31 2019/03/23 08:31
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1600

제2중부 고속도로(37)는 중부 고속도로(35)의 서울-수도권 구간을 확장하는 과정에서 추가로 건설된 고속도로이다.
도로를 확장해야겠는데 기존 도로는 다들 평지가 아닌 고가 교량 형태여서 그걸 건드릴 수는 없다. 그래서 옆에 도로를 더 만들게 됐다.

그럼 보통은 기존 도로는 전부 하행, 새 도로는 전부 상행.. 이런 식으로 개편하는 게 자연스러울 것이나, 중앙분리대를 제거하고 각종 도로 표지판들을 변경하고 진출입로를 이설하는 작업마저도 여의찮았는지 결국 기존 도로는 하나도 안 건드리고 그대로 놔 두게 됐다. "새 도로 추가.. 그 대신 새 도로는 중간에 진출입로가 없는 직행" 컨셉으로 제2중부 고속도로가 만들어졌다. 그리고 이 결과는 운전자들에게 "중부냐, 제2중부냐" 하는 눈치 게임으로 전가됐다.

중부와 제2중부가 병행하는 구간, 그리고 그 이북으로는 휴게소가 다음과 같이 3개가 존재한다.

(1) 마장 프리미엄 휴게소

중부와 제2중부 고속도로 사이의 섬이라는 꽤 적절한 위치에 굉장히 거대한 규모로.. 거의 복합 쇼핑센터 컨셉으로 비교적 최근에 만들어졌다(2013). 도로들보다 나중에 생겼다는 점으로 인해 진출입로에 입체 교차로 같은 건 없다. 그래서 중부에서는 상행만(하남 방면), 제2중부에서는 하행(청주 방면)만 이 휴게소에 접근 가능하다.

즉, 두 고속도로에서 한 휴게소를 공유하지만 방향은 제각기 반쪽짜리이다. 그리고 방향별로 주차장이 서로 분리돼 있기 때문에 들어왔던 차량이 방향을 바꿔서 나갈 수는 없다.

(2) 이천 휴게소

상행과 하행 휴게소가 제각기 3km 가까이 떨어져 있다. 상행은 두 고속도로가 공유하며, 나갈 때 중부와 제2중부 정도야 도로를 바꿔치기 할 수도 있다.
하행은 마장 프리미엄 휴게소와 아주 가까이 있는데, 얘는 오로지 중부 고속도로의 하행만 접근 가능하고 제2중부는 해당사항 없다. 그쪽은 어차피 마장 휴게소로 가면 되기 때문이다.

정리하자면, 이 구간에서 중부+상행이라면 마장과 이천 휴게소를 모두 이용할 수 있다. 그러나 제2중부+상행과 중부+하행은 이천 휴게소만 이용 가능하며, 제2중부+하행은 마장 휴게소만 이용 가능하다.

(3) 하남드림(구 만남의 광장) 휴게소

경부 고속도로에 있는 '만남의 광장 휴게소'의 중부 고속도로 버전이다. 과거에는 실제로 이름도 동일하게 '만남의 광장'이었다고 한다.
다만, 얘가 경부 고속도로 만남의 광장과 다른 점은 다음과 같다.

  • 경부 만남의 광장은 그래도 과거에 톨게이트가 있었고 현재도 시내 도로와 고속도로의 경계인 지점에 있는 반면, 하남드림은 앞뒤로 여전히 차들이 쌩쌩 달리는 고속도로 상에 있다.
  • 경부 만남의 광장은 상행 방면에서는 진입할 수 없는 반면, 하남드림은 상행 방면에서도 지하도를 거쳐서 진입 가능하다.

그렇기 때문에 경부의 상행에서는 죽전 휴게소가 마지막 휴게소이지만 중부의 상행은 하남드림이 마지막 휴게소이다.
그리고 이 두 만남의 광장은 모두 서울/동서울 톨게이트를 지나고 요금제가 개방식으로 바뀐 구간에 존재한다. 그렇기 때문에 차들을 진행 방향별로 분리하지 않으며, 나갈 때는 상행이나 하행 아무데나 자유롭게 나가면 된다. 단지, 경부 만남의 광장은 들어오는 게 하행에서만 가능하다는 차이가 있을 뿐이다.

Posted by 사무엘

2019/03/21 08:31 2019/03/21 08:31
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1599

영화들 중에는 주인공이 극단적인 사고 또는 범죄를 당해서 특이한 위험한 장소에 갇히고 거기서만 이야기가 진행되는 형태인 것이 몇 가지 있다.
이런 장르는 촬영 영역이 아주 좁고 등장 인물도 적은 특성상, 대작을 만들기는 어렵다. 하지만 굉장한 저예산으로도 작품을 너끈히 만들 수 있으며, 잘 만들면 스케일 대비 소재와 설정이 참신하다고, 작품성이 훌륭하다는 칭찬도 들을 수 있다.

가장 먼저 떠오르는 예는 (1) <베리드(Buried)>(2010)이다. 주인공은 생매장-_-을 당해서 지하의 관짝 안에 있으며, 영화는 온종일 이 좁은 관 안에서만 진행되니 촬영 하나는 기가 막히게 단순하고 쉬웠을 것 같다. 관을 구성하는 직육면체 옆면 네 개 중에서 하나는 촬영을 위해서 뜯어냈을 것이고..

주인공은 유일한 희망인 휴대전화로 전화 통화를 하면서 외부 사람에게 자기 위치를 알려주고 구조 받으려 애쓰지만.. 거기 지역이 지역인지라 일이 영 쉽지 않다. 영화 자체는 공식적으로 열린 결말로 끝나지만, 주인공은 사실상 죽는 것이나 마찬가지이다.

주인공은 명을 단축하는 그 어떤 치명상도 입은 게 없다. 하지만 저렇게 좁은 관 안에서 누운 채 꼼짝달싹 못 하는 채로 목마르고 굶주리며 아주 서서히 죽는 건 단칼에 푹찍악 해서 죽는 것 만만찮은 비참한 죽음인 게 틀림없다. 당장 화장실도 못 가고 변을 그 자리에서 배출해야 한다는 걸 생각해 보자..;;

사람은 가만히만 있어도 언젠가는 죽는다. 허나, 아무리 사람이 물리적으로 연약하다 해도 그 명줄이란 게 호락호락 쉽게 금방 끊어지지는 않는다. 좀 민망한 얘기이다만, 자살하려는 사람들이 더 빨리 죽으려고 굳이 목을 매달거나 옥상에서 뛰어내리거나 번개탄을 피우는 등의 수고를 괜히 하는 게 아니다.

그러고 보니 옛날에 조선에서는 사도세자가 관은 아니고 뒤주에 갇혀서 저렇게 죽었다.
<킬 빌 2>(2004)에서는 잘 알다시피 버드가 주인공 키도를 제일 천천히 고통스럽게 죽여 주겠다면서 생매장을 해 버리는데, 이건 나름 머리를 쓴 조치였다. 물론 이 영화에서는 생매장 씬이 10여 개에 달하는 전체 스토리 중 극히 일부 에피소드만을 구성할 뿐이며, 결정적으로 주인공이 비현실적인 인간 흉기인 관계로... 정권으로 관을 때려부수고 무덤을 탈출한다는 차이가 있다.

<베리드> 얘기가 좀 길어졌는데, 이것 말고 (2) <화씨 247도>(2011)는 주인공 남녀 일행이 뜨거운 사우나 안에 갇혀 버리는 내용이다. 문의 자그마한 유리창을 주먹으로 쳐서 깬 덕분에 최소한의 환기와 냉각은 가능해졌지만, 사우나는 어차피 온도에 따라 화력이 자동으로 조절되고 있으며 세 명이나 되는 사람이 얼굴을 거기로 들이민 채로 잠을 잔다거나 할 수는 없다. 나름 실화를 바탕으로 만들어졌다는데, 결말에서는 남자 주인공이 결국 죽는다..;;

(3) <12피트>(2017)는 자매지간인 아가씨 두 명이 커다란 수영장 내부에 갇히는 내용이다. 수영장의 수면 위로 덮개가 쳐지는 바람에 물 밖으로 머리를 내밀고 있기가 극도로 어려워졌다. 이 상태로 수영장 관리자는 퇴근을 해 버리고, 그대로 불금 주말이 시작된다..;; 주인공들은 점점 지쳐 가고 체온이 떨어지는데..
다행히 수영장에 다른 사람이 들어오긴 하지만, 관객들 열불나게 하는 짓을 벌이면서 주인공들을 호락호락 구해 주지 않는다.

<화씨 247>은 짐작하다시피 사우나의 내부 온도를 나타내며(섭씨 거의 120도), <12피트>는 수영장의 깊이를 나타낸다(3.7미터). 둘 다 주인공들이 처한 극한 상황의 특성을 제목으로 뽑았다는 점이 흥미롭다.

이런 장르의 영화 소재를 앞으로 뭘 더 떠올릴 수 있을지 궁금해진다.
가령, 엘리베이터 안에 갇히는 건... 설마 했는데 (4) <데블>(2010)이라는 작품이 있다. 5명이 타고 있던 고층 건물 엘리베이터가 갑자기 고장 나는데, 무척 인위적이고 비현실적인 설정이긴 하다만 불이 잠시 나갈 때마다 엘리베이터 안에서 누군가 한 명씩 다치거나 죽는다.;;

밀실에서 범인이야 뻔한 노릇인데, 저 탑승자를 뒷조사 해 보니 저마다 사기꾼, 폭력 전과 등등 경력이 화려하다.
현실에서는 엘리베이터가 충분히, 너무 안전하게 만들어져 나오기 때문에 고증을 많이 무시하지 않고서는 저런 식의 영화화가 곤란할 듯하다.

끝으로, 좀 옛날 영화인 (5) <폰 부스>(2002)는 사건 전개 장소가 시내 한복판이니, 사우나나 수영장 같은 통상적인 감금의 범주에 들지는 않는다. 하지만 빌딩숲 어딘가에 숨어 있는 저격수를 설정해서 "그 전화를 끊는 순간 네놈 목숨도 끊어질 줄 알아라"로 주인공의 발을 꼼짝달싹 못 하게 묶어 놓는 게 흥미로운 설정이다.

Posted by 사무엘

2019/03/19 08:34 2019/03/19 08:34
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1598

« Previous : 1 : ... 81 : 82 : 83 : 84 : 85 : 86 : 87 : 88 : 89 : ... 230 : Next »

블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2026/07   »
      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:
3941923
Today:
906
Yesterday:
1685