※ 들어가는 말

정렬은 검색과 더불어 컴퓨터가 인간에게 유용한 결과물을 내놓기 위해 내부적으로 가장 빈번히 수행하는 계산 동작에 속한다. 다른 알고리즘의 내부 과정으로 즐겨 쓰이기도 하고 말이다. 전산학 내지 컴퓨터 과학에서 정렬 문제가 얼마나 중요한지에 대해서는 더 말이 필요하지 않다.

정렬은 문제의 목표가 너무나 명확하고 실용적이며, 다양한 관점에서 문제의 접근이 가능하고 좋은 알고리즘과 나쁜 알고리즘의 차이도 아주 드라마틱하게 알 수 있기 때문에... 예로부터 그 특성과 해법이 연구될 대로 연구되어 왔다. 시간 복잡도 관념이 없던 초짜 프로그래머가 O(n^2)와 O(n log n)의 어마어마한 차이를 깨우치는 계기도 대체로 정렬 알고리즘을 공부하고부터이다.

n개의 원소에 대한 정렬 작업은 n개의 원소를 임의의 방식으로 늘어놓는 n!가지의 순열 중에, 원소들의 값 순서가 오름차순이나 내림차순이 유지되는 순열을 선택하는 작업이라고 볼 수 있다. 그리고 일반적인 정렬 알고리즘은 임의의 두 원소와의 비교를 통해 거기서 가능한 선택의 범위를 좁혀 나간다.

이런 원론적인 분석을 통해, 비교 연산 기반 정렬 알고리즘의 시간 복잡도는 아무리 기가 막힌 알고리즘을 고안하더라도 O(n log n)보다는 결코 더 좋을 수가 없다는 것이 증명되어 있다. 그리고 정렬 알고리즘 중, 제자리(in-place)라는 특성을 지닌 알고리즘은 교환(swap)이라는 동작도 공통적으로 사용하게 된다.

정렬 문제는 NP 완전 문제라고 알려져 있는 외판원 문제(TSP)에서 정점(vertex)들이 일렬로 쭉 나열되어 있는 특수한 경우라고 볼 수도 있다. 가까운 순서대로 순서대로 방문하는 게 정답일 테니 결국 정점들이 정렬된 것이나 마찬가지이다. 비록 domain이 1차원이 아닌 2차원 이상으로 가면 난이도가 곧바로 안드로메다 급으로 치솟지만 말이다.

※ O(n^2) 또는 O(n log n)인 비교 기반 알고리즘

역사적으로 굉장히 많은 수의 정렬 알고리즘이 고안되었으며 이들은 제각기 장단점과 특성이 있다. 알고리즘을 평가하는 주 잣대로는 자료 개수 n에 대한 시간 복잡도와 공간 복잡도가 있으며, 이들도 평균적일 때와 최악의 상황일 때를 따로 평가한다. 이 외에도 자료의 상태에 성능이 민감하게 달라지는지, 그리고 값이 같은 원소의 상대적인 순서가 보존되는지를 나타내는 순서 안정성(stability)을 따지기도 한다.

시간 복잡도가 O(n^2)에 속하는 정렬 알고리즘은 일명 '발로 짠 알고리즘'에 속한다. 직관적이고 구현하기 매우 쉬우나 성능이 쥐약이라는 뜻.
거품 정렬, 선택 정렬, 삽입 정렬이 대표적인데, 거품의 경우 배열이 아니라 아예 random access가 불가능한 연결 리스트 같은 컨테이너에다가 적용해도 좋을 정도로 바로 옆 원소와의 비교와 교환밖에 하지 않는다. 그 때문에 성능이 대단히 나쁘다.

선택 정렬은 비교에 비해 대입 연산이 적고 자료의 상태에 그리 민감하지 않은 게 특징이다. 그에 반해 삽입 정렬은 자료 상태에 따른 성능 편차가 크고 O(n^2) 알고리즘 중에서는 성능이 나은 편이기 때문에, 작은 범위의 입력에 한해서 종종 쓰이는 경우가 있다. 실제로 비주얼 C++의 qsort 함수 구현을 보면, 퀵 정렬을 쓰다가 구간이 8개 이하의 원소로 감소하면 거기는 삽입 정렬로 때운다.

O(n^2) 알고리즘들은 원리가 간단하기 때문에 공간 복잡도는 대체로 O(1)인 in-place이다. 한 쌍의 원소를 그때 그때 교환하기 위한 고정된 크기의 메모리밖에 쓰지 않는다는 뜻 되겠다. 시간이 비효율이면 공간 오버헤드라도 없어야 하지 않겠는가.

이론적인 시간 복잡도에 부합하는 O(n log n)급 알고리즘으로는 힙, 병합, 퀵 등이 있다. 이들은 시간 복잡도만 동일할 뿐 내부적인 특징은 정말 제각각이다.

일단 힙 정렬은 위의 세 알고리즘 중에서 유일하게 메모리 복잡도가 O(1)인 검소한 녀석이다. 그 대신 한 배열 안에서 왔다 갔다 하는 작업이 많아서 그런지 속도는 미세하게 다른 알고리즘보다 더 느린 편. 한 배열 안에서 heap 자료구조를 만든 뒤, 이것으로부터 정렬된 형태의 배열을 역순으로 만드는 두 단계의 과정이 무척 기발하며, 인간의 머리로 어째 이런 걸 생각해 낼 수 있는지 놀라움을 느낀다.

병합 정렬은 동급 시간 복잡도 알고리즘 중에서는 꽤 직관적인 편이고 또 유일하게 안정성도 있어서 좋다. 그러나 FM대로 구현한 녀석은 배열 복사본이 하나 더 필요하기 때문에 메모리 복잡도가 O(n)이나 되며, 대입에 대한 비용이 큰 자료구조에 대해서는 성능 하락의 폭이 큰 게 흠이다.

※ 퀵 정렬

한편, Tony Hoare이라는 영국의 전산학자가 1960년대에 20대 중반의 나이에 고안한 퀵 정렬은 정렬 알고리즘계의 종결자, 야생마, 이단아 같은 존재이다. pivot이라 불리는 중간값을 설정하여, 주어진 구간을 “pivot보다 작은 값, pivot, pivot보다 큰 값” 조건을 만족하게 swap 연산을 통해 바꾼다. 그 뒤, pivot을 기준으로 구간을 양분하여 양 구간도 재귀적으로 똑같은 작업을 한다. 알고리즘도 너무 명쾌하고 깔끔하지 않은가?

이 알고리즘은 대충 부분적으로 정렬되었거나 아예 완전히 무작위인 데이터에 대해서 매우 대단히 좋은 성능을 자랑한다. 그러나 pivot을 어떻게 정하느냐에 따라서 알고리즘의 성능이 크게 좌지우지되며, 자료의 상태에도 매우 민감해진다는 점이 간과될 수 없는 특성이다.

pivot이 데이터의 적당한 중간값으로 설정되지 못하고 하필이면 최소값이나 최대값으로 설정된 경우, 알고리즘 수행 후에도 구간은 깔끔하게 양분되지 못하고 하나씩만 줄어들게 된다. 이 경우 알고리즘의 수행 시간은 O(n log n)이 아니라 O(n^2)에 가까워진다! 역순으로 정렬된 데이터를 정렬하는데 구간의 맨 앞이나 맨 뒤의 값을 pivot으로 쓴다고 생각해 보자.

문제는 이때 시간 복잡도만 늘어나는 게 아니라는 것이다. 분할 정복법을 쓴다는 특성상 퀵 정렬은 재귀호출을 써서 구현되는데, 구간이 반씩 시원하게 안 쪼개지고 하나씩만 쪼개지면 재귀호출의 깊이도 자칫 n회가 될 수 있다는 뜻이다. 이 경우 프로그램은 stack overflow 오류가 발생하며, 이는 프로그램의 보안에도 악영향을 끼치게 된다.

다만, 쪼개진 구간 중에 원소 수가 많은 구간이 아니라 의도적으로 적은 구간부터 골라서 재귀적으로 처리하는 경우, 메모리 복잡도는 O(log n)으로 원천적으로 줄일 수 있다. 퀵 정렬 함수의 구현체 자체에 딱히 동적 배열 같은 게 없더라도 재귀호출 때문에 메모리 복잡도가 올라가며, 원소들이 정확하게 반씩 분할될 경우에 log n에 해당하는 깊이까지 간다는 뜻이다.

일반적으로 퀵 정렬의 구현체는 그냥 구간의 정중앙에 있는 원소만 pivot으로 지정하는 게 보통이다. 이렇게만 하더라도 O(n^2)의 최악 시간 복잡도를 만드는 입력 데이터를 일부러 만들기란 대단히 어려우며, 수학적으로 발생하기도 불가능에 가까운 건 사실이다.

하지만 공격자가 퀵 정렬 구현체의 알고리즘을 알고 있는 경우, 의도적으로 해당 알고리즘이 pivot을 요청할 만한 위치에 일부러 구간의 최대값이나 최소값을 집어넣어서 매 단계별로 퀵 정렬을 엿먹이는 게 불가능하지는 않다! 세상엔 그것만 전문적으로 연구한 사람도 있다. anti quick sort라고 검색해 보셈.. 이것이 퀵 정렬의 진정 오묘하고 이상한 면모라 하겠다.

이걸 이용하여 비주얼 C++의 qsort 함수로 테스트하면, 평소 같으면 인텔 i5 기준 눈 깜짝할 사이에 끝나는 정수 10만 개의 정렬이 수 초 대로 떡실신하는 기현상이 벌어지는 걸 볼 수 있다. 그런데 xcode의 C 라이브러리가 제공하는 qsort는 퀵 정렬을 쓰지 않는지 그런 것의 영향을 받지 않더라..

※ C/C++ 언어에서의 지원

C 라이브러리에 있는 qsort 함수는 콜백 함수에 전달해 줄 사용자 데이터--가령, 비교 옵션 같은 것--를 받는 부분이 없어서 무척 불편하다. 그래서 별도의 사용자 데이터는 전역 변수나 TLS(thread local storage)를 통해 얻어 와야 하는 번거로움이 있다. 이것이 비주얼 C++ 2005부터 도입된 qsort_s에서는 개선되었다.

한편, C++ 라이브러리에도 잘 알다시피 std::sort라는 함수가 있다. C 함수보다 type-safe할뿐만 아니라 iterator를 통해 포인터보다 더 추상적인 자료형도 정렬할 수 있으며, 비교도 직관적인 비교 연산자 아니면 functor로 편리하게 지정할 수 있어서 좋다. 또한 이건 템플릿 형태이기 때문에 정렬 코드가 해당 프로그램의 번역 단위에 최적화된 형태로 embed된다는 것도 더욱 좋다.

C의 경우 비교 연산 함수의 리턴값은 뺄셈 연산을 모델로 삼아서 '음수, 0, 양수' 중 하나를 되돌리게 되어 있다. 그러나 C++ 버전은 < 연산을 모델로 삼아서 그냥 true/false boolean값만 되돌리면 된다는 차이가 있다. 사실, 그것만 있어도 정렬이 되니까 말이다.

C++ 라이브러리에는 sort뿐만이 아니라 stable_sort도 있다. 하지만 실생활에서 꼭 stable_sort를 써야만 할 상황이 있는지는 모르겠다. 실제로 정렬 성능은 굳이 안정성이 지켜지지 않아도 되는 sort가 더욱 뛰어나다.

※ 기타 정렬 알고리즘

정렬 알고리즘의 시간 복잡도는 굳이 O(n^2) 아니면 O(n log n) 중 하나로만 떨어지는 게 아니다. 그 범주에 속하지 않는 대표적인 알고리즘은 셸 정렬이다. 고안자의 이름을 따서 명명된 이 알고리즘은 삽입 정렬이 대충 정렬된 자료에 대한 성능이 뛰어나다는 점을 응용하여, 삽입 정렬을 일정 구간별로 띄엄띄엄 반복해서 적용해 준 뒤 최종적으로 삽입 정렬을 full scale로 한번 돌려서 정렬을 끝낸다.

퀵 정렬이 pivot을 정하는 것이 판타지라면, 셸 정렬은 그 구간을 정하는 방식이 판타지이다. 셸은 분명 O(n^2)보다는 훨씬 더 뛰어난 성능을 보이지만 그렇다고 O(n log n)급은 아니다. 사실, 셸은 구간을 어떻게 설정하느냐에 따라서 시간 복잡도를 계산하기가 대단히 chaotic하고 어렵다.

구간을 두 배씩 좁히는 게 제일 나쁜 방법이이기 때문에 최악의 경우 도로 O(n^2)까지 떨어져 버리나, 약간 머리를 쓰면 O(n^1.5) 정도는 된다. 구간을 가장 잘 잡았을 때 최대 O(n (log n)^2)까지는 갈 수 있다는 것이 알려져 있다. 그래도 셸은 메모리 복잡도가 깔끔한 O(1)이고, 코딩이 상당히 짧고 간결하면서도 O(n^2)보다는 성능이 확실히 낫다는 데 의의가 있다.

앞서 말했듯이 정렬 알고리즘의 시간 복잡도의 한계가 O(n log n)이라는 것은 비교 연산을 사용하는 일반적인 알고리즘이 그렇다는 소리이다. 그런 방식으로 정렬을 하지 않는 알고리즘의 경우, O(n)짜리 알고리즘도 충분히 존재할 수 있다.

가령, 데이터의 도메인이 메달이어서 '금, 은, 동'이라는 세 종류밖에 없는 경우, 자료를 일일이 뒤져 볼 필요 없이, 각 메달의 개수를 세어서 금 a개, 은 b개, 동 c개라고 써 주기만 하면 될 것이다. 부동소숫점이나 문자열처럼 도메인이 굉장히 넓은 자료형은 그런 식으로 정렬할 수 없겠지만, 좁은 범위의 정수 정도면 그런 식으로 발상을 전환하여 비교 연산을 요청하지 않는 정렬 알고리즘을 쓸 수도 있다.

여기에 속하는 대표적인 알고리즘은 기수(radix) 정렬이며, 이 외에도 유사한 전략을 사용하는 알고리즘이 더 있다.

