요즘은 좀 덜해진 감이 있지만 한때 마소에서는 자사의 운영체제인 Windows에 단순히 기술과 기능뿐만 아니라 감성을 담으려고 애쓰곤 했다. 애플 진영만 감성 마케팅을 한 게 아니라는 얘기이다.
이쪽으로 굉장히 신경을 많이 썼던 때는 크게 세 시즌으로 나뉜다. (1) 95, (2) XP, 그리고 그 다음 (3) Vista 정도 되겠다.

Windows 95는 그야말로 마소의 Windows 개발 역사상 가장 큰 격변을 이룬 작품이었다.
그러니 3.1 시절의 너무 식상했던 tada.wav를 대신하여, 참신하고 세련되고 오픈되고 모던한 이미지를 표현할 수 있게, Windows 95 부팅이 마치 미래로 가는 창문을 열어젖히기라도 한 인상을 줄 수 있는.. 그런 시작음을 외주를 줘서 개발하게 되었다.

그 유명한 "또르릉~ 띵~ 띵.." 95의 시작음을 작곡한 사람은 Brian Eno이다. 파일 이름부터가 참 거창하게 The Microsoft Sound였다.
다만, 정작 그 작곡자는 DOS고 Windows고 전혀 사용하지 않는 골수 Mac 유저였다는 것이 훗날 당사자의 회고 인터뷰를 통해 알려지기도 했다.;;

Windows 95는 누구나 듣는 시작음뿐만 아니라, 소수의 매니아만이 열어 보는 이스터 에그 화면에다가도 고유한 음악을 집어넣었다. 여기에 들어간 음악이 바로 그 유명한 Clouds이다. 이거 작곡자는 Brian Orr이니 또 다른 Brian이다. 단, 이건 길이와 용량 관계상 wav가 아니라 mid 포맷이다.

저 작곡자가 회고하기를, 작곡을 의뢰받을 때 컨셉으로 받은 키워드가 clouds, floating, peaceful이었다고 한다. Windows 95는 부팅 스플래시 화면부터가 파란 창공과 구름이었으니까..;; 그래서 그 컨셉에 맞게 멜로디를 써 넣은 결과물이 저 음악이라고 한다.
여느 음악 예제들과 마찬가지로 Windows\media 디렉터리에 있었다고는 하지만 본인은 얘는 Windows 95가 실제로 사용되던 시절에 PC에서 찾아서 들어 보지 못했다.

저거 이후로 마소의 제품에서 이스터 에그 재생 중에 그럴싸한 음악이 나온 경우는 Visual Basic 5~6의 이스터 에그가 유일했던 듯하다. 공교롭게도 저 이스터 에그도 파란 창공을 배경으로 정육면체 상자 4개가 뱅글뱅글 돌아가고 그 배경 위로 개발자 명단이 스크롤 되어 올라간다.

물론 이스터 에그라는 것도 2002년 이후로는 마소의 제품에서는 싹 자취를 감춰서 볼 수 없게 됐지만 말이다.
우리나라 지하철에다 비유하자면, 처음에 인테리어가 좀 독특하게 꾸며졌던 역들이 모두 안전을 이유로 스크린도어로 뒤덮이고 불연재 재질로 교체되어 미관이 예전보다 안 좋게 바뀐 것과 비슷해 보인다. 뭐 아무튼..

Windows NT 4라든가 98~ME 사이에서는 전자 악기 기반의 시작음들이 많이 쓰였으며 특히 chimes, chord, ding 같은 메시지 비프음도 다시 만들어졌다. 이것들은 다른 아티스트들의 작품이다.

그러다가 Windows XP는 프로그램의 시청각 요소가 완전히 쇄신했다. 이제 PC의 속도와 메모리가 충분해진 덕분에 9x 계열 커널이 수명을 다할 때가 됐고, 그리고 64비트와 멀티코어 CPU도 등장하다 보니 하드웨어가 큰 변화를 겪을 시기였다. 이 시기에 맞춰 마소에서는 OOBE (out-of-box experience)라는 말까지 만들어 내면서 새 운영체제로 '사용자에게 새롭고 특별한 경험을 선사하자'에 목숨을 걸었다. 굳이 Windows 2002 대신 XP라는 브랜드명까지 만들면서 말이다.

일단 피아노 소리 위주인 시작음, 비프음들은 다 Bill Brown이라는 작곡가가 작곡하고 오케스트라를 동원해서 연주했다. 그리고 (1) Tour를 실행했을 때 나오는 고퀄의 배경 음악들도 이 사람의 작품이다. 시퍼런 Luna 테마와 풀밭 사진뿐만 아니라 음악도 Windows XP를 뭔가 종합 예술 작품 같은 인상을 심어 주는 요인이다.

사실, Windows XP는 애초에 설치를 하다가 작업이 마무리되고 비디오/사운드 카드가 자동으로 잡히고 나면 간단한 애니메이션과 함께 (2) 몽환적인 분위기의 intro 음악이 나온다. 길이도 무려 5분 24초나 된다. 이걸 듣고서 강렬한 인상을 받은 사람이 많았을 것이다.

그런데 이 유명한 음악의 작곡자는 의외로 Bill Brown이 아니며, 정확하게 알려져 있지 않다. 인터넷 상으로는 Brian Eno가 작곡했다는 말이 많지만 저 사람은 공식적으로는 Windows 95 로고송 이후로 딱히 마소와 다시 작곡과 관련된 계약을 맺은 내역이 없다.

Susan Ciani라는 미국의 여성 작곡자를 지목하는 곳도 있으나, 이 역시 정확한 출처나 근거가 부족하다. 이 곡은 Windows XP의 정체성 그 자체로서 Tour 음악과 쌍벽을 이룬다고 해도 과언이 아닌데, 작곡자가 공식적으로 미상인 것이 무척 미심쩍게 여겨진다.

그 뒤, Windows Vista부터 도입된 "따단 따단" 그 4개 음표짜리 전자음 멜로디는 Robert Fripp이라는 사람이 작곡한 것으로 알려져 있다. 이때 이후로 마소에서는 운영체제의 음악에 큰 신경을 쓰지 않고 있다.
옛날에는 사용자가 선택한 테마에 따라 GUI의 색상과 글꼴, 각종 사운드가 싹 달라지게 하는 게 유행이었고 애초에 Windows와 Office, Visual Studio 제품들도 버전이 바뀔 때마다 프로그램 외형과 색상도 같이 바뀌던 시절이 있었거늘, 그마저도 2010년대 이후로는 약발이 다한 모양이다.

시작음처럼 운영체제나 프로그램의 구동과 함께 연주되는 음악 말고.. Windows\media 디렉터리에 예제로 제공되는 음악들도 버전별로 바뀌어 왔다.
Windows 3.1 시절에는 canyon과 passport라는 이름의 mid 파일이 있었다. 95와 그 이후까지 존속했는지는 기억이 확실치 않다.

98/2000쯤에는 '엘리제를 위하여'를 포함해 뜬금없이 클래식 음악의 미디 파일이 갑자기 쭈욱 추가되었던 걸로 본인은 기억하는데, 후대 버전에서는 몽땅 다시 사라졌다.
그 대신 ME와 XP에서는 그 당시의 최신 외국 가요 음반에서 발취한 샘플 wma 한두 곡이 잠시 들어갔다. 미디로는 town, flourish, onestop이 들어가서 오늘날 Windows 10에까지 전해져 내려오고 있다.

특히 onestop의 경우 마치 음악계의 the quick brown fox jumps over the lazy dog처럼.. 미디에 정의되어 있는 모든 악기들을 일부러 모두 동원하는 형태로 만들어졌으며, 구간별로 분위기가 오락가락 하면서 굉장히 괴랄한 흐름과 중독성을 자랑한다.
뭔가 RPG 게임의 BGM 같기도 하고.. "이 음악 들으니 문득 집 앞 편의점까지 희망찬 모험을 떠나고 싶어졌어!" 뭐 이런 말이 나올 정도라고 한다..;;

Posted by 사무엘

2018/03/31 08:34 2018/03/31 08:34
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1473

다음 버전 개발 근황 -- 글꼴

0. 들어가는 말

날개셋 한글 입력기 9.3이 나온 지 벌써 50일 가까이 지났다.
올해 초, 이걸 만들던 당시에는 정말 머리를 쥐어뜯으면서 힘든 나날을 보냈다. '조합 안에 조합 생성', '조합과 후보 자동 완성' 같은 새로운 입력 도구들을 세세한 외형부터 시작해서 이들이 내부에서 입력 엔진과 통신하는 인터페이스를 완전 무에서 처음 창조해 내야 했으니 말이다.

단순히 당장 기능이 동작만 하게 만드는 게 아니라, 앞으로 두고두고 후회가 없을 정도로 최상의 성능과 최적의 확장 가능성을 현실적으로 잘 절충하여 구현한 거라고 개인적으로 확신을 갖는 것이 매우 어렵고 힘든 일이었다.
하지만 다 만들어 버리고 나니 언제 힘든 일이 있었냐는 듯 아무렇지도 않고 세상은 그저 평온하기만 하다.

본인은 2010년대 이래로 현재, 날개셋 한글 입력기의 개발 todo list의 분량이 역대 최저인 시기를 살고 있다. 그만치 마음의 부담을 많이 덜었다.
하지만 대단원을 장식할 그 마지막 기능을 과연 어떻게 설계하고 구현할지가 만만찮은 상태이고, '저것까지만 다 만들어 놓으면 뭐 어떻게 되겠지' 그렇게 생각하던 당시와 비교했을 때 세상이나 내 상태가 그렇게 확 달라진 것 같지는 않다. 이건 당연한 귀결인 건가..;; 아무튼.. 개발 근황 소식을 전하도록 하겠다.

1. 24픽셀 글꼴 추가

날개셋 한글 입력기는 지난 9.0 버전부터 150% (144dpi) 이상의 화면 확대 배율에서 24픽셀 비트맵을 지원하기 시작했다.
이건 굉장히 뜻깊은 과업이었으나, 24픽셀은 16픽셀에 비해 글꼴이 훨씬 적고 부족하다는 게 못내 아쉬움으로 남았다. 옛날에는 이게 화면용이 아니라 도트 프린터 인쇄용 크기였기 때문이다.

그랬는데.. 이때 본인의 머리를 스친 것은 1990년대에 PC 통신을 통해 한창 굴러다니던 아래아한글용 공개 글꼴들이었다. 이런 것들은 비록 비트맵이지만 문서에다 넣고 인쇄도 할 목적으로 쓰이므로 24픽셀, 그리고 심지어 레이저용 40픽셀 글립까지 있다. 내가 이것들 생각을 왜 지금까지 못 했을까?

본인도 중학교 시절까지는 글꼴에 완전 꽂혀서 수십여 종의 공개 글꼴들을 받아서 쓴 적이 있었다. 전문 업체에서 만든 글꼴의 퀄리티에 비할 바는 못 되었지만 저마다 개성이 담겨 있었다.
이것들은 아래아한글 1.0 ~ 1.2 시절, 비트맵 글꼴 에디터가 있던 시절에 근성의 도트 노가다를 통해 만들어진 물건이었다.

2.x도 아니고 1.5쯤 되면서 아래아한글은 자체적으로 조합 로직을 정의할 수 있는 한글 글꼴 포맷까지 제정했으나, 정작 예전처럼 사용자가 고정된 조합 로직 틀에서라도 한글 글꼴을 customize할 수 있는 통로는 완전히 막아 버렸다.
그리고 1.5, 그리고 2.0까지만 해도 보급 글꼴 말고 새로운 글꼴을 구해다 쓰는 건 마치 Doom 2에서 custom WAD 맵을 얹어서 플레이 하는 것만큼이나 번거롭고 귀찮고 어려웠다.

그때까지만 해도 아래아한글은 새로운 외부 글꼴을 등록해서 쓰는 것을 고려하여 설계되지 않았으며, 심지어 글꼴 파일 내부에 자기 이름이나 제조사 같은 메타정보도 들어있지 않았었다. 2.0 전문용에서 첫 도입됐던 한양 시스템의 윤곽선 글꼴조차도 말이다. 그 파일들은 나 같은 사람이 내부 포맷 분석에도 금세 성공했을 정도로 그냥 무식한 벡터 이미지 덩어리 그 이상도 이하도 아니었다.

그 와중에 과거에 만들어졌던 싸제 한글 비트맵 글꼴을 쓸 수 없어서 현기증 난다는 사용자들의 원성이 빗발치자.. 한컴에서는 이미 만들어진 글꼴들을 아래아한글 2.x 같은 후대 버전에서 사용할 수 있게 간단한 파일 포맷 변환 유틸 정도나 만들어서 던져 줬을 뿐이었다.

이렇게 상황이 열악하다가 그나마 1993년에 출시된 아래아한글 2.1은 잘 알다시피 통합 글꼴 포맷을 최초로 도입하고 글꼴 시스템 전용 환경설정 프로그램(fontcfg)까지 도입하면서 이 바닥 시스템이 크게 개선되었다. 뭐, 과거 회상은 이 정도까지 하기로 하고..

본인은 아래아한글이 제공했던 모든 HFT 폰트 파일들을 지금도 갖고 있지만, 그 시절에 추가로 받아서 썼던 공개 글꼴들은.. 너무 옛날 것이어서 그런지 컴퓨터가 바뀌는 과정에서 소실된 듯했다. 남아 있는 게 없었다.
그래도 인터넷 각지에 있는 고전 프로그램 자료실을 뒤진 끝에.. 현재까지 날개셋에 16픽셀로만 존재하던 다음 글꼴의 24픽셀 버전을 구해서 추출하는 데 성공했다. 굽은샘물, 손글씨, 타자기, 파도.. 4종이다.

사용자 삽입 이미지

특히 '손글씨'는 16픽셀에서는 획이 거칠고 별로 보기 좋지 않은데 24픽셀이 훨씬 더 보기 좋다.
굽은샘물은 언뜻 보기에는 예쁘게 생겼지만 '게'와 '계'가 분간되지 않는 등 결함이 몇 군데 있다. 그런데 24픽셀에서도 완전히 똑같은 결함이 남아 있다는 게 아쉬운 점이다.

명조 고딕, 거기에 기껏해야 필기체 일색이던 시절보다 글꼴 부족 문제가 좀 개선됐으면 좋겠다.
뭐, 한글뿐만 아니라 영문 글꼴도 아주 부족하긴 하지만.. 이건 더 답이 없는 문제인 듯하다.

2. 글꼴들의 내부 구조 최적화

그리고, 이렇게 글꼴 관련 작업을 하는 과정에서.. 내부적으로 조합형 한글 글꼴(*.hfn) 파일을 생성할 때 사용하는 빌드 툴을 대대적으로 손질하여 다시 만들었다. 그리고 꼼꼼한 최적화 기능을 추가했다.

최적화라는 게 무슨 말인가 하면...
샘물· 타자기 같은 글꼴들은 벌수가 적고 내부 구조가 굉장히 간단하다. 하지만 8*4*4 도깨비처럼 기존의 범용적인 조합 로직에 끼워 맞춰 글꼴을 만들다 보면 결국 같은 모양의 글자를 여러 벌에서 쓰도록 복붙을 해야 하고 중복 정보가 발생하게 된다.

먼 옛날, 날개셋 한글 입력기 5.3과 함께 본인이 처음으로 작성한 hfn 생성 툴은 사용자가 작성해 준 스크립트대로 곧이곧대로만 파일을 만들었다. 하지만 다음 버전에 들어가는 hfn 파일들은 크기가 약간이나마 더 작아져 있을 것이다.

==========
Processing 샘물2.txt..
Unit count: 19/21/27
Group size: 1/2/2
Bol count: 2/1/2
Load OK! (cell size 32, trivial? 1/1/1)

Group reduced to: 1/2/1
Wrote 113 glyphs

==========
Processing 타자기.txt..
Unit count: 31/29/31
Group size: 1/2/2
Bol count: 2/1/1
Load OK! (cell size 32, trivial? 1/1/1)

Group reduced to: 1/2/1
Wrote 122 glyphs

==========
Processing 굽은샘물.txt..
Unit count: 19/21/27
Group size: 1/3/2
Bol count: 3/1/2
Load OK! (cell size 32, trivial? 1/1/1)

Group reduced to: 1/3/1
Wrote 132 glyphs


 물론 지금 제공되고 있는 샘물 계열 글꼴들은 이미 손으로 직접 최적화한 굉장히 단순한 조합 로직 기반이기 때문에 16픽셀용이 크기가 4~5KB 남짓에 불과할 정도로 작다.
그런데 수동으로 최적화하면서 지금까지 생각하지 못했던 것이 바로.. 종성 그룹을 굳이 받침이 있는 경우와 없는 경우로 분류할 필요가 없다는 것이었다. 샘물 계열은 그 정의상 애초에 그거 구분이 필요하지 않은 글꼴이니까 말이다.

바로 이런 헛점을 프로그램이 추가적으로 찾아 줬기 때문에 종성의 그룹 수가 하나 더 줄어들었다. (group size → group reduced to)

아래아한글이 옛날에 제공하던 '가는샘물'과 '필기'는 나름 자체적인 조합 로직을 갖추고 있는 파일이다. 그럼에도 불구하고 최적화 검사를 해 보니 사용하지 않는 자모 그룹, 중복되는 벌이 존재하여 파일 크기를 몇백 바이트나마 더 줄일 수 있었다(16픽셀 버전 기준). 물론 겉으로 드러나는 글자 출력 모습은 하나도 변함없고 말이다.

==========
Processing 바탕.txt..
Unit count: 31/29/31
Group size: 2/11/3
Bol count: 10/4/4
Load OK! (cell size 32, trivial? 1/0/1)

Wrote 528 glyphs

==========
Processing 바탕24.txt..
Unit count: 31/29/31
Group size: 2/10/3
Bol count: 9/4/4
Load OK! (cell size 72, trivial? 1/0/1)

Group reduced to: 2/9/3
Wrote 492 glyphs

==========
Processing 돋움옛한글24.txt..
Unit count: 124/94/139
Group size: 1/8/2
Bol count: 6/2/4
Load OK! (cell size 72, trivial? 1/1/1)

Bol reduced to: 6/2/3
Group reduced to: 1/6/2
Wrote 1349 glyphs

==========
Processing 궁서옛한글24.txt..
Unit count: 124/94/139
Group size: 1/8/2
Bol count: 6/2/4
Load OK! (cell size 72, trivial? 1/1/1)

Bol reduced to: 6/2/2
Group reduced to: 1/4/2
Wrote 1210 glyphs


2000년대 중반에 마소에서 Office Plus pack을 통해 제공했던 옛한글 글꼴은 일단 6*2*4벌 구조이다. 하지만 굴림과 바탕 말고 돋움과 궁서는 시간에 쫓겨서 대충 만들었는지 종성의 벌수가 실질적으로 더 적다. 궁서는 구조적으로 아주 정교한 글꼴인데도 종성이 두 벌로밖에 이뤄지지 않았으니 왠지 엉성해 보일 수밖에 없을 것이다. (날개셋 한글 입력기에 포함돼 있지는 않음)

아래아한글이 제공하던 24픽셀 명조도.. 중성의 벌수가 10개이던 것이 9개로 더 줄어들었다.
오히려 화면용 16픽셀 명조는 11개인데.. 더 큼직한 공간에서 더 미려하게 만들어야 할 인쇄용 글꼴이 미세하게나마 구조가 더 단순한 것을 알 수 있었다.

==========
Processing unoptimized.txt..
Unit count: 19/21/27
Group size: 2/8/2
Bol count: 10/4/3
Load OK! (cell size 72, trivial? 1/0/1)

