몇 가지 C++ 이야기

1. 람다 함수와 내부 클래스 사이의 관계

본인은 몇 년 전엔 Visual C++ 2010으로 갈아탄 기념으로, <날개셋> 한글 입력기의 소스 코드 내부에서도 한 함수 안에 임시로 사용되던 잡다한 매크로 함수들을 대부분 람다나 템플릿 함수로 바꿨다. 이건 무식한 #define의 사용을 최소화한다는 코딩 컨벤션과 부합하는 리팩터링이었다. #define은 조건부 컴파일이나 ##처럼 정말로 컴파일러의 영역을 초월하여 전처리기의 도움이 필요한 영역에서나 제한적으로 사용할 것이다.

그 당시엔 매크로가 함수 안의 지역변수들을 건드리는 건 대부분 캡처로 기계적으로 치환해서 집어넣었다. 허나 무분별한 캡처 난발도 지금 보니 무척 지저분하게 보인다는 것을 깨닫는 데는 그리 긴 시간이 걸리지 않았다.
스케일만 한 함수 안으로 좁아졌을 뿐, 그 안에서 또 온갖 전역변수들을 덕지덕지 참고하여 결합도가 개판이 된 함수를 보는 듯한 느낌이어서 말이다. 소프트웨어공학 용어로는 common coupling에 해당한다.

여러 람다 함수가 비슷한 지역 변수들을 참조하는 경우, 그건 데이터와 함수를 몽땅 묶어서 함수 내부에 있는 local 클래스 형태로 떼어 내 버렸다. 어차피 람다의 캡처가 내부적으로는 그렇게 가상의 클래스 멤버 형태로 구현되니까 말이다.
그런데 클래스의 멤버 함수 내부에 있는 람다 함수가 지역 변수들도 참조하고 this 포인터까지 참조하는 경우.. 이건 좀 소속을 어디에다 둘지 좀 난감하긴 했다.

Visual C++ 2012부터는 드디어 캡처가 없는 람다에 한해 일반 함수 포인터로 typecast도 가능해졌다.

2. 템플릿 인자의 성격 분류

다음으로 C++ 템플릿 얘기를 하겠다.
지금까지 템플릿을 매크로 함수의 대체 용도로 사용했지만 한편으로는 이것을 공용체의 대체품으로도 잘 활용하고 있다.
예를 들어 클립보드의 내용을 type-safe하게 액세스 해 주는 클래스를 이런 식으로 사용한다.

CClipboard<WCHAR> cb(CF_UNICODETEXT);
CClipboard<DROPFILES> cb(CF_DROP);

예전에는 CClipboard라는 한 클래스 안에
union { PCWSTR pszText; DROPFILES *hDropFiles; };

라고 union을 쓰던 것을 typename T *pData 요렇게 바꾼 것이다.
어차피 한 클립보드 포맷 하에서 다양한 타입을 섞어서 쓸 일은 없으므로, 꼭 공용체가 필요한 상황이 아니기 때문이다.

그리고 템플릿 인자의 명칭을 용도별로 통일하고 있다. 크게 다음과 같은 네 가지가 있더라.

(1) 저것처럼 일반적인 임의의 type은 T이다.
(2) 참조 타입은 R이다. 이건 T 자체는 아니지만 call by value로도 전달할 수 있을 정도로 가볍고 T의 값을 온전히 나타낼 수 있는 key이다. T의 생성자에 단독 인자로 들어오는 게 가능하다.
가령, T가 String이라면 R은 const char*가 될 수 있다. 혹은 그냥 복사 생성자 격인 const T&도 상관없고.
R은 컨테이너 클래스다 추가하거나 검색하는 값을 지정할 때 쓰일 수 있다.

(3) 그리고 정수 인자는 N. 클래스에 소속된 static 배열의 원소 개수나 enum 값을 정의할 때 쓰인다.
(4) 끝으로, 람다든 functor든 함수 포인터든.. 어쨌든 실행 코드가 들어가는 부분은 O이다. F를 쓸까 하다가 기분 탓에 O가 됐다.

template<typename T, size_t N> class CStaticArray { T m_dat[N]; };
template<typename T, typename R=const T&> class CLinkedList { };
template<typename O> void EnumData(O func);

같은 식이다. 이것 말고 템플릿 인자의 용도는 또 뭐가 있을지 궁금하다.

다만, C++이 언어 문법 차원에서 템플릿 인자를 명백히 구분하는 건 정수형(N) 아니면 typename(T, R, O) 이렇게 둘뿐이다. 그러니 완전히 귀에 걸면 귀걸이이고 코에 걸면 코걸이이다. 템플릿 코드 자체만으로는 컴파일러가 문법 오류를 잡을 수 없으며, 그 템플릿에다 실제로 인자를 줘서 타입을 인스턴스화한 뒤에야 문제를 발견할 수 있다. 이것보다는 쓰임이 제한적이더라도, 좀 덜 와일드하고 통제 가능하고 쓰임을 예측 가능한 템플릿을 만들 수는 없나 싶은데 아직까지는 갈 길이 요원해 보인다.

아, 그나저나 템플릿 선언은 오로지 global scope 안에서만 가능하다. 한 함수 안에 있는 지역 클래스는 템플릿 형태로 선언될 수 없으며 일부 멤버 함수만이 템플릿 형태로 선언되는 것조차도 불가능하다.
람다 함수도 템플릿 형태로 선언될 수 없다. 미래의 C++ 문법에서는 이 정도 한계는 없어질 수도 있지 않겠나 하는 생각이 든다.

3. 전처리기에서 sizeof 연산자 떡밥

전처리기의 #if 조건식에서는 어지간한 정수 연산이 가능한 반면, 잘 알다시피 sizeof 연산자는 사용할 수 없다. sizeof의 값에 따라 곧장 조건부 컴파일이 가능하면 참 좋겠지만 그건 순리에 어긋나는 일이다. 컴파일러에게 넘겨 줄 코드를 전처리 하는 중인데, 임의의 구조체의 크기처럼 소스 코드를 컴파일해야만 값을 구할 수 있는 연산자를 전처리기에다가 요구할 수는 없기 때문이다.

과거에 C++에서 컴파일러와 링커 사이의 순리를 거스르는 시도를 하다 템플릿 export 흑역사가 발생했듯, sizeof도 전처리기와 컴파일러 사이에서 "닭이 먼저냐 계란이 먼저냐" 싸움이 될 수 있다.
전처리기를 이용한 조건부 컴파일러는 소스 코드의 이식성에 도움이 되는 기능인데, 정작 기계 종속적인 이식성 관련 정보를 무작정 표준 매크로 심벌로 집어넣을 수는 없다니 이거 참 역설이 아닐 수 없다.

물론 int나 void*의 크기 정도야 전처리기에다가 곧장 박아 넣을 수도 있다. 그러나 근본적으로 전처리기는 코드의 의미나 타겟 플랫폼 따위는 전혀 모른 채 시종일관 lexical한 치환만 하면서 완전 동일하게 동작하는 물건이며, 포인터 같은 기계 종속적인 사항에 대해서는 완전히 아오안이어야 한다.

이런 이유로 인해 포인터의 크기나 비트 endian-ness 같은 타겟 플랫폼 정보는 있으면 유용할 것 같음에도 불구하고 표준 predefined 매크로로 제공되지 않는다. 조건부 컴파일을 위해 그런 정보가 필요하다면 필요한 사람이 해당 매크로 심벌을 별도의 헤더나 컴파일러 옵션을 통해 지정해 놔야 한다. 전처리기가 월권 행위를 저지르지 않아도 되게 해 주기 위해서이다.

다만, 전처리기가 아닌 컴파일러의 입장에서는 sizeof는 컴파일 타임 때 값이 결정되는 아주 static하고 깔끔한 연산자이다. sizeof의 피연산자로는 타입 이름과 값이 모두 올 수 있는데, 값은 '직접 실행이 되지 않는다'. 쉽게 말해 sizeof( *((int *)NULL) )을 해도 뻑이 안 난다는 뜻.
그렇기 때문에 sizeof의 값은 static 배열의 크기를 결정하는 데 쓰일 수 있고, 또 case 문이나 템플릿의 정수값 인자에도 얼마든지 들어갈 수 있다.

Posted by 사무엘

2015/08/31 08:39 2015/08/31 08:39
,
Response
No Trackback , 6 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1133

Windows 10 사용기 외

1. Windows 10

그 이름도 유명한 Windows 10을 본인도 드디어 입수했다. Mac OS와 Windows 모두 10을 최종 메이저 버전으로 설정했다는 게 무척 흥미롭다.

개인 컴은 물론이고 회사 컴도 평소에 활발하게 쓰던 물건은 "Windows 10 다운로드 준비가 끝났는데 설치할까요?"가 진작부터 뜬 반면,
평소에 잘 안 쓰고 "여기에나 한번 10을 깔아 볼까?"라고 정작 비워 놨던 컴은 한참을 기다려도 Windows 10 설치 말이 없었다. "준비되면 알려 주세요"를 몇 번이나 클릭했는데도 감감 무소식. 이것도 평소에 로그인 횟수나 네트워크 트래픽을 감안해서 업데이트 순서를 짠 건지는 모르겠다.

참다못해 Windows 10 설치 프로그램을 직접 다운로드 해서 실행한 뒤에야 운영체제를 7에서 10으로 바꿀 수 있었다. 다운로드와 설치는 물론 금방 끝난 건 아니지만, 그렇다고 Windows 8.1을 설치하던 시절보다 터무니없이 오래 걸리지는 않았다.

설치 직후에는 바탕 화면의 personalize가 되지 않았다. 인증이 필요하대서.. "으잉? Windows 10은 완전 무료 아니었나? 자동 업데이트를 안 하고 수동 설치를 해서 그렇나?" 어리둥절했는데 재부팅을 한번 하고 나니 인증은 저절로 패스 됐고, 까맣던 배경 그림은 10을 설치하기 전의 모습으로 되돌아왔다.

비주얼에 대한 소감은..
프로그램의 제목이 가운데가 아니라 왼쪽 정렬로 돌아왔으며, 글씨 크기도 약간 더 크다가 다시 메뉴 글씨 크기와 동일하게 돌아왔다. 또한 화면을 부분만 차지하는 시작 메뉴가 되돌아왔으며 프로그램 창 주변에 그림자 그러데이션이 생긴 것은 일면 과거의 Windows Vista/7 시절로의 회귀를 떠올리게 한다.
창의 최소/최대화와 닫기 버튼이 굉장히 커지긴 했는데 정작 _ □ X 모양은 너무 대충 그려 넣은 듯. -_-;;

명령 프롬프트가 가로폭 조절이 가능해진 것이 무척 인상적이다. 하지만 글꼴은 여전히 제대로 customize가 안 된다. 하다못해 먼 옛날 9x 시절에는 코드 페이지가 무엇이냐에 따라 Courier New나 Lucida Console이라도 볼 수 있었는데 너무 칙칙한 글꼴은 오히려 NT 계열이 9x보다도 퇴보했다.

운영체제를 업데이트 한 직후, 이전까지 사용하던 프로그램들은 10에서도 거의 그대로 곧장 사용이 가능했다. 그러나 <날개셋> 한글 입력기는 편집기만 남아 있고 외부 모듈은 운영체제의 IME 목록에서 사라졌다.
허나 이건 프로그램을 재설치만 하면 해결되니 그리 심각한 문제는 아니다. 또한 지금까지 파악한 바로는 Windows 10은 문자 입력에 관해서 딱히 기술적으로 크게 바뀐 게 보이지 않는다. 심지어는 세벌식 파워업도 10의 MS 한글 IME를 대상으로 잘 동작한다.

2. <날개셋> 한글 입력기에서 발견된 문제

다만, <날개셋> 한글 입력기가 Windows 10에 대비하여 개선해야 할 사항은 크게 두 가지가 발견됐다.

(1) 첫째, Windows 10은 Metro 앱도 데스크톱에서 뭔가 일체형으로 연계하며 동작하게 된 듯하다. Windows 10의 상징이라 해도 과언이 아닌 엣지(Edge) 브라우저도 기술적으로는 Metro 앱이다. Metro 모드에서는 클래식 데스크톱 GUI를 사용하는 기능들이 동작하지 않는다. 고로 <날개셋> 제어판이나 About 대화상자 같은 것도 못 띄운다. (그걸 시도하면 그냥 씹히거나 프로그램 전체가 그냥 튕긴다)
8 시절에는 메트로 앱들은 데스크톱의 입력 도구모음줄에 접근을 할 수 없으니 신경 쓸 필요가 없었는데, 이제는 메트로 모드에서는 이런 기능들을 건드릴 수 없도록 도구모음줄 버튼이나 메뉴를 흐리게 처리해 줘야겠다.

날개셋 말고 다른 IME들은 이 문제를 어떻게 해결했느냐 하면 한글 IME는 아예 대화상자가 출력되는 명령들을 모조리 없애 버렸다. 심지어 8에는 있던 About 명령조차도 없앴다. 일본/중국어 IME는 GUI가 나오는 기능들은 몽땅 다른 프로그램을 통해 실행되게 되어 있다.

사실, 한글 IME도 부수/획수 한자나 필기 인식 같은 보조 입력 도구는 별도의 프로그램을 통해 구동한다. 그러나 내 프로그램은 이런 것들이 다 in-process 형태로 구동된다. 그래서 Metro 모드 같은 데서는 여러 제약이 걸리는 것 같다. 금방 해결 가능한 문제는 아닌 것 같고 프로그램 구조를 앞으로 어떻게 유지할지 생각을 해 봐야겠다.
참고로, 엣지 브라우저 자체는 크롬의 마소 버전이라고 생각해도 좋을 정도로 "빠르고" 산뜻해서 느낌이 굉장히 좋았다. 그거 하나는 인정한다.