정렬 알고리즘에 대해서는 메아리 풉에도 수학적으로 더 엄밀한 개념 기술이 있으므로 참고하시고, 또 이 홈페이지에는 이미 아시는 분도 있겠지만 본인이 학부 시절에 정렬 알고리즘 모음집이라는 간단한 프로그램을 짜서 올려 놓은 게 있다. 일부 검색엔진에서는 '사이트'로도 등록되어 있다. ㅎㅎ 관심 있으신 분은 거기 소스도 참고하시기 바란다.

* 여담이지만, 전산학 덕후와 해커들의 머리 싸움 덕질에는 끝이 없는지라, 퀵 정렬뿐만 아니라 hash 알고리즘을 엿먹이는 연구도 이미 될 대로 돼 있다.. 특정 해싱 알고리즘에 대해서 충돌만 골라서 일으키는 입력을 생성하는 것 말이다.

Posted by 사무엘

2012/10/04 08:24 2012/10/04 08:24
, , ,
Response
No Trackback , 5 Comments
RSS :
http://moogi.new21.org/tc/rss/response/740

(上에서 계속.. 현대 자동차 얘기를 하고 있었다.)

현대에서는 포니 2에 이어 후속 모델로는 프레스토와 엑셀이 나왔고, 중형차로는 국내 최장수 자동차 브랜드인 쏘나타가 탄생했다. 쏘나타도 그 전신은 코티나 마크 V의 파생형인 '스텔라'이지만, 후속 모델로 갈수록 미국 차와의 유사성은 없어지고 독창성이 증가했다.

한편, 당대의 최고급 모델이던 (각)그랜저는 미쓰비시 사와 공동 개발하여 동일한 차량을 한일 각국에서 서로 다른 브랜드로 시판했다. 처음엔 2000cc급만 나왔다가 2400cc와 3000cc 모델도 추후에 개발되었다. 오늘날이야 그랜저는 제네시스나 에쿠스에게 기함 타이틀을 내 주고, 그냥 쏘나타보다 약간 더 비싼 중대형급에 머물러 있지만, 사람들에게 각인되어 있는 그랜저의 이미지는 굳건하다.

또한 철덕이라면 그랜저와 새마을호 전후동력형 디젤 동차 사이에 매우 유사한 심상이 느껴질 것이다. 둘 다 등장 시기(1986 vs 1987)부터가 아주 유사하며 목적도 동일하다. 서울 올림픽을 앞두고 각각 최고급 승용차와 최고급 호화 열차가 필요했기 때문이다. 세월이 흘러 지금은 그때보다 굉장히 대중화(?)와 서민화가 진행되었지만 그래도 고급 물건의 대명사로 통용되고 있는 것도 공통적이다.

자동차 산업 합리화 조치가 풀린 뒤엔 포터(1톤)와 마이티(2.5톤) 트럭을 만들어서 기아의 봉고/타이탄과 경쟁하였다. 포터는 '짐꾼'이라는 뜻이고 마이티는 왈도체의 '힘세고 강한 아침' 할 때의 '힘센'이라는 뜻이니, 다들 트럭으로서는 적절한 작명이라 여겨진다. 한편, 그레이스라는 소형 승합차는 디젤 엔진으로 휘발유 엔진에 필적하는 정숙함을 구현해 내어 그 당시로서는 꽤 발전된 기술을 선보였다고 한다.

그리고 Aero City라는 대형 버스를 만들어서 대우 버스와 경쟁하는 양대 산맥을 구축했으며, 일본 차량을 기반으로 갤로퍼라는 SUV도 만들어서 쌍용 코란도를 제쳤다. 단, 갤로퍼는 현대 자동차가 아니라 '현대 정공'에서 제작하여 '현대 자동차 써비스'라는 다른 계열사와 다른 파생 회사에서 판매하는 형태였기 때문에, 전통적인 현대 자동차 라인의 제품이 아니다.

아래 그림에 나와 있듯, 현대 버스(왼쪽)는 대우 버스(오른쪽)와는 달리, 전통적으로 바퀴 위쪽의 차체 윤곽이 완전한 원호를 이루어 동그랗다는 특징이 있다.

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

이렇게 기술을 개발해 나가던 현대에서는 부품의 국산화 비율을 차츰차츰 높인 끝에, 드디어 설계부터 프레임, 엔진까지 모든 공정을 국산화하여 로얄티를 지불하지 않는 차를 내놓는 경지에 이르렀다. 그 첫 작품이 바로 엑셀의 후속 모델 소형차인 액센트(1994)이다. 포니가 자체 모델이라면, 액센트는 자체 개발이다. 자동차계의 KTX 산천 및 서울 지하철 609편성인 셈이다.

포니를 만들던 그 회사가 이제는 제네시스와 에쿠스를 만드는 경지에까지 이르렀다. 그 결실을 위해 공돌이들을 얼마나 갈아 넣었는지 나로서는 알 길이 없지만, 어쨌든 존경스럽다. 비록 국내에서 워낙 독보적인 위치에 있어서 가격 횡포도 많이 부리고, 이 때문에 현대라는 기업을 싫어하는 사람도 적지 않지만 말이다.

에쿠스야 1세대 '각진' 모델은 과거의 그랜저처럼 미쓰비시와의 공동 개발이지만, 제네시스는 현대의 독자 개발 모델이며 에쿠스도 2세대 모델은 외형이 제네시스와 더 비슷해져 있다. 제네시스와 에쿠스는 현대 차임에도 불구하고 외제차처럼 보이게 하려는 의도인지 차 주변에 현대 앰블렘이 보이지 않는 게 특징이다.

사용자 삽입 이미지

※ 쌍용 자동차

신진 자동차, 동아 자동차를 거쳐서 지금의 쌍용이 된 기업이다. '더블 드래곤'은 엄밀히 말하면 '쌍룡'으로 표기하는 게 맞으나, 어차피 저건 고유명사이니 굳이 꼭 맞출 필요는 없다.
여기는 잘 알다시피 코란도라는 4WD SUV 외길 브랜드만 밀어 온 기업으로 유명하다. 물론 옛날에는 자동차 산업 합리화 조치 때문에 쌍용에 배당된 차종 TO는 저것밖에 없어서이기도 했고. -_-;;

옛날에 4WD 차량은 “전쟁 났을 때 국가에서 군용차로 징발해 간다. 그 대신 차의 덩치에 비해 각종 세금은 파격적으로 감면”이라는 조건이 걸려 있었다. 그래서 전쟁 안 날 거라 믿고 코란도를 장만한 사람들도 적지 않았다. 물론 지금은 다 옛날 얘기가 됐지만.
그리고 코란도라는 이름은 “KORean cAN DO”라는 애국심 드립에서 유래되었다는 걸 혹시 아시는가? 어렸을 때 차 카탈로그에서 본 기억이 있다.

사용자 삽입 이미지
추억의 1세대 코란도.

나중에는 같은 SUV 차종 안에서도 코란도 패밀리라든가 무쏘라는 다른 차를 내놓기도 했으며, 소형 승합차 이스타나, 그리고 고급 승용차 체어맨을 만들었다. 그러나 자체 기술이 부족하여 주력 차종인 SUV에서 현대에게 추월당하고 주춤하다가, 중국 상하이 자동차에게 매각+처참한 먹튀를 당했다. 그리고 최근엔 잘 알다시피 구조조정+장기간의 파업 사태 때문에 기업 이미지가 상당히 나빠져 있다. 위기를 잘 극복해야 할 텐데.

공장이 평택에 있는 건 파업 사태와 관련된 뉴스 보도 때문에 알게 됐다.

※ 르노 삼성 자동차

삼성은 삼성 전자를 등에 업고 있는 굴지의 대기업이지만, 이 그룹의 회장님은 롤스로이스와 마이바흐를 굴리는 굉장한 자동차 덕후였으며 자기 회사에서 자동차까지 만들고 싶어하고 있었다. 그래서 1990년대 말에 자동차 계열사를 만들었지만, 이미 국내의 차 시장은 포화 상태였고 IMF까지 터지면서 삼성 자동차는 제대로 돌아갈 수가 없었다. 그래서 르노라는 외국 회사가 이를 인수하여 르노 삼성 자동차가 된 것이다.

자동차를 처음부터 혼자 만들 수 있는 회사는 없으니 여기서도 주로 일본 닛산 자동차를 현지화하여 생산· 판매하는 형태였다. 생산되는 승용차는 잘 알다시피 SM_n이라는 형태로 작명되었으며, 특히 현대 계열사를 싫어하는 사람들이 그 대안으로 삼성 차를 선호했다고 회자된다.
하지만 지금은 여전히 회사 사정이 안 좋아서 직원들을 상대로 희망 퇴직도 받고 있는 모양이다. 공장은 부산 강서구에 있음.

※ 아시아 자동차

아시아 대학교만큼이나 지금은 사라진 회사 이름이지만, 그렇다고 그 대학처럼 막장 행보를 간 회사는 물론 아니었다.
금호 그룹만큼이나 국내에 얼마 안 되는 호남 기업 중 하나이다. (그래서 공장도 광주에~!) 그러고 보니 아시아나 항공도 이쪽 계열사인데, 이 이름도 '아시아'에서 유래되었다. ㅎㅎ

1970년대에는 이탈리아의 피아트를 판매하다가 나중에 기아 자동차에 인수되었다. 둘 다 기업 앰블렘이 비슷하게 생긴 게 이 때문인 듯하다. '록스타'라는 SUV, 콤비· 코스모스라는 버스, 타우너라는 트럭이 이 회사의 제품이며, 특히 대형 버스 그랜버드는 오늘날까지도 살아 있는 브랜드이다. 쌍용이 SUV라면 아시아는 버스인 듯.

내가 초등학생일 때까지만 해도(지금으로부터 20년쯤 전~!) 아시아 자동차에서 만든 시내버스를 어렵지 않게 볼 수 있었고, 차내의 선바이저(sun visor)에는 “여행은 아시아 자동차 버스로”라는 문구가 적혀 있기도 했다. 아래 사진을 참고하라.

사용자 삽입 이미지

.
.
.

컴퓨터를 살펴보면 과거에는 PowerPC, Alpha, MIPS 등 여러 아키텍처가 있었다. 그러나 지금은 그냥 닥치고 x86(-64) 아니면 ARM밖에 살아남은 게 없다. 프린터는 HP 말고 다른 제조사는 가히 듣보잡으로 전락했고, 그래픽 카드는 nVIDIA에 기껏해야 ATI나 인텔 말고 지금 생존한 물건이 있나?

그런 것처럼 자동차도 과거에는 생각보다 다양한 제조사들이 존재하였으나, 지금은 기술과 자본줄이 탄탄한 한두 업체 말고는 다들 몰락했다. 사실은 어느 분야라도 안정화가 되고 나면 그렇게 되는 것 같다. 이 와중에 시종일관 살아남았고, 완전히 새로운 차를 밑바닥 부품부터 스스로 다 설계하고 창조하는 경지에까지 다다른 국내 기업은 사실상 현대가 유일하다.

물론 그게 전적으로 현대의 기술과 노력만으로 이루어진 건 아닐 것이다. 보호 무역 버프는 말할 것도 없고, 국가로부터 지원과 특혜도 엄청 받았을 것이며 “내수는 비싸게, 수출은 싸게” 식으로 온 국민이 간접적으로 국내 기업을 육성하는 데-_- 일조도 했을 것이다.

하지만 반대로 생각해 보면, 그런 특혜를 입지 않고서야 이미 수십년 이상의 격차가 존재하는 외국의 넘사벽급 자동차 기술을 어떻게 따라잡겠는가? 또한 그렇게 혜택을 처묵처묵하고도 먹튀하는 막장 기업도 많은 판에, 현대 정도면 그래도 다른 기업들보다 기술을 중시하여 성장도 많이 했다. 그로 인한 막대한 양의 수출+일자리와 국부 창출은 덤이고 말이다.

어쩌다 보니 현대를 좀 칭찬하는 논조가 되고 말았는데, 난 딱히 현대 자동차의 이익을 대변하는 사람이 아니며, 그저 “깔 건 까고 인정할 건 인정하자”라는 중립적인 시각임을 밝힌다. 철도 때문에 지금까지 상대적으로 가려져 있던 자동차 쪽 덕질을 오랜만에 해 보니 재미있다. ^^

Posted by 사무엘

2012/10/01 19:32 2012/10/01 19:32
, ,
Response
No Trackback , 6 Comments
RSS :
http://moogi.new21.org/tc/rss/response/739

교회사에서 특별히 성경의 역사를 공부해 보면, 영어 성경이란 건 위클리프 이래로 킹 제임스에 이르기까지 긴 시간 동안 아주 점진적으로 발전이 이뤄져 왔음을 알 수 있다.

영어로 된 최초의 신구약 성경전서(위클리프), 최초로 왕이 승인한 성경(커버데일/그레이트), 최초로 국내에서 인쇄(매튜), 최초로 중역이 아니라 원어에서 곧장 번역(제네바), 최초로 위원회가 조직되어 번역(비숍), 최초로 장· 절 구분 추가(제네바) 등등~~
그러다가 이 모든 장점들이 합쳐져서 성경의 종결자를 이룬 것이 킹 제임스 성경이다.

그런데, 대한민국의 자동차 역사도 그렇게 점진적이었다.
우리나라에 최초의 철도가 개통한 게 잘 알다시피 1899년 경인선인데, 그 무렵에 왕이나 외국 외교관을 중심으로 한반도에 최초로 자동차라는 기계가 다니기 시작했다. 1900년대 초에 고종 황제가 탄 어차(御車)는 영국에서 만들어진 다임러 리무진이었으며, 운전대는 오른쪽에 달려 있었다. 다만, 오늘날 전해져 오는 건 그 다음 1910년대에 도입된 순종 어차 위주이다.

일제 강점기 때는 시보레, 포드 같은 수입 외제차가 부유층을 중심으로 도로를 누볐다. 드라마 각시탈을 보니 올드카를 애써 임대한 것까지는 좋으나, 운전대가 우측통행을 염두에 둔 왼쪽에 있는 것은 고증 오류이다. 그 시절에는 한반도에서도 차량이 일본처럼 좌측통행을 했다.

(☞ 일제 강점기 시절의 자동차 광고)

그러다가 우리나라는 일제로부터 해방되었다. 그때까지만 해도 한반도에는 자동차 정비 공장까지는 있었지만, 그 불모지에서 자동차를 처음부터 끝까지 만들어 낼 수는 없었다.

