6.
운전자라면 이미 다 아실 것이고 예전에 이 블로그에서도 몇 차례 언급한 적이 있듯, 우리나라의 고속도로에는 하이패스라는 통행료 무인 자동 정산 시스템이 있다. 고속도로의 입출구에서 모든 차들을 강제로 잠시 세워서 현금으로 통행료를 걷는 시스템은 매우 불편하고 비효율적이며 운전자에게나 회사에게나 좋을 게 하나도 없었기 때문이다.

그래서 최소한 잔돈 취급을 없애서 톨비 결제 속도를 조금이라도 올리기 위해 먼저 과거에 있었던 고속도로 카드가 도입됐다 식당에서 현금 대신 식권, 버스에서 토큰이 도입된 것과 비슷한 이치다. 단, 신용카드는 안정성 문제 때문에 톨게이트에서는 취급을 전통적으로 거부해 왔다.

그 뒤, 통행료 결제를 원큐에 할 뿐만 아니라 차량을 정차시킬 필요도 없게 하기 위해 다음 세대 기술인 하이패스가 도입됐었으며, 종전의 고속도로 카드는 폐지됐다.
하이패스는 편리하기도 하고 2000년대 초에 도입되던 당시에는 나름 미래의 신기술을 염두에 둔 시스템이기도 했으나, 지금은 다소 아쉬움이 남는 물건으로 전락한 면모가 있다.

요즘은 하다못해 여느 건물의 지하 주차장에만 가도 차량 번호판이 자동으로 인식되고 따로 주차권이 발급되지도 않는다. 하지만 하이패스는 식별 태그나 카드 한 장만 구비한다고 끝나는 게 아니라 모든 차량들이 단말기를 사서 등록· 개통해야 하는 번거로운 체계이다.

이건 그 당시에 고려 대상이었던 보안 통신 방식과도 관계가 있으며, 또 단말기를 통해서 통행료 결제뿐만 아니라 도로 상태(정체 여부)와 기상 정보까지 중계하여 고속도로 종합 정보 시스템을 구축하겠다는 장기적인 계획을 보고 내린 결정이었다고 한다.
하지만 오늘날 교통 정보 시스템은 싸제 내비들에게 완전히 역할이 넘어갔으니 그 예상은 빗나갔다. 굳이 하이패스에서 그런 것까지 신경 쓸 필요는 없어진 것이다.

한국 도로 공사는 전국의 모든 차량들에 하루빨리 하이패스를 장착시키고 전국의 모든 고속도로에서 하루빨리 톨게이트들을 싹 없애고 싶어 안달이 나 있다. 그 취지와 심정은 이해하지만, 하이패스의 보급이 늦어지고 있고 당장 내 차에조차 아직 하이패스가 없는 이유는 모든 차량들에 일일이 단말기를 달아야 하는 그 불편한 구조 때문이다. 초기의 진입 장벽이 높으니 고속도로를 자주 이용하지 않는 운전자라면 별로 필요를 못 느끼는 것이다.

더구나 2000년대 이후로 전국의 수많은 고속도로들이 이 시스템 기반으로 거미줄처럼 개통돼 버렸으니 이제 와서 그걸 선뜻 고치지도 못한다.
물론 일개 건물의 간단한 무인 차량 인식 주차 시스템과, 전국에서 수십~수백만 대의 차량의 출입을 실시간으로 체크하고 금융거래를 안정적으로 수행해야 하는 고속도로 통행료 징수 시스템이 그 규모와 신뢰성이 서로 비할 바는 못 되는 것은 사실이다. 그래도 좀 더 가벼운 시스템이 초기에 도입됐으면 하는 아쉬움은 계속 남는다.

이건 마치.. 비슷한 1990년대 말에 고속철도를 건설하고 차량을 도입할 때.. "앞으로 뭐 서울-부산을 겨우 1시간 56분 만에 왕래할 텐데, 차내에 편의 시설 같은 건 별로 없어도 되겠지"라는 생각으로 객실에 콘센트를 전혀 설치하지 않은 것과 같은 급으로 예상이 빗나갔다.

2차 구간에 대전· 대구 도심 구간까지 이제 경부 고속철은 전구간이 완전 개통했음에도 불구하고 KTX는 그 정도로까지 빠르게 달리지는 못하고 있다. 그리고 철도 건설이 세월아 네월아 지연되는 동안 전국민에게는 스마트폰이 보급됐고 전기 충전 없이는 잠시도 견딜 수 없는 시대가 됐다. 그러니 새마을호에도 있는 콘센트가 KTX에 없는 것은 정서상 받아들여질 수 없었으며, KTX 산천에는 곧장 콘센트가 추가되었다.

서울 2기 지하철의 경우, 미래에 건설 예정인 3기 지하철과의 환승을 고려해서 나름 머리를 써서 여의도, 몽촌토성, 녹사평, 논현 등의 역을 만들었으나.. 몇 년 뒤 IMF 때문에 3기 지하철이 거의 다 파토 날 줄 그때 누가 예상했겠는가? 결국 기존 3· 7호선의 연장과 신규 9호선만이 예상대로 추진되었으며, 여러 역들 중에 여의도와 오금 역만이 미리 만들어 둔 확장 고려 설계의 수혜를 입었다.

이처럼 어떤 대규모 건축이나 공공재 시스템 구축 사업은 불과 10~20년 뒤의 미래도 예측하기 어려운 경우가 많다. 표준을 잘못 정해서 후손들이 대대로 고생하는 게 많다. 우리나라의 경우 한글 글자판도 이 범주에 속한다.
그럼에도 불구하고 우리나라의 경우 가정용 전기 전압 220V 승압과 철도 표준궤는 미래를 내다보고 적절한 타이밍에 밀어붙여서 표준이 다행히 잘 정착해 있다. 아직 1xx V의 굴레를 못 벗어나 있는 일본과 미국처럼 되지 않았다. 일본은 협궤까지 잔뜩 깔려서 인제 와서 이걸 어찌할 수도 없고 더 고생하고 있으니 말이다.

7.
2013년에는 베네딕트 교황이 몇백 년 만에 아주 이례적으로 재직 중에 자진 사임하더니만, 2016년엔 아키히토 일왕이 갑자기 생전퇴위를 선언했다. 어떤 조직의 최고 대빵을 종신직으로 수행하고 있던 사람이 갑자기 자기 지위를 내려놓아 버리면 전직 대빵에 대한 예우도 그렇고 그 아래에 있는 사람들이 여러 모로 난감해질 텐데.
이 방면으로도 자꾸 이변이 터지는 듯한 느낌이다. 특히 일본의 경우 일왕이 인간선언을 한 지 70년 만의 일이다.

일왕 중에는 전임인 히로히토, 교황 중에는 요한 바오로 2세가 재임 기간도 길었고 격변의 20세기 중후반 동안 엄청난 임팩트를 남겼다. 후임 중에 아직 이들만 한 행적을 남긴 사람은 없다. 물론 아직 시간이 충분히 흐르지도 않기도 했고.

그래도 재위 기간과 영향력으로 치면 이들조차도 영국의 엘리자베스 2세 여왕 앞에서는 다 버로우 타야 할 것이다. 자기 나라에서 올림픽이 두 번 열리는 걸 봤으며, 또한 국가 원수로서 2차 세계 대전과 유튜브, 트위터, 스마트폰 시대를 다 경험한 할머니이다.
자기 재임 기간 동안 미국 대통령을 도대체 몇 명이나 거쳐 갔는지도 모를 지경이고. -_- 무려 1920년대생으로, 우리나라로 치면 백 선엽 장군, 송 해 씨 같은 연배와 짬인데, 은퇴도 안 했다.

8.
(1) 주민등록증과 (2) 운전 면허증과 (3) 여권은 우리나라 어디에서나 개인을 법적으로 식별하는 데 완전히 동등한 효력을 가지는 신분증이다. 하지만 이들은 개인을 입증하는 관점 내지 목적이 다르기 때문에 서로 다른 특성과 장단점이 있다.
민증은 한번 만들어 놓으면 기본적으로 평생 가지만, 면허증은 소지자가 주기적으로 운전 능력을 입증하는 '갱신'을 해야 하며, 여권은 유효기간이 지나고 나면 재발급을 받아야 한다.

세 신분증들 중에 발급하는 데 금전적인 부담이 가장 많이 드는 것은 여권이다. 또한 여권은 신분증들 중 유일하게 카드가 아닌 수첩 형태이며, 소지자의 집 주소가 기재되어 있지 않다. 분실을 대비해 국내 거주지와 연락처를 기재하는 란이 있긴 하지만, 이건 optional한 정보이기 때문에 주민등록상의 소재지를 정확하게 담고 있지는 않다. 쉽게 말해 국내 거주지가 바뀌었다고 해서 여권을 업데이트하지는 않는다는 뜻이다.

과거에 자동차가 몹시 비싸고 귀하고 자동차 운전사가 완전 고소득 전문직이던 시절엔 면허증의 희소성이 매우 높았다. 그러던 것이 자동차가 넘쳐나고 개나 소나 운전을 하게 되면서 면허증이 거의 민증을 갈음하는 보편적인 신분증의 지위에 오른 것이다.
단, 면허증이 제아무리 흔해 빠진 신분증이 됐다지만 얘는 자동차를 몰 정도의 최소한의 신체· 정신 능력이 안 되는 사람은 소지할 수 없다.

군대를 예로 들어 보면, 정말 대한민국 남자라면 '개나 소나' 다 의무적으로 가는 곳이지만, 반대로 눈 하나 없거나 엄지손가락 하나만 없어도 결코 갈 수 없는 곳이 되지 않던가? 이와 비슷한 격이다. 면허증은 신분증들 중엔 능력에 의한 진입장벽이 가장 높다.

한편, 우리나라가 못살고 외국 여행을 함부로 할 수 없던 시절엔 여권도 면허증 만만찮게 능력 진입장벽이 높았다. 당장 비행기삯은 차치하고라도 유학, 이민, 사업 출장 같은 걸 아무나 할 수 있지는 않으니까. 하지만 이제는 여권 역시 아무나 언제든지 바로 만들 수 있게 된 지 오래다. 외국 나갈 일이 없는 사람들이 '안 만들' 뿐이지, 만들고 싶은데도 '못 만드는' 사람은 없다. (상습 여권 분실로 인한 페널티에 걸리지 않은 한)

이들에 비해 민증은 제일 범용적이고 원천적이다. 면허증은 do 지향인 반면, 민증은 순수하게 be 지향이기 때문이다. (뭐, 더 정확하게 쓰자면 면허증은 may do를 나타내고 자격증은 can do를 나타낸다는 차이도 있다.)
문득 신분증들 사이의 공통점과 차이점 생각이 나서 글을 써 봤다. 이를 영적으로 적용하면 구원받는 것도 자격증을 따는 게 아니라 순수 신분증을 획득하는 것이라고 풀이할 수 있겠다.

한 2000년대부터 국가 차원의 공신력이 있는 신분증에 들어가는 증명 사진은 당사자의 귀가 반드시 노출돼 있어야 하고, 배경은 어떻고 시선은 어떻고 옷차림은 어떻고... 같은 엄격한 가이드라인이 제정되어 적용되기 시작했다. 내 기억이 맞다면 이건 여권에서 처음으로 시작된 관행이다. 전자 여권의 도입 때문인지, 9· 11 테러 때문인지 무슨 계기로 이쪽 규정이 더 엄격해졌는지가 궁금하다.

9.
옛날에.. 사회 행정 금융 시스템이 몽땅 전산화되고 사회 곳곳에 CCTV 같은 게 생기기 전엔 나쁘게 말하면 온갖 편법과 비리가 횡행했다. 보는 눈이 없고 악행의 증거를 객관적으로 챙길 방법이 없어서 말이다.
단적인 예로, 세리들은 규정된 것보다 더 많은 세금을 걷어서 차액을 '삥땅'할 수 있었다. 그래서 걔들은 비록 부유할지언정 성경이 기록되던 시절부터 이미 민족의 반역자 취급을 받아 왔다.

그런데 세금을 걷는 관원의 입장에서 역으로 실드를 쳐 주자면.. 그렇게 백성들을 잠재적 탈세자로 악하게 보고 가혹하게 착취하지 않을 경우, 소수의 진짜 나쁜놈들이 실소득을 속이고 고의적인 탈세를 저지르는 걸 감지하고 예방할 방법도 없었다. 이러니 전근대 시절엔 농민과 관리 사이의 빈부격차가 넘사벽이고 백성들의 삶이 헬일 수밖에 없었다.

하지만 전산화라는 게 없던 시절에는 열악한 검증 능력과 느린 행정 속도를 교묘하게 역이용해서 Catch me if you can 같은 초대형 사기를 치는 괴수가 있을 수 있었으며, 또 나쁜 쪽뿐만 아니라 좋은 쪽으로 예외와 일탈도 존재 가능했다.
간단한 예로는 박카스 광고라든가 영화 <진주만>에서 나오는 것처럼 원래는 규정상 안 되는데 샤바샤바 해서 이름을 추가해 넣고 군대에 쓰윽 입대하는 거 말이다.
자서전을 보면 지 만원 박사도 어렸을 때 그런 편법 유도리 덕분에 육사 같은 상급 학교에 진학할 수 있었다고 그런다. 지금으로서는 불가능하다.

옛날엔 어느 인심 좋은 시골 의사가 환자에게 "님은 그냥 잘 챙겨 먹는 게 약입니다" 이러면서 아예 돈을 처방해 주기도 했다고 한다. 지금은 이랬다간 아마 의료법 위반으로 잡혀갈 것이다.
공항에서는 아무리 다 쓰러져 가는 허약한 노인이라 해도 모르는 사람의 짐을 들어 주면 안 됨. 마약 던지기 범죄에 연루될 수 있기 때문이다.

더 옛날에 1차 세계대전 시절에는 공중전이라는 게 처음 등장했는데 텅 빈 하늘에서 전투기 조종사들끼리 마치 중세 기사의 공중 버전마냥 기사도가 잠시 꽃폈다. 도전장을 공군 기지에다 떨어뜨리고 정정당당하게 기다리고 있다가 도그파이트가 벌어지고..;; 하긴 그때는 똑같이 사람을 죽이더라도 은폐 저격은 비열하고 치사한(!) 짓거리라는 참 순진 낭만하던 생각이 통용되던 시절이어서 그렇다. 그래서 그런지 지상에서는 지휘관들이 봤으면 뒷목 잡고 쓰러졌을 '크리스마스 휴전' 사건이 벌어지기도 했다(1914년, 개전 직후 첫 해 일회성으로 그쳤지만).

19세기 말 미국에서는 웬 노턴 1세 황제(1819?-1880)라고 미국의 황제를 자처한 기인 아저씨가 레알 기성 정치인들의 입지까지 위협할 정도로 인기를 얻으면서 자칭 황제 행세를 하다가 갔다. 이게 진지하다면 내란죄에 근접해 보이고, 개그라면 허 경영 같은 과대망상 병맛 느낌도 들지만, 그는 위험하지 않으면서 허 경영보다는 100배는 더 제정신이고 훌륭한 인품을 지닌 사람이었다. 요즘 시대라면 나타나기가 더욱 불가능에 가까웠을 것 같다.

지금 사회가 점점 더 자극적이고 관능적인 걸 쫓아가며 물질 황금만능주의로 치닫고는 있으나, 과거의 예외적인 깨알같은 인간미 추억에만 연연하느라 그 사회 시스템이 전산화 이전 시절로 되돌아갈 수는 없을 것이다. 과학 기술이 발달하면서 사회는 믿음과 양심이 필요한 형태가 아니라 법과 규정과 시스템대로만 돌아가는 형태로 바뀌는 게 불가피하기 때문이다. 과정이나 동기를 일일이 따질 시간은 없고 오로지 결과만이 중요하다.

10.
본인은 법학도가 아닌 관계로 잘은 모르겠지만.. 아래와 같은 개념은 마치 경제에서 성장과 분배 비율, 직접세와 간접세의 비율을 논하는 것만큼이나 딱 떨어지는 정답이 없고 사람 취향을 많이 타는 논쟁거리가 아닌가 생각된다.

  • 사법거래: 비록 쳐죽여도 시원찮을 큰 죄를 지었더라도, 여죄를 순순히 자백해서 행정력 낭비를 줄여 주고 공범 검거 같은 추후 수사에 큰 기여를 했다면 형량을 많이 줄여 준다. 전근대적인 고문이 말 그대로 채찍이라면, 사법거래는 당근에 해당되겠다.
  • 함정수사: 경찰 측이 용의자 내지 일반 시민에게 미끼를 던져서 일부러 죄를 짓도록 유도한 뒤, 누가 미끼에 걸리면 이때 뿅 나타나서 잡아 족친다. 암행 단속을 위해 단순히 사복 차림 내지 일반 차량으로 위장만 한 차원이 아니다.

사법거래는 공권력이 개인의 유죄를 입증하는 데 소요되는 공권력 행정력의 낭비를 줄여 준다는 점에서는 좋다. 억지로 옷을 벗기는 것보다는 당사자가 스스로 옷을 벗게 만드는 게 좋듯이 말이다. 하지만 절대적인 정의 구현이 핵심인 형사 사건에다가 "서로 좋은 게 좋은 거잖아" 식의 실용주의 경제 논리를 적용하는 것이라는 비판을 피하기 어렵다.

사법 제도와는 무관한 시스템이지만 비슷한 예로 '기여입학'이 있다. 얘는 당연히 학문의 전당에다가 실용주의 경제 논리를 적용한 거라는 비판이 있다(돈으로 해결 가능하지 않아야 하는 영역에 돈을??). 미국은 사법거래와 기여입학이 모두 활성화돼 있지만 우리나라처럼 아직 천민 자본주의의 때가 완전히 벗겨지지 않은 곳에다 이를 적용하는 건 좀 무리인 듯하다.

경찰이 하는 일은 창과 방패 중 거의 언제나 방패 역할에 국한돼 있다. 오목으로 치면 늘 흑이 아닌 백돌만 잡는 격이다. 하지만 어떤 범죄는 이런 불리한 위치에만 있어서는 증거를 확보하고 적발하고 뿌리뽑기가 대단히 힘들고 어렵다.
그렇기 때문에 이런 상황에서는 경찰이 불가피하게 좀 더 적극적이고 사악(?)하게 나간다. 개인 단위의 단순 잡범이나 흉악범을 잡는 것보다는, 마약이나 조직적인 위조지폐, 탈세, 간첩처럼 범죄 조직이 뿌리깊게 얽혀 있고 말단의 행동대원 한두 놈 잡는 걸로는 일망타진이 되지 않는 범죄가 그 대상이다. 이쪽으로 일이 더 전문화되면 그건 경찰이 아닌 방첩기관의 영역이 된다.