Bol reduced to: 2/1/2
Group reduced to: 1/2/2
Wrote 113 glyphs


이건 뭐.. 최적화 기능을 구현하면서 테스트 용으로 사용했던 극단적인 예제이다.
벌과 그룹 수가 최적화 결과 얼마나 드라마틱하게 줄어드는지, 한편으로 아래아한글에서 쓰이던 24픽셀용 '타자기'체가 얼마나 비효율적인 형태로 저장돼 있었는지를 알 수 있다.

그리고.. 이미 만들어져 있는 다른 글꼴 파일을 바이너리 수준에서 변환만 할 때는 신경 쓸 필요가 없지만, 순수하게 텍스트 포맷의 스크립트로부터 조합형 한글 글꼴을 빌드할 때 해결해야 하는 문제가 하나 있다.
바로, 문자열 형태의 벌 명칭을 서로 겹치지 않고 중간 공백(slack) 낭비도 없이 최대한 조밀하게 번호로 대응시키는 알고리즘이다. 모든 벌이 한 성분(초중종 중 하나)의 모든 낱자를 사용하고 있다면 신경 쓸 필요가 없지만, 여러 벌들이 듬성듬성 부분적인 낱자 그룹을 사용할 때는 문제가 복잡해진다.

이 문제는 곧이곧대로만 푼다면 시간 복잡도가 그냥 NP 완전이 된다.. =_=;; 그런데 (1) 지도에서 여러 점들의 주변에 지명을 서로 겹치지 않고 최대한 많이 적절하게 배치하기, (2) 여러 지점들의 최단 순회 경로 구하기, (3) 컴파일러의 코드 최적화 과정에서 제한된 레지스터 공간에 구간별로 주요 지역 변수들을 적절히 등재하기, (4) 수직· 수평으로만 움직일 수 있는 여러 자동차들이 좁은 부지 안에 세워져 있고 이 차를 빼기 위해서 저 차를 빼야 되고 다른 차를 원상복구 시키는 복잡한 상황에서 특정 차를 빼내는 순서 구하기 등.. 실생활에서는 이런 부류의 intractable한 휴리스틱 문제들이 의외로 많이 존재한다.

모든 벌에 대해서 그냥 심벌 테이블을 부여하고 번호가 아니라 문자열로 벌을 식별한다면 slack 걱정을 할 필요가 없긴 하다. 하지만 겨우 날개셋이 사용하는 글꼴 파일이 그런 것까지 사용할 정도로 구조가 복잡하지는 않기 때문에 그냥 숫자 기반 인덱스 체계를 채택한 것이다. 컴퓨터에서 문자열로 아이템을 식별하는 건 포인터와 동적 메모리 등.. 단순 숫자보다 성능 비용이 더 크다.

이로써 조합형 한글 글꼴 빌드 절차는 데이터 읽기 → (글립 최적화 → 조합 로직 테이블 최적화 → 명칭 순서 최적화) → 출력의 순으로 절차가 깔끔하게 완성됐다. 컴파일하는 기능뿐만 아니라, 반대로 빌드된 hfn 파일을 다시 텍스트 기반 스크립트로 환원하는 디컴파일 기능도 이 참에 넣어서 관련 기능을 완전히 끝장을 봤다.

좋은 tool이 완성됐으니, 이제 도스 시절을 풍미했던 아래아한글용 공개 비트맵 글꼴들을 애타게 기다린다. 혹시 지금까지 갖고 계시는 분들은 본인에게 제보해 주시면 좋겠다. 이제 글꼴 분석과 빌드 툴이 완벽하게 갖춰졌고 무엇이건 내 프로그램에 반영 가능하다.

3. 한글 로마자 입력기

끝으로, 10여 년째 기능에 변화가 거의 없던 한글 로마자 입력기 빠른설정에 '공업진흥청 방식'이라는 새 템플릿이 추가되었다.
이건 macOS에서 전통적으로 지원되어 온 로마자 입력 방식인데, 정작 날개셋 한글 입력기가 제공하는 기존 템플릿 5개와는 일치하는 게 없는 독자적인 입력 방식이었다.

ㅓ, ㅡ는 표준 표기법처럼 eo, eu로 입력하지만 ㅇ은 x로 입력하고 ㅚ는 oe로만 입력하며, Shift로 쌍자음을 입력하는 게 마치 여러 입력 방식을 짬뽕한 것 같다.
공진청 방식을 실제로 즐겨 사용한다는 외국인에게서 요청을 받아서 이 기회에 템플릿을 추가했다.

이로써 로마자 입력기 중에서 쌍자음을 1타(Shift도 포함)로 입력 가능한 템플릿은 북한 방식, Korean Writer 방식, 그리고 공진청 방식 이렇게 세 종류가 존재하게 됐다.
지금까지는 ㄸ, ㅉ, ㅃ가 종성에도 들어갈 수 있게 수식이 만들어졌다. 하지만 다음 버전에서는 이를 수정하여 이 낱자들은 언제나 초성에만 입력되게 할 것이다. 로마자 입력 방식이 딱히 옛한글을 염두에 둔 입력 방식은 아니기 때문이다.

4. 나머지

(1) 홈페이지에서 날개셋 한글 입력기를 소개하는 컨텐츠를 더 추가했다.
"첫 화면"에는 한국어가 아니라 한글 차원에서 연구했다는 개발 이념을 더 강조했고, "전반적인 특징"에도 기능 소개 스크린샷을 잔뜩 추가했다.
그리고 입력 도구, 빠른설정, 텍스트 필터만을 소개하는 "입력 보조 기능 소개"라는 페이지를 새로 추가했다. 내가 지금까지 이 바닥으로 참 어지간히도 많은 기능들을 꾸역꾸역 집어넣었다는 생각이 들었다.

(2) 24픽셀 상태에서 날개셋 편집기로 일본어· 중국어 IME를 사용해 보면(빈 입력 스키마), 조합 상태를 나타내는 밑줄이 여전히 16픽셀 기준으로 찍혀서 밑줄이 아니라 취소선처럼 나타나는 버그를 뒤늦게 발견했다. 9.0 이래로 지금까지 계속 이런 버그가 있었다는 뜻인데, 이제야 발견되어서 고쳤다. 심지어 세로쓰기일 때도 이런 문제가 없는데 요건 9.0 작업 당시에 코딩 실수가 있었던 모양이다.

요건 현재까지 날개셋 한글 입력기에서 발견된 거의 유일한 버그이다. 프로그램이 죽는다거나 하는 문제도 아닌 외형적인 실수이다.

5. 맺는 말

(1) 예전에도 한번 말한 적이 있을 것이다. 본인은 사실 생각 같아서는 기능 구현뿐만 아니라 API 구조도 마음에 안 드는 것들을 다 갈아엎고 싶고, 지금의 set/ist의 파일 포맷도 다 바꿔 버리고 싶다. 저 기능들을 처음 구현하던 시절이랑, 지금 이렇게 실물이 완성된 뒤에 본인이 생각하는 날개셋 한글 입력기의 내부 구조 및 전체 그림이 서로 같지 않기 때문이다. 지금이 당연히 더 추상적이고 체계가 더 잡혀 있다.

하지만 그건 사용자의 눈에는 띄는 게 전무한 리팩터링에 불과하기 때문에, 내부 구조가 깔끔해지는 것 말고 들이는 시간과 노력 대비 단기적으로 드러나는 성과가 너무 없고 리스크도 크다.
그런 것까지 무리하게 욕심 내서 다 추진하다가는 내가 학교 졸업을 도저히 할 수 없어지니.. 그건 하더라도 졸업 후에, 날개셋 버전이 10 정도로 확실하게 넘어갔을 때의 매우 장기적인 과제로 남기고자 한다. 이미 지금도 당초 계획보다 이미 1~2년 정도 지연이 발생해 있다. 당초 계획이 없던 기능들도 이것저것 막 구현하는데 나의 귀차니즘과 슬럼프, 능력 부족까지 겹쳐서..

어쨌든, 내 근황과 신상에 큰 변화가 생기지 않은 지금 여건 하에서... 적어도 2010년대 동안은(18~19) 날개셋 한글 입력기는 잠정적으로 9.5x 정도가 마지막 버전이 될 것이다.
9.5 이후로 당분간은 새 버전이 나오더라도 사소한 기능 추가나 버그 수정 위주로만 나올 것이다. 운전에다 비유하자면, 저단에서 고rpm으로만 계속 가속하는 데 한계를 느낀다. 나도 고단으로 변속 좀 해야지..

(2) 당연하다면 당연한 얘기인데, 요즘 문자 입력기들의 추세는 확실히 언어 데이터를 기반으로 자주 사용하는 단어, 앞으로 또 나올 가능성이 높은 문구를 미리 제시해 주는 것이다.
Windows에 내장된 한국어나 중국어 IME는 15년 전이나 지금이나 동작이 크게 달라진 것이 없는 반면, 일본어 IME는 그렇지 않다. Windows XP에서는 TSF의 새로운 기능을 활용해서 조합(composition)을 만들고 있는 중에도 cursor가 조합 안팎을 자유자재로 드나들 수 있는 natural input mode라는 걸 도입했다.

그리고 Windows 8이나 10쯤에서는 전통적인 "발음 입력+space → 단어를 나눠서 구간별로 변환"이라는 동작에서 탈피하여.. 구간 구분 없이 대충 말을 입력하다 보면 다음에 입력할 걸로 예상되는 말이 같이 후보 리스트에 뜨면서 한꺼번에 뭔가 '스마트'하게 동작하는 걸로 재미있게 바뀌어 있었다.

한글과 영문은 단어 단위 조합은 모바일에서만 존재하며, PC에서는 그런 계층이 없다. 일본어는 기왕 단어와 문장이 조합창을 몽땅 거치다 보니, 거기서 할 수 있는 모든 언어적인 변환은 다 끌어들이는 것 같다.
그 반면 한글은 기본적으로 글자 단위+rule 기반의(data 기반이 아닌) 간단한 조합만 거친다. 이런 구조에서 할 수 있는 온갖 기괴한 응용 기능은 지금까지 날개셋 한글 입력기의 주된 관심사요 개발 아이템이었다.

하지만 거시적으로는 한국어의 입력도 그런 기능에다가 어절 단위의 더 큰 규모의 조합과 접목할 필요가 있다. 이를 염두에 두고 이번 9.3에서는 "조합 안에 조합 생성"이라는 입력 도구가 추가되었다. 이것은 날개셋 한글 입력기의 개발 역사상 매우 의미심장한 기능이다.
내 여건이 허락하는 한, 날개셋 한글 입력기는 한글 같은 문자를 기반으로 하여 생각할 수 있는 모든 입력 기능을 구현할 수 있고, 이를 토대로 세벌식 자판의 장점도 덤으로 부각시킬 수 있는 입력 엔진으로 굳건히 성장할 것이다. 그리고 그 결과물이 프로그램과 논문으로 더 나오게 될 것이다.

Posted by 사무엘

2018/03/28 08:33 2018/03/28 08:33
Response
No Trackback , 5 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1472

코딩을 하다 보면 한 자료형(타입)에 속하는 값 내지 개체를 다른 타입으로 변환해야 할 때가 있다. 아주 직관적이거나(C에서 정수와 enum), 작은 타입에서 큰 타입으로 가기 때문에 정보 손실 염려가 없는 상황에 대해서는 해당 언어의 문법 차원에서 대입이나 함수 인자 전달이 곧장 허용되곤 한다. 바이트 수가 대등하더라도 signed보다는 unsigned가 더 큰 타입이고, 정수보다 실수가 더 큰 타입으로 여겨진다.

그렇지 않고 정보 소실의 여지가 있거나 서로 호환되지 않는 타입에다 값을 집어넣는 건 컴파일러의 경고나 에러를 유발한다. 이 상황에 대비하여 프로그래밍 언어들은 여러 형변환 함수들을 제공하는데, 씨크하게 괄호 안에다가 타입 이름만 달랑 쓰면 형변환 연산자로 인식되는 C/C++이 여기서도 참 유별난 면모를 보이는 것 같다.

형변환이란 건 값의 초기 타입과 목적 타입이 무엇이냐에 따라 내부에서 벌어지는 일이 매우 다양한 편이다.

(a) 없음: LPARAM에서 void*, void*에서 char* 같은 무식한 형변환은 소스 코드상의 의미만 매우 과격하게 바뀔 뿐, 내부적으로 행해지는 일은 전무하다! (클래스 상속 관계를 신경쓸 필요 없는 간단한 것 한정으로)

(b) 그냥 뒷부분 짜르거나 늘리기: int와 char 사이의 변환

(c) 정수와 실수 사이를 변환: 내부적으로 일어나는 일이 분명 간단하지는 않지만, 요즘은 이런 건 CPU 명령 한 줄이면 바로 끝난다. x86 기준으로 cvtsi2sd이나 cvttsd2si 같은 인스트럭션이 있다.

(d) 고정된 오프셋 보정: 단순한 형태의 다중 상속에서 제n의 부모 클래스 포인터를 얻으려면 이런 보정이 필요하다.

(e) 함수 호출: 해당 클래스에다 사용자가 구현해 놓은 operator 함수가 호출된다. 사실, 100과 "100"처럼 숫자와 문자열 사이의 변환도 컴퓨터의 관점에서 보면 이 정도의 cost가 필요한 작업이다.

(f) 상속 계층 관계 그래프 순회: 이건 객체지향 이념 때문에 형변환 연산이 언어 차원에서 가장 복잡해지는 상황이다. 위의 1~5와는 근본적으로 차원이 다르다. 가상 상속 체계에서 부모 클래스를 찾아가거나 dynamic_cast 형변환을 하려면 자기의 타입 정보 metadata를 토대로 주변 클래스 계층 그래프를 O(n) 시간 동안 순회해야 한다(n은 상속 단계).
사용자의 코드가 아닌 컴파일러의 코드만 실행됨에도 불구하고 런타임 가변적인 반복이 발생할 수 있으며, 객체와 메모리 상태가 어떤지에 따라서 형변환 결과가 dynamic하게 달라질 수 있다!

내부적으로 벌어지는 일은 대략 저렇게 분류 가능하고, 겉으로 소스 코드의 의미 차원에서도 형변환의 성격을 몇 가지로 분류할 수 있다.
처음에 C++에는 그냥 C-style cast밖에 존재하지 않았다. 그런데 그랬더니 형변환 연산이 발생하는 부분만 검색으로 뽑아내는 게 어려웠고, 또 단순한 형변환과 좀 위험한 형변환 같은 걸 따져 보기도 어려웠다. 표현 형태가 괄호와 타입 이름이 전부이니까..

그래서 1996~97년경, C++98의 발표를 앞두고 C++에는 길고 굉장히 기괴해 보이지만 용도별로 세분화된 형변환 연산자가 4종류나 추가되었다. namespace, bool, explicit, mutable 이런 키워드들과 같은 타이밍에 도입되었다. C++이 숫자는 몽땅 machine-word int로 어영부영 때우려던 C스러운 사고방식을 탈피하고, 예전에 비해 나름 type-safety를 따지기 시작한 그 타이밍이다. (예: C와 C++에서 sizeof('a')의 값의 차이는?)

새 연산자들은 모두 *_cast로 끝난다. 옛날의 재래식 형변환은 (NEWTYPE)value라고 썼고 C++ 문법으로는 NEWTYPE(value)도 허용되는 형태였다(NEWTYPE이 딱 한 단어 토큰으로 떨어지는 경우에 한해서).
그에 비해 새 연산자들은 *_cast<NEWTYPE>(value)라고 쓰면 된다. < > 안에다가 타입 이름을 쓰는 것은 템플릿 인자 문법에서 유래되었는데 나름 직관적이고 적절한 활용 같다.

1. static_cast

얘는 일상적으로 가볍고 큰 무리 없이 일어나는 일반적인 형변환을 거의 다 커버한다. (1) 큰 타입에서 작은 타입으로(실수에서 정수, UINT에서 int, long long에서 int 등..), 그리고 (2) 범용적인 타입의 포인터에서 더 구체적인 타입의 포인터로(void*에서 타 포인터, 기반 클래스*에서 파생 클래스 포인터) 말이다. 이게 대부분이다.

그리고 형변환 operator 호출이라든가, 다중· 가상 상속으로 인한 포인터 보정도 언어에서 보장돼 있는 메커니즘이므로 알아서 처리해 준다. 정말 대부분의 상황에서 앞서 나열했던 (a)에서 (f)까지, C-style cast를 대체할 수 있는 무난한 연산자이다. 단, f에서 typeid와 RTTI까지 동원되는 제일 비싸고 난해한 기능은 없으며, 이건 나중에 설명할 dynamic_cast가 전담하는 영역이다.

2. const_cast

얘는 값이 아니라 포인터/참조자형에서 C/C++ 특유의 한정자(qualifier) 속성만을 제거해서 더 범용적인 포인터로 만들어 준다. 그러므로 용도가 아주 제한적인 형변환 연산자이다.
C++에서 공식적으로 제공되는 qualifer는 const와 volatile이 있다. 이런 한정자는 가리키는 대상 타입과는 아무 상관 없고, 포인터를 이용해 그 메모리를 접근하는 방식 차원에서 제약을 부여할 뿐이다. 전자는 읽기 전용 속성이고, 후자는 멀티스레드에 의해 값이 언제든 바뀔 수 있음을 대비하라는 최적화 힌트이다.

Visual C++에는 __unaligned라는 확장 키워드도 저것들과 동급인 한정자이다. 이 포인터는 machine word 단위의 align이 맞춰지지 않은 주소가 들어올 수도 있으니 그렇더라도 뻗지 말고 보정하라는 뜻이다(성능 오버헤드 감수하고라도). align 보정을 알아서 너무 잘 해 주고 있는 x86 계열은 전혀 해당사항이 없고, 과거에 IA64를 지원하던 시절에 필요했던 키워드이다. 이것도 포인터 한정자 속성으로서는 굉장히 적절한 예이며, 이런 속성들을 const_cast로 제거할 수 있다.

3. reinterpret_cast

이건 의미론적으로는 제일 무식하고 생뚱맞고 위험하지만 내부 처리는 제일 할 것 없는 형변환 전문이다. (1) 정수와 포인터 사이를 전환하는 것, 그리고 (2) 서로 관련이 없는 타입을 가리키는 포인터끼리 전환하는 것.. 어디 범용적인 함수나 메시지로부터 아주 polymorphic한 데이터를 전달받아서 처리할 때에나 불가피하게 쓰일 법하다.

void*를 char*로 바꾸는 건 static_*으로도 되고 reinterpret_*으로도 된다. 하지만 const char*를 char*로 바꾸는 것은 static_*나 심지어 reinterpret_*로도 안 되고 반드시 const_*로만 해야 한다.
그런데 그래 봤자 reinterpret_*과 const_*는 어떤 경우에도 실질적인 내부 처리는 (a)뿐인(= 없음).. 참 허무한 연산자이다. 실질적인 처리가 없지만 이 숫자값을 해석하는 방식을 변경하는 이유는 분야별로 여럿 존재할 수 있다는 뜻이다.

재래식 C-style cast는 따지고 보면 1~3을 그냥 싸잡아서 구분 없이 수행해 준다. 그런데 가끔 드물게 다중· 가상 상속 관계의 타입 포인터끼리 형변환을 할 때 정석대로 보정 연산을 거친 포인터를 원하는지(static_*), 아니면 이것도 아무 보정 없이 동일한 메모리 주소에서 타입만 바꿔서 해석하고 싶은지(reinterpret_*) 모호해질 때가 있다.