그랬는데 최 무성· 최 혜성· 최 순성 엔지니어 삼형제가 국제차량제작이라는 무슨 다국적 기업 같은 이름의 회사를 설립하고, 1955년에 '시발(始發..;;)'이라는 지프형 자동차를 만들어 냈다. 수입한 부품을 조립하는 수준에서 크게 벗어나지 못했겠지만, 이게 바로 죽이 됐든 밥이 됐든 대한민국 땅에서 한국인이 최초로 만들어 낸 자동차이다. 해방 후의 시기이니 운전대는 왼쪽, 주유구는 오른쪽으로 우측통행 기준이다.

1960년대부터의 국내의 자동차 역사는 회사별로 살펴보도록 하겠다.

※ 기아

창업주가 한 근성 하는 분인 건 인정하지 않을 수 없다. 해방 이후부터 차근차근 자전거, 오토바이, 삼륜차를 거친 끝에 마침내 자동차까지 직접 만드는 수준으로 기업을 키웠기 때문이다. 지금의 삼천리 자전거가 원래 기아 산업의 계열사였다.

기아에서는 1960년대에 일본 차체를 바탕으로 삼륜차를 만들었다. 우리나라에도 지금은 태국이나 중국 같은 나라에서나 볼 수 있는 삼륜차가 다니던 시절이 있었다.

사용자 삽입 이미지

삼륜차는 승용차가 아니라 트럭 형태로만 만들었던가 보다.
이런 차는 덩치가 작아서 좁은 골목길에 잘 들어가고 가격과 유지비도 저렴해서 실속이 있었다. 그래서 짐 실어 나르는 생계 수단 및 사업 밑천으로 차를 장만하려는 사람들에게 큰 환영을 받았다고 한다.
지금은 잉여가 된 '1종 소형' 면허가 바로 삼륜차 운전이 가능한 면허이다.

1974년에 기아는 기존 일본차(마쓰다 파밀리아) 프레임을 기반으로 '브리사'라는 소형 승용차를 개발했는데, 이것이 대한민국 역사상 최초의 국산 보급형 승용차라고 한다. 정부가 요구한 수준의 국산화율을 가장 먼저 달성하였으며, 배기량도 1000cc대의 소형이어서 당대 세계 경제를 강타하던 오일 쇼크에 대응하기에도 유리했다고 한다.

사용자 삽입 이미지

본인은 초등학생 시절이던 1990년대 초에 브리사 실물을 몇 차례 본 적이 있다. 그래서 더욱 애증이 교차한다. 하지만 멀쩡한 모습뿐만 아니라 사고가 나서 부서진 폐차 상태의 모습으로도 많이 봤다. 사진으로는 저 흰색 사진이 유명해서 인터넷에 많이 나돌지만, 본인은 자주색 도색을 더 자주 봤다. 그리고 브리사 2는 실물을 본 적이 없다.

기아에서는 이미 브리사의 디젤 모델까지 개발을 염두에 두고 있었다. 그러나 1981년에 자동차 산업 합리화 조치로 인해 승용차 생산을 못 하게 되면서 계획은 흑역사가 되고, 그 대신 봉고(1톤급 소형 승합차 및 트럭), 타이탄(2.5톤 트럭), Boxer(4.5톤 트럭) 같은 다른 차종에서 근근히 인지도를 유지하게 된다. 특히 트럭으로는 유일하게 사륜구동이 가능한 영농인 최적화용 트럭인 '세레스'를 만들기도 했고, 비슷한 맥락에서 레토나나 두돈반 같은 군용차도 이 회사에서 만들어서 납품한다.

훗날 산업 합리화 조치가 풀리면서 기아에서는 그 이름도 유명한 승용차 프라이드를 내놓고, 중형차로는 콩코드를 밀기 시작했다. 1990년대로 들어서서는 캐피탈, 세피아, 크레도스 등 다양한 차들을 만들었으나, 오늘날은 쏘나타의 경쟁 모델인 K5, 그랜저의 경쟁 모델인 K7 같은 식으로 K_n이라는 간단한 네이밍으로 자사 제품에 이름을 붙이는 듯하다.

기아 자동차 소속 공장으로는 과거의 아시아 자동차 공장을 인수한 광주 공장, 화성 공장, 그리고 광명 소하리 공장이 있다. 다만, 잘 알다시피 기아 그룹이 IMF 시절에 부도가 나면서 오늘날 기아 자동차는 현대 자동차 그룹의 계열사가 되었다. 현대 자동차 그룹 아래에 현대 자동차와 기아 자동차가 나란히 있는 셈이다. 응?? 그래서 오늘날 생산되는 현대 차와 기아 차는 일부 엔진 부품이 상호 호환되기도 한다.

※ 대우

한때는 세계 경영(세계는 넓고 할 일은 많다~!)을 부르짖으며 자동차도 만들고 컴퓨터도 만들던 대기업이었는데, 지금은 완전히 잊혀진 브랜드로 전락했다. 안습.

대우 자동차는 현대나 기아에 비해서는 기업의 정체성을 설명하기가 다소 복잡하다. 신진 자동차 공업, 새나라 자동차, 새한 자동차 등 경영 주체가 여러 번 바뀌었던 회사가 최종적으로는 GM 코리아를 거쳐서 대우 계열사로 넘어온 형태이기 때문이다. 대우 자동차라는 정식 명칭이 붙은 회사가 생긴 건 1983년의 일이다. 물론 이 이름은 그로부터 20년 남짓밖에 존속하지 못했지만 말이다.

나의 어린 시절, 1990년대 초반에는 가끔 완전 옛날 스타일의 대형 트럭이 보였다. 요즘 국내에는 군용차를 제외하면 버스나 대형 트럭이 엔진룸과 앞바퀴가 운전석의 앞에 달린 형태가 없으며, 그런 건 미국에서나 볼 수 있다. 그런데 이 트럭은 미국 스타일이었고, 앞에 SMC라는 이니셜이 붙어 있었다. 그것도 지금 생각해 보니 새한 자동차의 작품이었던 것 같다.

1970년대 중반, 새한 자동차 시절에는 시보레 1700 프레임을 기반으로 제미니, 카미나 같은 차를 내놓았다가 최종적으로는 순우리말 명칭인 '맵시', '맵시-나'라는 소형차를 만들어서 현대 포니 및 기아 브리사와 경쟁했다. 이 차의 후속 모델이 바로 1980년대 중반에 출시된 르망이며, 현대 엑셀 및 기아 프라이드와의 경쟁 차종이다.

1980년대에 대우에서는 중· 대형차로는 로얄/살롱 브랜드를 밀었다. 로얄 XQ, 로얄 살롱, 슈퍼 살롱, 로얄 프린스 등등~ 이것은 독일의 GM 계열사인 오펠 사에서 생산한 '레코드'라는 차종의 파생형이다. 뒤이어 임페리얼이라는 희대의 기함급 차종을 내놓기도 했으나, 이것은 품질 문제로 인해 흑역사가 되었다.

이 시절에 계기판이 디지털 액정(자동차의 주행 속도가 아라비아 숫자로 뜸!)이고 헤드라이트에까지 와이퍼가 달린 차는 대우 차밖에 없었다. 그랜저에도 그런 오버스러운 옵션은 존재하지 않았다. 그러나 대우는 외제차 프레임을 우려먹기만 할 뿐 여타 토종 자동차 회사들에 비해 고유 모델과 기술의 개발에는 상대적으로 소홀했으며, 이것이 훗날 회사의 발목을 잡는 요인이 되었다.

대우에서는 대우 국민차라고 대우 조선(대우 자동차가 아님!) 산하의 다른 계열사를 통해, 그 이름도 유명한 '티코'라는 경차를 만들기도 했다. '다마스'와 '라보'라고 경차형 승합차와 트럭도 만들었고 심지어 지금도 종종 굴러다니는 게 보이지만, 역시 티코의 인지도에는 미치지 못하는 듯.

한때 대우가 쌍용 자동차를 인수하기도 하였으나 이는 번복되었고, 대우 그룹의 경영 악화로 인해 오히려 자기가 GM으로 다시 인수되었다. 2011년부터는 잘 알다시피 GM대우라는 이름에서 '대우'라는 단어가 아예 빠지고 그냥 '한국GM'이 되었다. 그렇게 자동차 제조사로서 대우라는 브랜드는 역사 속으로 사라졌다.

오늘날까지도 버스에서는 대우라는 브랜드가 압도 다수의 인지도를 차지하고 있다. 그러나 그 '대우 버스'는 지금의 한국GM과는 아무 관계가 없는 다른 기업이다.

※ 현대

현대 그룹의 창업주이고 일명 '왕 회장'이라고도 불리는 그분이 자동차 정비업에 만족하지 않고 자동차 제조업에까지 손을 뻗침으로써, 1967년부터 현대 자동차의 역사가 시작되었다. 공장은 울산과 아산에 있는 걸로 아주 잘 알려져 있고..

물론 현대라고 해서 용 빼는 재주가 있는 것은 아니기에 맨땅에서 자동차를 스스로 만들 수는 없었다. 그래서 처음엔 미국 포드 사로부터 기술 협력을 받아 미국 차인 코티나(Ford Cortina)를 국내에서 면허 생산했다. 그러나 포드와는 곧 결별하고 일본 미쓰비시 사와 제휴를 했는데, 현대 차들이 이례적으로 연료 주입구가 대우 차들과는 달리 오른쪽에 아닌 왼쪽에 달려 있는 게 이 때문일 것으로 추정된다.

승용차 '포니'를 빼고서 현대 자동차의 역사를 말할 수는 없다. 포니는 우리나라 최초의 고유 모델 승용차이다. 비록 여전히 일제 부품으로 엔진을 만들었고 설계도 한국인이 아닌 이탈리아의 '쥬지아로'라는 디자이너가 했지만, 어쨌든 현대 자동차는 1976년 이전엔 지구상에 존재하지 않았던 완전히 새로운 모양의 자동차를 한국 땅에서 생산해 냈고 수출까지 했다!

사용자 삽입 이미지

HD를 형상화한 현대 자동차의 옛 앰블렘이 참 인상적이다.
당장은 이득이 없는 것 같은 무모한 도전을 통해 경험과 기술이 쌓일 수 있었고, 그것이 오늘날의 현대 자동차를 있게 한 밑거름이 되었음은 두 말할 나위도 없을 것이다.

(下에서 계속)

Posted by 사무엘

2012/09/29 08:27 2012/09/29 08:27
, , , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/738

경의선 서울역-DMC는 서울에 있는 도시철도/광역전철 중 배차간격이 가장 긴 구간이다. 사실 인서울뿐만이 아니라 수도권 전체, 아니 전국의 지하철까지 다 포함해도 말이다. 1시간에 1대꼴이니 이건 배차가 길기로 악명 높은 중앙선이나 2호선 지선조차도 가뿐히 관광 태우기에 충분하다.

이 구간에 열차가 이렇게 드문 이유는 잘 알다시피 용산-서울역에서 수색 기지로 입출고하는 KTX 포함 일반열차들이 차지하는 트래픽 때문이다. 몇 분 간격으로 화물도 아닌 여객 열차들이 드나드니, 이곳 선로를 2복선 이상으로 확장하지 않는 이상 경의선 전철은 도저히 더 늘릴 수가 없다. 수색 기지를 지난 뒤인 DMC 이북부터나 현재의 중앙선과 비슷한 수준으로 열차가 다니는 실정이다.

서울역-DMC 사이에 있는 경의선의 중간 경유역으로는 신촌과 가좌가 있다. 외곽의 차량 기지 주변의 시종착역도 아니고, 엄연히 서울 중심부에 이런 특이한 역이 있다는 게 신기하게 느껴지지 않는가? 신촌은 지하철 2호선의 역과 이름이 겹치기도 하기 때문에 흔히 '기차 신촌 역'이라고 불린다. 하지만 엄밀히는 경의선 신촌 역이라고 부르는 게 더 정확할 것이다.

신촌과 가좌는 이렇게 열차 운행 위상은 동일하지만 실질적인 지위는 가히 하늘과 땅만큼이나 차이가 난다.
신촌은 연세대와 이화여대에 인접했다는 천혜의 위치 덕분에 이용객이 많고 진작에 큼직한 민자역사가 만들어졌다. 게다가 넓은 광장까지 있다! 일반열차도 안 서고 1시간에 1대꼴로 다니는 전철밖에 없는 것치고는 상당히 과분한 대우이다. 아담하던 구역사는 진작에 한쪽 구석으로 이설되었고, 역사적 가치를 인정받아 등록 문화재로 보존되어 있다.

그 반면 가좌 역은 아직 신역사가 건설 중이고 구역사는 서울 시내의 '간이역' 수준 그 이상도 그 이하도 아니다. 직접 가 보면 그 초라함과 허접함에 깜짝 놀라게 될 것이다.

가좌 역에서 내리면 우리를 반기고 있는 것은 급조한 듯한 임시 섬식 승강장 하나이다(그러고 보니 신촌도 섬식이라는 공통점이 있긴 하지만). 역사는 선로와는 따로 작은 건물로 존재하는데, 승강장에서 거기로 가려면 놀랍게도... 철길을 건널목으로 평면 횡단해야 한다! 수도권, 고상홈 전동차를 타는 역에 선로 평면 횡단이 존재하는 역은 수도권, 아니 전국을 통틀어 유일하게 여기밖에 없을 것이다. 가히 수도권 전철계의 '영동선 스위치백' 같은 유물(legacy)이지 않은지?

역 건물은 한 층밖에 안 되고 냉방도 없이 철도 대합실 같은 분위기이다. 내부에 층을 오르내리는 계단이란 게 없다. 비유하자면 지금의 인천 역과 딱 비슷한 분위기이다. 인천 역이 '바로타'이듯이 가좌 역도 '바로타'이다. 다만 인천 역은 종점이다 보니 두단식 승강장이 가능한 반면, 가좌는 그렇지 않으니 어쩔 수 없이 건널목이 존재하는 것이다.

신촌과 가좌 역에는 역사적인 사연이 있다.
철덕이라면 옛날에 용산선이라는 철도가 있었고 거기엔 중간에 효창, 서강이라는 역이 있었다는 걸 아실 것이다. 용산선은 용산 역에서 출발하여 지금의 서울 지하철 6호선과 비슷한 선형을 탔다가 가좌 역에서 지금의 경의선과 합류를 했다. 서강 역에는 당인리 화력 발전소로 분기하는 지선도 있어서 발전소에 석탄 공급 화차가 다녔으나, 이 선로는 1980년대에 폐선됐다.