특히 마약은 굉장히 가혹하다. 거의 연좌제에 가까운 수준으로 조금이라도 관련된 사람은 진심· 본의 따위는 묻지 말고 몽땅 잡아 가두는 식으로 수사하지 않으면 조직을 송두리째 소탕할 수가 없는가 보다. 그러니 함정에 걸리지 말라는 차원에서, 공항에서 짐을 옮겨 달라는 부탁조차 절대로 들어 주지 말라고 나라에서 홍보를 하는 것이다. "니가 마약을 했느냐 안 했느냐"가 아니라 "마약이 결과론적으로 너를 통해서 옮겨졌느냐"가 문제가 되기 때문이다.

참 매정하고 비인간적으로 보이지만 공권력은 그 존재부터가 이미 필요악이다(세금을 안 내거나 지금보다 훨씬 더 적게 내는 꿈같은 세상이 상상이 되는가?). 그런 걔네들이 또 다른 필요악을 동원하는 것 자체는 그리 이상한 일이 아니어 보인다. 경찰은 존재감 없이 있는 증거만을 토대로 수사해야 하는데, 적극성이 너무 커져 버리면 없는 증거를 조작해서 만들어 내는 수준까지 갈 테니 그건 또 경계해야 할 사항이다.

끝으로, 우리나라는 정당방위에 굉장히 인색한 반면, 애초에 총 들고 자기 집 지킨다는 관념이 강한 미국은 그에 대한 판정이 매우 관대하다. 이것도 사법거래· 기여입학· 함정수사 등에 대한 인식만큼이나 문화적인 차이 되겠다. 그래도 미국 쪽이 전반적으로 개인의 자유와 책임과 자율을 더 강조하는 선진적인 체계인 것 같다.

Posted by 사무엘

2017/01/08 08:29 2017/01/08 08:29
, , , ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1314

1.
영국은 섬나라이고 전통을 아주 좋아하며, 세계에서 보기 드물게 좌측통행을 하고 내부적으로 여러 왕국들로 갈라져 있어서 독립하네 마네 다투는 등, 유럽의 여느 나라와는 달리 독특한 점이 많다. 일찌감치 교황과 결별하여 정치· 종교적으로 내륙과는 별개노선을 갔으며, 리즈 시절에 그야말로 대영제국을 이뤘고 영어라는 자기네 언어와 킹 제임스라는 성경을 전세계에 퍼뜨린 것은 정말 비범한 면모가 아닐 수 없다.

그렇다 보니 영국은 역사적으로 볼 때 혼자 국제(= 유럽 대륙) 추세를 거스르면서 좀 고집 부리고 삽질한 사례도 있었다.
(1) 먼저 달력 얘기부터. 잉글랜드는 그레고리력의 도입 시기가 무려 1752년으로 유럽에서 압도적으로 제일 늦은 꼴찌였다(무려 100수십 년). 율리우스력보다 오차가 더 적고 정확한 역법임에도 불구하고 종교적인 원수지간인 교황이 만든 달력을 선뜻 받아들이고 싶지 않았기 때문이다.

로마 가톨릭이 워낙 절기를 많이 따지는 종교이기도 하고, 교황은 전세계에서 날고 기는 똘똘한 고위 성직자들 중에서 선출되기도 하니.. 천체의 운동을 계산하는 덴 머리가 비상하게 잘 돌아갔는가 보다.

영국의 대문호인 윌리엄 셰익스피어, 그리고 스페인의 대문호인 세르반테스는 동갑은 아니지만 공교롭게도 1616년 4월 23일, 같은 날에 세상을 떠난 걸로 알려져 있다. 하지만 세르반테스는 그레고리력 기준의 날짜이고 셰익스피어는 아직 율리우스력 기준의 날짜이기 때문에 두 사람이 실제로 같은 날에 24시간 이내의 시간차를 두고 죽은 건 아니라고 여겨진다. 뭐, 그래 봤자 기일의 차이는 2주를 넘지 않았을 것이고, 서로 다른 달력 체계에서 그렇게라도 날짜가 일치하는 것만 해도 용한 일이긴 하다.

돈키호테가 전편이 1604~05년에 출간됐고 후편은 1615년, 작가가 죽기 딱 1년 전에 완간되어 나왔다. KJV가 출간된 1611년과 동시대 되겠다.
단, 셰익스피어는 그 당시에 자국에서 출간되던 킹 제임스 성경과 관련해서 딱히 기여한 건 없는 걸로 여겨진다.

(2) 1749년엔 미국의 벤저민 프랭클린이 피뢰침을 발명했는데.. 피뢰침의 끝을 뾰족하게 만드느냐 뭉툭하게 만드느냐가 과학자들 사이에 논쟁거리가 되었다.
여기서 영국은 뭉툭한 피뢰침을 한동안 고집했다. 이유는.. 그게 과학적으로 옳아서가 아니라, 영국 황실에 반역하고 독립을 선언한 미국 식민지 역적놈이 뾰족한 걸 쓰기 때문이었다. 으음..

영국이 이런 사소한 데서 은근히 병맛스러운 고집을 많이 부렸구나. 무슨 에디슨과 테슬라의 교류 직류 논쟁도 아니고. 우리나라에만 당파 싸움이 있었던 게 아니다.
그러니 걸리버 여행기가 릴리풋 왕국(소인국) 편에서 계란 껍질을 어느 쪽부터 까는지를 갖고 대판 싸우는 풍자 장면을 넣었지 싶다. 그 당시 영국 상류층 사회라면 충분히 있을 수 있는 설정이다. 컴퓨터계에서 유명한 엔디언(비트 순서)이라는 단어가 여기에서 유래된 걸로 유명하다.

그런데, 나는 당연히 뾰족한 피뢰침이 과학적으로 집전 성능이 더 뛰어나고, 미국의 승리에 영국의 삽질로 알고 있었는데.. 영문 위키백과를 보니까 꼭 그렇지 않은 것 같다. 여전히 논쟁의 여지가 있다고 한다.

(3) 끝으로 20세기. 영국은 남극점 최초 정복 타이틀을 듣보잡 노르웨이 사람에게(로알 아문센) 빼앗긴 것을 도저히 견딜 수 없었다. 아문센에 대해서 대놓고 인신공격 음해 언플을 벌인 건 물론이고, 학교 교과서에다 자국의 스콧이 남극점에 먼저 도달했다고 대놓고 주작 왜곡 거짓말을 가르치기까지 했다. 그러다 이웃 나라들로부터 극심한 비웃음을 견디다 못해 슬쩍 고쳤다. 이 정도면 6· 25 북침 왜곡쯤은 약과인 거 같다.

그러니, 이런 영국은 자기들이 표준궤나 표준시 같은 세계 표준을 만들면 만들었지, 남이 만든 표준은 잘 안 쓴다. 도량형에 미터법도 안 쓰고, 화폐단위는 여전히 파운드를 고집하며 최근에는 유럽 연합에서 탈퇴까지 감행했다. 이런 조치에 대한 호불호 내지 정치적인 옳고 그름을 논할 생각은 없다. 단지, 쟤들이 그러고도 남을 친구들이라는 심정이 이해된다.

2.
영국 하면 왠지 빨강이 떠오른다. 레드 코트, 적기 조례 등등..
그래서 보너스로 red와 대응하는 다른 색들과 그 심상· 용도로는 어떤 게 있을까? 언어에도 유의어와 반의어라는 관계가 있는데 이런 거 찾아보니까 결과가 재미있다.

  • 황색: 주의와 경고(축구 반칙 카드)
  • 백색: 적혈구와 백혈구, 적색거성과 백색왜성(천문), 러시아 적백 내전, 전방과 후방에서 교통수단의 등화 차이(브레이크등/전조등· 후진등)
  • 흑색: 지나친 G로 인한 블랙아웃과 레드아웃(항공우주), 빨강-검정 나무 자료구조(전산), 홍차의 한국어와 영어 표현 차이. 비행기 블랙박스는 사실은 검정이 아니라 찾기 쉬우라고 고채도 홍/황색 계열 도색임.
  • 녹색: 적십자와 녹십자, 적록 색맹, 적조와 녹조, 횡단보도 신호등, 엘리베이터나 주식에서 상하 관계
  • 청색: 수도꼭지와 정수기에서 온수와 냉수, 각종 게임에서 양 진영의 깃발 색깔, 남녀(단, 성차별적이라고 요즘은 색깔 구분은 안 하는 듯), 헤모글로빈과 헤모시아닌 기반 혈액

3.
독자 여러분은 개를 식용하는 것에 대해 어찌 생각하시는가?
본인의 생각을 말하자면 개 역시 인간이 필요에 따라 키우는 여러 가축 중 하나이고, 동시에 필요한 경우 여러 단백질 공급원 중 하나일 뿐이다. 얘만 특별하게 취급돼야 할 필요는 없으며, 하물며 같은 개 중 일부 품종은 애완견용이고 일부는 식용으로 분류해야 할 필요조차 없다고 본다. 애완견이 특별히 영양학적으로 독이라도 들어있어서 못 먹는 건 아닐 테니 말이다.

개는 특별히 똑똑하고 충성심이 뛰어나고 인간과 닮았고 무슨 감정이 있고 어떻다고..? 그게 그렇게까지 특별한 메리트가 되지는 못한다는 뜻이다. 개가 아무리 사랑스러워 봤자 인간과는 달리 죽으면 완전히 소멸이며, 내세에서 다시 인격체로서 만나 보지도 못한다. 또한 잡아먹히는 개가 불쌍하면, 더 열악한 환경에서 인간에게 착취당하고 잡아먹히는 다른 동물들은 뭐가 되는가?

애견 동호인 분들께는 죄송한 얘기지만, 개만 유독 집 밖이 아닌 집 안에 들여놓고, 그걸로도 모자라 옷 입히고 고기까지 먹이면서 키우는 건 그거야말로 개를 잡아먹는 것보다 더 비인간적이고 비정상인 현상으로 보인다. 헐벗고 굶주리고 못사는 '사람'도 부지기수인데..! 애완견 키우는 분들이 다 그렇다는 얘기는 물론 아니지만 애완견에게 과도하게 집착하는 사람들이 다른 선과 악이나(절대악과 필요악) 생명 우선순위를 논하는 데서는 좀 이성이나 판단력이 이상하게 비성경적인 쪽으로 꼬이는 경향이 있다. 뭐, 브리짓 바르도 같은 아줌마는 그 중에서도 평균 이상 굉장히 막장으로 추해진 사례이고.

더구나 선진국들에서는 휴가철만 되면 유기견들 처리하느라 난리도 아닌데, 지금 시국에서는 개가 진정 불쌍하다면 애견인들이 "개 잡아먹지 말라"에 앞서 "개를 버리지나 마라/말자"부터 더 강조해야 하지 싶다. 식용으로 잡아먹히는 거나, 약물 주입으로 안락사 당하는 거나(주인을 못 찾아서, 혹은 주인이 고의로 인수를 거부해서) 죽는 건 똑같다.

무슨 성매매를 합리화하고 공창을 만들듯이 개 도축도 규격을 정하고 완전히 법의 양지로 끌어올리자는 얘기가 진작부터 있었다. 하지만 "대한민국은 이렇게 개도 FM에 규정된 대로 잡아먹는 나라요"라고 대외적으로 홍보를 하는 건 동네 창피한 일인 걸로 간주되는 듯하다. -_-;; 마치 눈치 보느라 흉악범 사형 집행조차 못 하듯이. 개 잡아먹는 게 그렇게도 부끄러운 일인가? 좀 민망한 현실이다. (난 개고기는 일부러 피하지는 않고 회식 자리에서 있으면 먹는 정도로 먹는다.)

4.
사람들이 자기가 실제로 소유한 것보다 더 많이 소유한 것처럼 남에게 허세 부리고 싶어하는 심정은 어디에나 있는 듯하다.
예비군들은 전투복에 자기가 이수하지 않은 온갖 특기 마크를 덕지덕지 붙여서 일명 전투복 튜닝을 하고.. 군 내부의 높으신 분들도 비슷한 맥락으로 온갖 훈장들을 제복에다 주렁주렁 붙인다.

이런 맥락으로, 자동차의 경우 엠블럼이나 머플러를 튜닝해서 실제 성능보다 더 고성능인 차인 것처럼 위장을 하는 경우가 많더라.
내 또래의 직딩이 어떻게 쏘나타 2400cc 내지 제네시스 쿠페 3800cc를 굴리나 궁금했는데, 알고 보니 머플러와 엠블럼만 교체해서 상위 사양인 것처럼 보이게 했을 뿐, 실제로는 2000cc 차였다.

지금까지 에쿠스 5000cc를 생각보다 자주 본 적이 있었다. 이것들 중에도 실제로는 기본 사양 3800cc이지만 엠블럼 위장을 한 놈이 있을지 모르겠다. 에쿠스는 제로백이 5~6초대인데, 성능이 성능이니만큼 3800cc와 5000cc는 제로백이 약 1초 가까이 차이가 난다고 한다. 길이가 5m가 넘는 거대한 차량이 밟으면 그렇게 쑥 가속을 받고 튀어나간다는 게 상상만 해도 신기하다. 괜히 고성능이 아니다.

자가용 승용차는 배기량이 높아질수록 성능은 좋아지지만 연료 소모량이 증가하여 연비도 떨어지며, 무엇보다도 자동차세도 왕창 붙어서 유지비가 증가한다. 아파트 모델하우스에 가서 크기가 3종류인 집을 구경하니 마치 2000cc, 2400cc, 3000cc 차급을 보는 것 같은 생각이 들었다.
그런데 차량의 배기량 위장은 당장 도로에서의 안전을 위협한다거나 무슨 계급 위조 및 사칭만치 해로운 짓은 아니기 때문에 딱히 법적으로 금지되어 있지는 않은 것 같다.

5.
옛날 자동차와 지금 자동차를 비교해 보면 엔진이 동일 배기량으로도 출력과 연비가 비약적으로 향상되고 승차감이 좋아졌으며, 그러고도 환경오염은 덜 시키는 쪽으로 발전해 왔다. 하지만 엔진의 구동 원리는 100여 년 전이나 지금이나 피스톤 왕복 형태를 크게 벗어나지 않아 있다. 또한 엔진의 회전력과 토크를 바퀴를 굴리는 데 적합한 형태로 변환해 주는 변속기도 수동은 톱니바퀴, 자동은 유압 토크 컨버터로 형태가 정착해 있다.

이런 관행을 바꾸려는 시도를 한 이의를 제기하는 대표적인 발명으로는 방켈/반켈(Wankel) 또는 로터리라고 불리는 엔진이 있다. 피스톤이 동그란 삼각형 모양으로 생겼고 얘가 두 바퀴가 아니라 한 바퀴를 돌면서 흡입-압축-폭발-배기 행정을 해치운다. 그건 좀 2행정 엔진을 닮았다.

로터리 엔진은 왕복 운동을 회전 운동으로 힘들게 변환하는 계층이 필요하지 않으며 연료의 폭발을 더 직관적으로 회전 운동으로 연결해 준다. 그래서 배기량 대비 엔진의 출력이 더욱 높다는 장점이 있으나.. 기계적으로 만들기가 더 어렵고 엔진 부품의 마모가 더 심하고 유지비가 많이 들어서 엔진의 주류로 등극하지는 못했다.
장점이 있긴 하지만 아직 단점이 더 커서 실용화되지 못하고 있는 기술이라는 점에서는 무탄피 총알과도 비슷해 보인다.

또한 변속기에도 일명 CVT라고 불리는 '무단 변속기'가 있다. 얘는 N개의 단을 나타내는 이산적인(discrete) 톱니바퀴로 변속을 하는 게 아니라, 일종의 원뿔대 모양의 체인에다가 벨트를 걸어서 최고단과 최저단 사이의 아무 동력비라도 표현할 수 있게 했다.
얘는 의도한 대로 잘만 만들 수 있다면 변속 충격 없이 자동 변속기처럼 부드러우면서, 연비는 수동 변속기처럼 좋은 이상적인 동력 변환 계통을 구현할 수 있다.

하지만 CVT 역시 변속기의 주류가 되지 못한 건 다 기술적인 한계가 있기 때문이다. 얘는 대형차의 고출력 엔진에 적용되기에는 아직 무리가 있으며, 여전히 경차· 소형차급에 머물러 있다. 자전거만 해도 체인이 견딜 수 있는 힘보다 너무 강한 힘으로 밟으면 체인이 미끄러지거나 심하면 파손되지 않던가? CVT는 그런 게 기존 변속기보다 더 취약하다는 뜻이다. 예전에 인천대교에서 퍼져서 대형 사고를 일으켰던 마티즈 CVT 때문에 CVT에 대한 더 나쁜 편견과 오해가 생기기도 했다.

아무튼, 내연기관에 왕복 운동이 아니라 회전 운동을 직통으로 만들어 내는 로터리 엔진이라는 게 있는데.. 전기 모터에는 회전 운동이 아니라 직선 운동을 직통으로 만들어 내는 모터가 있다. 바닥에 달린 유도 레일을 따라 출력을 발생시키는 이 모터를 '선형 유도 모터'라고 한다.

초전도 자기부상열차는 모두 선형 유도 모터(LIM) 기반이다. 하지만 그런 방식이 아닌 경전철도 LIM 기반인 경우가 있으며, 국내의 경우 용인 경전철이 대표적인 예이다.
일본에서는 L0계라고 불리는 '주오(중앙) 신칸센'이 개통하여 최고 시속이 무려 600km를 넘어섰다. 주행 속도가 너무 빠르기 때문에 안전을 위해 창문은 무슨 비행기 창문처럼 작아졌으며, 앞뒤로 운전실이 없지만 그래도 앞뒤 경치를 구경할 수 없다.

그리고 열차의 앞뒤는 비행기보다도 더할 정도로 극단적으로 납작하고 뾰족해졌다. 기계가 들어갈 공간, 승객이 탈 공간을 좀 뺏겨도 좋으니 어떻게든 공기 저항을 극단적으로 줄이려고 발악에 가깝게 노력한 흔적이 보인다.
그도 그럴 것이 공기 저항은 고속 주행 교통수단의 최악의 적이며, 속도가 올라갈수록 저항도 급격히 커지기 때문이다. 비행기는 그래도 대기가 상당히 희박해져 있는 고도에서 날지만 열차는 공기 농도가 제일 짙은 지표면을 달리지 않는가?