(2) 둘째, 엣지에서 페이스북에 접속해서 댓글을 써 보니, 한참 글자를 입력하다가 비한글 문자를 입력하는 순간, 예전에 입력 중이던 글자가 몽땅 사라지는 문제가 있었다. "가을에." -> "에."만 남는 식.
또한 명령 프롬프트에서도 내 프로그램으로 한글을 입력해 보면, 조합하던 글자가 덮어써진다. "가" "가을" "가을에"가 되는 게 아니라 "가" "을" "에". 신기하게도 MS IME로 한글을 좀 입력하다가 다시 날개셋으로 돌아오면 명령 프롬프트는 이 문제가 없어짐.

100% 재연 가능하고, MS IME는 안 그런데 내 프로그램만 그렇고.. 현상 자체는 완벽하게 파악이 됐지만 이건 또 MS가 무슨 농간을 부린 건지 허무한 생각이 든다. 아마 둘 다 같은 원인 때문일 거라고 추측만 하는데.. 제일 골치 아픈 부류의 문제이며 해결이 쉽지 않을 수도 있다.

3. key의 인식 방법과 관련된 customization

이건 Windows 10과는 관계가 없는 다른 얘기이다.
<날개셋> 한글 입력기는 한글을 생성하기에 앞서서 key 입력을 인식하는 방식 자체를 사용자화 가능하다. 크게 다음과 같은 시나리오가 있다.

(1) 원래는 먹던 특정 key를 먹지 않게 할 수 있다: 표준 두벌식의 경우 오로지 A~Z 26개 key만 사용하기 때문에 나머지 숫자와 기호는 따로 IME가 가로채는 게 아니라 그냥 응용 프로그램으로 넘겨서 숫자나 기호를 입력하게 한다.

(2) 원래는 먹지 않던 특정 key를 IME 입력으로 바꿀 수 있다: space의 경우, 굳이 IME가 처리할 필요가 없는 key이다. 하지만 인디자인 버그 같은 걸 해결하기 위해서 IME가 굳이 가로채어 공백을 되돌리게 할 수 있다.

(3) 특정 key를 IME 입력 대신 다른 key로 바꿀 수 있다: 내 프로그램으로 드보락 글쇠배열을 사용하는 경우, 보통은 드보락 방식대로 IME가 알파벳을 보내게 한다. 하지만 글쇠 누름 날개셋문자를 사용하면 아예 해당 알파벳 key 입력을 응용 프로그램에다가 보내게 할 수도 있다.

쉽게 말해 비한글 알파벳이나 공백 같은 문자를 IME 방식으로 보내느냐, 그렇지 않고 더 원초적인 key 메시지 형식으로 보내느냐를 제어할 수 있다는 뜻이다.
단축키 같은 것은 key 입력 메시지 형태로 온 것만 인식되지 IME 방식으로 전달된 문자로는 인식되지 않는 경우가 많기 때문이다. MS Excel의 경우, 영문을 key 입력 형태로 입력했을 때에만 함수의 목록 자동 완성이 처리되지만 IME 방식으로 입력된 것에는 반응하지 않는다.

(1)과 (2)는 6.5 버전에서 기본 입력 스키마에 해당 기능이 추가되고 구현됐다. 한편, '글쇠 누름' 날개셋문자는 비교적 최근인 7.9 버전에서 추가되었다.
현재 개발 중인 8.2 버전은 (3)이 소개하는 '글쇠 누름' 날개셋문자의 사용을 홍보하기 위해, 제어판의 글쇠배열 편집 창에서 숫자/문자를 입력하는 '일반 문자' 날개셋문자를 그에 상응하는 '글쇠 누름' 날개셋문자로 자동으로 모두 바꾸는 기능을 추가했다.

그리고 더 간단히, 사용자가 누른 key에 해당하는 글쇠 누름 날개셋문자를 자동으로 입력시키는 모드도 추가했다. 예전에는 세벌식 한글, 두벌식 한글, 이동, 영문 알파벳 같은 모드가 있었는데 그런 것처럼 새로운 모드를 넣었다는 뜻이다. 아주 자연스럽게 기능 확장이 가능하다.

또한 (2)를 더 쉽게 설정할 수 있게 하기 위해, 단축글쇠를 입력받는 대화상자에다가는 지금 누른 가상 키코드의 문자값에 해당하는 '일반 문자' 날개셋문자를 자동으로 배당하는 기능도 추가했다.
알파벳이나 숫자를 key 입력으로 보내는 것과 IME 문자열로 보내는 것의 차이에 대해서는 도움말에다가도 더 자세한 설명을 넣을 예정이다.

4. 고기 먹고 싶음 -_-

아직 어쩔 수 없는 여름이긴 하지만 그래도 날씨가 예전보다 덜 더워져서 무척 좋다.
난 어릴 때부터 왜 태양이 긍정적인 심상이고 그늘이 부정적인 심상인지를 이해를 못 했을 정도로 몸에 열이 많고 더위를 싫어했다.

이제 좀 가을이 되고 나면 날씨 탓할 일 없이 더 집중해서 코딩을 진행하고 싶은데.. 프로그램 완성의 꿈은 요원하기만 하다.
도깨비불 현상 없는 입력 방식이 심리적으로 무척 도움이 된다. 세벌식은 없어질래야 없어질 수 없고 없어져서는 안 되는 한글 입력 방식이다. 뭐 그건 그렇고..

사용자 삽입 이미지
나만 저렇게 생각하는 게 아니었구나.
돼지는 훌륭한 단백질 공급원이고, 단백질은 훌륭한 스트레스 해소원이다.

사용자 삽입 이미지
그래서 위대한 령도자 세종대왕도 약을 빨거나 한 건 아니고 고기를 드시면서 한글을 창제하시였다. 한글이 없었으면 내 프로그램도 없었겠지.
(다만, 그 덕분에 저분은 세자 시절부터 굉장한 비만이었으며, 당뇨 같은 성인병도 평생 달고 살았다고 함. 본인도 키에 비해서는 좀 과체중인 상태..;;)

Posted by 사무엘

2015/08/28 08:31 2015/08/28 08:31
, , ,
Response
No Trackback , a comment
RSS :
http://moogi.new21.org/tc/rss/response/1132

C++의 클래스에는 여느 객체지향 언어와 마찬가지로, 정적 바인딩이 아닌 실행 시간 동적 바인딩을 거쳐 호출되는 가상 함수라는 개념이 있다.
일반 함수들은 선언만 해 놓고 정의를 안 했다면, 그 함수를 실제로 호출한 곳이 있을 때만 링크가 시도되고 링크 에러가 난다. (물론 DLL을 만들 때처럼 외부로 export된다고 명시된 함수라면, 우리 모듈 내부에서 당장 안 쓰이더라도 당연히 몸체가 존재해야 함. 이건 논외로 한다.)

이런 일반 함수와는 달리 가상 함수는 해당 클래스에 속하는 개체가 생성되어 가상 함수 테이블이 만들어질 때 이미 참조가 발생한다. 그렇기 때문에 일단 선언을 한 이상, 코드 내부에서 직접적으로 호출을 안 하더라도 반드시 정의가 돼 있어야 한다. 그래야 링크 에러가 안 난다.
가상 함수이지만 자기 몸체가 존재하지 않아도 되는 예외적인 경우는 단 하나, 순수 가상 함수뿐이다.

순수 가상 함수가 존재하는 클래스는 반쯤은 불완전한 타입과 비슷하다. 불완전하기 때문에 그 클래스의 인스턴스를 직접 만들 수 없다. 누군가가 얘를 상속하여 파생 클래스를 만든 뒤, 모든 순수 가상 함수들에 대한 몸체를 구현해 줘야만 한다. 몸체를 안 만들고 선언만 해 놓으면 컴파일은 되지만 그제서야 링크 에러가 나게 된다. 즉, 순수 가상 함수는 가상 함수의 링크와 관련된 문제를 파생 클래스로 보류하는 역할을 한다.

순수 가상 함수가 들어있는 추상 클래스의 인스턴스를 직접 만드는 것은 컴파일러에 의해 원천 봉쇄되고 금지되어 있다. 그런데 아주 특수한 상황에서는 이렇게 몸체가 없는 추상 클래스에 대한 직접 호출이 일어날 때가 있다. 예를 들어 아래와 같은 경우이다.

class CDerived;
class CBase {
    CDerived *m_pt;
public:
    CBase(CDerived *p);
    virtual void Pure() = 0;
};

class CDerived: public CBase {
public:
    CDerived(): CBase(this) {}
    void Pure() { puts("Ahh"); }
};

CBase::CBase(CDerived *p): m_pt(p) { p->Pure(); }

int main() { CDerived obj; }

생성자에서 자기 자신이 아직 완전히 초기화되지 않은 상태일 때 Pure()을 호출해 버리니, 마치 열차가 탈선한 것처럼, 지뢰찾기에서 지뢰를 밟은 것처럼 허를 찔리는 것이다.
사실 forward 선언까지 동원해 가면서 굳이 m_pt를 CDerived의 포인터로 지정하지 않아도 된다. 그냥 기반 클래스와 동일한 CBase *로 지정해도 에러가 나는 건 마찬가지이다.

참고로, CBase의 생성자에서 저렇게 번거롭게 p->Pure()을 하지 않고 그냥 this에 대해서 Pure()을 호출하려 시도하면 그건 순수 가상 함수임에도 불구하고 동적 바인딩이 아니라 정적 바인딩이 일어난다. CBase에 Pure 함수가 정의돼 있지 않다고 링크 에러가 난다. 그걸 별도의 포인터 값으로 억지로 우회함으로써 아직 몸체가 없는 순수 가상 함수에 도달할 수 있다. 위의 코드는 굉장히 인위적인 예이지만, 멀티스레드 race condition 같은 데서도 드물게 이런 일이 벌어질 수 있다.

C#은 생성자에서도 동적 바인딩이 잘 처리되는 게 보장되는 반면, C++은 옛날에 만들어지기도 해서 성능 보전을 위해서인지 설계 관점이 다소 보수적이다. 기반 클래스의 생성자는 자신이 파생 클래스의 생성 과정의 일부라 하더라도 파생 클래스에 대한 단서를 전혀 얻을 수 없고 언제나 기반 클래스의 형태로만 동작한다.
여러 모로 생성자에서 가상 함수를 호출하는 건 좋지 않다는 걸 알 수 있다. DllMain 안에서 또 DLL을 로딩하지 말고 가능한 한 거기서는 몸을 사리고 있어야 하는 것과 비슷한 맥락이다.

초기화가 되기 전에 가상 함수 테이블이 가리키는 주소값이 NULL이었다면, 그냥 더 생각할 것도 없이 운영체제 차원에서 '잘못된 연산' 에러가 나고 그걸로 끝이 날 것이다. 그러나 그렇게만 하는 건 재미(?)가 없고 데이터를 조작하다가 발생한 여느 null pointer 에러와 구분도 어려우니 컴파일러는 가상 함수 테이블에다가 순수 가상 함수에 대한 디폴트 핸들러의 주소를 넣어 준다. 주소를 한 번만 넣어 주면 끝이니 딱히 성능에 영향을 받을 일이 없고, 따라서 debug뿐만 아니라 release 빌드에도 못 넣을 이유가 없다.

개체가 정상적으로 초기화됐다면 그 주소는 사용자가 작성한 파생 클래스의 함수로 당연히 대체된다. 그러나 그게 아직 바뀌지 않았다면 핸들러 함수로 간다. 얘는 assert(0)과 비슷한 급의 예외를 발생시키는 것 말고 딱히 하는 일은 없다. 다만, 이 일이 벌어졌을 때 사용자가 지정해 준 함수가 호출되게 할 수는 있다.

가상 함수가 몸체가 온전하지 않으면 컴파일 에러(순수 가상 함수에 대한 클래스 선언)와 링크 에러(...)뿐만 아니라 이렇게 런타임 에러까지 드물게나마 발생할 수 있다는 걸 알 수 있다. 런타임 에러라니 왠지 C++답지 않아 보이지만, 이건 그래도 운영체제나 컴퓨터 차원으로 갈 런타임 에러를 언어 시스템 차원에서 수습 가능한 런타임 에러로 바꿔 주는 체계까지는 갖춰져 있다. 배열 첨자 초과나 division by 0 에러는 전자로 바꾸려면 매번 값을 사전에 점검해야 하기 때문에 오버헤드가 큰 반면, 순수 가상 함수 호출은 그냥 디폴트 함수 주소만 설정해 주면 되니까 말이다.

Posted by 사무엘

2015/08/26 08:34 2015/08/26 08:34
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1131

1. 평행사변형의 넓이, 평행육면체의 부피

2*2 크기의
(a b)
(c d)

행렬이 있을 때, 이 행렬의 행렬식이라고 불리는 D 값은 a*d - b*c로 정의된다. ax+by = 얼마, cx+dy = 얼마 요런 방정식의 근을 구하는 식을 세워 보면 행렬식은 x, y의 분모에 들어가 있다. 그러니 이 값이 0이면 근은 부정이나 불능으로 빠지게 된다.

한편, 행렬식에는 기하학적인 의미가 있다. 원점에서 (a,b)를 잇는 선분이 가로변, 원점에서 (c,d)를 잇는 선분이 세로변인 평행사변형의 넓이를 나타내기 때문이다.

그도 그럴 것이 이 행렬은 한 변의 길이가 1인 (0,0), (1,0), (1,1), (0,1)이라는 정사각형을 (a,b), (a+c, b+d), (c,d)라는 평행사변형으로 옮긴다. (a+b)*(c+d)라는 전체 직사각형에다가 주변의 합동인 삼각형 두 쌍의 넓이를 빼면, 평행사변형의 넓이로 남는 것은 ad-bc뿐이다. 아래 그림을 보시라.

(a+c)(b+d) - b(a+c) - c(b+d) = d(a+c) - c(b+d) = ad + cd - bc - cd = ad-bc

사용자 삽입 이미지