그리고 사실은 용산선이 오리지널 경의선 구간이었다. 경의선은 서울 역보다 시기적으로 훨씬 더 앞서 있다. 서울 역에서 출발하여 신촌을 경유하는 경의선 신선은 나중에 1920년대가 돼서야 생겼다. 신촌 역이 바로 이 시기에 영업을 시작했으며, 오늘날 서울 역의 전신인 경성 역은 그로부터 수 년이 더 지나서야 완공되었다.

그래서 서울 역 이북으로 신촌, 가좌 쪽으로 가는 선로는 왼쪽으로 상당히 급격한 드리프트가 있고 중간에 터널까지 지난다. 뭐, 용산선도 드리프트가 없는 건 아니지만, 경의선 신선은 구선보다도 없는 길을 무리해서 힘들게 만든 흔적이 역력함을 알 수 있다. 경의선이 처음부터 서울 역을 기준으로 만들어졌다면 이런 곡선이 생기지는 않았을 것이다. 이 선로는 용산 대신 경성/서울 역을 뒤늦게 한반도의 허브 철도역으로 육성하기 위한 결과물인 것이다.

용산선은 여객 취급은 안 하고 코레일이 내부적으로 비밀 아지트처럼 사용하는 단선 철길이었는데 수 년 전에 드디어 폐선되어 선로가 철거되었다. 그러나 해당 구간은 지하화되어 복선 전철로 재건설되고 있다. 이미 공항 철도가 동일 구간을 아주 깊숙히 지나고 있기도 하고 말이다.

그래서 수도권 전철 경의선은 지하에서 용산까지 간 후, 지금의 중앙선 복선 전철과 직통 운행을 하게 된다. 내가 예전에도 글로 썼듯이, 서울 역에 공항 철도가 들어왔다면 용산 역엔 경의선이 들어온다는 뜻이다. 경춘선 ITX 청춘에 이어 경의선까지 말이다!

신촌과 가좌의 미래의 위상은 여기서도 또 갈린다. 가좌는 지하 용산/경의선에 속하는 구간이기 때문에 지하에 승강장이 새로 건설된다. 그런데 용산을 기점으로 하는 지하 경의선이 개통하더라도 지금 신촌을 경유하는 서울 계통 경의선도 여전히 존치하기로 결정되었다. 이들의 인지도와 수요도 결코 무시할 수 없기 때문. 지금 DMC에서 멈추는 열차들만 용산까지 그대로 연장되고, 서울-신촌 경유 열차는 변동 사항이 없이 1시간에 1대꼴로 유지된다는 뜻 되겠다.

그래서 지금은 비록 초라한 몰골의 가좌 역이지만, 계획대로라면 이 역은 지상 승강장과 지하 승강장을 모두 갖춘 특이한 역으로 거듭날 것이다. 파주, 문산 쪽으로 간다면야 지상과 지하 중 먼저 오는 열차를 아무거나 타면 될 것이며, 용산으로 가려면 지하 승강장으로 가고, 서울로 가려면 시각표 보고 지상 승강장으로 가면 된다.

새롭게 태어나는 가좌 역의 모습이 기대된다. 용산 방면 지하 경의선 복선 전철이 개통하면 이것이 경의선의 본선 역할을 할 것이고, 기존의 서울-신촌 방면 지상 경의선은 열차의 회송이 main이고 전동차는 보조인 지선 성격으로 위상이 바뀔 것으로 보인다.

그런 의미에서 철덕이라면 환골탈태하기 전의 지금의 가좌 역을 어서 다시 방문하여 구경해 보시기 바란다. 열차가 1시간에 1대꼴로 다니니, 인서울이면서도 어지간한 근성 없이는 찾아가기 쉽지 않은 곳이다. 본인은 밀덕, 역사덕, 애니덕 등 여러 덕 중에서도 하필 철덕이 된 것을 기쁘고 자랑스럽게 생각하며, 특히 새마을호 Looking for you를 계기로 철덕이 된 것은 그저 하나님의 은혜라고 생각한다. ^^;;

Posted by 사무엘

2012/09/27 08:37 2012/09/27 08:37
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/737

평양 지하철 소개

예전에 올린 글들을 보면 알 수 있듯, 본인은 반공-_- 성향이 강하며 정치적으로 북한 정권을 극도로 싫어하는 사람이다.

그들은 공산주의고 나발이고 간에 우리나라를 삥뜯고 시종일관 민폐만 끼쳐 왔으며, 눈엣가시 같은 짓만 골라서 해 온 놈들이다. 일본이 과거사에 대해서 사과를 한 적이 없는 것만큼이나 쟤들도 사과를 하고 개과천선한 적이라고는 없다. 대남 적화 야욕은 60년 전이나 지금이나 하나도 바뀐 게 없다.

그들은 국제 사회로부터 주어진 개방과 회생의 기회는 죄다 제 발로 거절하고 세계 최악의 생지옥 국가를 만들었다. 지도자라는 작자는 국민들을 먹여 살릴 돈과 물자로 핵 무기나 만들고 개인적인 향락만 즐겼다. 자기들이 잘못해 놓고는 기근이 미국의 경제 봉쇄 때문이라고 남 탓만 한다. 그러니 아량과 자비를 베풀고 예쁘게 봐 줄 구석이 어찌 눈꼽만큼이라도 있으리요?

사실, 극소수의 정신줄 놓은 좌빨 종북주의자를 제외하고는 북한 정권을 좋아하는 사람 자체가 있을 리 없겠지만(스톡홀름 증후군?), 그 극소수의 인간들이 국가에 끼치는 손해가 워낙 막심하기 때문에 우리나라의 법은 그런 부류들의 눈높이에 맞춰져 부득이 다소 원시적이고 자유를 침해하는 것처럼 보이는 게 약간 있는 것도 사실이다.

다만, 본인은 그런 정치관과는 별개로, 북한에 대한 학문적인 관심과 호기심도 많은 편이다. 같은 한국어와 한글을 쓰는 동족이 지도자 잘못 만난 죄로 어떻게 저 정도로 맛이 가고 흑화해 갔는지... 이 21세기에 서울에서 100km도 채 떨어져 있지 않은 한반도 북부에 어떻게 절대로 가 볼 수 없는 위험한 지역이 존재할 수 있는지가 솔직히 신기하고 궁금하지 않을 수 없으니까 말이다.

게다가 요즘은 ㄱㄱㅇㅅ라는 충격과 공포의 물건이 있어서 전에는 일반인이 보리라고는 꿈에도 상상할 수 없던 장소들까지 다 들여다볼 수 있으니 얼마나 좋은가. (특히 지리덕에게!) DMZ와 판문점도 보이고 예전에 국기 높게 달기 경쟁을 하던 대성동· 기정동 마을까지 항공 사진으로 다 볼 수 있다. 북한의 정치범 수용소도 볼 수 있고 평양 시내도 들여다볼 수 있다.

자, 북한에도 관심이 많고 한국 철도에도 관심이 많은 사람이라면 북한의 철도 체계, 그리고 더 세부적으로는 평양의 지하철에 대해서도 관심이 가게 된다. 오늘은 이 주제에 대해서 얘기를 좀 하겠다.

부정할 수 없는 사실: 북한은 지하철이 다니는 나라치고는 상당히 가난한 나라이다.
그래도 북한이 최초의 지하철을 만들기는 남한보다 1년 더 먼저 만들었다.

평양 지하철은 노선이 딱 두 개이다. 1973년에 첫 개통한 천리마선과, 1975년에 개통한 혁신선. 노선별로 역이 10여 개밖에 안 되기 때문에 아주 소규모이다.
다음은 평양 지하철의 역명과 주변의 역세권을 나열한 것이다.

사용자 삽입 이미지

* 천리마선 (종축)

붉은별: 장경동, 2인민병원
전우: 혁신선 전승 역과의 환승. (역명이 서로 다름)
개선문: 말 그대로 개선문이 인근에 있음
통일: 칠성문, 모란봉 야외극장
승리: 김일성 광장
봉화: 해방산 려관
(영광): 평양 역, 김책 공업 전문 대학, 고려 호텔
(부흥): 화력 발전소

서울에 한강이 있다면 평양에는 대동강이 있다. 봉화 역 이남으로는 하저 터널을 뚫고 평양의 강남과 강북을 지하철로 연결하겠다는 게 북한의 당초 계획이었다고 한다.

그러나 1971년, 하저 터널 건설 중에 터널이 붕괴되어 10여 명의 인부들이 목숨을 잃는 사고가 난 뒤 그 계획은 철회되었다. 사실, 한강 하저 터널은 남한도 삼부 토건이 서울 지하철 5호선을 건설하면서 1990년대에 와서야 NATM 공법으로 해낸 어려운 과업이다.

북한은 하저 터널 대신 강을 따라 서쪽으로 평양 역과 화력 발전소, 그리고 김 일성의 생가 쪽으로 노선을 연장하는 쪽으로 계획을 변경했다. 이것이 1987년에 개통한 만경대선인데, 사실상 천리마선의 연장이나 다름없다. 마치 서울 지하철 3호선과 일산선의 관계와 비슷한 셈. 영광과 부흥 역은 만경대선 구간이다.

* 혁신선 (횡축)

광복: 광복 다리
건국: 평남선 보통강 역
황금벌: 경흥관
건설: 류경 호텔
혁신: 서평양 려관
전승: 천리마선 전우 역과의 환승. 전우동. 지하철도 건설 박물관, 2· 8 문화회관
삼흥: 김일성 종합 대학
광명: 금수산 기념 궁전. 김 일성이 죽은 후 이 역은 열차가 무정차 통과.
락원: 대성산 유원지

북한의 어지간한 유명 시설들이 망라되어 있다.
류경 호텔이 '건설' 역의 역세권에 있는 것은 적절한 조치라고 생각된다. -_-;;;

자, 북한은 지하철 노선이나 역에다 이름을 붙일 때도 지명 같은 건 갖다 버리고, 이념적인 보통명사를 선호한다는 걸 알 수 있다. 부역명으로 주변에 있는 기관이나 시설명을 병기해 주기는 하지만, 인근에 철도역이 있는데도 이를 싹 무시하고 전혀 다른 이름을 부여하는 건 뜻밖이다.

환승역이 어차피 하나밖에 없긴 하지만, 환승 통로라는 자비 같은 건 없다. 완전히 밖으로 나갔다가 다시 들어가야 하며, 노원 역을 능가하는 막장환승을 자랑한다. 북한 지하철은 무진장 깊기도 하고 말이다.

우리나라는 '지하'라는 단어에 부정적인 뉘앙스가 있다고 간주하여, 공식적으로는 지하철이라는 용어를 버리고 '도시철도' 내지 '광역전철' 같은 포괄적인 용어를 선호한다. 그에 반해 북한은 철저하게 지하를 강조하여 로고타입에도 선명하게 '(지)'자가 적혀 있다.
평양 시내에는 노면 전차가 따로 있지, 지하철이 지상 구간을 달린다거나 하는 건 없다.

그럼 얘들은 차량 기지는 어디 있는지 궁금해진다. 다 지하에 있어서 안 보이나? 하긴, 북한은 워낙 비밀이 많은 스텔스 국가인지라 저 지하철 노선 이상으로 지하 철도가 많이 있을 거라 추정되기도 한다. 심지어 평양 시내에서 한참 떨어진 평양 순안 국제 공항까지도 사실 철도가 존재할지 누가 알겠는가?

지도를 보면 알 수 있지만, 대동강은 중간에 섬이 여럿 있으며 그 중 하나가 능라도이다. 평양의 여의도 같은 섬인데, 여기에는 10만 명이 넘는 관중들을 수용할 수 있는 세계 최대의 스타디움인 5· 1 경기장이 있다. (북한에는 삼일절만큼이나 기념일 날짜를 그대로 이름으로 붙인 시설명이 종종 있다) 북한 특유의 매스게임, 카드 섹션 같은 게 공연되는 장소가 바로 여기이다. 하지만 평양의 지하철은 강을 건너는 노선이 없는 관계로 이 경기장은 지하철 역세권이 아니다.

평양 지하철은 표준궤에 3량 내지 4량 1편성이고, 제3궤조(우리나라처럼 공중에 팬터그래프가 달린 형태가 아님) 직류 750V 전기를 쓰니 남한의 직류 1500V와는 전압이 절반인 것으로 알려져 있다. 좌측통행인지 우측통행인지는 딱히 동영상을 못 봐서 잘 모르겠다. 역의 인테리어가 러시아 식의 유리궁전이라는 것, 그리고 요즘은 전력난이 심해서 전동차 운행을 제대로 못 한다는 것 정도는 이미 잘 알려진 사실이다.

이상으로 평양 지하철에 대해서 본인이 아는 내용을 최대한 나열해 보았다.
무려 9호선까지 있고 세계 5위권의 방대한 전철망을 구축한 우리 서울과는 달리 평양의 지하철은 인프라의 성장이 20세기 이후로 사실상 완전히 멈췄고, 그야말로 초라하기 그지없다. 솔직히 지금 경제력으로는 있는 지하철을 굴릴 여력도 없으니 말이다.

모 우익 논객의 말마따나 평양 주석궁에 탱크가 진격하기에 앞서-_-, 남과 북의 철도가 평화적으로 연결되면 좋겠다. 먼저 북녘 동포들에게 변개되지 않은 하나님 말씀인 흠정역 성경이 들어가고, 덤으로 <날개셋> 한글 입력기가 이념을 초월하여 한글을 사용하는 사람이라면 누구에게나 유용한 도구로 사용되면 좋겠다.

Posted by 사무엘

2012/09/24 19:28 2012/09/24 19:28
, , ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/736

1. C/C++이 빌드가 느린 이유

베테랑 프로그래머라면 이미 다 알기도 하겠지만, C/C++ (특히 C++)은 강력한 대신 정말 만년 굼벵이 언어가 될 수밖에 없는 요인만 일부러 골고루 가진 채 만들어졌다 해도 과언이 아닌 것 같다.