한때 우리나라 언론에서는 '리니어'가 선로가 커브가 전혀 없이 곧은 직선이라는 뜻이라고 아주 말도 안 되는 오보를 내보낸 적이 있었다. 10여 년 전에 <인천 관광 2009년부터 전차로>라는 기사를 내보내면서 군용 탱크 사진을 내보냈던 것과 같은 급의 병크이다!

Posted by 사무엘

2017/01/05 08:35 2017/01/05 08:35
, , , , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1313

<날개셋> 한글 입력기는 잘 알다시피 16년 전에 개발된 1.0과 지금의 8.6이 요구하는 운영체제 사양(그리고 사실상 하드웨어 사양도)에 차이가 전혀 없는 좀 사기급의 프로그램이다. 32비트 에디션은 Windows 95/NT4 이상에서도 돌아간다. Win95쯤은 안드로이드 스마트폰 내부에서 가상 머신으로도 돌리는 지경이 됐는데도 말이다. 뭐, 내 프로그램은 게임처럼 딱히 최신 사양빨을 타는 분야의 프로그램이 아니며, 한글이 무슨 한자처럼 처리하는 데 메모리가 엄청 많이 든다거나 아랍· 태국 문자처럼 내부 메커니즘이 복잡한 것도 아니기 때문이다.

Windows는 API 함수들이 유니코드를 표방하는 2바이트 문자열을 취급하는 버전(W 함수)과 비유니코드 일명 'ANSI 인코딩'을 표방하는 1바이트 문자열을 취급하는 버전(A 함수)으로 나뉘어 있다. 맥이나 리눅스 같은 타 운영체제에서는 찾을 수 없는 독특한 형태이다. 물론 문자 집합이라는 건 굳이 인코딩 단위에 얽매여 있지는 않으니, 1바이트라는 단위는 그대로 놔 두고 UTF-8만 사용해도 유니코드 지원은 가능했다. 하지만 Windows는 호환성 때문인지 문자 집합과 함께 인코딩까지 완전히 바꿔 버리는 방식을 채택했다. 그래서 wchar_t도 4가 아닌 2바이트이며, UTF-16을 유난히 좋아한다.

Windows NT는 W가 기본이고 A도 호환성 차원에서 지원하지만 Windows 9x는 메모리 부족 문제로 인해 A만 지원하고 W는 아예 제공하지 않았다. 그러니 일반적으로는 Windows 9x를 지원하려다 보면 유니코드를 지원할 수 없어서 깨진 문자 크리 때문에 프로그램의 국제화에 애로사항이 꽃폈으며, 반대로 W 함수만 사용하면 가정에 NT 계열보다 더 많이 보급돼 있던 9x 계열 운영체제를 지원할 수 없었다.

이 딜레마를 해소하는 방법은 일단 프로그램은 W 함수 기반으로 개발한 뒤, 9x에서는 특별히 W 함수 진입로에서 함수 argument를 변환하고 나서 A 함수를 호출하는 일종의 훅/thunk DLL을 구동하는 것이었다. <날개셋> 한글 입력기는 이 테크닉을 사용한다.
훅 DLL의 소스 코드는 동작 방식의 특성상, import table상의 함수 이름 문자열과 거기에 대응하는 훅킹 함수 포인터를 명시한 테이블을 갖고 있다. 또한 기존 Windows API 함수와 프로토타입이 동일하지만, 하는 일에는 살짝 차이가 있는 함수도 즐겨 사용한다.
이런 걸 구현할 때는 C/C++ 언어에 존재하는 다음과 같은 기능들이 유용하게 쓰였다.

1.
함수 훅킹 테이블을 만들 때 #define과 더불어 #(문자열화)와 ##(토큰 연결)라는 전처리기 연산자를 즐겨 썼다.
_FUNC(SetWindowTextW) 하나로 { "SetWindowTextW", (FARPROC)My_SetWindowTextW } 요걸 표현할 수 있으니 전처리기 연산자를 써서 매크로를 정의하는 게 완전 딱이지 않은가?
C언어는 전처리기의 단항 연산자는 # 1개로, 이항 연산자는 # 2개로 표현해서 나름 직관성을 추구했다. 그리고 안 그래도 전처리기 연산자는 C/C++의 고유한 연산자와는 섞여서는 안 되는데 굳이 # 말고 다른 기호를 끌어다 쓰지 않아서 형태 구분이 잘 되게 했다.

그런데 여기서 문제가 하나 있다.
문자열화 연산자는 매크로 전개를 한 놈을 문자열로 바꾸는지, 아니면 언제나 주어진 인자를 문자 그대로 문자열로 바꾸는지를 본인은 엄밀하게 생각을 하지 않고 지냈다. #define ToString(a) #a라고 정의해 주면, ToString(SetWindowText)은 "SetWindowText"로 바뀌는지, 혹은 "SetWindowTextW"나 "SetWindowTextA"로 바뀌는지 궁금했다.

이에 대한 정답을 먼저 말하자면, # 연산자는 그 자체로는 매크로 전개를 전혀 하지 않는다. 그렇기 때문에 저 문제의 정답은 "SetWindowText"이다.
만약 W/A가 붙은 놈을 얻고 싶으면 매크로를 한 단계 더 거쳐 줘야 한다. #define ToString_Expanded(a) ToString(a)를 선언한 뒤, ToString_Expanded(SetWindowText)라고 명령을 내리면 그제서야 "SetWindowTextW"(또는 A)가 얻어진다.

물론 딱히 매크로가 없는 인자를 넘기면 ToString_Expanded는 그냥 ToString과 동일한 결과가 나온다. 이런 차이가 있다는 걸 근래에 알게 됐다.

C/C++ 코드에는 검증과 디버깅을 위해 assert 부류의 매크로를 볼 수 있는데, C 언어 표준 매크로 상수와 연산자들은 상당수가 얘를 구현하기 위해 만들어진 게 아닐까 싶을 정도이다.
상식적으로 생각해 봐도, 실행 파일 내부에 "result > 0이라는 수식의 assertion이 실패했습니다. 아무개.cpp n째 줄입니다." 정도의 검증 명령이 삽입되려면 딱 봐도 __FILE__, __LINE__이 들어가야 했을 것이고 검증 대상 수식은 # 연산자에 의해 문자열로 바뀌었을 거라는 걸 알 수 있다.

파일명과 줄번호는 바이너리 형태의 디버그 심벌에도 포함되긴 하지만, result > 0처럼 대놓고 코드를 구성하는 문자열은 # 연산자 없이는 답이 없다. 이런 사기급의 전처리 기능은 C/C++ 외의 다른 언어에서는 유례를 거의 찾을 수 없지 싶다.

2.
또한 decltype이라는 연산자가 있는 줄을 난생 처음 알았다. 연산자이긴 하지만 되돌리는 게 어떤 값이 아니라 타입 그 자체이다. typeid처럼 RTTI와 관계 있는 기능도 아니며, 컴파일 타임 때 결정되는 고정 타입이다. 그래서

auto x=3.4f;
decltype(3.4f) x = 3.4f;
float x=3.4f;

는 의미가 모두 동일하다. auto와도 어떤 관계인지 바로 알 수 있을 것이다.
sizeof는 값 또는 타입을 모두 받아들여서 값(크기. 고정된 정수)을 되돌리는 반면, decltype은 값을 받아서 타입을 되돌린다는 차이가 있다. 또한 sizeof와 decltype 모두 그 값을 실제로 실행(evaluate)하지는 않는다.

auto는 타입과 동시에 변수값 초기화를 할 때 번거로운 타이핑을 줄여 준다. decltype은 값을 동반하지 않고 타입 자체만을 명시할 때 매우 유용하다. 템플릿 인자를 명시하거나 형변환을 할 때, 길고 복잡한 namespace나 함수 포인터의 프로토타입을 쓰는 수고를 덜어 준다. typedef를 하자니 번거로운 이름을 떠올려야 하는데.. 그럴 필요도 없어진다. 가령,

CAPIPtr<int (*)(int flags, WPARAM wParam)> pfnAbout(hNgsLib, "ngsAbout");

라고 쓸 것을

CAPIPtr<decltype(&::ngsAbout)> pfnAbout(hNgsLib, "ngsAbout");

로 간편하게 대체 가능하다. 함수의 이름만으로 그 함수의 포인터의 프로토타입을 간단히 명시할 수 있으니 얼마나 편리한가? API 훅킹 라이브러리를 만들 때도 이런 문법이 매우 유용할 수밖에 없다. 훅킹 대상인 Wndows API들이야 헤더 파일에 프로토타입이 다 선언돼 있으므로 그걸 decltype의 피연산자로 주면 되기 때문이다..

또한, 과거에는 클래스에서 함수 포인터 형변환 연산자 함수를 선언할 때는 C++ 문법의 한계 때문에 반드시 그 함수 프로토타입을 typedef부터 해야 했다. 하지만 decltype은 여기서도 그런 번거로움을 응당 없애 준다. 아래 코드를 보면 차이를 알 수 있다.

class CMyTable {
    static int _Func();
public:
    //과거
    typedef int (*PFN)();
    operator PFN() { return _Func; }

    //현재
    operator decltype(&CMyTable::_Func)() { return _Func; }
};

decltype 연산자는 Visual C++ 2010부터 지원됐다. 함수 포인터에다가 람다를 바로 대입하는 건 2010은 아니고 2012부터 지원되기 시작했다. 물론 캡처가 없는 람다에 한해서. 람다는 함수 포인터보다 더 추상적인 놈이기 때문에 calling convention은 컴파일러가 알아서 다 해결해 준다.

C++은 잘 알다시피 A *B와 A B(), (A)+B 같은 문장이 A와 B의 정체가 무엇인지에 따라(타입? 값?) 파싱 방식이 완전히 달라진다. 템플릿이 추가된 뒤부터는 <와 >조차도 이항 연산자 vs 타입 명시용의 여닫는 괄호처럼 해석이 달라질 수 있게 되었고, 21세기에 와서는 템플릿 인자를 이중으로 닫을 때 굳이 > > 안 하고 >>로 써도 되게 문법이 바뀌었다. 저게 제대로 돌아가려면 값과 타입의 구분이 더욱 절실히 필요하다.

이런 특성 때문에 템플릿의 컴파일 편의를 위해 typename이라는 힌트 키워드가 도입되었으며, auto와 decltype도 동일한 용도는 아니지만 비슷한 맥락에서 type과 관련된 기술을 돕기 위해 등장한 게 아닌가 싶다.

3.
유니코드 API 훅킹 DLL을 만든다면, SetWindowTextW라면 WCHAR 문자열 형태로 전달된 인자를 char 문자열로 바꾼 뒤 A 함수에다 전달하고, GetWindowTextW라면 먼저 내부적으로 char 버퍼를 준비해서 A 함수를 호출한 뒤, 그걸 WCHAR로 변환해서 사용자에게 되돌리는 형태로 전달한다.

물론 용례가 무궁무진한 메시지를 주고받는 함수라든가 GetOpenFileName처럼 입· 출력 겸용 복잡한 구조체를 운용하는 함수, SystemParametersInfo처럼 PVOID 하나에 온갖 종류의 데이터를 주고받는 함수라면 훅킹 함수를 만들기가 아주 까다로워진다. 하지만 그 함수가 제공하는 모든 기능에다 일일이 변환 기능을 넣을 필요는 없다. 다양한 플래그와 기능들 중에서 내 프로그램이 실제로 사용하는 것에 대해서만 변환을 하면 된다.

그런데 훅킹 함수 중에는 의외로 아무 변환 없이 인자를 그대로 A 함수로 넘기기만 하고 리턴값도 아무 보정 없이 그대로 되돌리는 것도 있다. 훅킹 함수 단계에서 딱히 할 게 없다고 말이다.

그 대표적인 예로는 리소스를 리소스 ID가 아니라 메모리 포인터 차원에서 저수준으로 읽어들이는 DialogBoxIndirect와 LoadMenuIndirect가 있다.
얘들이 인자로 받아들이는 DLGTEMPLATE와 MENUTEMPLATE 구조체는 내부에 PCTSTR 같은 게 없으며, 애초에 A/W 구분이 없다. 왜냐하면 저 구조체는 메모리가 아니라 디스크에 저장되는 리소스 데이터 포맷을 기술하기 때문이다. Windows 9x용이든 NT계열용이든 실행 파일이야 서로 완전히 동일한 포맷이며 리소스들은 모두 유니코드 형태로 저장된다. 그러니 인자가 동일한데 저 두 함수도 원론적으로는 굳이 W/A 구분을 할 필요가 없다.

그럼에도 불구하고 이런 함수에도 굳이 A/W 구분이 존재하는 이유는 얘들이 내부적으로 대화상자와 메뉴 윈도우를 생성할 때 사용하는 CreateWindowEx 함수가 A/W 구분이 존재하며, 9x에서는 W 버전이 존재하지 않기 때문이다. 비록 리소스 데이터 상으로는 원래의 언어 텍스트가 들어있지만, 운영체제가 관리하는 윈도우의 텍스트 버퍼는 ANSI 기반이니 그걸 운영체제의 표준 기능만으로 제대로 표시할 방법도 없다.

그렇다면.. Windows 9x에서는 DialogBoxIndirectW나 LoadMenuIndirectW가 호출 됐을 때,
SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE / NULL; 을 하지 말고..
return DialogBoxIndirectA( ... ) / LoadMenuIndirectA( ... ); 를 해도 되지 않았나 하는 의문이 남는다. 직통으로 A로 포워딩하는 거 말이다.
그럼 9x에서는 현 ANSI 인코딩으로 표현되지 않는 문자들은 비록 깨져서 출력되겠지만 최소한 메뉴나 대화상자가 뜨고 동작은 하지 않겠는가?

하지만 그건 별 의미가 없다고 생각돼서 조치를 취하지 않은 것 같다. GetOpenFileNameW, CreateFileW, CreateWindowExW, GetMessageW, SendMessageW 등등.. Windows 프로그램의 근간을 이루는 함수들이 유니코드 버전은 몽땅 동작하지 않는데 저런 것만 살려 놔서 뭘 하겠나? Windows 9x에서는 최소한의 유니코드 문자를 찍는 GDI 함수만이 제 기능을 하며, MessageBoxW는 인자들을 char 형태로 변환해서 예외적으로 지원해 주고 있다. 최소한의 에러 메시지를 찍고 종료하는 기능만은 유니코드 API 직통으로 동작하게 말이다. =_=;;

Posted by 사무엘

2017/01/02 08:25 2017/01/02 08:25
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1312

<날개셋> 한글 입력기를 사용하는 프로그램들은 입력 설정을 바꾸는 UI인 '날개셋 제어판'을 꺼내는 기능이 들어있다. 그 프로그램으로는 편집기, 외부 모듈, 입력 패드, 그리고 타자연습 이렇게 4종류가 있는데, 제어판을 꺼내는 방법은 일반 메뉴, 입력 도구모음줄, 시스템 트레이 우클릭 메뉴, 대화상자의 버튼 이렇게 형태가 제각각 모두 다르다. 어째 이렇게 전부 다를 수 있는지도 궁금한 지경이다.

그런데 사실은 이 프로그램들이 내부적으로 제어판을 운용하는 방식도 전부 다르며, 미세한 차이가 존재한다. 본인은 이와 관련된 코드 리팩터링 작업을 하다가 차이점들을 글로 한데 정리해 보았다.

1. modality

중앙 집권적인 EXE 프로그램인 편집기와 입력 패드는 modeless 형태로 구동된다. 제어판 창을 띄워 놓은 채로 본문의 입력란으로 얼마든지 이동 가능하며, 제어판의 우측 하단에 '적용'이라는 버튼도 응당 존재한다.

그러나 외부 모듈과 타자연습은 modal이다. 외부 모듈의 경우 중앙 집권이 아니라 각각의 프로세스들에 붙어서 돌아가는 일종의 떨이이며, 제어판이 떠 있는 동안 자기가 담당하던 스레드가 없어지는 것을 포함해 갖가지 상황이 밖에서 벌어질 수 있다. 그렇기 때문에 상황 관리의 복잡도를 제어하기 위해서 제어판 창이 뜬 동안은 원래의 입력란으로 돌아갈 수 없게 했다.

타자연습은 외형은 중앙 집권이긴 하지만, 무슨 텍스트 에디터처럼 문자 입력이 상시 가동 중인 채로 제공되는 프로그램은 아니다. (타자 연습 화면은 메뉴에서 뭔가를 선택한 뒤에만 나타남) 그러니 제어판을 띄워 놓은 채로 타자 연습을 한다거나 메뉴로 돌아가는 상황은 생각하지 않으며, 자연스럽게 modal UI를 채택했다. 날개셋 제어판을 열었다면 이것부터 닫아야 이전 단계 화면으로 돌아갈 수 있다.

2. 입력 설정을 읽고 쓰는 방식

사실, 대화상자의 모달 여부나 다른 것보다도 이게 제일 중요하고 본질적인 차이점이다.

(1) 중앙 집권적인 편집기와 입력 패드는 형태가 제일 깔끔하고 단순하다. 그냥 공통 단일 입력 설정 하나만을 읽고 쓰는 걸로 끝이다.
공통 설정은 <날개셋> 한글 입력기 커널이 관리하며, 이걸 파일로 저장한 게 바로 imeconf.dat이다. 그러면 프로그램을 다음에 실행할 때도 예전의 입력 설정들이 고스란히 보존된다.

제어판을 '확인'으로 종료하면 메모리와 파일이 모두 업데이트된다. 그러나 '미저장 확인'이나 '적용'을 누르면 메모리만 업데이트된다. 이 두 버튼은 대화상자를 닫는지 여부의 차이만이 존재한다.

항목 미저장 확인 확인 취소 적용
설정을 메모리 default로 적용 O O X O
설정을 파일 default로 적용 X O X X
대화상자를 닫음 O O O X

(2) 위의 두 프로그램과는 달리 타자연습은 imeconf.dat를 읽거나 쓰지 않으며, 사용자 계정에 있는 자체적인 입력 설정을 사용한다. 그렇기 때문에 공통 설정이라는 개념을 사용하긴 하지만 메모리 수준에서만 사용하지 imeconf.dat를 다루는 파일 수준은 사용하지 않는다. '확인'과 '미저장 확인'의 구분이 존재하지도 않는다.

(3) 끝으로, 외부 모듈은 타자연습과는 또 정반대다. 외부 모듈은 타자연습 같은 독자적인 원천이 있는 건 아니며 필요하다면 imeconf.dat 파일을 읽고 쓰기도 한다. 하지만 그걸 메모리에 보관할 때는 공통 설정을 사용하는 게 아니라 스레드별로 자신만의 고유한 storage를 사용한다.