이런 문제도 있고, 또 C++에다가 좀 제대로 된 객체지향 언어의 기능을 뒤늦게 갖추려다 보니 새로운 형변환 메커니즘이 필요해졌다. 이쯤 되니까 형변환 연산자도 별도의 예약어로 도입해서 구분하지 않고서는 도저히 버틸 수 없는 지경이 됐다. 그럼 다음으로 제일 괴물인 형변환 연산자에 대해서 살펴보도록 하자.

4. dynamic_cast

가상 함수를 쓰면 기반 클래스의 포인터를 주더라도 자신이 실제로 속한 파생 클래스에 해당하는 멤버 함수가 알아서 호출된다.
그리고 다중 상속 때 가상 상속을 쓰면, 여러 부모 클래스들이 동일한 조부모 클래스로부터 상속받았을 때 공통 조부모가 한 번만 상속되는 마술(?)이 일어난다. 물론 객체지향 언어에서 유연한 코드 재사용성을 보장하는 모든 마술에는 그에 상응하는 성능 오버헤드가 대가로 따른다는 점은 감안할 필요가 있다.

그런데 과거의 C++은 상속과 함수 호출에서 이렇게 언어 차원의 동적 바인딩이 지원되는 것과 대조적으로, 형변환에는 "동적 바인딩 + 무결성"을 보장하는 메커니즘이 딱히 없었다. 이놈이 A의 파생 클래스이긴 하지만 더 구체적으로 A의 자식들 중에 B가 아닌 C의 파생 클래스가 진짜로 맞는지, 이 멤버에 접근하고 이 함수를 호출해도 안전하겠는지 말이다.

C++은 void*가 있을지언정, 언어 차원에서 모든 클래스들의 공통 클래스(Java의 Object 같은)라는 개념이 없다. 그리고 클래스 내부의 vbtl에 직접 접근한다거나, 가상 함수의 포인터 값을 보고 클래스 종류를 판별할 수 있을 정도로 C++이 ABI가 몽땅 왕창 노출돼 있느냐 하면 그렇지도 않다(겉에는 공통의 썽킹 함수의 주소만 노출돼 있기 때문). 뭔가 어정쩡하다.

그러니 MFC 같은 옛날 라이브러리들은 자체적으로 CRuntimeClass 같은 타입 메타정보를 구비하고, 이놈이 CObject의 파생이긴 하지만 특정 클래스의 파생형이 정말 맞는지 런타임 때 확인하는 함수를 자체적으로 구현해야 했다.
C++이 아무리 C의 저수준 고성능 제어 이념을 계승했다 해도 명색이 객체지향 언어인데 그런 기능조차 없는 건 좀 아니라 여겨졌는지, 훗날 언어 차원에서 타입 식별 정보와 전용 형변환 연산자가 도입됐다. 그 결과물이 바로 dynamic_cast이다.

함수도 virtual, 상속도 virtual인 걸 감안하면 얘의 이름은 기술적으로 virtual_cast라 해도 과언이 아닐 듯하다. 하지만 static_* 이라는 단어가 이미 있으니 그것보다 더 값비싼 형변환이라는 의미로 dynamic_*이라는 이름이 최종적으로 붙었다. static_*은 이놈이 진짜 조상 관계가 맞는지 확인하지 않고 그냥 O(1) 복잡도짜리 기계적인 오프셋 보정만 해서(필요한 경우) 파생 클래스로 형변환해 주는 반면, dynamic_*은 타입 식별자를 직접 확인까지 한다는 것이다.

가상 함수의 구현을 위해서는 함수 포인터 테이블과 테이블의 포인터(멤버)가 필요하고, 가상 상속의 구현을 위해서는 기반 클래스의 포인터(멤버)가 필요하다. 그것처럼 동적 바인딩 형변환을 구현하려면 클래스 이름과 계층 관계를 기술하는 메타데이터와 함께 그놈의 포인터(멤버)가 필요하다.

가상 함수와 가상 상속 지원을 위한 데이터는 비공개로 꽁꽁 감춰져 있는 반면, 동적 바인딩 형변환을 위한 타입 식별자 데이터는 공식적으로 스펙이 공개돼 있고 일반 프로그래머들이 언어 요소를 통해 접근할 수 있다. 일명 RTTI (run-time type info)이다.
#include <typeinfo>를 한 뒤 typeid() 연산자로 const type_info&라는 구조체, 아니 클래스를 들여다보면 된다. typeid는...

  • sizeof와 마찬가지로 오버로딩 할 수 없다.
  • sizeof와 마찬가지로 타입 이름과 일반적인 값을 모두 받을 수 있다. 단, sizeof는 값이 올 때는 괄호를 생략할 수 있는 반면, typeid는 그렇지 않아서 언제나 뒤의 피연산자를 괄호로 싸야 한다.
  • sizeof는 결과값이 무조건 정적 바인딩으로 구해지는 반면, typeid는 바인딩이 정적과 동적 사이에서 어정쩡하다. 피연산자가 타입 이름이거나, 값이더라도 int 같은 primitive type 내지 상속이고 가상 함수고 아무것도 없는 구조체라면.. sizeof와 마찬가지로 수식을 실제로 evaluate하지 않는다. 그러나 뭔가 상속 관계 규명이 필요하다 싶은 개체라면 런타임 계산이 행해진다.

쉽게 말해 typeid는 MFC로 치면 obj->GetRuntimeClass()와 RUNTIME_CLASS(classnam)의 역할을 혼자 모두 수행한다는 뜻이다.
그럼 관련 타입들에 대해 DECLARE_DYNAMIC 내지 IMPLEMENT_DYNAMIC도 어딘가에 행해져야 할 텐데, 그건 C++ 컴파일러가 typeid 연산자가 쓰인 곳을 총체적으로 따져서 알아서 처리해 준다.

이런 RTTI 기능은 대다수의 C++ 컴파일러에서 사용 여부를 옵션으로 지정할 수 있게 돼 있다.
RTTI를 사용한 상태에서 dynamic_cast를 사용하면 실제 타입이 그게 아닌데 그 파생 타입으로 형변환을 시도하는 경우.. 피연산자가 포인터였다면 NULL이 날아오고, null이 가능하지 않은 참조자를 줬다면 예외(exception)를 날리게 된다.
이것도 다중 상속까지 생각한다면 상속 관계 그래프를 타고 오프셋을 보정하는 알고리즘이 가히 판타지가 될 것 같다.

이상.
객체지향 언어라는 게 그냥 구조체에다가 this가 자동으로 전달되는 함수가 같이 딸려 있고, 상속에 다형성 정도 지원되는 게 전부라고 생각하는 게 얼마나 순진한 생각인지 알 것 같다.
PC 환경에서 C를 초월하여 C++이 보급된 게 1990년대 초쯤인데, 이 무렵에 템플릿과 다중 상속도 도입됐다. 처음부터 있었던 게 아니다. 그리고 그때부터 C++은 뭐랄까, 괴물 같은 복잡도를 자랑하는 치떨리는 언어로 변모한 것 같다.

구리고 지저분한 면모가 많지만 그래도 그 정도 객체지향 이념에다가 고성능 저수준 제어까지 이만치 잡은 건 얘가 C++이 가히 독보적인 것 같다.
그러니 형변환 연산자도 언어를 따라 이렇게 복잡해진 것이다. 일례로, Java는 final이 있을지언정 포인터도 없고 const니 volatile 같은 건 신경 쓸 필요도 없는데 뭐 저런 구분이 필요하겠는가? 그러니 지금도 여전히 C-style cast만으로도 충분한 것이다.

Posted by 사무엘

2018/03/25 08:30 2018/03/25 08:30
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1471

1. 영동과 부강 역 인근의 비밀 선로

경부선 성환 역은 동쪽 인근의 학정리 야산을 다 점유하고 있는 군부대 탄약창으로 이어지는 선로가 있는 것으로 잘 알려져 있다.
그 외에도 대전 근처의 영동 역도 서쪽으로 거대한 탄약창으로 이어지는 선로가 있다. 육군 종합 행정 학교가 영동 제일 요양 병원 근처로 이전해 오긴 했는데, 탄약창은 그것보다 더 북쪽에 있는 별개의 부대이다.

한편, 부강 역은 가까운 동북쪽으로는 철길이 아니고 도로이지만, 거대한 컨테이너 화물 기지가 있고, 또 뭔가 일반인이 범접할 수 없어 보이는 코렁시설이 있다. 단순 화물 기지이기만 했으면 저렇게 민간 지도에서 가려지고 숨겨지지는 않았을 것이다.
그리고 서쪽으로는 2010년에 '부강화물선'이 개통하여 민간 지도에도 보이는 화물 기지가 있다.

저기는 세종 자치시 중심부와 꽤 가까우며 바로 옆에 호남 고속선이 지나는 게 흥미로운 형태이다. 나름 물류 허브도 추구하는 것 같다.

2. 서울 지하철 5호선의 동쪽 지선

서울 지하철 5호선은 강동 역 이후로는 잘 알다시피 상일동과 마천 방면으로 Y 자 모양으로 노선이 갈라진다. 역 수는 마천 지선이 좀 더 많다.
상일동 지선: 고덕 차량기지가 지나는 관계로 서울 지하철 5호선 중 가장 먼저 개통한 구간이다(왕십리-상일동).
서울 지하철 2호선이 군자 차량기지를 끼고 신설동-종합운동장이 가장 먼저 개통한 것과 같은 이치이다. (그때는 성수 지선이 2호선의 본선이었음)

2010년대에 들어서 두 지선은 서로 다른 방향으로 변화하게 되었다.
마천 지선은 기존 지하철의 연장 건설로 인한 환승역이 생겼다. 오금이 3호선과 환승되고, 그리고 앞으로 올림픽공원은 9호선과 환승될 예정이다.
그 반면, 상일동 지선은 노선 자체가 하남시 쪽으로 더 연장되어 길어질 예정이다. 환승역이 생기는 것과, 노선이 더 연장되는 것 중 어느 게 더 호재일까?

상일동 지선은 서쪽이 한강으로 막혀 있어서 타 노선과 환승이 될 여지가 없는 반면, 마천 지선은 동쪽이 청량산으로 막혀 있어서 자기 자신이 더 연장될 여지가 없다. 무척 독특한 차이점이다.

상일동 쪽은 한강과 가까우며 명일 공원, 길동 공원, 고덕산 이렇게 언덕과 근린공원이 많다. 그런데 '굽은다리'라는 역은 도대체 무슨 다리가 굽었다는 말인지 모르겠으며, '고분다리'라는 명칭과도 맞물려서 초행 방문자를 더욱 헷갈리게 하고 있댄다.

마천 쪽은 둔촌동이라는 역명도 그렇고 왠지 '둔촌 이 집' 선생을 기리는 흔적이 눈에 띈다. 후손들이 자기 선조를 띄워 주려고 많이 노력을 한 것 같다. '둔촌'은 강서구의 '등촌'과는 전혀 무관한 명칭이다.

마천역은 처음 만들던 당시에는 특전사 부대와 군인 아파트 보안 때문인지 지금의 오금로· 마천로 큰길 쪽으로 출구를 못 만들고 마천 초등학교 쪽의 엉뚱한 골목길에다가 출구를 만들었다.
하지만 군부대가 몽땅 이전하고 아파트가 지어지고 있는 지금으로서는 큰길 쪽에 지하철 출구가 만들어지지 못한 게 안타까운 지경이 됐다. 사실, 지금 용산구에 있는 미군 기지가 대거 이전하고 나면.. 미군 기지 때문에 잔뜩 굴곡지고 왜곡돼 있는 기존 서울 지하철들의 선형도(특히 4호선) 눈에 더욱 띌 것이다.

3. 크게 뒤집어엎어진 서울 지하철 계획

서울에 지하철에는 원래 이렇게 계획되었다가 대판 갈아엎어져서 사라진 흑역사가 두 건 정도 존재한다.

(1)
하나는 1970년대, 양 택식 서울 시장 시절에 나왔던 구 1기 지하철 계획이다. 강남이나 여의도 따위 안중에 없이 강북 사대문 안만을 염두에 둔 방사형으로 5개 노선을 계획했다. 특히 5호선은 천호대로와 종로를 직결 운행했으며(나중에는 송파대로와 성남대로까지!!), 종로 구간은 1호선과 복복선 선로를 나란히 달릴 예정이었다.

그렇게 계획이 추진되어서 일단 서울 지하철 1호선이 갖은 난관을 뚫고 잘 개통했다. 그랬는데.. 1974년 광복절, 육 영수 여사 피격 사건이 터졌다. 그래서 양 시장 인생에서 최고의 순간이어야 했을 지하철 개통식은 아주 침통한 분위기에서 치러져야 했으며, 박 대통령도 참석을 못 했다.

생각을 해 보시라. 옛날 대한뉴스 영상을 보면 2호선 때 전대갈부터 시작해 1990년대 분당선 때(김 영삼)까지만 해도 지하철· 전철이 개통하면 대통령이 친히 와서 시승을 하고 관계자들 노고를 치하해 주는 게 관행이었다. 하물며 우리나라 최초의 지하철의 개통은 경부 고속도로의 개통에 준하는 기쁘고 뜻깊은 일이거늘, 그 당시의 기록 영상을 보면 정작 1호선 개통 때 박 대통령의 모습은 어디에서도 찾을 수 없다. 영부인이 총을 맞았으니 거기 갈 수가 없게 됐기 때문이다.

그리고 이 사건에 대한 책임을 지고 양 시장은 경질됐다. 운이 참 더럽게 없는 것 같다. 후임 구 자춘 시장은 1호선이야 이미 완공돼 버렸고 어차피 구간도 종로밖에 없으니 그렇다 치지만 나머지 1기 지하철 계획을 싹 갈아엎어 버렸다.
그는 서울을 다핵도시로 키울 생각을 하고 20분 만에 선을 쭉쭉 그어서 이례적인 지금의 2호선 순환선 디자인을 확정했다.

오늘날 서울 1기 지하철들은 초기 지하철이다 보니, 얕긴 하지만 추후 건설된 지하철들과 환승이 막장인 편이다. 1기와 2기끼리는 그렇다 치더라도 서울 지하철 최초의 환승역인 신설동 역까지 잠실 역에 준하는 평행형 승강장 막장환승인 이유는 이렇게 지하철 건설 계획의 리부트와 단절이 있었기 때문이다. 구 계획대로 1호선과 5호선의 환승을 염두에 두고 만들어 뒀던 신설동 역 지하 3층 유령 승강장이 버려지게 된 것도 같은 이유 때문이다.

(2)
다음으로 대판 나가리 난 중대 계획은.. 짐작하셨겠지만 3기 지하철 계획이다. 이건 1990년대 초중반에 계획이 나왔지만 몇 년 못 가 외환 위기· IMF· 긴축 재정으로 인해 흑역사로 전락했다.
지하철 건설 기술과 노하우가 그럭저럭 축적되다 보니, 2기 지하철들은 미래에 또 건설될 3기 지하철과의 환승을 미리 대비까지 하면서 건설되었다. 그런데 그것들이 상당수 활용되지 못하게 되었으니 안타까운 일이다.

이렇듯, 지하철 건설 역사 한 분야만 봐도 사람 일이라는 건 가까운 미래조차도 알 수 없는 것 같다. 앞에서 언급된 양 택식 서울 시장만 해도 지하철 잘 만들어 놓고는 개통식 당일에 저런 일이 터질 줄 누가 예상했겠는가?

환승 대비를 해 놓은 것으로는 7호선 논현(11호선), 6호선 녹사평(11호선), 8호선 몽촌토성(9호선, 색깔띠까지) 등 내가 당장 떠오르는 것만 해도 여럿 있다. 그나마 여의도는 십자형 환승역으로 예상이 정확하게 잘 적중했으며, 오금과 가락시장도 3호선의 남쪽 연장에 대비한 설계가 계획대로 쓰였다. 9호선이 연장되어 8호선과 만나는 건 당초 계획됐던 몽촌토성이 아니라 석촌이 당첨됐다.

3기 지하철 계획 중 기존 노선의 연장(3호선 오금, 7호선 부평구청)과 9호선 신규 건설만이 살아남았다. 이들은 2009~2012년 사이에 완공됐다. 10호선은 서울 외곽 내지 시외 구간만이 변별성을 인정받아 광역전철 신안산선으로 대체됐으며, 그야말로 2020년대 중반은 가야 결과물이 나올 듯하다.

11호선도 남쪽의 서울 외곽· 시외 구간만이 원형 그대로 살아남아서 사철 광역전철인 신분당선으로 대체됐다. 하지만 북쪽은 아직 논현까지 올라오지 못한 상태이다.
12호선은 서울 최초의 경전철로 대체되었는데, 원래 계획되었던 왕십리 환승은 가망 없고 신설동에서 시작한다.

요약하자면 구 1기 지하철은 그냥 나가리 났다. 3기 지하철은 일부만 살아남고 나머지는 일부 구간이 서울 지하철이 아니라 민자 광역전철이나 경전철이라는 생소한 형태로 부활했다.

4. 미래의 간선 철도: 경강선, 강원선, 중부내륙선

우리나라는 1970년대의 태백선 이후로 지방에 장거리 간선 철도 건설의 맥이 끊기다시피했다. 그 뒤로 생긴 철도들은 다 수도권 광역전철이나 공항 철도가 아니면 다 기존 철도의 선형을 유지한 고속화나 복선· 전철화, 선형 개량뿐이었기 때문이다.
그러다가 2010년대 중후반에 가서는 서쪽으로는 소사-원시선이 만들어지고 있으며, 동쪽으로는 철도 소외지이던 강원도에 철도다운 철도가 만들어지기 시작했다. 횡축으로는 경강선이요, 종축으로는 동해선이다.

2018년 현재 경강선은 두 파트로 나뉘어 있다. 하나는 원래 '성남여주선'이라는 이름으로 판교-이매-여주를 이으며 현재 통근형 전동차가 다니는 그 광역전철 노선이다. 다른 하나는 원주에서 분기하여 영동 고속도로와 유사한 선형을 타고, 기존 태백선보다 더 북쪽인 평창-강릉 방면으로 가는 준고속선이다.

얘는 물론 평창 동계 올림픽 때문에 만들어졌지만 올림픽이 끝난 뒤에도 나름 '동서 고속철' 역할을 잘 감당할 것이다. 영동 고속도로는 이미 2001년에 온통 고가 놓고 터널 뚫어서 지금과 같은 깔끔한 형태로 잘 개통했건만 철도가 지금까지 너무 오랫동안 낙후해 있었다.

원주 서쪽의 중앙선 철도는 이미 진작부터 복선 전철화가 완료됐으며, 선형 개량을 통해 열차의 주행 속도가 경부선 급으로 상향 조정됐다. 서울에서 강원도로 가기 위해 경유해야 하는 곳이 먼 옛날의 영주(영동선!)에서 제천(태백선)을 거쳐 원주로 점점 더 짧아졌다.

미래에, 대략 2020년대 중반쯤엔 경강선은 여주와 원주 구간이 한데 연결될 것이며, 더 나아가 판교 이후의 서쪽으로도 더 확장돼서 광명을 찍고 시흥시 월곶까지 갈 것이다. 중간에 성남, 의왕, 안양 사이 구간은 나름 외곽순환 고속도로와도 비슷한 선형이 된다는 게 아주 흥미롭다. 그래, 거기도 철도가 진작에 필요했다.
이렇게 된 이상 쭉쭉 서쪽으로 가서 제2경인 고속도로처럼 인천 공항까지 가 버려도 되지 않을까 싶다.