뭐가 굼벵이냐 하면 두 말할 나위도 없이 빌드 속도 말이다. C#, 자바, 델파이 같은 다른 언어나 툴과 비교하면 안습 그 자체. 이건 컴퓨터의 속도만 빨라진다고 해서 극복 가능한 차원의 차이가 아니라 구조적으로 심한 부담과 비효율 때문이다. 이 점에 대해서는 본인도 예전에 여러 글을 블로그에다 언급한 적이 있다.

  • 일단 C++은 태생이 바이트코드 같은 가벼운 가상 기계가 아니라 철저하게 기계어 네이티브 코드 생성 지향이다. 다른 가벼운(?) 언어들과는 위상부터가 다르며, 이 상태에서 최적화까지 가면 부담은 더욱 커진다. 게다가 이 언어는 설계 철학이 컴파일 시점 때 최대한 많은 걸 미리 결정하는 걸 지향하고 있다. 가령, 자바에 inline이라든가 함수 호출 규약, 레지스터, C++ 수준의 static한 템플릿 메타프로그래밍, 혹은 링크 타임 코드 생성 같은 개념이 있지는 않다.
  • 또한 이 언어는 근본적으로 문법이 상당히 문맥 의존적이고 복잡하여, 구문 분석이 어렵다. 단적인 예로 함수 선언과 객체 선언 A b(c); 변수 선언과 단순 연산식 B*c; 형변환 연산과 단순 연산식 (c)+A 가 c가 무엇인지 문맥에 따라 왔다갔다 하면서 완전히 달라진다. 거기에다 C++의 경우 템플릿, 오버로딩, namespace ADL까지 가면 난이도는 정말 안드로메다로. 다른 언어는 O(n log n) 시간 복잡도만으로도 되는 구문 분석 작업이 C++은 반드시 O(n^2)을 쓰지 않으면 안 되는 과정이 있다고 한다.
  • 빌드를 위해 전처리, 링크 같은 복잡한 계층이 존재하며, 특히 링크는 병렬화도 되지 않아 속도를 더욱 올릴 수가 없는 작업이다. 한 모듈에서 참조하는 함수의 몸체가 다른 어느 번역 단위에 있을지는 전혀 알 수 없다!
    그런데 요즘 C++ 컴파일러의 트렌드는 1에서 잠시 언급했듯이 링크 타임 때의 코드 생성과 최적화(인라이닝 포함)여서 이런 병목 지점에서 더욱 많은 작업량이 부과되고 있다. 이런??

이런 특징은 유독 C/C++ 언어만 개발툴/IDE에서 프로젝트를 만들면 온갖 잡다한 보조 데이터 파일들이 많이 생성되는 이유와도 일맥상통한다. 소스 코드를 잽싸게 분석하여 인텔리센스 같은 똑똑한 IDE 기능을 제공하기가 여타 언어들보다 훨씬 더 어렵기 때문이다.

2. 인클루드의 문제점

그런데, 네이티브 코드 생성, 복잡한 문법 같은 것 이상으로 C/C++의 빌드 시간을 더욱 뻥튀기시키고 빌드 작업을 고달프게 하는 근본적인 요소는 전처리 중에서도 다름아닌 #include 남발이다. C/C++은 남이 만들어 놓은 함수, 클래스, 구조체 같은 프로그래밍 요소를 쓰려면 해당 헤더 파일을 무조건 인클루드해 줘야 한다.

일단 이건 문법적으로는 인위적인 요소가 없이 깔끔해서 좋다. 인클루드되는 헤더는 역시 C/C++ 문법대로 작성된 일반 텍스트 파일이며, 내가 짜는 프로그램이 참조하는 명칭들의 출처가 여기 어딘가에는 반드시 있다고 보장됨을 내 눈으로 확인할 수 있다. 그러나 DB 형태로 최적화된 바이너리 파일이 아니라 파싱이 필요한 텍스트 파일이란 점은 일단 빌드 속도의 저하로 이어진다. 이게 문제점 하나.

본격적인 C++ 프로그램을 하나 만들려면 표준 C/C++ 라이브러리뿐만이 아니라 윈도우 API, MFC, 그리고 다른 3rd-party 라이브러리, 게임 엔진 등 갖가지 라이브러리나 프레임워크가 제공하는 헤더 파일을 참조하게 된다. 이것들을 합하면 한 소스 코드를 컴파일하기 위해 인클루드되는 헤더 파일은 가히 수십, 수백만 줄에 달하게 된다.

게다가 이 인클루드질은 전체 빌드를 통틀어 한 번만 하고 끝인 게 아니라, 이론적으로는 매 번역 단위마다 일일이 새로 해 줘야 한다. 헤더 파일 의존도가 개판이 돼 버리는 바람에 헤더 파일 하나 고칠 때마다 수백 개의 cpp 파일이 재컴파일되는 문제는 차라리 애교 수준이다. 문제점 둘.

보통 헤더 파일에는 중복 인클루드 방지를 위한 guard가 있다.

#ifndef ___HEADER_DEFINED_
#define ___HEADER_DEFINED_

/////

#endif

그런데 #if문 조건을 만족하지 못하는 줄들은 단순히 구문 분석과 파싱만 skip될 뿐이지, 컴파일러는 여전히 중복 인클루드된 헤더 파일도 각 줄을 일일이 읽어서 #else나 #endif가 나올 때까지 들여다보긴 해야 한다.

많은 사람들이 간과하는 사실인데(사실 나도 그랬고), 그때는 컴파일 작업만 잠시 중단됐을 뿐, 전처리기는 전체 소스를 대상으로 여전히 동작하고 있다. 중복 인클루드가 컴파일러의 파일 액세스 트래픽을 얼마나 증가시킬지가 상상이 되는가? guard만 있다고 장땡이 아니며, 이게 근본적으로 얼마나 비효율적인 구조인지를 먼저 알아야 한다. 문제점 셋.

그리고 이 #include의 수행 결과를 컴파일러나 IDE로 하여금 예측이나 최적화를 도무지 할 수가 없게 만드는 치명적인 문제는 극단적인 문맥 의존성이다.
헤더 파일은 그저 static한 함수, 클래스, 변수 선언의 집합체가 아니다. 엄연히 C/C++ 소스 코드의 일부를 구성하며, 동일한 헤더라 해도 어떤 #define 심벌이 정의된 상태에서 인클루드하느냐에 따라서 그 여파가 완전히 달라질  수 있다.

극단적인 예로, 한 소스 파일에서 #define 값만 달리하면서 동일 헤더 파일을 여러 번 인클루드함으로써, 템플릿 비스무리한 걸 만들 수도 있단 말이다. 일례로, 비주얼 C++ 2010의 CRT 소스에서 strcpy_s.c와 wcscpy_s.c를 살펴보기 바란다. 베이스 타입만 #define을 통해 char이나 wchar_t로 달리하여 똑같이 tcscpy_s.inl을 인클루드하는 걸로 구현돼 있음을 알 수 있다...;;

물론 인클루드를 실제로 그렇게 변태적으로 활용하는 예는 극소수이겠지만 인클루드는 여타 언어에 있는 비슷한 기능인 import나 use 따위와는 차원이 다른 개념이며, 캐싱을 못 하고 그 문맥에서 매번 일일이 파싱해서 확인해 보는 수밖에 없다. 문제점 넷이다.

이런 문제 때문에 여타 언어들은 텍스트 파싱을 수반하는 인클루드 대신, 별도의 패키지 import 방식을 쓰고 있으며, Objective C도 #include 대신 #import를 제공하고 헤더 파일은 무조건 중복 인클루드가 되지 않는 구조를 채택하여 셋째와 넷째 문제를 피해 갔다.

비주얼 C++도 #pragma once라 하여 #endif를 찾을 것도 없이 중복 인클루드를 방지하고 파일 읽기를 거기서 딱 중단하는 지시자를 추가했다. 이건 비표준 지시자이긴 하지만 전통적인 #ifdef~#endif guard보다 빌드 속도를 향상시키는 테크닉이기 때문에 프로그래머의 입장에서는 사용이 선택이 아닌 필수이다. 물론, 단순히 중복 선언 에러만 방지하는 게 아니라 특정 헤더 파일의 인클루드 여부를 알려면 재래식 #define도 좀 해 줘야겠지만 말이다.

외부에서 기선언된(predefined) 프로그래밍 요소를 끌어오는데, namespace나 package 같은 언어 계층을 거친 명칭이 아니라 생(raw-_-) 파일명의 지정이 필요한 것부터가 오늘날의 관점에서는 꽤 원시적인 작업이다. 개인적으로는 인클루드 파일의 경로를 찾는 메커니즘도 C/C++은 너무 복잡하다고 생각한다.

""로 싸느냐 <>로 싸느냐부터 시작해서, 인클루드가 또 다른 파일을 중첩으로 인클루드할 때, 다른 인클루드 파일을 자기 디렉터리를 기준으로 찾을지 자신을 인클루드 한 부모 파일의 위치로부터 찾을지, 프로젝트 설정에 명시된 경로에서 찾을지 같은 것 말이다…;; 게다가 인클루드 명칭도 #define에 의한 치환까지 가능하다. #include MY_HEADER처럼. 그게 가능하다는 걸 FreeType 라이브러리의 소스를 보면서 처음으로 알았다.

그런데 그러다가 서로 다른 디렉터리에 있는 동명이인 인클루드 파일이 잘못 인클루드되기라도 했다면.. 더 이상의 자세한 설명은 생략. 내가 무심코 선언한 명칭이 어디엔가 #define 매크로로도 지정되어 있어서 엉뚱하게 자꾸 치환되고 컴파일 에러가 나는 것과 같은 악몽이 발생하게 된다! 문제점 다섯.

이것도 어찌 보면 굉장히 문맥 의존적인 절차이기 때문에, 오죽했으면 비주얼 C++ 2010부터는 인클루드/라이브러리 디렉터리 지정을 global 단위로 하는 게 완전히 없어지고 전적으로 프로젝트 단위로만 하게 바뀌었다는 걸 생각해 보자.

C++ 프로젝트에서 MFC의 클래스나 윈도우 API의 함수를 찍고 '선언으로 가기'를 선택하면 afxwin.h라든가 winbase.h 같은 표준 인클루드 파일에 있는 실제 선언 지점이 나온다. 그 방대한 헤더 파일을 매 빌드 때마다 일일이 파싱할 수가 없으니 인텔리센스 DB 파일 같은 건 정말 크고 아름다워진다.

그에 반해 C# 닷넷 프로젝트에서 Form 같은 클래스의 선언을 보면, 컴파일러가 바이너리 수준에서 내장하고 있는 클래스의 껍데기 정보가 소스 코드의 형태로 생성되어 임시 파일로 뜬다…;; 이게 구시대 언어와 신세대 언어의 시스템 인프라의 차이가 아닌가 하는 생각이 들었다.

그래서 이런 C++ 인클루드 체계의 비효율 문제는 어제오늘 제기되어 온 게 아니기 때문에, 컴파일러 제조사도 좀 더 근본적인 문제 회피책을 간구하게 됐다. 그래서 나온 것이 그 이름도 유명한 precompiled 헤더이다. stdio.h나 stdlib.h 정도라면 모를까, 매 번역 단위마다 windows.h나 afx.h를 일일이 인클루드해서 파싱한다는 건 삽질도 그런 삽질도 없으니 말이다..

3. precompiled header의 도입

일단 프로젝트 내에서 "인클루드 전용" 헤더 파일과 이에 해당하는 번역 단위를 설정한다. 비주얼 C++에서 디폴트로 주는 명칭이 바로 stdafx.cpp와 stdafx.h이다. 모든 번역 단위들이 공용으로 사용하는 방대한 양의 프레임워크, 라이브러리의 헤더를 몰아서 인클루드시킨다. 컴파일러 옵션으로는 Create precompiled header에 해당하는 /Yc "stdafx.h"이다.

그러면 그 헤더 뭉치들은 stdafx.cpp를 컴파일할 때 딱 한 번만 실제로 인클루드와 파싱을 거치며, 이 파일들의 분석 결과물은 빠르게 접근 가능한 바이너리 DB 형태인 프로젝트명.pch 형태로 생성된다.

그 뒤 나머지 모든 소스 파일들은 첫 줄에 #include "stdafx.h"를 반드시 해 준 뒤, Use precompiled header인 /Yu "stdafx.h" 옵션으로 컴파일한다. 그러면 이제 stdafx.h의 인클루드는 실제 이 파일을 열어서 파싱하는 게 아니라, 미리 만들어진 PCH 파일의 심벌을 참고하는 것으로 대체된다! 앞에서 제기한 인클루드의 문제점 중 첫째와 둘째를 극복하는 셈이다.

pch 파일이 생성되던 시점의 문맥과 이 파일이 실제로 인클루드되는 시점의 문맥은 싱크가 서로 반드시 맞아야 한다. 그렇기 때문에 소스 코드에도 문맥상의 제약이 걸린다. PCH를 사용한다고 지정해 놓고 실제로 stdafx.h를 맨 먼저 인클루드하지 않은 소스 파일은 Unexpected end of file이라는 컴파일 에러가 발생하게 된다. PCH 개념을 모르는 프로그래머는 C++ 문법에 아무 하자가 없는 외부 소스 코드가 왜 컴파일되지 않는지 이해를 못 할 것이다.

당연한 말이지만 stdafx.h가 인클루드하는 헤더 파일의 내용이 수정되었다면 PCH 파일은 다시 만들어져야 하며, 이때는 사실상 프로젝트 전체가 리빌드된다. 그러므로 stdafx.h 안에는 거의 수정되지 않는 사실상 read-only인 헤더 파일만 들어가야 한다.

인클루드 파일만 수십, 수백만 줄에 달하는 중· 대형 C++ 프로젝트에서 PCH가 없다는 건 상상조차 할 수 없는 일이다. 얼마 되지도 않는(많게 잡아도 200KB 이내) 소스 코드들이 high-end급 컴에서 그것도 네트워크도 아닌 로컬 환경에서 빌드 중인데 소스 파일 하나당 컴파일에 1초 이상씩 잡아 처먹는다면, 이건 인클루드 삽질 때문일 가능성이 매우 높다. 이 경우, 당장 PCH를 사용하도록 프로젝트 설정을 바꾸고 소스 코드를 리팩터링할 것을 심각하게 고려해야 한다. 이건 작업 생산성과 직결된 문제이기 때문이다.