다시 말해 외부 모듈은 공통 설정 파일만 사용하지 메모리를 사용하지는 않는다. 이렇게 설계된 가장 큰 이유는 외부 모듈이 자신과 동일한 날개셋 커널을 사용하는 프로그램 밑에서 동작하더라도 EXE/DLL 모듈간에 입력 설정 충돌을 일으키지 않고 서로 입력 설정을 따로 관리할 수 있게 하기 위해서이다. 외부 모듈 혼자만 이렇게 남을 알아서 피해 가면 되며, 다른 프로그램들은 굳이 이렇게 동작해야 할 필요가 없다.

외부 모듈에서 연 제어판은 앞서 언급한 이유로 인해 '적용' 버튼은 사용할 수 없다. 하지만 '미저장 확인'은 있는데, 이걸 누르면 지금 맞춘 입력 설정이 외부 모듈을 사용하는 모든 프로그램들에서 한데 동기화되는 게 아니라 지금 실행 중인 프로그램/스레드에서만 잠깐 사용 가능하게 바뀐다. 한글 표현 방식 옵션도 레지스트리에 영구적으로 기록되는 게 아니라 지금 메모리에만 반영된다. 이런 것도 외부 모듈이 스레드별로 독립된 storage를 갖고 있기 때문에 구현 가능하다.

아, 하나 더.. '미저장 확인'은 제어판을 입력 도구모음줄을 통해서 열었을 때에만 나타난다. 운영체제의 제어판/설정이 제공하는 메뉴를 통해서 연 것은 문자 입력 문맥이 아니기 때문에 '미저장 확인'이 가능하지 않으며, '확인'을 눌러서 파일에 기록하는 영구적인 저장만이 제공된다.

3. 외부 모듈과 패드 공통

편집기와 타자연습은 자체 구현된 전용 에디트 컨트롤에다가만 글자를 생성한다. 하지만 외부 모듈과 입력 패드는 타 프로그램에다가 글자를 생성한다. 그렇기 때문에 이들 프로그램에서는 제어판의 시스템 계층에 '한글 표현 방식'이라는 탭이 존재하며, 빠른설정이나 낱자 결합에서 옛한글을 지정하면 "한글 표현 방식부터 맞춰야 옛한글이 실제로 나타납니다"라는 안내문이 나타난다.

또한 텍스트를 보낼 수만 있지 읽어 오고 고칠 수 없는 환경에서는 이런 동작과 관련된 기능과 옵션들이 몽땅 사용 불가 상태가 된다. 앞 글자로 달라붙는 bksp 옵션이나 단어 단위 한자 변환이 대표적인 예이다.
입력 패드는 이런 기능들이 무조건 사용 불가이고, 외부 모듈은 TSF A급 환경에 한해서 그런 기능들을 사용할 수 있다.

4. 외부 모듈 only

외부 모듈에서 제어판을 띄웠을 때는 유일하게 시스템 계층에 '외부 모듈 관리' 탭이 나타나지 않는다. 그 대신 '고급 시스템 옵션'을 갖고 있다.
또한, 구현체들 중 유일하게 외부 모듈만이 Alt가 섞인 단축글쇠를 전혀 인식할 수 없기 때문에 단축글쇠 대화상자에서 Alt를 나타내는 슬라이더가 사용 불가 상태로 바뀐다.

지금까지 논의한 사항들을 또 표로 깔끔하게 정리하면 다음과 같다. 아래의 4개 항목은 자료구조· 알고리즘의 차이가 아니라 그냥 UI 처리 차원에서의 차이일 뿐이다. 타자연습은 특이사항이 거의 존재하지 않아서 단순하고, 외부 모듈은 동작 환경이 변화무쌍하다 보니 조건부로 동작하는 게 굉장히 많음을 알 수 있다.

항목 편집기 외부 모듈 패드 타자연습
설정 data 저장소 공통 스레드별 따로 공통 계정별 따로
공통 설정(메모리) 조작 O X O O
공통 설정(imeconf.dat 파일) 조작 O O O X
'미저장 확인' 지원 O 입력 문맥일 때만 O X
modeless. '적용' 지원 O X O X
옛한글 설정 필요 안내문 X O O X
입력 기능 제약 처리(TSF A 미비) X IME/TSF B 한정 O X
Alt 단축키, 외부 모듈 관리 숨김 X O X X
2글자 이상 조합 제약 안내문 X TSF B 한정 X X

Posted by 사무엘

2016/12/30 19:35 2016/12/30 19:35
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1311

1. 블레이크 스톤 (Blake Stone)

먼 옛날 기억을 되살려 보니, 본인은 1990년대 중반에 울펜슈타인이나 둠 같은 id 사의 게임 말고 다른 계열의 3D FPS 게임을 친구 집 컴퓨터에서 본 적이 있었다. Doom처럼 좀 SF스러운 분위기이지만 Doom은 아니고 그것보다는 기술 수준이 뒤떨어졌다. 열쇠가 없는 상태로 잠긴 문을 열려고 하면 깜찍한 소리로 문이 열리지 않는다는 피드백이 왔다. (울프와 둠에는 청각 피드백이 없음)

그리고 제일 결정적인 단서로는.. 체력이 막대기나 숫자나 주인공의 얼굴 상태로 표시되는 게 아니라 검은 배경에 초록색 파형인 심전도 그래프로 나타났다. 완전히 죽어 버리면 물론 심장 박동이 없어진다.
이 모든 조건을 만족하는 게임을 검색해 보니.. Blake Stone이다.

사용자 삽입 이미지

기술적으로는 얘는 울펜슈타인 3D 엔진을 기반으로 개발되었다. 그렇기 때문에 높이를 표현할 수 없으며 레벨 배경은 여전히 건물 안으로 제한된다. 그러나 얘는 울프와는 달리 (1) 바닥과 천장에도 텍스처를 얹어서 그래픽을 고급화했으며, (2) 시점에서 먼 곳은 살짝 더 어둡게 표시되는 걸 구현했다. 게다가 얘도 미래가 배경이기 때문에 높이가 없다는 것만 빼면 전반적으로 둠과 분위기가 비슷해 보인다.

얘는 분명 나쁘지는 않은 게임이었으나, 스케일과 기술 수준 등에서 둠의 적수가 될 수는 없었다. 게다가 너무 늦게 나왔다. 출시일이 1993년 12월. 얘가 발매되고 나서 겨우 1주일 뒤에 Doom이 출시되는 바람에 블레이크 스톤은 존재감이 싹 묻혀 버렸다.

2. 퀘이크 1의 베타 3

본인은 중학교 말년에 어느 이웃집 형이 가져온 불법복제 백업CD를 통해서 퀘이크라고 둠의 다음 세대 게임을 처음으로 접했다. 그때는 지금 같은 고속 인터넷망이 없었으니 어둠의 경로에서 소프트웨어 불법복제를 책임지던 매체는 CD였다.
요즘은 아무 컴퓨터에나 당연하게 달려 있는 CD 쓰기/굽기 기능도 그때는 고가의 기계를 따로 돌려야 할 수 있는 첨단 기능이었다. 어디 그 뿐이랴? 요즘은 인터넷, USB 메모리, 외장 하드의 발달로 인해 광학 드라이브의 필요 자체가 극도로 줄어들어 있으니 격세지감이 아닐 수 없다.

그렇게 본인은 오랫동안 도스용 퀘이크 1을 즐겼다. 486 66MHz짜리 컴퓨터로는 퀘이크는 기본 최저 해상도인 320*200/240대에서나 제대로 프레임이 나왔지 640*480으로는 도저히 제대로 할 수 없었다. 그러고 보니 동일한 영어 이니셜을 갖고 "그 FPS(1인칭 슈팅) 게임은 이런 사양의 컴터에서 FPS(초당 프레임 수)가 얼마나 나오냐?" 이런 드립을 치는 게 가능하구나.;;

세월이 흘러 2000년대 중반이 되었고, 본인의 컴퓨터는 퀘이크를 처음 접하던 시절보다 당연히 성능이 월등히 더 향상된 걸로 바뀌었다. 본인은 옛날 생각에 Windows용으로 포팅된 퀘이크를 고전 게임 사이트에서 구해서 돌려 봤다.
그런데 이 퀘이크는 내가 옛날에 하던 퀘이크와는 미묘하게 다른 게 많았다. 정자체에 가깝던 화면 글꼴은 bloody한 분위기를 내려는 듯 좀 흘리고 날린 형태로 바뀌었다. 메뉴를 꺼내면 뒤의 게임 배경이 단순히 팔레트만 바뀌는 게 아니라 시꺼먼 하프톤 점이 쫙 깔렸다.

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

각 에피소드별로 모아야 하는 아이템의 이름을 본인은 10여 년 가까이 sigil이라고 알고 있었는데 이 게임은 rune이라고 표현했다.
그 이름도 유명한 무기 파워업 아이템은 quake power이라고 알고 있었는데 그게 이 퀘이크에서는 quad damage였다.
새로 구한 퀘이크는 에피소드 2의 마지막 레벨은(비밀 레벨 말고) 물웅덩이를 중심으로 사방으로 이동해서 다리를 내는 형태였는데 내가 기억하는 맵이 아니었다. 또한 에피소드 3의 마지막 레벨의 끝부분에서 Vore 두 놈은 높은 곳에서 튀어나왔으나, 이 게임은 다리 아래 용암 바닥에서 튀어나왔다.

차이점은 이 뿐만이 아니다. 에피소드 1의 보스인 Chthon의 생김새도 내가 기억하던 모양이 아니었다. 내가 하던 퀘이크는 얼굴도 몸통과 비슷한 색깔이고 눈이 있었던 반면, 이 퀘이크는 딱히 눈 같은 게 없고 얼굴 전체가 세로로 난 입의 이빨 같은 것으로 둘러져 있었다.

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

결정적으로 게임 전체의 최종 보스인 Shub-Niggurath와 싸우는 마지막 레벨은 완전히 다른 맵으로 바뀌어 있었다.
내가 기억하는 마지막 맵은 요렇게 뭔가 에피소드 4의 비밀 레벨과 비슷한 분위기였다. 나중에는 천장이 청록색인 거대한 필드에 도달하고, Shub-Niggurath가 있는 곳까지도 갈 수는 있지만 여기서 더 보스를 죽이거나 게임을 진행해서 엔딩을 볼 수는 없었다. 적을 다 죽인 뒤에 어떻게 해야 할지를 몰랐다.
(그나저나 퀘이크는 다 뭘 참고해서 몬스터들의 이름을 지었는지, 스펠링이 다 읽기 힘든 형태이다.)

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

이렇게 내가 경험했던 퀘이크에 차이가 존재했던 이유는 이미 제목에 쓰여 있다. 내가 중딩 시절 옛날에 했던 퀘이크는.. 바로 퀘이크 정식 버전이 발매되기 불과 2주 남짓 전에 유출된 '0.8 베타 3 버전'이었기 때문이다. 그게 어째 어떤 복돌이가 만든 백업 CD에 포함되었던 것이다. 난 10년이 넘게 베타 버전 퀘이크가 정식 퀘이크인 줄로 알고 있었다.
그래도 이런 것도 이제 검색해 보면 구체적인 출시 내역을 다~ 알 수 있고.. 옛날 기억도 어지간한 건 다 복원 가능한 세상이 됐으니 참 대단하다.

beta 3 구버전의 구동 및 플레이 동영상을 짤막하게나마 유튜브로 다시 보니 감회가 새롭다. 구버전은 마지막 레벨을 클리어 하는 엔딩이 원래 존재하지 않았던 게 맞았다. 미완성이었다.
그에 반해 정식 버전은 적절한 타이밍에 순간이동 장치로 들어가서 Shub-Niggurath의 몸 속으로 '텔레프래깅'을 하는 방식으로 적을 죽여서 엔딩을 볼 수 있다.

더 생각나는 걸 열거하자면, 몬스터 Ogre가 톱질 하는 소리가 구버전 것은 정식 버전의 것보다 피치가 좀 더 낮았다. Vore가 쏘는 탄환이 구버전은 그냥 용암 fireball과 동일했지만 정식 버전은 보라색 공으로 바뀌었다.

또한, 주인공이 뭔가에 깔려 죽었을 때 구버전은 be crushed라는 말을 썼지만 정식 버전은 be squished라고 말을 바꿨더라. 본인은 crush라는 단어를 둠과 퀘이크를 통해서 알게 됐다. Doom 2에서도 레벨 6이 The crusher이기도 하고 말이다. 끝으로, 구버전에서는 Ogre 이상 몬스터들은 로켓 같은 무기로 오버킬을 당해도 결코 육편 피떡으로(gibbed) 변하지는 않았던 걸로 기억한다.

id에서 1990년대 중반에 개발했던 Doom과 Quake들은 묘사가 잔혹할 뿐만 아니라 오각형, 염소 뿔 등 의도적으로 오컬트나 사탄 숭배교를 표방하는 듯한 비주얼이 많이 들어가 있었다.
그래서 자국 내에서도 이런 비판을 많이 받았는가 보다. 파일로 제공되는 도움말 문서를 보면, FAQ 중 하나로 "Are you guys Satan-worshipers?"가 있고, 이에 대한 답변은 No 한 마디로 간단히 일축해 놓았다.

그런데, 이것도 구버전의 도움말 문서는 간단히 No만 있는 게 아니라.. "아니요, 우리는 그냥 오각형과 666을 좋아할 뿐입니다."라는 부연 설명도 들어있었다. 중고딩 시절에 분명히 읽었던 기억이 있다.
id는 전통적으로 종료 확인 메시지도 그렇고 말을 전반적으로 익살스러운 농담조로 하는 걸 좋아하긴 한데, 이 질문에다가도 답변을 그런 식으로 하면 기독교 단체 같은 데에다가 더 큰 논란과 어그로를 일으킬 것 같으니 저 말을 삭제한 듯하다.

3. 기타

10대 중반의 나이로 접했던 FPS들은 나의 가치관 형성에 많은 영향을 줬다.

사용자 삽입 이미지

요렇게 거의 270도 턴을 해서 미로처럼 꼬불꼬불 들어가게 돼 있는 건물 화장실을 이용한 적이 있었다.
벽면 텍스처도 규칙적인 형태인 게 무슨 둠 맵 같다. 문은 좌우로 열리는 게 아니라 셔터처럼 위로 열릴 것 같다.
이런 모양의 맵을 설계하면 bsp 파일이 어떤 형태로 만들어질까 이런 게 머리에 어른거린다. -_-;;

정확하게 1인칭 시점인 건 아니지만 툼 레이더도 있다.
선유도 공원을 가 보면 거긴 잡초가 낀 야외 콘크리트 구조물이 영락없이 툼 레이더 1 맵 같다.
손에 쌍권총 쥐고 옆으로 점프라도 하고 싶은데.. 그랬다가는 다치겠지.

말이 나왔으니 말인데, 내 생각에 적당히 스토리만 잘 짜 놓으면 우리나라 DMZ를 배경으로 툼 레이더 커스텀 레벨 얼마든지 만들 수 있다고 본다. GP에 들어가서 아이템 먹고 북한군을 때려잡는 식으로 진행될 것이다.
Demilitarized Zone 이름도 얼마나 근사하냐? ㅋㅋ

Posted by 사무엘

2016/12/28 08:35 2016/12/28 08:35
,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1310

다음 버전 개발 근황 2

이번 8.8은 이례적으로 다음 버전 개발 근황을 두 차례나 올리게 됐다. 분량을 나눠야 할만큼 작업이 활발하게 진행 중이라는 뜻이다. 번호를 9부터 시작해야 하나 싶은데.. 첫 근황을 올린 지 시간이 몇 주 정도 지났으니 다시 1로 리셋하도록 하겠다.
난 정말 날개셋 한글 입력기 코딩을 할 때가 제일 즐겁고 행복하다.

1. 사용자 정의 조합 편집 관련 기능 개선

10여 년 전 3.9 버전에서 '고급 입력기'가 첫 도입되었다. 원래 있던 '기본 입력기'의 지원 범위는 한글 한 글자의 조합인데(지금은 방점을 임시로 같이 묶어서 표현하는 것까지만 추가로 더 포함), 고급 입력기는 그 한계를 넘어서 한글이 아닌 글자의 조합도 표현하거나, 한글 하나를 여러 글자(한글 또는 비한글 모두)로 풀어서 표현하는 것을 지원한다. 전자를 위해서 '사용자 정의 조합'이 도입되었고, 후자를 위해서 '한글 출력 치환' 기능이 추가되었다. 이로써 조합 상태와 관련된 추상화 계층이 그럭저럭 완성됐다.

이번 버전에서는 그 중 '사용자 정의 조합' 쪽에 편의 기능이 추가되었다. 먼저, 제어판에서 사용자 정의 조합만 단독으로 파일로 저장하고 불러올 수 있게 했다. 바이너리 형태로 저장하는 것은 별 의미가 없다고 여겨져서 XML 형태만 지원한다.

그러니 이제 글쇠배열은 영문 쿼티로 그대로 두고, 알파벳을 기반으로 여러 방식으로 특수문자들을 입력하고 싶을 때 사용자 정의 조합 로직만 교체해서 문자 입력을 할 수 있게 됐다. 이 달 초에 소개했던 새 기능 중, 글쇠배열 이름뿐만 아니라 입력 스키마의 자체적인 이름을 부여하는 기능도 이런 맥락에서 도입된 것이다.

단, 비슷한 기능을 하는 한글 출력 치환은 여전히 단독 저장을 지원하지 않기 때문에 이건 형평성 문제가 될 수 있다. 그럼에도 불구하고 사용자 정의 조합에만 단독 저장과 불러오기 기능이 추가된 이유는.. 불러오기와 관련하여 큰 개선 사항이 또 생겼기 때문이다.

지금까지 사용자 정의 조합 로직을 설계하기 위해서는 w를 눌렀을 때 분기할 상태, 그 뒤에 a를 눌렀을 때 분기할 상태 등등을 숫자를 생각하면서 사용자가 일일이 설계해야 했다. 이런 불편을 덜기 위해 이번 버전에서는 이 프로그램이 내부적으로 사용하는 자료 구조를 그대로 노출하는 XML 스키마 말고, 사용자 친화적인 일명 '쉬운 문법' 스키마도 추가했다.

"wa = わ" 이런 쌍만 쭉 지정해 주면 w와 wa를 입력했을 때의 중간 과정과 상태 번호를 자동으로 매겨 준다는 것이다. 입력 파일 포맷을 구체적으로 어떤 형식으로 짜면 되는지는 물론 예제에다 소개해 놨다. 쉽게 말해 아래 그림에서 왼쪽처럼 만들어 놓으면 자동으로 오른쪽으로 만들어 준다는 뜻이다.

사용자 삽입 이미지

