« Previous : 1 : ... 153 : 154 : 155 : 156 : 157 : 158 : 159 : 160 : 161 : ... 221 : Next »

들어가는 말

  • 프로젝트 단위: 말 그대로 한 개의 결과물을 생성하는 것을 목표로 하는 한 비주얼 C++ 프로젝트당 하나씩만 생성되는 파일이다. 리소스는 특수한 경우가 아니면 보통 프로젝트마다 하나만 있기 때문에, per-프로젝트인 것으로 간주된다.
  • configuration 단위: 한 프로젝트 내에서 debug나 release 별로 따로 생성되고, x86이나 x64 같은 플랫폼별로 다 따로 생성되는 파일이다.
  • 소스 단위: 번역 단위(translation unit)별로 다 제각각 생성되는 파일이다. configuration에도 물론 종속적이며, 다 따로 생성된다.

※ 프로젝트를 열면 생성되는 것

APS (프로젝트 단위)

전통적인 윈도우용 실행 파일(EXE/DLL)을 빌드하기 위해서는 잘 알다시피 컴파일된 코드뿐만 아니라 리소스도 같이 들어가는데, 그 리소스를 명시해 주는 '리소스의 소스', 일명 리소스 스크립트는 바로 *.rc 파일이다. 그리고 *.rc와 일반 소스 코드 *.cpp는 resource.h에 정의된 심벌들을 통해 동일 리소스를 식별하게 된다.

그런데 매번 일반 텍스트 형태로 된 rc 파일을 resource.h와 엮어서 파싱하자니 불편하다. 리소스 스크립트는 텍스트 에디터를 써서 사람이 손으로 편집한 뒤 컴파일하기에는 적합하지만, IDE 같은 소프트웨어가 자동으로 다뤄 주기에는 비효율적인 구조인 것이다.

그래서 비주얼 C++은 리소스 ID까지 포함하여 리소스 스크립트의 바이너리 representation을 따로 만들어 두고 지낸다. APS 파일이 존재하고 이게 RC나 H 같은 텍스트 소스에 비해 outdate되지 않았다면, 프로그램은 매번 텍스트를 파싱하는 게 아니라 APS 파일을 곧장 읽는다.

비주얼 C++에서 프로젝트를 처음으로 열어서 리소스 뷰로 리소스들을 처음 열람하면, 프로그램이 리소스 컴파일러를 가동해서 뭘 파싱하면서 시간이 오래 걸린다. 하지만 다음에 열 때부터는 리소스가 곧바로 빨리 열리는데, 이것이 바로 APS 파일 덕분이다.

CLW (프로젝트 단위) deprecated

이것은 비주얼 C++ 4~6 사이에, 그 이름도 유명한 MFC Class Wizard (클래스 마법사) 때문에 도입되었던 부가정보 파일이다.
MFC 클래스에서 파생된 윈도우 클래스 같은 데서 메시지 핸들러(마법사의 용도가 굳이 메시지 핸들러뿐인 건 아니지만)를 추가하려면 일단 헤더 파일에 afx_msg void OnXXXX가 추가되어야 하고, 메시지 맵 BEGIN_MESSAGE_MAP() 밑에 ON_MESSAGE_***가 추가되어야 하고, 끝으로 소스 파일에 해당 멤버 함수의 몸체가 추가되어야 한다.