아놔, 이렇게 글을 길게 쓸 생각은 없었는데 너무 길어졌다.
요컨대 C++ 프로그래머라면 자기의 생업 수단인 언어가 이런 구조적인 비효율을 갖고 있다는 걸 인지하고, 상업용 컴파일러 및 개발툴이 이를 극복하기 위해 어떤 대안을 내놓았는지에 대해 관심을 가질 필요가 있다.

자바, C#, D 등 C++의 후대 언어들은 C++과 문법은 비슷할지언정 이 인클루드 체계만은 어떤 형태로든 제각각 다 손을 보고 개량했음을 알 수 있다. 아까도 언급했듯, 하다못해 Objective C도 중복 인클루드 하나만이라도 자기 식으로 정책을 바꿨지 않던가.

한 가지 생각할 점은, C/C++은 태생이 이식성에 목숨을 걸었고, 언어의 구현을 위해 바이너리 레벨에서 뭔가 이래라 저래라 명시하는 것을 극도로 꺼리는 언어라는 점이다. 그래서 대표적으로 C++ 함수 decoration이 알고리즘이 중구난방인 아주 대표적인 영역이며, 함수 calling convension도 여러 규격이 난립해 있고 모듈/패키지 같은 건 존재하지도 않는다. 그런 차원에서, 비록 비효율적이지만 제일 뒤끝 없는 텍스트 #include가 여전히 선호되어 온 건지도 모르겠다.

4. 여타 언어의 인클루드

여담이다만, 본인은 베이직부터 쓰다가 C/C++로 갈아탄 케이스이기 때문에 인클루드라는 걸 처음으로 접한 건 C/C++이 아니라 퀵베이직을 통해서였다.

'$INCLUDE: 'QB.BI'

바로, 도스 API를 호출하는 인터럽트 함수와 관련 구조체가 그 이름도 유명한 저 헤더 파일에 있었기 때문이다.

C/C++에 전처리기가 있는 반면, 베이직이나 파스칼 계열 언어는 개념적으로 그런 전처리기와 비슷한 위상인 조건부 컴파일이나 컴파일 지시자는 주석 안에 메타커맨드의 형태로 들어있곤 했다. 그러나 여타 프로그래밍 요소를 끌어다 오는 명령은 메타커맨드나 전처리기가 아니라, 엄연히 언어 예약어로 제공하는 게 디자인상 더 바람직할 것이다.

그리고 파워베이직은 퀵베이직 스타일의 인클루드 메타커맨드도 있고, 파스칼 스타일의 패키지 지정 명령도 둘 다 갖추고 있었다.

Posted by 사무엘

2012/09/21 19:25 2012/09/21 19:25
,
Response
No Trackback , 5 Comments
RSS :
http://moogi.new21.org/tc/rss/response/735

요즘 코딩 잡설

1.

<날개셋> 한글 입력기의 개발 작업은 단순히 새로운 기능을 구현하거나 알려진 버그를 수정하는 것 말고도, 멀쩡히 동작하는 기능의 내부 구현 형태를 바꾸는 리팩터링도 무시 못 할 비중을 차지하고 있다.

이미 지금도 문제가 없긴 하지만, 열기-닫기 내지 할당-해제 같은 패턴은 어지간하면 클래스의 생성자와 소멸자가 알아서 하게 바꿔서 리소스 누수(leak)를 컴파일러 차원에서 원천적으로 차단하고 있으며,
최근에는 비주얼 C++ 2010으로 갈아탄 덕분에 지저분한 임시 #define 함수들을 지역 변수 형태의 람다 함수로 교체하는 재미가 쏠쏠하다. 예를 들어 이런 것 말이다.

BEFORE
#define PickNumber(e) ((e)[1] ? wcstol((e), &f, 16): *(e))

AFTER
auto PickNumber = [&f](PCWSTR e) -> int { return e[1] ? wcstol(e, &f, 16): *e; };

별도의 함수로 추가하기에는 너무 지엽적이고 한 함수 안에서만 잠깐 쓰고 마는 반복적인 루틴들은 람다로 싸 주는 게 딱이다. type-safety가 보장되고, scope도 엄격하게 정해지고, 이 루틴을 매번 인라인으로 expand할지 아니면 그냥 함수 호출로 처리해서 코드 크기를 줄일지를 컴파일러가 좀 더 유연하게 판단할 수 있기 때문에 아주 좋다.

예전에는 C++에 대해서 C with classes라고 배웠겠지만, 이제는 C++은 C with classes라고만 정의하기에는 설명에 누락된 요소가 너무 많아졌다.
람다 함수를 전역 변수로 선언하는 건 문법적으로 불가능하지는 않으나, 그럴 바에야 그냥 재래식 형태의 함수를 하나 선언하고 말지 아무런 특별한 의미가 없을 것이다.

2.

그런데, 이렇게 리소스 누수를 막기 위해서 노력하고 있지만 구조체에다 함께 넘어온 핸들이나 메모리 포인터는 그것만 따로 클래스의 소멸자가 자동으로 소멸하게 할 수 없으니 구조적으로 여전히 누수 위험이 존재한다.

예를 들어 CreateProcess 함수는 실행 후 해당 프로세스에 대한 핸들을 PROCESS_INFORMATION 구조체에다가 되돌려 준다. 이 값을 이용해서 프로그램은 자신이 새로 실행한 프로그램이 실행이 끝날 때까지 기다린다거나 할 수 있다. 하지만 실행된 프로세스가 종료되더라도 그 프로세스를 가리키던 핸들은 해제되지 않는다. 호스트 프로그램이 핸들을 닫아 줘야만 완전히 해제된다.

CreateProcess 함수를 자주 쓴다면 핸들 해제까지 모든 작업을 자동화해 주는 클래스를 만들어서 쓰는 게 효과적이다. 사실, 이 함수는 받아들이는 인자가 많고 기능 한번 쓰는 게 번거로운 편이기 때문에 클래스를 쓸 법도 하지만, 어쩌다 한 번 쓰고 마는 특수한 함수를 전부 클래스로 감싸는 건 좀 낭비처럼 보이는 게 사실이다.

<날개셋> 편집기에는 있으나마나한 잉여이긴 하지만 명색이 텍스트 에디터이다 보니 인쇄 기능이 있다.
그런데 한때는 인쇄를 한 뒤에 자신이 사용한 프린터 DC를 해제하지 않아서 GDI 개체 누수가 생기는 버그가 있었다.
물론 이건, 리소스 제한이 있는 윈도우 9x에서 이 프로그램을 한 번 실행한 후, 프린터 드라이버를 이용한 인쇄(화면 인쇄 말고) 명령을 연달아 몇백, 몇천 번쯤 내려야(한 번에 몇백, 몇천 페이지를 인쇄하는 것과는 무관) 여파가 나타날 버그이니, 현실적으로는 아무런 위험이 아닌 것이나 마찬가지이다.

이 문제의 원인은 PrintDlg 함수가 PRINTDLG 구조체에다가 넘겨준 hDC 멤버(프린터 DC)를 해제하지 않아서였다.
그런데 이런 실수가 들어갈 법도 했던 게, MSDN에서 해당 함수나 해당 구조체에 대한 설명 어디에도, 사용이 끝난 DC를 처분하는 것에 대해서는 언급이 없었다.
이거 혹시 해제해야 하는 게 아닌지 미심쩍어서 내가 우연히 잉여질 차원에서 다른 예제 코드를 뒤져 본 뒤에야 DeleteDC로 해제를 해야 한다는 걸 알게 되었고, 예전 코드에 리소스 누수 버그가 있음을 깨달았다.

하긴, 내 기억이 맞다면, COM 오브젝트도 프로그래머가 Release를 제대로 안 해서 개체 누수가 하도 많이 생기다 보니 MS에서도 골머리를 썩을 정도였다고 하더라. 현실은 이상대로 되질 않는가 보다.

3.

윈도우 운영체제의 device context에 대해서 보충 설명을 좀 할 필요를 느낀다.
DC라는 건 그림을 그리는 매체가 (1) 화면, (2) 메모리(대부분은 화면에다 내보낼 비트맵을 보관하는 용도), 아니면 (3) 프린터 이렇게 셋으로 나뉜다. 화면용 DC는 GetDC나 GetWindowDC를 통해 HWND 오브젝트로부터 얻어 오고 해제는 ReleaseDC로 한다.

그러나 나머지 두 DC는 화면 DC와는 달리, DeleteDC로 해제한다는 차이가 있다. 화면용 DC는 운영체제가 통합적으로 관리하는 성격이 강한 반면, 나머지는 전적으로 사용자 프로그램의 재량에 달린 비중이 커서 그런 것 같다.

메모리 DC는 화면 같은 다른 물리적인 매체 DC와 연계를 할 목적으로 만들어지는 가상의 DC이므로, 보통 CreateCompatibleDC를 통해 이미 만들어진 DC를 레퍼런스로 삼아서 생성된다. 레퍼런스 DC가 무엇이냐에 따라서 하다못해 pixel format 같은 거라도 차이가 날 수 있기 때문이다.

그 반면 프린터 DC는 대개 가장 수준이 낮은 CreateDC를 통해 만들어지는데, 응용 프로그램이 특정 디바이스를 지목해서 DC를 하드코딩으로 생성하는 경우는 거의 없고 보통은 사용자에게 인쇄 대화상자를 출력한 뒤에 운영체제의 GUI가 생성해 주는 DC를 그대로 사용하면 된다.

사실, 프린터야 인쇄 전과 인쇄 후에 프린터에다 초기화 명령을 내리고 종이를 빼내는 등 여러 전처리· 후처리 작업이 필요하고 그런 저수준 명령은 프린터 하드웨어의 종류에 따라 다 다르다.
메모리는 프린터만치 하드웨어를 많이 가리지는 않겠지만, 그래도 그래픽을 보관하기 위해 메모리를 할당하고 나중에 해제하는 작업이 필요하다.

그에 반해 단순히 화면에다가 그림을 찍는 건 각 context별로 좌표를 전환하고 클리핑 영역 설정을 바꾸는 것 외에는 별다른 오버헤드가 존재하지 않는다. 도스 시절의 그래픽 라이브러리는 그런 DC 같은 추상화 계층 자체가 아예 존재하지도 않았으니 말이다.
그런 오버헤드의 위상이 ReleaseDC와 DeleteDC의 차이를 만든 것 같다.

Posted by 사무엘

2012/09/19 19:32 2012/09/19 19:32
,
Response
No Trackback , 8 Comments
RSS :
http://moogi.new21.org/tc/rss/response/734

지난 8월 4일에 전쟁 기념관에서 열린 <북한 실상 바로 알기> 특별 교육 프로그램 시리즈 중 하나에서 강의를 들은 내용을 요약하고 나의 의견을 추가하였다.

0. 들어가는 말

- 전반적으로 신앙 간증 집회 분위기 반, 예비군 가서 듣는 안보 강연 같은 분위기 반이었다.
- 탈북자들이 하나같이 중국을 거쳐서 먼 길을 우회한 끝에 한국으로 들어오는 이유는 당연히... 대놓고 휴전선을 넘어서 남하하기란 불가능하기 때문이다. (아니면 전투기 째로 휴전선 넘어서 귀순하든가)

1. 북한 내부 사회 구조

- 북한군은 병이 10년이고 간부는 그보다도 더 긴 어마어마한 의무 복무 기간을 자랑하는데, 군생활 동안 일반 정기 휴가가 없다... 캐안습. (포상 휴가만 있음)
- 북한에도 투표가 있긴 하다. 김 일성· 김 정일 얼굴 액자가 놓여 있는 투표소에 들어가는데, 투표 용지에다 투표자의 이름과 생일을 적고 투표를 해야 한다. ㅋㅋㅋㅋㅋㅋ 익명 비밀 투표 따윈 없다.
- 북한에서는 평양 거주 핵심 계층이 아니면, 통행증 없이는 시· 도도 못 드나든다. 비행기, 열차 같은 교통수단은 사실상 고위 간부 가족이나 외국인 전용이다.
- 김 대중· 노 무현 정권이 퍼 줬던 어마어마한 양의 물자들은 전부 다 핵 실험 자금으로 간 건 아니겠지만, 군대로만 갔지 어쨌든 굶주리는 주민들에게 간 건 절대 아니라고 한다. (북한에는 굶주리는 주민들에게 물자를 시골 구석구석까지 보내 줄 도로 인프라도 없다.)
- 평민들은 총칼과 폭력으로 악랄하게 통제하고, 간부들끼리는 정치 장교를 둬서 서로 밀고와 배신이 가능하게(= 단합을 못 하게) 아주 마귀적인 시스템을 잘 갖춰 놨다고 한다. 북한이 내부 사정이 저렇게 막장인데도 호락호락 혁명이나 봉기가 안 일어나는 이유가 이것 때문임.

2. 북한 사람들의 심리

- 북한 사람들이 김씨 부자를 찬양하거나 애곡하면서 오버액션 하는 건 한 80%는 진심이고, 20%만이 생존하기 위해 마음에 없는 연기를 하는 거라고 한다. 남한이 자기네보다 잘 산다는 정보도 이미 퍼져 있지만, 뼛속까지 세뇌된 거짓 교리의 힘도 만만찮다고 한다.
- 당에서 하도 “미제를 죽입시다 미제는 나의 원쑤”만 세뇌시키니까 한 탈북자가 어렸을 때 자기 어머니에게 “왜 그래요? 미국 사람도 다 나쁘지는 않지 않나요?” 이렇게 진짜 궁금해서 질문을 했는데... 곧바로 귀싸대기가 날아오고 맞아 죽을 뻔 했다고-_-;;. 주체사상 앞에서는 천륜이고 뭐고 없다.
- 그래도 북한도 변화가 아주 없는 건 아니어서 이불 뒤집어쓰고 몰래 한국 가요와 한국 드라마를 접하는 북한 사람도 적지 않다고 한다.
안 그래도 국가가 국민들을 제대로 먹여 살리지도 못하니, 젊은이들을 중심으로 '당보다 나' 생각이 퍼지고 있고, 북한 당국은 주민들이 남한 사정에 대해 잘 알게 되는 것을 무척 불편하게 여긴다.

3. 대학 교육