사실, 현재 예제로 제공되는 일본 문자 입력 로직은 히라가나/가타카나 테이블로부터 로직을 자동으로 짜 주는 툴을 내부적으로 돌려서 생성한 것이다. 사람의 손으로 60개가 넘는 state들을 알파벳 순으로 노가다로 생성한 건 물론 아니니까. 그 툴을 <날개셋> 한글 입력기 내부에 정식으로 포함시킨 거라고 생각하면 된다.

물론, 쉬운 문법은 내 프로그램이 인식하고 변환하는 걸 지원한다는 뜻이지, 프로그램이 내부적으로 사용하는 자료구조는 아니다. 그렇기 때문에 불러오기만 지원되지 저장은 안 된다. 쉬운 문법 원본은 사람이 따로 관리하고 갖고 있어야 한다.

또한 제어판 대화상자에서도 어떤 상태에 대해서 'called from 링크'를 찾아서 보여주는 기능을 추가했다. 30이라는 상태가 있으면 그 30이라는 상태로 분기하는 예전 상태.. 가령, 25번 상태에서 a를 누르면 30으로 간다는 걸 보여준다. 현재는 선택막대를 더블 클릭하면 앞으로 나아가는 calls to 링크만 지원되고, 뒤로 돌아갈 수는 없어서 불편했는데 이것 역시 개선됐다.

2. 단어 단위로 한자 변환

단어 단위로 한자 변환을 할 때 지금처럼 '뒤에서 앞으로 탐색'뿐만 아니라 '앞에서 뒤로 탐색'도 가능하게 옵션을 추가했다(제어판 - 편집기 계층). 예를 들어, '토선생'이라는 신조어에 대해서 한자 변환을 하면 '선생'이 아니라 '토선'이 블록이 잡힌 상태가 되며, '토선'을 변환한 뒤에 cursor는 다시 '생' 뒤로 되돌아온다.

지금까지는 조합 중인 글자나 cursor 바로 앞에 있는 글자가 반드시 단어의 마지막 글자에 포함돼 있어야 했지만 이제 그런 제약이 없어진 것이다. MS IME 내지 아래아한글이 동작하는 방식을 내 프로그램도 드디어 제대로 지원하게 됐다.

사용자 삽입 이미지

단어 단위로 한자를 변환하는 기능은 2012년에 나온 6.7 버전에서 '시범적으로' 추가되었다. 어차피 TSF A급 프로그램에서나 제공 가능한 기능이고(이건 MS IME도 마찬가지임), 그저 없는 것보다는 낫다는 차원에서 추가된 액세서리 기능일 뿐이기 때문에 동작 방식이 기존의 한 글자씩 변환하는 기능의 연장선 수준을 벗어나지 못했다. 저렇게 cursor가 통째로 앞으로 갔다가 변환 후에 되돌아오는 것은 <날개셋> 한글 입력기의 기존 프로토콜로 표현할 수가 없었다.

그러다가 그 내부적인 한계는 수 년 뒤, 작년에 나온 8.2에서 프로토콜을 확장함으로써 극복되었다. 입력 패드 구현체에다가도 한자 변환 기능을 구현하면서 작업을 싹 다시 했기 때문이다. 이때 테스트 차원에서 한자어가 굳이 cursor 바로 옆에 있지 않은 변환도 구현 가능한 것까지 확인했다.

하지만 이 코드는 말 그대로 테스트 코드일 뿐, 기존 한자 변환 코드와 융합 가능한 형태가 아니었기 때문에 조건부 컴파일로 기존 코드와 테스트 코드 둘 중 하나만 적용 가능했다. 게다가 테스트 코드는 중간에 ESC를 눌러서 한자 변환을 취소했을 때에도 cursor만 곱게 이동하는 게 아니라 변환 대상 텍스트를 불필요하게 덮어쓰는 등 한계도 있었다.

그러다가 8.8 버전에 와서야 오랜 숙제로 남아 있던 한자 후보 변환 관련 코드를 완전히 다시 작성했다. 한글 조합 상태일 때와 그렇지 않을 때를 불문하고 동일한 로직이 동작하게 했으며, 한 글자 한 글자 답답하게 앞으로 탐색하던 것을, 쿨하게 처음부터 맨 앞 글자들을 최대 30자 가까이 가져온 뒤 후처리를 하는 걸로 과정을 단순화했다. 그러면서 기존의 뒤-앞 탐색 부분에다가 옵션으로 seamless하게 병행하는 차원에서 앞-뒤 검색도 구현해 넣었다.
'초성 지향 도깨비불'(예: '간'을 '가ㄴ'으로)이나 호환용 낱자 변환으로 인해 당장 화면에 보이는 글자와 후보 변환 때 사용되는 글자가 서로 같지 않을 때의 보정 처리도 이 기회에 별도의 추상화 계층으로 싹 분리했다.

앞-뒤로 탐색했을 때 단어 단위 한글-한자 변환이 가능하지 않으면 그냥 지금 조합 중이거나 cursor 앞에 있는 한자 한 글자에 대한 한자 변환이 시도된다. 한글을 조합하던 중에 앞-뒤로 탐색하면 뒤-앞 방식으로 동작할 때와는 달리 지금 조합이 종료된다. 왜냐하면 cursor가 잠시 앞으로 이동해 버릴 수도 있으니 지금 조합 상태가 더 유지될 수 없기 때문이다. 그래도 한자로 변환될 예정인 부분이 블록으로 잠시 highlight가 되기 때문에(외부 모듈) 이 방식이 시각 피드백은 더 우수하다.

이런 뜻깊은 기능의 구현과 리팩터링 작업이 왜 이제 와서야 행해졌는지 모르겠다. 작업에 개인적으로 큰 보람을 느낀다.
그리고 이 작업을 하는 과정에서 덤으로 외부 모듈의 버그도 하나 발견해서 수정했다. TSF A급 프로그램에서 한글을 조합하지 않은 상태에서 한글 뒤에서 도구모음줄의 한자 버튼을 눌렀을 때.. 편집기와 같은 저런 한자 변환이 케바케로 되지 않는 문제가 있었다. 적어도 8.x 버전 내내 존재했으리라 여겨지는 문제이다.

3. 비주얼 관련

깨알같은 사소한 변화이긴 하다만, 지난 8.6에서 외부 모듈에서 추가되었던 '키보드 드라이버 변경' UI에다가 방패 아이콘을 추가했다. 이건 변경을 위해 관리자 권한이 필요하다는 걸 강조하기 위해서이다.
버튼에다가는 옵션 하나만 추가하면 문구 옆에다 방패 아이콘을 간단히 넣을 수 있지만, 콤보박스나 static 컨트롤에다가 방패 아이콘을 넣는 기능은 없는 것 같다. picture 컨트롤을 하나 더 집어넣어야 했다.

사용자 삽입 이미지

Windows는 IDI_ERROR, IDI_QUESTION 같은 몇 가지 기성 아이콘을 제공하기 때문에 응용 프로그램에서는 LoadIcon이나 LoadImage 함수를 통해 이것들을 가져올 수 있다. 메시지 박스에다가는 MB_ICON* 형태의 플래그를 지정해서 집어넣을 수도 있다.
그리고 Vista부터는 사용자 계정 컨트롤에 대한 시각 피드백을 제공하라는 취지로 방패 아이콘도 IDI_SHIELD라는 명칭으로 제공한다. 이들은 모두 내부적으로는 user32.dll의 리소스 형태로 존재한다.

IDI_SHIELD는 그 성격상 32*32 이상의 크기보다는 글자와 어울리는 16*16, 20*20 같은 아담한 형태로 훨씬 더 많이 쓰인다. 그런데 문제는 얘는 LoadIcon이나 LoadImage 같은 재래식 함수로는 제대로 그릴 수 없다는 것이다.

옛날부터 있었던 IDI_ERROR 같은 메시지박스 아이콘들은 최대 크기가 끽해야 48*48이 고작인 반면, 21세기에 추가된 IDI_SHIELD는 무려 256*256 크기까지 존재한다. 구조가 특이해서 그런지 LoadIcon은 물론이고 LoadImage로 크기를 지정해 줘도 작은 크기가 로딩되지 않고 제일 큰 놈으로만 그려진다. 큰 그림을 16*16, 20*20으로 강제로 줄이면 보기가 아주 흉측해진다.

그래서 방패 아이콘은 내 경험상 Vista에서부터 추가된 LoadIconMetric이라는 특수한 함수를 써야만 작은 크기로 보기 좋은 HICON을 가져와서 그릴 수 있다. 이 함수는 user32도 아니고 생뚱맞게 comctl32.dll에 있다. 세월이 흐르면서 API 설계의 일관성이 좀 깨지고 뒤죽박죽이 된 것 같다.

게다가 리소스에 있는 아이콘을 있는 그대로 read-only 공유 형태로 가져오기만 했다면 대개는 DestroyIcon을 할 필요가 없는데 저 함수의 리턴값은 언제나 소멸 처리도 해야 해서 더 불편하다. 뭐, Windows 프로그래밍에는 아이콘 나부랭이 하나 찍는 일에도 이런 복잡한 사정이 있더라.

그리고.. 진짜 사소한 거다만, 도움말에 있는 프로그램 스크린샷들을 드디어 Windows 10 기준으로 다 교체했다.

4. 옛한글 입력 설정이 되지 않았을 때의 알림 메시지

이제 더 고칠 게 없을 것 같은 입력 패드에도 improvement가 있었다.
트레이에 있는 입력 패드 아이콘을 마우스로 가리키고 있으면 현재 선택돼 있는 글자판의 이름이 툴팁으로 뜨는데, 이게 제어판의 실행으로 인해 글자판의 이름이 변경된 것은 같이 업데이트 되지 않는 걸 뒤늦게 발견하여 수정했다.

그리고 외부 모듈과 입력 패드는 옛한글을 입력하기 위해서는 제어판 시스템 계층의 '한글 표현 옵션'부터 먼저 설정해 줘야 한다는 공통점이 있다. 비트맵 자체 글꼴 전용 환경인 편집기와는 달리, 밖에서는 사용자가 옛한글을 지원하지 않는 글꼴을 사용하고 있을 수 있으며, 또 옛한글을 표현하는 방식도 역사적인 이유로 인해 여러가지가 존재하기 때문이다.

하지만 이런 사정을 모르는 사용자의 입장에서는 분명히 옛한글 입력으로 설정을 했는데 옛한글이 찍히지 않는다면 프로그램의 이상이라고 생각할 수 있다. 물론 제어판도 편집기가 아닌 타 환경에서 동작할 때는, 사용자가 빠른설정이나 낱자 결합 규칙을 옛한글과 관련된 것으로 변경할 때 "옛한글을 사용하려면 한글 표현 방식도 맞춰 주세요"라고 짤막한 메시지를 출력하긴 하지만 이것만으로는 좀 부족한 감이 있다.

입력 패드는 키보드 포커스를 건드리지 않고 시스템 트레이에다 말풍선 형태로 메시지를 찍는 아주 편리한 UI를 갖추고 있으니 이런 기능을 추가하는 게 아주 용이하다.

사용자 삽입 이미지

(layered window든 동영상이든 화면에 보이는 건 무엇이건 print screen으로 간단히 캡처하는 시대가 된 지 오래인데, 시스템 트레이의 말풍선 윈도우는 의외로 고전 테마에서는 print screen 캡처가 안 되더라..! DWM이 동작하는 AERO 테마를 띄워야 캡처가 됐음..)

참고로 지금까지 입력 패드는 입력과 관련된 기술적인 에러 메시지를 찍는 기능만 있었다. 권한 차이로 인해서 입력 패드가 메시지를 보낼 수 없는 윈도우일 때, 그리고 해당 윈도우가 IME 컨텍스트가 존재하지 않아서 문자 입력이 가능하지 않을 때 말이다.

다 외부 모듈/IME 말고 입력 패드에서만 존재하는 상황을 가리킨다. 외부 모듈은 애시당초 로그인 화면에서도 동작할 정도로 권한 걱정 따위는 할 필요가 없으며, IME 컨텍스트가 존재하지 않는 윈도우에서는 처음부터 버튼들이 다 흐리게 바뀌고 입력이 차단되기 때문에 사후에야 이런 상황을 알아채고 에러 메시지 처리를 할 일도 없기 때문이다.

사용자 삽입 이미지

다만, 옛한글 설정을 해 달라는 메시지는 입력 패드와 외부 모듈이 공통으로 출력할 명분이 있는 것들이다.
외부 모듈은 어떤 경우에도 시스템 트레이를 건드리지 않고 동작하기 때문에 다소 투박하긴 해도 그냥 메시지 박스를 이용한다.

사용자 삽입 이미지

외부 모듈은 TSF B급 프로그램에서 어쩔 수 없는 기술 한계로 인해 조합이 튕긴 것을 감지해서 메시지 박스를 찍어 주는 게 있었다(처음에 2글자 이상 길이의 조합을 만들 수 없음). 히스토리 문서를 보니 저건 그리 오래 되지도 않은 8.5 버전에서 추가된 기능이다. 이건 IME가 아닌 TSF 프로토콜을 취급하는 외부 모듈에서만 유일하게 발생하는 현상이다. 그런데 이제 이런 식으로 메시지를 찍어 주는 상황이 하나 더 추가된 것이다.

사용자 삽입 이미지

이런 식으로 예전에 만들어 뒀던 체계를 확장하여 유용한 UI가 간단하게 추가되는 건 참 뜻깊은 일이다.

Posted by 사무엘

2016/12/25 19:35 2016/12/25 19:35
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1309

시험 부정행위

우리나라에서 사람 인생에 큰 영향을 주며 이 때문에 부정행위도 많은 대표적인 전국구 시험은 수능과 토익이 아닐까 한다. 수능은 10여 년 전인 2004년 11월에 치러진 2005년도 시험에서 200여 명에 달하는 수험생이 조직적으로 컨닝을 한 게 뒤늦게 적발돼서 전국적으로 난리가 나기도 했다. 비슷한 시기에 벌어졌던 밀양 집단 성폭행 사건과 더불어 국가적인 흑역사가 아닐 수 없다.

수능 부정 적발은 뒤끝도 굉장히 오래 간 걸로 기억한다. 이미 대학에 갓 입학한 학생이 수능 무효로 인해 입학이 취소된 건 차라리 양반이다. 더 옛날에 그냥 넘어갔던 것까지 조사를 해 보니, 심지어 대학을 졸업하고 그 경력으로 학사장교로 들어가기까지 했는데 뒤늦게 수능 무효 → 대학 입학과 졸업 무효 → 학사장교도 무효로 테크트리가 모조리 아작난 사례도 있다. 이건 학력위조 적발로 인한 임관 무효 말고, 또 별개로 있었던 사례다.

저 부정행위의 여파로 인해 수능 시험엔 일체의 전자기기가 한 치의 자비심 없는 절대금기가 되었다. 특히 시험장에서 휴대전화란 마치 공항 세관에서 마약, 군대 훈련소에서 담배와도 같은 악의 축 취급을 받게 됐다. 굳이 벨소리가 나지 않아도, 끄고 배터리까지 분리해 놨더라도, 1교시 시작 전에 전화기를 제출하라고 곱게 말로 할 때 제출하지 않은 게 적발되면 무조건 각서 쓰고 퇴장이다. 묻지도 따지지도 않고, 부정행위 정황이 있건 없건 이번 수능은 무효가 되니 내년에 다시 쳐야 된다.

사실 이마저도 죄질이나 처벌 수위가 가장 약한 '규정 위반'에 속하기 때문에 이번 수능만 무효 처리되고 끝나는 거다. 하물며 대놓고 부정행위를 한 게 적발된다면 올해뿐만 아니라 1년간 추가로 수능 응시 자격 상실이 뒤따른다.
그런데 매년 전국에서 수~열몇 명 정도는 바보같이 휴대전화 규정이 걸려서 퇴장 당하는 안습한 사람이 꼭 나온다고 한다. 자기가 아니라 부모님이 넣어 놓은 휴대전화가 뒤늦게 발견된다거나..;;

시험 부정 행위는 크게 다음과 같은 네 카테고리로 나뉜다.

  • 혼자: 시력, 컨닝페이퍼
  • 다른 수험생과 짜고: 답안지 보여주기, 특정 동작으로 신호
  • 다른 외부인과 짜고: 무선 통신, 대리 시험
  • 시험 관계자와 짜고, 혹은 시험장 "밖에서" 혼자: 아예 문제와 답안 유출, 답안지 바꿔치기

'혼자' 테크닉을 봉쇄하기 위해 같은 문제지도 A형과 B형으로 막 나뉘어 배부된다. 같은 문제를 풀더라도 수험생이 마킹하는 답안이 제각각이 되게 말이다. 컴퓨터 프로그래밍으로 치면 소스 코드의 난독화 테크닉과 비슷하다.
그리고 컨닝페이퍼를 책상 주변에다 미리 만들어 놓으려는 시도를 분쇄하려고 학생들의 고사장 좌석도 랜덤화한다. 마치 Windows Vista에서 도입된 실행 주소 랜덤화 기법이 떠오른다.
옛날에 초딩 시절에도 도학력고사 같은 큰 시험은 아예 반을 싹 바꿔서 쳤던 걸로 기억한다.

수험생간에 필기구가 가리키는 방향, 기침, 시선 등으로 신호를 주고받는 건.. 물증이 없는데 감독이 어지간히 눈썰미가 있지 않으면 혐의를 어떻게 입증해서 어떻게 잡아 내는지 궁금하다. 주고받는 학생들도 언제 걸릴지 모르는데 완전 조마조마한 상태에서 어떻게 그 짓을 할지? 멘탈이 어지간히 강하지 않고서는 할 수 없을 것이다.

그래서 요즘 시험에서 부정행위를 저지르는 사람들은 위의 1~2단계는 제끼고 최소한 외부인과 공모하는 3단계부터 시작한다. 초소형 카메라와 도청 장치를 만드는 기술이 발달한 덕분이다. 덕분에 시험장에 출입할 때는(특히 중간에 화장실에 갔다 올 때) 애들을 공항에서 검문하듯이 X선 금속 탐지기라도 통과시켜야 할 판이다.
하긴, 굳이 시험이 아니어도 노름판 같은 데서도 이런 방식으로 사기 치는 놈들이 많긴 하다. 몰래 카메라로 상대방의 패를 다 본다거나, 밖에 있는 고수에게 바둑판을 보여주고 훈수를 듣는다거나.

차량은 시험장과 적당히 떨어져 있는 외부에서 통신을 주고받는 부정행위 공범에게 훌륭한 엄폐성과 주거성을 제공하는 도구이다. 그렇기 때문에 저 부정행위 사건 이후로는 시험 시간 동안엔 시험장 주변의 반경 200m 이내에는 차량의 주정차도 전면 금지되었다. 이건 단순히 소음 방지가 아니라 이런 보안을 고려한 측면이 있다. 소음이 문제라면 차량 통과 자체를 금지시켜야지?

