뭐, 무식이 자랑이랄 수는 없겠지만,
본인은 전산학 내지 컴퓨터공학의 여러 분야들 중에서 문외한에 가깝게 제일 모르는 분야는 통신, 네트워크, 웹, 보안 쪽이다.
왜 제일 모르느냐 하면, 저건 컴퓨터 한 대만으로 독학이 가능하지 않고, 뭔가 '감'을 터득할 수 없는 분야이기 때문이다. 그래서 그런 걸 잘하는 사람들이 부럽다..

일례로 완전 최저수준 소켓 API와, 고수준 HTTP API 사이의 중간 과정에 대한 감이 전혀 없다. 후자도 분명 전자를 이용해서 구현됐을 텐데, 내부 구현이 어찌 돼 있는지 난 아는 게 없다.
그리고 네트워크 트래픽이 컴퓨터의 I/O 병목엔 어떤 영향을 끼치는지, 그 패킷이 어떻게 해서 한 운영체제 내부의 특정 응용 프로그램으로 잘 전달이 되는지, DDoS 공격이 서버 컴퓨터에 어떤 물리적인 영향을 끼쳐서 서버를 뻗게 할 수 있는지, (아님 단순히 프로세스/스레드의 무한 스폰으로 인해 소프트웨어적인 자원 고갈만으로 뻗는 건가?)

HTTP 프로토콜에서 파일 업로드는 어떤 절차를 거쳐서 되는지, 방화벽이라는 게 정확히 뭘 하는 물건인지,
왜 구닥다리 윈도 2000/XP sp0을 띄운 채로 랜선을 꽂으면 뭐가 뚫려서 어떻게 되는지..
요즘은 네트워크 패킷은 하부 계층에서 압축이나 암호화를 좀 하는 게 있는지 등등..

이런 것들은 난 하나도 모..른..다. 저런 거 하나도 몰라도 <날개셋> 한글 입력기 개발하는 덴 아무 지장이 없기 때문이다.
그렇다고 해서 내가 컴퓨터 명령어 체계나 운영체제/소프트웨어 자체의 내부 구조나 보안에 대해 전혀 모르느냐 하면 그것도 물론 아니니.. 지식의 분포가 좀 불균형하다면 불균형한 셈이다.

본인은 초딩 중고학년 때 개인용 PC, 중학교 때 모뎀과 PC 통신, 고등학교 때 인터넷과 이메일, 그리고 대학교 때 무선 인터넷과 휴대전화의 순으로 문명의 이기들을 접했다. 랜 선이라는 걸 태어나서 처음으로 구경한 게 고등학교 때부터인데, 그 기간 동안 언젠가 집도 인터넷 접속 방식이 전화 모뎀에서 전용선으로 바뀌었다. 그때가 한창 전국적으로 인터넷 전용선이 깔리던 시절이었으니까.

지금까지 통신 기술은 정말 눈부신 속도로 발전했다.
신문· 방송에서 기자의 이메일을 공개하는 게 대세가 된 게 1990년대 후반부터이다.
지금으로부터 10여 년 전엔 '블로그'라는 단어가 도전 골든벨의 마지막 문제의 답이었다는 게 믿어지시는가? (그것도 학생이 못 맞혔고 그 당시엔 내게도 생소했다)

옛날에는 인터넷 연결을 위해서도 PC 통신을 할 때처럼 먼저 전화를 걸어야 했다. 사용 시간 카운터가 올라가는 자그마한 인터넷 연결 창이 뜬 동안만 인터넷을 이용할 수 있었다.
또한, 모뎀과 마우스를 동시에 사용하려면 두 물건을 서로 COM 포트가 충돌하지 않게 배치를 해야 했다.
Windows 3.x에서는 운영체제 차원의 네트워크 지원이 전무하기 때문에 트럼펫 Winsock인지 뭔지 하는 걸 먼저 설치해야 했다.

이 모든 것들이 지금은 까마득한 옛날 이야기가 됐다.
지금은 뭔가 그렇게 상태를 확인하면서 인터넷을 해야 하는 상황은 스마트폰 태더링으로 무선 인터넷을 쓸 때 정도이고 이것도 제약, 압박감, 부담 같은 건 옛날과는 비교할 수 없이 작아졌다.
메인보드가 어떻게 공간 워프를 했는지, 요즘은 유선 랜과 무선 랜도 전부 내장되어 나온다. 따로 뭘 장착조차 할 필요 없이 바로 접속만 하면 된다.

오늘날 인터넷이라고 불리는 그 통신망은 OSI 레이어 계층 중 제3계층(네트워크 계층)을 차지하는 IP라는 프로토콜을 기반으로 동작한다. IPv4, IPv6 같은 주소 체계도 이 계층에서 규정하는 것이기 때문에 모든 인터넷 통신은 이 체계를 기반으로 구성되어 있다.

그리고 그 아래의 제4계층(전송 계층)에는 인터넷 프로토콜을 따르는 네트워크 패킷을 보내는 방식의 차이를 규정하는 프로토콜이 있는데, 크게 TCP와 UDP가 있다.
TCP는 보낸 패킷이 반드시 순서대로 도착한다는 것은 보장되지만, 보냈던 단위랄까 형태가 그대로 도착하지는 않는다.
aaa, bb, ccccc, ddd, e 이렇게 패킷을 보냈으면 받는 쪽은 aa, ab, bccc, cc, dd, d, e 뭐 이렇게 받을 수도 있고 다른 형태가 될 수도 있다. 조립은 받는 쪽에서 알아서 해야 한다.

UDP는 TCP와는 달리 보낸 패킷이 원래의 형태 그대로 간다는 보장은 되지만.. 일부 패킷이 전송 과정에서 누락될 수가 있다.
즉, 위의 경우 ddd가 누락돼서 aaa, bb, ccccc, e 이렇게 갈지도 모르지만.. 일단 간 놈은 원래 형태 그대로 간다. 패킷의 누락 여부 판단을 받는 쪽에서 알아서 해야 한다.
그래서 TCP는 일종의 스트림 지향적이며, UDP는 개개의 패킷이 모 아니면 도 형태로 가는 메시지 지향적이다.

형태도 보존되고 누락 현상도 없는 만능 프로토콜이 없는 이유야 뭐, 세상에 값도 싸고 성능도 좋은 물건은 존재하지 않기 때문인 것과 같은 맥락일 것이다.
그게 필요하면 UDP 같은 걸 기반으로 패킷 누락을 감지하고 재전송을 요청하는 로직을 응용 프로그램이 별도로 구현해 줘야 한다.

온라인 게임에서는 “기관총 난사 내지 캐릭터 이동 같은 것만 UDP이고 나머지는 다 TCP”라는 말 한 마디로 요약된다.
자주 발생하기 때문에 반응성이 중요하고 적당히 좀 씹혀도 상관 없는 것만 UDP이고.. 나머지 크리티컬한 것들은 다 TCP를 써야 한다는 뜻이다.
그러나 온라인 게임에서 발생하는 트래픽의 상당수, 대략 70% 가까이는 그래도 UDP 방식이라고 한다.

실시간으로 스트리밍되는 대용량 오디오/비디오 데이터도 자명한 이유로 인해 UDP 방식으로 전송된다.
이런 차이를 보면, TCP와 UDP의 관계는 사실상 무손실 압축과 손실 압축의 관계나 마찬가지인 것 같다.

TCP의 경우 응용 프로그램이 아니라 아래의 프로토콜 차원에서 패킷의 누락을 감지하여 누락이 있는 경우 재전송 요청을 한다.
그런데 모바일처럼 네트워크 환경이 원래 워낙 열악해서 패킷 손실이 굉장히 자주 발생하는 곳에서는
TCP 방식에서는 끝도 없이 재전송 요청을 하고 받은 데이터에 결함이 없는지 체크와 빠꾸만 반복하느라 응용 프로그램이 그 동안 멍하니 있어야만 하는 일이 발생한다고 한다.
즉, TCP가 구조적으로 오버헤드가 더 크니, 네트워크가 열악한 곳에서는 그런 동작을 감안해야 한다.

게임에서 그래픽 엔진, 물리 엔진, 동영상/캡처 엔진도 아니고 네트웍 엔진 미들웨어로 먹고 사는 분이 국내에 일단 한 분 계신다. 넷텐션의 대표이사인 배 현직 씨. 이 업계에서는 이미 유명인사이다.

난 네트워크 쪽 프로그래밍을 해 본 건 먼~ 옛날에 소켓 API 대충 뚝딱해서 오목을 만들고..
DirectPlay를 써서 스크래블 정도 보드 게임에다가 네트웍 플레이를 넣어 본 게 전부이다. 그래도 그것만으로도 굉장히 재미있는 경험이었다.
저수준에서 패킷 암호화, 각종 오류 처리 그런 건 모른다. DPlay는 나름 하드웨어 독립을 추구한 통신 API이긴 한데, 요즘은 모뎀이고 시리얼 케이블 그딴 건 다 없어졌으니 그런 추상화 계층이 필요가 없어지면서 자연스레 도태했다. 잘은 모르겠지만 제1계층(물리 계층)은 거의 획일화가 돼 버린 것 같다.

※ 여담. IPX는 어디로 갔는가?

스타크래프트에서 배틀넷 말고 그냥 LAN으로 친구들끼리 팀플을 할 때, 옛날에는 배틀넷 다음으로 위에서 둘째인 IPX를 으레 고르곤 했다. 그러나 어느 패치 때부터인가 맨 아래에 UDP가 추가되었으며 그걸 고르는 걸로 구조가 바뀌었다. IPX는 동작하지 않기 시작했다. 어찌 된 일일까?

IPX라는 프로토콜은 똑같이 이더넷 랜선으로 통신을 하지만, 오늘날의 인터넷과는 다른 방식으로 통신을 한다. 즉, IP와 대등한 제3계층에서 방식이 다른 프로토콜인 것이다. IPX는 옛날에 네트워크 솔루션으로 유명했던 노벨 사에서 개발했고 실제로 매우 널리 쓰이기도 했지만 오늘날은 IP에 밀려서 사라졌다.

Windows 95때까지만 해도 네트워크 구성요소들을 설치하고 나면 기본으로 깔리는 것은 IPX였다. TCP/IP 지원 기능은 운영체제 CD를 넣어서 별도로 설치해야 했다. 무슨 말이냐 하면, 오늘날 당연시되고 있는 이 컴퓨터의 IP 주소를 설정하는 기능이 Windows 95에는 기본으로 없었다는 뜻이다. (심지어 네트워크 기능이 설치된 컴에서도)

사용자 삽입 이미지

그 다음 1990년대 중후반, Windows 98부터는 인터넷의 중요성이 워낙 크게 부각되기 시작했으니 TCP/IP 지원도 같이 포함되었다.
여담이지만 Windows에서는 등록정보/속성을 나타내는 단축키가 R인 편인데, 95에서는 유독 저 대화상자에서만 R은 삭제이고, 등록정보는 P였다. 무척 불편했는데 이 역시 98부터는 다같이 R로 개선됐다.

하긴, 본인도 옛날부터.. Windows가 근거리 네트워크 차원에서 제공하는 컴퓨터 간의 폴더 공유 기능과, 웹브라우저로 띄우는 인터넷은 기술적으로 무슨 관계인가 궁금하긴 했다.
인터넷 열풍 앞에서 IPX는 점점 잉여로 전락했으며, Vista부터는 드디어 IPX 지원이 hlp 도움말만큼이나 짤렸다. 그래서 스타크래프트도 근거리 팀플에 인터넷 프로토콜을 사용하는 UDP 지원이 추가된 것이다.

Posted by 사무엘

2015/02/05 08:37 2015/02/05 08:37
, , , ,
Response
No Trackback , 10 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1058

1. 오픈소스

잘 알다시피 C/C++은 메모리 할당이나 문자열 등, 바이너리 차원에서 뭔가 언어나 구현체가 buliding block을 규정해 놓은 게 없다시피하며, 그나마 표준이 나온 것도 강력한 구속력을 갖고 있지는 못하다. 그러니 이 지저분함을 참다 못해서 COM 같은 바이너리 규격이 나오고 닷넷 같은 완전히 새로운 프레임워크도 나왔다.

아니면 일각에서는 소프트웨어 컴포넌트를 재배포할 때, 빌드된 라이브리러리를 주는 게 아니라 난독화 처리만 한 뒤 소스 코드를 통째로 넘겨주면서 빌드는 이 코드를 쓰는 쪽에서 자기 입맛대로 알아서 하라는 극단적인 조치를 취하기도 한다. 차라리 오픈소스 진영이 이런 점에서는 융통성이 더 있는 셈이다.
하지만 어지간한 컴덕력을 갖추지 못한 사람은.. 복잡한 빌드 시스템/configuration들을 이해할 수 없어서 소스 코드까지 통째로 줬는데도 줘도 못 먹는 촌극이 벌어진다.