- 북한의 대학은 고등학교 졸업 후 바로 진학하는 건 한정돼 있고, 먼저 군대나 직장 근무 후에 재교육 차원에서 들어가는 비중이 높다고 한다.
- 아무나 대학에 못 가고 출신 성분과 계급이 중요하지만, 김일성대 리과대학이나 김책 공업 종합 대학 같은 일부 이공계 대학은 오로지 실력만으로 들어갈 수 있다고 한다. 역시 공돌이는 어디서나 필요하니까 말이다. -_-;; 북한에서 핵 무기와 미사일을 연구하는 사람이 누구이겠는가?
- 일단 대학에 가면 개인이 떠안는 등록금 부담은 없지만, 거의 전원 기숙사 생활, 학급제, 모든 수업에 지정 좌석제 때문에 사실상 고등학교 생활의 연장선이다.
- 학생들은 방학 때는 수시로 군사훈련이나 각종 행사에 동원된다. 과거에 우리나라에도 있었던 교련 과목 따위와는 스케일이 넘사벽 급. 매스게임이나 90도 다리 꺾기 제식 훈련을 하는 애들도 군인만 있는 게 아니라 상당수가 학생들이라고.
- 학비 부담 없고 개인 자유도 없는 건 무슨 사관학교 같은 컨셉이다..? -_- 그럼 거기는 진짜 사관학교는 어떤지, 그리고 대학원 진학은 어떻게 하는지가 궁금해졌으나 분위기 상 연사에게 차마 더 물어 보지는 않았다.

4. 결론

- 나라 없는 설움은 겪어 본 사람만이 안다. 그리고 자유의 소중함도 두 말할 나위가 없다. 북한은 정말 말 그대로 자유가 없는 나라이다(집회와 결사, 사상과 종교, 거주지 이동 등). 자유는 공짜가 아니다.
- 국내에서 활개를 치고 있는 '빨갱이' 종북주의자들을 보는 탈북자들의 심기도 편할 리가 없다.
- 연사들은 이런 주제에 관심을 갖고 들으러 찾아온 사람들에게 무척 고마워하면서 관심을 호소했다. 많은 사람들이 이 북한의 현실을 알고 남에게 전해 주고 경각심을 가지면 좋겠다고.
- 참석자 중에는 내가 젊은 나이에도 불구하고 북한에 대해 굉장한 관심을 갖고 질문을 진지하게 하고 오후 강의까지 듣는 것을 대견스럽게 여기는 분이 계셨다.

5. 추가 잡설

- 6· 25뿐만이 아니라, 강화도 조약 이래로 시작된 우리나라의 수난의 근· 현대사에 대해서 다시 생각하는 계기가 되었다. 북한이든 일본이든 맨날 외세를 탓하고 원망만 해서는 아무 발전도 이룰 수 없을 것이다. 그러나 아무 일도 없었다는 듯이 과거사를 존재감 없이 묻어 두고만 살 수도 없는 노릇. 가끔 이렇게 옛날에 있었던 끔찍한 비극에 대해서 자극 충전을 받는 날도 필요하다.
- 그런 의미에서…
6· 25 이전부터 북한이 남한의 건국을 방해하고 좌익 불순분자들을 선동하여 온갖 추악한 난동을 저지른 건 싹 외면하고, 6· 25 때 미군이나 국군이 민간인을 일부 오인해서 죽인 것만 들추면서 역사를 왜곡하는 불순한 부류들이 성경 변개자만큼이나 더욱 싫어진다. ㅡ,.ㅡ;;

Posted by 사무엘

2012/09/16 19:33 2012/09/16 19:33
, , , , ,
Response
No Trackback , 7 Comments
RSS :
http://moogi.new21.org/tc/rss/response/733

1.

<날개셋> 한글 입력기는 제어판에서 불러다가 곧장 쓸 수 있는 20여 개의 다양한 예제 입력 방식들을 덩달아 제공하고 있다.
6.7 이후 다음 버전에서는 예제 데이터에 아래와 같은 여러 변화가 생길 예정이다.

- 6.7에서 잘 알다시피 종성 지향 두벌식을 활용하여 'MS 두벌식'이라는 유형 파일이 추가되었는데.. 여기에다가 한글 자모 외의 숫자와 기호는 글쇠를 먹지 않게 하는 입력 스키마 설정도 추가했다. (지난 6.5에서 추가된 글쇠 인식 customize 기능으로) 어차피 시스템의 영문 글자판과 똑같은 글자는 IME가 입력시키는 게 아니라 아예 글쇠 자체를 가로채지 않고 응용 프로그램으로 넘겨 준다는 뜻.
이것까지 갖춰 주면 진짜 MS IME와 고증이 100% 일치하게 된다. 특히 외부 모듈에서 말이다.

- 네벌식이 글쇠배열 *.key이 아니라 오토마타와 낱자 결합 규칙을 갖춘 유형(*.ist) 파일로 승격되었다.
받침을 입력하려면 모음을 아무 모음이나 써도 되는 게 아니라 타자기 설계 차원에서 받침용으로 의도된 모음을 써야만 하며, 그렇지 않으면 받침은 다음 글자로 튕긴다.

모음의 용도를 구분하는 건 다양한 방법으로 할 수 있다. 비받침용 모음은 0으로 대응하는 가상 받침을 같이 입력되게 하여 여타 받침과의 결합을 차단시킬 수도 있는데, 본인의 경우 두벌식 모음과 세벌식 모음으로 구분하여 오토마타가 O 변수를 써서 구분하도록 하는 방법을 썼다.

이 외에도 네벌식 오토마타는 초+중(+종)과 중+종은 허용하지만, 초에서 바로 종은 허용하지 않게 설계되어 있다. 97 이전의 도스용 아래아한글이 이런 오토마타를 갖추고 있었다. 또한 ㅒ, ㅖ가 바로 입력 가능하지 않다는 특성상 ㅑ+ㅣ, ㅕ+ㅣ로 해당 모음을 입력할 수 있게 했다.

네벌식은 그나마 옛날 타자기 표준이라는 역사적인 의미가 있고, <날개셋> 기능 활용면에서 의미가 있어서 추가했을 뿐, 타자 관점에서 효율적인 입력 방식은 절대로 아니다. 특히 공 병우 세벌식에 비하면 이런 허접하고 불편한 타자기로 한글 입력을 해야 했을 옛날 타자수들을 생각하면 그저 안구에 습기가 찰 뿐이다.

- 일명 '한소프트 세벌식'과, '드보락 호환 두벌식' 글쇠배열은 효용성이 떨어진다고 판단하여 삭제했다.
특별히 '한소프트 세벌식'에 대해 보충 설명을 하자면, 정체가 불분명하고 원문 자료를 제공하던 사이트도 운영이 중단되어 접속이 불통된 지가 이미 수 년이 지난 상태이다. 글쇠배열도 어차피 그리 잘 만들어진 것도 아닌지라 퇴출을 결정했다. 특히 숫자를 저렇게 Shift를 누른 채 양손으로 입력하게 해 놓으면 도대체 어쩌라는 건지? -_-

2.

현재 '세벌식 순아래' 글쇠배열이라는 게 있어서 예제 파일도 아니고 아예 프로그램에 내장되어 있는 배열 중 하나이다.
그러나 이것은 장기적으로는 *.key 급으로 격하될 예정이다. 내장 데이터로 쳐 주기에는 너무 듣보잡화해 있기 때문이다.

공 병우 박사의 이념을 물려받은 권위와 정통성 있는 세벌식 연구 기관에서--한글 문화원이라든가, 한글 문화원이라든가...-- 앞으로 390과 최종을 통합하는 새로운 세벌식 표준안을 제정한다면, 그 새 배열이 지금 순아래가 있던 자리를 대체하게 될 것이다.

그리고 그 통합안은 더 장기적으로는 390을 또 대체하게 될 수도 있다. 과거에 390이 389를 대체했듯이 말이다.
통합안은 기호 문제 때문에 최종보다는 390에 훨씬 더 가깝게 만들어질 것이다.
그 반면 2000년대부터 세벌식을 접한 사람들은 390보다는 최종이 더 많다. 본인도 최종 사용자.
최종은 27개 겹받침 모두 수록이라는 궁극의 아킬레스건이 있기 때문에 상징적인 의미가 크며, 통합안이 나온 뒤에도 별도로 존속할 가능성이 높다.

이런 이유로 인해, 기존 390 사용자들만 통합안으로 갈아타면, 최종과는 달리 390은 존재의 의미를 상실하여 역사 속으로 사라지게 될 것이다. 이것이 나의 짧은 생각이다.

3.

내 프로그램에는 역사적으로나 설계 방식면에서 의미가 있는 세벌식 글쇠배열 몇 개가 key 파일로 제공되고 있다. 세벌식 389는 받침 배열이 390과 최종의 짬뽕 같으면서도 숫자가 노트북 PC의 키패드 배열과 일치한다는 특징이 있으며, '송 영상'(닉: 길동무)이라는 분이 고안한 영상 세벌식은 세벌식계의 떡밥인 왼쪽부터 시작하는 세벌식을 나름 독창적으로 구현한 배열이다.

누가 만들었는지 모를 왼손/오른손 세벌식은 no shift로도 모자라서 진짜 말 그대로 한 손으로 타자를 치는 것에 특화되어 있다. 내가 알기로 영문 드보락 자판에도 이런 왼손/오른손 변종 배열이 있다. 아마 옛날에 도스용 에디터 같은 데서 이것저것 수집한 자료이지 싶다.

이런 것들은 역사적인 의미 외에 실용적으로 쓰일 가능성이 높지 않으며, 오토마타나 낱자 결합 규칙 같은 것도 그냥 일반적인 PC용 한글 입력기의 설정을 그대로 가져와 쓰는 것만으로도 충분하기 때문에, 유형 파일이 아니라 글쇠배열 형태로만 간단히 제공된다.

4.

현재 프로그램이 기본 제공하는 예제 입력 방식이 20여 개가 있다지만, 파일 하나가 겨우 몇백~몇천 바이트밖에 하지 않으니, 다 합쳐도 크기는 3만 바이트가 채 되지 않는다.
본인은 <날개셋> 한글 입력기의 사용자가 만든 UCC..는 아니고 UCI (user-created input methods) 데이터를 받는다.
마음에 드는 건 프로그램의 다음 버전에다 같이 수록도 흔쾌히 해 줄 것이다. 사실은, 이런 데이터만 공유하는 커뮤니티가 좀 있으면 좋겠다.

선정 기준은 다음과 같다. 하나 이상을 잘 만족하면 된다.

- 아이디어가 기술적으로 독창적일 것: 복벌식이나 신세벌식 같은 것. 이런 식으로 <날개셋>의 조건부 수식과 오토마타, 가상 낱자, 더 나아가 특수 글쇠 따위를 잘 활용하여 두벌식과 세벌식 사이를 왔다 갔다 하는 독창적이고 기발한 한글 입력 방식은 얼마든지 웰컴이다. 수록 0순위임. 다만 한 아이디어 당 한 개, 많아야 두 개로 국한임.

- 역사적 가치가 있거나, 인지도· 권위가 있을 것: 역사성이라 함은 앞서 언급했던 여러 legacy 세벌식 글쇠배열 말이다. 아니면 다수가 쓰거나 명목상의 표준이기라도 해야 한다.
북한 국규 표준은 나름 그쪽에서 권위를 가지고 통용되는 입력 방식이니, 통일을 대비해서라도 예전에 key로만 제공되던 것을 최근에 완전한 유형 형태로 격상했다. 아래아한글 97과 맥 OS, MS 두벌식 같은 기존 메이저 소프트웨어가 미묘하게나마 차이가 존재하는--그것도 오토마타 차원에서!-- 독창적인 한글 입력 방식을 제공하는 것도 바람직한 일이다.

휴대전화용 3대 표준 입력 방식(천지인, 이지한글, SKY-II)은 기술적 독창성과 권위를 모두 갖추고 있으니 두 말할 나위도 없이 수록이다. 사실 이것들을 포인팅 장비로 써 볼 수 있는 보조 입력 도구(패드)도 만들어야 하는데, 아직 6.7에서는 숙원을 못 풀었다.

- 타자 행동 관점에서 아주 효율적이거나 독창적일 것: 모바일용 입력 방식은 워낙 기술적인 메커니즘이 많은 반면, PC용 입력 방식은 딱히 그런 trick은 없이 그냥 글쇠배열 논쟁으로 흐르는 경향이 있다.
역사적인 뿌리나 인지도가 없고 그렇다고 기술적인 독창성도 없는 마이너 글쇠배열이 <날개셋>의 예제로 등재되기 위해서는 진짜 타자 효율이라도 압도적으로 좋다는 증거가 있어야 한다. 그게 아니면 순아래/한손 배열처럼 장애인 접근성 분야라도 파든가.

'영상 세벌식'은 타자 능률까지는 모르겠지만 왼쪽에서 오른쪽으로 흐르는 세벌식이라는 점이 독창성을 인정받아 예제 데이터로 수록되어 있다. 앞서 말한 기술적인 독창성 말고, 배열 자체가 독창적이라는 뜻이다.

- 한글 입력과 관련된 실생활에서 유용할 것: <날개셋> 한글 입력기는 기본적으로 한글 입력에 특화되어 있기 때문에 예제 데이터도 한글 입력 방식을 우대함을 원칙으로 한다. 한글이 아닌 문자는 한국 문화권에서 한글과 같이 즐겨 쓰이는 문자들로 국한한다.

가령, 일본어 문자는 아무래도 아랍· 태국-_- 문자보다야 한국에서 더 친숙하며, <날개셋> 고급 입력기의 사용자 정의 조합 기능을 이용해서 간단히 커버 가능한 예이기 때문에 히라가나와 가타카나가 모두 수록되어 있다. 구결도 마찬가로 국어 정보학 분야에서 유용하기 때문에 수록이다.
콜맥 글자판은 한글 입력과 관계가 없는 영문이지만, 드보락 다음으로 나름 인지도가 있는 마이너 배열인지라 영문 배열은 딱 하나만 선택해서 넣었다.

이상으로 내가 예제 입력 데이터를 선별하여 수록하는 대원칙을 공지했다.
저런 조건 중 하나 이상을 만족하고 기존 예제들과도 완전히 다른 입력 방식이 과연 얼마나 있을지는 잘 모르겠지만, 내 프로그램을 통해 여러 창의적인 한글 입력 방식이 많이 만들어지고 쓰이면 좋겠다.