이 평행사변형에서 대각선을 구성하는 (a,b)와 (c,d)를 연결하면 사각형을 반으로 쪼갤 수 있다. 다시 말해 원점과 (a,b), (c,d)를 꼭지점으로 하는 삼각형의 넓이는 ad-bc의 절반이 된다.

다음으로..
저렇게 두 점 A(ax, ay)와 B(bx, by)가 있을 때, A-원점-B 자취의 방향을 판단하는 공식이 있다(시계 방향인지 반시계 방향인지). bx*ay - by*ax이며, 여기에는 배후에 삼각함수 sin( alpha-beta )가 숨어 있다.

그런데 위의 두 점 (a,b), (c,d)도 코싸인/싸인 alpha와 코싸인/싸인 beta라는 극좌표 형태로 표현하면, 행렬식 a*d-b*c 역시 결국은 sin( alpha-beta )과 패턴이 동일함을 발견할 수 있다. 두 쌍의 숫자를 각각 서로 엇갈리게 곱해서 빼는 것 말이다.
이렇듯, 행렬식에 두 벡터의 사잇각에 대한 삼각함수 값이 들어있으니, 벡터의 길이만 정규화하면 각도를 구할 수 있다. 또한 두 변의 길이와 그 사이의 끼인각을 알고 있는 삼각형의 넓이는 A*B*sin(theta)/2로 간단하게 결정된다.

그리고 이 식을 확장하면 삼각형뿐만이 아니라 여러 삼각형들로 분해 가능한 단순다각형(선분들이 서로 만나지만 않으며 볼록하거나 오목할 수 있음) 넓이 내지 폴리곤 패스 방향을 구할 수도 있다. 넓이와 방향(넓이의 부호)이 같이 구해진다.

2차원에서는 이 정도로 분석이 되고, 3차원으로 가면 어떨까?
짐작했겠지만 3*3 행렬의 행렬식은 그 행렬을 구성하는 3개의 벡터들을 축 내지 기저로 삼는 평행육면체의 부피와 같다. 직교좌표에서 모든 점들의 최대치에 해당하는 직육면체의 부피에다가 또 모서리 주변의 온갖 사면체들의 부피를 빼야 하니 식이 굉장히 복잡할 것이다. 3*3 행렬의 행렬식이 항이 6개나 되고 2*2의 것보다 훨씬 더 복잡한 것은 이 때문이다.

하지만 3차원에서도 언제나 부피만 구하는 건 아니다.
차원만 2차원이 아닌 3차원으로 확장한 뒤, 원점에서 출발하는 두 벡터를 가로변 세로변 축으로 삼는 평행사변형의 넓이는 어떻게 구하면 좋을까? (삼각형의 넓이도 당연히 자동으로 포함) 즉, 3차원 공간 안에서의 2차원 평면인 것이다. 이건 2*2 행렬식보다는 어렵겠지만 3*3 행렬식보다는 쉬울 것이다.

그리고 이것이 바로 벡터의 '외적'(벡터곱) 연산이 하는 일이다. 아마 고등학교에서는 내적까지만 하지 외적은 안 배우지 싶다. 단, 내적부터 먼저 개념을 좀 복습해 보자.

2. 벡터의 내적

왜 각 성분을 차례대로 곱한 것을 더하면 내적이 나오는 걸까?
이 원리의 배후에는 코싸인 제2법칙이 있다.
아까 두 변의 길이(두 선분의 길이를 각각 A와 B라 하자)와 그 사이의 끼인각을 아는 삼각형의 넓이를 구했는데, 이 경우 삼각형이 유일하게 결정되었으므로 나머지 한 변의 '길이' C도 당연히 구할 수 있다. 그 삼각형의 모든 특성이 파악 가능한 것이다. C^2 = A^2 + B^2 - 2*A*B*cos(theta) 이다.

이건 피타고라스의 정리의 일반적인 경우이며, 증명하는 방법이 상당히 많다. 여기에서는 제일 직관적인 해석학적 방법 하나만 소개하고 넘어가겠다.
선분 A와 B가 원점을 지나고 선분 A는 x 축에 평행하다고 한다면 선분 A는 (0,0)과 (a,0)을 지나게 될 것이며, 0도인 선분 A로부터 theta도만치 떨어진 선분 B는 선분 B는 (0,0)과 (b*cos(theta), b*sin(theta))를 지난다. 임의의 차원의 임의의 위치에 있는 어떤 삼각형 ABC라도 변환을 통해 그렇게 2차원 평면에서의 일반화가 가능하기 때문이다.