경강선은 그렇고.. 종축으로 동해선은 먼저 부산 시내 기장군 정도 구간이 전면적인 선형 리모델링과 동시에 광역전철로 탈바꿈했다. 서울· 수도권 광역전철이 등장한 지 무려 40여 년 만의 일이다.
그리고 과거에 동해남부선이라고 불리면서 포항 북부에서 끊겼던 철길은 계속 북상할 것이다. 그래서 영덕과 울진에 역사상 최초로 철도가 들어오게 된다. 그래서 지금 영동선의 지선으로 취급되고 있는 삼척선과 만나고, 이것까지 동해선으로 흡수되어 동해 역까지 가게 된다.

이렇게 되면 동해-강릉도 전부 동해선으로 흡수되고 영동선은 영주-동백산-동해 구간만 불리게 될 가능성도 있어 보인다.
강릉 이북으로 최북단의 제진 역 구간은 동해중부선이라는 명칭으로 불렸으니, 국도 7호선에 상응하는 길다란 종축 철도는 일관되게 동해선이 되는 게 자연스럽겠다.
이렇듯, 경강선과 동해선 모두 말단에 광역전철 구간이 있으면서 나머지 강원도 방면 구간은 찢어진 채로 공사 중이라는 공통점이 있다.

아, 까먹을 뻔 했는데... 경강선은 여주와 원주로 가는 중에 부발 역 일대에서 중부내륙선이 분기하기도 할 예정이다. 얘는 충주(충북선)와 문경(문경선)을 경유하면서 점촌· 김천(경북선)과 연결된다. 현재의 말 그대로 영남대로 내지 중부내륙 고속도로와 얼추 비슷한 선형이 되는 셈이다.

지금이야 안 그래도 좁은 땅에 이미 건설된 도로와 철도도 많다. 그러니 옛날에 경부선과 경부 고속도로를 처음 만들 때처럼 총력전 치르듯이 일을 추진할 필요는 없고, 이미 있는 구간에서 뭔가 아쉽다 싶은 missing link만 찔끔찔끔 잇는 식으로 길을 만드는 게 추세이다. 고속도로도 갈수록 세 자릿수 번호가 늘어고 있듯이 말이다.

Posted by 사무엘

2018/03/22 08:35 2018/03/22 08:35
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1470

1. 물과 공기의 차이

1기압에 온도가 20도대인 지구의 공기는 사람이 활동하기 쾌적한 환경이다. 기온이 체온에 근접하면 체열을 밖으로 제대로 내보낼 수가 없어서 더위를 느끼게 된다.
하지만 20도대의 물은 수영을 하기에 여전히 꽤 차가운 물이다. 물의 온도는 체온에 근접해야 그럭저럭 미지근하고 물놀이를 할 만하다고 여겨진다.

7~80도대, 심지어 그보다 더 뜨거운 공기는 사우나에서 겪을 수 있으며 잠깐 정도면 버틸 만하다. 그러나 같은 온도의 물은.. 닿는 즉시 화상을 입는 뜨겁고 위험한 물이다. 그렇다면 여기서 온도라는 건 도대체 과학적으로 무엇을 의미하는 걸까?
사실, 인체 내부는 온도의 변화에 의외로 취약하다. 체온이 정상보다 단 몇 도만 더 높거나 낮아지면 사람은 정상적인 활동을 못 하고 앓아눕게 된다.

저온에서는 세포의 물질대사가 정상적으로 이뤄지지 못한다. 사람이 추운 실외에서 이불 같은 거 안 덮고 그냥 자다간 그대로 동사하는 수가 있다.
그리고 반대로 고온도.. 뇌를 구성하는 단백질은 40도 정도만 돼도 생화학적으로 변성되고 맛이 가 버린다고 그런다. 여기에는 내부의 감기 같은 질병 때문에 열이 나는 것, 혹은 외부의 혹독한 무더위 때문에 열이 나는 것(열사병..)이 모두 포함된다.

특히 소아 때 이런 열병을 오랫동안 겪는 건 굉장히 치명적인지라, 병이 나은 뒤에도 머리와 몸에 영구적인 장애가 남기 십상이다. 헬렌 켈러가 대표적인 예이다. 우리나라는 한여름에 어린이집 교사와 운전사의 부주의로 인해 어떤 아이가 한여름 땡볕에 더운 차내에서 몇 시간째 방치되어 혼수 상태에 빠진 사고가 몇 건 나곤 했는데 그 애가 지금은 어찌 됐나 모르겠다.

기왕 말이 나온 김에 더 끔찍한 얘기를 꺼내자면, "왕창 고온인 공기" vs "온도 자체는 그리 높지 않지만 열전달 효율이 넘사벽급으로 높은 유체" 이 둘의 차이가 따지고 보면 화형과 팽형의 차이를 만든다. 새까맣게 탄 시체도 끔찍하지만, 검지만 않을 뿐 뻘겋게 익고 퉁퉁 불어 터진 시체도 끔찍하기는 마찬가지일 것이다.

뜨거운 공기든 뜨거운 물이든, 작열통은 의학적으로 볼 때 신체 절단과 더불어 인간이 느끼는 가장 끔찍하고 괴로운 고통으로 여겨지고 있다. 성경에서도 지옥이 괜히 결코 꺼지지 않는 "불"로 묘사되는 게 아니다. 주토피아에 나오는 것처럼 "ice them"이면 차라리 양반이지, 진짜 본좌는 "burn them"인 것이다.

수은주가 달린 일반 온도계를 펄펄 타는 불에다 던져 넣으면 재질에 따라서 불타거나 녹고 깨지고 파괴될 것이다. 그러나 펄펄 끓는 물에 넣어서 100도대의 온도를 측정하는 것 정도는 문제가 없다. 이걸 생각하면 단백질로 구성된 인체(생체)만이 그런 저고온(?)에 굉장히 취약한 재질이라는 걸 유추할 수 있다. 고온의 공기는 그나마 고온의 여파가 끼치는 '정도'가 덜한 매체이기 때문에 상대적으로 더 오래 버틸 수 있을 뿐이다.

2. 습도

앞에서 물의 온도와 공기의 온도 얘기가 나왔는데.. 사실은 사람이 공기 중에서 더위나 추위를 느끼는 것에는 공기의 온도뿐만 아니라 잘 알다시피 공기에 포함된 습기도 굉장히 큰 기여를 한다. 이런 차이 때문에 날씨가 더워도 굉장히 기분 좋게 더울 때가 있는가 하면, 그렇지 않을 때도 있다.

낮 기온이 30도를 훌쩍 넘겨 40도에 근접한다면, 직사광선은 굉장히 뜨겁고 따가우며 땀 나고 더운 게 맞다. 실내에서는 에어컨을 틀어야 한다.
그래도 기온만 높지 습도가 별로 높지 않다면 상황이 낫다. 밖에서 조금만 바람이 불거나 그늘에 들어가면 금세 시원함이 느껴진다. 그리고 저녁이 되어 해가 지면 언제 그랬냐는 듯이 기온이 굉장히 신속하게 내려간다.

이와 반대로 습도가 높으면.. 낮 기온이 30도를 채 넘지 않고 심지어 해가 안 나더라도 푹푹 찌며 쏟아지는 땀을 감당할 수 없어진다. 땀이 나도 잘 증발하지도 않기 때문에 불쾌지수가 최고로 치닫는다.
이런 날이 꼭 밤에도 기온이 내려가질 않고 열대야가 계속돼서 사람들을 고통에 시달리게 만든다. 에어컨은 온도가 아니라 습도를 낮추는 기능 때문에 더 필요하다.

습도라는 건 공기 중에 존재하는 수증기의 농도를 말한다. 물은 1기압에서 섭씨 100도에서 끓기 시작해서 몽땅 수증기로 바뀌며, 물의 끓는점이나 어는점은 잘 알다시피 공기의 압력에 따라 달라진다(고산 지대에서는 물이 더 낮은 온도에서 끓음). 물이 공기를 녹이고 있는 것만큼이나 반대로 공기도 수분을 함유할 수 있다.

그 뿐만 아니라 100도보다 낮은 온도에서도 공기와 접촉하는 수면에서는 일부가 수증기로 증발하기도 한다.
이건 안개 내지 구름과는 다른 개념이다. 그건 수증기가 아닌 미세한 물 알갱이가 공기 중을 떠다니는 것이다. 이는 마치 순수한 회색과 흑백이 교대로 촘촘하게 늘어선 디더링의 차이와도 비슷하다. 물이 어떻게 이런 두 형태로 모두 존재할 수 있는지 난 완전히 직관적으로 이해를 못 하겠다.

공기가 머금을 수 있는 습기의 한계라는 게 공기의 온도에 따라 달라지기 때문에 습도에는 절대 습도와 상대 습도라는 개념이 따로 존재한다. 물이 온도가 올라갈수록 기체를 녹이고 있기 어려워지는 것과 마찬가지로, 공기 역시 온도가 올라갈수록 습기를 품기 어려워진다. 이 때문에 같은 양의 수분을 머금고 있더라도 온도가 올라가면 상대 습도는 더 올라간다. 겨울이 전반적으로 건조하고 여름이 전반적으로 습한 것도 이런 상대 습도의 차이 때문이다.

3. 진공

그럼 물과 공기가 있는 상황이 아니라, 반대로 물과 공기가 전무하여 진공에 가까운 우주로 나가면 온도라는 게 어떤 영향을 끼칠까? 솔직히 말해 이 역시 난 잘 모르겠고 상상이 잘 안 된다.
공기 제로, 압력 제로, 습도 제로이다 보니 거기는 햇볕을 받느냐 마느냐에 따라 뻑하면 영하 1~200도와 영상 수백 도를 오르내리게 된다. 단지 그 온도의 여파가 지구 표면보다 훨씬 덜하다. 진공인 게 인체에 훨씬 더 해롭지, 온도 자체가 사람을 즉시 태워 죽이거나 얼려 죽이지는 않는다.

하지만, 그럼 우주 공간에서 온도가 영향을 전혀 안 끼치는가 하면 그것도 아니다. 그렇기 때문에 인공위성은 뱅글뱅글 돌면서 몸체가 태양열을 골고루 받게 자세를 잡는다. 이거 조절 잘못해서 배터리가 과열이라도 되면 터지는 사고가 나기 때문이다.
또한, 우주 공간에서 수성이나 금성 같은 내행성으로 날아가는 탐사선은 커다란 양산처럼 생긴 차폐막을 앞에 두르는 형태로 설계되었다.

달에 착륙했던 아폴로 우주선 승무원들 역시 직사광선을 피해서 그늘 또는 저녁 시간대를 선택해서 주로 활동했다.
그러니 이런 정황들을 종합했을 때, 진공에서의 온도라는 게 어떤 의미를 갖는지 갈피를 잘 못 잡겠다. 일단 진공이라면 앞서 언급했듯이 각종 물질의 상태가 바뀌는 온도(끓는점, 어는점??)부터가 우리의 상식을 벗어나는 형태로 바뀌어 버리기도 하니 말이다.

참고로, 사람이 우주에 맨몸으로 있으면 체내의 공기가 유출되고 체액이 끓어오르면서 신체 상태가 잠수병 이상으로 엉망진창이 되며, 수 분 이내로 의식을 잃고 곱게 질식사한다. 그렇다고 해서 눈알이 튀어나오거나 몸이 풍선 터져듯이 터지면서 끔살 당하지는 않는다. 더구나 잘 훈련받으면 수 초 정도 동안 맨몸으로 우주 공간에 노출되고도 살 수 있다.

당연한 얘기이지만 진공은 무중력과는 별개의 조건이다. 진공은 쇠구슬과 깃털이 똑같은 속도로 떨어지는 것이고, 무중력은 둘 다 둥둥 떠다니는 것이다. 물론 둘 다 현실에서는 도저히 상상이 안 되는 상황인 건 마찬가지이다만... 우주 개발 초기에 선진국에서 벌어졌던 여러 테스트· 훈련, 생체 실험-_- 중에는 진공을 버티는 것이 당연히 포함돼 있었다. 원심 가속기나 자유 낙하를 통해 흉내 냈던 무중력과는 별개의 코스이다.

4. 열이 전달되는 원리

온도를 변화시키는 열이 전파되는 메커니즘으로는 "대류, 전도, 복사"라는 세 종류가 있다. 이 개념 자체는 초· 중딩 과학 시간에 진작부터 배운다. 그런데 이게 생각보다 굉장히 심오한 개념이다. '열전달'은 전자기학, 뉴턴 역학, 양자 역학만큼이나 물리학의 엄연한 한 분야이고 공대에서 전공 필수 과목이다.

'대류'(convection)는 유체(주로 기체) 내부에서 온도의 차이가 나는 분자들이 물리적으로 직접 상하로 움직이고 돌면서 열이 골고루 전달되는 것을 말한다. 바닷가에서 바람이 끊임없이 부는 것(땅과 물의 엄청난 비열 차이!), 화재 현장에서 불꽃이라든가 뜨거운 공기가 굳이 위로 솟구치는 것이 모두 대류 현상이다.
그러니 이건 가장 거시적인 규모의 열 전달이다. 분자간의 온도 차이가 아니라 단순 농도· 밀도 차이로 인해 발생하는 '확산'과는 다른 현상이다.

열은 매체가 저렇게 몸소 움직이지 않아도 전해질 수 있다. 펄펄 끓는 냄비의 영향으로 냄비 손잡이라든가 안에 넣어 뒀던 국자까지 뜨거워지는 게 대표적인 예이다. 금속의 분자가 직접 움직일 리는 없으니, 이때는 직접 이동이 아니라 일종의 열역학적 '진동'이라는 미시 현상을 통해서.. 마치 소리가 전해지듯이 열이 전해진다. 이 현상을 '(열)전도'(thermal conduction)라고 한다.

진정 골때리는 건 가장 미시적인 현상인 '복사'(radiation)이다. 열은 전자기파의 형태로 아무 매질· 매체가 없는 곳에서도 퍼져 나가서 전해질 수 있다. 적외선이라고 다들 들어 보셨을 것이다. 애초에 전자기파는 파동 같기도 하고 입자 같기도 한 이상한 물건이니 말이다. 저 복사는 copy나 duplication과는 아무 관계 없고, '내리쬠'이라는 뜻이다.

'복사'라는 게 있기 때문에 아무것도 없는 우주 공간에서도 태양열이 지구로 전해질 수 있다. 음파(소리)는 순수한 진동일 뿐이기 때문에 공기가 없는 곳에서는 퍼져 나갈 수 없고 속도도 훨씬 더 느리지만, 복사열은 위상이 빛과 같다.
전자레인지는 그릇은 별로 데우지 않고 안의 음식만 데우는 것이 무척 기가 막히고 신기한데, 이것만 봐도 열은 대류나 전도 같은 물리적인 접촉이 아니어도 직통으로 전해지는 게 가능함을 알 수 있다. 복사는 도선의 전기 저항으로 인해 발생하는 열하고도 물론 다른 개념이다.

물을 끓이면 왜 물이 가만히 있질 않고 보글보글 사정없이 요동치는지, 무슨 근거와 원동력으로 저러는 걸까? 이건 열전달 내지 물질의 상태 변화와 관련된 여러 미시적인 현상들이 복합적으로 작용한 결과이다.
그리고 지표면에서 물과 공기뿐만 아니라 그 밑으로 땅 속에서도 물질이 끊임없이 순환하고 화산· 지진 같은 지질 현상이 발생하는 원동력도 따지고 보면 맨틀의 대류 같은 열 때문이라고 해도 과언이 아니다.

지구의 자전은 아주 서서히 느려지고 있지만, 지구 내부는 지열 때문이든 자기장 때문이든 활동이 여전히 왕성하고, 옛날보다 오히려 더 활발해지는 듯한 느낌이다. 성경이 말하는 대로 지구 발 밑에 지옥이라는 뜨거운 장소가 있다면, 지구의 내부 구조와 양상은 타 행성과는 근본적으로 다를 수밖에 없을 것이다.

5. 열병합 발전소 -- 열전달과 폐열 재활용의 실제 사례

발전소 중에는 우리에게 친숙한 수력· 화력이나 원자력 말고 '열병합'이라는 놈이 있다. 얘는 사실 화력 발전의 파생 변종이다.
물을 끓이고 증기 터빈을 돌려서 발전기를 가동하려면 열을 만들어 내야 하는데, 이 열이라는 게 열역학 이론적인 한계 내지 기술의 한계로 인해 전부 전력 생산에 쓰이지는 못한다. 거의 과반이 폐열로 버려진다. 딱히 재활용할 길이 없기 때문이다.

8~90도에 달하는 뜨거운 물이 한 트럭이 있다 해도, 그것만으로 아무리 용을 써 봤자 100도 이상으로 실제로 펄펄 끓는 물을 한 컵만치라도 만들어서 증기 기관을 굴릴 수는 없잖은가? 그런 맥락에서 말이다.

전체 열량의 합이야 물론 90도짜리 물 한 트럭이 100도짜리 물 한 컵보다 더 많을 것이다. 그러나 외부에서 에너지를 소비하여 열을 가해 주지 않는 한, 물의 온도 자체를 스스로 높이는 것은 불가능하다. 그건 열역학적으로 자연스러운 방향을 거스르는 일이기 때문이다.

열병합 발전소는 이런 어중간한 열이 담긴 온수를 수집해서 난방용으로 주변 지역에 공급해 준다. 전기만 파는 게 아니라 열도 판다. 전동차에 회생 제동이 있다면 화력 발전소에는 이런 열병합 시설이 있는 셈이다.

그럼 처음부터 모든 화력 발전소에다 열병합 시설을 추가하면 되지 않나 의문이 들 수 있다. 하지만 이 열이라는 건 폐열을 기껏 수집한다고 해도 무슨 태양 복사열처럼 간편하게 전해서 활용 가능한 게 아니다. 열이 담긴 물을 수송할 수 있는 거리에 큰 한계가 있다. 쟤들은 대류, 전도, 복사가 아니라 송유관처럼 '열배관'이라고 극한의 보온 시설이 갖춰진 비싼 특수 수도관에다가 온수를 공급하는 형태로 열을 전한다.

그러니 열병합 발전은 온수를 곧장 중앙 집중 난방용으로 활용할 수 있는 대도시 위주로 소규모 화력 발전+열병합 시설을 갖춘 '지역 난방 공사'의 형태로 운영된다.
2018년 현재 경의선 곡산 역 근처에는 전국에서 유일하게 열병합 발전소의 자체 구비가 아니라 정식 화력 발전소와(한국 동서 발전 소속) 연계하는 대규모 열병합 발전소가 있다. 전국 유일의 인서울 화력 발전소인 당인리 발전소와 비슷한 격의 명물인 듯한데, 얘들도 얼마 못 가 더 외곽으로 이전하지 싶다.

그나저나 원자력 발전소에서도 폐열이 담긴 온수(원자로 냉각용)가 나오긴 한다. 그렇다고 해서 원자로를 소형화해서 대도시 근처의 지역 난방 공사를 운용할 수는 없으니(..; ) 이런 온수는 양식 같은 다른 용도로 활용되는 편이다. 원자력 발전소는 우주 기지와 마찬가지로 육지에서 최대한 떨어진 바닷가에 건설된다는 공통점이 있으니 말이다.

6. 차든지 뜨겁든지

"나는 네가 차든지 뜨겁든지 하기를 원하노라." 이건 성경에서 예수님 말씀의 직접 인용일 정도로 유명한 문구(계 3:15)이다.
물론 성경의 저 문맥에서 제일 좁은 뜻은 양자택일하라고 해서 진짜로 '차가운 극단'으로 나가지 말고 "영이 뜨거운 가운데"(롬 12:11) 주님을 섬기라는 책망 내지 독려이다. "그럴 거면 차라리 나가 죽어!" / "학교 때려치우고 공장이나 가!" 이런 부모나 선생의 막말 꾸중이 진짜로 애더러 공장 가거나 나가 죽으라고 하는 말이 아니듯이 말이다.