<날개셋> 한글 입력기는 한글 입력과 관련된 그런 지적 재산들을 모두 구현하고 관리할 수 있는 프로그램이니 말이다.
그런 기반을 마련하기 위해 초창기엔 가장 엄밀한 극단이라 할 수 있는 공 병우 세벌식부터 추구한 뒤, 점차 더 generic한 쪽으로 내려오고 있는 중이다.

여담이지만, '한글 로마자 입력 방식'처럼, 그 자체가 한 입력 방식이 아니라 특정 포괄적인 아이디어 하에서 세부적으로 다양한 입력 방식이 파생되어 나올 수 있다면, 그건 유형 파일이 아니라 아예 별도의 '빠른설정'이라는 플러그 인 프로그램이 담당하게 된다.

Posted by 사무엘

2012/09/13 19:18 2012/09/13 19:18
, , ,
Response
No Trackback , 6 Comments
RSS :
http://moogi.new21.org/tc/rss/response/732

우리는 학교에서 산수를 배우면서 덧셈을 가장 먼저 배우고, 그 뒤 이들을 묶은 형태인 곱셈을 배운다. 그리고 나중에는 곱셈을 묶은 거듭제곱이라는 걸 배운다. 그런데 덧셈이나 곱셈과는 달리, 거듭제곱은 같은 수를 곱하기를 반복한다는 직관적인 정의만 적용해서는 정수가 아닌 형태의 거듭제곱을 생각하기가 쉽지 않다.

지수법칙이 있으니 유리수 승까지는 그래도 괜찮다. 정수 승과 정수 제곱근으로 설명이 가능하기 때문이다. 하지만 이를 초월하여 2^sqrt(2)처럼 임의의 실수 승은 어떻게 해야 계산하고 표현할 수 있을까?

거듭제곱을 모든 실수 범위에서 정의함으로써 지수함수라 불리는 연속인 함수의 형태로 영역을 확장하는 데는, '연속'이라는 개념에서 알 수 있듯, 미분의 도입이 큰 기여를 했다. 자연상수 e가 발견되고 로그 함수라는 게 생기면서 지수함수는 로그의 역함수로서 정체성을 확립하게 되었다.

이 글에서 수학적으로 엄밀한 디테일을 다 말할 수는 없지만, e는 임의의 수에다 제곱근을 반복하면서 지수가 0에 가까워지고 원래 수가 1에 한없이 가까워질 때 그 수의 소숫점, 다시 말해 숫자에서 1을 뺀 값이 지수와 어떤 비례 관계가 있는지를 규명하는 과정에서 발견되었다. (1+1/n)^n에서 n의 무한대 극한이라는 원론적인 정의가 바로 거기서 유래되었으며, 사실, (1+x/n)^n라고 해 주면 그냥 exp(x)를 바로 구할 수 있다.

exp(x)는 0인 지점에서 기울기가 1이다. 그리고 미분해도 도함수가 자기 자신과 같다는 매우 중요한 특징이 있다. 즉, y=exp(x)에 있는 점 (x, y)의 기울기는 곧 y라는 뜻. 그럼 이놈의 역함수는 어떻게 될까? 본디 함수와 그 역함수는 y=x에 대해 대칭이므로, 점 (y, x)의 기울기는 곧 1/y여야 한다.

미분하면 1/x가 되는 함수를 찾아보니 다름아닌 ln x이다. 복잡한 거듭제곱을 곱셈으로, 곱셈은 덧셈으로 바꿀 수 있는 놀라운 함수 말이다. 그래서 exp는 로그의 역함수인 고로 그 정체가 e^x인 지수함수라는 결론이 도출되고, 그래서 로그와 지수의 함수 관계가 완성될 수 있었다.

a^b = e^(b ln a)이다. ln a^b는 b ln a이므로 양변에 자연로그를 씌우면 아주 자연스럽게 유도 가능하다.

또한 굳이 밑이 e가 아닌 임의의 a인 로그를 생각하더라도, log[a] b = ln b / ln a이다. 이것은 지수보다는 약간 연상이 쉽지 않을지 모르나, 이 경우를 생각해 보자. 로그의 정의상 당연히 a^(log[a] b) = b이다. 이 식 자체에다가 자연로그를 씌우면 log[a] b * ln a = ln b가 되고, 그 후의 논리 전개는 생략. ㄲㄲ

이건 굉장히 유용한 특징이다. 이 덕분에 exp와 ln만 있으면 우리는 임의의 지수의 값도 구할 수 있고, 임의의 로그의 값도 구할 수 있게 된다.
그럼 이 함수의 값을 실제로 컴퓨터로 어떻게 계산하면 좋을까?

이걸 연구하는 분야는 기호, 순수함, 추상성만을 좋아하는 전통적인 수학에서 벗어나 일면 '지저분한 이단아'처럼 보일 수도 있는 수치해석 쪽으로 가게 된다. 컴퓨터에서 exp, ln, cos, sin 같은 기초적인 초월함수의 값을 조금이라도 더 빠르고 정확하게 구하는 기법은 이미 천재 컴덕후, 수학자, 공학자들이 연구할 대로 다 연구해서 소프트웨어 정도가 아니라 아예 하드웨어에 회로에 다 코딩되어 있을 정도이다.

그러니 내가 거기에 뭔가를 더 기여할 게 있다고 여겨지지는 않는다. 단지 본인은 핵심적인 아이디어만 직접 짜 보고 넘어갈 생각이다.
초월함수들의 근사값을 계산하는 아이디어도 미분에서 유래되었다. 단순히 1차함수 접선을 구하는 것을 넘어서 곡선과 가장 가까운 n차 근사 다항식을 구하는 것으로 생각을 확장하면 '테일러 근사 다항식'이라는 걸 구할 수 있고, 무한히 미분 가능한 함수에 대해서는 무한합인 그 이름도 유명한 테일러 급수라는 걸 유도할 수 있다.

* 이말년 씨리즈와 Taylor series는 서로 느껴지는 포스가 확 다르다. 흠.;;;

exp(x)는 예전에도 글로 썼듯이, 원론적인 정의를 이용해서 값을 구할 리는 절대 없고, 테일러 급수를 쓴다. 아래의 코드는 16항 정도까지 계산했다.

double pseudo_exp(double x)
{
    double v=1.0, p=x,q=1.0;
    for(int i=1; i<=16; i++, v+=p/q, p*=x, q*=i);
    return v;
}

단, 이 급수는 x=0인 지점을 기준으로 구한 것이기 때문에 x가 8~9 정도만 넘어가도 오차가 상당히 커진다. 하지만 컴퓨터 부동소숫점에도 한계가 있으니 항을 한없이 많이 계산할 수도 없는 노릇이고, 큰 수에 대해서는 다른 방법으로 보정이 필요할 듯하다.

한편, 로그는 어떻게 구할까?
자연로그도 다음과 같은 직관적인 형태의 테일러 급수가 있긴 하다.

ln (1+x) = x (+-) x^n/n (n이 짝수일 때는 빼기, 홀수일 때는 더하기)

그런데 이 급수는 x가 -1 초과 1 미만일 때만 유효하고, 따라서 0과 2 사이의 로그값만 구할 수 있다. 나머지 범위는 항을 제아무리 많이 계산해 줘도 아예 원천적으로 원함수와 전혀 일치하지 않게 된다. 테일러 급수가 모든 범위에 대해서 무조건 만능은 아님을 알 수 있다.

하지만 지수함수와는 달리 로그는 ln ab = ln a + ln b라고 큰 숫자를 한없이 쪼개는 마법과 같은 공식이 있다. 그러므로 저것만 있어도 아무 수의 로그라도 구할 수 있다.

즉, ln 2의 값을 미리 갖고 있다가 임의의 수에 대해서 2보다 작아질 때까지 ln 2를 따로 더하면서 계속 2로 나누면 된다. 2는 잘 알다시피 2진법을 쓰는 컴퓨터가 좋아하는 수이기도 하고 말이다. 그래서 주어진 수가 2 이하의 범위에 들어오면 그때 급수를 써서 최종적으로 구한 로그값을 또 더하면 된다.

double pseudo_ln(double x)
{
    #define LN_2  0.693147180559945
    double v=0;
    while(x>2.0) x/=2.0, v+=LN_2;

    double x_min1 = x-1, xm = x_min1;
    for(int i=1; i<=16; i++, xm*=x_min1)
    if(i&1) v+=xm/i; else v-=xm/i;
    return v;
}

단, 이 급수의 아쉬운 점은 x가 0이나 2라는 양 극단에 가까워질 때 정확도가 꽤 크게 떨어진다는 점이다. 이는 계산하는 항의 수를 충분히 크게 잡아도 쉽게 극복되지 않는다.

그래서 성능면에서 실용적인 가치는 별로-_- 없지만, ln이 exp의 역함수라는 점을 이용하여, 기존 exp 함수로부터 방정식 근 찾기 기법을 이용하여 로그를 구하는 방법도 이 기회에 실습하게 되었다. 그게 없으면 무엇보다도 초기에 정확하게 값이 구해진 채 공급되어야 하는 LN_2 내장값은 또 어떻게 구하겠는가?

일변수 방정식의 근을 찾는 기법은 수치해석 교재에서 가장 먼저 다뤄지는 기초 주제이다.
exp(x)는 기복이 없으며 그래프 모양이 워낙 예쁘고 원천적이기-_- 때문에, 근이 존재하는 초기 구간만 잡아 주면, 어떤 근 찾기 알고리즘을 쓰더라도 근을 구할 수는 있다. 단지 얼마나 빨리 수렴하여 구해 내느냐가 문제일 뿐이다. 애초에 지수 함수 정도면 범용적인 방정식 근 찾기 알고리즘이 과분하게 느껴질 정도로 특징이 너무 분명하기도 하고 말이다.

가장 먼저 나오는 것은 이분법(bisect)이다. 이게 자료구조에서는 이분법이라고 하면 이분 검색처럼 log n 시간 만에 문제를 푸는 효율적인 알고리즘이라고 불리는 반면, 수치해석에서는 정수 개의 discrete한 데이터가 아니라 사실상 연속인 구간을 다룬다. 그렇기 때문에 통상적인 이분법조차도 수렴이 느린 비효율적인 방법으로 간주된다.

double solve_bisect( double (*pfn)(double), double v )
{
    double f=-10.0, t=10.0; int nt=0;
    while(1) {
        double d = (f+t)/2.0;
        double p = pfn(d) - v;
        if(fabs(p) < 0.000001) break;
        if(p > 0) t=d; else f=d; nt++;
    }
    return f; //nt는 계산 횟수 counter
}

double pseudo_ln2(double x)
{
    return solve_bisect(pseudo_exp, x);
}

근 찾기 알고리즘 중에서는 뉴턴-랩슨(혹은 그냥 뉴턴) 법이 수렴이 매우 빠른 알고리즘으로 여겨지고 있다. 이 방법은 이분법과는 달리 시작점과 끝점이라는 구간을 잡고 시작하는 게 아니라 시작점에서 해당 방정식의 접선을 그은 뒤, 접선과 x축이 만나는(y=0) 지점을 다음 점으로 잡는다. 이 작업을 방정식의 값이 근인 0과 충분히 가까워질 때까지 반복한다.

사용자 삽입 이미지


방정식 f(x) 위의 점 (x_0, f(x_0))을 지나는 접선의 방정식은 기울기가 f'(x_0)일 테니 y=f'(x_0)*x + f(x_0) -f'(x_0)*x_0이 된다. 그래야 x에다가 x_0을 집어넣었을 때 f'(x_0) 나부랭이는 소거되고 함수값으로  f(x_0)만 남기 때문이다.
이 접선이 y=0을 만족시키는 다음 점 x_1을 방정식으로 풀면 x_1=x_0 - f(x_0)/f'(x_0)이라는 생각보다 깔끔한 식이 나온다. 이를 코딩하면,

double solve_newton( double (*pfn)(double), double (*pfn_prime)(double), double v )
{
    double d = 0.0; int nt=0;
    while(1) {
        double p = pfn(d) - v;
        if(fabs(p) < 0.000001) break;
        d = d - p / pfn_prime(d); nt++;
    }
    return d;
}

double pseudo_ln3(double x)
{
    return solve_newton(pseudo_exp, pseudo_exp, x);
}

뉴턴 법 함수는 도함수의 포인터를 별도로 받는데, exp는 어차피 도함수가 자신과 동일하므로 자신을 한번 더 넘겨주면 된다는 점도 특징이다.

프로그램을 실제로 돌려 보면 알겠지만, 동일 오차 범위를 줬을 때 뉴턴 법은 이분법보다 통상 2~5배나 더 빨리 수렴하는 걸 볼 수 있다. 마치 퀵 정렬이 pivot을 기준으로 자료가 그럭저럭 이미 정렬돼 있을수록 더욱 빨리 동작하고 자료의 상태에 민감하듯, 뉴턴 법은 접선과 가까운 형태의 부드러운 곡선일수록 수렴이 더욱 빨라진다.

자, 지금까지 공대 학부 수준의 아주 허접한 수학 덕질을 감각 유지 차원에서 잠시 복습해 보았다. 각종 공식들이 유도되는 원리를 좀 더 깊이 생각해 보고 싶으나 시간과 여유가 부족하다.

아, 수학에서는 거듭제곱뿐만이 아니라 팩토리얼까지도 자연수가 아닌 실수, 그리고 복소수 범위에까지 정의하고 그 증가폭을 측정하는 방법에 대해 연구가 진행돼 있다. 그 일환으로 ln n!이 n log n - n과 얼추 비슷하다는 스털링의 공식이 있고, 그보다 더 괴랄한 감마 함수라는 것도 있어서 z! = Γ(z+1)이다. 머리 좋은 똘똘이들의 수학 덕질의 끝은 도대체 어디까지인지를 다시 생각하게 된다.

Posted by 사무엘

2012/09/10 19:32 2012/09/10 19:32
, , , , ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/731

« Previous : 1 : ... 147 : 148 : 149 : 150 : 151 : 152 : 153 : 154 : 155 : ... 215 : Next »

블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2024/04   »
  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        

Site Stats

Total hits:
2676914
Today:
1482
Yesterday:
2124