그런데 이 일을 모든 소스 코드를 일일이 파싱하면서 추가 지점을 찾아서 하기란 여간 어려운 일이 아닐 수 없다.
C++은 선언 따로, 정의 따로이고(C#, 자바는 그렇지 않다) 정의부가 반드시 어느 번역 단위에 존재해야 한다는 제약이 전혀 없다. FM대로 하는 건 15년 전의 펜티엄 컴으로는 무리였다.

그래서 편의상 클래스의 선언부와 메시지 맵의 주변엔 클래스 마법사만이 식별하는 문자열이 들어간 주석이 있고, 클래스 마법사는 그 구간을 대상으로만 작업을 신속하게 했다. 그리고 그걸로도 부족해서 클래스 마법사의 파싱 결과가 CLW 파일에 들어갔다. 식별자 주석을 건드리면 클래스 마법사가 제대로 동작하지 못했다.

21세기에 나온 비주얼 C++ .NET과 그 이후 버전은 CLW 파일을 만들거나 사용하지 않으며, 클래스 마법사 주석 없이도 멤버 함수나 핸들러의 추가를 그럭저럭 정확하게 해낸다. 사실 클래스 마법사 자체가 비주얼 C++ 200x에서는 사라졌다가 2010에서부터 부활했다.

※ 빌드하면 생성되는 것

OBJ (소스 파일 단위)

비주얼뿐만이 아니라 전세계의 어느 C/C++ 컴파일러를 돌리더라도, 소스 코드를 컴파일하면 이것이 매 소스, 즉 번역 단위별로 생성된다. 소스 코드를 번역한 기계어 코드가 obj 파일 포맷에 맞게 들어있는데, 때로는 기계어 코드뿐만 아니라 각종 디버깅 정보와 링크 때 링커가 참고할 만한 메타데이터도 잔뜩 가미된다.

static library라고 불리는 LIB는 별개의 포맷이 아니라, 그냥 여러 번역 단위들을 컴파일한 obj들의 컬렉션일 뿐이다. obj를 단순히 lib로 합치기만 할 때는 링크 에러가 나지 않는다(즉, 선언된 심벌들이 반드시 정의되어야 할 필요가 없다.)

RES (configuration 단위)

리소스 스크립트를 컴파일하여 생성되는 결과물이다. 리소스 스크립트의 바이너리 최적화 형태인 APS와 무엇이 다르냐고 물으신다면, 차이가 적지 않다.
APS는 리소스 스크립트 파일의 표현 형태만 메모리 친화적으로 바꾼 것이기 때문에 ID_RADIO1 같은 상수 명칭의 문자열 원형과 심지어 조건부 컴파일을 위한 스펙까지 다 보존되어 있으며, 참조하는 비트맵 같은 데이터 파일도 파일명 형태로 존재한다. APS 파일로부터 RC 파일과 resource.h 파일을 복원해 낼 수 있다.

그러나 RES는 상수는 다 숫자로 박히고 참조하는 데이터 파일도 모두 내부에 embed되었으며, 이 상태 그대로 실행 파일에다 링크되어 들어가기만 하면 되는 상태인 것이다.

PCH (configuration 단위)

pre-compiled header인 stdafx.h와, 이에 대응하는 번역 단위인 stdafx.cpp를 컴파일하여 얻은 각종 컴파일 context들, 즉 함수와 클래스 선언, #define 명칭 등등을 바이너리 형태로 보관하고 있는 파일들이다. 이게 있으면 stdafx.h를 인클루드하라는 명령은 실제 헤더 파일을 파싱하는 게 아니라 그냥 pch 파일을 참조하는 것으로 대체된다.

컴파일러의 버전이 올라가고 각종 플랫폼 SDK의 크기가 커질수록 이 파일의 크기도 야금야금 커져 왔다. 이거 없이는 C++은 살인적인 인클루드질 때문에, 느린 빌드 속도를 도저히 감당할 수 없다.

PDB (configuration 단위)

빌드 결과 만들어진 EXE/DLL에서 기계어 코드의 어느 부분이 어느 소스의 몇째 줄에 대응하는지(소스 코드 자체는 없고 소스의 경로만), 이 함수에서 이 지역변수의 이름이 무엇인지 등을 담고 있는 디버그 정보 데이터베이스이다.

디버그 모드가 아니라 릴리스 모드로 빌드한 최적화된 실행 파일이라도, PDB 파일을 참조하게 하는 최소한의 정보만이라도 남겨 두면, 나중에 프로그램이 뻗는다거나 할 때 소스상으로 최소한 어느 지점에서 뻗었는지를 개발자의 컴에서 확인해 볼 수 있다. 개발자의 컴엔 직전에 이 바이너리를 빌드하면서 같이 생성된 PDB 파일이 존재하기 때문이다.

ILK (configuration 단위. 대개 디버그 빌드에서만)

증분 링크(incremental link)를 위한 context 정보가 들어있다.
이것은 프로그램의 빌드 속도를 올리기 위한 테크닉이다. 매번 링크를 처음부터 일일이 새로 하는 게 아니라, 처음에 빌드할 때 바이너리를 좀 여분을 둬서 듬성듬성 큼직하게 만들어 두고, 다음부터는 바뀐 obj 파일 내용만 기존 바이너리의 자기 지점에다 대체하는 방식으로 빌드를 신속하게 끝낸다. 혹은 뒷부분에다가 새로운 빌드 내용을 계속 추가해 넣기만 하고, 예전 빌드 내용을 무효화시키는 방법도 쓴다.

요즘 디버그 빌드가 단순히 최적화를 안 한 것 이상으로 릴리스 빌드보다 빌드된 바이너리의 크기가 유난히 큰 이유가 여기에 있다. 게다가 Edit and continue 기능을 위해서도 여분 공간이 필요하기 때문에 크기가 커질 수밖에 없다. 디버그 빌드 바이너리를 바이너리 에디터로 들여다보면, 온통 0xCC (no op)으로 도배가 되고 내부가 헐렁함을 알 수 있다.

MS 오피스도 2007 이전 버전을 보면 방대한 워드/엑셀 문서를 편집할 때 바뀐 내용만 짤막하게 저장하는 옵션이 있었다. 그게 일종의 증분 저장 기능이다. 지금은 그게 보안상으로 문제가 되기도 하고 문서 파일 포맷이 크게 바뀌었으며, 굳이 증분 저장을 안 써도 될 정도로 PC 성능이 좋아졌다고 여겨져서 그런 기능이 없어졌지만 말이다.
증분 링크는 보통은 디버그 모드 빌드에서만 쓰인다.

VC???.idb (configuration 단위. 대개 디버그 빌드에서만)

ILK 파일과 마찬가지로 빌드 시간의 단축을 위해 존재하는 파일이다.
디버그 모드로 빌드를 해 보면, 헤더 파일이 바뀌었더라도 해당 헤더를 인클루드하는 cpp 파일들이 전부 리빌드되는 게 아니라 가끔 'Skipping.. (no relevant changes detected)'이러면서 넘어가는 파일도 있다. 그리고 대체로 이런 컴파일러의 판단이 맞다. 헤더 파일을 고쳤더라도 클래스의 선언부 같은 크리티컬한 부분이 아니라 그냥 주석 같은 trivial한 부분만 바뀌었기 때문에 굳이 리빌드가 필요하지 않다는 걸 어떻게 판단할까?

컴파일러가 제공하는 Enable Minimal Rebuild (/Gm) 옵션 때문에 가능하다. 이게 지정되면 빌드 과정에서 프로젝트명이 아니라 고정된 이름의 의존성 판단용 부가정보 파일이 생긴다. ???는 해당 비주얼 C++의 버전이다. 2008의 경우 90, 2010의 경우 100.

정리하자면, 빌드와 함께 생성되는 파일들 중, 실제로 링커에 의해 EXE/DLL 따위를 만드는 데 동원되는 파일은 OBJ, RES이다.
빌드 시간을 단축시키는 데 쓰이는 파일은 PCH, IDB, ILK이다.
PDB는 프로그램의 문제 추적을 위해 추후에 쓰이는 파일이다.

※ 편의 기능 + 빌드

SBR (소스 파일 단위), BSC (configuration 단위)

자, 이 파일은 빌드를 하면 생성되지만, 프로그램의 빌드나 디버깅을 위해서 반드시 생성해야만 하는 파일은 아니다.
방대한 양의 소스 코드를 컴파일하고 나면 컴파일러는 그 소스 코드의 모든 내부 구조에 대해서 알게 된다. 그걸 알아야만 기계어 코드를 생성할 수 있을 테니까.

컴파일이 끝났다고 그 정보를 그냥 버리는 건 아깝기 때문에, 일정한 파일 포맷을 제정하여 이것을 소스 코드에 대한 browsing에 활용할 수 있다. 가령, 이 클래스 멤버 함수의 정의는 어디에 있고, 이 함수가 호출하는 함수와, 이 함수를 호출하는 함수와의 그래프 관계는 어떻고 하는 것 말이다. 소스 코드가 텍스트라면, browse 정보는 정교하게 짜여진 색인인 셈이다.

이 개념과 파일 포맷은 비주얼 C++의 아주 초창기 시절부터 존재했다.
그리고 비주얼 C++은 버전 6까지는, 프로젝트를 빌드할 때 browse 정보도 같이 이렇게 덤으로 빌드되게 해서 browse 정보를 조회하는 기능을 갖추고 있었다. SBR과 BSC의 관계는 C/C++ 소스 코드에서 OBJ와 EXE의 관계와 정확히 같다. 한 번역 단위를 컴파일하면 한 SBR이 생겼고, SBR들을 뭉쳐서 BSC 파일이 생성되었다.

물론 이렇게 하면 빌드 시간이 더욱 길어졌고, 굳이 browse 기능을 쓰지 않는 사람도 있었기 때문에 이 기능은 철저히 선택사항이었다. 그리고 닷넷부터는 이 정보를 만들지 않더라도, 뒤에서 설명할 인텔리센스 정보만으로 IDE 차원에서 browse 대체 기능을 갖추기 시작했다.

※ 인텔리센스

NCB (프로젝트 단위) deprecated

sbr/bsc보다는 나중에, 시기적으로는 clw와 비슷한 타이밍(비주얼 C++ 4)에 만들어진 파일 포맷이다.
바야흐로 비주얼 C++ 4에서는 최초로 Class View라는 게 생겨서 프로젝트에 존재하는 모든 클래스와 멤버, 전역 변수/함수들을 표시하는 기능이 추가되었다. ncb는 browse 정보를 만드는 것만치 소스를 심도 있게 일일이 다 까 보지는 않고, 그보다는 단순하게 코드를 파싱하여 해당 기능을 빠르게 구현하는 데 필요한 부가 정보를 저장했다.

Class View가 도입되었던 초창기에는 소스 코드를 매번 빌드는 아니어도 저장을 해야만 컨텐츠가 업데이트되었다. 그나마 저장하지 않고도 실시간으로 업데이트가 되기 시작한 건 VC 6부터이다.
그리고 VC 6에서는 잘 알다시피 초보적인 수준의 인텔리센스 및 멤버 표시/자동 완성 기능이 구현되었고, 그 정보 역시 ncb 파일에다 저장되었다. 당연히 같은 프로젝트를 만들어도 ncb 파일의 크기는 더욱 커지게 됐다.

비주얼 C++이 버전업되면서 인텔리센스는 성능이 더욱 강력해졌다. 바로 닷넷에서부터는 #define 심벌이 추가로 인텔리센스의 혜택을 입기 시작했으며 템플릿도 제대로 지원되기 시작했다. 오동작 빈도도 더욱 줄었다.

그러나 이 모든 것은 여전히 10년 전의 ncb 파일을 기반으로, 진품이 아닌 가짜 parser를 임기응변 식으로 확장하면서 구현된 것이기 때문에, 어느 수준 이상의 정확도를 낼 수는 없었으며 복잡한 C++ 문법의 모든 것을 수용하는 데에도 근본적인 한계가 있었다.

가령, 클래스 멤버 함수의 선언이 복잡한 #define 매크로 안에 숨어 있으면 Class View에 이것이 제대로 나타나지 않았다. 갑자기 빌드 configuration이나 플랫폼을 확 바꿔 버리면 인텔리센스가 멘붕을 일으켰으며, 복잡한 조건부 컴파일 구간에 숨어 있는 코드도 인텔리센스가 상황 파악을 제대로 못 하는 경우가 많았다. 멘붕의 정도가 심하면 인텔리센스가 아예 동작을 멎어 버리기도 했기 때문에, 수시로 ncb 파일을 지우고 다시 만들어 주는 건 필수 작업이었다.

SDF (프로젝트 단위), IPCH (configuration 단위)

위와 같은 기존 ncb 기반 인텔리센스의 문제를 극복하고자 비주얼 C++ 2010은 안 그래도 C++11 때문에 문법도 대폭 확장해야 하는데 이 기회에 인텔리센스 엔진을 완전히 갈아 엎었다. SQL server compact edition이라는 전문 DB 엔진을 쓰기 시작했다.

2010부터는 가짜 parser가 아니라 진짜 컴파일러와 똑같은 수준의 parser가 background에서 모든 소스와 헤더 파일들을 일일이 파싱하여 실시간으로 심벌 정보를 고친다. 정확한 문맥을 파악하고 있기 때문에 100% 정확한 인텔리센스가 제공되며, 예전처럼 좀 오동작한다 싶어도 잠시 기다려서 파싱 정보가 갱신되고 나면 곧장 똑바로 동작하기 시작한다.

다만, 이런 첨단 기술이 공짜로 된 건 아니기 때문에, 어지간한 C++ 프로젝트는 이제 인텔리센스 파일만 수십~100수십 MB씩 디스크를 쳐묵쳐묵 하는 대가를 감수해야 한다. 어느 프로젝트를 열든지 동일하게 공유되는 MFC나 플랫폼 SDK의 인텔리센스 정보는 여러 프로젝트들이 한데 공유만 할 수 있어도 인텔리센스의 용량이 크게 줄어들 텐데, 무척 아쉽다.

그래도 비주얼 C++ 제작진에서 일말의 배려를 했다 싶은 대목은, 인텔리센스 DB 파일이 생성되는 곳만 한 곳에 따로 대체 지정이 가능하다는 것이다. 프로젝트-옵션이 아니라 도구-옵션에서 “텍스트 편집기-C/C++/고급”으로 가면 fallback location을 지정하는 옵션이 있으며, 이것만 해 주면 비주얼 C++로 만드는 모든 프로젝트들의 인텔리센스 DB는 거기 아래로 한데 모이게 된다.

이렇듯, 비주얼 C++ IDE나 컴파일러가 생성하는 보조 파일들의 용도와 배경에 대해서 공부하면 C/C++ 언어의 특성을 알 수 있고, 프로그래밍 언어에 대한 비판적인 안목, 그리고 언어의 비효율을 극복하고 조금이라도 개발 도구의 생산성을 올리기 위해 해당 제작진이 어떤 꼼수를 동원했는지에 대해서도 알 수 있다.

Posted by 사무엘

2012/10/16 08:30 2012/10/16 08:30
,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/744

문자의 집합인 문자열(string)은 어지간한 프로그래밍 언어들이 기본으로 제공해 주는 기본 중의 기본 자료형이지만, 그저 기초라고만 치부하기에는 처리하는 데 내부적으로 손이 많이 가기도 하는 자료형이다.

문자열은 그 특성상 배열 같은 복합(compound) 자료형의 성격이 다분하며, 별도의 가변적인 동적 메모리 관리가 필요하다. 또한 문자열을 어떤 형태로 메모리에 저장할지, 복사와 대입은 어떤 형태로 할지(값 내지 참조?) 같은 전략도 구현체에 따라서 의외로 다양하게 존재할 수 있다.

그래서 C 언어는 컴퓨터 자원이 열악하고 가난하던 어셈블리 시절의 최적화 덕후의 정신을 이어받아, 언어 차원에서 따로 문자열 타입을 제공하지 않았다. 그 대신 충분히 크게 잡은 문자의 배열과 이를 가리키는 포인터를 문자열로 간주했다. 그리고 코드값이 0인 문자가 문자열의 끝을 나타내게 했다.

그 이름도 유명한 null-terminated string이 여기서 유래되었다. 오늘날까지 쓰이는 역사적으로 뿌리가 깊은 운영체제들은 응당 어셈블리나 C 기반이기 때문에, 내부 API에서 다 이런 형태의 문자열을 사용한다.
그리고 파일 시스템도 이런 문자열을 사용한다. 오죽했으면 이를 위해 MAX_PATH (=260)같은 표준 문자열 길이 제약까지 있을 정도이니 말 다 했다. 그렇기 때문에 null-terminated string은 앞으로 결코 없어지지 않을 것이며 무시할 수도 없을 것이다.

딱히 문자열만을 위한 별도의 표식을 사용하지 않고 그저 0 문자를 문자열의 끝으로 간주하게 하는 방식은 매우 간단하고 성능면에서 효율적이다. 지극히 C스러운 발상이다. 그러나 이는 buffer overflow 보안 취약점의 근본 원인을 제공하기도 했다.

또한 이런 문자열은 태생적으로 문자열 자기 내부엔 0문자가 또 들어갈 수 없다는 제약도 있다. 하지만 어차피 사람이 사용하는 표시용 문자열에는 코드 번호가 공백(0x20)보다 작은 제어 문자들이 사실상 쓰이지 않기 때문에 이는 그리 심각한 제약은 아니다. 문자열은 어차피 문자의 배열과는 같지 않은 개념이기 때문이다.

문자열을 기본 자료형으로 제공하는 언어들은 대개 문자열을 포인터 형태로 표현하고, 그 포인터가 가리키는 메모리에는 처음에는 문자열의 길이가 들어있고 다음부터 실제 문자의 배열이 이어지는 형태로 구현했다. 그러니 문자열의 길이를 구하는 요청은 O(1) 상수 시간 만에 곧바로 수행된다. (C의 strlen 함수는 그렇지 않다)

그리고 문자열의 길이는 대개 machine word의 크기와 일치하는 범위이다. 다만, 과거에 파스칼은 이례적으로 문자열의 크기를 16비트도 아닌 겨우 8비트 크기로 저장해서 256자 이상의 문자열을 지정할 수 없다는 이상한 한계가 있었다. 더 긴 문자열을 저장하려면 다른 특수한 별도의 자료형을 써야 했다.

과거에 비주얼 베이직은 16비트 시절의 버전 3까지는 “포인터 → (문자열의 길이, 포인터) → 실제 문자열”로 사실상 실제 문자열에 접근하려면 포인터를 이중으로 참고하는 형태로 문자열을 구현했다. 어쩌면 VB의 전신인 도스용 QuickBasic도 문자열의 내부 구조가 그랬는지 모르겠다.

그러다가 마이크로소프트는 훗날 OLE와 COM이라는 기술 스펙을 제정하면서 문자열을 나타내는 표준 규격까지 제정했는데, COM 기반인 VB 4부터는 문자열의 포맷도 그 방식대로 바꿨다.

일단 기본 문자 단위가 8비트이던 것이 16비트로 확장되었다. 마이크로소프트는 자기네 개발 환경에서 ANSI, wide string, 유니코드 같은 개념을 한데 싸잡아 뒤죽박죽으로 재정의한 것 때문에 문자 코드 개념을 좀 아는 사람들한테서 많이 까이고 있긴 하다. 뭐, 재해석하자면 유니코드 UTF16에 더 가깝게 바뀐 셈이다.

OLE 문자열은 일단 겉보기로는 null-terminated wide string을 가리키는 포인터와 완전히 호환된다. 하지만 그 메모리는 OLE의 표준 메모리 할당 함수로만 할당되고 해제된다. (아마 CoTaskMemAlloc) 그리고 포인터가 가리키는 메모리의 앞에는 문자열의 길이가 32비트 정수 형태로 또 들어있기 때문에 문자열 자체가 또 0문자를 포함하고 있을 수 있다.

그리고 문자열의 진짜 끝부분에는 0문자가 1개가 아니라 2개 들어있다. 윈도우 운영체제는 여러 개의 문자열을 tokenize할 때 double null-termination이라는 희대의 괴상한 개념을 종종 사용하기 때문에, 이 관행과도 호환성을 맞추기 위해서이다.

2중 0문자는 레지스트리의 multi-string 포맷에서도 쓰이고, 또 파일 열기/저장 공용 대화상자가 사용하는 확장자 필터에서도 쓰인다. MFC는 프로그래머의 편의를 위해 '|'(bar)도 받아 주지만, 운영체제에다 전달을 할 때는 그걸 다시 0문자로 바꾼다. ^^;;;

요컨대 이런 OLE 표준 문자열을 가리키는 포인터가 바로 그 이름도 유명한 BSTR이다. 모든 BSTR은 (L)PCWSTR과 호환된다. 그러나 PCWSTR은 스택이든 힙이든 아무 메모리나 가리킬 수 있기 때문에 그게 곧 BSTR이라고 간주할 수는 없다. 관계를 알겠는가? BSTR은 SysAllocString 함수를 통해 생성되고 SysFreeString 함수를 통해 해제된다.

'내 문서', '프로그램 파일' 등 운영체제가 특수한 용도로 예정하여 사용하는 디렉터리를 구하는 함수로 SHGetSpecialFolderPath가 있다. 이 함수는 MAX_PATH만치 확보된 메모리 공간을 가리키는 문자 포인터를 입력으로 받았으며, 특수 폴더들을 CSIDL이라고 불리는 일종의 정수값으로 식별했다.

그러나 윈도우 비스타에서 추가된 SHGetKnownFolderPath는 폴더들을 128비트짜리 GUID로 식별하며, 문자열도 아예 포인터의 포인터 형태로 받는다. 21세기에 도입된 API답게, 이 함수가 그냥 메모리를 따로 할당하여 가변 길이의 문자열을 되돌려 준다는 뜻이다. 260자 제한이 없어진 것은 좋지만, 이 함수가 돌려 준 메모리는 사용자가 따로 CoTaskMemFree로 해제를 해 줘야 한다. SysFreeString이 아님. 메모리만 COM 표준 함수로 할당했을 뿐이지, BSTR이 돌아오는 게 아닌 것도 주목할 만한 점이다.

예전에 FormatMessage 함수도 FORMAT_MESSAGE_ALLOCATE_BUFFER 플래그를 주면 자체적으로 메모리가 할당된 문자열의 포인터를 되돌리게 할 수 있는데, 이놈은 윈도우 NT 3.x 시절부터 있었던 함수이다 보니, 받은 포인터를 LocalFree로 해제하게 되어 있다.

이렇게 운영체제 API 차원에서 메모리를 할당하여 만들어 주는 문자열 말고, 프로그래밍 언어가 제공하는 문자열은 메모리 관리에 대한 센스가 추가되어 있다. 대표적인 예로 MFC 라이브러리의 CString이 있다.

CString 자체는 BSTR과 마찬가지로 언뜻 보기에 PCWSTR 포인터 하나만 멤버로 달랑 갖고 있다. 그래서 심지어 printf 같은 문자열 format 함수에다가 "%s", str처럼 개체를 명시적인 형변환 없이 바로 넘겨 줘도 괜찮다(권장되는 프로그래밍 스타일은 못 되지만).

그런데 그 포인터의 앞에 있는 것이 단순히 문자열 길이 말고도 더 있다. 바로 레퍼런스 카운트와 메모리 할당 크기. 그래서 문자열이 단순 대입이나 복사 생성만 될 경우, 그 개체는 동일한 메모리를 가리키면서 레퍼런스 카운트만 올렸다가, 값이 변경되어야 할 때만 실제 값 복사가 일어난다. 이것을 일명 copy-on-modify 테크닉이라고 하는데, MFC 4.0부터 도입되어 오늘날에 이르고 있다. 이는 상당히 똑똑한 정책이기 때문에 이것만 있어도 별도로 r-value 참조자 대입 최적화가 없어도 될 정도이다.

메모리 할당 크기는 문자열에 대해 덧셈 같은 연산을 수행할 때 메모리 재할당이 필요한지를 판단하기 위해 쓰이는 정보이다. MFC는 표준 C 라이브러리에 의존적이기 때문에 이때는 응당 malloc/free가 쓰인다. 재할당 단위는 보통 예전에 비해 배수 단위로 기하급수적으로 더 커진다.

CString이 그냥 포인터와 크기가 같은 반면, 표준 C++ 라이브러리에 존재하는 string 클래스는 비주얼 C++ 2010 x86 기준 개체 하나의 크기가 28바이트나 된다. 길이가 16 이하인 짧은 문자열은 그냥 자체 배열에다 담고, 그보다 긴 문자열을 담을 때만 메모리를 할당하는 테크닉을 쓰기 때문이다. 그리고 대입이나 복사를 할 때마다 CString 같은 reference counting을 하지 않고, 일일이 메모리 재할당과 값 복사를 한다.

글을 맺겠다.
C/C++이 까이는 여러 이유 중 하나는 라이브러리가 지저분하고 동일 기능의 중복 구현이 너무 많아서 혼란스럽다는 점이다. 문자열도 그 범주에 정확하게 속하는 요소일 것이다. 메모리 할당과 해제 자체부터가 구현체 중복이 한둘이 아니니... 어지간히 덩치와 규모가 있는 프레임워크 라이브러리는 그냥 자신만의 문자열 클래스 구현체를 갖고 있는 게 이상한 일이 아니다. 하지만 그건 C/C++이 쓰기 편리한 고급 언어와 시스템 최적화 오덕질이라는 두 토끼를 모두 잡으려다 어쩔 수 없이 그리 된 것도 강하다.

문자열에 대한 이야기 중에서 일부는 내가 예전 블로그 포스트에서도 한 것도 있지만, 이번 글에 처음으로 언급한 내용도 많을 것이다. 프로그래밍 언어 중에는 문자열을 다루기가 기가 막히게 편리한 것이 있는데, 그런 것도 내부적으로는 다 결국은 컴퓨터가 무진장 고생해서 결과물을 만들어 내는 것이다.
컴퓨터가 받아들이고 뱉어내는 문자열들이 내부적으로 어떤 구현체에 의해 어떤 처리를 거치는지를 생각해 보는 것도 프로그래머로서는 의미 있는 일일 것이다.

Posted by 사무엘

2012/10/13 08:26 2012/10/13 08:26
, , , ,
Response
No Trackback , 8 Comments
RSS :
http://moogi.new21.org/tc/rss/response/743

난 철도의 영으로 충만한 사람이다. 지난 2004년 초, 새마을호 열차 객실과 Looking for you 음악이라는 두 조건이 동시에 만족되었을 때, 본인은 딱 왕하 3:15와 비슷한 과정을 거쳐 뼛속까지 철덕으로 거듭났다. 한국 철도가 대한민국 국민이라는 집합이 아니라 ‘나’라는 개인을 위한 육상 교통수단임을 실감하였으며, 철도를 나의 개인적인 정신 지주로 영접했다.
 
나는 철도 덕분에 그야말로 세상을 보는 안목과 가치관이 송두리째 바뀌었다. 철도가 나에게 얼마나 아름다운 꿈과 희망을 주고 감성을 키웠는지, 게다가 심지어 애국심과 국토 사랑 정신까지 고취시켜 줬는지를 나는 언제라도 얼마든지 간증할 수 있다. Looking for you를 안 들어 봤기 때문에 새마을호 여행만도 못한 별 허접하고 수준 낮은 체험을 갖고서 천국 간증이네, 은사주의네 하면서 사람들이 속는다고 난 생각한다.
 
본인은 지난 수 년 동안 성경을 알고 영적으로 양육을 받으면서 하나님과 세상에 대해서 잘못 생각하던 것들이 교정되었다. 잘못된 방향으로 극단적인 선택을 하려던 것이 바로잡혔으며, 모나고 괴팍하던 성격도 예전에 ‘비해서는’ 굉장히 많이 부드러워졌다. 죄에 대한 감각이 더욱 민감해졌고, 지금 상황에 맞는 성경 구절이 더욱 빠르게 생각나는 수준이 되었다.
 
그런데 그런 와중에도 철도 사랑은 변함없이, 아니 오히려 시간이 흐를수록 더욱 강렬해졌다. 철도는 예수님도 좋아하시거나 최소한 묵인· 용인하는 게 틀림없다는 확신을 하게 됐다. 철도교와 기독교는 모순이나 대립 관계가 아니다. 내가 한 번 받은 구원을 잃지 않는 것만큼이나 세상 그 어느 것도 새마을호 안에 있는 철도 사랑으로부터 나를 떼어 놓을 수 없을 것이다. 내 머리 내부에 ‘연예, 오락, 스포츠, 유흥’ 분야는 오로지 철도가 100% 꽉 장악해 있어서 다른 영화, 드라마, 유행가, 스포츠 따위가 전혀 들어가지 않는다.
 
이렇듯 이 글을 읽는 분들, 그리고 본인에 대해서 개인적으로 아는 분들은 본인이 못 말리는 철도광이라는 것을 잘 안다. 왜냐하면 내가 맨날 철도 얘기를 떠벌리고 다니기 때문이다. 학교에서도, 교회에서도, 회사에서도 내가 철도 덕후라는 걸 모르는 사람은 없다. 내 개인 홈페이지의 방문객 중에서는 더 말이 필요 없다.
그리고 몇몇 지인들은 내게서 맨날 주워 들은 게 있어서 섬식 승강장, 복선, 경부선, 폐색 구간 같은 용어 정도는 구사하며, 심지어 Oh Glory Korail (한국 철도 공사 사가) 노래의 멜로디를 기억하기도 한다!
 
그런데 우리 다같이 좀 생각해 보자. 그런 것처럼, 나의 주변에 있는 사람들은 나 자신으로 인해서 복음이나 예수님, 성경에 대한 인식이 조금이라도 올바른 쪽으로 바뀐 게 있는가?
당장 예수님 영접하고 구원받아서 교회 출석을 시작하는 것까지는 바라지도 않는다. 단지,
 
“교회 댕긴다는 인간들이 마음에 안 들긴 하지만 그래도 저 사람은 예외적이고 좀 믿는 구석이 있어 보인다. 쟤 앞에서는 기독교 욕 함부로 하기가 좀 껄끄럽다. 성경에 대해서 만에 하나 궁금한 게 있으면 앞으로 저 사람에게 물어 봐야겠다” 정도의 평판이라도 있는가?
 
우리가 믿는 복음은 언뜻 보기에 도저히 믿어지지 않는 황당한 낭설 같지만 정말로 살아 있고 권능이 있다. 지금은 죽고 없는 옛 성현들의 듣기 좋은 격언 같은 차원이 절대로 아니다. 복음은 제일 쉬운 구원의 길을 제시함에도 불구하고, 한편으로 절대로 “나만 구원받고 끝”으로 혼자 머물러 있지도 않는다. 영원도, 구원도 없이 그저 세상적인 오덕질에 불과한 철도 복음만 해도 저 정도인데, 진짜 혼을 회심시키고 구원시키는 예수님의 은혜의 복음은 밖으로 퍼져 나가지 않으면 못 배기는 존재이다.
 
그래서 이를 필사적으로 막고자 마귀는 지능안티들을 참 많이도 만들어 놨다. 정상적으로 성령 충만한 크리스천하고는 아무 상관도 없는 이상한 위선자, 개념 없는 광신자, 나약한 루저 이미지를 예수쟁이와 딱 연결시켜 놓았다. 그리고 영적 의무를 게을리하고 있으면서 스스로 합리적이고 똑똑하다고 생각하는 육신적인 크리스천들로 하여금, 자신들이 고작 저런 위선자· 광신자 부류와 같지 않음을 다행으로 여기게 만들었다. 이 얼마나 수준 낮고 답답한 생각인가?
 
성경을 살펴보자. 출애굽기의 모세는 열 가지 재앙이 내려지던 시절에 파라오가 제안한 각종 절충안들(가긴 가되 애들은 놔두고 가라, 짐승을 일부는 남겨 놔라 등)을 단호히 거부함으로써 영적 교훈을 남겼다. 파라오는 이스라엘 백성들의 종교 행위가 주변이나 후세에 끼칠 파급 효과를 최소화하려고 나름 잔머리를 굴렸던 것이다.
 
그리고 다리오 왕 시절의 다니엘이 있다. 그때 내려진 칙령은 모든 관료들에게 일일이 청문회로 사상 검증을 실시해서 예수쟁이들을 색출해 내겠다는 게 아니었다! 그러니 그냥 한 달 동안은 혼자 골방에서 숨어서 기도를 몰래 해도 됐음에도 불구하고 다니엘은 참 고지식하게도 여전히 늘 하던 대로 공개적으로 “나 하나님 믿소” 티를 다 내면서 기도를 하다가 사자굴에까지 갔다 왔다. 이 점을 우리는 잊지 말자. (단 6:7,10)
 
예수 믿고 구원받은 사람에게서 끊임없이 예수님 얘기가 나오는 건 철도 덕후한테서 맨날 철도 얘기와 Looking for you 얘기가 나오는 것과 같다. 더 직설적인 비유를 동원하자면 똥에서 똥 냄새가 끊임없이 흘러나오고, 방사성 물질로부터 방사능이 끊임없이 나오는 것과도 같다. 우리끼리 하는 얘기로, 똥 냄새가 안 나는 똥이 똥일 수가 있을까?
 
“남에게 티를 내거나 강요는 절대로 하지 말고 예수는 너 혼자만 조용히 믿어라”는 완전히 잘못된 생각이다. 그건 온전한 신앙의 자유가 아님을 알아야 한다. 기독교 신앙은 그런 식으로 절뚝발이 형태로 믿을 수 있는 게 아니다.
 
세상의 국가들 사이에도 전쟁을 하는데 룰이라는 게 존재한다. 군인과 민간인을 분명히 구분하여 민간인의 피해가 없게 하고, 정정당당하게 싸운 군인은 설령 포로로 잡히더라도 명목상으로나마 최소한의 인권을 보장받게 하기 위해서이다.
 
현대전에서 어떤 군사 집단이 범죄자나 테러리스트 조직이 아니라 교전권을 갖춘 정식 군대로 인정을 받으려면, 신원이 알려진 사령관에 의한 명확한 지휘 체계가 있어야 하고 모든 전투원이 통일된 고유한 복장을 갖춰서 피아 식별이 공개적으로 가능해야 한다. 그리고 무기를 겉으로 공공연히 휴대하고 다녀야 한다.
 
이것은 영적 전투에도 시사하는 바가 매우 크다. 우리 쪽 진영의 사령관에 대해서야 더 말이 필요하지 않을 것이다. 그리고 전투원인 우리 역시 자신의 영적 소속과 정체성에 대해서 세상을 상대로 떳떳하고 정정당당하게 드러내고 노출시킬 생각을 해야지, 부끄러워하지 말아야 한다. 민간인으로 변장하고 적진에 침투한 뒤에 주머니에서 수류탄이나 툭 던지고 도망치는 식으로 어줍잖게 싸워서는 공을 세우기도 어려울 뿐더러, 그러다가 나중에 적에게 잡혔다간 더욱 처참한 꼴을 당하게 된다.
 
그러면서 크리스천들은 “당연히” 실수도 많이 한다. 복음을 전하는 열성이 너무 지나쳐서 너무 극성스럽게 굴 때도 있고, 낙담한 나머지 육신이 앞서서 추한 모습을 보이게 될 때도 있다. 그래서 도리어 복음에 대한 간증을 잃게 만드는 행동을 종종 한다. 본인 역시 그 누구보다도 그런 사고를 많이 쳤다.
 
그런데 그건 자연스러운 귀결이다. 철도 복음이야 안 믿었다고 지옥 갈 일도 없고, 버스와 철도가 힘을 합쳐서 모로 가든 서울만 빠르고 안전하고 편하게 가면 된다고 가르치는 복음이니, 서로 얼굴 붉힐 일도 없고 그냥 소꿉장난 수준밖에 안 된다. 그에 반해 예수님의 복음 같은 엄청나고 극단적인 복음은 전하는 과정에서 사람이 실수를 안 하는 게 더 이상한 노릇이다. 예수님도 그걸 뻔히 알면서도 복음을 전하는 사명을 성도들에게 맡기셨다!
 
실수했으면 하나님께는 회개하고 사람에게는 잘못을 인정하고 사과한 후 다시 본업으로 복귀하면 된다. 앞으로는 같은 실수를 안 하면 그걸로 끝이다. 마치, 봐도 뭔 말인지 모르겠다고 성경을 아예 안 읽는 것이 잘못이듯, 미숙함을 핑계로 자신의 영적 소속을 드러내고 알리는 일을 언제까지나 주저하는 것은 하나님 보시기에 좋은 모습이 될 수 없다.
 
죽으면 다 끝이라고 생각하는 사람, 절대적인 선과 악이 없다고 생각하는 사람, 자기 의를 철석같이 믿는 사람, 성경에서 어떻게든 꼬투리를 잡으려는 사람들을 내 논리와 지식으로 완벽하게 설득하려고 마음먹는 것 자체가 어찌 보면 오만이다. 언제 하나님께서 당신더러 그러라고 하셨던가?
 
잃어버려진 자들에게 그냥 내 인격을 걸고서 하나님의 의와 심판, 죄와 복음을 가감 없이 있는 그대로 알려 주기면 하면 된다. 단지 그 말이 정말로 듣는 사람을 “위해서” 진심으로 하는 말이라는 사랑의 호소력을 불어 넣는 것은 우리가 할 수 있는 일이다.
 
이런 맥락에서 봐도 예수님 영접은 어렸을 때 빨리 하는 게 여러 모로 유리하다. 세상 연륜과 성경 교리와의 충돌의 폭이 그리 크지 않으며, 사고를 친 것도 크게 허물이 되지 않기 때문이다. 어렸을 때 작은 시행착오를 몇 번 미리 겪고 거기에 적응과 면역이 되고 나면, 장성한 뒤부터는 탄탄대로이다. 평생 흥청망청 살다가 죽기 바로 직전에만 예수 믿으면 된다고? 큰일 날 소리이다.
 
그리고 하나 더 생각해 볼 게 있다. 교회사를 살펴보면 우리에게 귀감이 되는 여러 신앙의 선배들이 있고 특히 순교자들이 있다. 그분들을 존경하고 그분들의 삶으로부터 도전을 받는 것까지는 좋다. 그러나 그들만 우리로서는 엄두도 못 낼 무슨 엄청난 초인적인 일을 해냈다는 식으로 괴리감을 두려는 것은 상당히 위험한 결말로 빠질 수 있다.
그런 사고방식이 악화되면 천주교 성인 제도가 된다! 이럴 때 우리는 약 5:17 같은 말씀을 생각하면서 영웅 콤플렉스를 교정해야 할 것이다.
 
하늘에 가면 우리도 초대 교회 시절이나 중세 암흑기 시절의 경험담을 들으면서 놀라겠지만, 그 선조들 역시 말세에 벌어진 말도 안 되는 교리적 배도와 총체적 혼돈, 그리고 인터넷과 스마트폰 시대에 진리를 사수해 낸 성도들의 싸움 얘기를 들으면 아마 까무러칠 것이고 소스라치게 놀랄 것이다.
 
우리가 존경하는 상당수의 옛 믿음의 선배들은 진화론이 뭔지도 몰랐을 것이며, 영어 성경 역본이 200종이 넘어가고 이중 대부분은 변개되는 시대가 올 거라고는 상상도 못 했을 것이다. 일례로 인간 횃불이 되고 사자에게 잡아 먹히며 순교한 옛날 사람들은, 말세엔 근본주의 크리스천들이 사형 제도 폐지를 반대하고 비판하게 될 거라고 꿈엔들 예상했을까?
우리 같은 마지막 시대 라오디게아 팀은 사육신은 못 돼도 생육신은 충분히 된다. 어깨를 펴고 살도록 하자.
 
도산 안 창호는 이렇게 말했다.
“주변에 왜 인물이 없느냐고 탄식하지 마십시오. 왜 당신이 그 인물이 될 생각을 안 하십니까?

안 창호 자신이 크리스천이기도 했고, 저건 성경적으로도 굉장히 통찰력이 있는 말이다. 지금 우리의 다음 세대는 우리보다 영적으로 더욱 어둡고 암울하고, 경제적으로도 더 가난하고 어렵고 힘든 세상을 살게 된다. 환란 전 휴거라는 약속마저 없으면, 정말 꿈도 희망도 답도 없다. 재물이 없으면 시편 37편 같은 신앙밖에는 후세에 물려줄 게 없다.
 
정말 심각한 마음으로 구국의 일념으로 길거리에서 프리칭을 하고, 전도지를 나눠 주고, 성경과 신앙 서적을 출간하거나 출간을 후원하고, 주일학교 아이들을 가르쳐야 할 것이다. 이 세상은 그리스도의 군사가 그저 잠자코 있기에는 너무 위급한 상황이다. 우리도 초대 교회 시절의 순교자처럼 살 수 있고, 살아야만 한다. 단지 그 삶을 실현하는 배경과 방법이 다를 뿐이고, 그 방법을 실천할 기회는 오늘날 시국이 말해 주듯 주변에 널려 있다.
 
“사람의 관점에서는 위기인 게 하나님의 관점에서는 기회”라는 사고방식으로 살면 나의 영적 본분을 잊지 않고 살 수 있고 삶을 사는 방식도 많이 달라질 거라는 생각이 든다. 내가 감당하지도 못할 엄청난 권면을 함부로 늘어놓은 건 아닌가 싶어 글을 맺기가 부담스러우나, 그러나 본인 역시 또 시행착오를 겪으면서 차츰차츰 내 삶을 하나님의 방식에 맞춰 보련다. 내가 그걸 지향하면서 살고 있기 때문에, 교회에서도 철도 오덕질 그렇게 하고도 안 짤리고 보직 유지하고 버티고 있는 게 틀림없다. ㅋㅋ

Posted by 사무엘

2012/10/10 08:30 2012/10/10 08:30
, ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/742

※ 의식주

인간이 살기 위해 없어서는 안 되는 핵심 요소를 가리키는 용어로 '의식주'라는 유명한 말이 있다.

먼저 의(의복).
사람은 누구나 알몸으로 태어나지만, 아무리 가난한 사람이라 해도 최소한의 옷 한 벌은 무조건적으로 갖추고 있다. 신기하지 않은가?
우주 공간이나 사막이나 극지방 같은 극도의 악천후에서 살지 않는 이상, 벌거벗고 지낸다고 해서 당장 생물학적으로 목숨이 위태로워지지는 않는다. 옷이 무슨 물이나 산소나 음식 같은 물질도 아닌데 말이다.

그럼에도 불구하고 사람은 옷이 없으면 다른 사람들과 결코 제대로 생활할 수 없다. 다른 동물들과는 달리 오직 인간만 말이다. 성경은 그렇게 된 이유를 제시하고 있다.

또한 옷은 착용자의 신분과 격식을 나타내는 역할도 하기 때문에, 옷차림은 문화와 예절에서도 매우 중요한 위치를 차지하고 있다. 특정 상황에서 적절한 의상이 갖춰져 있지 못하면 사회적으로 상당히 난감해진다. 오죽했으면 성경에서도 결혼식 예복을 갖춰 입지 못한 사람이 예식장에서 강퇴 당하는 비유가 등장한다(마 22:11-13). 교리적으로 담고 있는 메시지는 따로 있다는 걸 감안하더라도 말이다.

다음으로 식(음식)이다.
사람은 일차적으로는 물론 체력을 얻어 생명을 유지하기 위해 밥을 먹는다. 그러나 식생활은 단순한 연명 활동을 넘어 입을 심심하지 않게 하고 좋은 기분과 컨디션을 유지시키는 등, 사회생활과 대인관계에서 의외로 매우 중요한 역할을 차지한다. 그래서인지 문명이 존재하는 사회에는 식사 예절이라는 것도 문화에 따라 아주 정교하게 발달해 있다.

인간이 하루에 두어 차례 일과 활동을 중단하고 식사를 해야 하는 건 사실 생산성이라는 관점에서만 보면 비효율과 손해인지도 모른다. 그러나 과학 기술의 발달로 인해 인간에게 필요한 열량과 영양분을 단번에 주입할 수 있는 알약 같은 게 개발된다 하더라도 수천 년간 지속되어 온 인간의 전통적인 식사 관행이 근본적으로 바뀌지는 않을 것이다.

그리고 사실은, 인터넷과 스마트폰이 발명되고 달과 화성으로 우주선을 보내는 오늘날 21세기에조차도 인류의 식량 문제의 해결은 요원하다. 전세계에는 여전히 굶주리는 사람이 많으며, 인간의 식량은 수천 년 전이나 지금이나 여전히 땅의 소출, 다시 말해 농업에 전적으로 의존하고 있다. 그리고 농업은 예나 지금이나 하늘을 바라보고 의지해야만 돌아갈 수 있는 산업이다. 이는 우리에게 시사하는 바가 크다.

끝으로 주(집)이다.
요즘 젊은이들이 집 문제 때문에 결혼조차 엄두를 못 내게 될 정도로 이와 관련된 사회적 병폐가 심각하다. 땅의 절대적인 면적이 좁은 건 아니지만 사람들이 너무 좁게 사는 게 문제이다. 아무 곳에나 덥석 정착해서 사는 게 아니라 여기저기 입지 조건을 안 따질 수가 없기 때문이다.

하지만 성경은 의와 식에 비해서 '주'는 상대적으로 덜 강조하는 것 같다. 산상수훈인 마 6:25라든가 만족을 명령하는 딤전 6:8을 봐도, 의와 식은 명시되어 있지만 주는 누락이다. 예수님 역시 변변한 거처가 없이 사셨다(마 8:20).

이는 다른 이유는 없고, 크리스천들이 세상에서는 영적으로 나그네· 순례자로 산다는 사상이 반영되어서 그런 것 같다. 진짜 본향은 하늘에 따로 있으니까. 집이 그렇게도 중요하다면, 누구 말마따나 성경도 이렇게 기록되었을 것이다. “그러므로 남자가 자기 아버지와 어머니를 떠나 '집을 장만하고,' 자기 아내와 연합하여 그들이 한 육체가 될지니라.” (창 2:24 패러디)

※ 휴대용 식량

그럼 이제부터는 의식주 중에서 '식'에 대한 이야기를 계속하겠다.
전통적으로 인간의 식사는 현장에서 갓 조리된 따끈한 음식을 충분히 가까운 곳(동일 건물)에서 바로 느긋하게 먹는 형태였다. 사실 여건이 허락한다면 그게 가장 바람직하다.

그러나 학교나 일터에서, 혹은 야외에서는 일일이 음식을 조리해서 먹을 수가 없기 때문에 근처에 식당조차 없다면 남는 선택은 도시락밖에 없다. 남의 행동이나 생각을 무슨 일이 있어도 반드시 저지시키고 싶을 때, “도시락 싸 들고 다니면서 말리겠다”라는 관용구가 쓰이는데, 이게 도시락의 어떤 특성을 반영하여 만들어진 표현이겠는지를 잘 생각해 보자. ㅋㅋ

그나마 학교는 이제 전부 급식 체제로 바뀌었고 그걸로도 모자라서 무료 급식까지 시행되고 있다 하니, 학부모의 입장에서는 도시락을 일일이 싸 줘야 하는 부담은 덜게 되었다. 저게 무슨 돈으로 가능하겠는지에 대한 정치적 견해의 차이는 차치하고라도 말이다.

아무래도 도시락은 정식으로 차려 먹는 밥보다야 덜 따뜻하고 덜 신선하며, 원하는 형태의 요리를 마음껏 먹을 수 없다는 제약이 존재한다. 더구나 단순히 점심 한 끼나 그렇게 때우는 정도가 아니라 뱃사람이나 군인의 식단은 어땠을까? 지금 같은 냉동이나 식품 보존 기술이 발달하기 전에는 고기 같은 건 닥치고 소금에 절이는 수밖에 없었을 것이고, 보존성을 위해 맛을 크게 희생한 식품만 맨날 섭취해야 하는 건 당사자들에게 큰 고역과 스트레스였을 것이다.

오늘날 단순 도시락 이상의 위상으로 통용되는 휴대용 식량으로는 다음과 같은 것들이 있다.

1. 비행기 기내식

사용자 삽입 이미지
주행 속도가 느리고 공간이 넉넉한 배야 장거리 여객선에는 주방이 있다. 열차에도 식당칸이 있다. 고속버스는 그냥 휴게소에 들르면 끝..;; 그러나 비행기는 그런 것까지 갖출 여건은 안 되니, 8시간 이상 장거리 노선을 뛰는 여객기에서는 미리 납품받은 기내식을 승객들에게 공급하게 된다.

기내식을 받아 먹는 느낌은 참 독특하다. 비록 비행기에서 직접 조리를 한 음식은 아니지만, 그렇다고 일회용 용기에 달랑 담긴 한솥 도시락이나 예비군 점심 도시락 수준의 '대충'도 아니다. 기내식은 항공사의 이미지와도 큰 관련이 있다 보니, 세계 각국의 항공사들은 기내식을 최대한 맛있고 싸구려 티 안 나고 실제 식사와 비슷하게 만들려 애쓴다.

하지만 공중에서는 단순히 데우는 수준 이상의 조리를 하기가 힘들고, 또 기내에 배기는 냄새와 뒷처리도 고려해야 하기 때문에 기내식을 한없이 고급화할 수도 없는 노릇이다.

기내식은 일반 식사보다 의도적으로 고지방· 고칼로리를 추구하며 제조된다. 사고가 발생했을 때 극단적인 상황에서 승객의 생존율을 높이기 위해서이다. 한 끼가 거의 1000kcal에 달한다니 말 다 했다. 그리고 지상보다 더 기압과 습도가 낮은 곳에서 먹는 걸 염두에 두기 때문에, 입맛을 돋우려고 조미료와 기름도 더 많이 넣고, 더 짜거나 더 달게 만든다. 보기와는 달리, 기내식만 많이 먹으면 건강에 별로 안 좋을 것 같다.

2. 전투 식량

식량의 조달은 식욕이 왕성한 수많은 장정들을 거느리는 군대를 운영하는 데 결코 소홀히 할 수 없는 중요한 요소이다.
군대에서도 주둔 중이나 평시에는 실시간으로 조리된 밥과 국과 반찬을 식판으로 퍼서 먹는 '일반 식사'가 나온다. 그러나 야전에서 훈련이나 작전 수행 중일 때는 역시 portable한 전투 식량이 배급된다.

야전에서 음식을 취급하는 속도는 행군 속도와도 직접적인 관련이 있다. 전투 식량은 휴대성과 보존성이 좋아야 하고 최소한의 물이나 불로 조리가 가능하며, 정 사정이 여의치 않으면 그냥 날로도 먹을 수 있어야 한다. 체력 소모가 극심한 병사들이 먹는 음식이니, 굉장한 고열량이어야 하는 건 두 말할 나위도 없고.

그러고도 전투 식량은 병사들의 입맛에 착 맞고 절대적으로 맛있어야만 한다. 참혹한 전장에서 병사들에게 일말의 즐거움을 선사하고 사기를 진작시킬 수 있는 거의 유일한 수단은, 밥이라도 잘 먹여 주는 것뿐이기 때문이다. 그러니 알고 보면 총포의 기술 발달에 만만찮게, 식품 가공 기술의 발달도 군의 선진화와 현대화에 굉장히 큰 기여를 한 셈이다.

그러니 전투 식량은 앞서 언급한 기내식만큼이나 조미료가 많이 들어가고, 일반인들이 많이 먹으면 비만에 걸릴 요소가 듬뿍 가미된다. 한국군에서는 굳이 야전에 안 나가고 내무 생활을 하는 중에도 이따금씩 정규 식사 대신에 전투 식량이 병사들에게 식사로 지급되는 때가 있는데, 이는 유통기한이 임박한 전투 식량 재고분을 소진하기 위해서이다.

밀덕 중에는 국군이나 미군의 전투 식량을 구해 먹으려고 벼르는 사람도 있다. 일반 음식보다 열악한 여건에서 먹으라고 만들어진 음식을 일부러 찾아서 먹는 이유는, 자신이 민간인이 아닌 군인이라는 특권 의식을 경험하고 싶어서인 것 같다. 전투 식량은 포장과 내용물 등 봐야 할 게 여럿 있기 때문에, 링크를 하나 소개하는 걸로 그림 소개를 대신하겠다.

참고로 전투 식량은 진짜 비상 식량과는 다른 개념이다. 비상 식량은 추락한 비행기의 조종사나, 조난 당한 선원이 구조될 때까지 무인도나 망망대해에서 생존을 위해서 섭취하는 고농축 영양제 같은 음식이다. 단순히 야전에서 작전 수행 중에 먹는 게 아니라, 작전 수행 중에 돌발상황이 불가피하게 생겼을 때 먹는 것이다. 비상 식량은 먹게 될 일이 없기를 바라면서 만들어지기 때문에 오로지 보존성과 휴대성만이 강조될 뿐, 맛은 고려 대상이 아니다.

3. 우주 식량

우주인은 군인만치 그렇게 격렬한 육체 활동을 하지는 않으므로, 우주 식량은 전투 식량만치 고열량을 추구해야 할 필요는 없다. 하지만 무중력 내지 우주 공간에서는 지상에서처럼 음식 맛이 잘 느껴지지 않기 때문에 우주식은 역시 기내식 만만찮게 조미료 도배가 되어야 한다. 또한 무중력 공간에서 인체가 잃기 쉬운 칼슘 같은 영양소를 우주식이 특별히 보충해 줘야 할 필요도 있다.

사용자 삽입 이미지
다음으로 물리적인 형태를 살펴보면, 우주식은 같은 영양 성분이면 무게와 부피를 줄이는 게 중요하다. 그러기 위해서는 진공 건조가 잘 되어야 하며, 그리고 가루· 부스러기가 날리는 형태여서는 절대로 안 된다. 무중력 상태에서 음식 파편이 날리면 심각하게 골치 아파지기 때문. 그런 게 기계 내부로 빨려들어가 기계의 고장을 야기할 수도 있다.

그러니 초기의 우주식은 닥치고 튜브+빨대 형태였다. 먹을 때 입을 크게 안 벌려도 되고, 파편 유출 사고(?)가 일어날 위험이 가장 적었기 때문이다. 그러나 기계와 영양학적 효율을 위해 맛을 크게 희생한 초기의 우주식은 우주 비행사들의 불만을 야기할 수밖에 없었으며, 기술의 발달 끝에 지금은 어지간한 형태의 음식들은 다 우주식으로 개량이 가능해졌다. 김치, 라면, 불고기, 비빔밥, 미역국 같은 것도 모두 우주에서 먹을 수 있다.

우주식은 무중력 상태에서도 음식과 식기가 흩어지지 않게 식판에 이례적으로 벨크로(찍찍이)와 자석이 붙어 있다.
이렇듯, 비행기 기내식과 군대 전투 식량, 그리고 우주 식량은 대체로 영양이 보강되어 있고 휴대성과 보존성이 강화되어 있다는 큰 공통점이 있으면서 세부적인 조건은 살짝 차이가 있음을 알 수 있다.

Posted by 사무엘

2012/10/07 08:32 2012/10/07 08:32
, , , , , , ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/741

※ 들어가는 말

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

정렬은 문제의 목표가 너무나 명확하고 실용적이며, 다양한 관점에서 문제의 접근이 가능하고 좋은 알고리즘과 나쁜 알고리즘의 차이도 아주 드라마틱하게 알 수 있기 때문에... 예로부터 그 특성과 해법이 연구될 대로 연구되어 왔다. 시간 복잡도 관념이 없던 초짜 프로그래머가 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

« Previous : 1 : ... 153 : 154 : 155 : 156 : 157 : 158 : 159 : 160 : 161 : ... 221 : Next »

블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

Site Stats

Total hits:
3048692
Today:
1854
Yesterday:
2058