하지만 뭐, 좀 더 넓게 비유적으로는 "인간이라면 모름지기 이거 아니면 저거, 모 아니면 도 진영을 확실하게 골라서 화끈하게 살아라, 박쥐 같은 밍숭맹숭 회색분자 기회주의자가 되지 마라"라는 뜻으로 볼 수도 있다.

앞서 비유를 들었던 것처럼.. 폐열만 어중간하게 담긴 미지근한 물은 아예 펄펄 끓을 정도로 뜨거운 물이나, 얼음이 껴 있을 정도로 차가운 물에 비해서 효용이 낮다. 찬물과 더운물을 섞어서 미지근한 물을 만들기는 쉽지만, 미지근한 물이 저절로 찬물+더운물로 분리되지는 않기 때문이다. 열을 가하든 냉각을 시키든 에너지를 써야 한다. 이건 물이 위에서 아래로 흐른다는 것만큼이나 절대적인 사실이다.

그러니 "차든지 뜨겁든지 하길 원한다"란, "정체되거나 뒤쳐지지 말고 늘 전진하길 바란다", "아래로 떨어지지 말고 위를 향해 오르길 바란다" 같은 말을 "열역학적 엔트로피가 감소하는 쪽으로 나아가길 바란다"라는 비유를 동원해서 한 것이라 볼 수 있다.

그 밖에..

  • 학창 시절에 열역학을 더 열심히 공부했으면, 비빔면을 끓여 먹을 때 이 정도 양은 물을 몇 번 헹궜을 때 면을 완전히 식힐 수 있을지를 숫자와 수식으로 모델링할 수 있겠다는 생각이 든다..;;
  • 물에 대해서는 "10리터를 한꺼번에 끓이는 것보다 5리터부터 먼저 데운 뒤 나머지 5리터를 추가로 부으면 10리터 전체를 더 빨리 끓게 할 수 있다.", 심지어 "뜨거운 물이 더 빨리 언다" 같은 믿기 힘든 말도 존재한다. 물에다 날씬한 돌을 잘 던지면 수면에 몇 번 통통 튀는 것도 가능한데, 그것만큼이나 본인은 저런 현상은 어떻게 과학적으로 가능한지 입증할 만한 지식이 부족하다.

  • 한국어는 '덥다'와 '뜨겁다', '춥다'와 '차갑다'의 차이가 존재하는 게 꽤 절묘한 것 같다. '덥다/춥다'는 사람이 느끼는 관점이고, '뜨겁다/차갑다'는 온도를 내는 해당 객체의 관점이다. "난 지금 덥다" / "난 뜨거운 남자다"처럼 말이다.
  • 물리라는 학문은 계속 미시적으로 파고들다 보면 결국은 역시 '파동과 입자'로 귀착된다. 그리고 직선이나 포물선이나 갖고 노는 게 아니라 결국은 삼각함수의 형태로 표현되는 진동을 논하게 된다. 중력의 영향을 받지 않는 듯한 미시세계 입자들의 끊임없는 불규칙한 운동.. 일명 '브라운 운동'은 어떻게 벌어지는지, 걔네들은 무슨 원동력으로 계속 운동하는지, 텔레비전의 백색잡음 같은 움직임도 왜 발생하는지.. 따지고 보면 참 궁금한 게 많다. 이런 것도 열역학과 전혀 무관한 게 아니다.

Posted by 사무엘

2018/03/19 08:36 2018/03/19 08:36
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1469

(2011년에 썼던 글에 내용을 보충하고, 본인의 실제 답사기를 추가했다.)

오늘날 중앙선과 동해선(구 동해남부선)의 환승역인 경주 역은 무려 3· 1 운동 직전인 1918년 가을에 생겼다. 우리나라 최초의 철도인 경인선과는 약 20년의 격차가 있지만 그래도 이것만으로도 역사가 굉장히 길다고 할 수 있다. 철도 덕후라면, 모름지기 1920년대, 30년대, 40년대 이렇게 연도만 입력하면 그 당시에 개통해 있던 철도 노선도가 머리에 쫙 떠올라야 한다.

그때 경주 역은 동해남부선 경주-포항 구간과 함께 개통한 역이었다. 중앙선은 1939년 전구간 개통이니까 그로부터 또 20년 뒤의 일이다. 다만, 동해남부선과 비슷한 시기에 경주-영천 구간도 중앙선이 아니라 경동선이라는 이름으로 경주로 가는 다른 철도가 있긴 했다. 지금 대구선의 전신뻘 된다.

중앙선이 완전 개통하기 얼마 전이던 1935년 12월, 동해남부선은 남쪽 구간이 마저 개통하여 포항-경주-울산-부산이 한데 연결되었다. 일제는 포항 이북으로도 쭉 철도를 놓으려고 하였으나 전쟁으로 인해 이 계획은 무산되었고, 그 상태 그대로 패망하게 되었다. 동해선이 그냥 동해 '남부'선으로 남게 된 게 이 때문이며, 이 철도가 일제가 한반도에 부설하던 마지막 철도였다.

본인이 전에도 글로 썼지만, 일제 강점기가 장기화됐다면 한반도의 교통 인프라도 일본의 그것과 굉장히 비슷해졌을 것이다. 자동차 도로는 좌측통행을 할 것이고 일제 강점기 때 그랬던 것처럼 한반도에도 지역별로 개성 넘치는 여러 사철이 등장했을 것이다. 동해남부선도, 경춘선도 시작은 다 사철이었다.

대륙 진출(진출이라고 적고 침략이라고 읽는다)을 염두에 두고 애초부터 표준궤로 건설되었던 경부선과는 달리, 동해남부선은 무려 762mm 협궤였다. 바로 과거의 수인선과 동일한 협궤였던 것이다. 그러나 조선 총독부가 이를 인수한 후 이내 표준궤로 개궤했다.
이렇게 완성된 경주 역 주변의 동해남부선과 중앙선의 선형은 아래와 같았다.

사용자 삽입 이미지

남서쪽의 충효동 방면을 호남선 목포 방면이라고 보고,
경주 역이 있는 동남쪽 울산 방면을 경부선 대구 방면이라고 보고,
나원 역이 있는 북쪽의 포항 방면을 경부선 서울 방면이라고 보면
이는 대전 역의 위상과 정확히 같다.

이들 철도가 처음 생긴 시절에는 초록색 선이 없었다.
그리고 경부선과 호남선이 만나는 지점도 사정이 정확하게 이와 같았다. 호남선에서 경부선 상행 방면으로 바로 가는 선로가 없었다. (곡물을 일본으로 반출하려면 부산으로만 가면 됐으니..)

그래서 목포에서 부산이 아닌 서울로 가려면 대전 역에서 기관차의 방향을 바꿔야 했으며,
영천에서 울산이 아닌 포항으로 가려면 경주 역에서 기관차의 방향을 바꿔야 했다.

이 때문에 서울을 오가는 호남· 전라선 열차들이 대전에서는 기관차의 방향을 전환하느라 정차 시간이 길었으며 대전 역이 대기 시간 동안 짬을 내어 사먹는 우동으로 유명해지기도 했다.
이런 불편을 덜기 위해 호남선은 1978년 복선화 과정에서 서울 방면으로 직통하는 삼각선이 추가되었다.

한편, 다시 중앙선 얘기로 돌아오면, 중앙선의 건설로 인해 경주에는 시내를 정면 관통하여 경주 역을 잇는 분홍색 선로가 생겼다.
그렇잖아도 중앙선 역시 남쪽에서 올라오는데, 기왕이면 금관총· 대릉원이 있는 좀더 남쪽으로 선로를 만들지 왜 저렇게 부자연스러운 급커브를 만들었는지 모르겠다.
원래 경주 역은 중앙선이 아니라 동해남부선의 선형에 맞춰진 형태라는 걸 쉽게 알 수 있다. 저것도 나름 서울-신촌 사이와 비슷한 급커브라는 생각이 든다.

본인은 초딩 시절에 흥무 초등학교를 다닌 적이 있는데,
빨강-파랑 초기 도색을 한 새마을호가 수시로 드나들던 걸 본 기억이 선하다. 벌써 20년 가까이 전의 이야기이다.
철도는 확실히 지역을 분단시키는 효과가 있긴 했다.

이 선로는 경주 시내를 정면으로, 그것도 여러 건널목을 만들면서(평면 교차) 관통한지라 문제가 많았다. 딱 큰길만 따라간 것도 아니고, 이쪽 일대의 위성 사진 지도를 보면 정확하게 수평· 수직선이 아니라 분홍색 비스듬한 선을 따라 건물들의 배치가 왜곡돼 있는 걸 알 수 있다. 그게 옛 철길의 잔재이다.

사용자 삽입 이미지

그래서 경주 인근에서 (1) 동해선 상행으로 분기의 어려움과 (2) 경주 시내 관통으로 인한 불편을 해소하기 위해 1980~90년대에 일련의 조치가 취해졌다.
먼저, (1) 1985년엔 중앙선과 동해선 상행을 연결하는 초록색 선이 새로 생겼다. 그리고 초록색 선과 분홍색 선이 분기되는 충효 제2터널 지점에 '서경주'라는 이름의 작은 신호소가 생겼다.

그 뒤.. (2) 기존의 분홍색 선로를 철거해 버리고 영천에서 경주 시내에서는 초록색과 보라색 선만으로, 즉 황성동 쪽까지 엄청난 우회를 해서 다니게 선로를 바꿨다. 분홍색 선 대신, 북쪽의 '금장터널'이 있는 곳에 아주 작은 삼각선을 만들어서 중앙선 영천 방면과 동해선 울산 방면을 왕래할 수 있게 한 것이다. 아래 그림에서 before과 after의 차이를 주목하라.

사용자 삽입 이미지

경주는 워낙 문화 유산이 많이 남아 있는 도시이다. 그러니 이런 시내 관통 선로 철거의 배후에는 유네스코의 권고가 있기도 했다고 한다. 철도와는 다른 분야로 최근엔 태릉 선수촌이 지방으로 완전히 이전했는데, 이 역시 인근에 있는 태릉과 강릉을 보존하고 유네스코 세계 유산에 정식 등재하기 위해서 취해진 조치였다. (뭐, 선수촌의 부지 크기와 확장 문제도 작용했지만 말이다.)
시대가 바뀌니 일제가 졸속으로 동해남부선 철길을 막 놓으면서 경주 시내를 땅따먹기 하던 시절과는 정반대 조치가 취해지고 있는 셈이다.

선로가 이렇게 되긴 했지만, 그래도 경주 쪽의 수요가 많기 때문에 포항-대구를 왕래하는 열차들은 어차피 경주는 들렀다 가는 관행이 한동안 계속되었다. 이 때문에 이 노선에는 전진과 후진이 비교적 자유로운 전후 대칭 동차형 열차가 다니곤 했다.

그래서 1992년 11월 1일, 북쪽에 새로 생긴 선로 분기 지점의 근처에 '금장'이라는 이름의 역이 생겼다. 개업 당시에는 일개 듣보잡 신호장에 불과하였으나, 메이저인 경주 역이 위치가 저렇게 어정쩡하던 것에 대한 반사 이익을 제대로 받아서 급성장했다.

인근에 아파트촌과 동국대 경주 캠퍼스가 있어서 위치도 나쁘지 않았다. 그래서 잠시 새마을호가 정차하는 역으로까지 발전했다. 2005년부터는 코레일이 포항 승객의 편의와 열차 속도 향상을 위해 경주 역을 무정차 통과시키고 대신 이 역에 열차를 꾸준히 늘렸다.
참고로 2005년에는 대구선이 외곽으로 이설되면서 금장과 이름도 비슷한 금강 역이 생겼으나, 이 역은 이렇다 할 인기를 얻지 못하고 이내 여객 취급 중지 처분을 받았다. 이와도 아주 대조적이지 않은지?

또한, 금장 역의 개통으로 인해 기존의 서경주 신호소는 존재의 목적을 완전히 상실하고 폐쇄됐다. 건물 자체는 철거되지 않고 경주시 부엉길 9-34라고 도로명 주소까지 할당받아 있지만, 전혀 쓰이지 않고 방치되어 있다.
이에 본인은 이번 설에 고향을 방문한 기념으로 옛 중앙선 선로의 분기 지점 주변을 답사하고 왔다.

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

중앙선의 경주 시내 관통 구선로 중에는 형산강을 건너는 교량이 있었다. 선로가 철거된 뒤에는 교각만 10년 가까이 덩그러니 방치돼 있다가 2002년경에 '장군교'라는 이름의 인도교로 리모델링 됐다. 김 유신 장군 묘가 근처에 있다고 다리 이름도 저렇게 붙었다.

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

과거 중앙선의 흔적은 교량뿐만 아니라 송화산 언덕 아래를 지나는 터널로도 남아 있다. 한때 열차가 다니던 이 터널은 이제 끝이 막힌 채 누군가의 창고로 쓰이고 있었다.

사용자 삽입 이미지

그 반면, 오늘날 새로 만들어서 쓰이고 있는 철길과 터널의 모습은 이렇다.

사용자 삽입 이미지

이 굴다리는 아주 짧고 작지만, 굴다리를 도대체 어떻게 만들었는지 다리 아래에서는 목소리가 마치 마이크를 튼 것처럼 굉장히 잘 울렸다.

사용자 삽입 이미지

근처에 있는 송화산 등산로를 약간 올랐다. 그래서 현재 쓰이는 금장 방면 중앙선 우회 선로(왼쪽)와 장군교(오른쪽)를 모두 볼 수 있는 풍경을 카메라에 담았다. 아마 본인이 지금 서 있는 곳의 아래에 아까 그 폐터널이 지나지 싶다.

송화산은 동국대 경주 캠퍼스를 감싸는 그냥 듣보잡 동네 뒷산처럼 생겼지만, 나름 국립공원이더라. 그래서 서울 북한산에서나 보던 방문객 집계 게이트도 있었다. 이 산은 경치나 문화재 같은 무슨 메리트가 있는지는 모르겠다. 

이렇게 교량과 폐터널을 둘러보고 언덕도 올라 봤는데, 그럼 서경주 신호장은 어디에 있을까?
폐터널도, 언덕도 아닌 다른 민가의 뒤쪽으로 가서 철길 근처에 접근해야 했다.
주변은 시골 마을이다 보니 개를 키우는 집이 많았는데, 이 개들이 온통 시끄럽게 짖어 대면서 외부인인 본인을 반겼다.

무슨 강력 미제 사건을 보면 "범행이 벌어지던 당시에 현장에 있던 사나운 개가 웬일로 짖지 않았다. 개를 미리 어떻게 처리했거나, 아니면 범인은 면식범이다." 이런 식으로 설명된 게 있다. (예: 2008년 대구 초등생 납치 살해 사건) 그게 무슨 의미인지를 몸소 체험할 수 있었다. 어쨌든..

사용자 삽입 이미지

우와..! 잡초로 뒤덮힌 이 폐건물을 보라. 이것이 민가나 다른 건물이 아니라 구 서경주 신호장이다. 창문이나 출입문 같은 건 일부러 다 틀어막은 듯하다. 문자로 된 그 어떤 간판이나 표지판도 존재하지 않는다.

수풀 덤불을 헤치고 현장에 도달하니, 마치 서부 전선 DMZ 안에 내팽겨쳐져 있다는 파주 장단면 사무소 폐건물을 보는 듯한 느낌이었다.
(비슷하게 DMZ 근처에 나뒹굴고 있던 녹슨 증기 기관차 정도야 인양해서 복원 후 임진각에다 전시도 해 놨다. 하지만 건물은.. 철원 노동당사처럼 해당 지역 자체를 수복하지 않은 이상, 딴 데로 옮기기가 곤란할 수밖에 없다.)

사용자 삽입 이미지

사실, 이 건물은 애초에 이 구간을 지나는 열차 안에서 차창 밖으로도 잠깐이나마 어렵지 않게 구경할 수 있다.
하지만 금장 역의 전신이라 할 수 있는 시설을 자가용을 몰고 찾아가서 이런 시간이 정지한 듯한 오지에서 대면하게 되니 참 뿌듯함이 느껴졌다. 옛 서경주 신호소를 아는 사람이라면 우리나라 철도 역사에 대한 내공이 꽤 갖춰진 철덕이라고 불리기에도 손색이 없을 것이다.

※ 추가 설명

1. 신호소와 신호장

신호소와 신호장은 모두 뭔가 철도 정거장처럼 생겼고 철도라는 그래프에서 vertex 역할을 한다. 하지만 승강장이 없고 여객 취급을 하지는 않는다.
내가 아는 바에 따르면 신호장은 단선에서 열차의 교행을 대비한다는 용도가 더 강하고, 신호소는 선로의 분기와 합류를 취급한다는 성격이 더 강하다.

철도 시설의 기술 발달과(특히 CTC화) 자동화로 인해 신호소는 더 만들지 않는 게 추세이지만, 전국에 신호소는 다섯 군데 정도 더 있다고 한다(미전, 북송정, 북영주, 용강, 신대).

2. 경주 역과 인근 역들의 미래 운명

한때는 경주 역에서 합체· 분리를 하는 포항· 울산 행 새마을호 복합 열차가 다녔고 서울-부전 새마을호까지 경주 역을 경유하였으나, 2010년에 KTX 신경주 역이 개통한 뒤엔 다 이제 옛날 추억이 됐다. 지금은 경주에서 서울로 바로 가는 열차는 레어템인 중앙선 열차(밤차 또는 낮의 완행)뿐이다. 그러니 서울로 가려면 동대구 역에서 열차를 갈아타든가 아니면 신경주 역으로 가야 된다.
신경주 역에서 서울로 가는 KTX가 평균 4~50분 간격으로 신경주 역에 정차해 주고 있으니, 이 구닥다리 역에 장거리 열차를 남겨 둘 이유가 전혀 없는 것이다.

포항은 한때는 KTX 비수혜 지역으로 여겨져서 서울-포항 새마을호가 하루 2차례 다니긴 했다(경주 대신 서경주에 정차). 그러나 이 역시 2015년에 동해선 KTX가 개통하면서 폐지됐다.
앞으로 몇 년 안 가 지금 경주 시내의 재래선 철길들은 다 없어질 예정이다. 기존 중앙선과 동해남부선조차도 신경주 중심으로 복선 전철화· 선형 개량을 거듭할 것이기 때문이다.

단, 신경주 뿐만 아니라 현곡 초등학교 근처에도 지금의 서경주와 나원 역을 대체하는 역이 생길 예정이니 이는 반가운 일이다. 다만, 난립하고 있는 여러 역명들의 교통 정리가 필요해 보인다.
그리고 경주 역은 역사적 상징성을 인정받아, 철길이 없어지는 것과는 별개로 건물 자체는 영구 보존하기로 지난 2013년경에 결정되었다. 그럼 이웃의 다른 지역은 상황이 어떨까?

  • 영천: 역시 경주와 비슷한 시기에 생겼고 중앙선과 대구선의 환승역이지만, 고속철의 영향을 받은 것이 전혀 없고 주변의 지역이 바뀌는 것도 없다 보니.. 지금의 위상이 계속 유지된다. 그냥 근처의 동대구 역에 빌붙는 게 더 편하니까.
  • 포항: 원래 있던 역은 보다시피 싹 없어지고 사라졌다.
  • 울산: 고속선과 기존 동해남부선이 서로 너무 멀리 떨어져 있는 덕분에 고속철 울산 역과 기존 태화강 역이 공존하게 된 경우라 하겠다.

Posted by 사무엘

2018/03/16 08:30 2018/03/16 08:30
, , ,
Response
No Trackback , a comment
RSS :
http://moogi.new21.org/tc/rss/response/1468