아예 시험지나 답안지를 사전에 빼돌리는 건 방송으로 치면 어지간한 방송 사고를 넘어 전파 납치 같은 급의 엄청난 범죄가 된다. 굳이 관계자를 매수하는 것뿐만 아니라 혼자 건물에 침입하는(!!) 짓도 여기에 해당되는데, 우리나라는 최근에 이런 사건도 두 번이나 발생한 바 있다.

대표적으로 전국민을 충격에 빠뜨렸던 사건은 2013년 말에 발생했던 연세대 로스쿨 캐비닛 사건 되겠다. 문제의 그 학생은 변호사 시험도 아니고, 학교 내부 시험 문제를 빼돌리려고 교수 연구실에 몰래 침입해서 컴퓨터에다 해킹 프로그램을 설치하려 했다. 연구실 도어락의 비번은 그 교수가 문을 열고 있을 때 옆에서 몇 차례 몰래 훔쳐봐서 '시력'으로 익혔다고 한다.

그런데 으슥한 밤에 웬 학생이 혼자 교수 연구실에 들어가는 걸 동일 로스쿨의 다른 학생이 우연히 보고 수상하게 여겨서 경비 직원에게 신고했고, 경비원이 출동했다. 경비원들이 오는 소리가 들리자 그 대담한 학생은 근처의 캐비닛 안에 황급히 들어가 숨었지만... 결국은 덜미가 잡혔다. 들켰을 때의 심정이 어땠을까? "당신은 누구요?" 개쪽에 개망신에;;;

걔는 태어나서 지금까지 어디서든 1등이란 걸 놓쳐 본 적이 없었댄다. 물론 이전에 치른 시험들도 부정행위의 도움으로 1등을 한 게 있었겠지만 모든 성적이 송두리째 조작은 아닐 것이고, 그 친구도 기본적인 머리와 실력이 있으니 컨닝 없이도 올백· 올1등까지는 아니어도 최소한 상위권은 유지 가능했을 것이다.

학부는 당연히 서울대 졸업. 허나 로스쿨은 서울대를 못 가고 겨우 연세대에 그친(?) 것에 무척 애석해하면서 재수를 했지만 뜻을 이루지 못했다. 로스쿨은 어딜 가든 그야말로 전국에서 날고 기는 공부 기계 암기 괴물들의 집단이 아니던가? 법학 전공도 아닌 그에게는 서울대가 아니라 연세대 로스쿨도 감지덕지이고 엄청난 학업량을 따라가기가 버거운 곳이었다.

결국 그 친구는 수단과 방법을 가리지 않고 1등 강박관념을 유지하기 위해, 어찌 보면 지금까지 늘 해 오던 대로 저런 짓까지 감행하게 됐고 그게 이번에는 통하지 않았다. 그는 당연히 징계 제적을 당해서 짤렸으며 법조계 쪽으로는 영원히 발을 들일 수 없게 됐다. 몇 년 전에 고려대 의대생 성추행 사건 가해자가 출교 당하고 의료계에서 매장 당한 것과 같은 처지가 됐다.

이 소식을 접한 동기생들은 그를 그냥 '캐비닛'이라고 부르면서 혀를 차고 허탈해했다. "그럼 그렇지 어떻게 저렇게 시험만 쳤다 하면 올A+을 제조하는 천재일 수가 있나 싶었는데 역시 제 실력이 아니었구나. ㄲㄲㄲㄲ"
그는 최종적으로 징역 1년에 집행유예 2년이 선고됐다. 동기생 출신인 본인의 모 지인에게 듣기로는, 걔는 IT 쪽으로 뭔 사업을 시작했다고 한다. 헐.. 뭘 해도 근성과 끈기는 있으니 나쁜 짓만 안 하면 성공은 하겠다..;;

캐비닛 사건이 가라앉고 얼마 뒤엔(2016년 3월) 웬 공무원 지망생이 정부 서울 청사 인사혁신처에 몰래 침입해서 자기 점수를 고치고 합격자 명단 문서 파일에 자신을 올려 놓다가 결국은 잡혔다. 이 사람은 공부는 그 연세대 캐비닛만치 잘한 것 같지 않지만, 잔머리와 대담성은 어쩌면 캐비닛을 능가한다고도 감히 말할 수 있다. 도대체, 무슨 약 빨고 어떻게 그런 생각을 해서 그 정도로 실행했는지 그 엽기성과 대담함에 경의를 표하지 않을 수 없다..;;

정부 서울 청사는 나름 청와대와 같은 급의 보안 시설인데 내부 헬스장에서 직원의 출입증을 훔치고, 경비가 허술한 시간대에 여러 사람들 사이에 껴서 뒷문으로 들어가는 식으로 감시를 피했다. 게다가 캐비닛의 경우 강의동 건물 자체는 출입이 가능하니 훔쳐보는 걸로 방 비번을 알 수 있었지만, 저 공시생은 처음 들어가 보는 정부 청사 안에서 하필 청소부가 편의를 위해 버젓이 적어 놓은 비번을 이용해 방에 침입해 들어갔다고 한다. (직원이 출근하기 전에 미리 방에 들어가서 청소를 끝내 놔야 하므로)

게다가 이 사람도 과거 이력은 더 화려했다는 게 밝혀졌다. 꾀병으로 약시 진단서를 받고 기간을 위조까지 해서 각종 시험에서 응시 시간을 1.5배 더 받았다. 다음으로, 매 시험의 1배수 시간이 끝날 때마다 수능 문제의 정답이 인터넷에 공개된다는 점을 이용하여, 그때 화장실에 가서 사전에 잘 숨겨 놓은 휴대전화로 인터넷에 접속 후 답을 알아 와서 마킹을 했다. 이런 허점을 찾아냈다는 것 자체가 보통 잔머리가 아닌 거 같다~!
또한 대학 진학 후에 7급 공무원 시험의 지역 예선급 시험을 학원에서 칠 때는 대놓고 문제 유출까지 해서 압도적인 1등을 차지하기도 했다.

이러니 여죄를 수사한 경찰들부터가 경악하고 "얘는 국정원 같은 데에 특채 좀 시켜야겠다 ㄲㄲㄲㄲ" 이런 반응을 보였을 정도였다. 거기는 그야말로 위장과 조작이 직업인 곳이니, 혹시 사법 거래라도 하면 난폭운전 폭주족을 F1 서킷으로라도 보내는 인재 활용을 할 수 있을지는 모르겠다. -_-;;

그야말로 영화 Catch me if you can을 떠올리게 하는 행적이 아닐 수 없다. 그 영화의 배경에서는 그래도 사회 시스템이 전산화되기 전이었으니 그 짓이 가능했을 뿐이다. 지금은 서버 DB를 직접 해킹하지 않는 한, 어설프게 엑셀/워드 문서 몇 개에다 점수 조작하고 자기 이름 넣어 봤자, 없던 공무원 자리가 하나 더 뿅 생겨서 자기 신분이 성공적으로 조작될 리는 만무하다.

컨닝을 소재로 기가 막힌 첩보물 학원물 짬뽕을 만들 수 있을지는 모르겠다. 웹툰으로는 <빵점동맹>을 참 재미있게 봤는데, 재미는 있지만 그래도 현실과는 많이 동떨어진 허구라는 점을 감안해야겠다. 백 희지는 주토피아에서 토끼 주디 같고 남캐인 임 수영은 여우 닉 같은 느낌이 든다.

그렇게 정성껏 컨닝을 할 시간과 머리가 있으면 공부나 빡세게 하라고 다들 말한다. 하지만 차근차근 공부하는 데 필요한 지적 능력과, 정교하게 컨닝하는 데 동원되는 지적 능력이 같지는 않은 관계로.. 사람을 변별하는 중요 시험엔 지금 이 순간에도 누군가가 부정행위를 자행하고 있을 가능성이 높다. 이 각박한 경쟁 사회에서 앞으로 또 무슨 엽기적인 부정행위자가 적발되어 뉴스 사회면을 장식할지 알 수 없는 노릇이다.

* 여담

1. 이 글에서 자세히 언급하지는 않았지만 토익도 나라 망신을 시킬 정도의 대규모 부정행위가 몇 번 저질러지고 적발된 적이 있다. 그래서 이 때문에 다수의 선량한 토익 응시자들이 싸잡아 큰 불편을 겪어야 했다. 불법복제 때문에 정품 사용자가 도리어 피해를 입은 것처럼 말이다. (올라가는 구입 가격, 제품 인증 관련 번거로움)

2. 그러고 보니 정부 서울 청사는 더 전에 2012년에도 어떤 이상한 사람이 무단으로 뚫고 들어가서 투신 자살까지 했었다. 어째 그리 보안이 허술한지 그것도 질타 사항이다.

3. 예전에 '짜장면'이 표준어로 인정받게 됐을 때 '컨닝'도 같이 좀 등재됐으면 하는 아쉬움이 있다. 자장면만큼이나 커닝도 완전 현실성이 없지 않은가?

Posted by 사무엘

2016/12/20 08:29 2016/12/20 08:29
, , ,
Response
No Trackback , 3 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1307

오늘날 Windows에서 실행되는 모든 프로그램들.. exe, dll 따위는 잘 알다시피 portable executable이라는 형식으로 만들어져 있다. 하지만 이 파일 포맷도.. 처음 만들어지던 당시에 여전히 컴퓨터에서 현역이던 도스와 최소한의 호환성을 유지할 필요가 있었기 때문에, 맨 앞에 MZ로 시작하는 16비트 도스 헤더를 여전히 갖추고 있다.

호환성이란 게 딴 게 아니고, 도스에서 Windows용 프로그램이 실행됐을 때 컴퓨터가 다운되는 게 아니라 "이 프로그램은 도스용이 아닙니다" 같은 짤막한 에러 메시지라도 뜨게 하는 것 말이다.

옛날에 Win32s가 제대로 설치되지 않은 상태에서 32비트 프로그램을 Windows 3.1에서 실행했더니.. "상위 버전에서 실행해 주십시오 / Win32s를 다시 설치해 주십시오" 이런 말이 메시지 박스 형태로 뜨는 게 아니라 황당하게 This program cannot be run in DOS mode라고.. 지금 시스템이 아예 Windows가 아닌 듯한 자비심 없는 메시지가 도스창에 떴다. 20여 년 전에 그 인상이 무척 강렬했었다. 요즘은 32비트 OS에서 64비트 exe의 실행을 시도해도 에러 메시지가 그 정도로 막나가는 형태는 아니다.

Windows용 프로그램들은 빌드할 때 그렇게 도스에서 잘못 실행됐을 때를 대비해 짤막하게 대신 실행해 줄 도스용 일명 "stub" 프로그램을 링크 옵션으로 지정할 수 있다. 이름하여 /STUB. 이걸 지정하지 않으면 아까 같은 저런 짤막한 에러 메시지 한 줄만 찍는 기본 stub 프로그램이 들어간다.
16비트 시절에 Visual C++ 1.5x를 보면 그 예제 stub 프로그램 자체가 winstub.exe라고 있었다. 하지만 그 이후부터는 디폴트 stub 프로그램은 그냥 링커 내부에 내장되어 버렸는지 그런 게 따로 있지는 않다.

프로그램을 특수하게 빌드하면 그런 stub을 아예 전혀 집어넣지 않는 것도 가능하다. 맨 앞에 MZ, 그리고 0x3C 오프셋에 PE 헤더가 있는 지점만 들어있으면 되고 나머지 칸은 몽땅 0으로 채움. 심지어 PE 헤더가 0x3C 오프셋보다도 전에, 도스 EXE 헤더가 있어야 할 지점에서 바로 시작하는 것도 가능하다.

미래에 마소에서 빌드하는 EXE/DLL들은 번거로운 This program cannot be ... 메시지를 떼어내고 이렇게 만들어져 나올지도 모른다. 물론 이런 프로그램은 Windows 환경에서 실행하는 건 문제 없지만 만에 하나 어느 레트로 변태 덕후가 그걸 굳이 도스에서 실행해 보면 컴퓨터가 어찌 되는지 책임 못 지는 상태가 될 것이다.

반대로 기본 stub 대신에 꽤 규모 있는 16비트 프로그램을 집어넣어서 동일 EXE가 도스에서도 그럭저럭 기능을 하고 Windows에서도 GUI를 띄우며 제대로 실행되는 프로그램을 만든 경우가 있다. Windows 9x 시절엔 레지스트리 편집기가 그러했다. 이건 Windows에서 보기 드문 하이브리드 universal binary 형태의 프로그램인 것 같다.
16비트 프로그램이 자기 자신 EXE를 열어서 PE 헤더를 파싱해서 리소스 같은 걸 읽어들이는 코드가 같이 빌드되었다면.. 도스 파트가 나중에 합쳐진 Windows 파트와 더불어 한 리소스를 공유하는 형태로 실행될 테니 이 역시 무척 흥미로울 것이다.

이 시점에서 문득 궁금해졌다.
링커가 얹어 주는 기본 stub 프로그램은 명령어가 겨우 몇 바이트밖에 되지 않는다. 얘들은 무슨 의미를 갖고 있는지, 혹시 옛날 16비트 NE 시대와 지금의 PE 시대에 stub 프로그램에 차이가 있는지..?
그래서 오랜만에 도스 API와 8086 어셈블리 명령어 레퍼런스까지 찾아서 stub 프로그램을 분석해 봤다.

stub 프로그램의 코드는 이게 전부이다.

(1) 0E        PUSH CS
(2) 1F        POP DS
(3) BA 0E 00  MOV DX,000E
(4) B4 09     MOV AH,09
(5) CD 21     INT 21
(6) B8 01 4C  MOV AX,4C01
(7) CD 21     INT 21
"문자열"


(1), (2) 맨 앞의 PUSH와 POP은 데이터 세그먼트를 코드 세그먼트의 값과 맞추는(DS=CS) 일종의 초기화이다. 스택에다가 CS 값을 넣은 뒤 그걸 DS로 도로 가져오는 거니까.
지금 이 프로그램은 화면에다 찍을 에러 메시지도 기계어 코드와 정확하게 같은 영역에 있으므로 저건 수긍이 가는 조치이다.

(3) 그 다음으로 DX 레지스터에다가 16진수로 0xE, 즉 14를 기록한다. 저 stub 프로그램은 길이가 정확하게 14바이트이다. 이 값은 프로그램의 시작 지점을 기준(0)으로 해서 그로부터 14바이트 뒤에 있는 문자열을 가리킨다.

(4) AX 레지스터의 high byte에다가 9를 기록한다.

(5) 이렇게 기록된 AX와 DX 레지스터 값을 토대로 0x21 인터럽트를 날려서 도스 API를 호출한다. 도스 API 중 9는 DX가 가리키는 주소에 있는 문자열을 화면, 정확히는 표준 출력에다가 찍는 기능을 수행한다.
그런데 굉장히 기괴한 점이 있는데.. 얘가 받아들이는 문자열은 null-terminated가 아니라 $-terminated여야 한다!

믿어지지 않으면 아무 Windows용 EXE/DLL이나 헥사 에디터로 열어서 앞부분의 에러 메시지 텍스트가 무슨 문자로 끝나는지를 확인해 보시기 바란다.
왜 그렇게 설계되었는지 모르겠다. 파일이나 디렉터리 이름을 받는 도스 API들은 당연히 null-terminated 문자열인데 말이다.

(6) 그 다음, AX 레지스터에다가 0x4C (high)와 0x1 (low)을 기록하고..

(7) 또 도스 API를 호출한다. 0x4C는 프로그램을 종료하는 기능을 하며, 종료와 동시에 low byte에 있는 1이라는 값을 에러코드로 되돌린다. 정상 종료는 0인데 1은 뭔가 오류와 함께 종료되었음을 나타낸다.
사실, 도스 API 레퍼런스를 보면 AH 값으로 0도 프로그램을 종료시키는 역할을 하는 듯하다(도스 1.0때부터 최초). 하지만 모종의 이유로 인해 그건 오늘날은 사용이 별로 권장되지 않으며 0x4C가 원칙이라 한다(도스 2.0에서부터 추가됨).

이렇게 분석 끝. 정말 간결 단순명료하다.
참고로 도스 EXE에서 헤더를 제끼고 기계어 코드가 시작되는 부분은 0x8~0x9 오프셋에 있는 unsigned short값에다가 16을 곱한 오프셋부터이다. 가령, 거기에 04 00 이렇게 적혀 있으면 0x40 오프셋부터 디스어셈블링을 해 나가면 된다. EXE는 헤더에 고정 길이 구조체뿐만 아니라 가변 길이인 '재배치 섹션'이 나오고 그 뒤부터 코드가 시작되기 때문이다.

그럼 과거 16비트 Windows에서 쓰이던 stub은 어떻게 돼 있었을까?
거의 차이가 없긴 한데, 문자열이 들어있는 위치와 얘의 주소를 전하는 방법이 달랐다.

(1) E8 53 00  CALL 0056
"문자열"
20 20 20 20 .. padding 후
(2) 5A        POP DX
(3) 0E        PUSH CS
(4) 1F        POP DS
(5) B4 09     MOV AH,09
(6) CD 21     INT 21
(7) B8 01 4C  MOV AX,4C01
(8) CD 21     INT 21


(1) 맨 먼저 JMP도 아니고 웬 CALL 인스트럭션이 나온다. 기계어로 표기할 때는 인자값이 0x53이어서 3바이트짜리 자기 자신 인스트럭션 이후에 0x53바이트 뒤로 가라는 뜻이 되는데, 영단어로 바꿔서 표기할 때는 자기 자신 원래 위치 기준으로 0x56바이트 뒤가 된다. 이 위치는 그냥 바로 다음 (2) 명령이 있는 곳과 같다.

(2) 함수 호출을 했는데 RET를 하는 게 아니라 스택을 pop하여 DX 레지스터에다 가져온다. 그렇다. 아까 그 call에 대한 복귀 주소에 문자열이 담겨 있으니, 아까 같은 하드코딩이 아닌 요런 방식으로 문자열 주소를 얹었다.

(3) (4) 이제부터는 아까처럼 DS = CS 해 주고,

(5)~(8) 아까와 동일. 문자열을 찍은 뒤 프로그램을 종료한다.