이런 라이브러리 내지 유닛, 패키지는 기계어 코드로든 다른 바이트 코드로든 소스 코드가 바이너리 형태로 용이하게 재사용 가능한 형태로 가공되어 있는 파일이다.
그런데 실행문이 들어있는 소스 코드가 반드시 그대로 노출돼야만 하는 분야도 있다.

크게 두 갈래인데, 하나는 C++의 템플릿 라이브러리이고, 다른 하나는 웹 프로그래밍 언어 중에서도 전적으로 클라이언트 사이드에서 돌아가는 자바스크립트이다.
동작하는 환경 내지 타겟은 둘이 서로 완전히 극과 극으로 다르지만, 전자는 컴파일 때 최적화 스케일의 유연성 때문에, 그리고 후자는 선천적으로 기계 독립적이고 극도로 유연해야만 하는 웹의 특성상 오픈소스가 강제되어 있다.

자바스크립트는 비록 전통적인 기계어 EXE를 만드는 데 쓰이는 언어는 아니지만 그렇다고 해서 만만하게 볼 물건이 절대로 아니다. 람다, 클로저 등 어지간한 최신 프로그래밍 언어에 있는 기능은 다 있으며, 플래시에 하드웨어 가속 3D 그래픽까지 다 지원 가능한 경지에 도달한 지가 오래다.
또한 웹에서의 영향력이 워낙 막강하다 보니 전세계의 소프트웨어 업체들이 눈에 불을 켜고 실행 성능을 필사적으로 끌어올려 놓았다. 비록 컴파일을 통한 보안 유지는 안 되지만, 어느 수준 이상의 코드 난독화 기능도 당연히 있다.

뭐, C++ 표준 템플릿 라이브러리도 헤더 파일을 열어 보면, 남이 못 알아보게 하려고 코드를 일부러 저렇게 짰나 싶은 생각이 든다. 온갖 주석이 곁들여져서 알아보기 쉽게 널널하게 작성된 C 라이브러리의 소스들과는 형태가 달라도 너무 다르다..

C++ 템플릿에 대해서 한 마디 더 첨언하자면.. 제한적으로나마 함수나 몸체를 일일이 인클루드해서 노출하지 않아도 되는 방법이 있긴 하다.
몸체를 한 cpp(= 번역 단위)에다가만 구현해 놓은 뒤, 거기에다가 소스 코드 전체를 통틀어 그 템플릿이 인자가 주어져서 쓰이는 모든 형태를 명시만 해 주면 된다.

template Sometype<char>;
template Sometype<wchar_t>;

템플릿 함수에 대해서 template<> 이렇게 시작하는 특정 타입 전용 케이스를 만드는 것과 비슷해 보이는데..
위와 같은 식으로 써 주면, 해당 코드가 컴파일될 때 이 템플릿이 저런 인자로 실현되었을 때의 대응 코드가 모두 생성되고, 이게 다른 오브젝트 파일들이 링크될 때 같이 연결되게 된다. 이런 문법이 있다는 것을 15년 동안 C++ 프로그래밍을 하면서 처음 알았다.

물론 저것 말고 다른 임의의 새로운 타입으로 템플릿을 사용하고 싶다면 그렇게 템플릿을 사용하는 번역 단위에서 또 다시 템플릿의 선언부와 몸체를 싹 읽어들여서 분석을 해야 한다.
아마 과거의 export 키워드가.. 저런 템플릿 인자의 사용 형태를 자동으로 파악하는 걸 의도하지 않았나 싶은데 그래도 세상에 쉬운 일이란 없었던 듯하다.

2. 웹 프로그래밍의 성격

HTML, CSS, 자바스크립트 삼신기는 마치 웹 프로그래밍계에서의 삼권 분립이기라도 한 것 같다. 아무래도 당장 화면에 표시되는 핵심 컨텐츠가 HTML이니 요게 행정부에 대응하는 듯하며, HTML을 표시할 규격을 정하는 CSS는 사법부에 가깝다. 끝으로, 인터랙티브한 동작을 결정하는 자바스크립트는 입법부 정도?
물론 HTM 파일 하나에다가 스타일과 자바스크립트 코드를 다 우겨 넣었다면 그건 뭐 “짐이 곧 국가다, 법이다” 식으로 코드를 작성한 형태일 것이다.

예로부터 본인이 느끼기에 웹 프로그래밍은 뭔가 시대의 최첨단을 달리는 것 같고 간지와 뽀대가 나고 실행 결과가 사용자에게 가장 직접적으로 드러나 보이는 신기한 영역인 것 같았다. 하지만 (1) 코드와 데이터, 클라이언트와 서버, 코딩과 디자인의 역할 구분이 영 모호하며, 컴퓨터의 성능을 100% 뽑아내는 듯한 전문적이고 하드코어한 느낌이 안 들어서 마음에 안 들었다. 가령, 도대체 어디서는 java이고 어디서는 jsp이고 어디서는 js인지?

(2) 또한 이 바닥은 작성한 소스 코드가 제대로 보호되지 못한다. 서버 사이드에서만 돌아가는 PHP 같은 건 클라이언트에게는 노출이 안 되겠지만 그것도 서버 개발자들끼리는 결국 오픈소스 형태로 공유될 수밖에 없으니 말이다. 옛날에 제로보드의 소스가 그랬듯이.

끝으로, (3) 특정 CPU 아키텍처나 플랫폼에 구애되는 게 없다 보니 기반이 너무 붕 뜨는 느낌이고, 브라우저마다 기능이 제각각으로 달라지는 거 호환 맞추는 노가다가 필요한 것도 싫었다.
뭐, IE와 넷스케이프가 경쟁하고 IE6이 세계를 사실상 평정했던 먼 옛날에는 그랬고 지금은 이 문제는 많이 해소됐다. 바야흐로 2015년, HTML5 표준안까지 다 완성된 지경이니, 웹 프로그래밍도 이제 충분히 성숙했고 기반이 탄탄히 잡혔다. 격세지감이다. ActiveX도 점점 퇴출되는 중이다.

2004년에 IE6에 대한 대항마로 파이어폭스 0.8이 혜성처럼 등장했고, 2008년엔 구글 크롬이 속도 하나로 세계를 평정해서 IE의 독점 체계를 완전히 견제해 냈다. 지금은 크롬이 속도는 괜찮은 반면, 메모리 사용량이 너무할 정도로 많아서 파이어폭스가 다시 반사 이득을 보는 구도이다. 오페라는 Windows에서는 영 좀 마이너한 콩라인 브라우저가 아닌가 모르겠다.
그리고 무슨 브라우저든지 버전업 숫자 증가폭이 굉장히 커졌으며, 탭 브라우징에  메뉴와 제목 표시줄을 숨겨 놓는 인터페이스가 필수 유행이 돼 있다.

3. 보안 문제

세월이 흐르면서 웹 프로그래밍 환경이 좋아지고 있는 건 사실이지만, 보안 때문에 예전엔 바로 할 수 있었던 일을 지금은 못 하고 뭘 허가를 얻고 번거로운 절차를 거쳐야 하는 건 다소 불편한 점이다.
특히 내가 느끼는 게 뭐냐 하면, 한 HTML 파일에서 자신과 다른 도메인에 있는 CSS나 JS 같은 걸 덥석 인클루드 하는 걸 브라우저가 굉장히 싫어하게 됐다는 점이다. 이런 걸 이용한 보안 취약점 공격이 지금까지 많았는가 보다.

"이 사이트에는 안전한 컨텐츠와 위험한 컨텐츠가 같이 섞여 있습니다. 위험한 것도 모두 표시하시겠습니까?"라는 메시지가 바로 이런 상황에서 뜬다.
IE의 경우 예전에 잘 표시되던 사이트가 갑자기 표시되지 않을 때, 권한 취득을 위해 레지스트리에다 자기 프로그램 이름이나 사이트를 등록하는 등 조치를 취해야 했다.
구글 크롬은 발생 조건이 IE와 동일하지는 않지만, 자체 판단하기에 악성 코드의 실행을 유도하는 걸로 의심되는 지시문이 HTML 소스에 있는 경우, 화면 전체가 위험 경고 질문 화면으로 바뀐다.

최근에는 크롬과 IE에서는 멀쩡하게 보이는 웹 페이지가 파이어폭스에서만 제대로 표시되지 않는 문제가 있어서 회사 업무 차원에서 사이트 디버깅을 한 적이 있었다. 요즘 세상이 무슨 세상인데 웹 표준이나 렌더링 엔진의 버그 때문일 리는 없고, 파이어폭스가 자바스크립트 엔진으로 하여금 외부 도메인로부터 인클루드된 CSS 속성에 접근하는 걸 허용하지 않아서 발생한 문제였다.

4. 파일 관리가 되는 게시판

본인도 여느 프로그래머와 마찬가지로 다니는 회사에서 요즘 모바일에 웹까지 별별 걸 다 손대며 지냈다. 하긴, 공학 박사라 해도 취업 후에는 돈 되는 분야, 뜨는 분야를 따라 자기 주전공 연구 분야가 아닌 것도 손대 봐야 할 텐데 하물며 그보다 급이 낮은 단순 엔지니어들은 말이 필요하지 않을 것이다.

요즘은 게시판이나 블로그 엔진을 만들려면 단순무식한 텍스트 기본 폼이 아니라 위지윅 웹 에디터가 필수이다. ckeditor 컴포넌트에다가 이미지 업로드 기능을 연결해 넣을 일이 있었는데 이것도 여간 골치아픈 일이 아니라는 걸 작업을 하면 할수록 깨닫게 됐다.
손이 정말 많이 간다. 하지만 그걸 일일이 하지 않으면 이미지는 단순 외부 링크밖에 못 넣는 반쪽짜리가 된다.

이미지 파일이 하나 HTTP 규격대로 업로드되어 왔으면 서버 측에서는(PHP든 JSP든 무엇이든) 파일 크기가 적당한지(개별 파일 크기와 지금까지 업로드된 파일의 전체 크기 모두) 체크하여 적당하다면 이름을 중복 없는 랜덤 이름으로 바꿔서 서버에 저장한다. 이름에 한글이 들어간 파일이라고 업로드나 로딩이 제대로 안 되는 일이 없어야 하니까.

그 뒤에 그 그림을 불러올 수 있는 URL을 에디터 컴포넌트에다가 알려 준다. 이것도 간단하게 만들자면 그냥 서버의 특정 디렉터리를 그대로 노출하는 식으로 만들면 되겠지만 보안상 위험하니 가능한 한 제3의 장소에서 파일을 되돌리는 서버 프로그램 URL을 주는 게 안전하다.

위지윅 에디터에서는 임의의 개수의 파일이 업로드될 수 있기 때문에 글에 얽힌 첨부 파일들을 따로 디렉터리나 DB 형태로 관리해서 글이 삭제될 때 같이 지워지게 해야 한다.
사실, 이쪽으로 조금만 더 신경 쓰면 글별로 아예 첨부 파일 관리자라도 간단한 형태로 만들어야 하게 된다. 우와..;;

그리고 골때리는 건, 아직 작성 중이고 정식으로 등록하기 전의 임시 상태인 글에 첨부된 그림들을 처리하는 방식이다.
일단은 그림들이 임시 폴더에다가 올라가고 주소도 임시 폴더 기준이지만 글이 정식으로 등록됐다면 글 중에 삽입된 이미지들의 주소를 수동으로 바꿔야 하고 파일도 옮겨야 한다.
또한 그 상태로 글이 더 등록되지 않고 사용자가 back을 눌렀다면, 서버에 올라왔던 임시 파일들도 나중에 지워 줘야 한다. 이런 것까지 도대체 어떻게 다 구현하지?

이건 일게 위지윅 에디터 컴포넌트가 감당할 수 있는 수준이 아니기 때문에 그걸 블로그 엔진이나 게시판에다 붙여 쓰는 웹 프로그래머가 자기 서버의 사정에 맞게 세팅을 해야 한다.
겨우 이미지 업로드 기능 하나만 달랑 구현하는 테크닉을 소개한 블로그만으로는 정보가 너무 부족했다.
Windows에서 공용 컨트롤에다 드래그 드롭을 처음부터 직접 구현하는 것만큼이나 손이 많이 갔다. 나 같은 이 바닥 초짜로서는 그저 경악스러울 뿐.

프로그램의 완성도를 더 높이려면, 사용자가 곱게 이미지 파일만 올리는 게 아니라 php나 html 같은 보안상 위험한 파일을 올리는 건 아닌지 감시해야 한다. 첨부 파일 정도가 아니라 위지윅 웹 에디터 자체도 위험하다고 그런다. HTML이 근본적으로 문서와 코드가 뒤섞인 형태이다 보니 정말 매크로가 잔뜩 든 Office 문서처럼 취급되는가 보다.
아무튼, 나모 웹에디터와 제로보드가 뜨던 시절에 비해 요즘 웹은 너무 방대하고 복잡하다.

Posted by 사무엘

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