그럼 선분 C의 길이는 저 (a,0)과 (b*cos(theta), b*sin(theta)) 사이의 거리와 같다. 그러므로 길이의 제곱은 (a - b*cos(theta)^2 + (b*sin(theta))^2 가 된다.
이 식을 풀면 a^2 - 2*a*b*cos(theta) + b^2*cos(theta)^2 + b^2*sin(theta)^2 가 된다.
b^2항은 cos(theta)^2 + sin(theta)^2 이므로 1로 약분돼 없어지고, 결국 코싸인 제2제곱 공식이 고스란히 나온다.

그럼, A, B, C를 이제 벡터라고 생각하고 2차원이 아니라 각 축별 좌표를 코싸인 제2제곱 공식에다 대입해 보자.
A=(a1,...,an), B=(b1,...,bn) 같은 식이다. C는 두 벡터의 차이 A-B와 같다.
벡터의 절대값의 제곱은 잘 알다시피 거리의 제곱과 같기 때문에 각 성분들의 제곱을 모두 더한 것과 같다. 그러므로

∑ [i=1..n] (ai^2 + bi^2 - 2*ai*bi) = ( ∑ [i=1..n] (ai^2 + bi^2) ) - 2*A*B*cos(theta) 로 식이 대충 떨어진다.

A와 B에서 각 성분들의 제곱을 합을 구하는 부분은 좌우변 공통이므로 소거되고.. 남는 것은
2*A*B*cos(theta) = ∑ [i=1..n] 2*ai*bi 이다. 여기서 양변을 2로 나눠 주면 내적 공식이 아주 깔끔하게 유도된다. 콜?

벡터의 내적은 그냥 숫자 하나(스칼라)만으로 답이 떨어지며, 벡터의 각 성분들을 차례대로 곱해서 더하기만 하면 된다. 내적에는 두 벡터의 사이각의 '코싸인' 값이 들어있기 때문에, 두 벡터가 서로 수직인지를 벡터의 길이와 무관하게(= 정규화 안 하고도) 간편하게 판별할 수 있다. 코싸인 90도는 0이므로!

내적은 계산이 딱히 어렵지 않을 뿐만 아니라, 2차원이고 3차원이고 어느 차원이든지간에 계산법이 동일하다는 것도 큰 장점이다. 두 벡터의 사이각을 구하는 용도로는 완전 딱이다. cos(alpha-beta) = cos(alpha) cos(beta) + sin(alpha) sin(beta) 인 것에도 2차원일 때의 내적 공식이 숨어 있다는 걸 발견할 수 있다.
또한, 생긴 모양 덕분에 벡터의 내적을 행벡터(행이 하나. 수평선-_-)와 열벡터(열이 하나. 수직선)의 곱으로 표기하기도 한다. (행과 열뿐만이 아니라 횡과 종도 어느 게 어느 건지 종종 헷갈릴 때가 있다만..;;)

3. 벡터의 외적

그에 반해 외적은 결과값도 벡터이다. 그리고 3차원일 때에만 정의된다. 계산값의 각 차원과 피연산자들이 일대일로 딱 밀착해 있는 관계로 3차원 말고는 선택의 여지가 없기 때문이다.

성분이 (a1,a2,a3)인 벡터 A와, 성분이 (b1,b2,b3)인 벡터 B의 외적은
(a2*b3-a3*b2, a3*b1-a1*b3, a1*b2-a2*b1)이라고 정의된다.
어 그런데 이거, 두 쌍의 숫자를 각각 서로 엇갈리게 곱해서 빼는 게 2*2 행렬식을 구하는 것과 비슷해 보인다. 맞다.
그래서 벡터 A, B가 동일 평면상에 있어서 a3와 b3 같은 게 동시에 0이기라도 하면, 해당 변수가 포함된 항은 모두 소거된다. 이 경우 외적은 그냥 2*2 행렬식과 동일해진다.

또 생각할 점은.. 3*3 행렬식을 구하는 것도 특정 row와 col을 제외한 2*2 행렬식을 구하는 것의 연속이라는 점이다. 그래서 외적 구하는 공식을
(i  j  k )
(a1 a2 a3)
(b1 b2 b3)

의 행렬식이라고 표현하기도 한다. 물론 여기서 i~k는 스칼라값이 아니라 각각 (1,0,0), (0,1,0), (0,0,1)에 해당하는 단위벡터이다. 그러니 스칼라와 벡터가 뒤섞여 있는 저 행렬은 대수적인 의미는 딱히 없다. 외적 구하는 공식을 좀 더 뽀대나게 표현하는 용도로만 쓰이는 셔틀일 뿐이다. 그래도 결국은 3*3 행렬식과 닮긴 닮았다.

행렬식을 구하는 공식에서 j에 해당하는 부분은 더하는 게 아니라 뺀다. 그렇기 때문에 외적 공식에서는 1,3이 아니라 3,1 순서로 쓴 뒤에 더하는 것으로.. 다시 말해 양수를 빼는 게 아니라 음수를 더한다고 표현을 달리 했다. 둘 다 동일한 의미이므로 부호에 주의하기 바란다.

벡터는 스칼라와는 달리 '크기'뿐만 아니라 '방향'이라는 정보가 추가로 들어있다.
외적 연산을 통해 구해진 벡터는 일단 크기는 두 벡터의 크기의 곱에다가 사잇각의 sin값을 곱한 것과 같다. 그러므로 3차원 공간에서 두 3차원 벡터가 만드는 평행사변형/삼각형의 넓이를 구할 수 있다.

외적 식을 전개해서 크기의 제곱을 해 보면, 각각의 두 벡터의 크기의 제곱을 곱하고 거기에다 벡터의 내적값(양 벡터의 각 성분들을 서로 곱해서 더함)의 제곱을 뺀 것과 같다고 식이 전개된다. A^2 - B^2 꼴이 되기 때문에 (A+B)(A-B)로 인수분해를 하고 싶은 충동이 느껴지지만 여기서는 식을 다른 형태로 바꿔야 된다.

내적에는 역시 두 벡터의 크기의 곱에다가 사잇각의 cos이 들어 있으니 이것의 제곱이라면 두 항이 결국 |A|^2 * |B|^2를 공통으로 갖고 있고 (1  - cos^2 )가 남는다. 그리고 이것이 sin^2과 같다는 건 두 말하면 잔소리이고.

외적의 크기에 벌써 이렇게 유용한 정보가 들어있는데, 방향은 더욱 흥미롭다.
짐작하겠지만 두 벡터의 외적의 방향은 두 벡터와 수직이다. 물론 위쪽도 수직이고 아래쪽도 수직인데, 해당 좌표계의 동일 부호가 향하는 쪽으로 방향이 결정된다. 두 기저 벡터에 대한 외적을 구하면 나머지 기저 벡터가 튀어나온다.

애초에 두 벡터의 외적은 그 두 벡터와의 내적이 모두 0인 벡터 중에 크기가 저렇게 AB sin(theta)로 나오는 놈을 구한 것이다. a1*c1 + a2*c2 + a3*c3 = 0과 b1*c1 + b2*c2 + b3*c3 = 0을 만족하는 (c1,c2,c3)을 직접 구해 보면 안다. 저것만으로는 식보다 미지수 개수가 더 많으니 (c1,c2,c3)가 하나로 딱 떨어지지 않고 c1과 c2가 c3의 배수인 것처럼 관계식만 나온다. 그런데 c3의 특정값일 때 c1,c2에 있던 분모가 싹 소거되고 c1~c3이 저렇게 아주 대칭적이고 깔끔하게 나오는데 그게 바로 외적값이다.

이런 유용함 때문에 외적이 3차원에서의 전유물이라고 여겨지는 것이다. 이항연산에 딱 최적화돼 있지 않은가.
물론, 외적은 수직이라는 게 위아래가 모두 존재한다는 특성상 교환 법칙이 성립하지 않고 A×B=-B×A이다. 뭐, 4차원 이상에서는 두 벡터와의 내적이 모두 0인 벡터는 3차원일 때처럼 일직선상의 형태로 유일하게 떨어지지가 않는다. 그러니 외적과 같은 접근 방식이 큰 의미가 없어져 버린다.

끝으로, 3차원에서 벡터의 내적과 외적은 삼중곱이라는 연산을 통해 한데 만난다. 3개의 벡터 A,B,C를 축으로 하는 평행육면체의 부피를 구하고 싶으면 아까처럼 벡터들을 3차원 행렬의 행렬식으로 표현해도 되지만, 밑면에 속하는 두 벡터 A×B의 외적을 구한 뒤 거기에다 C와 내적을 구하면 된다. (A×B)·C! 그게 결과적으로 행렬식을 구한 것과 같은 계산 결과가 도출된다. 왜 그런지는 아까 그 외적 구하는 행렬과 일반 행렬의 행렬식을 늘어놓고, 거기에다가 내적을 구하는 공식까지 적용한 뒤 서로 비교하며 생각해 보면 된다.

Posted by 사무엘

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

한국과 일본은 가까운 이웃 나라이지만 기독교계 종교에 대한 정서는 거의 지구와 금성의 차이만큼이나 극과 극이다. 물론 일본뿐만이 아니라 북한 내지 중국하고 비교해 봐도 극과 극에 가까운 건 마찬가지이지만.

난 솔직히 말해 일본의 보편적인 종교관을 잘 모르겠다. 완전히 불교도 아니고 유교, 도교도 아니고 전적으로 샤머니즘이라고 봐야 하는 건지? 신사는 무엇이고 덴노(일왕/천황)는 무엇이고 이들이 정치 종교 통합적인 존재인지? 어쨌든 기독교 배경이 절대로 아닌 것만은 확실하다. 한국 사람들은 기독교인이 아니라고 해도 딱히 단군을 숭배한다거나 하지는 않는데?

일본은 서양 문물을 잘 받아들이고 근대화를 잘해서 서구 열강의 식민지가 되지 않았으며, 반대로 다른 식민지를 거느리고 침략 전쟁을 일으키기까지 할 수 있었다. 서양 문물을 받아들이는 과정에서 흔히 '기독교'라고 부르는 종교 교리도 당연히 전파되었고 선교사들도 들어왔다. 단, 엄밀히 말하면 기독교 계열은 아니고 스페인의 예수회가 주축이 된 천주교 중심이었다.

16~17세기 사이는 조선에서는 임진왜란이 벌어져서 나라가 작살이 나 있었고, 서양의 영국에서는 엘리자베스 1세, 제임스 1세, 찰스 1세의 순으로 왕이 바뀌고 있었다. 신대륙에서는 버지니아 주 제임스타운, 포카혼타스 이런 일이 벌어지고 있었다.
그런 와중에 스페인에서는 종교 개혁을 저지하고 유럽을 다시 가톨릭화하기 위해 예수회가 만들어졌는데... 이와 비슷한 시기에 일본에 천주교가 전래되었다.

조선도 한때 천주교에 대한 박해가 없지는 않았다. 황 사영 백서 사건 같은 병크 때문에 스스로 매를 번 것도 있었고. 하지만 그 기간이나 규모는 일본의 박해에 비할 바는 못 됐다. 임진왜란을 일으켰던 도요토미 히데요시부터가 극렬 "안티개독"이었으며, 정말 중세 종교 재판을 뺨치는 가학· 변태적인 악랄한 고문과 형벌로 신자들을 괴롭히고 죽이고 박멸했다. 천주교고 기독교고 그딴 건 그 양반이 알 바 아니었을 테고.

다른 때도 아니고 서양에서는 킹 제임스 성경이 나오는 동안 동양에서 저런 일이 벌어졌다는 것이, 천주교 기독교를 떠나서 일단은 안타까운 일이다. 이 시기에 있었던 일들은 일본의 B급 새디스트 사극 영화의 좋은 소재가 되어 왔다. <쇼군의 새디즘>처럼.
사람을 십자가에다 묶어 놓고 산 채로 창으로 옆구리를 찌르기, 미꾸라지가 가득한 어항에다 사람을 옷 벗겨서 집어넣기, 썰물 때 바다 갯벌에다가 십자가 기둥을 꽂고 사람을 거꾸로 묶어 놓기(그 상태로 나중에 밀물이 되면..;;) 이런 건 중세 서양에서는 못 본 장면 같다. -_-;;

사용자 삽입 이미지

또한, 육체적으로 끔찍한 형벌이나 고문은 그렇다 치더라도.. 이런 것도 있었다고 한다.

"그들은 당시의 기독교인을 색출, 고문, 박해하는 형태를 여러 가지로 연구하면서 철두철미하게 기독교 박해를 자행했다. 십자가나 예수나 마리아 상을 새긴 동판이나 목판 위를 밟게 함으로써 기독교인이 아니라는 것을 증명하는 후미에 제도는, 1629년 나가사키에서 시작되어 전국에 걸쳐 오랜 기간 사용되었다."


"어디에 절을 해라", "입으로 믿음을 부인해라", 아니 단순무식하게 "김 일성 개XX 해 봐라" 식의 더 간단한 판별법도 있었을 텐데, 저건 그야말로 성상, 형상을 중요하게 생각하는 천주교 스타일에 최적화된 판별법이라 여겨진다. 나 같았으면 저런 건 걸릴 게 없었을 것이다. 마치 주의 만찬이 끝나고 남은 빵과 포도 주스는 아무렇지도 않게 누가 몽땅 집어먹거나 여느 잔반을 처리하듯이 임의 처분해도 되는 것처럼 말이다. (사실, 소설 <바비도>에 나오는 것처럼, 성찬식에 대한 견해 하나만으로도 서양에서는 한때 순교 사유였다. 기독교인이 천주교인에게 죽임을 당했다는 뜻이다.)

북한은 민주화에 실패하고 8월 종파 사건을 계기로 완전 김씨 일가의 철권독재 생지옥으로 전락했다. 종교도 주체사상 외에는 당연히 전면 말살. 스페인은 종교 개혁이 실패하고 다시 가톨릭 국가로 돌아가서 20세기까지만 해도 누가 '개신교인'(천주교의 입장에서 기독교의 호칭)이 되면 잡혀 가는 나라가 됐다.

그것처럼 일본도 이 박해를 못 이기고 천주교/기독교를 막론하고 양놈(이 또한 엄밀히 말하면 양놈이 아니라 유대계=_=) 종교는 거의 씨가 말라 버렸으며, 그 상태가 오늘날에 이르렀다. 개신교 계열 교파가 나중에 안 들어온 건 아니지만, 그래도 1억이 넘는 일본 인구 중에 그나마 명목상 교회 다니고 예수 믿는다는 사람은 몇십만 명밖에 되지 않는다.

이런 배경이 있는 일본은 오늘날까지도 이슬람도, 공산주의도 아니고 나름 자유 진영의 강대국 선진국인 것치고는 상당히 이례적인 기독교 선교의 불모지로 여겨진다. 솔까말 기복신앙에 대한 반례이기도 하다. 뭐, 국가가 부유한 것만치 국민들이 다 잘사는 건 아니더라도 말이다. 쟤들이 과거에 한국의 크리스천들을 박해하긴 했지만 역사 전체를 통틀어 봤을 때 아예 자국민에 대한 천주/기독교 박해는 그 이상이었다는 점도 고려할 사항이다.

이런 상황에서, 한국인이 일본 선교를 가는 건 마치 요나가 니느웨로 설교하러 가는 것과 비슷해 보인다. 앗시리아가 훗날 북왕국 이스라엘을 멸망시킬 게 뻔히 보이니, 요나는 니느웨로 가기 싫어서 하나님 앞에서 얼마나 생쑈를 했던가? 하지만 인제 와서 일본에서 니느웨 같은 대각성 부흥이 과연 일어나기라도 할지는 좀 회의적이다. 성경적으로 민족주의를 적용할 문맥이 있고 그게 별 의미나 영양가가 없는 문맥도 있는 법이다.

한국은 역사가 워낙 스펙타클하다 보니, 조선 정부에 의한 박해보다는 일제 말기에 일제로부터의 박해, 그리고 해방 후에 공산주의자에 의한 기독교 박해가 더 부각되는 편이다. 그리고 아시아의 여느 나라들과는 달리, 기독교회가 이 정도로 양적 성장을 이루는 이례적인 선례를 세계에 남겼다. 신자라면 감사할 일이다.

Posted by 사무엘

2015/08/21 08:31 2015/08/21 08:31
, , , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1129

삼손 평전

성경에 나오는 삼손은 이스라엘의 사사치고는 너무 괴팍하고 특이했던 사람이며, 천하장사였지만 여자에게 배신을 당해서 인생을 망친 비운의 사나이라고 비기독교인에게도 그럭저럭 알려져 있다.
삼손은 혼자서 많은 블레셋 사람들을 살상했지만, 그렇다고 해서 정식으로 군대를 소집하고 전쟁을 벌여서 블레셋으로부터 확실하게 독립을 쟁취해 낸 건 아니었다. 오히려 명색이 민족 지도자인데 적국의 여자와 연애를 하면서 너무 똘끼 넘치게 행동했다.

민족 대표, 민족 지도자라 불리는 사람은 적국이 아니라 그냥 중립적인 외국인을 애인· 배우자로 맞이하는 것만 해도 민족 정서상 대외 평판에 절대로 좋게 작용하지는 않는다. <이 연걸의 정무문> 영화에서 일본인 여자를 사귄 진진, 그리고 에티오피아 여인과 재혼했다고 비방을 받은 모세(민 12:1) . 거기에다 우리나라 초대 대통령 이 승만도 그 많고 많은 한국 여자를 놔 두고 '호주댁'과 결혼했다고 비방 받곤 했다. 삼손 역시 그런 격이었다.

사사기 14장을 보자. 그는 부모의 반대를 무시하고 첫 블레셋 여인에게 청혼을 하러 갔다. 그런데 포도원에서 사자를 만났다. 이건 단순히 위험에 처한 상황이기에 앞서 여러가지로 이상한 상황이었다. 왜 포도원인 걸까?

삼손은 나사르 서원의 적용을 받는 사람이어서(삿 16:17) 머리를 깎는 것뿐만이 아니라 포도도 절대 금지이다. 단순히 알코올 성분이 든 포도주를 안 마시는 것 정도를 넘어, 아예 생 포도송이, 포도 주스, 건포도 같은 것도 먹어서는 안 된다. (민수기 6장 참고) 불자에다 비유하자면 오신채를 먹어서는 안 되는 것처럼 말이다. 그런데 삼손은 왜 포도원을 갔을까?
그리고 포도원에서 뱀이라든가, 혹은 여우(아 2:15)처럼 성경에도 나오고 이솝 우화에도 나오고 침입자로서 비교적 흔히 연상 가능한 동물을 만난 게 아니라, 하필 야생 맹수인 사자를 만난 걸까?

구약 역사서에서 동물 사자는 사람을 징벌하는 용도로 종종 쓰였다. 그러나 저기서는 하나님께서 삼손에게 몬스터/몹만 소환한 게 아니라 Doom 2 게임으로 치면 Berserk 파워업 치트키를 먹여 주셨다. 다윗은 돌팔매질로 맹수들을 내쫓았다지만, 삼손은 도구가 필요하지 않았으며 그냥 맨주먹으로 사자를 찢어 죽였다. FPS 용어로 치면 gib을 했다.

그런데 이런 놀라운 무용담은 "하나님이 나를 지켜 주셨습니다"라는 공개적인 간증거리가 될 수가 없었다.
"하나님께서 화재 현장에서 저를 기적적으로 지켜 주셨습니다" / "오 그래요? 어디서 화재를 겪으셨는데요?" / "나이트클럽에서요" =_=;;; 이런 꼴이었기 때문이다.
삼손은 공인으로서 포도원 안에서 그런 일을 겪었다는 얘기를 입 밖에 낼 수 없었다.

나중에는 삼손이 죽인 사자의 시체에도 이변이 생겼다. 더러운 구더기가 들끓는 게 아니라 여왕벌이 들어오기라도 했는지 안에 벌꿀이 생겼다. 하지만 아무리 꿀이어도 그렇지 저건 시체 안에 담겨 있는 건데..;;
원효대사의 이야기만 봐도, 밤에 마셨던 물이 더러운 해골 안에 담겼었다는 걸 알게 되자, 그 사람이 우웩~ 하면서 난리를 치지 않았던가.

그런데 삼손은 참 멘탈도, 비위도 강했나 보다. 부정한 걸 만져서는 더욱 안 되는 나사르 인인데도 손수 사자의 시체를 뒤져서 꿀을 가져와서는 자기도 먹고 부모에게도 줬다.
이것 다음에 꿀 이야기는 사무엘기상 14장에서 또 나오는데 이것과는 어떤 영적 관계가 있으려나 모르겠다.

삼손은 혼자서 사자를 때려 잡고 그 시체에서 꿀까지 득템한 행적을, 비록 공개적으로 간증은 못 하지만 개인적으로는 분명 뿌듯한 자랑거리라고 생각한 듯하다. 율법을 몽땅 어긴 건 안중에 없고 말이다.
그래서 결혼식을 앞두고 친구들에게 그걸 절대 풀지 못할 수수께끼 문제로 냈다. 사자와 꿀이라니, 전혀 어울리지 않는 조합을 평범한 사람들이 떠올리기란 물론 불가능했다.

삼손의 친구들은 치사하게도 삼손의 여친을 공갈 협박해서 답을 알아 냈다. 이것은 오늘날 정보 보호· 보안의 관점에서도 의미 있는 사례이다. 이중 삼중으로 복잡하게 암호를 걸어 놔도, 관리자 당사자 내지 그 지인을 매수하거나 족치는 데 성공하면 그냥 끝이니까. 결국 모든 문제의 정점에는 사람이 있다.

이 때문에 삼손은 내기에서 졌으며, 옷 서른 벌을 마련하느라 다른 동네의 블레셋 사람 30명을 쥐도 새도 모르게 죽이게 됐다. 옷 서른 벌 때문에 30킬이라니 오늘날로 치면 그야말로 개 싸이코패스 연쇄살인마가 따로 없다. "피해자들은 모조리 시체도 없이 실종"되거나 또는 "피해자들은 모두 겉옷이 없어졌다는 공통점 있음" ㅎㄷㄷㄷㄷ
그 시절에 마을 곳곳에 CCTV가 없었던 게 정말 천만다행이었다.

사용자 삽입 이미지

(모든 그림의 출처는 삼손의 생애를 그린 칙 출판사 만화 전도지 <Superman?> (1990) 편.)

그리고 또 생각할 점이 있다. 피해자가 입고 있던 옷을 그대로 벗겨서 줘야 하니, 사람을 죽이는 것도 옷이 찢어지거나 핏자국이 묻지 않게 매우 조심해서 죽여야 했다는 점이다. 때리거나 칼로 찔러서는 안 되고, 뒤에서 덮쳐서 목을 조르거나 비틀기라도 해야 했을 것이다. 물론 삼손은 워낙 천하장사 인간흉기였으니, 사람 목을 움켜쥐고서 손가락에 까딱 힘만 주면 곧바로 경동맥이 작살이 났을 테고, 사람을 무슨 개미를 눌러 죽이듯이 죽일 수 있었을 것이다.;;

나중에 당나귀 턱뼈로 블레셋 사람 1천 명을 죽인 건 고전 FPS로 치면 천하무적 주인공이 잡몹들을 싸그리 사냥하면서 1000 kill / frags를 달성하는 것과 똑같다. 삼손의 이야기는 은근히 게임스럽다. 턱뼈가 아니라 칼이나 창 같은 진짜 냉병기가 있었다면 삼손의 주변에 있던 적군들은 그냥 죽는 수준을 넘어 그야말로 사지가 남아나지 못했을 것이다.

단, 이 사건은 포도원에서의 사자 킬과는 달리 공적인 찬양 명분이 충분함에도 불구하고 삼손은 딱히 하나님께 영광을 돌리지 않고 "내가 당나귀 턱뼈로 시체로 산을 쌓으며 1천 킬을 달성했도다"라고 으쓱하기만 했다(삿 15:16).
이에 삼손은 싸움을 다 이겨 놓고는 곧 극심한 갈증으로 인해 죽을 지경이 됐다.
하나님은 삼손에게 당장의 기도 응답과 승리는 주시지만 그래도 뼈 있는 경고도 같이 주셨다는 것이 눈에 들어온다. 그 경고에 담긴 메시지를 삼손이 더 일찍 간파했으면 자기 자신이나 민족이 훨씬 덜 불행을 겪어도 됐을 텐데 말이다.

이것들 말고도 삼손의 차력 기행 중에는 성을 탈출하기 위해 그 크고 무거운 성문을 잠금 장치까지 포함해 통째로 뜯어 버린 뒤, 그걸 방패 삼아 등에 지고 도망친 것이 있다(삿 16:3). 못해도 수백 kg 내지 몇 톤에 달하는 무게였을 것이다.
이 정도면 블레셋의 입장에서는 그야말로 전의를 상실케 하는 충격과 공포 도시전설 괴담이었을 것이다. (그런데 나중엔 상황이 역전되어 블레셋에서 골리앗이 배출된 것이 참 공교롭다. 삼손과 골리앗이 일대일 맞장을 떴으면 어땠을까? =_=)

그런데 삼손이 그저 무식하게 힘만 셌는가 하면 그건 또 아니었다.
"삼손이 가서 여우 삼백 마리를 붙잡아 꼬리와 꼬리를 묶고 불붙는 나무 조각들을 취하여" (삿 15:4)
이것도 얼마나 대단한 일인지를 알아야 한다. 오로지 복수를 위해서 산과 들로 나가서 여우를 300 마리나... 그것도 학살이 아니라 산 채로 수집하는 데 시간과 노력이 얼마나 들었을까?

이건 힘만 세다고 가능한 일이 아니다. 퀘이크로 치면 Frags나 Excellent가 아니라 Impressive, Perfect, Accuracy 같은 분야에 속하는 어려운 일이었다. 생포하는 과정에서 여우를 다리 같은 걸 조금이라도 다치게 해서도 안 된다. 여우들을 꼬리에 불을 붙인 채 풀어 줘서 남의 밭을 왕창 불태워 버릴 작정이었으니 말이다. 얘들은 마지막 순간까지 밭을 전속력으로 뛰어다닐 수 있는 상태여야 한다!

삼손은 늘 개인 플레이를 하다 보니, 딱히 자기 동지· 부하나 공범이 있었는지는 모르겠다. 만에 하나 공범이 좀 있다 하더라도 여우를 300마리나 곱게 사로잡는 건 어떤 방법으로든 정말 보통일이 아니다. 삼손은 굉장한 근성가이였음을 알 수 있다.

.
.
.

이런 삼손이 한 순간의 실수로 인해 너무 허무한 최후를 맞이했다는 것은 참으로 애석한 일이다. 자기의 엄청난 능력에도 불구하고 헬스 트레이너-_-로든 성경· 정치· 군사 어느 쪽으로도 후계자 양성을 못 하고 정규전 한 번 제대로 못 치른 채 혼자 원맨쇼만 일삼다가 자폭으로 모든 것을 끝냈다.

삼손은 들릴라의 치명적이고 위험천만한 질문에 대해 너무 째째하고 진지하고 재미없게(?) "당신, 내 힘의 근원을 알려고 했다간 다쳐. 그런 건 다시는 묻지 마시오. / 내 힘은 우리 민족의 신인 주 하나님께서 주신 것이오" 이렇게 원천차단 돌직구를 날리기에는... 너무 대인배였다.
예전에 사자를 죽이던 시절부터 수수께끼와 내기를 즐기던 근성이 여전했다. 이제는 절대로 유출되어서는 안 되는 자기의 힘의 근원마저도 수수께끼 소재로 삼아서 "그게 궁금해? 그럼 내가 힌트 줄 테니 알아맞혀 보셔" 유흥거리로 생각했다. 그러면서 살얼음판위를 걷는 것과 같은 위태로운 게임을 계속했다. 이것은 그의 치명적인 실수였다.

하지만 그는 영적으로 순진하고 철딱서니 없는 구석은 있을지언정, 교활하거나 나쁜 사람은 절대로 아니었다. 동족들로부터 버림받을지언정 자기가 먼저 동족을 해치지는 않으려 했으며, 철저하게 피아 구분을 할 줄 알았다. "나 한 몸 죽어서 다른 사람들을 살리리라"라는 뭔가 요나와 예수님의 예표스러운 행적도 있다.
그 스펙과 지위에도 불구하고 여자한테는 완전 순애보였고..;; 어쨌든 교활 잔머리의 천재인 발람하고는 억만 광년 떨어진 성향이었다.

"오 하나님이여 간구하옵나니 이번 한 번만 나를 강하게 하사 블레셋 사람들이 내 두 눈을 뺀 것을 단번에 원수 갚게 하옵소서. (...) 나를 블레셋 사람들과 함께 죽게 하소서"(삿 16:28,30)는 눈물이 핑 돌 정도의 회한 서린 비장한 기도가 아닐 수 없다. 그도 그럴 것이 저 사람은 적군에게 잡히고 나서 꼴좋다고 얼마나 새디스틱한 모욕과 능멸을 당했겠는가?

사용자 삽입 이미지

(우리로 치면 ㅎㅎ/ㅋㅋㅋ를 W를 붙여서 haw haw라고 표현하는 건 칙 만화 전도지의 고유한 관행이다.)

가정용 소형 맷돌이 아니라 아예 감옥에 있는 대형 맷돌은 나귀나 소 같은 동물을 동원해서 돌리는 물건이다. 요즘 같았으면 당연히 내연기관이나 모터를 쓰지 생명체는 쓰지도 않는다. 자동차에 밀려서 인력거가 사라진 것처럼 말이다. 삼손은 딴 데다 비유하자면, 배나 건물의 지하 기계실 같은 데서 평생 힘만 쓰는 동력 셔틀로 전락하고 말았다(삿 16:21).
작업 환경이 어두컴컴한 건 삼손에게는 별 상관이 없다. 어차피 눈이 없는 상태이니까. 사람을 무진장 귀찮게 하던 파리나 모기가 결국 생포당해서 날개가 뽑힌 것과 비슷한 격이다.

삼손의 생애를 각색한 드라마 중에는 (1) 들릴라가 마치 가룟 유다마냥 나중에 양심의 가책을 느껴서 삼손을 배반한 것을 후회하고 울고불고 매달리는 이야기가 들어간 게 있다. 요즘 사람들은 절대적인 선악 구도보다는 대체로 입체적인 인물 위주의 휴먼 드라마를 좋아하니까.
또한, 삼손이 마지막 순간엔 자기가 신전의 기둥을 붙드는 것을 도와 준 소년에게는 몰래 (2) "고맙소. 당신은 이 신전에서 최대한 멀리 빨리 탈출하시오."(내가 곧 신전을 무너뜨릴 거니까)라고 귀띔을 하는 애드립이 있기도 하다.
물론 이에 대해서는 "진실은 저 너머에"이다. (1)과 (2)는 성경에 직접적인 언급은 없는 애드립일 뿐인 것을 감안하도록 하자.

사용자 삽입 이미지

성경에 따르면 삼손은 거대한 석조 건물을 맨손으로 붕괴시킴으로써, 자폭하면서 죽인 블레셋 사람 수가 생전에 죽인 블레셋 사람 수보다 더 많았다고 말한다. 저 정도 대형 신전은 폭탄 테러로 붕괴시킨다고 해도 TNT가 몇 톤짜리가 필요했겠는가? =_=;;; 이거 정말 그냥 웃어 넘길 일이 아니다.
(참고로 1995년 오클라호마 폭탄 테러가 TNT 2.3톤 정도의 위력이었으며 영국의 Gunpowder plot이 위력계수 0.55짜리 흑색 화약 1.n톤이니 TNT 1톤에 약간 못 미치는 정도의 위력으로 추정됨)

성경에 기록된 생전의 kill 수만 해도 최하 1000+30+알파인데, 저 붕괴 사고로 몰살당한 사람은 제일 적게 잡아도 3천 명가량으로 추정한다(삿 16:27, 30). 블레셋 민족의 원수 삼손이 재주 부리는 꼴을 보러 사람들이 참 많이도 몰려와 있었기 때문이다. 20년 전에 삼풍백화점 붕괴 사고로 죽은 사람이 600여 명, 부상자가 900여 명이니 저 살상 규모가 얼마나 엄청났는지를 추측할 수 있다.

삼손은 히브리서의 믿음장에 이름이 올라 있으며, 덕분에 자살· 자폭을 하고도 정황상 얼마든지 구원받는 게 가능하다는 예로도 언급된다. 삼손이 실존 인물일 뿐만 아니라 하늘나라에서 만날 수 있는 믿음의 선진이라는 것을 히브리서의 저자(아마 사도 바울)가 확실하게 인증한 셈이다. "삼손도 믿음으로 신전을 무너뜨렸다." 같은 말이 "더 이상의 자세한 설명은 생략한다" 속에 포함되어 있을 것이다(히 11:32).
또한 딸을 죽이는 대형 사고를 치긴 했지만 그래도 믿음이 있었고 나쁜 사람은 아니었던 입다 역시 같이 믿음장에 있는데, 삼손은 저 사람하고도 뭔가 통하는 맥이 있어 보인다.

그나저나 머털도사가 삼손 이야기에서 모티브를 땄나 싶은 생각이 든다. 거기에도 머리털이 다시 자라는 게 나오는데..;;

Posted by 사무엘

2015/08/18 08:27 2015/08/18 08:27
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1128

오사마 빈 라덴이 죽은 지가 벌써 4년이 넘었다.
저 한 사람을 잡으려고 미국이 정말 얼마나 천문학적인 돈(= 국민 세금)을 쏟아부어야 했는지를 생각하면 아마 치가 떨릴 것이다.
정확한 근거는 모르겠다만, 한때 미국 해병대 내부에서는 "빈 라덴을 용서하는 것은 신이 하실 일이겠지만, 둘의 만남을 주선하는 것은 우리 일이다."라는 대박 간지 넘치는 모토가 설정돼 있었다고 한다. 짱이다. 같은 메시지도 단어만 바꾸면 저렇게 센스 넘치게 표현할 수가 있구나!

뱀발을 내밀자면, 비슷한 패턴의 대사가 테이큰 3에도 있었다.
"내게 이틀만 주시오. 그러면 내가 무죄인 것과 진짜 범인이 누군지를 모두 입증해 보이겠소!"
"밀스 씨, 당신의 무죄 여부는 법정에서 판단할 일이지 내 관할이 아니오. 내가 할 일은 당신을 붙잡아서 법정에 세우는 일이지. 단지 그 뿐이오.
당신이 그 길로 도주한다면 LAPD(로스앤젤레스 경찰국), FBI, CIA가 모두 당신을 찾아 나설 것이고 당신을 저지시킬 거요."
"굿 럭." 아, 뿅 갈 거 같다.

뭐, 빈 라덴은 실제로 신에게서 용서받았을 확률은 매우 희박하며(단순 악행 때문만은 아님), 정작 실제로 둘의 만남을 주선한 것도 해병대가 아니라 해군 대테러 특수부대였다.
그 시절을 좀 회상하자면, 그는 하필 킹 제임스 성경 반포 딱 400 주년 당일에 사살당했다. (2011년 5월 2일) 이건 뭐 그냥 우연이라 치자.
듀크 뉴켐 포에버는 원래 북미 기준으로 5월 3일에 나오려 했는데 마지막으로 딱 한 번 더 연기돼서 6월 10일로 낙점됐었다.

이렇게 빈 라덴을 사살하기 위해서 미국은 전국, 전세계 각지에서 놈의 근황과 행동을 추적하고 치밀한 첩보 활동을 벌여야 했다. 미국은 세계에 큰 영향을 끼치고 있는 나라이고 그만큼 적도 많으니까.
세계 각국에는 보이지 않는 곳에서 방첩 활동을 하는 국가 기관이 있다. 그것이 우리나라는 한때는 중앙정보부, 안기부였다가 지금은 국가정보원, 혹은 국정원이다. 미국이나 이스라엘처럼 국내· 국외가 나뉘어 있지는 않고 단일 기관이다.

전철 안에서 111 신고 전화를 홍보하면서 "국가정보원에서는 마약, 국제범죄, 테러, 산업 스파이 등 ..." 어쩌고 하는 방송을 종종 들은 적이 있을 것이다.
요즘 국정원에서 실제로 하는 일은 예전만치 오로지 대공· 대북 업무보다는 제3세계 산업 스파이 단속의 비중이 더 크다. 이런 교묘한 범죄를 잡아 내는 건 경찰만으로는 증거 확보에 어려움이 있는 관계로, 불시에 확 덮쳐서 범죄 순간의 스냅샷을 떠 주는 전문 첩보 기관의 도움이 필요하다.

국정원 요원은 딸을 구하기 위해서가 아니라 국익을 지키기 위해서 진짜로 리암 니슨처럼 어디에 위장해 들어가고 감시하고, 필요하다면 폭력도 쓰고 희대의 난폭운전도 벌이는 등 별 짓을 다 해야 한다. 그러다 들키면 납치· 고문· 살해 등을 당하기도 하고 이때 모기관으로부터는 필요하다면 "우린 저런 요원을 보낸 적 없는데? 모름." 버림받기까지 하는 것도 국익을 위해 감수해야 한다.

국정원이 워낙 뽀대와 간지가 나고 연봉과 복리후생이 좋다고는 하지만, 그걸 다 공짜로 제공해 줄 리가 없으니.. 그저 간지만 보고 도전할 만한 곳이 결코 아니다. 임기응변과 근성, 예리한 관찰력이 부족한 사람, 그 어떤 사람과도 친근하게 붙어서 원하는 정보를 얻을 정도의 화술과 사회성을 갖추지 못한 사람, 일반인 평균 이상의 지력과 체력이 없는 사람, 미행을 티 안 나게 못 하고 어디서든 거짓말을 실감나게 못 하는 사람은 저런 기관에 절대로 들어가서는 안 된다.

특히, 자기 분야의 밥벌이 기술만 평생 연마하며 살고 싶은 사람, 처자식과 평범한 가정 꾸리고 가정적인 남편이 되고 싶은 사람은 더욱 가지 말아야 한다. 고로 국정원 같은 곳은 본인에게 맞는 직장은 전~혀 아니다.
그럼에도 불구하고 국정원 요원 신입 공채는 SKY 출신에 외대 출신들이 대거 몰려 경쟁률이 몇십~100몇십 대 1이라고 한다.

본인이야 인간의 죄성의 본질을 아는 크리스천으로서, 절대악을 다스리기 위해 불가피한 필요악의 존재에 대해 긍정적이다. 북한 같은 절대악이 있는 우리나라 같은 상황에서는 더욱 그러하다. 그러나 그 필요악이 옛날에는 잡으라는 빨갱이는 안 잡고, 반공을 빌미로 도리어 자국민에게 나쁜짓을 한 것도 있음은 사실이다. 그래서 첩보 기관인 국정원 역시 대국민 이미지가 아직까지도 마냥 좋지만은 않다.

이런 위압적이고 코렁탕 스러운-_- 이미지를 쇄신하기 위해서인지 국정원에서는 일반 시민들을 대상으로 꽤 오래 전부터 한 달에 두 번꼴로, 국정원 요원이 하는 일이나 당할 만한 일을 소재로 추리 퀴즈를 연재하고 있다. 국정원 요원이 반쯤은 탐정 같은 일도 하는구나. 기출문제가 이미 몇백 개나 쌓여 있다.
영화나 드라마를 많이 본 사람들은 어지간히 뻔한 설정이나 스토리, 클리셰를 보면 다음 장면이 바로 예상이 되듯이, 추리 소설 덕후들은 딱 보면 바로 답이 나올 정도의 난이도라고 한다.

추리소설의 설정에만 파묻히면, 마치 Doom 2를 너무 많이 한 것처럼 정말 세상에 어디 믿을 놈 없고 기술을 어디 팔러 홀몸으로 나갔다가는 반드시 살해당할 것만 같고, 마약 조직이나 사이비 종교에 있다가 탈출하게 되면 정말 목숨을 부지 못할 것 같다.
시신을 전기 장판으로 덮어서 따뜻하게 유지해서 체온 감소로 사망 시각을 추정하지 못하게 하는 건 2008년 부산 청테이프 살인 사건에서 실제로 있었던 일인데 이걸로 모티브를 딴 건지?
또한 커피에 독을 타서 누구를 독살한 건 대한제국 마지막 황제 순종이 어린 시절에 비슷하게 당한 일이다. 국정원 간부라면 역사· 시사· 상식의 달인일 테니 이런 것에서도 소재를 차용했을지도 모르겠다.

추리 문제를 푸는 건 어찌 보면 거짓 증언에서 설정 오류를 찾아내는 것이니, 역덕후들이 사극을 보면서 고증오류 찾아내며 낄낄대는 것과 비슷한 것 같다. 뭔가 교집합이 있다. 나 같아서도 당장 떠오르는 철도 추리 퀴즈는 전철이 절연구간에 진입해서 좀 어두워졌을 때 객실에서 무슨 살인 사건이 터졌고, 어느 철도 덕후 탐정이
"범인이 제시한 사진은 합성· 조작된 것입니다. 2014년에는 아직 수인선 전철이 거기까지 개통하지 않았습니다."
"그 복복선 선로 구간에서 일반열차가 내선을 주행한다는 건 있을 수 없는 일입니다."
이런 데서 단서를 찾아내면 무척 아름다운 이야기가 완성될 것 같다.
이와 관련된 본인의 옛날 글을 한번 보시라. 거기서도 이미 '셜록 홈즈' 같다는 댓글이 있었다. ^^

실제로, 국정원 추리 퀴즈 기출문제들 중 다른 건 내가 뭔 소리인지 몰라서 대부분 그냥 해답을 봤지만,
이것만은 그냥 문제를 다 읽기도 전에, 저거 그림만 보고는 출제 의도와 논리상의 헛점을 0.n초 만에 바로 알아챘다. <전철 3호선 살인 사건> 편.

우측통행을 하는 전동차 선로 사진을 들이밀면서 '1호선 보산 역'이라고 주장하며 알리바이를 내세우다니 거 참.. ^^
아 추가로, 그림에 나와 있는 전동차가 폭이 좀 작은 편인지라,
혹시 지방 지하철의 중형 전동차 사진을 찍어 갖고 와서는 서울· 수도권 전철이라고 사기 치는 건 아닌가도 생각을 했는데 그건 아니었다.

정말로 아는 만큼 보이고 시뮬레이션이 된다.
추리 소설은 걸핏하면 정체불명의 시신이 발견되는 것으로 이야기가 시작되지만, 역사적으로 시신의 신원이 전혀 밝혀지지 않아 영구 미제 사건으로 남은 사건도 있다. 1948년 12월, 오스트레일리아 소머튼 해수욕장의 "Taman shud" 사건은 그 많은 똘똘이들이 추리를 하고도 도저히 답을 못 구한 사건 중 하나이다. 그는 아마 어느 나라에서 보낸 흑색 첩보 요원이 아니었나 싶다.

뭐, 그런 현실 설정 말고도
"다음과 같은 도로 지도에서 다리를 최소 몇 군데를 끊으면 A에서 B지점까지 테러리스트의 도주 경로를 완전히 차단할 수 있을까?"
이건 그래프에서 단절점 내지 브릿지를 찾는 문제이니 컴공/전산 전공자에게는 익숙할 것이고,
A~E가 전부 범인이라 지목하는 사람이 다 다를 때 진짜 맞는 증언을 찾는 문제는, 이건 가로· 세로로 O X 표 만들어서 IQ 테스트 하듯이 풀면 되겠다. 실제로 이런 문제는 정보 올림피아드 문제 같다는 생각이 들기도 했다.

간단한 문자/문자열 암호 풀이 문제도 있고.
국정원 추리 퀴즈 중에서 누군가가 남긴 추상화 형태의 다잉메시지 퍼즐을 푸는 건...
지난 2009년에 어떤 만화가가 원주 시정 홍보지의 삽화에다 대통령 욕설을 교묘하게 적어 놓은 걸 찾아 내는 것 같은 생각이 들었다. -_-;;

사용자 삽입 이미지

아무튼, 세상엔 걍 생업 전선에서 자기 근로만 하면서 갑님으로부터 월급 받는 것에 만족하지 못하고, 저렇게 맨날 머리 굴리면서 세상을 참 복잡하고 스릴 넘치게 사는 사람이 많다.
그리고 한편으로 인간은 죄 때문에 죄를 감시하고 죄의 결과를 수습하느라 정치 권력이 없을 수가 없게 되었으며, 그 비효율적인 일에 무지막지한 양의 세금이 쓰이게 되었다는 것도 알 수 있다.
난 저렇게 외국에서 탐내고 국내에서 보호해야 할 가치가 있는 기술을 개발할 능력이라도 좀 있으면 좋겠다. =_=;;

* 끝으로, 국정원과 아무 관계 없는 여담..;;
국정원은 영어로 national intelligence service여서 영어 이니셜이 NIS이다.
그런데 본인이 난생 처음으로 본 NIS라는 글자는 페르시아의 왕자 2의 구성 파일 중 하나인 NIS.DAT였다.
크기가 거의 1MB에 달하고 아마 가장 큰 파일이었는데, 이름의 의미는 무엇인지 모르겠지만 컷씬 그래픽이 들어있었다.
20여 년 전에 페르시아의 왕자 2를 디스켓으로 불법복제를 해 봤기 때문에 인상적인 파일 이름을 아직까지 기억하고 있다.

Posted by 사무엘

2015/08/15 08:39 2015/08/15 08:39
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1127

한때 Windows에서 바탕 화면에 배경 그림을 표시하는 방식은 '바둑판, 화면 중앙, 화면 크기에 맞춤'이라는 딱 세 가지 중 하나를 선택할 수 있었다.
이것은 GDI에서 직사각형 영역에다 비트맵을 뿌리는 함수로 치면 각각 PatBlt, BitBlt, 그리고 StretchBlt에 대응한다. 지금은 몇 가지 방식이 더 나오는데, 그건 그림의 종횡비와 화면의 종횡비가 다를 때 확대를 어떻게 할지를 결정하는 것이므로 개념적으로 StretchBlt에 대응하는 셈이다.

GDI에서 비트맵 그래픽을 표현하는 추상적인 핸들 자료형은 잘 알다시피 HBITMAP이다. 그러나 위의 세 *Blt 함수들 중 어느 것도 HBITMAP을 인자로 받지 않는다. 이는 어찌 된 일일까?
이들은 비트맵을 자기들이 처리하기 용이한 형태로 바꾼 파생 자료형을 대신 사용한다. PatBlt를 사용하려면 뿌리려는 비트맵을 브러시로 바꿔야 하며, BitBlt와 StretchBlt는 해당 비트맵에 대한 그래픽 조작이 가능한 메모리 DC를 추가로 준비해야 한다. 그럼 그 구체적인 내역을 살펴보자.

모노크롬 아니면 16색 그래픽이 있던 시절, 도스용 그래픽 라이브러리에는 8바이트로 표현되는 8*8 단색 패턴이라는 게 있었다. 그 작은 공간으로도 벽돌, 사선 등 생각보다 기하학적으로 굉장히 기발한 무늬를 표현할 수 있었다.
Windows는 2000/ME까지만 해도 배경 그림은 오로지 BMP만 지원했으며(액티브 데스크톱을 사용하지 않는 한), 배경 그림이 차지하지 않는 나머지 영역은 그런 무늬로 도배하는 기능이 있었다. 물론 이것은 트루컬러 그래픽과는 영 어울리지 않는 낡은 기능이기에, XP부터는 깔끔하게 없어졌다.

사용자 삽입 이미지

PatBlt는 직사각형 영역을 주어진 브러시로 채우는 함수이다. 즉, 이 함수가 사용하는 원천은 함수의 별도 인자가 아니라 해당 DC에 선택되어 있는 브러시이다. 그럼 얘는 Rectangle이나 FillRect와 하는 일이 거의 차이가 없는 것 같아 보인다. 이 세 함수의 특성을 표로 일목요연하게 정리하면 다음과 같다.

  PatBlt FillRect Rectangle
경계선을 current pen으로 그음 X X O (유일)
경계면을 current brush로 채움 O No, brush를 따로 인자로 받음 O
사각형 좌표 지정 방식 x, y, 길이, 높이 RECT 구조체 포인터 x1, y1, x2, y2 (RECT 내용을 풀어서)
래스터 오퍼레이션 지정 O (유일) X (= PATCOPY만) X (왼쪽과 동일)

다들 개성이 넘쳐 보이지 않는가? =_=;;
Rectangle은 선을 긋는 기능이 유일하게 존재하며, FillRect는 유일하게 사용할 브러시를 매번 인자로 지정할 수 있다. 그 반면 PatBlt가 유일하게 갖추고 있는 기능은 래스터 오퍼레이션인데, 사실 이것이 이 함수의 활용도를 크게 끌어올려 주는 기능이다. 이에 대해서도 앞으로 차차 살펴보도록 하겠다.

브러시는 '2차원 면을 바둑판 형태로 채우는 어떤 재질'을 나타내는 GDI 개체이다. 가로선· 세로선· 대각선 같은 간단한 무늬는 CreateHatchBrush로 지정 가능하지만 이건 오늘날에 와서는 영 쓸 일이 별로 없는 모노크롬 그래픽의 잔재이다.
CreateSolidBrush는 아무 무늬가 없는 순색 브러시를 표방하긴 하지만, 그래도 16/256컬러 같은 데서 임의의 RGB 값을 넘겨 주면 단순히 가장 가까운 단색이 아니라 ordered 디더링이 된 무늬가 생성된다.

그리고 다음으로 비트맵으로부터 브러시를 생성하는 함수는 바로 CreatePatternBrush이다.
여기에서 사용할 비트맵은 가장 간단하게는 CreateBitmap이라는 함수를 통해 생성할 수 있다. 이 함수가 인자로 받는 건 비트맵의 가로· 세로 크기와 픽셀 당 색상 수, 그리고 초기화할 데이터가 전부이다. 아주 간단하다.

그러나 이 비트맵은 그냥 2차원 배열 같은 픽셀 데이터 덤프 말고는 그 어떤 정보도 담겨 있지 않으며, 이걸로 만들 수 있는 건 구조가 극도로 단순해서 어느 그래픽 장비에서나 공통으로 통용되는 모노크롬 비트맵뿐이다. 즉 그 도스 시절의 8*8 패턴 같은 극도로 단순한 비트맵만 만들 수 있다. 오늘날에 와서 CreateBitmap은 모노크롬 비트맵 생성 전용이라고만 생각하면 된다.

모노크롬 비트맵을 기반으로 만들어진 DC나 브러시는 다른 solid/hatched 브러시와는 달리 자체적으로 색상 정보가 담겨 있지 않다. 그렇기 때문에 이때는 그래픽을 뿌리는 DC가 갖고 있는 텍스트의 글자색(값이 0인 곳)과 배경색이(값이 1인 곳) 양 색깔로 선택된다는 점도 참고하자. MSDN에 명시되어 있다. (0과 1 중 어느 게 글자인지 이거 은근히 헷갈린다. 빈 배경에서 뭔가 정보가 있다는 관점에서는 1이 글자 같아 보이기도 하니 말이다.)

그리고 브러시는 origin이라는 게 있어서 어떤 경우든 이를 원점으로 하여 바둑판 모양으로 뿌려진다. oxoxoxox라는 무늬가 있다면, 0,0부터 8,0까지 뿌린다면 oxoxoxox로 뿌려지지만 1,0부터 9,0까지 뿌린다면 ox가 아니라 xoxoxoxo가 된다는 뜻이다.

모노크롬이 아닌 컬러 비트맵을 저장하고 찍는 절차는 좀 복잡하다. 이미 컬러를 표현할 수 있는 DC로부터 CreateCompatibleDC와 CreateCompatibleBitmap을 거쳐서 비트맵을 생성해야 한다. 아니면 CreateDIBitmap를 써서 DIB라 불리는 '장치 독립 비트맵' 정보로부터 HBITMAP을 생성하든가.. 얘는 그냥 비트맵 데이터뿐만 아니라 팔레트 정보 같은 것도 담긴 헤더를 인자로 받는다. 출력할 그래픽 데이터와 출력 매체의 픽셀 구조가 다를 때를 대비해서 추상화 계층이 추가된 것이다.

원래 패턴 브러시는 8*8의 아주 작은 비트맵만 취급할 수 있었다. 그러나 NT 내지 95 이후의 버전부터는 그 한계가 없어지면서 브러시와 오리지널 비트맵 사이의 경계가 좀 모호해졌다. 그래도 PatBlt는 작은 비트맵 무늬 위주의 브러시를 래스터 오퍼레이션을 적용하여 그리는 용도에 원래 최적화돼 있었다는 점을 알아 두면 되겠다.
윈도우 클래스를 등록할 때 우리는 WNDCLASS의 hbrBackground 멤버를 흔히 (HBRUSH)(COLOR_WINDOW+1) 이런 식으로 때워 버리곤 하는데, 여기에다가도 저런 패턴 브러시를 지정해 줄 수 있다. 그러면 그 윈도우 배경에는 자동으로 바둑판 모양의 비트맵이 배경으로 깔리게 된다. 이런 식의 활용도 얼마든지 할 수 있다.

한편, 비트맵을 찍는 동작에는 그냥 있는 그대로 뿌리는 것뿐만이 아니라 래스터 오퍼레이션을 통해 반전을 해서 찍기(PATINVERT), 타겟 화면을 무조건 반전시키기(DSTINVERT), 타겟 화면을 무조건 검거나 희게 바꾸기 같은 세부 방식의 차이가 존재할 수 있는데, 앞서 언급한 FillRect뿐만 아니라 InvertRect나 DrawFocusRect 같은 함수도 사실은 PatBlt의 기능을 이용하여 다 구현 가능하다. cursor를 깜빡거리는 건 두 말할 나위도 없고 말이다.

임의의 색깔로 음영을 표현하는 것이라든가, 특히 이동이나 크기 조절을 나타내는 50% 반투명 검은 음영 작대기/테두리는 모두 이 함수의 xor 래스터 오퍼레이션으로 표현된다. 그걸 구현하는 데는 PatBlt 말고는 선택의 여지가 없다는 뜻. 흑백을 xor 연산 시키면 "원래 색 & 반전색"이 교대로 나타나니까 말이다.

사용자 삽입 이미지
물론 요즘은 (1) 걍 테두리 없이 해당 개체를 즉시 이동이나 크기 조절시키는 것으로 피드백 또는 (2) 알파 블렌딩을 이용한 음영이 대세가 되면서 전통적인 xor 음영은 점점 비중이 줄어들고 있긴 하지만, PatBlt 함수는 그래도 이렇게 유용한 구석이 있다.

이런 PatBlt에 반해 BitBlt는 비트맵을 SelectObject시킨 DC를 원본 데이터로 사용하기 때문에 컬러 비트맵의 출력에 더 최적화되어 있다. PatBlt처럼 비트맵을 바둑판 모양으로 반복 출력하는 기능은 없으며, 딱 원본 데이터의 크기만큼만 출력한다. PatBlt와는 달리 고정 origin이 없고 사용자가 찍으라고 한 위치가 origin이 된다. StretchBlt는 거기에다가 확대/축소 기능이 추가됐고 말이다.

이 정도면 비트맵 API에 대한 개념이 충분히 숙지될 수 있을 것이다. 각종 아이콘과 마우스 포인터들도 다 마스크 비트맵 AND와 컬러 비트맵 XOR이라는 래스터 오퍼레이션을 통해 투명 배경 내지 반전을 구현한다는 건 두 말하면 잔소리이다. 물론 오늘날은 알파 채널로 투명도를 구현하면서 래스터 오퍼레이션의 의미는 다소 퇴색했지만 말이다.
그럼 이제 비트맵 API들에 대한 개인적인 의문점과 아쉬운 점을 좀 나열하며 글을 맺겠다.

(1) GDI는 후대에 등장한 다른 그래픽 API들과는 달리, 글꼴을 제외하면 벡터와 래스터 모든 분야에서 안티앨리어싱과는 담을 싼 구닥다리 API로 전락해 있다. 그러니 비트맵을 정수 배가 아닌 확대/축소를 좀 더 부드럽게 하거나, 아예 임의의 일차변환을 한 모양으로 출력하려면 최소한 GDI+ 같은 다른 대체제를 써야 한다.

(2) 운영체제가 가로줄, 세로줄 같은 몇몇 known pattern에 대해서 CreateHatchBrush 함수를 제공하긴 하는데, 50% 음영 정도는 오늘날에도 많이 쓰이기 때문에 known 패턴이 좀 제공되어야 하지 않나 싶다. 그게 없어서 수많은 프로그램들이 내부에 0x55, 0xAA 배열을 일일이 생성해서 패턴을 만드는 것은 낭비이다.
오히려 cursor는 CreateCaret 함수에 (HBITMAP)1을 줘서 50% 음영을 만드는 기능이 있는데, 정작 그건 별로 쓸 일이 없다.

(3) 브러시 말고 펜으로 선을 그리는 걸 xor 반전 연산으로 하는 기능은 없는지 궁금하지 않으신지? 임의의 사선이나 원 테두리를 그렇게 그리는 건 그래픽 에디터를 만들 때도 반드시 필요하니 말이다.
물론 그런 기능이 없을 리가 없다. SetROP2라는 함수로 그리기 모드를 바꿔 주면 된다. 단, 여기서 입력받는 래스터 오퍼레이션 코드는 BitBlt가 사용하는 코드 체계와는 다르다. 비트맵 전송 API들은 화면의 원본 픽셀(D), 그리려는 픽셀(S)뿐만 아니라 패턴(P)이라는 변수가 또 추가되어서 원래는 3변수 코드를 사용한다. BitBlt는 PatBlt가 하는 일까지 다 할 수 있는 모양이다.

Posted by 사무엘

2015/08/12 08:33 2015/08/12 08:33
, , , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1126

타이머 API 이야기

컴퓨터 프로그램이라는 건 원래 처음부터 끝까지 컴퓨터가 그야말로 눈 깜짝할 사이에 전속력으로 실행해 버리는 물건이다. 그러나 컴퓨터에는 정밀한 시간 측정 기능이 있으며, 프로그램이 원하는 경우 자신이 실행되는 주기를 그에 맞춰 인위로 조절할 수 있다.
일명 타이머 기능인데, 이것은 컴퓨터가 액세서리 차원에서 제공하는 부가 기능이 아니라 컴퓨터 자체의 내부 동작 방식의 특성상 컴퓨터가 반드시 갖추고 있는 기능이다. 단적인 예로 난수 생성을 위한 씨앗(= 매번 달라야 하는 초기값)도 내부적으로 재고 있는 시각으로부터 얻을 정도이다.

컴퓨터가 속도가 매우 느리고 자원이 부족하고, 한 프로그램이 컴퓨터의 전체 자원을 독점할 수 있던 옛날에는 매번 타이머를 측정하면서 0.n초가 경과하는 것을 프로그램이 일일이 감시하는 방식으로, 즉 polling 방식으로 동작했겠지만, 지금은 그건 어림도 없는 소리이다. 자기에게 time slice를 주는 운영체제에다가 '알람' 요청을 해서 알람이 왔을 때 동작하게 해야 한다.

Windows에서 이 기능을 사용하는 아주 대표적인 방법은 타이머 API이다. SetTimer, KillTimer와 그 이름도 유명한 WM_TIMER 메시지.
타이머는 그 성격에 따라 게임이나 멀티미디어 재생기 등에서 프레임 간격 유지를 위해 1초에 수십 번씩 돌아가는 (1) 아주 정밀한 놈부터 시작해, 수백 밀리초~수 초 정도의 간격으로 사용하는 (2) 일반적인 타이머, 그리고 드물게는 수 시간~수 일 주기를 갖는 (3) 장기 타이머도 있다. 운영체제의 보급 타이머는 일단은 가성비가 적당히 우수한 '일반적인' 용도에 가장 적합하게 설계돼 있다. 이게 무슨 뜻인지를 설명하면 이렇다.

보급 타이머도 명목상으로는 수십 밀리초 정도의 정밀도를 지원한다. 하지만, WM_TIMER는 WM_PAINT만큼이나 메시지 큐에서 처리 우선순위가 무척 낮은 메시지이기 때문에 컴퓨터가 아주 바쁘고 윈도우 메시지 트래픽이 아주 많을 때는 정밀도가 떨어질 수 있다. 더구나 Windows는 근본적으로 리얼타임 운영체제가 아닌 관계로, 커널의 시간 스케줄링을 초월해서까지 무조건적인 초정밀도는 애초에 보장되지도 않는다. 타이머의 정밀도가 올라갈수록 필요한 시스템 자원과 부하도 더 커질 테니, 초정밀 타이머가 필요하다면 QueryPerformanceCounter나 멀티미디어 타이머 같은 다른 전문 API를 쓰고 동기화도 커널 오브젝트 같은 다른 방법을 써서 해야 한다.

한편, 다른 쪽 극단에 있는 장기 타이머는 응용 프로그램 자체의 동작이라기보다는 업데이트 주기를 체크하거나 사용자에게 적당히 덜 성가신 주기로 뭔가를 알리는 용도로 사용된다. 이 정도면 타이머라기보다는 알람에 더 가깝다.
개인적으로는 지금으로부터 "5000밀리초 간격으로" 같은 것 말고, 절대적인 시각.. 예를 들어 1970년 1월 1일 0시 정각 이래로 40억 5800만 초가 딱 경과했을 때처럼 절대적인 시각을 기준으로 trigger되는 진정한 '알람' 타이머도 필요하다고 생각한다.

시계 프로그램을 만들 때는 이런 타이머 API가 더 유용하지 않겠는가? 그리고 장기 타이머를 사용할 정도의 상황이라면 지금으로부터 시간이 얼마만치 지났는지보다는 매일 몇 시가 됐는지가 더 중요한 경우가 많을 것이기 때문이다.

이렇게 극단적으로 짧은 주기의 타이머나 극단적으로 긴 타이머 말고, 보통 주기의 타이머는 여러가지 용도로 쓰인다. 가령, 키보드는 누르고 있는 동안 키 입력이 하드웨어 차원에서 자동으로 반복 전달되는 반면 마우스는 그런 게 없는데, 마우스를 누르고 있는 동안 자동 스크롤이 되는 것은 타이머로 처리가 가능하다. 그리고 간단한 비동기적인 처리를 위해서도 타이머가 약방의 감초처럼 쓰인다.

이게 도스의 제약인지 아니면 인텔 x86 CPU 차원의 제약인지 구체적인 내역은 기억이 안 나지만, 도스 시절에는 컴퓨터의 타이머 해상도가 1/18.2초여서 최소 주기가 약 55밀리초였던 것 같다. Windows 9x 시절에만 해도 운영체제의 타이머의 정밀도는 그 정도였다고 MSDN에 기록돼 있었는데 NT 계열은 하드웨어를 또 어떻게 튜닝했는지 타이머가 그것보다 훨씬 더 정밀해졌다.

자, 그럼 이 글에서는 Windows의 일반 타이머 API에 대해서 더 자세히 알아보자.
SetTimer 함수의 인자로는 타이머의 발동 주기뿐만 아니라 (1) 타이머를 메시지로 받을지 아니면 함수 호출로 받을지, (2) 그리고 메시지로 받는 경우 동일 메시지에서 이 타이머만을 식별할 번호를 지정하면 된다. SetTimer 함수는 사용하는 방법이 생각보다 좀 복잡하다.

(1) SetTimer에다가 뭔가 윈도우 핸들을 전해 주는 경우, 타이머는 메시지로 받을 수도 있고 콜백 함수로 받을 수도 있다. 두 가지 선택의 여지가 있으며, 타이머 식별 번호는 우리가 임의의 자연수로 일괄 지정해 줄 수도 있다.
(2) 그 반면 윈도우 핸들이 없이, 윈도우를 전혀 생성하지 않고도 타이머를 사용할 수 있다. 그 대신 이때는 몇 가지 제약이 따른다. 메시지가 아닌 콜백 함수로만 통지를 받을 수 있으며, 타이머 식별자는 우리가 지정할 수 없다. SetTimer 함수가 되돌린 값을 별도의 변수에다 보관하고 있어야 한다.

마치 에디팅 엔진의 기능만을 따로 떼어서 windowless 리치 에디트 컨트롤이 존재하는 것처럼 타이머도 windowless 타이머가 존재하는 셈이다. 물론 SetTimer가 무슨 스레드를 만들기라도 해서 따로 돌아가는 건 아니기 때문에, 비록 windowless 타이머를 사용한다 하더라도 메시지 loop은 돌리고 있어야 타이머가 동작할 수 있다.

개인적으로는 (1)과 (2)의 특징을 취합하는 방법이 없는 게 아쉽다. 윈도우 핸들을 지정해 줘서 WM_TIMER 메시지를 받는데 타이머 식별자는 내가 일괄 지정한 게 아니라 운영체제가 기존 타이머들과의 충돌을 피해서 동적으로 배당한 값이 오는 형태 말이다.
서브클래싱 내지 후킹을 한 윈도우에 대해서 타이머를 걸 때는 하드코딩된 타이머 ID를 써서는 안 된다. 원래의 윈도우 프로시저가 사용하는 고정 타이머 ID와 충돌을 일으킬 수 있기 때문이다. 마치 윈도우 메시지가 서로 충돌하는 것처럼 말이다.
이때는 충돌이 없음이 보장되는 windowless 타이머를 써야 한다. 하지만 windowless 타이머는 다음과 같은 이유로 인해 사용이 불편하다.

첫째, 콜백 함수에 user data를 넘겨 주는 추가 인자가 없다. 그래서 user data는 전역 변수나 TLS 값 같은 불편한 방법으로 얻어 와야 한다.
둘째, 윈도우가 붙은 타이머는 같은 ID값으로 타이머를 지정하는 경우, 기존 타이머가 새 타이머로 자동으로 대체된다. 그러나 windowless 타이머는 그런 기능이 없기 때문에 기존 ID에 대해서 KillTimer를 하고 다시 SetTimer를 해서 새 ID를 얻는 작업을 수동으로 해 줘야 한다. 다시 말해 기존 타이머의 재지정이 어렵다.

결국, 충돌을 피하기 위해서는 windowless 타이머를 써야 하는데 이 타이머도 윈도우가 붙은 타이머하고 비슷하게 동작하도록 추가 군더더기 기능을 구현한 클래스를 만든 뒤에야 그럭저럭 쓸 만하게 됐다.
윈도우가 있는 타이머와 없는 타이머에서 서로 필요한 기능을 취합하는 방법이 없어서 불편하다는 걸 다시 한 번 확인할 수 있었다.

그나저나 SetTimer 함수에서 ID를 받는 부분은 포인터나 핸들을 넘기는 용례가 없는데 자료형이 왜 UINT가 아닌 UINT_PTR로 잡혀 있는지 이것도 개인적으로는 의문이다.

Posted by 사무엘

2015/08/09 08:21 2015/08/09 08:21
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1125

1. 우리말에서 "교장 선생님 말씀이 계시겠습니다"는 높임법이 잘못 적용된 비문이다. 그러나 성경에서 "처음에 말씀이 계셨느니라"(요 1:1)는 높임법이 아주 적절하게 쓰인 문장이다.
사실, '말씀'이나 '지옥' 같은 단어는 성경 용어로서 어쩌면 영어 단어보다도 잘 번역되고 잘 만들어진 단어이다. 성경 번역 같은 데서 영어에 비해 한국어의 어휘와 문법의 한계가 많은 편이지만, 그래도 한국어가 오로지 약점밖에 없는 건 또 아니다.

2. 여느 관광 여행이나 맛집 방문 같은 것이야 당연히 백문이 불여일견이며, 직접 가서 겪어 보고 체험하는 것이 더 낫다. 그런데 글을 통한 간접 체험이 당대의 직접 체험보다 더 확실하고 더 낫다고 보증이 돼 있는 유일한 예외가 있다. 무엇일까? (힌트: 벧후 1:19, 요 20:29)

3. 세상의 다른 고전 문헌이나 골동품은 아무래도 제일 오래 된 필사본이 원문, original에 가장 근접해 있을 거라고 여겨진다. 그렇기 때문에 그런 것들은 나이가 깡패이다.
그러나 이 책만은 애초에 오래 된 필사본 같은 건 닳아 없어지고 남아 있질 않으며, 반대로 최근까지도 잡초처럼 많이 필사되어 읽히고 내용이 일치하는 것으로 교차 검증된 집단이 진본이다. 어렵게 생각할 것 없다.

4. 세상의 다른 모든 텍스트들은 원어가 당연히 원저자의 의도에 가장 근접해 있고 가치가 가장 높다. 그래서 예전에 일본어 중역이던 텍스트가 나중에 직통 번역으로 재출간되고, 그 분야 전문가는 아예 원어를 공부해서 원문을 다시 구해다 읽는다.
그러나 이 책만은 번역본이 원어 원문에 꿀릴 게 없으며, 오히려 다른 n차 파생 번역본을 모두 판단하는 절대 기준이 되는 번역본을 보유하고 있다. 이건 무엇일까?

1과 2, 그리고 3만 해도 불신자의 통념을 한참 거스르며 상식을 벗어난 논리이다. 기독교는 원래 그런 종교이다.
그러나 반대로 1~3을 일단 믿는 사람이라면 4를 믿지 못할 이유는 추호도 없다고 여겨진다. 논리적으로 그렇지 않은가? 안타깝게도 안 그러시는 분도 있지만, 그분의 양심과 믿음이 그러려니 하고 넘길 수밖에.
이것 말고 다른 분야에서도 1~9를 다 믿으면 10도 당연히 믿지 못할 이유가 없는데 도대체 왜 저러시나, 딱히 충분히 대안이 될 만한 논리 체계가 있는 것도 아닌데 싶은 게 있다.

아무리 상수도관에서 깨끗한 물이 만들어져도 가정의 수도관이 더러우면 최종 소비자는 더러운 물을 받을 수밖에 없다. 4는 앞의 다른 명제들을 성립 가능하게 하는 전제조건이라 해도 과언이 아닐 것이다.
그럼 다음으로, 이 4에 대한 보충 설명 차원에서 성경을 구성하는 언어 계층에 대해서 살펴보자.

1. 원어
성경의 '원문'이 기록된 언어이다. 구약은 히브리어(다니엘서 같은 일부는 아람 어라 함), 신약은 그리스어(헬라· 희랍은 그리스와 동의어).
그럼 원어로 원문만 읽으면 끝이고 성경의 내용 전수에 아무 문제가 없을 거라고 생각하기 쉽다. 그러나 현실에서 다음과 같은 이유로 인해 문제가 그렇게 간단하지가 않다.

(1) 오늘날 성경의 최초 자필 원본은 어디에도 남아 있지 않다.
또한 그 어떤 성경 필사본도 단일 필사본에 성경 66권이 온전히 집대성돼 있지 않다. 즉, 이것들은 partial이다. 내용 자체가 변개된 필사본이 있긴 하지만, 변개되지 않은 계열의 필사본이라 해도 결국은 빠짐없이 모아서 짜깁는 과정이 필요하다.

(2) 성경이 기록되던 당시에는 이들 언어가 인지도와 중요성이 있었지만, 지금은 그 원어가 사어가 돼 있다.
여느 언어들이 그렇듯이 원어의 어휘 역시, 같은 단어라도 문맥에 따라 뜻이 달라지는 것들이 굉장히 많다. 이 단어가 여기서는 무슨 뜻인지 분별해 줄 수 있는 절대무오한 권위자 역시 오늘날 존재하지 않는다. 오늘날 사도의 표적이 없는 것만큼이나 존재하지 않는다. 인간이 만든 사전· 어휘집도 100% 믿을 게 못 된다. 그러니 원어 원문만 있다고 해서 장땡이 아닌 거다. 환상을 깨시라.

2. 영어
오늘날 원어 원문이 갖고 있는 위의 문제들을 모두 해결하여 원어(영어) 원문(KJV) 차원의 지위를 가진 절대기준이다. 솔까말 기독교는 논리로만 따지면 권위에 호소하는 오류(?)가 있는 종교인데, 그 원천적인 권위가 무엇인지 정도는 인류 역사를 주관하는 하나님께서 보장을 해 주셔야 하지 않겠는가? 그래야만 그 체계 하에서 최소한의 '논리'와 일관성을 갖추지 않겠는가. 원어 원문 문제를 해결하기 위해서 배교한 불신자 신학자들의 말장난에 의존하지 않아도 되게 말이다.

원어가 불필요하다거나 무의미하다는 게 아니다. 단지 성경의 이 구절에서 이 원어를 어떻게 해석하는 게 맞는지 모르겠으면 KJV의 번역을 보면 된다. 요일 2:23 후반부가 원래 필사본에 있는지 없는지 궁금하면 KJV 구절을 보면 된다는 얘기다. 원어 원문조차도 KJV로 판단 가능하다. KJV는 단순히 가장 뛰어난 번역, 우수한 번역 차원이 아닌 것이다. 관점이 완전히 다르다. 이런 번역본 KJV에 하나님의 영감이 있는 걸까 없는 걸까? 판단은 여러분이.

KJV를 최종 권위로 믿고 안 믿고를 떠나서 신자라면 가슴에 손을 얹고 양심적으로 생각을 해 보시라. 기독교라면 저런 절대 기준이 상식적으로 있어야 하지 않겠는가? 우리가 믿는 건전한 하나님이라면 언어 접근성으로 사람 차별을 하지 않으시며, 그런 것쯤은 보장해 주셨을 것 같지 않은가?

지옥에 대해 경고를 하기 위해 굳이 죽었다가 살아난 사람을 보내서 증언시킬 필요가 없듯(눅 16:27-31),
원어가 무슨 뜻인지 파악하기 위해서 하나님께서 현대인들을 위해 굳이 그 시절의 원어 토박이를 무덤에서 끄집어내어 보내실 필요가 없다. 킹 제임스 성경이 있기 때문이다.
또한, 예수님께서 "나를 본 자는 이미 아버지를 보았거늘"(요 14:9)라고 책망했듯이, 킹 제임스 성경을 읽은 사람은 이미 원어 성경을 읽은 것이나 마찬가지이다. 그런 관계인 것이다. 이런 식으로 성경 언어의 관계는 성경의 여러 비유들을 통해 설명할 수 있다.

3. 자국어
절대 기준인 영어 KJV를 바탕으로 건전한 교리관을 갖춘 양심적인 번역자가, KJV의 표현을 그대로(가령, '유월절' 대신 '이스터', '기뻐하라' 대신 '잘 있으라', 요일 5:6-7도 온전히 갖추고 등) 자국어로 일관성 있는 스타일로 번역할 수 있다. 그 성경은 교회에서 예배를 드리고 복음 전하고 신앙 서적을 만드는 용도로 쓰일 수 있다. 이것은 "신들과 같이", "절대무오한"은 못 되더라도 노아나 욥이 "완전했던" 것과 같은 완성도를 갖췄다.

해당 자국어의 특성을 이용해서 번역을 아주 적절하게 할 수도 있지만(하나님, 말씀, 지옥 등), 어휘와 문법 체계의 특성상, 그리고 해당 언어권의 문화· 관습의 한계로 인해 KJV 특유의 도치나 중의성, 운율, 미묘한 문법 요소들을 다 담지 못할 수도 있다. 그건 해당 언어나 번역자의 자질 문제가 아니다. 성경 강사가 영어 KJV를 참고하여 보충 설명을 해 주면 된다. 마치 데나리온이라는 화폐 단위가 요즘 물가로 얼마 정도라고 얘기하듯이. 오늘날 영어의 지위를 생각하자면 히브리어, 그리스어를 꺼내는 것보다야 상황이 훨씬 더 나아진 것이다.

(그럼 원어에서 영어로 번역될 때는 원어의 모든 뉘앙스가 고스란히 옮겨지는 게 가능했느냐 하는 반문이 있을 수 있다. 이에 대해서는 언어학적인 팩트 답변도 있고, 그것만으로 좀 확신이 안 서서 믿음의 영역으로 그냥 받아들이고 넘겨야 하는 면모도 있다. 이 글에서는 이 정도까지만 얘기하도록 하겠다.)

다만, 각 언어마다 최종 권위가 제각각 또 있는 건 아니다. 그건 최종 권위라는 게 무슨 뜻인지 파악을 못 한 말도 안 되는 소리다. 단지 모든 언어적인 혼란을 일축하는 중심점에 영어 KJV가 있다는 것이 KJV 유일주의 신자의 믿음이다.

'영킹 유일주의자', '이중 영감론자' 등의 누명 내지 딱지에 전혀 움츠러들 필요가 없다. 그럼 그들이 미는 대안은 뭔데? '원어 원문 유일주의자'이건 '영어 숭배자'건 '자국어 만능주의자'이건, 어느 편에 서더라도 그에 대한 멸칭은 얼마든지 지어낼 수 있다. 무엇을 선택하든지 결국은 뭔가를 신념으로 믿는다는 점에서는 똑같다. 마치 무신론도 유신론만큼이나 동일하게 신념이고 신앙인 것처럼 말이다.

그럼 우리는 무엇을 믿어야겠는가? 원어 원문은 앞서 말했듯이 실체가 없으며, 반대로 자국어 최종 권위 운운은 당장 생각해 봐도 말도 안 되는 소리이다. 그러느니 차라리 영어 중심이 가장 현실성 있고 균형 잡혔으며, 실제로 KJV의 출간 이래로 지난 400여 년간 역사적인 증거와 열매도 넘치는 건전한 관점이다. 애초에 예수천당 불신지옥 같은 과격하고 극단적인 교리를 믿는 신자가, 한 성경 역본만이 절대적으로 옳고 이와 일치하지 않는 역본은 틀렸다고 믿지 못할 이유는 단언하건대 절대로 없다.

Posted by 사무엘

2015/08/06 08:31 2015/08/06 08:31
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1124


블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2015/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:
3936581
Today:
997
Yesterday:
4175