이런 초간단 초미니 프로그램은 exe가 아니라 com 형태로도 만들지 말라는 법이 없어 보인다. com은 그 어떤 헤더나 시그니처도 없이 첫 바이트부터 바로 기계어 코드와 데이터를 써 주면 되는.. 정말 원시적이기 그지없는 바이너리 덤프일 뿐이기 때문이다. 빌드 날짜, 버전, 요구하는 아키텍처나 운영체제 등등 그 어떤 부가정보도 존재하지 않는다.

요즘 프로그래밍 언어들이 기본 제공하는 런타임들의 오버헤드가 너무 크다 보니, 이에 대항하여 세상에서 제일 작은 "Hello world" 프로그램 이런 것에 집착하는 덕후들이 있다. Windows 프로그램의 경우 프로그램을 특수하게 빌드하여 CRT 라이브러리는 당연히 떼어내고, 코드와 데이터도 한 섹션에다 우려넣고, 거기에다 후처리까지 해서 단 몇백 바이트만으로 MessageBoxA(NULL, NULL, "Hello, world!", 0) 하나만 호출하는 프로그램을 만든 예가 있다.

그러나 이런 것들도 com 앞에서는 몽땅 버로우 타야 한다. 얘는 아예 파일 포맷 자체가 없으니까. 이 이상 더 줄일 수가 없다. com 형태로 만든 Hello world 프로그램은 겨우 20몇 바이트가 전부이다.
무슨 명령어를 내렸는지 기억은 안 나지만 컴퓨터를 재시작시키는 com 파일이 있었는데, 얘는 크기가 겨우 2바이트에 불과했다.

(1) BA 0C 01  MOV DX,010C
(2) B4 09     MOV AH,09
(3) CD 21     INT 21
(4) B8 01 4C  MOV AX,4C01
(5) CD 21     INT 21
그 뒤에 "Hello, world!$" 같은 문자열. 따옴표는 제외하고.


com은 exe처럼 코드/데이터 세그먼트 DS=CS 따윈 전혀 신경 쓸 필요 없이, 바로 본론부터 들어가면 된다. 그 대신 com은 16비트 단일 세그먼트 안에서 코드와 데이터 크기 한계가 모두 64K라는 치명적인 한계를 갖는다. 메모리 모델로 치면 그 이름도 유명한 tiny 모델 되겠다. 애초에 exe가 16비트 CPU에서 저 한계를 극복하고, 또 멀티태스킹에 대비하여 재배치도 가능하게 하려고 만들어진 포맷이기도 하다.

아, 아주 중요한 사항이 있다. com에서는 첫 256바이트, 즉 0x100 미만의 메모리 주소는 시스템용으로 예약되어 있어서 사용할 수 없다. 내 코드와 데이터는 0x100부터 시작한다. 그렇기 때문에 저 프로그램의 코드 크기는 12바이트이고, 문자열은 0xC 오프셋부터 시작하긴 하는데 거기에다가 0x100을 더해서 DX에다가는 0x10C를 써 줘야 한다.

Windows PE에다 비유하자면 0x100이 고정된 base address값인 셈이다. 그리고 DX의 값은 그냥 VA이지 RVA가 아니다.
과거에 굴러다니던 exe/com 상호 변환 유틸리티들이 하던 주된 작업 중 하나도 이런 오프셋 재계산이었다. 그리고 com에서 exe라면 모를까 더 넓은 곳에서 좁은 곳으로 맞추는 exe -> com은 아무 exe에 대해서나 가능한 게 물론 아니었다. (단일 세그먼트 안에서만 놀아야..) 과거 도스에 exe2bin이라는 외부 명령어가 있었는데 걔가 사실상 exe2com의 역할을 했다.

아무튼, 저 바이너리 코드와 문자열을 헥사 에디터를 이용해서 입력한 뒤, 파일을 hello.com이라고 명명하여 저장한다. 이걸 도스박스 같은 가상화 프로그램에서 도스 부팅하여 실행하면 신기하게도 Hello, world!가 출력될 것이다.
고급 언어를 사용하지 않고 컴파일러 나부랭이도 전혀 동원하지 않고 가장 원초적인 방법으로 나름 네이티브 실행 파일을 만든 것이다. 사용 가능한 코드와 데이터 용량이 심각하게 작다는 것과, 요즘 64비트 Windows에서는 직통으로 실행조차 할 수 없다는 게 문제이긴 하지만. (네이티브 코드라는 의미가 없다~!)

이런 식으로 컴퓨터에 간단히 명령을 내리고 램 상주 프로그램이나 바이러스 같은 것도 만들기 위해 옛날에는 debug.com이라는 도스 유틸리티가 요긴하게 쓰였다. 간단한 어셈블러/디스어셈블러 겸 헥사 에디터로서 가성비가 뛰어났기 때문이다. edlin 에디터의 바이너리 버전인 것 같다.

오늘날 어셈블리어라는 건 극소수 드라이버/컴파일러 개발자 내지 악성 코드· 보안· 역공학 같은 걸 연구하는 사람들이나 들여다보는 어려운 물건으로 전락한 지 오래다. 하지만 이것도 알면 디버깅이나 코드 분석에 굉장한 도움이 될 듯하다.
디스어셈블리 자체는 주어진 규칙대로 바이트 시퀀스를 몇 바이트씩 떼어서 명령어로 분해해 주는 비교적 간단한 작업일 뿐이다. 파서(parser)가 아니라 스캐너(scanner) 수준의 작업만 하면 된다.

하지만 디스어셈블리가 골치 아프고 귀찮은 이유는 코드의 첫 실행 지점을 정확하게 잡아서 분해를 시작해야 하며, 그래도 어느 게 코드이고 어느 게 데이터인지가 프로그램 실행 문맥에 의해 시시각각 달라지고 무진장 헷갈리기 때문이다. 데이터는 백 날 디스어셈블링 해 봤자 아무 의미가 없고, 오히려 코드의 분석에 방해만 된다. 이런 역공학을 어렵게 하기 위해서 디스어셈블러를 엿먹이는 테크닉도 보안 분야에는 발달해 있다.
하긴, 코드와 데이터가 그렇게 경계 구분 없이 자유자재로 변할 수 있는 게 "폰 노이만 모델 기반의 튜링 기계"가 누리는 극한의 자유이긴 하다.

Posted by 사무엘

2016/12/17 08:34 2016/12/17 08:34
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1306

친일파 문제 끝장내기

난 소위 말하는 친일파· 민족 반역자라는 건 다음과 같은 세 그룹으로 나뉘며, 이들을 분명히 나눠서 생각할 필요가 있다고 본다.

  • A: 매국의 대가로 일제로부터 예우 받고, 당대에나 지금까지도 일체의 참회 없이 대대로 금수저로 잘 쳐먹고 잘살고 있어서 어그로 (조선 말기의 일부 관료· 지주· 황족 출신과 그 후손)
  • B: 일제 강점기 동안 독립운동가를 고문하고 때려잡는 짓을 해서 어그로 (악질 헌병· 경찰 복무자)
  • C: 공인으로서, 지식인으로서 먹고 살려고 권력에 아부하고, 자기 인지도를 이용해서 신사참배 내지 황국신민 징병 권유 같은 짓을 해서 어그로

그리고 난 이들의 적극성과 죄질은 명백하게 A > B > C의 순이라고 생각한다. 비록 비주얼한 임팩트는 B가 가장 강하겠지만 실질적으로는 A가 앞선다. 시간적인 등장 순서도 A, B, C의 순에 가깝고, 해당되는 사람 수도 A < B < C로 갈수록 많아진다.
점점 더 당대 사람의 책임보다는 애초에 나라를 말아먹은 선조의 책임이 더 커지며, 죄질이 가벼워지고 정상 참작의 사유가 생기는 건 당연지사다. 단적인 예로, 내가 그 시절을 살았다고 가정하더라도 내가 C가 될 가능성은 조금 있지만 B나 A 같은 간 큰 짓을 할 가능성은 거의 없다.

일제 강점기 35년을 나치 치하에 한 몇 년만 점령당했던 프랑스하고 비교하는 사람들이 있는가 본데, 어디 비교할 걸 비교해야지..
세대가 바뀌어 버리니 한반도의 경우 해방 당시엔 태극기 모양을 기억 못 하는 사람이 있을 정도였고 어린애들은 "어? 우리나라(=일본)가 전쟁에서 졌는데 왜 어른들이 다들 기뻐해?" 이럴 정도였다. 프랑스가 나치 독일한테 이렇게까지 오래 점령당하고 세뇌당했었냐? -_-;;

이 완용· 송 병준 같은 놈들은 A이고, 노 덕술은 B, 김 활란 같은 사람은 C다. 박 정희가 B에 속한다고 생각하는 사람들이 많으나 그 증거는 없다. 그냥 평범한 도적을 잡는 일만 한 경찰이나 중공군하고만 싸운 군인이라면 난 문제삼지 않음.
그 시절에 일제 치하의 공무원(교사, 경찰, 헌병 등등)에 취업하려 한 것 자체는 요즘 애들이 전부 공무원, 대기업에만 몰리고 과학고 나와서 의대에만 몰리는 것하고 하나도 다를 바 없다고 여겨진다. 공부 잘하면 다들 남을 통솔하고 다스리는 안정적인 직업을 갖고 싶어하지, 너 같았으면 평생 농사만 짓거나 프롤레타리아 로동자 기술자로만 살았겠는가? 굳이 "조센징 엿먹어라"가 아니라 단순 출세욕 때문이라는 것이다.

난 죄질을 A > B > C의 순으로 판단하기 때문에.. 똑같은 일제 강점기 여주인공이 나오는 영화인데 C급인 <청연>은 친일 논란에 휩싸이면서 망한 반면, A급에 '준하는' <덕혜옹주>나 <명성황후>는 어째 항일투사로 미화되면서 필요 이상으로 흥행하는지 이해가 잘 안 된다. 비록 그 사람들은 적극적인 매국노와 같은 급은 아니지만, 그렇다고 잘한 것도 전~혀 없는데도 말이다.

그 시절에 이 완용 내지 을사오적 같은 특정 개인 몇 놈만 없었다고 해서 대세가 뒤엎어졌다거나 나라가 안 망할 수 있었다거나 한 게 아님은 주지의 사실이다. 다만, 이 완용이 진짜 개새끼인 이유는 매국 행위 자체가 아니라 그 뒤의 태도와 처신이 더 크게 작용했기 때문이다. 그건 정말로 그 어떤 실드의 여지도 없다.

A, B는 몰라도 C는 법적인 처벌까지는 아니고 도의적인 비판· 비난, 불이익 수준이 적절하다고 여겨진다. 음주운전 적발 경력 같은?
그 시절에 일제한테 그 정도 협조 내지 영혼 없는 립서비스조차 안 했으면 조선인이 기업을 경영하고 고급 기술을 그만치 만질 기회라고는 있을 수 없었을 거란 점을 감안해야 한다.

물론 그땐 그랬다 치더라도 해방 후에도 일말의 반성이 없이 고개 꼿꼿하게 세우고 잘나가고 있다면 그 꼴은 보기가 참 민망하겠지만, 그래도 C 욕하는 그 의협심 강한 깨시민들도.. 자기가 그런 위기에 처하면 걔네 역시 십중팔구 변절하고 깃발 바꿔 달 거라는 건 내가 절대 장담한다. -_-;; 지금 중국에 대한 태도만 봐도 안 봐도 비디오, 안 들어도 오디오다.

신사참배 갖고 한국 교회가 썩었네 어쩌네 하는 좌독들이 있다. 물론 신사참배 결의는 한국 교회의 치욕적인 흑역사인 건 사실이다. 또한, 이 역시 외압에 굴복하여 신사참배를 결의한 것 자체보다도, 해방 후에까지 곧장 참회하지 않고 뻣뻣한 목을 유지한 것이 더 큰 죄악이긴 하다.

허나, 한편으로는 지들은 그 상황에서 과연 꼿꼿하게 버텼을까? 일제가 그땐 가족까지 인질로 잡아서 얼마나 치사하고 악독하게 협박과 회유를 일삼았는데? 흔히 생각하는 단순히 부정부패 때문에 굴복한 거 아니다! 그런 것들을 감안해서 판단한다. 저건 비판받을 사항이긴 하나, 자기가 도덕적으로 우월한양 정죄를 일삼고 교회 정체성을 부정할 정도는 명백히 아니다.

우리나라가 건국 초기에 반민특위의 해체와 친일 군경· 관리 재등용 때문에 문제가 되고 좌빨들에게 두고두고 꼬투리를 잡히고 있는 것 역시 사실이다. 이건 C보다는 수위가 높은 B 때문에 그런 것이다. 그런데 그들이 아마 롤모델로 삼고 있을 북한조차도 B들을 재등용해서 쓴 건 동일할 뿐만 아니라, 내가 늘 말하지만 그건 정말 전적으로 불가피하게 그렇게 된 것이다. 인재가 부족해서 말이다.

"친일 군경들이 해방 후에 그대로 옷만 갈아입고 반공투사로 변신"
내가 분명히 말하는데 이건 절~대로 부정적이기만 한 현상이 아니다! 그땐 그런 반공투사라도 없으면 안 됐다! 그 시절에 불가능했던 일을 이루지 못했다고 자꾸 이상한 피해의식 망상 집어넣는 선동질에 속지 마라.

우리나라는 건국 당시에 대통령과 내각 등 브레인들이야 당연히 다 독립운동가에 광복군 출신이었다. 단지, 말단에서 궂은일 하는 중하급 군경 간부들 중에는 일제 부역자들이 있었다. 이거는 램 4MB에서 돌아가는 Windows 95가 32비트 껍데기 밑에 불안정한 16비트 도스 코드가 부득이하게 호환성 때문에 여전히 포함돼 있던 것과 하나도 다를 바 없는 이유 때문이었다.
대한민국 건국 당시의 백성들의 컴퓨터 사정은 Windows NT 따위는 절대로 돌릴 수 없는 여건이었으니 말이다.

자, 그럼 마지막으로 남는 A급 잔당들은?
일일이 색출해서 재산 몰수했으면 좋겠지만 워낙 극소수이기도 하고 B급과 겹치는 놈도 있고, 그 혼란한 와중에 일일이 죄질을 파악해서 공산당 식으로 일을 처리하기 어려웠다. 이런 놈들이 일부 오늘날까지 호의호식하고 있는 건 안타깝고 화가 나긴 하지만, 정말로 예외적인 경우일 뿐이지, 대다수 평범한 사람들이 세상 비관하고 탓할 정도는 절대 아니다.

2010년이던가, 산낙지 살인 사건 기억하는가? 이건 단순 사고가 아니라 보험사기를 노리고 남자가 여친을 살해한 악질 살인이 거의 확실해 보이는 모든 심증 정황이 있었다. 그럼에도 불구하고.. 결정적인 증거가 없고 피해자인 여자애가 일찍 화장되어 없어져 버리는 바람에 용의자는 증거불충분으로 덜컥 무죄 선고를 받아 버렸다! 그런 것과 비슷하다. 세상엔 그런 분통 터지는 일도 있다. 무고한 사람을 막 빨갱이로 몰아가서 억울한 피해자를 만든 '유죄 추정의 원칙'만큼이나, '무죄 추정의 원칙'도 이런 식으로 한계와 부작용이 있는 법이다. 100% 만능이 아니다.

또 다른 예로는, 가평과 춘천 사이에 있는 남이섬 유원지가 A급 친일파 반역자 민 모 씨 가문의 후예 소유라고 알려져 있다.
물론 직접적으로 일제로부터 향응 차원에서 남이섬을 하사받기라도 한 건 아니다. 그랬으면 해방 후에 국가에서 정말 0순위로 몰수했어야지. 반쯤은 자기들이 원래부터 한국은행장도 배출할 정도로 출세하기도 했고, 그래서 1970년대에 자기 돈 내고 전주인으로부터 남이섬을 통째로 산 거라고 한다. 이 정도면 어디까지가 친일매국의 댓가이고 어디부터가 개인 사유재산권 추구인지 따지기가 솔직히 모호한 구석이 있다.

하지만 다른 사람도 아니고 명백한 친일파 후손이 남이섬을 우리나라 안의 딴 세상 '나미나라 공화국'처럼 꾸며 놓았다는 걸 알면 그게 마냥 재미인 것 같지는 않아 보인다.;; 잠재적 내란죄? 남이섬이 평범한 국공립 유원지라면 마케팅을 그런 식으로 할 리는 절대 만무하지 않겠는가?
거기서 한 해 벌어들이는 입장료 수입이 어마어마하다던데, 이 문제에 양심이 민감한 분이라면 남이섬 관광 같은 건 안 가는 게 바람직할 것이다.

이상이 맨날 말도 많고 탈도 많은 친일 청산 문제에 대한 나의 생각이다.
지금까지 살펴보았듯, 친일파라는 건 용어의 정의 내지 범위는 오락가락 하는데 빨갱이와 더불어 한국 사회에서 남을 인격 모독하고 억울하게 매장할 수 있는 마법의 단어 양대산맥이다.

자기도 감당하지 못했을 '의로운' 잣대를 강요하면서 남을 정죄하고 쓸데없이 세상 비관하는 건 옳지 못한 자세이다. 하지만 반대로 C급만 부각시키면서 단순 가담자와 악질 주동자를 한데 싸잡아 "그땐 누구나 다 그랬을 것"이라는 양비론으로만 퉁치는 것 역시 옳지 못하다. (1) 나라도 그땐 그럴 수밖에 없었겠냐 (2) 동족을 괴롭히고 등쳐먹는 등 물질· 정신적으로 악한 영향을 적극적으로 끼쳐서 사익 챙겼냐 (3) 그 뒤에 참회· 반성하고 있냐 정도를 잣대로 판단하면 큰 오류에 빠질 일은 없으리라 여겨진다.

참고로 아래의 부류들은 이 글에서 진지하게 다루는 부정적인 심상의 '친일파' 라인이 아니므로 오해 없도록 하자.

  • 김 옥균: 매국 의도 제로. 정말 악의 없이 일본을 선하게 보고 걔들로부터 도움을 받으려 했던 옛날 사람일 뿐이다.
  • 오덕: 그냥 비정치적으로 일본 문화만을 좋아하는 사람.
  • 김 완섭: 그냥 책 팔아먹으려는 관심병자 또라이 생계형 친일파일 뿐이다. 이 승만 대통령이 잘못한 게 부정선거 야당 탄압 독재 등등 많은 흑역사 과오들을 제치고 평화선을 그어서 독도를 빼앗은 것이라고 말하는데 더 일고의 가치가 있겠나? 국가 정체성에 아무 위협이 되지 않으며 다른 사람들에게 위화감을 끼치지도 않는다. 그런데 이런 사람이 5 18 민주화 운동 유공자랍시고 국가로부터 예우와 혜택도 받고 있다.
  • 일본과 일제 강점기에 대해 필요 이상으로 긍정적으로 말하는 편인 일부 우파 논객: 진짜 일본과 커넥션이 있고 자기 재산 지키고 싶고, 한편으로 우리나라가 망하길 바라는 개XX라면 절대로 저런 짓 안 한다. 얼굴 내밀고 소신 발언 하면서 어그로 끄는 멍청한 짓 따윈 절대 안 한다. 일부 과격 극단으로 치우친 견해가 있더라도 진짜 북한과 커넥션이 있고 지령도 받고 있는 종북 좌빨보다야 비교가 무의미할 정도로 해롭지도 위험하지도 않은 사람들이다.