1. Java 언어가 쓰이는 곳

Java는 C++과 달리 로컬 환경용으로는 그다지 재미를 못 본 언어 및 런타임 환경이다.
콘솔(게임기 말고, 명령 프롬프트..)용 프로그램이 Java로 만들어진 건 거의 못 봤다. 오히려 Java는 한때는 표준 입력으로부터 숫자 한 줄 입력받는 것조차도 온갖 패키지를 import하고 예외 처리 try catch까지 갖춘 뒤에야 가능한 불편한 언어였다.

GUI(프레임워크 이름이 Swing이던가?)로 넘어오면 VirtualBox나 OpenOffice처럼 일부 크로스 플랫폼 프로그램이 GUI 껍데기를 Java로 만든 경우가 좀 있다. (Windows 한정으로는 C#의 Windows Form와 경쟁?) 아, 직장에서 일정 관리 용도로 사용하는 ProjectLibre도 흔치 않은 Java 기반 로컬 GUI 프로그램이다.
하지만 Java가 제공하는 GUI는 외형이 네이티브 GUI에 비해 이질적이고 느리고 런타임 오버헤드가 커서 범용성 말고는 가성비가 좋지 않았다.

한편, Java는 애플릿(Applet)이라는 이름으로 웹(클라이언트)에서 돌아간 적도 있다. 웹에서 꽤 고차원적인 수학 그래픽, 화면 왜곡과 전환 애니메이션, 시뮬레이션(특히 교육용) 등을 출력할 목적으로 플래시와 경쟁하는 구도로 한때 쓰였으나.. 그것도 다 지나간 일이다. 현재는 망했으며, 개발사로부터 지원도 끊긴 지 오래다.

오늘날 Java는 로컬 PC가 아닌 안드로이드 앱, 그리고 jsp 기반 서버사이드 언어(웹 서버)로 회생해 있다.
뭐, Java 런타임을 컴퓨터에 설치해 보면 설치 중에 '전세계 수십억 개의 기기가 Java를 기반으로 돌아가고 있습니다'라고 자랑하는 문구가 뜨기도 하는데, 그것도 임베디드보다는 모바일을 말하는 게 아닌가 싶다.

Java는 오늘날 그렇게 됐고 로컬 PC 환경에서는 지금도 수많은 프로그래밍 언어들이 난립해 있지만, 웹에서는 그야말로 Java에서 이름만 빌려온 JavaScript로 대동단결 천하통일이 이뤄진 게 신기하기 그지없다.

2. 새로운 언어

애플에서 Objective-C에 한계를 느끼고 Swift라는 언어를 만들었더니,
그에 질세라 안드로이드 진영에서도 Java 대신 '코틀린'이라는 완전히 다른 언어를 만들었다. 왕년에 C++ 컴파일러를 열심히 만들었던 사람이 D 언어를 개발했듯, Java 기반 안드로이드 개발 환경을 열나게 만들었던 JetBrains라는 회사에서 전용 언어도 만들고 이를 Google에서도 채택한 것이다.

오늘날 베이직은 마소에서밖에 만들지 않는 언어가 됐고, 파스칼은 그냥 델파이의 전유물이 됐고 이제는 델파이라는 이름 자체가 RAD 툴 겸 프로그래밍 언어의 이름으로 등극했다. 언어가 워낙 많이 마개조됐기 때문이다. 또한 옵씨의 경우는 애초부터 애플이 언어에 대한 일체의 권리를 언어 고안자로부터 사 버려서 사유화했다.
저런 언어들에 비해, C/C++은 너무 심하게 파편화가 됐을지언정--언어 문법 자체보다는 라이브러리나 ABI 계층에서--, 특정 기업에 의해 좌지우지된다는 느낌은 상대적으로 덜 든다.

Visual Studio 등 요즘 IDE들은 프로젝트 전체에 존재하는 클래스들을 쭉 보여주는 기능이야 당연히 기본으로 갖추고 있다. 그런데 클래스들을 namespace 계층 + 이름의 알파벳 순으로만 보여주는 게 아니라, 기반 클래스별로 분류해서 보여주는 기능이 있으면 프로젝트들의 성격을 파악하는 데 더 도움이 될 것 같다. 그러면 그 클래스들의 성격을 파악할 수 있으니 말이다.

3. 간단한 자료형과 복잡한 자료형 or 클래스 객체

오늘날 객체지향을 표방한다는 많은 고급 언어들이 int 같은 (1) primitive type과 (2) 클래스 객체, 혹은 (1) 스칼라와 (2) 복합 자료형(리스트 같은)을 서로 구분해서 다루고 서로 다르게 취급한다. 함수 인자와 리턴값을 주고받을 때 (1)은 값을 그대로 전달하고 (2)는 메모리에 한 인스턴스만 둔 뒤 주소값만 주고받고 레퍼런스 카운팅을 하는 것이 대표적인 차이점 되겠다. Java, 파이썬 등 여러 언어들이 이런 정책을 사용한다.

순수 극한 객체지향 언어 중에는 1과 2의 구분조차 없애고 모든 것을 객체로 취급하여 타입 식별자라든가 기반 클래스 소속을 부여하는 물건도 있다. 하지만 그렇게까지 하기에는 속도와 메모리 오버헤드가 너무 크고 실용성도 떨어지니 많은 언어들이 최소한의 primitive type 정도는 허용하는 타협을 한다.

C++이야 어느 타입이건 어느 방식으로 전달할지(값, 주소/참조)를 몽땅 명시적으로 수동 지정이 가능하다. 그러나 포인터를 노출하지 않는 언어에서는 그런 구분이 프로그래머의 지정 없이 자동으로 행해진다. 포인터가 없는 언어라고 해서 포인터가 원래 하던 일 자체를 안 하는 것은 전혀 아니니까 말이다.

그런데 1과 2의 얼추 중간쯤에 속하는 물건은 문자열, 그리고 단순 구조체 정도다.
문자열이야 언어 차원에서 상수 리터럴도 존재하고 정말 기본 자료형과 복합 자료형의 중간에 속하는 독특한 물건인지라, 각 언어와 라이브러리마다 구현 형태가 제각각이다.

그리고 생성자, 소멸자, 상속, 가상 함수 따위 전혀 없고 레알 int 같은 primitive type의 묶음으로만 이뤄진 레알 C 스타일 구조체를 프로그래밍 용어로는 POD(plain old data)라고 한다. C++의 경우 POD 구조체만이 중괄호 { }를 이용한 멤버 별 값 초기화가 가능하다.
물론 POD도 개당 수십~수백 바이트를 차지하는 덩치 큰 놈이 될 수도 있지만 이는 논외로 하고..

단순 구조체를 클래스와 구분시킨 것은 C#의 신의 한수로 보인다. 사실, POINT, SIZE, COMPLEX(복소수)처럼 숫자 둘로만 달랑 이뤄진 간단한 구조체는 상속이 뭐고 없고 함수 인자도 그냥 값 형태로 전달하고 싶은 그런 물건들이니 말이다.

4. sizeof 연산자가 없음

개인적으로 Java를 쓰면서 C++에 비해 굉장히 특이하다고 오래 전부터 생각했던 점은..
가장 먼저, (1) int를 함수에다 주소/참조로 전달을 할 수 없어서 swap 함수를 구현할 수 없다는 것이다. int는 무조건 값으로만 전달되니 말이다.

(2) 클래스에 소멸자라는 게 없다 보니, 메모리는 GC 덕분에 뒷일 생각할 필요 없이 new만 늘어놔도 되는데 파일은 닫는 코드를 수동으로 매번 써 줘야 하는 게 처음엔 아주 웃기게 느껴졌었다.

(3) 그리고 끝으로.. sizeof 연산자가 없는 것에서 한번 더 경악했다. 수동 메모리 할당이 없고 포인터도 없고, sizeof마저 없다는 건 어떤 개체의 메모리 내부 덤프 같은 건 꿈에도 생각하지 말라는 얘기다.
primitive type이야 개별 크기가 어떤 기기의 JVM에서도 불변 동일하니(가령, int 4바이트, long 8바이트..) 굳이 sizeof에다 요청하지 않아도 되고..

하긴, 특정 개체의 메모리 주소 같은 걸 어디에다 낼름 누설해 줄수록 프로그램의 엔트로피가 커지고 Garbage collector가 추적해야 하는 영역이 넓어지고, 메모리 누수와 잘못된 포인터 에러가 날 확률이 높아지니.. 그런 건 최대한 애초에 할 필요가 없게 만들어야 할 것이다.
하지만 바이너리 덤프를 바로 못 하면 파일을 읽고 쓰는 작업도 좀 불편할 것 같다. 하다못해 과거에 베이직조차도 포인터는 없어도 숫자의 binary dump를 문자열 형태로 얻거나 그걸 디코딩하는 함수는 있었거늘 말이다.

C#은 Java처럼 가상 머신 기반에 자동 메모리 관리가 제공되는 언어이지만 이런 것들이 Java보다는 융통성이 있다. ref라는 키워드가 있어서 primitive type도 call by reference가 가능하다.
그리고 sizeof 연산자가 제한적으로 지원된다. primitive type과 단순 구조체 한정으로만 사용 가능하며, 자동 관리를 받는 자기 객체(managed class)에 대해서는 sizeof를 여전히 할 수 없다.

C#의 초창기 구버전에서는 아예 unsafe 코드 안에서만 sizeof를 사용할 수 있었지만 나중에 제약이 완화되었다.
그래도 C#과 Java 어느 경우든, sizeof라는 건 저수준 메모리 접근과 관계가 있는 기능이지, 가상 머신 고수준 코드와는 어울리지 않는 건 공통인 듯하다.

5. 객체의 배열

그러고 보니 Java는 클래스 객체들이 다 기본적으로 포인터 단위로 취급된다는 특성상, 객체의 동적 배열을 만드는 것도 좀 불편하다.

MyObject[] arr = new MyObject[n];

이건 MyObject들을 담을 배열 객체부터 하나 만드는 것이다. 그 뒤에 arr[0]부터 arr[n-1]에다가 MyObject의 인스턴스를 new로 할당하는 건 사용자가 또 직접 해야 한다.
C++이야 Java처럼 행동하고 싶으면 MyObject **arr을 하면 되고, 아니면 MyObject가 default 생성자만 있다면 곧장 MyObject *arr = MyObject[n]으로 객체의 배열을 만들 수 있으니 융통성이 있다.

그리고 한 객체가 여러 클래스들을 돌아다니면서 쓰이다 보면, 이 오브젝트가 값만 일치하는 게 아니라 본질적으로 그 오브젝트가 맞는지.. C++로 치면 주소값이 같은지, Java조차도 문자열은 equals 대신 ==를 써서 행하는 그 비교 대상값이 일치하는지.. 주소를 직접 확인하고 싶은 경우가 있다.

C/C++은 & 연산자가 있으니 일도 아닌 반면, Java는 디버거 창이 아닌 로그 확인을 위해서는 toString을 일일이 해 줘야 하는 것이 불편한 점으로 남는다.
포인터라는 게 위험하다고 직접 노출을 금기시하다 보니, 포인터로 직통으로 할 수 있는 일까지 좀 불편하게 바뀌는 건 어쩔 수 없나 보다.

6. 클로저

C++은 수동 메모리 관리뿐만 아니라 pointer-to-member(더 나아가 함수 포인터라는 것 자체까지)라는 것도 오늘날의 타 언어들이 제공하는 깔끔한 클로저와는 전혀 관계 없는 원시적이고 지저분한 물건이다. C/C++은(정확히는 C가) machine word를 정말 좋아하는 언어이며, '포인터' 하나만으로 간편하게 구현 가능하지 않은 요소는 쿨하게 제공 안 하는 게 전통적인 설계 철학이었다.

그러니 함수 안에 함수를 만드는 걸 지원하지 않으며(당대의 경쟁 언어이던 파스칼과 달리), C++도 클래스 안에 클래스, 함수 안에 지역 클래스 같은 건 전적으로 접근성 scope 구분만 할 뿐, 자기 밖에 있는 클래스 멤버나 함수 지역변수에 자동으로 접근하는 메커니즘 같은 건 제공하지 않는다. 즉, Java 용어로 말하자면 static class만 지원한다는 것이다.

C++만 쓰다가 Java나 Objective-C 같은 타 언어에서 this 내지 상부 클래스 멤버에 접근 가능한 함수를 스레드 콜백 등으로 자유자재로 넘겨줄 수 있는 걸 보니 아주 신기했다. 요즘은 함수형 언어 영향을 받아서 함수 몸체를 이름조차 안 붙이고 바로 넘겨줘도 되니 더 편하다.

이것도 타 언어들이 더 객체지향 철학에 따른 것이고, C++이 기괴하고 경직된 구조의 언어인 셈이다. 물론, C++의 사고방식은 저런 유연한(?) 함수 포인터(C++ 용어), 셀렉터(옵C 용어), 클로저(???)를 구현하기 위해 내부적으로 부과되는 시공간 성능 오버헤드가 무엇인지 생각하는 데는 도움이 될 듯하다.

C++은 저게 없는 대신에 P2M을 갖고 있는 셈인데.. C++의 또 다른 괴물 기능인 다중 상속과 연계하려다 보니 P2M도 도저히 '날포인터' 하나만으로 간편하게 구현할 수가 없어졌으니 참 아이러니이다. 이건 언어 설계 차원에서의 결함이나 마찬가지라고 봐도 할 말 없을 정도이며, 이와 관련하여 본인이 몇 년 전에 글을 쓴 적이 있다.

그나저나 Java의 상속 "extends A"는 C++로 치면 ": public A", 다시 말해 언제나 public 상속과 같은 것이겠지?
난 부모 멤버들에 대한 접근에 더 많은 제약을 가하는 private, protected 상속은 사용해 본 적이 없다.

7. 자동 메모리 관리, 동적 배열 등, 나머지 생각들

뭐, 직장에서 Java 개발도 좀 해 보니 깔끔한 1파일 1클래스 구조에다 헤더와 소스 구분이 없고 코드 파싱과 빌드가 정말 광속인 것(C++ 외의 타 언어들이 대부분 그렇지만), throw IllegalArgumentException("value must be >=0") 예외 처리가 사실상 assertion failure이나 마찬가지이니 assert(0 && "error") 이런 테크닉이 없어도 되는 것들은.. 마음에 든다.

ABI 계층이 파편화 없이 딱 잘 통합돼 있고, 무식한 헤더 파일 대신 패키지로부터 추출· 복원된 프레임워크 소스와 코딩 컨벤션.. 매번 다시 컴파일 되면서 특정 타입과 완전히 결합해 버리는 C++의 템플릿 대신에, 진짜로 유연한 void*를 캡슐화했다고 볼 수 있는 제네릭.. 이런 건 부럽기도 하고 현대의 프로그래밍 언어와 프로그래밍 환경이 얼마나 발전했는지를 뒷북으로나마 느낀다.

프로그래밍에서 "네이티브 코드 + 메모리 100% 수동 관리"(C/C++) 아니면 "가상 머신 + garbage collector 기반의 메모리 자동 관리"(C#/Java)의 차이는, 자동차 운전으로 치면 수동 vs 자동 변속기와도 비슷할 정도로 큰 차이인 것 같다. 자동차에서는 면허 조건이 달라질 정도로 큰 차이를 만들며, 프로그래밍에서도 뒷일 생각 안 하고 마음대로 new를 남발해도 되냐 그렇지 않느냐의 차이는 매우 크다.

(그럼 메모리가 자동 관리되는 언어에서는... 무슨 서버나 GUI 프로그램이 아니고 일체의 아이들 타임이 없이, 자기 할 일만 일괄 처리하고 끝나는 콘솔(명령 프롬프트)+단일 스레드 프로그램이라면 GC가 언제 어떻게 개입하여 동작하는지 의문이 든다. 뭐 그냥 메모리 할당 부분에서.. 자원 반납 없이 지금까지 메모리를 사용한 양이 도를 넘어선다 싶으면 그때 동작할 수도 있긴 하겠다.)

C++을 까는 사람들의 심정은 이해한다. 하지만 C++이 그렇게 지저분하고 자비심이 없는 대신에 어떤 넘사벽급의 강력한 네이티브 코드를 생산할 수 있는지, 그리고 Java가 편한 대신에 내부적으로 성능을 얼마나 많이 희생했고 런타임 오버헤드가 더해졌는지를 전부 논하지 않고 단편적인 비교만으로 언어 호불호를 논하는 것은 바람직한 태도가 아니라고 여겨진다. 둘 다 장단점이 있고 고유한 주 용도가 있는 법이다.

옛날에 잠깐 써 봤던 파스칼은 포인터도 있고 자유로운 call by reference도 지원했지만 그 포인터가 C처럼 막강하고 배열과 연계되는 건 절대 아니며, 동적 배열을 못 만들었다. 아무리 파스칼이 교육용 언어를 표방하고 만들어졌다 해도 베이직으로도 가능한 기능이 없는 건 말이 안 되니 저건 후대의 파생 언어에서 개선되었지 싶다.

Posted by 사무엘

2018/03/13 08:34 2018/03/13 08:34
, ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1467

Windows API에서 BitBlt는 DC간에 비트맵 블록을 찍어 주는 아주 중요한 함수이다.
장치 독립 비트맵인 DIB라는 게 컬러 비트맵의 원활한 처리를 위해서 Windows 3.0에서 처음 도입되었지, DDB와 관련된 CreateBitmap, BitBlt 같은 함수, 그리고 컬러 brush 자체는 Windows의 초창기부터 있었다.
단순히 memcpy나 memmove 같은 함수와는 달리, BitBlt는 1차원이 아니라 2차원 평면을 표현하는 메모리 영역을 취급하는 관계로 동작 방식이 더 복잡하다.

BitBlt의 처리 대상인 두 DC는 내부 픽셀 포맷 같은 게 당연히 서로 호환이 돼야 한다.
원시적인 모노크롬 비트맵의 경우, 위치와 크기에 해당하는 좌표의 x축이 바이트 경계(8의 배수)로 딱 떨어지지 않을 때의 복잡한 보정이 필요하다.
그리고 원본과 타겟 DC가 동일한 경우, memmove 같은 overlap 처리도 x축과 y축 모두 고려하여 memmove보다 더 복잡한 상황 가짓수를 처리해야 한다.

BitBlt는 비트맵을 그냥 찍는 게 아니라 원본(S), 타겟(D)에 대해서 비트 단위 연산을 시킨 결과를 집어넣도록 아주 범용적으로 설계돼 있다. 일명 raster operation이다.
게다가 래스터 연산의 피연산자가 저 둘만 있는 게 아니라 타겟 DC에 지정되어 있는 브러시 패턴(P)까지.. 무려 세 개나 존재한다. 그래서 BitBlt는 PatBlt라든가 InvertRect 함수가 하는 일을 다 할 수 있을 정도로 범용적이다.

원본 S를 있는 그대로 복사해 넣는 건 SRCCOPY이고, 그냥 타겟 비트맵을 반전만 시키는 건 ~D이다.
그리고 마스크 비트맵(흰 배경에 검은 실루엣) M과 그림 비트맵(검은 배경에 실제 그림) S에 대해서 D&M|S (각각 and, or 연산)를 해 주면 보다시피 직사각형 모양이 아닌 스프라이트를 찍을 수도 있다. 래스터 연산을 갖고 할 수 있는 일이 이렇게 다양하다.

BitBlt가 사용하는 래스터 연산은 3비트짜리 정보(S, D, P)에 대해서 임의의 1비트(0 또는 1) 값을 되돌리는 함수라고 볼 수 있다. 이 함수가 받을 수 있는 인자의 종류는 8가지(2^3)이고.. 서로 다른 래스터 연산 함수는 2^(2^3)인 총 256가지가 존재할 수 있다.
그리고 SRCCOPY, SRCPAINT 같은 것들은 그렇게 존재 가능한 래스터 연산 함수를 나타내는 값이다. 각 변수별로 S는 11110000, D는 11001100, P는 10101010 이런 식으로 정해 놓으면 00000000부터 11111111까지가 S|D, D&~P 등 각 변수들을 조작한 모든 가짓수를 나타내게 된다.

그런데 컴퓨터에서 범용성과 성능은 대체로 동전의 양면과도 같아서 하나를 살리다 보면 다른 하나를 희생해야 하는 관계이다.
the old new thing 블로그의 설명에 따르면.. 과거 16비트 시절에 BitBlt는 사용자의 요청을 파악해서 좌표 보정 같은 전처리 준비 작업을 한 뒤, 실제로 for문을 돌면서 점을 찍는 부분은 내부 템플릿으로부터 기계어 코드를 실시간으로 생성해서 돌렸다고 한다. 이게 무슨 말인가 하면.. 단순히

void Loop(int l, int t, int r, int b);
Loop(r.left, r.top, r.right, r.bottom);

수준이 아니라

template<int LEFT, int TOP, int RIGHT, int BOTTOM>
void Loop()
{
    for(int j=TOP; j<BOTTOM; j++)
        for(int i=LEFT; i<RIGHT; i++)
            어쩌구저쩌구;
}

Loop<r.left, r.top, r.right, r.bottom>();

이런 걸 추구했다는 뜻이다.
비트맵을 찍을 때 범위 체크는 매 픽셀마다 그야말로 엄청나게 자주 행해지는 일이다. 그러므로 그 한계값을 컴퓨터의 입장에서 변수가 아닌 상수로 바꿔 버리면 레지스터도 아끼고 성능 향상에 도움이 될 수 있다.

16비트 Windows에는 32비트 OS 같은 가상 메모리 관리자라는 게 없으며, Java/.NET 같은 가상 머신과 garbage collector도 없었다. 그 대신 (1) 메모리의 단편화를 방지하기 위해 moveable한 메모리 블록들의 주소를 수동으로 한데 옮기고 (2) discardable한 메모리 블록을 해제하는 동작이 있었다.

가상 머신이 없으니 just-in-time 컴파일이라는 개념도 있을 리 없다. 하지만 BitBlt의 저런 동작은 Java 내지 JavaScript의 JIT 같아 보이기도 한다. 물론 진짜 JIT 기술보다는 코드 생성 패턴이 훨씬 더 정향화돼 있고 단순하지만 말이다. (뭐, BitBlt의 세부 알고리즘 자체가 단순하다는 뜻은 아님)
그리고 그 시절엔 DEP도 없었다. 메모리에 데이터가 담겼건 실행 가능한 코드가 담겼건, 아무런 차별이 없었다.

게다가.. 저 때는 그래픽 출력과 관련된 하드웨어 지원조차도 없었다. 1990년대 일반 VGA 화면에서는 화면이 갱신될 때 마우스 포인터의 잔상이 남지 않게 하는 처리조차도 소프트웨어적으로 해야 했다. IBM 호환 PC는 전통적으로 게임기용 CPU에 비해서 멀티미디어 친화적이지 않은 컴퓨터로 정평이 나 있었으며, 그나마 좀 미려한 그래픽 애니메이션을 보려면 한 프로그램이 하드웨어 자원을 독점하는 도스밖에 답이 없었다. 그러니 BitBlt 같은 함수는 CPU 클럭을 하나라도 줄이려면 정말 저런 눈물겨운 최적화라도 해야 했던 것이다.

이런 여러 이유로 인해 16비트 Windows 시절에는 지금의 32/64비트보다 어셈블리어라든가 실시간 코드 생성 테크닉이 확실히 더 즐겨 쓰였던 것 같다.
외부에서 호출 가능한 콜백 함수를 지정하기 위해 껍데기 썽킹 함수를 생성해 주는 MakeProcInstance (해제하는 건 FreeProcInstance)부터가 그 예이며..

또 그때는 API 훅킹도 대놓고 훅킹 대상 함수 메모리 주소에다가 내 함수로 건너뛰는 인스트럭션을 덮어쓰는 식으로 행해졌다. 지금이야 이식성 빵점에 가상 메모리와 프로세스 별 메모리 보호, 멀티스레드 등 여러 이유 때문에 위험성이 크고 사용이 강력히 비추되는 테크닉으로 봉인됐지만 말이다.

Windows 95는 비록 32비트 명령어와 32비트 메모리 주소 공간을 사용하지만 GDI 계층은 여전히 16비트 코드를 쓰고 있으니 내부적으로 과거의 테크닉이 그대로 쓰였다. 그 당시의 PC 환경에서는 최고의 성능을 발휘했겠지만, 그리기 코드 자체의 32비트화, 좌표계의 32비트 확장이라든가 멀티스레드 대비 같은 건 전혀 불가능한 레거시로 전락할 수 밖에 없다.

그에 반해 Windows NT의 BitBlt는.. 이식성이 전혀 없는 기계어 코드 실시간 생성 같은 테크닉이 쓰였을 리가 만무하며, 어느 플랫폼을 대상으로나 동일하게 적용 가능한 C 코드로만 구현되었을 것이다. 겉으로 하는 동작은 비슷해 보여도 내부 구현은 완전히 달랐으며, 같은 사양의 PC에서 속도가 더 느린 것은 어쩔 수 없었다. 그 대신 NT의 코드는 플랫폼과 시대를 뛰어넘어 살아 남을 수 있었다.

뭐, 1990년대에는 OS/2도 얼리어답터들이 관심을 갖던 레알 32비트 운영체제이긴 했는데.. 얘는 Windows NT와 달리 32비트 계층도 코드가 전반적으로 그리 portable하지 않았다고 한다. 그러니 타 CPU로 포팅은 고사하고 훗날 같은 CPU에서 64비트에 대처하는 것도 유연하게 되기 어려웠으리라 여겨진다.

그에 반해, OS가 아니라 게임이긴 하다만 Doom은 Windows NT와 비슷하게(약간만 타이밍이 더 늦은) 1993년 말에 첫 출시됐는데.. 세계를 놀라게 한 3차원 그래픽을 실현했음에도 불구하고 어셈블리어를 거의 사용하지 않고 순수하게 C 코딩만 한 거라는 제작사의 증언에 업계가 더욱 충격에 빠졌다. 사운드처럼 상업용 라이브러리를 사용한 부분의 내부 구현을 제외한 전체 소스 코드가 수 년 뒤에 공개되면서 이 말이 사실이었음이 입증되었다.

도스에서 Doom을 가능케 한 것은 쑤제 어셈블리어 튜닝이 아니라 Watcom 같은 최적화 잘 해 주는 32비트 전용 C 컴파일러였다.
엔진 코드가 C로 나름 이식성 있게 깔끔하게 작성된 덕분에 Doom은 소스가 공개되자마자 오픈소스 진영의 덕후들에 의해 온갖 플랫폼으로 이식되면서 변종 엔진과 게임 MOD들이 파생돼 나올 수 있었다. 물론 소스 공개 이전에도 상업용으로 갖가지 플랫폼에 출시되기도 했고 말이다.

오늘날이야 컴퓨터 아키텍처라는 게 2, 30년 전 같은 춘추전국시대가 아니며, 가상 머신이라든가 웹 같은 환경도 발달해 있다. 그러니 "C언어는 이식성이 뛰어나다" 이런 식의 진술이 뭐 거짓말은 아니지만 약간 어폐가 있다. 하지만 BitBlt API부터 시작해서 이식성 있는 코드와 그렇지 않은 코드가 궁극적으로 어떤 상태가 되었는지를 생각해 보니 이 또한 의미 있는 일인 것 같다.

다시 Windows API 얘기로 돌아와서 글을 맺자면..

  • BitBlt는 비트맵 출력 API 중에서는 그나마 가장 기본적인 형태이다. StretchBlt는 비트맵을 크기를 변형(확대· 축소)해서 찍을 수 있다. 이들의 DIB 버전에 대응하는 것은 각각 SetDIBitsToDevice와 StretchDIBits이다.
  • TransparentBlt와 AlphaBlend는 아까 같은 AND/OR 래스터 연산 대신 color key 내지 알파 채널을 적용해서 투명색이 적용된 비트맵을 찍어 주는 함수이다. Windows 98/2000에서 새로 추가됐다. 본인은 사용해 본 적이 없다.
  • PatBlt는 원본 DC의 지정이 없이 브러시 패턴과 타겟 DC와의 래스터 연산만이 가능한 마이너 버전이다.
  • PlgBlt와 MaskBlt는 마스크 비트맵까지 한꺼번에 받아서 스프라이트 처리가 가능한 버전이다. 거기에다 PlgBlt는 일차변환을 적용해서 직사각형이 아닌 임의의 평행사변형 모양으로 비트맵을 찍을 수도 있는데.. Windows 9x에서는 지원되지 않고 NT에서만 존재해서 그런지 본인 역시 이런 함수가 있다는 걸 아주 최근에야 알게 됐다.

실무에서는 이렇게 비트맵을 한꺼번에 찍어 주는 함수를 쓰지, SetPixel이라든가 무식한 FloodFill 같은 기능은 그래픽 출력에서 쓸 일이 거의 없는 것 같다.
BitBlt과 유사 계열의 비트맵 출력 GDI 함수들은 비트 연산을 다루는 시대 배경에서 만들어진 만큼, 요즘 PNG 이미지처럼 비트맵 내부에 들어있는 알파 채널을 제대로 취급하지 못한다. 그리고 비트맵을 확대해서 출력할 때의 안티앨리어싱도 부드럽게 처리를 못 한다. 레거시 코드에다가 그런 기능까지 플래그로 넣기에는 너무 복잡하고 지저분해져서 그렇지 싶다. 그도 그럴 것이 GDI는 하드웨어 통합적으로 얼마나 추상적으로 설계되었던가?

현대의 화면 래스터 그래픽에서 필요로 하는 최신 기능들은 한때 GDI+가 따로 담당하다가 요즘은 그것도 너무 느리다고 도태됐고 Direct2D 같은 다른 패러다임으로 옮겨 갔다.

Posted by 사무엘

2018/03/10 08:37 2018/03/10 08:37
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1466

1.
10여 년 전, 본인이 MMORPG 게임을 개발하던 회사에서 병특 복무를 하던 시절에는 주된 업무 주문이 기존 제품을 WOW와 더 비슷하게 고치는 것이었다. 그 당시에 WOW가 인기가 장난이 아니었기 때문에..
지금은 회사에서 어쩌다 보니 안드로이드 앱을 개발하게 됐는데, 주된 작업이 기존 제품을 다른 유명 SNS 앱과 더 비슷하게 고치는 것이다.

세상은 유행이 변하고 이런 식으로 돌고 도는 것 같다. 20년쯤 전에 Codeguru 같은 사이트에서 MFC CWnd 클래스를 상속받아서 온갖 기발한 UI 컨트롤 내지 MS Office 스타일의 도구모음줄을 만들어 올리고 테크닉을 공유하는 게 유행이었다면.. 지금은 안드로이드에서 더 현란하고 기가 막힌 화면 전환, GUI 컨트롤을 만드는 게 유행의 뒤를 물려받아 있다. 또한 소스포지라든가 다른 사이트들은 다 망하고 개발자 관련해서는 오로지 스택 오버플로우와 github만이 본좌이다..

요즘은 소프트웨어로 엔드 유저로부터 수익을 내는 방식이 "특정 기술과 기능, 컨텐츠 자체에 대한 접근성" 자체는 아니어 보인다. 기술과 기능, 컨텐츠는 이제 너무 넘쳐나고 저렴해진지라, 정말 획기적이고 새로운 게 아니면 그닥 변별이 안 되는 것 같다. id 소프트웨어가 1990년대 옛날처럼 없던 그래픽 기술을 새로 개발하고 선보이면서 주목받지는 않고 있는 것을 생각하면 된다.
그 대신 요즘은 다들 모바일에서 다들 내 자신의 정체성을 표현하는 데 드는 대가로 수익을 내는가 보다. 이모티콘, 아바타 같은 것 말이다.

2.
카카오톡에서 그냥 있으니까 정말 아무 생각 없이 기본 이모티콘들을 지금까지 써 왔다.
라이언인지 뭔지 알 게 뭐야? 난 그냥 개그만화 보기 좋은 날의 '쿠마키치'=_=;; 짝퉁이라고 생각해 왔는데.. 알고 보니 사자였구나. 갈기 없는 숫사자랜다~ ㅋㅋ

카카오프렌즈 이게 사이버 공간을 떠나서 봉제인형과 각종 캐릭터로 로얄티 받으면서 그야말로 돈을 빗자루로 쓸어담고 있다고 한다. 우리나라는 둘리· 호돌이처럼 옛날에 히트 쳤던 걸 제외하면 캐릭터 지지리도 못 만드는 나라인 걸로 본인은 알고 있는데..-_-;; 어째 이걸로도 돈줄을 성공적으로 텄구나.

영화나 만화처럼 뭔가 스토리가 있는 매체에서 유래된 캐릭터가 아니고,
앵그리버드의 동그랗고 눈썹 짙은 빨간 새처럼 인기 게임에서 유래된 것도 아니고..
그저 채팅 앱의 이모티콘에서 유래된 캐릭터가 이렇게 초대박을 치리라고는 난 절대로 예상할 수 없었다.

한낱 아스키 아트에 불과하던 이모티콘이란 게 그 다음으로는 찐빵 얼굴에 기하학적이고 추상적인 기호, 픽토그램 수준이었다가 이제는 거의 움짤 수준으로 변모하고 있다.
그리고 이게 말을 직접 하기 난감할 때, 얼굴 표정과 므흣한 감정을 대신 전달하는 용도로 생각보다 유용하기도 하다.

그리고 유튜브 동영상의 썸네일 이미지도 있다. 그냥 동영상의 임의 구간 스틸 영상을 썸네일로 지정하는 게 너무 당연하다고 생각해 왔고 문제 의식을 하나도 느끼지 않고 지냈는데.. 이것도 글자와 그림을 넣어서 귀신 같이 꾸며 주는 앱이 있다...;;

난 그냥 날개셋이나 우직하게 만들고 논문 써야지, 미래의 시장 판도를 예측하고 유행을 읽고 돈 벌 아이템 찾는 사업가 기질은 정말 아닌 것 같다.. ㅠㅠ

그나저나 애니메이션 이모티콘들은 gif도(꼴랑 256색) 아니고 플래시도 아니고(모바일에서 망함..) 도대체 무슨 기술을 써서 표현하는지 궁금해진다. 애니메이션 png 규격이 완성되기라도 했나..?

3.
프로그래밍 환경 내지 플랫폼을 처음부터 오랫동안 접하면 API나 방법론이 수시로 바뀌는 것 때문에 귀찮고 지저분한 일을 많이 겪게 된다. 안드로이드 내지 Cocoa API에서 숱하게 나오는 deprecated 경고들을 보니 그런 생각이 더욱 절실히 든다. 이러니 인터넷에 굴러다니는 코드들을 아무거나 선뜻 믿고 쓸 수가 없다.

그런 시절을 몽땅 건너뛰고 모든 게 그럭저럭 갖춰지고 안정화된 뒤에 프로그래밍을 시작하면 지저분한 일을 겪지는 않는다.. 하지만 이젠 모든 게 다 갖춰진 너무 방대하고 복잡한 프로그래밍 시스템 속에서 방황하게 된다. 오늘날의 웹 내지 앱 개발 환경과 30여 년 전 GWBASIC 내지 끽해야 도스 API 인터럽트 프로그래밍 환경은 얼마나 극과 극으로 다른가?
Windows는 그나마 초딩 시절부터 내가 오랫동안 써 왔으니까 익숙해진 것이지, 내가 2, 30년쯤 뒤에 늦게 태어났으면 프로그래밍을 진로로 정하지 않았을 가능성이 높아 보인다.

옛날에는 컴퓨터 성능이 빈약하고 각종 소프트웨어 시스템도 지금보다 훨씬 더 단순했겠지만.. 그게 아무 이유 없이 단순한 건 아니었다. 성능 대비 기계값이 지금보다 훨씬 더 비싸고 프로그래밍 관련 정보를 구하기도 더 어려웠다. 금수저 내지 최소한 중산층 집안이 아니면 아무나 컴퓨터를 접할 수 없었으며, 프로그래밍 저변이 지금처럼 널리 확대될 수 없었다. 그때나 지금이나 프로그래밍 여건에 관한 한 일장일단이 있다.

그 최첨단 문명의 이기인 컴퓨터가 그래도 기업· 연구소· 군대· 정부 기관의 전유물이 되지 않고, Google 같은 엄청난 검색 엔진이 부자들의 전유물이 되지 않고, 만민에게 정보 접근성이 주어지고 차별이 사실상 없어진 것은 매우 다행이고 축복이어 보인다. 비록, 그 반대급부로 어린애들에게 음란물과 폭력물에 대한 접근성까지 너무 올라간 것은 좀 심각한 문제이지만 말이다.

이건 컴퓨터가 진지하고 심각한 일뿐만 아니라 엔터테인먼트 도구로도 활용해도 돈벌이가 되니까 자본주의 내지 시장 경제 논리에 의해 대중화가 된 것일 뿐이다. 돈줄을 따라 자연스럽게 이뤄진 현상에 대해서 너무 지나치게 염세주의 음모론적으로 해석할 필요는 없을 것이다.

컴퓨터가 워낙 비싼 기계이니까 소수의 엘리트에게만 기회를 주는 게 아니라, 일단 컴퓨터 자체는 팍팍 뿌린다. 대량생산으로 가격을 낮추고 온갖 교묘한 우회 결제 수단으로(= 일시불로 기계값을 몽땅 내지 않아도 되게 오랫동안 찔끔찔끔..) 소비자 부담을 줄여서 말이다. 그 뒤 온갖 컨텐츠들로 더 많은 수익을 내는 게 소비자에게도 좋고 생산자에게도 좋은 것이다.

4.
미국이나 이스라엘, 인도 같은 나라 말고 유럽에서 나름 세계구급 IT 강국을 꼽자면 노키아와 리누스 토르발스를 배출한 핀란드가 떠오르는 편인데.. 옛날에는 불가리아도 한 끗발 했었다.
우리나라에서는 비슷한 이름의 요구르트 때문에 "생명 연장이라는 게 요구르트만 딥다 쳐먹는다고 되는 게 아닙니다" 같은 개드립의 원산지라는 이미지가 강한 편이다.;;

하지만 저 나라는 요구르트만 개발한 게 아니라, 1980년대에 국가적으로 컴퓨터 교육을 실시하고 나름 고급 IT 엔지니어 육성을 했다.
1989년 5월에 제 1회, 국제 정보 올림피아드라는 게 최초로 개최된 곳도 미국이나 다른 유명한 나라가 아니라 바로 '불가리아'였다. 이 역시 생각할 점이다.

그런데 문제는 그렇게 양성된 컴퓨터 똘똘이들이 자국에서 자기 재능을 좋은 방향으로 쓰면서 돈과 명예를 얻는 인프라가 없었다는 거다.
그래서 불가리아에서 컴퓨터와 관련해서 세계구 급으로 선한 게 개발되어 나온 게 없었다. 그 대신 불가리아 산 컴퓨터 바이러스들이 악명을 떨쳤다.
당장 떠오르는 건 DIR-II, 어둠의 복수자(dark avenger) 바이러스. 이들의 원산지가 바로 저 나라였다. 그러고 나서 1990년대 이후부터 불가리아의 존재감은.. 지금 다들 알려진 바와 같다.

그 먼 옛날에 컴퓨터 바이러스를 만들 정도의 사람이라면 그 비싸고 귀하던 IBM PC의 내부 구조와 도스 API, x86 어셈블리를 다 마스터 했고 컴공이 얼마나 대단했겠는가? 그랬는데 만들어 낸 건 고작 남에게 해를 끼치는 물건뿐이었던 거다.
지금도 북괴에서는 아무리 컴퓨터 똘똘이가 돼 봤자 하는 일은 당의 명령대로 오픈소스들 다 무단으로 베껴서 이상한 프로그램 만들거나, 중국으로 외화벌이 정보전사로 파견 나가서 사이버 범죄, 남조선 종북 여론몰이 같은 지저분한 짓밖에 없다. 그런 것과 비슷한 맥락의 안타까운 상황이다.

5.
전에 얘기한 적이 있던가?
본인은 초딩 저학년 때 8비트 컴, 중· 고학년 때 16비트 IBM 호환 PC, 중학교 때 Windows와 PC통신, 고등학교 때 인터넷, 대학교 때 휴대전화, 대학원 때 스마트폰을 순서대로 접했다.
대학교 때 기숙사에서 10Mbps 유선 랜을 처음으로 접했고, 2003년쯤에 무선 인터넷과 USB 메모리라는 걸 처음으로 접했다.

내가 대학을 졸업할 즈음부터 대학원 연구실을 시작으로 유선 랜의 속도가 100Mbps로 올랐으며, 그로부터 얼마 안 가 교내 네트워크 주소들이 공인 고정 class B ip 대신, 가변 사설 ip로 바뀌었지 싶다.
이제는 그냥 무선 인터넷으로도 신호가 좋은 곳에서는 늘 100Mbps까지는 아니어도 수십 Mbps의 속도가 나오고, 유튜브로 HD급 동영상을 실시간 스트리밍으로 보는 시대가 됐다. 개인적으로는 정말 충격적이다. PC통신으로 사진 한 장 받던 시절의 전송 속도와 지금 속도를 비교하면 말이다.

지금이 우주 정거장 관광 단가가 1억 원 근처까지 내려갔다거나, 스페이스 오딧세이 2001 영화가 묘사하는 세상이 오지는 않았다. 컴퓨터의 클럭 속도가 싱글 코어로 10GHz를 넘어간다거나 하지도 않았다. 하지만 과거에 상상하지 못했던 SNS 미디어, 고화질 동영상과 더 새끈해진 글꼴, 각진 게 아니라 날렵한 외형의 자동차들이 시대의 변화를 대신 말해 주고 있다. 이것 말고도..

  • 휴대전화가 없던 시절엔 오지에서 운전을 하다가 차가 퍼지거나 사고가 나면 보험사 연락을 어떻게 했을까?
  • CCTV와 블랙박스가 없던 시절엔 교통사고 과실 비율 산정이 얼마나 주먹구구식으로 진행됐을 것이며(바퀴가 굴러가는 이상 절대적인 100:0이란 존재하지 않는다.. -_-), 가해자와 피해자가 뒤바뀐 억울한 경우도 얼마나 많았을까?
  • 구글과 riss.kr, dbpia가 없던 시절에 도대체 학술 문헌 검색을 어떻게 하고 논문이란 걸 어떻게 썼을까? (일일이 도서관 찾아다니면서 실물을..)
  • msdn이야 그렇다 치더라도, 스택 오버플로우와 검색 엔진이 없던 시절에는 도대체 생소한 플랫폼에 대한 프로그래밍 자료 검색을 어떻게 하면서 코딩을 어떻게 했을까?
  • 15년 전이나 지금이나 그냥 이더넷 랜선을 꽂는 건 동일한데 랜 카드와 내부 기술 기반이 뭐가 바뀌었길래 인터넷 속도가 옛날보다 10배 이상으로 뻥튀기될 수 있었을까??
  • 전자기파의 물리적인 특성이 100년 전이나 지금이나 바뀐 건 없을 텐데 텔레비전의 화질은 어쩜 이렇게 좋아졌을까?

이런 걸 생각하면 과학 기술, 특히 정보 통신 분야의 기술이 세상을 얼마나 드라마틱하게 바꿔 놓았는지를 알 수 있다.

그 전까지 몇몇 얼리어답터들이나 쓰던 PDA, 휴대용 MP3 플레이어가 휴대전화와 결합해서 스마트폰으로 탄생한 건 정말 2000년대의 혁신 중의 혁신이 아닐 수 없었다. 그냥 크기도 작고 기능도 열악한 특수 목적 컴퓨터의 범주인 '임베디드'로부터 '모바일'이라는 완전히 새로운 범주가 파생돼 나왔다. Windows CE가 Phone, Mobile 등 갖가지 브랜드로 재탄생해야 한 것을 보면 이해하기 쉽다. 지금이야 그냥 10이라는 브랜드로 통합됐고, 스마트폰 OS로는 안드로이드가 지구를 평정했다만 말이다.

물론 냉동 기술이나 플라스틱, 의학· 생명 공학처럼 굳이 IT에 속하지 않는 획기적인 기술도 있다.
기술의 발달 덕분에 쿼츠 시계는 기계식 태엽 시계를 가격과 성능 모든 면에서 쳐발랐고, LED는 백열등은 말할 것도 없고 형광등까지 쳐발라서 인류가 발명한 가장 고효율 광원을 달성했다. 핵 무기는 같은 무게의 재래식 폭탄에 비해 위력 계수에 0을 몇 개 더 붙였다.

이런 정도의 혁신이 앞으로 어느 분야에서건 또 나올 게 있으려나 모르겠다.
무탄피총, 실리콘 반도체를 능가하는 컴퓨터 소자, 자동차에서 현행 기계들의 한계를 극복하는 무단 변속기나 반켈 엔진, 혁신적인 무선 송전이나 2차 전지, 핵융합 발전 같은 것 말이다.

6.
정보 통신 쪽 얘기를 계속하자면..
자동차의 번호를 자동으로 인식하는 무인 단속 카메라와 무인 주차 시스템도 지금이야 너무나 당연하게 여겨지고 있지만, 국내에 본격적으로 도입된 건 1990년대 중후반부터이다. 지금으로부터 20년이 채 되지 않았다. 이런 게 없던 시절에는 과속· 신호 위반 같은 건 경찰관이 숨어 있다가 위반 차량을 강제로 불러다 세우는 식으로 단속을 할 수밖에 없었다.

차량 번호판을 인식하는 기술은 길거리에서 도난· 수배 차량이나 세금· 통행료 상습 체납 차량을 즉시 잡아내는 데에도 아주 요긴하게 쓰이고 있다. 이게 없었으면 컴퓨터와 행정 전산망이 있더라도 사람이 일일이 차 번호를 입력해서 조회해야 했으니 일이 얼마나 불편했을지 모른다. 스피드건이 생각보다 속도를 굉장히 정확히 측정해 주는 것만큼이나 신기한 일이다.

단순히 편해진 것뿐만 아니라 세상 돌아가는 시스템이 더 객관적이고 공정하게 바뀐 것은 얼마나 축복인지 모른다. 모든 것이 전산화되고 컴퓨터가 통제하는 세상에 대해 무슨 666 짐승의 표인 것처럼 공포심만 가질 필요는 없을 것이다.

Posted by 사무엘

2018/03/07 08:26 2018/03/07 08:26
, ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1465

1. 도스 시절 명령어

도스에서 파일(과 디렉터리)을 다른 곳에 복사하는 명령은 copy이다. 얘는 명령 셸인 command.com이 자체 지원하는 내장 명령이다.
그런데 도스에는 copy의 일종의 강화 버전이라 할 수 있는 xcopy라는 것도 있으며, 이건 별도의 프로그램을 통해 실행되는 외부 명령이다.

xcopy는 일반 copy와 달리 (1) 서로 다른 드라이브 사이에 서브디렉터리까지 재귀적으로 통째로 복사하는 걸 지원했으며, (2) 복사에 사용하는 메모리 버퍼 크기가 더 크고, 여러 개의 파일을 한꺼번에 읽은 뒤(대략 수백 KB 정도 크기까지) 타겟에다 쓰는 걸 지원했다. xcopy의 전치사 X는 일반적으로 cross라고 발음되었다.

지금 생각해 보면 이 둘은 차별화 요소가 전혀 될 수 없으며 굳이 분리할 필요가 없다. 그냥 copy가 원래 (2)처럼 동작하면 되고, (1)은 /s 같은 옵션을 추가해서 지원하면 될 일이다.
하지만 옛날에 xcopy가 외부 명령으로 존재했던 이유는 겨우 그 정도 고급 복사 옵션/기능마저도 command.com에다 상시 집어넣고 있기에는 메모리가 부족하고 아까웠기 때문이다. 옛날에는 베이직 인터프리터가 Ready 출력할 메모리조차 아까워서 프롬프트를 Ok로 바꾼 시절이 있었다는 것 기억하시는가? (단 3바이트를 아끼려고!) 검색을 해 보니 xcopy는 1987년, MS-DOS 3.2에서 첫 도입됐다고 한다.

하긴, 옛날에는 다단계 디렉터리를 재귀적으로 탐색하면서 뭔가를 하는 일이 쉽지 않아서 외부 유틸리티를 많이 이용해야 했다. 파일 찾기는 말할 것도 없고, 지우는 것도 옛날에는 deltree라는 명령이 따로 있었지 싶다. 그건 지금은 del에 /s옵션으로 통합됐지만 말이다.
유닉스 계열 셸은 내장과 외장 명령어 구분이 어찌 되나 모르겠다. cp, mv, rm은 외부 프로그램인 것 같던데 설마 pwd, cd 이런 것들도 다 외부 프로그램이려나?

그리고 옛날에는 플로피 디스크(일명 디스켓)란 게 쓰여서 파일 시스템 차원에서 완전히 똑같은 디스크 복제를 해 주는 diskcopy라는 외부 명령이 있었다. 얘는 굳이 외부 명령으로 만들 거면 디스크 이미지 파일을 만들거나 이로부터 복제 디스크를 만드는 기능도 같이 있었으면 좋았을 거라는 아쉬움이 남는다.

1.2~1.44MB짜리 디스크를 단일 드라이브에서 복사하려면 source와 target 디스켓을 여러 번 갈아 끼워야 했는데.. Norton Utilities 같은 다른 유틸리티들은 EMS 및 XMS 메모리를 활용하여 한 번만 갈아 끼우고 바로 복사가 된다는 걸 장점으로 내세우곤 했다. 프로그램을 하나 설치하려 해도 "제품의 이제 1~N번 디스크를 넣고 아무 키나 누르세요" 이러던 참 아련한 옛날 추억이다.

2. 16비트 Windows의 실행 모드

예전에 언급한 바와 같이, 1990년대 초중반에 Windows라는 운영체제가 32비트 플랫폼으로 처음 갈아타던 시절에는 Win32 API라는 것의 구현체가 Windows NT, Windows 95, Windows 3.1+Win32s라는 세 계통으로 나뉘었다. NT가 가장 이상적인 레퍼런스 구현체이고, Win32s는 제일 허접하다.

그리고 그 중간의 95는 비록 NT처럼 스레드도 지원하고 도스로부터 많이 독립했다고는 하지만, 도스로부터 완전히 독립했다기보다는 도스를 내부적으로 흡수· 합병한 것에 가깝다. Windows NT의 마이너 축소판이라기보다는 Win32s가 각종 한계 없이 제대로 구현된 형태라고 보는 게 더 타당하다.

그런데 95 계통의 전신이라 할 수 있는 Windows 3.0이 나왔을 때에는 동일 제품· 단일 바이너리 하에서 실행 모드가 세 갈래로 나뉘었다. 바로 8086 리얼 모드, 286 표준 모드, 386 확장 모드이다.
Windows NT 4가 가장 많은 아키텍처를 지원하는 32비트 운영체제였다면, Windows 3.0 (3.1 말고)은 실행 모드가 가장 다양했던 16비트 도스용 운영 환경(?)이었다.

원래 Windows 1과 2에서는 리얼 모드밖에 존재하지 않았으며, Windows는 진짜 도스 위의 덧실행 껍데기 그 이상도 이하도 아니었다. 리얼 모드에서는 메모리가 기본 640K밖에 없었으며, 그 번거로운 16비트 Windows 3.x보다도 프로그래밍 환경이 더 열악했다.

그러다 Windows 2.0의 후속 버전으로 2.1은 286 표준 모드를 도입한 Windows/286과, 386 확장 모드의 전신인 Windows/386 이렇게 두 갈래로 나왔다. 사실, 16비트 80286 프로세서에도 보호 모드가 있긴 했지만 프로그래밍에 애로사항이 많았는지 그걸 사용한 프로그램은 매우 소수였으며 여전히 거의 봉인돼 있었다. 그냥 EMS/XMS 같은 규격으로 메모리를 수백 KB 남짓 더 사용할 수 있다는 것에 의미를 둬야 했다. 386 확장 모드도 첫 버전답게 문제가 많았으며, 2.11이 더 나와야 했다.

그러다가 Windows 3.0은 리얼, 286 표준, 386 확장을 모두 통합하여 출시됐다. 이때는 DPMI 규격도 갓 제정되었기 때문에 2.x 시절보다 보호 모드 지원도 더 개선되었다.
이때는 3.0에다가 멀티미디어 API가 최초로 추가된 확장팩이 나왔으며, 3.1은 네트워크 API가 추가된 Windows for Workgroup 3.11, 그리고 중국어 입출력 지원이 추가된 3.2 이런 식으로 기능 확장팩이 많던 시절이었다. 지금처럼 인터넷을 통한 업데이트가 가능한 시절도 아니었으니 말이다.

Windows 3.1에서는 리얼 모드가 삭제되고 win /2 또는 /3을 통해 286 표준 모드만 지원하지만, 이것은 성능이 굉장히 많이 열화된 모드였다. 그리고 Windows 95부터는 표준 모드도 빠져서 기술적으로 언제나 386 확장 모드로만 동작하는 형태가 됐다.

이렇듯, Windows 3.x는 비록 순수한 32비트 운영체제는 아니지만 보호 모드라든가 도스용 프로그램을 내부에서 구동할 때처럼 일부 기능에서 80386 이상 CPU가 제공하는 가상화 기능을 사용하기 때문에 결과적으로 32비트 CPU가 필요했다. 386 확장 모드가 지원하는 기능이 그런 것이었다.
이는 과거에 존재하던 PIF 편집기가 확장 모드에서는 표준 모드에 비해서 지원하는 옵션이 얼마나 더 다양해지는지를 보면 얼추 짐작 가능하다. 286 표준 모드에서는 요게 전부이던 옵션이..

사용자 삽입 이미지

386 확장 모드에서는 XMS뿐만 아니라 EMS 규격, 그리고 멀티태스킹 우선권 등 다양한 옵션들이 별도의 대화상자와 함께 추가되는 걸 알 수 있다.

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

Windows 95 역시 순수 32비트 프로그램이 아니면 도스용 프로그램에 대해서만 제대로 된 가상 머신과 선점형 멀티태스킹을 지원했다. 16비트 Windows 프로그램을 강제 종료하면 운영체제와 얽혀 있는 스레드 동기화 오브젝트 같은 것이 얼어붙으면서 시스템의 안정성에 심각한 문제가 발생하곤 했다.

LimitEmsPages 이런 함수는 32비트가 아니라 이미 Windows 3.x 시절부터 deprecated돼 있었는데, 아마 리얼 모드 시절의 잔재이지 싶다.
Windows 1~2.x용 실행 파일은 후대 Windows와 실행 파일 포맷은 동일하지만(NE) 내부의 플래그로 구분되어 있어서 3.x에서 실행하면 "제대로 실행되지 않을 수 있음" 경고가 뜨곤 했다. 요컨대 Windows의 역사를 살펴보면, 16비트에서 32비트로 넘어갈 때만치 큰 변화는 아니지만, 같은 16비트 안에서도 1~2.x와 3.x 사이에 보호 모드가 도입되기 전과 후에는 기술적으로 나름 변화와 단절이 있었다고 볼 수 있다.

3. 외주로 제작되었던 보조 프로그램과 게임

Windows에 내장돼 있는 기본 프로그램들 중에는 마소에서 직접 만들지 않은 외주 프로그램도 있다. Windows 95 시절에 잠깐 있었던 하이퍼터미널이라는 터미널 접속 프로그램이 대표적인 예로, 스플래시 화면이라든가 각종 UI 외형이 대놓고 기존 마소 프로그램과는 어울리지 않아서 마소 자체 개발이 아니라는 걸 알 수 있었다.

또한 게임 중에서도 Windows XP에까지 제공되었던 3D 핀볼(시네마트로닉스 개발, 맥시스 유통)은 외부 프로그램이었으며, Vista/7 시절에 그래픽이 쇄신했던 지뢰찾기 등의 기본 게임들도 외주였다(Oberon games).

그런데 더 옛날에 Windows 3.1의 내장 프로그램들을 보면, 굉장히 단순하게 생겼고 이 정도면 자체 제작했을 법도 한 프로그램이 About 대화상자를 보면 외주인 경우가 은근히 더 있었다. 게다가 아래의 목록에서 보다시피 제작자/제작사가 프로그램마다 완전 제각각이었다.

  • 계산기: Kraig Brockschmidt
  • 터미널: Future Soft Engineering 사
  • 레코더: Softbridge 사
  • 지뢰찾기: Robert Donner & Curt Johnson
  • 카드놀이: Wes Cherry

그러니 Vista/7 이전에 제공되던 기본 게임들도 알고 보니 외주였던 셈이다. 특히 지뢰찾기는 Windows 1.0부터 3.0까지 존재하던 Reversi(일명 오델로)를 대체할 목적으로 3.1에서 처음 선보였던 게임이기도 하다.
레코더의 경우 Windows의 역사상 거의 전무후무하게 존재하던 키보드· 마우스 매크로 유틸리티인데 95와 그 이후로는 결코 재등장하지 않았으니 희소성이 크다.

물론 이것들 말고 프로그램 관리자, 파일 관리자, 제어판이라든가 간판 앱인 문서 작성기(오늘날의 워드패드)와 페인트(오늘날의 그림판)은 외주가 아니라 내부 자체 제작이다.

4. 색깔의 미묘한 차이

말이 나왔으니 옛날 추억 회상을 더 해 보자면,
컴퓨터의 주메모리가 아니라 비디오 메모리가 딱 1MB이던 시절에는 Windows 3.1의 비디오 모드를 (1) 꼴랑 640*480 저해상도인 대신에 트루컬러, (2) 800*600에서 적당하게 하이 컬러, 아니면 (3) 1024*768에서 256색.. 셋 중 하나로 골라 쓰는 재미(?)가 있었다.

그리고 Windows 3.1은 원래는 우리에게 익숙한 짙은 파란색으로 윈도우의 굵은 틀을 표현했지만, 하이 컬러 이상부터는 어인 일인지 은은한 하늘색으로 색깔을 바꿔 표시했다. 왜 무슨 근거로 색깔을 바꿨는지는 모르겠지만 개인적으로 아주 신기하게 느껴졌었다.

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

그 이전 3.0은 슈퍼 VGA나 트루컬러로 진입할 일이 있긴 있었는지, 그래픽 드라이버가 개발되거나 3.1 것과 호환되긴 했는지에 대해 본인은 아는 바가 없으며, 이에 대해서 회의적이다.

Posted by 사무엘

2018/03/04 08:30 2018/03/04 08:30
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1464


블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2018/03   »
        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:
2674740
Today:
1432
Yesterday:
1540