요런 개념을 표로 일목요연하게 한 번쯤 정리할 필요를 예전부터 느꼈던지라, 잠시 짬을 내어 만들었다. C++ 프로그래머라면 고개가 절로 끄덕여질 내용 되겠다. 

토큰 type 앞 type 뒤 value 앞 양 value 사이 value 뒤
&   참조자형 명시 address-of (L-value만) 비트 AND  
&&   R-value 참조자형 명시   논리 AND  
*   포인터형 명시 배열 및 포인터 역참조 곱셈  
±     양/음 부호 덧셈/뺄셈  
괄호() (1) 형변환(typecast)
(2) 타입 선언자 나열 순서 조절
함수형 명시 연산 순서 조절   함수 호출
대괄호[]   배열형 명시     배열 참조
부등호<>   템플릿 인자 명시   비교 템플릿 함수 인자
콤마,       (1) 콤마 연산
(2) 함수 호출/템플릿 인자 구분
(3) 변수 선언 구분
 

  • 괄호는 영어에서 접속사도 되고 지시형용사/지시대명사, 관계대명사까지 다 되는 that만큼이나 정말 다재다능한 물건이다.
  • 베이직은 이례적으로 =, ()가 쓰임이 중첩되어 있다. =가 대입과 동등 연산을 모두 담당하며, ()가 함수 호출과 배열 첨자를 모두 담당한다. 함수 호출은 문법적으로 매우 제한된 문맥에서만 허용되니, C/C++같은 함수 포인터가 존재하지 않는다면 () 중첩이 아주 불가능하지는 않은 듯하다.
  • C/C++은 @ $ ` 기호를 전혀 사용하지 않는다. 예전에 베이직은 각종 기괴한 기호들을 이용하여 변수의 자료형을 표현하곤 했다. A$는 문자열, A%는 정수, A#은 실수 같은 식이다.
  • 파스칼은 포인터형을 선언하는 토큰이 ^인데, C/C++와는 달리, 포인터형을 나타낼 때와 포인터를 역참조할 때 토큰이 등장하는 위치가 서로 다르다. ^Type 그리고 Value^ 이런 식.
  • int *a와 int a[5]배열에 대해서는 똑같이 *a를 쓸 수 있지만, 잘 알다시피 배열의 역참조와 포인터의 역참조는 개념적으로 다르다. C/C++을 처음 공부하는 초보자가 굉장히 혼동할 수도 있을 듯하다.
  • 포인터는 다중 포인터가 존재할 수 있고 역참조도 여러 단계를 연달아 할 수 있다. 그러니 *가 여러 개 연달아 올 수 있다. 그 반면, 참조자는 구조적으로 딱 한 번만 참조/역참조가 가능하게 만들어진 포인터의 축소판이다. 그렇기 때문에 &&에다가 별도로 R-value 참조자라는 물건을 집어넣을 수도 있다. 이걸 생각해 낸 고안자는 정말 천재다.
  • 일반적으로 &는 address-of 연산자이며 R-value를 상대로는 적용이 되지 않는다.
    그러나 일부 값은 L-value가 아님에도 불구하고 & 연산자의 적용이 가능하며, 심지어 a와 &a가 동일하게 취급되는 것도 있는데, 바로 static 배열과 일반 함수이다.
    기본적으로 포인터의 성격을 갖추고 있는지라 &를 안 해도 기본적으로 자신의 주소가 되돌아오고, &를 붙여도 무방하다는 오묘한 특징이 있다.
  • 한편, C/C++에서 배열은 고유한 자료형임에도 불구하고 함수의 인자로 전달되거나 리턴값이 될 때는 그냥 포인터든 배열의 포인터든 포인터의 형태로만 전달된다. 배열 그 자체가 전달되지는 못한다.
    배열을 생으로 함수를 통해 주고 받으려면 구조체에다가 배열을 집어넣어야 한다.

Posted by 사무엘

2015/01/02 19:39 2015/01/02 19:39
,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1046

EXE와 DLL의 경계

1.
프로그래밍을 하다 보면 단독 실행이 가능한 EXE 형태의 프로그램만 만드는 게 아니라, 다른 프로그램에 부속물로 붙거나 여러 프로그램들 사이에서 공유되는 라이브러리, 플러그 인 같은 걸 만들 때가 있다.
플러그 인 정도야 호스트 프로그램이라도 분명하게 존재하니 양반이지만, 임의의 프로토콜을 갖는 공용 라이브러리는 static LIB이든 DLL이든, 그 자체로 단독 실행이 가능하지 않다. 그렇다 보니 그 라이브러리를 사용하는 프로그램을 또 별도로 만들어야 해서 테스트와 디버깅이 여러 모로 불편하다.

그래서 Windows에서 DLL을 만드는 솔루션의 경우, 그 솔루션에다가 DLL을 테스트하는 간단한 EXE도 프로젝트로 따로 만드는 게 보통이다.
Visual C++은 지난 2005부터인가 프로젝트를 새로 생성할 때, 솔루션 디렉터리 아래에 동명의 프로젝트 디렉터리가 한 단계 더 생기고, 한 솔루션에 소속된 프로젝트들의 생성물은 다 동일한 output 디렉터리에 만들어지도록 기본 동작 방식이 바뀌었다. obj 같은 임시 파일들만이 프로젝트별로 자기 고유한 위치에 생성된다. 이것은 나름 바람직한 조치라 여겨진다.

2003 이하 2005 이상
프로젝트1\Release\프로젝트1.exe
프로젝트1\Release\프로젝트1.obj
프로젝트1\프로젝트2\Release\프로젝트2.dll
프로젝트1\프로젝트2\Release\프로젝트2.obj
솔루션\Release\프로젝트1.exe
솔루션\Release\프로젝트2.dll
솔루션\프로젝트1\Release\프로젝트1.obj
솔루션\프로젝트2\Release\프로젝트2.obj

그런데, 발상을 전환하면 DLL을 생성하는 소스를 기반으로 곧바로 EXE를 만들어 DLL의 함수들을 의외로 굉장히 간편하게 테스트를 할 수 있다.
링커의 SUBSYSTEM 옵션 하나만 바꿈으로써 WinMain을 사용하는 GUI 프로그램과 main을 사용하는 콘솔 프로그램을 곧바로 전환할 수 있듯, EXE와 DLL은 똑같이 PE 헤더가 있는 실행 파일이며 본질적인 차이가 거의 없다. 구조체 필드 값이 일부 차이가 나고 entry point에서 같이 전달되는 인자의 타입이 다를 뿐이다.

DLL 프로젝트에서 configuration을 하나 만든다. 테스트와 디버그가 목적이므로 Debug 빌드 것을 초기값으로 가져오면 되겠다. configuration 이름은 Debug EXE 정도로 하자.
그 뒤 프로젝트 속성의 General (일반)으로 가서 Target Extension (대상 확장명)은 .dll이던 것을 당연히 .exe로 바꾼다.
그리고 제일 중요한 Configuration Type (구성 형식)을 Dynamic Library (.dll)이던 것을 Application (.exe)으로 바꾼다.

'확인'을 누른 뒤, DLL 소스의 한구석엔 원래의 DLL엔 없던 WinMain 내지 main 함수를 추가하고, 그 안에다 호출하고 싶은 DLL 클래스/함수들을 마음껏 사용하며 테스트한다.
이것만 해 주면 끝이다. 프로젝트를 이 configuration대로 빌드해서 돌리면 된다.

별도의 EXE를 따로 만들어서 테스트를 하는 거라면 그 EXE에 또 테스트 대상 DLL을 로딩하는 코드가 추가되어야 하지만 DLL 자체의 소스로부터 EXE를 생성하면 그런 번거로운 절차가 필요하지 않으니 더욱 좋다. EXE 자체에 DLL의 코드가 그대로 포함되기 때문이다.

static LIB을 만드는 프로젝트도 이런 식으로 별도의 EXE 생성 configuration을 만들어서 테스트가 가능할 것이다.
다만 DLL/EXE와는 달리 static LIB는 링크 절차가 존재하지 않고 그냥 컴파일만 가능하면 라이브러리 파일이 만들어지기 때문에 이로부터 온전한 EXE를 만들려면 추가적인 링커 설정 같은 게 필요할 것으로 보인다.

2.
여담이다만 DLL뿐만 아니라 EXE도 DLL처럼 export 심벌을 가질 수 있으며 그걸 GetProcAddress를 통해 얻어 올 수 있다.
EXE만 자신이 로딩한 플러그 인 DLL로부터 함수를 얻어 오는 게 아니라, DLL 역시 자신을 로드한 EXE로부터
GetProcAddress( GetModuleHandle(NULL), "GetHostInfo") 이런 식으로 코드를 얻을 수 있다. 이것도 참 기발한 발상이 아닐 수 없다. 어디 활용할 데가 없을까?

내가 개인적으로 굉장히 놀란 것은, 저렇게 한 프로세스 공간의 주인 역할을 하는 EXE가 아니라..
완전히 다른 EXE를 로딩해서 거기에 있는 코드를 실행하는 것도 가능하다는 것이다. EXE는 보통 0x400000 같은 고정된 주소에 로드되며 재배치 정보가 존재하지 않기 때문에 자기 위치에 로드가 못 되면 로딩이 실패한다.

그런데 자신과 로드 주소가 겹치는 EXE도 LoadLibrary를 하면 일단 작업이 성공하며 리소스 추출뿐만이 아니라 GetProcAddress도 실행 가능한 듯하다. 이쯤 되면 EXE와 DLL의 경계가 어찌 되는지가 궁금해진다.

3.
아무 중간 계층 없이 C/C++ 언어만으로 뭔가 라이브러리를 남에게 제공하는 건 애로사항이 적지 않다.

  • 디버그 or 릴리스?
  • 32 or 64비트?
  • 최종 형태는 DLL or LIB?
  • VC++ 어느 버전? (보안 기능 링크 에러)
  • 사용하는 CRT의 형태는 DLL or static?

이런 식으로 상호 일치해야 하는 변수가 급격히 늘어나기 때문이다. 조건부 컴파일이 괜히 필요했던 게 아니다.
C/C++ 런타임 라이브러리도 비주얼 C++의 버전이 바뀜에 따라 내부적으로 야금야금 더해지고 바뀌는 기능이 있기 때문에--특히 보안 관련-- static 링크하는 경우 빌드 툴의 버전이 안 맞으면 이상한 심벌명에서 링크 에러가 나고 각종 문제가 생기기 쉽다.

그나마 같은 비주얼 C++끼리이니까 망정이지 서로 다른 컴파일러끼리 C++ 클래스 라이브러리를 공유한다면 name decoration까지 문제가 됐을 것이다. 사실상 공유 불가능이다.
옛날에는 문자 집합의 크기(일명 유니코드/ANSI)조차도 변수가 따로 있었을 정도이지만 요즘은 그래도 유니코드, 정확히는 wide string만 고려하면 되니 그건 그나마 나아졌다.

이 문제가 워낙 복잡하니..
일차적으로는 COM 같은 바이너리 표준이 나왔을 것이다.
아니면 그냥 소스 코드를 통째로 넘겨줘서 필요한 사람이 알아서 빌드해서 쓰게 하든가. 그 라이브러리가 애초부터 오픈소스 진영의 작품이라면 다행이지만, 상업용 코드라면 인터페이스 부분을 제외한 나머지에다가는 난독화 처리가 필요할 것이다.

그것도 싫으면 저런 골치아픈 요소들을 싹 잊어버리고 자바/C# 같은 바이트코드 기반으로 가는 수밖에 없는데... 그건 물론 성능은 COM보다도 엄청나게 더 희생시킨 귀결일 것이다.
그래도 아무 클래스에나 public static void Main만 있으면 그게 곧 실행 가능한 물건이고 빌드 속도도 안드로메다 급으로 빠르며 골치 아픈 32/64비트 구분 같은 것도 없는 환경이.. C++ 프로그래머로서 참 부럽게 느껴질 때가 있다.

Posted by 사무엘

2014/12/31 08:30 2014/12/31 08:30
, , , ,
Response
No Trackback , 10 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1045

PL(프로그래밍 언어)계에서 함수형 프로그래밍 언어는
자동차 엔진으로 치면 로터리 엔진, 발전소 업계로 치면 핵융합 발전 같은 뭔가 이상은 높지만 현실은 아직 좀 시궁창인 그런 떡밥스러운 영역으로 간주되는 것 같다.

전산학을 전공해서 PL 수업을 들은 분이라면 이미 잘 아시겠지만, 프로그래밍 언어란 크게 절차형과 선언형으로 나눌 수 있다.
절차형은 튜링 기계라는 컴퓨터의 특성을 그대로 반영하여 메모리로부터 값을 읽은 뒤 연산을 수행해서 값을 변경하고, 메모리 위치도 바꾸는 절차를 순차적으로 일일이.. 즉 HOW 위주로 기술하는 언어이다. 정보 올림피아드 경시부가 다루는 것은 응당 절차형 프로그래밍 언어를 활용하여 프로그램을 작성해서 문제를 해결하는 능력이다.

(덧붙이자면 튜링 기계에다가 데이터뿐만 아니라 코드, 즉 상태 변환 로직까지 동일한 메모리에다 올려서 해석하는 계산 모델이 바로 오늘날 컴퓨터의 근간이 된 프로그램 내장형, 즉 폰 노이만 모델이다. 자동차 엔진로 치면 정말 외연 기관에서 흡입-압축-폭발-배기 4행정 내연 기관으로 변모한 수준의 발전이 아닌가 싶다.)

한편, 선언형은 우리가 원하는 솔루션의 정의 내지 조건이 이러하다.. 라고만 써 주는 형태의 WHAT 지향형 언어이다. 그러면 컴퓨터가 알아서 문제를 풀어 낸다.
따지고 보면 데이터베이스 질의어인 SQL은 DLL, EXE 같은 실행 파일을 만드는 용도의 언어가 아닐 뿐이지 아주 대표적인 선언형 언어이다. 복잡한 DB에서는 질의어를 만드는 것도 굉장히 복잡한 일이 되며, 두 DB간의 JOIN은 어떻게 시키고 어느 구문부터 풀이해서 최적의 성능으로 질의를 수행할지 결정하는 것도 아주 어려운 축에 든다. 이런 거 성능 소요 시간을 몇 % 단축시키는 알고리즘을 개발해 내면, DB를 연구하는 전산학과 대학원 연구실에서는 그게 곧바로 논문감이 된다.

흔히 인공지능 문제 풀이형 언어로 알려져 있는 프롤로그도 선언형 언어이다. 이건 진짜 여러 변수들을 선언한 뒤 변수들간의 인과관계를 쭈욱 나열해 주면 이를 바탕으로 언어 런타임이 문제의 답을 찾아 낸다.

까놓고 말해 절차형 프로그래밍 언어로 "아인슈타인의 퍼즐" 같은 걸 풀려면, 프로그래머가 재귀호출에 각종 백트래킹 알고리즘을 직접 구사해야 하니 앞에서 말했던 정보 올림피아드 경시부 급의 기술이 필요하다. 그러나 프롤로그에서는 "영국인.집 = 빨강, 스웨덴.애완동물 = 개" 이런 식으로 단서만 주어진 규칙대로 쓴 뒤 쿼리를 날리면 금붕어를 기르는 사람의 국적을 구할 수 있다.

아마 네모네모 로직이나 스도쿠 같은 것도 해답이 갖춰야 할 조건을 명시하는 것만으로 바로 풀 수 있지 싶다. 단서들을 바탕으로 뺑뺑이를 돌리는 추론 과정은 언어 런타임 내지 엔진이 해 준다.
대학 학부 시절, OR개론 수업 때 잠시 접했던 선형계획법 문제 풀이 프로그램인 k-opt도 역시 지정된 문법에 따라 변수와 부등식들을 써 놓고 최소화/최대화 조건을 명시하면 프로그램이 해를 찾아 주니.. 일종의 선언형 프로그래밍 언어 런타임에 속한다고 할 수 있겠다.

그러니, 절차형 언어의 컴파일러는 최적화를 하는 게 기계어 코드 생성이나 멀티코어 병렬화 같은 아주 미시적인 것과 관계가 있는 반면, 선언형 언어의 수행 방식을 최적화하는 것은.. 거시적인 알고리즘 전략까지 결정해야 하니 더욱 까다로울 것이다. 미시적인 건 해당 언어 엔진이 아주 정교하게 구현되어 있지 않은 이상 신경 쓰기 힘들다.

앞서 언급한 SQL이나 프롤로그는 선언형 프로그래밍 언어 중에서 일종의 '논리 지향'인 물건들이다. 그런데 선언형의 하위 범주로는 '함수 지향', 함수형 프로그래밍 언어라는 게 있다. 이게 절차형보다는 좀 더 수학자스러운 형태로 컴퓨터를 부려먹는 방법을 기술하는 방법론이라고 한다. (함수형이 여느 절차형 프로그래밍과 계산 능력이 동등하다는 것은 튜링 기계와 람다 대수가 동치라는 것이 증명됨으로써 알려져 있다.)

순수한 함수형 프로그래밍 언어에서는 지저분한 대입 연산이 없고 한번 생성된 값은 변경 없이 계속 그 값으로 남아 있다. 새로운 값이 계속 생성될 뿐이다. 사실 문자열을 이런 사고방식으로 처리하는 라이브러리나 언어, 프레임워크에서는 이미 있는 문자열을 변경하는 게 굉장히 어렵기도 하다. Windows RT의 String 클래스도 그랬던 걸로 기억..

함수형 언어에서는 대입이 없으니 응당 뺑뺑이 loop도 있을 수 없다. loop을 대신하는 것은 재귀호출이다! loop조차도 기존 값을 계속 바꾸는 게 아니라 새로운 값을 자꾸 만들어 내는 방식으로 구현된다는 뜻이다.
처음에 해답의 범위가 0부터 100 사이에 있었다면 그 다음 턴에는 0부터 50 (log n 시간 복잡도), 혹은 0부터 99로 자가호출이 이뤄지고, 이것이 문제가 완전히 해결될 때까지 반복된다. 왜냐하면 이 문제의 솔루션이 바로 그런 형태로 귀납적으로 정의돼 있기 때문이다. 팩토리얼이든, 두 수의 최대공약수이든, 정렬이든 다른 무엇이든.

이 패러다임에서는 함수가 다른 여느 데이터와 완전히 동일한 수준으로 다른 함수의 인자가 될 수 있고, 특히 이름 없이 함수의 몸체만을 여느 값처럼 달랑 전해 줄 수 있고, 다른 함수로부터 합성되고 유도된 새로운 함수가 함수의 리턴값이 될 수 있다. 새로운 함수가 동작하는 데 필요한 주변 문맥은 클로저라는 물건이 알아서 다 처리해 준다.
C/C++의 함수 포인터에 머리가 굳은 프로그래머라면 calling convension은 무엇인지, this 포인터가 포함된 멤버 함수인지, pointer-to-member라면 다중 상속으로 인한 부가 오버헤드는 없는지 같은 디테일 때문에 머리가 복잡해질 것이다.

함수형 언어에서 if문은 응당 자기 자신도 조건이 만족하는 쪽의 값을 되돌리는 함수 형태이다.
그러나 if는 조건이 만족하는 쪽만 '계산'이 행해질 터이니 if(a) b; else c; 를 나타내는 if(a, b, c)는 통상적인 함수 호출 func(a, b, c)와 의미상으로 완전히 동일할 수는 없다. 예약어로 따로 해석되고 취급을 받아야 할 듯하다.

물론 이런 함수형 프로그래밍 언어가 구현되기 위해서는 현실에서 컴파일러가 최적화해 줘야 하는 것, 그리고 언어 런타임이 해 줘야 하는 오버헤드가 적지 않다. 끝없이 새로운 값을 생성해 내더라도 이제 참조가 끝나서 더 쓰이지 않는 값은 GC가 알아서 제거해 줘야 하고, 재귀호출, 특히 tail recursion 정도는 알아서 메모리 복잡도를 O(n) 급으로 늘리지 않는 일반적인 loop처럼 돌아가게 컴파일러나 런타임이 최적화를 해 줘야 한다. 함수를 값처럼 부드럽게 다루는 것도 기술적으로는 단순 함수 포인터 이상의 추상화 계층이 필요하며, 말처럼 쉬운 일이 아니다.

예를 들어.. X라는 함수가 있는데.. 얘는 a라는 인자를 받고는,
b라는 인자를 받아서 a에다가 b를 더한 값을 되돌리는 Y라는 함수를 되돌린다고 치자.
결국 Y는 X라는 함수가 호출될 때 전달되었던 매개변수 내지 그때 생성된 X 내부의 지역 변수에 의존하여 동작하는데..
나중에 Y가 단독으로 호출될 때는 X라는 함수는 실행이 끝나고 그 문맥이 존재하지 않는다. 이를 어찌하리?
이런 딜레마를 피하기 위해 C/C++ 언어는 애초에 함수 안에 함수를 만드는 걸 지원하지 않는 쪽으로 설계되었으며, C++의 functor 같은 것도 전부 자기가 자체적으로 데이터 멤버를 갖고 있는 객체 형태로 만들어지게 된 것이다.

또한, 아무리 대입이 사이드 이펙트가 남는 지저분하고 기피되어야 하는 연산이고.. 다른 모든 연산을 loop 대신 재귀호출로 때운다 해도.. 당장 외부 파일/키보드로부터의 input은.. 대입 연산 없이는 감당이 도저히 불가능하다. 그리고
return t1.len() > t2.len() ? t1: t2
처럼 그 재귀호출의 결과값을 취사 선택하는 간단한 판단을 위해서라도 임시 변수에 대입하는 것 정도는 근본적으로 전혀 없을 수가 없다.
어디 그 뿐이랴. 대용량의 단일 데이터를 대상으로 여러 함수들이 포인터만 주고받으며 동작하다 보면, 한 함수가 자기 argument 안에 입출력 대상인 모든 데이터를 집어넣는 것은 무리가 있다.

허나 함수형 프로그래밍이 성능면에서 불리한 요소만 있는 건 아니다. 대입으로 인한 side effect 같은 게 없으니 소스 코드의 정적 분석은 더 용이할 것이고 병렬화 등 입맛에 맞는 최적화에도 더 유리할 것이다. 애초에 선언형 프로그래밍 언어는 구체적인 실행 방식은 그런 똑똑한 컴파일러나 언어 엔진에게 맡기고 있으니까.
이러니 PL 분야를 연구하는 전산학자나 수학 덕후들이 함수형 프로그래밍 언어에 더욱 열광하는 듯하다. 저런 패러다임이 응집도· 결합도 같은 소프트웨어 공학적인 측면에서 더 깔끔한 코드를 만드는 데 도움이 되는 것은 덤이다.

대학교 전산학과에서는 함수형 프로그래밍 언어로 보통 Scheme을 실습하는 편이다. 본인도 먼 옛날 학부 시절에 '프로그래밍의 이해(PP)'라는 과목을 통해 그 물건을 접했으며, 그걸로 무슨 다항식의 곱셈을 하는 프로그램도 숙제로 만들고 여러 덕질을 했었다. 함수형 언어의 진짜 본좌라고 일컬어지는 Haskell 같은 건 난 모름.;;

여담이지만 지금 생각해 보니, 온갖 복잡한 괄호가 배배 꼬여 있는 Scheme 코드는.. 언어학에서 문장 구문 분석을 괄호로 표현해 놓은 것과 사뭇 닮았다는 생각이 들었다. (S (NP .. ) (VP ...)) 이러는 식.
Schme에서는 S 대신에 define, lambda, if 따위가 있을 것이다.

물론 그때는 본인은 <날개셋> 한글 입력기 개발에 도움이 안 되는 건 진짜 생까고 무시하던 시절이어서 그 코스의 의미를 제대로 이해를 못 했다. 왜 괜히 계산 과정을 이 따위로 어색하게 표기를 하는지..??
그건 사칙연산 같은 기초적인 연산자조차도 통상적인 표기법이나 우선순위를 깡그리 무시하고 정말로 오로지 함수 위주로.. 프로그래밍이, 아니 계산(computing)이라는 작업 자체를 몽땅 주어진 규칙대로 피연산자들을 처리해서 reduce하는 과정이라고 극도로 추상화한 귀결일 것이다. 일종의 발상의 전환인 것이다. car, cdr 명령이 튜링 기계로 치면 메모리 셀을 이동하고 값을 읽는 동작에 해당할 것이다.

단, Scheme도 마냥 순수 함수형 언어이기만 한 것은 아니다. 필요한 경우 대입 연산이 있을 수 있고 일부 절차형 패러다임 구문을 집어넣을 수도 있다. 마치 C#에서 부분적으로 unsafe, unmanaged 코드를 집어넣듯이 말이다.
그리고 반대로 C++ 역시, 기본적으로 객체지향 패러다임을 주류로 내세운 절차형 언어이지만 최근에는 함수형 프로그래밍 패러다임도 받아들여서 람다 함수를 기존 함수 포인터나 functor의 대용으로 쓸 수 있게 되었듯이.. 요즘 언어들의 대세는 자기 정체성은 유지하면서 다른 패러다임에서도 유용한 건 적극 받아들이는 것인 듯하다.

과연 함수형 프로그래밍 언어가 그저 대학교 과목에서나 잠깐 접하고 마는 떡밥 내지 PL 분야의 연구자들만 쓰는 도구 수준을 넘어.. 현업에서 적극 즐겨 쓰이는 날이 올지 모르겠다. 지금 현업에서 전혀 안 쓰인다는 말은 아니지만 아직까지는 수학 덕후, 컴덕후들의 전유물이라는 인상이 강한 편이니 말이다. 그래도 한 가지 확실한 건, 함수형 프로그래밍 패러다임을 실현해도 될 정도로 요즘 컴터 환경이 좋아지자, 각종 언어들에도 이 패러다임이 적극 많이 도입되고 이게 유행을 타고 있다는 사실이다.

여담으로, 람다 대수를 고안한 앨론조 처치는 family name이 어째 '교회'다..;; 독실한 신자 가문이기라도 했나 싶은 잡생각이 든다.

그리고 궁금한 게 있는데.. 이름 없는 함수에서 재귀호출을 해야 할 때 함수 자기 자신을 가리키는 this, self 같은 키워드는 없는가 싶다.
이 의문은 C++에서 람다 함수가 추가되었을 때부터 여러 프로그래머들에 의해 제기되어 왔다. 하지만 뾰족한 해결책은 없으며, std::function에다가 자신을 저장한 뒤, 그 변수명을 캡처로 도로 넘겨 줘야만 재귀호출이 가능하다. Scheme 역시 일단 def로 자기 이름을 지은 뒤, 그 이름을 호출해 줘야 된다.

재귀호출을 그렇게도 좋아하는 함수형 언어가

[](int x) { return x<=1 ? 1: this_func_itself(x)*(x-1); }

개념적으로 this_func_itself에 해당하는 키워드 같은 건 정말 없는 건지 신기한 노릇이 아닐 수 없다.

Posted by 사무엘

2014/12/28 08:39 2014/12/28 08:39
, , ,
Response
No Trackback , 9 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1044

1.
우리나라가 옛날보다는 소프트웨어 불법복제에 대한 경각심이 좀 생겼고 불시단속도 강화된 관계로..
기업들 역시 대기업· 중소기업을 막론하고 직원들의 컴퓨터에 혹시 불법복제 소프트웨어가 깔려 있는지 자체 단속을 하는 편이다.
그런데, 그런 소프트웨어 자체를 생산하는 일을 하는 IT 기업에서는 추가적으로 민감한 물건이 더 있다.
자사가 개발하는 제품의 내부에 혹시 오픈소스 솔루션이 예기치 않게 들어가지 않느냐 하는 것이다.

당장 돈을 안 들이고 원하는 문제를 쉽고 빠르게 해결할 수 있다고 해서 출처 명시 없이, 혹은 라이선스가 요구하는 사항을 이행하지 않고 그런 솔루션들을 자기 소스 코드에다 불쑥 집어넣었다가는..
글쎄, 그 회사와 회사 제품이 완전 듣보잡 마이너 내수급이라면 별로 문제되지 않고 넘어갈지 모른다.
그러나 외국으로도 널리 널리 쓰이고 돈을 빗자루로 긁어모으는 인기 프로그램이라면 얼마 못 가 당연히 걸리고 발목 잡히게 된다.

왕년에 병역비리 내지 논문 표절을 저지른 사람이 나중에 고위 공직자가 되려 할 때 청문회에서 탈탈 털리듯이 말이다.
오픈소스를 표방하는 자유 소프트웨어라고 해서 저작권이 전혀 없는 게 아니기 때문이다. 아무나 완전히 제멋대로 고치고 이득을 챙겨도 되는 '인류 공용 자산'급인 public domain이 아니다.

방대한 분량에 복잡한 로직을 가진 소프트웨어는 설령 소스 코드가 있다 해도 누가 함부로 찔끔찔끔 고칠 수 있는 물건이 아니다. 그래서 그런지 설령 컴파일된 바이너리이고 소스 코드가 없다 하더라도, 여기에 어떤 오픈소스 솔루션이 무단으로 포함되었는지 정도는.. 오픈소스 진영에서 매의 눈으로 감시해서 다 적발해 낸다. 컴파일된 바이너리 코드의 패턴 분석을 정교하게 하는 듯.

그래서 그렇게 자기는 이윤을 챙기면서 엔진 내부에 무료 오픈소스를 무단으로 사용한 양심불량 소프트웨어는 hall of shame 같은 데에 올라서 업계에서 국제적으로 망신을 당하며,
GPL 라이선스 급의 오픈소스를 잘못 따다가는 그걸 사용한 프로그램 전체에 대해 소스 공개라는 형벌(?)을 당하게 된다. 실제로 그런 소스 공개형을 당한 사례도 몇 건 있다. 물론, 프로그램 소스만 공개이지 그 소프트웨어 제품에 사용된 각종 데이터나 리소스까지 죄다 공개는 아니기 때문에 완전한 기술 유출은 아니다.

대기업의 경우, 자사 직원들만치 꼼꼼하게 감시하지는 않는 하청업체로부터 납품받은 소스에 이런 지뢰가 들어있어서 골탕을 먹는 경우가 있는 모양이다. 그래서 납품 계약을 할 때부터 그 솔루션에 오픈소스 저작권 문제가 확실하게 없다는 걸 확인시키며, 문제가 생길 경우 이런 책임을 지우겠다는 식으로 얘기를 하는 걸 본인은 업계에서도 경험했다.

여담이지만, 요즘은 응용 언어학에서 다루는 말뭉치(코퍼스)에도 저렇게 저작권 바람이 불고 있다.
그래서 국립 국어원에서도 21세기 세종 계획 성과물로서 세종 말뭉치를 배포하다가.. 저작권이 문제가 되는 데이터를 빼고 분량이 다소 감소한 말뭉치를 새로 배포하기 시작했다. 그 얘기를 들으면서 IT업계의 오픈소스 얘기가 문득 떠올랐다.

2.
요번 학기에 학교에서 오픈소스와 관련된 세미나 수업을 들었다. 오늘날 IT 업계에서 오픈소스라는 트렌드가 차지하는 비중을 더욱 절실히 알 수 있었다. 자유 소프트웨어는 무엇이고 오픈소스 소프트웨어는 무엇인지, 그리고 리눅스 진영과 리처드 스톨먼 진영의 차이는 무엇인지 예전에는 거의 관심이 없었고 아는 게 없었는데 이제 좀 어렴풋이 알게 되었다. 유익했다.

오늘날은 정말로 거의 모든 분야에서 github, sourceforge 같은 오픈소스 진영의 저작물을 이용하지 않고는 실용적인 프로그램을 만들기가 대단히 어렵거나 가성비가 안 맞아 의미가 없는 지경이 되었다. 그 오픈소스 진영에서 활동한 이력은 만천하에 공개된 자기 스펙과 자산이 되기 때문에, 당장 소프트웨어의 무료 공개로 인한 금전 손실 이상의 이득이 돌아온다는 말에는 공감이 됐다.

확실히 IT 업계에서 경력을 쌓는 방식의 패러다임이 바뀌긴 했다. 내 지인 중에서도 이 바닥에서 워낙 유명한 사람이 있다. 그 친구는 이런 수업 따위는 들을 필요가 없거나, 아니면 나처럼 따로 자료 찾으며 과제를 낑낑대며 할 필요조차 없이, 평소에 하던 오덕질을 갖고 학점 따위 그냥 날로 먹는 게 가능했을 것이다.

내게 오픈소스라는 건, 이거 소스 코드 하나쯤은 공개해 버려도 내 밥줄에 아무 지장 없고 그것보다 더 나은 것 정도는 얼마든지 또 만들어 낼 수 있고, 이 프로그램의 UI, 데이터 파일들을 업계 표준 관행으로 굳히는 효과까지 노릴 수 있는 괴수들의 전유물일 뿐인 것 같다. =_=;; 물론 그런 진영에서 기여를 하는 프로그래머들이 매우 고마운 대인배인 건 사실이지만, 일단은 무슨 희생, 헌신, 자선 행위라기보다는 “가진 자의 여유” 구도라는 것이다.

또한, 마냥 다 공개하는 게 능사만은 아닐 텐데.. “경쟁자가 따로 이득을 보기 vs 경쟁자를 원천 견제하고 차단하기”라는 결말의 차이가 어떤 과정에서 생기는지 궁금하다. 가령, IBM은 하드웨어계의 오픈소스라 할 수 있는 PC 규격을 내놓았고 그게 결국 세계를 평정하긴 했지만, 이 때문에 정작 자기는 아무 이득도 못 보고 PC 사업에서 손을 떼게 됐단 말이다.

id 소프트웨어에서도 둠과 퀘이크의 소스를 공개하긴 했지만, 그래도 해당 세대의 기술이 충분히 한물 갈 정도로 굉장히 나중에 공개하지 않았던가. 물론, 엔진을 유료로 판매하기도 하는데 소스를 돈 주고 산 업체들이 손해를 보지 않게 하려고 좀 늦게 공개하는 것도 있긴 하지만 말이다.
오픈소스가 과연 개발자와 사용자가 모두 윈윈 할 수 있는 소프트웨어 생태계로 계속 명맥이 유지되는 게 가능할지 지켜볼 일이다.

아 그리고, 이 모든 사실에도 불구하고 <날개셋> 한글 입력기는 완성품을 복사해서 사용하는 것만 무료이지 여전히 소스 비공개 사유 소프트웨어이다. 물론 오픈소스 저작물을 사용한 것도 최소한 C++ 코드 내부엔 전혀 없다. 거기 들어있는 모든 모듈의 모든 기계어 코드는 한 치의 예외 없는 100% 자작이다.
전세계에 한국어와 한글이 쫙 퍼져 있고 세벌식 자판이 주류 글자판이 돼서 누구나 이런 응용 한글 입력 엔진의 필요를 느끼고 있고 사용자가 왕창 많고 거기에 다른 형태의 돈줄까지 있다면야 내가 거기에 공개적으로 기여를 해서 오픈소스계에 등단(?)할 수도 있겠지만.

시장과 수요 자체가 극도로 마이너한데... 공개해 봤자, 한글 IME를 연구하는 일부 극소수 오덕들에게나 좋은 일을 하게 되고, 내가 노력을 보상받는 길도 없고, 날개셋의 리눅스나 맥 버전을 뚝딱 책임감 있게 만들어 줄 사람이 있는 것도 아닌 와중에..
무작정 오픈소스화는 현실에 맞지 않는다고 여겨진다. 오픈소스 진영이 있다고 해서 모든 프로그래머가 흙 파서 먹고 살 수 있는 건 아니다.  8.0 정도가 나온 뒤부터는 내 프로그램의 장기적인 생존 방법에 대해서도 슬슬 생각을 해 봐야겠다.

3.
저건 나름 학점이 붙은 과목이기 때문에, 세미나만 있는 있는 게 아니라 과제도 있었다. 관심이 가는 오픈소스 프로젝트를 하나 선정해서 그에 대해 조사를 하고, 관심이 있으면 자그마한 오타 수정이라도 직접 하나 contribute를 해 보라는 것.

그래서 평소에 프로그래밍 언어에 대한 관심이 무진장 많으며, 나보고도 줄곧 이것저것 다른 언어를 익혀 보라고 권유를 하던 대한민국 오픈소스 진영의 거물이자 프로그래밍의 천재 T모 군의 도움을 받았다. (IOCCC 입상자 ㅋㅋㅋㅋ) 권유의 대상도 예전부터 파이썬, D, 자바스크립트로 계속 바뀌어 오다가 요즘은 Rust로 정착했다.

Rust는 모질라 재단에서 자체 개발한 새로운 절차형 시스템 프로그래밍 언어이다. Java/C# 같은 언어는 full-time 쓰레기 수집기가 갖춰진 별도의 가상 기계에서 동작하는 반면, 이 언어는 그런 형태의 쓰레기 수집기가 없이 C++과 동급의 네이티브 코드로 컴파일되는 언어이다. 메모리 관리 수준은 걍 Windows RT의 C++/CLI와 비슷한 급이 되겠다.

웹 브라우저 렌더링 엔진을 만드는 단체가 웬 PL을 새로 만들게 되었는지는 나름의 이유가 있다. 렌더링 엔진도 응당 GPU와 멀티코어의 도움을 받아야 할 터인데 여러 페이지들이 자연스럽게 멀티코어를 활용하는 게 아니라 단일 페이지가 멀티코어를 적절히 활용하게 만들자니 C/C++로 만들어진 기존 코드들은 답이 없는 지경이었나 보다.

또한 C/C++은 알다시피 빌드 시간이 매우 길고 컴파일러가 잡아 주지 못하는 메모리 관련 버그에 무방비로 노출되어 있는 등, 대형 프로젝트의 진행에 전통적인 한계와 약점도 적지 않다. 그래서 대형 프로젝트의 개발과 유지에 적당한 새로운 언어를 찾았는데 마땅한 대안이 없자 언어를 직접 고안하게 되었다.

이 언어는 모질라 재단에 소속되어 있던 개발자인 Graydon Hoare가 개인적으로 설계하던 언어를 재단이 인수하여 발전시킨 것이다. 지금은 모질라 재단뿐만 아니라 삼성전자도 Rust의 개발을 후원하고 있다고 한다.
아직도 활발하게 개발되고 있어서 0.12까지 나왔는데.. (12는 소수점이 아니라 그냥 정수. 0.2보다 최신 버전임) 긴 베타 기간을 거친 후, 가까운 미래에 Rust의 1.0 정식 버전이 나올 예정이라 한다.

즉 멀티코어 병렬 처리 편의와 자동 메모리 관리, 그리고 성능을 만족시킬 목적으로 만들어진 언어라는 뜻인데, 구글에서 비슷한 시기에 개발한 Go라는 언어하고도 경쟁 구도인 듯하다. 하지만 둘은 패러다임이 좀 다른 언어이다.

Rust에서 모든 값은 그 값이 대입된 변수나 구조체 필드, 넘겨받은 함수 인자 등의 이름에 귀속된다. 이름은 자기에게 귀속된 값에 대한 소유권을 가지며, 다른 이름으로 값을 대입하면 그 이름으로 소유권이 이전된다. 예를 들어, 다른 언어에서 a = b와 같은 대입 연산은 b의 값을 a로 복사하거나, b가 가리키던 객체를 a도 함께 가리키겠다는 의미를 갖는 데 반해, Rust에서는 b가 가지고 있던 값을 a로 이동시킨다는 의미가 된다. C++로 치면, C++11에서 첫 도입된 R-value reference type과 개념적으로 비슷하다.

만약 함수의 리턴값을 받지 않는다던가 하는 일로 인해 소유권을 넘겨주지 못한 채로 이름이 사라지게 되면, 거기에 귀속되었던 값도 함께 사라지면서 그 값을 담았던 메모리도 해제되게 된다. 프로그래밍 언어 이론의 관점에서 보자면, binding과 object의 생명 주기를 일치시킨 셈이다.

Rust 컴파일러는 컴파일 단계에서 소유권이 어떻게 이전되는지를 모두 추적할 수 있으며, 필요한 메모리 할당 및 해제 코드를 컴파일 중에 정확한 위치에 삽입한다. 이런 방법으로 Rust는 런타임 오버헤드가 없는 안전한 메모리 관리를 이루어 낸다.
일단 대충 이 정도 조사하면서 그쪽 진영에서 뜨고 있는 이슈와 작업 진행 방식들을 조사해 봤다.

Posted by 사무엘

2014/12/14 08:34 2014/12/14 08:34
, , ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1039

1. disabled 스타일 개론

소프트웨어 GUI에서 대화상자나 메뉴 같은 구성요소를 보면, 상태를 나타내는 속성 중에 논리값으로 enabled 여부라는 게 존재한다.
이게 false여서 disable된 물건은 비록 화면에 보이긴 하지만 흐리게 표시되며 완전히 없는 물건으로 취급된다. 사용자의 키보드나 마우스 조작에 반응을 하지 않으며 선택할 수도 없다.

Windows 운영체제에서는 윈도우에서 WS_DISABLED라는 스타일 비트가 이런 역할을 한다. 기본 스타일에 이 비트가 지정되어 있는 윈도우는 키보드 포커스를 받을 수 없으며, 거기에다 대고 마우스 포인터를 움직여도 통상적인 WM_MOUSEMOVE, WM_?BUTTONDOWN 같은 메시지가 오지 않는다.

즉, availability는 어느 정도 운영체제가 직접 관리를 해 주는 예약된 속성이다.
어떤 윈도우가 enabled인지 여부를 알려면 IsWindowEnabled 함수를 호출하면 된다.
IsWindowEnabled(hWnd)는 !(GetWindowLongPtr(hWnd, GWL_STYLE)&WS_DISABLED)와 동치라고 생각하면 된다.

enabled 여부를 설정하는 함수는 EnableWindow이다. 이때 대상 윈도우는 WM_ENABLE라는 메시지를 받음으로써 자신의 availability 속성이 바뀌었다는 통지를 받는다.
Get과 마찬가지로 SetWindowLongPtr를 통해 스타일을 수동으로 바꿔 줘도 거의 같은 효과를 낼 수 있다.
단, 이 방법은 간단히 전용 함수를 호출하는 것보다 번거로우며, 이렇게 속성이 바뀌면 대상 윈도우는 WM_STYLECHANGED만을 받지 WS_DISABLED 비트에 차이가 있더라도 WM_ENABLE 메시지가 오지는 않는다.

2. 화면에 그리기

대화상자 컨트롤이라면 WM_ENABLE 메시지가 왔을 때 자신을 화면에 다시 그리는 처리를 한다.
가령, 평소에는 COLOR_WINDOWTEXT라는 시스템 색상으로 글자를 찍은 반면, disable된 뒤부터는 COLOR_GRAYTEXT 색상으로 글자를 다시 찍는다.

지금이야 Windows 8 때부터 고전 테마라는 게 사라져서 점차 과거의 유물이 돼 가지만..
옛날에 Windows UI를 보면, 메뉴나 도구모음줄에서 사용할 수 없는 항목은 글자가 단순히 회색이 아니라 흰색 위에 회색이 깔려서 뭔가 음각 엠보싱처럼 그려지곤 했다.

사용자 삽입 이미지

그거 처리를 해서 disable 상태를 화면에 표현하는 건 DrawState라는 함수 호출 한 방이면 바로 된다. 이건 딱 회색 3D 대화상자 스타일이 도입된 Windows 95와 NT4에서 첫 추가된 함수이다. 게다가 텍스트와 비트맵(아이콘) 모두 그렇게 그리는 걸 지원한다.

비트맵의 경우, 마스크 비트맵을 바탕으로 엠보싱을 만들며, 이 테크닉은 지금도 그대로 쓰인다. 그렇기 때문에 요즘은 32비트 비트맵 내부의 알파 채널이 투명색을 대신하는 지경이 되었음에도 불구하고 DrawState 함수로 disable 상태의 엠보싱 아이콘을 그리려면 비트맵에 모노크롬 흑백 배경 마스크 비트맵도 넣어 줘야 한다.
뭐, 궁극적으로는 트루컬러 아이콘이라면 구시대스러운 비트 연산이 아니라, 투명도를 높이고 채도를 낮춰서 그림을 더 엷고 탁하게 만드는 '현대적인' 방식으로 disable 상태를 그려야 하겠지만 말이다.

3. disabled 윈도우의 또 다른 용도

그러면 이런 disabled 속성은 오로지 대화상자 내부의 컨트롤 같은 WS_CHILD급 윈도우에서만 쓰이는가 하면 그렇지 않다. 타 윈도우의 자식이 아니라 top-level이 될 수 있는 WS_POPUP급 윈도우도 WS_DISABLED 속성을 줘서 생성할 수 있다. 이 윈도우는 화면이 달랑 떠 있기만 하지 사용자가 포커스를 줘서 키보드 입력을 할 수가 없게 된다.

사실, 한 프로세스 안에서 child 윈도우는 클릭했다고 해서 딱히 포커스가 자동으로 거기로 옮겨지지는 않는다. 포커스가 가는 건 해당 윈도우가 WM_LBUTTONDOWN이 왔을 때 SetFocus를 자체적으로 호출했기 때문이다.
그러나 소속된 프로세스가 다른 top-level 윈도우의 경우, 클릭하면 일단 그 창으로 WM_ACTIVATE 메시지가 가고 컨텍스트 전환이 발생한다. 이것은 프로그램의 의사와 무관하게 운영체제가 일방적으로 하는 일이었는데, disabled 윈도우는 그걸 막을 수 있다.

물론 disabled 윈도우는 앞서 말했듯이 정상적인 마우스 메시지가 오지 않는데, 그래도 이것도 받는 방법이 있다.
disabled라고 해도 top-level 윈도우에는 기본적으로 마우스 포인터 설정을 위해서 WM_SETCURSOR 메시지 정도는 온다. 이 메시지의 lParam에는 이 윈도우에 원래 오려고 한 메시지가 담겨 있기 때문에 이를 토대로 비록 disable 상태이지만 마우스 동작에 반응을 하도록 프로그래밍이 얼마든지 가능하다. child 윈도우가 아닌 top-level 윈도우이기 때문에 이런 메시지가 온다.

disabled 편법을 쓰지 않고 '클릭해도 반응만 하지 포커스가 바뀌지는 않는 간단한 윈도우'라는 개념은 비교적 늦게 등장했다. Windows 2000부터 WS_EX_NOACTIVATE라는 확장 스타일이 정식으로 도입된 것이다. 이런 윈도우는 ShowWindow에다가도 단순히 SW_SHOW가 아니라 SW_SHOWNOACTIVATE를 줘야 한다.

4. 상태 변경과 관련된 연계 작업들

대화상자에서 컨트롤을 enable/disable시키는 상황은 크게 선천적인 것과 후천적인 것으로 나뉜다. 전자는 WM_INITDIALOG에서 단 한 번 enable 여부가 결정되고 그 뒤에 대화상자가 닫힐 때까지 그 상태가 바뀔 일이 없는 경우를 말한다.
후자는 한 컨트롤의 조작 여부나 값에 따라 다른 컨트롤의 enable 상태가 인터랙티브하게 수시로 바뀌는 경우를 가리킨다. 예를 들어 라디오 버튼이 특정 항목으로 맞춰져 있을 때만 조건부로 사용 가능해지는 하부 조건 체크박스 같은 것. UI 프로그래밍을 해 본 분이라면 이 분류가 수긍이 갈 것이다.

그런데 이렇게 컨트롤들의 상태를 바꾸는 건, 단순히 한 윈도우에 대해 EnableWindow를 호출하는 것 이상으로 이와 결합된 반복 패턴이 여럿 존재한다.

(1) 첫째, 대상 컨트롤의 이전에 있는 label 컨트롤을 같이 enable/disable시키는 경우이다. 대상 컨트롤이 버튼이라면--push, check, radio 모두 포함-- 그 자체가 &로 시작하는 Alt 액셀러레이터 글쇠를 갖는다. 그러나 나머지 edit, list, combo 박스 같은 것들은 자신의 액셀러레이터가 없으며 그 이전의 static 라벨로부터 액셀러레이터를 넘겨받는 형태이다.

따라서 그런 컨트롤이 disable됐다면 자기의 앞의 컨트롤도 같이 disable되는 게 이치에 맞다. 앞의 단순 label은 보통 독자적인 컨트롤 ID가 없이 그냥 IDC_STATIC인 경우도 많으므로 핸들값을 GetWindow(hCtrl, GW_HWNDPREV)로 얻어 오는 수밖에 없다.

(2) 둘째, 대상 컨트롤을 화면에서 감출 때에도 ShowWindow(hCtrl, SW_HIDE)만 할 게 아니라 disable을 시켜 줘야 한다. 왜냐 하면 enable 상태인 컨트롤은 비록 화면에 없더라도 Alt 액셀러레이터에는 반응을 해서 사용자가 여전히 기능 접근이 가능하기 때문이다.
본인은 개인적으로는 이게 바람직한 설계가 아니라고 생각하며, Windows가 왜 그렇게 동작하는지 알지 못한다. 하지만 어쨌든 Windows 95부터 8.1에 이르기까지, 조건부로 컨트롤들을 보였다가 숨겼다가 하는 UI의 경우, 감춰지는 컨트롤은 disable도 시켜 줘야 한다.

(3) 셋째, 지금 키보드 포커스를 받고 있는 컨트롤이 disable되는 경우, 포커스를 자신의 다음 컨트롤로 옮기는 일을 해당 프로그램이 수동으로 해 줘야 한다. 이걸 안 해 주면 그 컨트롤이 disable된 뒤부터 키보드 상태가 꼬여 버린다.
이 단서의 주된 적용 대상은 push-버튼이다. 대표적인 예로는 프로퍼티 페이지에 있는 '적용' 버튼. 이 버튼을 누른 순간 이 버튼은 사용 불가 상태가 되며, 사용자가 다른 설정을 또 건드려 줘야 다시 사용 가능해지니 말이다.

본인의 개인적인 생각은 이것도 역시 운영체제가 자동으로 처리해 줘야 하는 게 아닌가 싶지만, 현실은 시궁창이다. 생각보다 자비심이 없다..;;
대화상자에서 특정 컨트롤에다 포커스를 주는 건 SetFocus를 해서는 안 되며, 대화상자 부모 윈도우에다가 WM_NEXTDLGCTL 메시지를 보내는 방식으로 해야 한다. 그렇게 해야 대화상자의 default 버튼에 굵은 테두리가 그려지는 처리가 올바르게 된다.

그래서 본인은 위의 세 시나리오를 모두 감안하여 대화상자 컨트롤을 enable/disable시키는 함수를 별도로 만들어서 사용하고 있다.
그림을 좀 더 곁들이면 전반적으로 설명하기가 더 편하겠다는 생각이 드는데.. 귀찮아서 생략한다. ㄱ-

Posted by 사무엘

2014/12/08 08:29 2014/12/08 08:29
, ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1037

윷놀이

1. 오목은 선수(흑돌)가 굉장히 유리한 반면, 윷놀이는 잡히는 것 때문에 선수가 그다지 유리하지 않다는 게 굉장히 흥미로운 차이점이다.
특히 보드에 처음 놓이는 말이 기존 말을 잡는 거는.. FPS로 치면 개념적으로 spawn으로 인한 텔레프래깅과 동일하다.

2. 윷놀이는 자동차가 발명되기 한참 전에 고안된 게임이겠지만 말의 주행 패턴이 자동차 수동변속기와 굉장히 닮은 것 같다.
도-개-걸-윷-모는 1~5단, 빽도는 후진 1단

3. '지옥'은 윷놀이의 랜덤함과 사행성을 더욱 배가시켜 놓은 요소임이 틀림없다.

4. 옛날에.. 한 1992~94년 사이이던가..? 어떤 은행원 아저씨가 윈도 3.1용으로 윷놀이 게임을 만든 게 있어서 했던 기억이 있다. 아마추어 프로그래머일 텐데 파일 구성상으로는 비주얼 베이직은 아니고.. 나름 쌩 Windows API를 써서 C언어로 만들었지 싶다.
나름 자기 아들이 윷을 던지는 동영상까지 들어있었는데.. 그 시절에 그런 개발 환경과 동영상 촬영 장비를 생각하자면 보통 집안은 아니었던 것 같다.

5. 프로그래머의 관점에서 생각해 보자면, 윷놀이 컴터 AI를 만드는 거 생각보다 재미있을 듯하다. 일단 윷말 놓는 방식이 가짓수가 굉장히 많으며, 굽는 것, 다음에 잡힐 확률, 그리고 각 말이 빽도가 걸렸을 때 후진하는 방향 state 같은 것들도 은근히 처리하기 까다롭기 때문이다.
5m + 4n + 3 = 10 or 12 이런 거 어쩌면 선형계획법으로 모델링할 수 있을지도 모른다.
다변수 정수 연립방정식 푸는 건 지뢰찾기를 풀 때도 써먹곤 했는데 어쩌면 그것과 비슷한 논리인지도..;;

* 펜티엄 4 + 윈도 XP 초 구닥다리 똥컴에서는.. 페이스북처럼 자바스크립트가 왕창 돌아가는 페이지는 IE8로는 먹통 수준으로 너무 느려서 도저히 열람을 할 수가 없고..
역시 크롬이 정답이다.

Posted by 사무엘

2014/11/30 08:36 2014/11/30 08:36
,
Response
No Trackback , 3 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1034

1.
비교적 최근에 알게 된 건데..
C/C++에서 default문은 굳이 case의 맨 마지막에 있지 않아도 된다. =_=;;;
그래서 case 1: .. default: ... case 2: 이런 식으로 라벨들이 따라오고 일부 항의 끝에 break까지 생략되어 있다면 생각보다 꽤 기괴한 로직을 구현할 수도 있다.

뭔가 발상의 전환이 느껴진다. 어떻게 활용 가능한지는 더 생각을 해 봐야겠다.
물론 파스칼의 case else문은 그렇지 않으며, 반드시 맨 마지막에 와야 한다.

2.
컴퓨터에서 부동소수점은 연산을 하는 게 까다로워서 하드웨어적인 도움을 진작부터 받아 왔다. 하지만 연산뿐만 아니라 이미 있는 수를 10진법 형태의 문자열로 나타내거나 문자열로부터 역변환하는 것도 생각보다 몹시 어렵다. 에니악 같은 초창기 컴퓨터가 괜히 굉장한 비효율을 감수하고라도 10진법 기반으로 설계되었던 게 아닌가 싶다.

이와 관련된 정보는 printing float numbers 같은 키워드로 구글링을 하면 얻을 수 있다.
이 작업은 어떤 f * 2^e에 대해서 f' * 10^e' <= f * 2^e < (f'+1) * 10^e'가 성립하는 최소의 f'/e'를 찾는 것인데, 결국 컴퓨터 프로세서가 기본 단위로 처리 가능한 범위를 넘는 big number 연산까지 필요할 정도라고 한다.

2진법 부동소수점은 1/2^n이 아닌 사실상 거의 모든 소수들이 순환소수로 표현되어 뒷부분이 잘린다. 0.1, 0.3 이런 소수도 컴퓨터에서 표현되는 형태는 순환소수라는 뜻이다. 순환소수를 화면에 출력할 때는 그래도 10진법 유한소수인 것처럼 표시하는 것이니 컴퓨터에서 부동소수점은 본질적으로 100% 정확한 정밀도가 보장되지 않는 셈이다.

3.
Visual C++ 201x는 200x에 비해서 매우 강력해진 인텔리센스, 새로 디자인된 IDE, C++1x 언어 기능 같은 게 부각되는 편이다. 하지만 그것 말고도 IDE가 매우 편리해진 면모가 최소한 둘 있는데...
이제 IDE의 버전이 올라갈 때마다 프로젝트 파일을 매번 강제로 업그레이드 하지 않아도 되고, 그리고 컴파일러 툴킷을 직접 고를 수 있게 된 점이다.

이로써 IDE가 개별 프로젝트나 빌드 툴과는 좀 더 독립한 구도가 됐다.
이것은 딱히 새로운 기능 추가가 아님에도 불구하고 옛날에 도스 시절에 멀티부팅 기능이 추가된 것만큼이나 매우 편리해진 조치이다. (autoexec.bat / config.sys에 일종의 조건부 실행 로직을 추가하여, 부팅 configuration을 직접 고를 수 있는 것)

4.
본인은 예전에 precompiled header에 대해서 글을 쓴 적이 있다. 그때에도 언급했지만, 본인은 성질이 좀 급한 관계로 PCH 없이 소스 코드가 엄청난 분량의 인클루드 반복 때문에 컴파일 속도가 굼뜨는 걸 못 참는다.
그런데, 프로젝트 전체를 분석하면서 중복 인클루드로 판단되는 파일들을 자동으로 감지해 주는 기능이 있으면 좋지 않을까? 그것들을 stdafx.h로 대체하고 그 파일에다가 인클루드들을 몰아 넣는 것이다. 물론, 빈번하게 인클루드되긴 하지만 수정도 빈번하게 되는 편이기 때문에 pch에다 넣어서는 안 되는 것 판단은 사람이 하면 된다.

이건 마치 데이터베이스에서 테이블과 쿼리들을 분석하면서 자주 쓰이는 테이블 내지 애트리뷰트는 인덱스를 넣는 최적화 기능과 비슷한 구석이 있는 것 같다.

5.
자동차의 특성이 컴퓨터 소프트웨어의 특성과 매우 비슷하다고 여겨지는 점이 몇 가지 있다.

  • 내릴 때 실내등이나 각종 라이트가 완전히 꺼졌는지 확인하고, 블랙박스는 장시간 주차시 자체 전원 차단 기능이 켜져 있는지 확인해야 한다. → 메모리 leak 예방과 개념적으로 일치한다.
  • 급발진: 아주 희귀한 상황에서 갑자기 발생하는 치명적인 버그에 해당한다.
  • 자동 vs 수동 변속기: 옛날이라면 컴파일러가 자동 생성한 코드 vs 수제 어셈블리 코드.. 정도와 대응하고, 지금이라면 managed vs native 코드와 대응하는 듯하다. 요즘은 자동 변속기도 어지간한 수동 조작에 뒤쳐지지 않을 정도로 효율이 굉장히 좋아졌으니 말이다.

6.
세상에는 분야를 불문하고 여러 단체가 공동으로 뭔가 통합 작품이나 프로토콜을 만드는 경우가 있다. 따지고 보면 킹 제임스 성경도 성공회와 청교도가 연합해서 작업한 그런 통합 작품이다.
하지만 그런 통합 작품이 실질적인 통합을 이루지 못하고 그냥 기여를 가장 많이 한 단체의 전유물로 전락해 버리는 경우도 있다. 그런 예를 몇 가지 들어 보자면 다음과 같다.

  • HFT 통합 글꼴: 지금은 아래아한글밖에 안 쓰는 완전 옛날 유물이 됐다.
  • 공동번역 성서: 에큐메니컬 성경이라지만 현실은 역시 천주교 전용 성경일 뿐이다.
  • 타이젠 OS: 당초 취지와는 달리, 컨소시엄을 구성하던 협력사들은 다 빠져나가고, 사실상 삼성 전자밖에 관심이 없는 모바일 OS가 됐다.

삼성은 예전에도 아래아한글과 MS 워드가 뻔히 있음에도 불구하고 수익성과는 별개로 훈민정음을 오랫동안 밀었다.
그런 것처럼 모바일 OS 하나 정도는 우리가 자체 기술을 갖고 있어야 한다는 차원에서 타이젠을 꾸준히 미는 듯하다. 안드로이드와 iOS의 텃새에도 불구하고 정말 막대한 자금을 투자하여 타이젠 앱 프로그래머를 육성하는 중이다.

7.
비주얼 C++이 컴파일러, IDE, 디버거 등 모든 차원에서 64비트를 완벽하게 자체 지원하기 시작한 건 2005부터이다.
그런데 나는 그 시절부터 굉장히 궁금했던 게...
devenv IDE는 예나 지금이나 32비트 프로그램임에도 불구하고 어떻게 64비트 바이너리를 아무 제약 없이 바로 디버깅 하고 메모리 내부를 잘도 들여다볼 수 있느냐 하는 것이었다.

운영체제 차원에서 64비트와 32비트가 서로 얼마나 격리되어 있는지는 이 바닥에 짬밥깨나 있는 프로그래머라면 누구나 잘 알 테고. 그러니 결론은 하나. 별도의 64비트 EXE를 띄워서 IPC(프로세스 간 통신)를 하지 않고서는 이 정도의 자연스러운 연계는 절대 가능하지 않다는 것이었다.

확인해 보니 이 예상이 맞는 듯하다. 32비트 프로그램을 디버깅 할 때는 안 그러는데 64비트 프로그램을 디버깅 할 때는 msvsmon이라는 일종의 64비트짜리 원격 디버그 호스트 프로그램이 같이 뜬다. 그리고 디버깅이 끝나면 얘도 실행이 종료된다. EXE 크기가 수MB에 달하는 결코 작지 않은 프로그램이긴 한데, 얘가 뭔가 하는 일이 많은 것 같다.

8.
끝으로.. 시간 복잡도, 공간 복잡도라고 하면 전산학에서나 다루는 무슨 뜬구름 잡는 개념처럼 들리기 쉬운데, 현실에서도 의외로 간단한 예가 있다.

먼저, 자전거를 잠그는 자물쇠로는 열쇠 방식이 있고 숫자 다이얼 방식이 있다.
전자는 열쇠만 있으면 금방 자물쇠를 딸 수 있다. 후자는 번거로운 열쇠를 챙기지 않아도 되지만 원하는 숫자까지 다이얼을 맞추고, 다시 잠김 모드로 옮기는 시간이 오래 걸린다.

나는 프로그래머로서 이걸 경험할 때마다 시간/공간의 tradeoff라는 생각이 들곤 한다. 열쇠 자물쇠는 열쇠라는 공간이 필요하고 열쇠를 분실하지 않게 주머니 관리를 잘 해야 하지만, 열고 잠그는 건 배열 테이블을 참조하듯이 O(1) 시간 만에 즉시 끝낸다.
숫자 자물쇠는 열쇠가 없어도 되어 심리적으로 편하지만, 다이얼을 맞추기 위해 마치 매번 탐색을 하고 연결 리스트의 노드를 찾듯이 O(n) 시간 작업을 매번 해야 하기 때문이다.

옛날에 브라운관 모니터가 어느 수준 이상의 대형화가 도저히 불가능하고 LCD 모니터에 밀려 도태한 주 이유가..
바로 화면 크기 n에 따른 공간 복잡도가 O(n^3)이나 되었기 때문이다. 무게나 가격까지 그 정도로 급격하게 증가했고.
색감이 좋다고는 하지만, 그래도 전자총을 뒤에서 화면 크기만큼이나 거리를 두고 쏴 줘야 하니, 화면의 크기가 커질수록 어마어마한 양의 공간을 잡아먹는 것을 감당할 수가 없었다.

그리고 지구본(지구의)도 생각난다.
알 만한 분들은 이미 다 아시겠지만, 메르카토르 도법 평면 지도에는 아프리카 대륙은 실제보다 굉장히 작게 나오고, 그린란드 내지 러시아는 말도 안 되게 면적이 부풀려져 있다.

왜곡 없이 둥근 지구 위에서 세계 각국의 위치에 대한 실질적인 공간 감각을 키우는 데는 지구본 만한 게 없다. 그리고 지구본이 비치된 책상 앞에서 누가 머리 싸매고 있으면 왠지 간지 나고 멋있어 보이기도 하나..

지구본 얘도 크기에 따른 공간 복잡도가 O(n^3)인 부피를 차지하는 물건이고, 안 쓸 때 딱히 접거나 분해해서 부피를 축소시키는 방법도 여의치 않다 보니 실용성이 떨어진다.
현실적으로는 입체 효과까지 지원하는 구글 어스 같은 지도 어플이 대안이지만.. 그래도 이런 건 실물이 아쉽기도 하다. (어플은 여러 사람이 한 지구의 여러 지점을 한데 공유하면서 서로 비교할 수 없음)

다시 프로그래밍 얘기로 돌아오자면, 현실에서는 단순무식한 알고리즘이 O(n^2) 정도의 복잡도가 나오는 게 약간 머리를 굴림으로써 O(n log n) 정도로 최적화되는 경우가 많은 듯하다. 정렬이 대표적인 예이고, 그 외에도 빠른 푸리에 변환이라든가 최장 증가 수열 찾기 문제도 이런 범주에 속한다.

그리고 단순무식하게 접근했을 때 지수함수 복잡도가 되는 게, 다이나믹 프로그래밍으로 중간 계산 결과를 저장함으로써 메모리 복잡도 O(n^2), 시간 복잡도 O(n^2) 내지 O(n^3)이 되는 경우가 많다.
아예 O(n)으로 간단하게 줄어드는 건 피보나치 수열이나 팩토리얼을 구하는 것처럼 문제 자체가 극도로 단순한 경우밖에 없을 것이다.

Posted by 사무엘

2014/11/19 08:22 2014/11/19 08:22
, ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1030

오늘은 오랜만에 옛날 GWBASIC 추억 얘기를 또 늘어놓아 보겠다. 예전에 했을 법도 해 보이는데 여러 키워드로 검색을 해 보니 안 한 것 같다. 베이직 얘기를 전문적으로 하는 건 한 2년 만의 일이다.

GWBASIC은 초딩이었던 본인을 프로그래밍의 세계로 이끈 추억의 장난감이다.
본인은 어릴 때부터 컴퓨터가 다른 전자 기기와는 뭔가 차원이 다른 대단한 물건이라는 걸 실감했다.
텔레비전은 오로지 일방적으로 전달만 하는 물건인 반면, 컴퓨터는 내가 직접 명령을 내려서 모니터에 찍히는 글자의 색깔을 바꿀 수 있고, 내가 원하는 화면을 구성할 수 있고, 그림도 그릴 수 있고 소리도 내고 파일로부터 정보를 읽고 쓰면서 뭔가 '나만의 능동적인 세계'를 표현할 수 있기 때문이다.

본인은 딱히 머리가 빨리빨리 잘 돌아간다거나 수학 덕후 최적화 덕후 기질이 있지는 않았다. 단지, 새로운 세계를 표현하는 것 자체에 집착했다. 그래서 정보 올림피아드도 경시에서는 영 재미를 못 보고 그 대신 공모 부문에서 다 입상했다.

GWBASIC은 결과를 즉시 확인할 수 있는 대화식 구조라는 게 굉장히 인상적이다. 행번호에 GOTO문 남발은 굉장히 기괴하고 거추장스러운 개념이긴 하지만, 행번호가 없는 명령은 곧장 실행되고, 행번호가 붙은 명령은 메모리에 등록되어서 나중에 행번호 순으로 한꺼번에 실행된다는 그런 발상은... 참 아무나 할 수 있는 게 아닌 듯하다. RUN, MERGE, DELETE, CHAIN처럼 기억된 프로그램 자체를 확장하거나 바꾸는 명령이 있다는 것도 기괴하고 말이다.

GWBASIC에는 프로그램을 불러오거나 저장하는 명령으로 LOAD, SAVE가 있다. 그런데 GWBASIC은 좀 특이한 게, 여느 프로그래밍 툴처럼 소스 코드를 plain text로 저장하는 게 아니라 내부 바이너리 바이트코드로 저장하는 게 기본 옵션이다. 바이트코드는 같은 소스를 저장했을 때 plain text보다 크기가 작고, 로딩/저장 속도도 더 빠르다는 이점이 있다.

세월이 워낙 많이 흘렀기 때문에 지금은 그 바이트코드의 포맷이 다 알려져서 인터넷에 굴러다닌다. 포맷이 정식으로 공개된 건지 아니면 해커들이 리버스 엔지니어링을 해서 알아낸 건지는 잘 모르겠다.
다른 프로그램에서도 소스 코드를 볼 수 있게 저장하려면 SAVE"파일이름", A라고 뒤에 A를 덧붙여야 한다.

그리고 한편으로 P라는 옵션이 있다. P 옵션은 A와는 반대로 소스 코드를 내부 바이너리 코드로 저장하되 그걸 XOR 기반의 간단한 암호화까지 해서 저장한다.
P 옵션으로 저장된 소스는 불러와서 실행은 가능하지만, LIST로 내용을 열람하거나 코드를 수정할 수 없다. 따라서 비록 GWBASIC에 소스 코드를 EXE로 컴파일하는 기능은 없지만, 다 만든 프로그램을 남에게 인계할 때는 P 옵션으로 저장된 프로그램 파일을 전해 주면 소스 코드 유출을 막을 수 있다. 이론적으로는 말이다.

그러나 그 시절의 GWBASIC에 무슨 전문적인 코드 암호화나 난독화 기능이 있었던 것도 아닌데, 겨우 그 정도의 허접한 보호 기능은 당연히 뚫리고도 남았다.
P 옵션의 암호화 방식도 다 알려져 있고, GWBASIC의 버그를 이용하여 보호 기능 자체를 뚫어 버리는 방법도 존재한다. 이것은 아주 오래 전부터 베이직 프로그래머들 사이에 나돌던 공공연한 비밀 테크닉이었다.

자, 0xFF 문자 2개로 구성된 2바이트짜리 파일(가칭 UN.BAS)을 만든다. 간단하지만 키보드로 바로 입력할 수 없는 문자이긴 한데.. 헥사 에디터를 쓰든지 아니면 GWBASIC 자체를 이용해서 이런 파일을 생성하는 프로그램을 짜서 돌려도 된다.

그 뒤, P 옵션이 붙은 임의의 소스를 LOAD한 뒤, 그 상태에서 UN.BAS를 뒤이어 LOAD하고 나면..
기존 소스의 프로텍션이 풀리고 LIST 열람이 가능해지는 걸 볼 수 있다. 마법과 같은 일이 벌어진다.

사용자 삽입 이미지

그 뿐만이 아니다.
NEW를 입력해서 기억되어 있던 소스를 다 지운 뒤에도 UN.BAS를 LOAD하면.. 방금 지워졌던 소스가 되살아난다.

사용자 삽입 이미지

이게 도대체 어째서 가능할까? (UN.BAS는 0xFF 0xFF일 뿐, 저 소스 코드 자체가 들어있는 거 절대 아님.. -_-)
사실, GWBASIC은 내부적으로 기억하고 있는 바이트코드를 디코딩해서 LIST로 출력하고 소스 코드를 고치는 것을 허용할지 여부를 간단한 boolean 변수 하나로만 판단하는 듯하다. 저장할 때 XOR 인코딩 여부 역시 그 변수로 판단하며, 불러올 때의 XOR 디코딩 여부는 파일 앞부분에 있는 헤더로 판단한다.
그러니, 그 메모리 주소의 값만 바꿔 버리면 프로텍션을 곧바로 풀 수 있다. GWBASIC의 보안 체계는 근본적으로 허술했던 것이다.

그리고 GWBASIC의 고유 파일 포맷에 따르면, 프로텍션이 걸리지 않은 파일은 0xFF로 시작하고, 걸린 파일은 0xFE로 시작한다.
그러므로 0xFF 0xFF 2바이트짜리 파일은 GWBASIC으로 하여금 프로텍션 플래그는 해제하지만 그 뒤에 거의 즉시 파일이 끝나 버리기 때문에 메모리 상의 다른 소스 코드는 건드리지 않는 역할을 하는 듯하다.

원래는 그렇게 파일이 갑작스럽게 끝났을 때의 처리를 GWBASIC이 깔끔하게 해야 하지만 그렇지 못하기 때문에 졸지에 프로텍션만 풀어 버리는 게 가능한 듯하다.
NEW를 한 것까지 어떻게 undo를 하는지까지는 잘 모르겠지만 말이다.

아무튼, 이런 게 오늘날의 소프트웨어 보안 용어로 치자면 일종의 버그이고 exploit이다.
만약 GWBASIC이 Windows, Office, Visual Studio처럼 오늘날까지 살아 있는 제품이고 GWBASIC이 세계 기업들의 돈줄을 좌지우지하는 솔루션이었다면 이건 뭐 당장 긴급 업데이트/패치감이 됐을 것이다. 회사의 자산인 소스 코드가 간단한 해킹으로 죄다 유출되게 생겼으니 말이다.

업데이트 명분으로 맨날 귀가 따갑도록 나오는 “악의적으로 조작된 파일을 열 경우 임의의 코드가 실행... 까지는 아니어도 뭐가 어찌될 수 있는 보안 취약점이 Microsoft 모 제품에서 발견되었습니다” 문구가 가리키는 게 바로 이런 거다.
자그맣게 조작된 파일이 GWBASIC의 저장 프로텍션을 풀어 버리니, 이 꼼수가 보안의 관점에서 오늘날 시사하는 바가 크다고 생각되어서 문득 글을 써 보았다.

옛날에, 286 XT/AT를 갖고 '교육용 컴퓨터' 이러던 시절에는 단색 그래픽 모드에서 동작하는 여러 '교육용 소프트웨어'들도 있었다. CAI라고 들어 보셨는가?
'약수와 배수', '컴퓨터 개론' 같은 타이틀이 있었는데, 개중에는 정말 놀랍게도 GWBASIC으로 개발된 것도 있었다.
물론 런타임인 GWBASIC.EXE와 소스 코드들은 다 파일 이름과 확장자를 교묘하게 바꿨고, 실행은 CAI.BAT라는 파일로 했다.

소스 코드를 열어 보니 당연히 프로텍션이 걸려 있었다. 그러나 본인은 저 테크닉을 이용하여 프로텍션을 풀고 코드를 열람해 보기도 했다. 분량이 상당히 방대했으며 지금 다시 봤으면 여러 재미있는 아이디어들을 발견했을 법도 해 보지만, 본인은 그 당시엔 프로그래밍 실력의 부족으로 인해 그다지 충분한 재미를 못 봤다.

그 느리고 허접한 GWBASIC으로 자체 한글 출력과 그것도 모자라서 입력까지 구현했는데 과연 어떻게 구현했을지가 궁금해지지 않는가?

GWBASIC의 후신인 QBasic이야 고릴라 내지 NIBBLES 같은 예제 게임 프로그램이 MS-DOS 5.0에 같이 곁들여 제공되기도 했다.
순수 GWBASIC으로 근성으로 만들어진 프로그램은? 먼 옛날에 무슨 허접한 자동차 경주 게임 같은 걸 본 게 마지막이다. 각 스테이지의 이름은 태양계의 행성 이름이었는데... 기억하는 분이 있으려나 모르겠다.

상용 제품인 QuickBasic도 GWBASIC의 전통을 이어받아 소스 코드를 자기 고유 포맷으로 저장하는 기능이 있었다. 물론 GWBASIC과 호환되는 포맷은 아니었다. 그리고 축소판인 QBasic은 그런 기능이 없다.
지금은 '큐베'라고 하면 음악 DAW 프로그램인 큐베이스(Cubase)가 먼저 떠오르는 세상이 됐으니 이것도 격세지감이다.

* 그리고 GWBASIC과 관련된 추가 여담.
IBM PC(=도스)용으로 이식된 GWBASIC이야 기본 프롬프트가 Ok이지만, 더 구닥다리 8비트 롬 베이직 같은 걸 보면 프롬프트가 READY인 경우가 있다. 빌 게이츠 아저씨가 GWBASIC을 최초로 만들 때 원래 의도했던 메시지는 ready였다고 한다.
그 사고방식은 오늘날 같은 Windows+GUI 시대에까지도 남아 있다. Excel이나 Visual Studio, 심지어 MFC 기본 어플이.. 내부적으로 더 처리할 메시지가 없이 사용자의 입력만 기다리는 idle 상태로 진입했을 때 아래의 상태 표시줄에 나타나는 메시지는 바로 Ready이며, 우리말로는 그냥 '준비'이다.

그랬는데 ready가 ok로 바뀐 이유는.. 메모리를 단 3 바이트라도 더 확보하기 위해서였다고 한다. 뿌우우... 묵념.

단, Ok도 다 대문자 OK도 아니고, 대문자 O에 소문자 k로 정해진 이유는 본인으로서는 지금도 알 길이 없다.

Posted by 사무엘

2014/10/15 08:23 2014/10/15 08:23
, ,
Response
No Trackback , 6 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1018

« Previous : 1 : ... 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 : ... 23 : Next »

블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

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

Site Stats

Total hits:
2997452
Today:
143
Yesterday:
1296