Posted by 사무엘

2016/12/14 08:31 2016/12/14 08:31
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1305

오옷, 지금까지 내 블로그에서 데이터베이스에 대한 얘기가 거의 없었던 것 같다.
오늘날 정보화· 컴퓨터 세상의 근간을 담당하는 핵심 소프트웨어 기술을 꼽자면 (1) 운영체제(!!), (2) 컴파일러(컴퓨터에서 돌아가는 모든 프로그램들을 생성..), (3) 손실/무손실 압축 알고리즘, 그리고 (4) DB엔진이지 싶다. 딱히 무순으로 나열한 것임.

요즘은 전국민의 신분 근황, 학생들의 모든 학적 정보, 카드 거래 내역, 병원 진료 내역 등등등~ 모든 기록과 행적이 전산화됐다.
그리고 저기서 전산화라는 건 곧 DB화를 의미한다. DB 엔진 없이는 이 복잡한 세상이 돌아갈 수 없는 지경이 된 지 오래다. 또한 key-value 개념부터 시작해 삼라만상의 정보들을 다 표와 표를 융합해서 구축한다는 '관계형'이라는 모델, 그리고 정규화 계층 같은 DB 이론도 깊이 들어가면 생각보다 굉장히 심오하고 복잡하다.

똑같이 총이라 해도 권총부터 시작해 소총, 중기관총, 대포까지 다양한 크기가 있듯이 DB 엔진이라는 것도 스케일이 생각보다 매우 다양하다.
네트워크를 통해 들어오는 수백~수만~수백만 건의 동시 접속 트랜잭션을 소화하면서 방대한 양의 데이터를 극도의 안정성(그 대신 성능 오버헤드도..)을 보장하면서 처리하는 대형 DB 엔진이 있다.
이런 건 일반 사용자가 개인용 PC에서 돌릴 일은 없는 물건이다. 오라클 내지 MS SQL Server 같은 프로그램의 제일 고급 에디션이 이 범주에 해당할 것이며 이런 건 가격도 왕창 비싸다.

MySQL은 저 정도로 방대한 스케일은 아니지만 원격· 다중 접속을 지원하고 로컬 내지 중소규모 웹 서버에서 굴리는 용도로 가성비가 아주 좋다. 게시판이나 블로그 엔진들이 컨텐츠를 얘를 기반으로 구축하곤 한다.

MS Office에 포함돼 있는 Access 정도로 가면 다중 접속은 이제 없고, 서버가 아닌 클라이언트 지향 DB가 된다. 개인용 컴퓨터에서 엑셀로 처리하기엔 좀 방대한 양의 데이터를 엑셀보다 더 프로그래밍 지향적으로 전문적으로 처리하는 도구로 격이 더 낮아진다. 예전에 Visual C++ 책을 봐도 DB 관련 API는 꼭 한 챕터가 할당돼 있었으며, ODBC는 큰 DB, DAO는 좀 작은 DB라고 봤었다.

개인적으로는 성경을 DB로 구축하니 좋았다. 성경은 신구약 전체가 31000구절쯤 되고 역본을 10여 개 갖고 있으면 구절 수가 몇십만 개에 달한다. 그리고 내가 원하는 구절만 쿼리를 날려서 찾는 건 아무래도 스프레드 시트보다는 응당 DB가 제격이다.

또한, 먼 옛날에 컴퓨터 학원에서 dBase III+를 배우던 추억이 떠오른다. 얘도 그 당시로서는 Access에 준하는 체급의 개인용 DBMS라 볼 수 있겠다. SQL이 아닌 독자적인 문법 기반이었고, 명령 프롬프트 모드도 있고 메뉴를 띄워서 DB 파일을 관리하는 assist 모드도 있어서 UI가 독특했다. 또한 dBase가 생성하던 DBF 파일은 도스 시절에 아래아한글도 전화번호부에서 사용하고 DB Viewer를 제공할 정도로 옛날에 꽤 대중적인 파일 포맷이었다.

여느 워드 프로세서나 스프레드 시트와는 달리, DB 프로그램에서는 각 데이터에 속하는 속성들을 자료형과 크기까지 꽤 까다롭게 미리 지정해 놓고 데이터를 넣어야 한다. 프로그램 코딩을 할 때 말고 '자료형'이라는 개념을 따지고 생각해야 하는 분야는 아마 DB밖에 없지 싶다.

사실은 프로그래밍 언어 중에도 자료형이 엄격하지 않고 귀걸이 코걸이 식으로 변할 수 있는 언어가 있다. 그리고 DB 자료형은 엔진에 따라 다르긴 하지만 프로그래밍 언어의 그것과는 달리 딱히 기계 친화적으로 지정하지 않아도 되는 경우가 있다. 숫자형의 표현 범위를 2진법이 아닌 10진법 기준 자릿수로 지정하는 것처럼 말이다.
전화번호는 절대로 숫자형으로 지정하지 말고 문자열형으로 지정해서 넣어야 한다고 학원 선생님에게서 들은 기억이 남아 있다.

"명령줄 기반 + UI + 반쯤 절차형 프로그래밍 환경"이라는 점에서는 이런 DB 프로그램은 매쓰매티카 같은 수학 패키지와도 구조가 비슷한 구석이 있는 것 같다. 아무나 함부로 접근하기는 어렵다는 공통점도 있고 말이다.

그에 비해 엑셀은 어떤가? 대용량 데이터를 취급하는 성능은 DBMS보다 뒤쳐지고, 수식 계산은 수학 패키지에, 비주얼과 레이아웃 기능은 워드 프로세서에 밀린다. 엑셀은 심벌 연산이나 임의 자릿수 계산 기능이 없으며(수학 패키지), 성능을 위해 위지윅(워드 프로세서)도 포기했다.

그럼에도 불구하고 엑셀은 이들 이념을 어중간하게 절충해서 얻은 접근성과 성능, 가성비 덕분에 일반 사용자에게 최고의 업무 처리 앱이 되었다고 볼 수 있다. 일종의 포지셔닝을 잘해서 승리자가 됐다. 한 값이 바뀌었을 때 관련된 셀의 값들이 연달아 쫙 바뀌는 동적인 문서를 손쉽게 만들 수 있는 게 최고의 강점인 듯하다. 또한 피벗테이블/차트는 SQL 같은 거 하나도 몰라도 SELECT 쿼리에서 특히 GROUP BY를 적절하게 구현해 줬다고 볼 수 있다.

DBMS는 굳이 사람만 쓰는 건 아니고 다른 컴퓨터 프로그램이 로컬에서 내부적으로 사용하기도 한다. 에.. 그러니까, 사람이 관리하는 데이터 말고 프로그램이 자기 혼자만 취급하는 데이터를 관리할 목적으로 말이다. 이런 데에 미들웨어 컴포넌트처럼 쓰이는 DB 엔진은 덩치가 더욱 작고 백업· 응급 복구 같은 안전 기능이 없는 대신, 크기· 성능 오버헤드가 더욱 작고 빠르다.

예전에 파일 포맷에 대해서 글을 쓴 적이 있었다. 내 프로그램이 테이블 형태이고 수정이 빈번한 몇백만 개의 대용량 데이터를 다루는데, 파일 포맷을 새로 만들기는 심히 귀찮고 그렇다고 단순 선형적인 바이너리/텍스트 컨테이너 포맷을 쓰기에는 성능이 우려된다면, 범용성으로 인한 약간의 오버헤드를 감수하고라도 저런 내장형 소형 DB를 얹는 게 좋은 선택이 될 수 있다.

괜히 파일 내부에서 골치 아픈 청크가 어떻고 헤더가 어떻고 데이터를 바이너리 비트 수준에서 신경 쓸 필요 없이, 그냥 테이블 스키마.. 이건 프로그래밍 언어로 치면 C/C++ 쓰던 게 아주 고수준 언어로 바뀐 것과도 같다. DB 구조 자체가 일종의 파일 시스템에 대응하니까.

특히 데이터 전체를 무식하게 메모리에 다 올려서 작업하는 형태가 아니라면 DB의 가성비가 더욱 올라간다. 요즘 시대에 다 차려져 있는 밥상인 검증된 오픈소스 솔루션을 놔두고 개발자가 B+ 트리 같은 거 일일이 구현하면서 삽입 삭제 수정 케이스를 일일이 테스트 할 이유가 없기 때문이다.

이런 컴퓨터지향적인 DB는 DB가 하는 본연의 작업에다가 비교/정렬/데이터 변형 알고리즘 같은 일부 핵심 작업만 내가 custom으로 작성한 함수로 대체할 수 있어서 대단히 강력하고 편리하다. 당연히 C/C++로 작성하여 네이티브 코드로 빌드한 함수로 말이다. 파이썬이나 Lua처럼 C/C++ glue에 뛰어난 고급 언어가 있듯, glue에 최적화된 DBMS도 응당 있다.

Visual Studio의 경우 인텔리센스 엔진이 ncb 자체구현 DB를 쓰던 것이 2010부터는 자사의 SQL Server "Compact Edition" DB 기반으로 바뀐 것으로 유명하다. 그런 건 DB를 사용하기 꽤 적절한 용례로 보인다. C++ 문법이란 건 앞으로 또 뭐가 생기고 어떻게 변할지 모르는데 그런 것에 대응하는 것도 파일보다는 DB 지향이 더 유리하겠다.

MS 것 말고도 이 바닥의 유명한 오픈소스 소형 DBMS로는 SQLite가 있다. 리처드 힙이라는 아저씨가 만들었는데, 그냥 오픈소스로도 모자라 골치아픈 LGPL, MIT 라이선스 그딴 것조차 거부하고 소스를 걍 public domain으로 뿌렸다..;;; 그러면서 "님이 받은 만큼 님도 남에게 베풀어 주세요"를 저작권 notice랍시고 적은 게 전부이고.. 천재에다 신자이고 굉장한 대인배이신 듯하다.

The author disclaims copyright to this source code. In place of a legal notice, here is a blessing:
- May you do good and not evil.
- May you find forgiveness for yourself and forgive others.
- May you share freely, never taking more than you give.


모질라 재단의 이메일 클라이언트 유틸인 ThunderBird는 워낙 대용량 편지함을 관리하다 보니 내부 파일이 SQLite DB인 듯하며, 안드로이드 OS에서도 얘를 적극 활용 중이라고 한다. 그러고 보니 소형 DB들은 MS것과 오픈소스 모두 제품명에 compact, lite라는 '꼬마'를 나타내는 단어는 꼭 들어가 있다.

본인도 회사에서 SQLite를 좀 다룰 일이 있었다.
SQLite는 코드가 다양한 플랫폼에서 다양한 문자 인코딩(UTF-8, UTF-16 빅/리틀/디폴트)에 대비하여 API가 굉장히 세심하게 설계된 게 인상적이었다. 하긴, 인코딩에 따라 한글 같은 건 글자 수가 달라져 버리니 정보량에 매우 민감한 DB에서 그걸 민감하게 다루지 않을 수가 없다. 간단하게 단일 문자열로 통합· 추상화가 가능하지 않다는 얘기다.

콜백 함수는 자신이 받고 싶은 문자열의 형태를 지정해 줄 수 있으며, 콜백 함수 자체의 인자는 char도, wchar_t도 아닌 const void*로 돼 있다.
그리고 DB 내부에서 사용하는 문자열뿐만 아니라 열고 싶은 DB 파일을 지정하는 것도 16비트 문자열형 버전이 따로 있는데, 이건 Windows처럼 16비트 문자열을 네이티브로 쓰는 OS에서 CreateFileW 같은 W API를 쓰면서 제 성능을 낼 수 있게 한 배려로 보인다.

다음은 DB와 관련된 여러 문자열 처리 관련 잡설들이다.

1. 정렬

프로그래밍 언어들이 제공하는 문자열 비교는 정말 단순무식하게 숫자 비교의 연장선으로서 각 문자들의 코드값 비교 그 이상도 이하도 아니다. 허나 실생활에서는 오름차순/내림차순부터 시작해 대소문자 구분, 언어 정보를 고려한 비교 같은 복잡다양한 옵션이 필요하다.

대중적이고 자주 쓰이는 옵션은 SQL에서도 언어 차원에서 (1) 옵션을 제공한다. 하지만 좀 더 복잡한 정렬을 위해서는 값을 그대로 비교하는 게 아니라 (2) 사용자가 변조한 값을 비교한다거나 (3) 아예 비교 함수 자체를 customize할 수 있어야 한다.
물론 (3)만 있어도 (1)과 (2)는 다 처리가 가능하니 C 언어의 qsort 함수는 비교 함수만 인자로 받는다. 그러나 파이썬의 정렬 함수는 (1)~(3)까지 다양한 방식으로 운용 가능하다. SQL은 collation이라는 개념으로 정렬 알고리즘 자체를 customize할 수 있다.

2. 토큰화

구분자를 사이에 두고 여러 문자열들이 뭉쳐 있는 문자열을 토큰화해서 문자열(단어)들의 리스트로 뽑아내는 건 탈출문자 인코드/디코드만큼이나 이 바닥에서 굉장히 흔하게 행해지는 작업인 것 같다. 파이썬의 경우 split이라는 메소드가 있다.

그런데 토큰화라는 게 두 부류가 있다. 하나는 구분자가 whitespace 부류이기 때문에 "A    B"나 "A B"나 똑같이 A와 B로 분간되는 것이다. A와 B 자체는 빈 문자열이 될 수 없다.
다른 하나는 구분자가 콤마나 세미콜론 같은 부류이며, 한 구분자가 정확하게 한 아이템만을 분간한다. A,,,B라고 쓰면 A와 B 사이에 빈 문자열이 두 개 더 걸려 나온다..

C가 제공하는 오리지널 strtok는 컨텍스트를 받는 인자가 없어서 (1) 토큰 안에서 또 토큰 구분을 할 수 없으며 멀티스레드 환경에서 사용하기에도 위험하다. 그뿐만이 아니라 얘는 (2) whitespace형 토큰화만 지원하기 때문에 콤마형 토큰화에는 사용할 수 없다는 것도 단점이다. 그래도 뭔가 문자열을 또 복사하고 생성하는 게 없고 성능 하나는 나쁘지 않기 때문에 컨텍스트 인자만 추가해 주면 여전히 유용한 구석은 있다.

DB를 텍스트 형태로 덤프 백업하면 그냥 csv 형태로만 뱉는 게 아니라, 그대로 SQL을 실행만 하면 DB의 재구성이 가능하게 INSERT INTO xxx VALUES가 붙은 형태로 백업되는 것도 많다. DB 스키마는 그냥 CREATE TABLE ... 형태가 될 것이고.
코드와 데이터의 경계가 모호하다. DB 백업도 뭔가 JSON 같은 포맷과 연계 가능하지 않을까 하는 생각이 잠시 들었다.

3. 검색어의 전처리

SQL로 문자열을 검색하고 싶으면 그 이름도 유명한 LIKE 연산자를 쓰면 된다. 어지간한 프로그래밍 언어라면 함수 형태로 구현되었을 기능이 SQL에서는 연산자이다.
얘는 정규 표현식과 같지는 않은데 반쯤은 정규 표현식을 닮은 문법을 지원하여, A LIKE B는 A가 B라는 패턴을 만족하는지 여부를 되돌린다. 0개 이상의 임의의 문자열을 뜻하는 와일드카드가 *가 아니라 %이다. XXX로 시작하는 문자열, 끝나는 문자열, 중간에 XXX가 포함된 문자열 같은 게 다 이걸로 커버 가능하다.

그런데 탈출문자/와일드카드가 존재하는 모든 문자열 체계가 그렇듯이 그 탈출문자 자체는 어찌 표현하느냐가 또 문제가 된다. 이를 위해 SQL에서는 A LIKE B 다음에 ESCAPE C라고, '필요한 경우' 탈출문자를 사용자가 지정해 줄 수 있다. 그래서 \%, \_ 이런 식으로 와일드카드 자체를 표현할 수 있다. 탈출문자 자체는 역시 그 탈출문자를 두 번 찍으면 표현 가능.
탈출문자로는 C/C++처럼 역슬래시를 써도 되지만, 다른 걸 지정해 줘도 된다. SQL은 의외로 이런 데에 유도리가 있다. LIKE는 뒤의 ESCAPE와 합쳐져서 삼항 연산자 역할도 한다고 생각하면 되겠다.

다음으로, SQL에서 문자열 상수(리터럴)는 작은따옴표 또는 큰따옴표로 모두 표현 가능하다. 문자열 내부에 작은따옴표가 있으면 큰따옴표로 둘러싸면 되고, 그 반대의 경우를 사용해도 된다. 그런데 고약하게 문자열 내부에 두 종류의 따옴표가 모두 존재한다면 그 따옴표 자체는 따옴표를 두 번 찍어서 표현하면 된다. 이건 LIKE 연산자가 아니라 SQL 파서 자체에서 인식하는 탈출문자이므로 LIKE 연산자가 인식하는 탈출문자와는 성격이 다르다. C/C++로 비유하자면 위상이 \ 탈출문자와 printf % 탈출문자와의 관계와도 같다.

쿼리 내부에서 따옴표 탈출문자의 처리는 매우 철저하게 해야 한다. 안 그러면 이건 SQL injection이라는 보안 취약점이 되기 때문이다. SELECT ... WHERE id='A' 이런 식으로 쿼리를 작성했는데 A 내부에 또 작은따옴표가 존재해서 문자열 상수를 종결해 버리고, 사용자가 입력한 문자열이 쿼리의 실행에 영향을 줄 수 있다면.. WHERE 절을 언제나 true로 만들 수 있고 DB 내용을 몽땅 유출할 수 있기 때문이다. 이런 사건이 대외적으로는 '해킹' 내지 '개인정보 유출'이라고 보도된다.

Posted by 사무엘

2016/12/11 08:38 2016/12/11 08:38
,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1304

« Previous : 1 : ... 110 : 111 : 112 : 113 : 114 : 115 : 116 : 117 : 118 : ... 230 : Next »

블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2026/08   »
            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:
4001634
Today:
1810
Yesterday:
6203