국어, 언어학 잡설

1. '여' 불규칙

ㄱ부터 ㅎ까지
'가다(go), 나다(bring forth), 사다(buy), 자다(sleep), 차다(kick), 타다(get on), 파다(dig)'
라는 용어들을 생각해 보면, 이들은 과거형은
'갔다, 났다, 샀다, 잤다, 찼다, 탔다, 팠다'
라는 아주 규칙적인 패턴으로 활용된다.

그러나 잘 알다시피 '하다'(do)만은..
'하였다' 아니면 '했다'라고 굉장히 이상하게 활용된다. 중등학교 국어 시간엔 이를 '여' 불규칙이라고 배운다.
그런데 어미 '여'가 쓸데없이 붙는 것도 이상한데, 그게 축약되어서 '했다'가 되는 건 또 뭐냐..? '하다' 말고 그 어떤 용언도 활용 시에 ㅏ와 ㅐ가 그런 식으로 연계하여 변하는 경우는 없다.

'가다'의 경우, 다른 'Xㅏ다' 용언과는 달리, 명령형에서 '가거라'라고 생뚱맞은 '거'가 불규칙으로 첨가되기는 한다. 그러나 이 '거'는 명령형에서만 첨가되지 '해서/하여서, 했다/하였다'의 '여'에 비하면 등장이 훨씬 제한적이다.

'갔다', '팠다'처럼 '핬다'라는 단어는 한국어의 역사상 존재한 적이 없었던 걸까? 원래 있긴 했는데 혹시 전설모음 역행동화가 일어나서 '했다'라고 바뀌기라도 한 건 아닐까? 난 잘 모르겠다.

본인은 '바라다'가 자꾸 '바랬다', '바랬는데', '바램'처럼 활용되는 것도 비슷한 맥락의 현상이 아닌가 하고 거의 10년도 넘게 더 전부터 생각해 왔다. '하다'는 '함'이 '햄'으로 바뀌는 건 아니니 둘이 완전히 같은 양상은 아닌지도 모른다.
그리고 이것도 자음 하나만 다른 '자라다'는 활용 과정에서 '라'가 '래'로 바뀌는 현상이 결코 전혀 없으니 참으로 이상하지 않을 수 없다. '자랐다', '자랐는데', '자람' 등.. =_= 신기하다.

2. 지난 학기에 들은 변형 생성 문법 수업의 편린

(1) 아무래도 교재가 영어 원서이고 영어 통사론을 다루는 비중이 적지 않다 보니.. 선생님이 영어 고어 문법 얘기도 종종 하셨다. 그래서 내 머리엔 KJV 영어가 떠오른 적이 적지 않았다.

옛날에는 be + 동사PP가 수동태뿐만 아니라 마치 지금의 have + 동사PP처럼 완료 시제를 나타내기도 했다고 한다. KJV에 "is come"이 왜 이리도 많이 등장하는지 이제 알 것 같다.

(2) have가 의문문으로 등장할 때 Do you have 대신 곧바로 Have가 나오는 거..
난 개인적으로 have ye any meat? (요 21:5)가 곧장 떠오르던데 오늘날에도 이런 패턴이 영국의 일부 방언으로 남아 있다고 한다.
마치 C언어로 치면, C이긴 한데 오늘날 안 쓰는 오리지널 K&R 스타일 C 같은 느낌이다. #include 과감히 생략하고 main 함수에 int나 return 다 생략하고 바로 printf("hello, world!");를 하는... 좋게 말하면 간결하고 나쁘게 말하면 불친절한 스타일 되겠다.

(3) 문법을 설명하는 데도 문장 구조 binary tree를 그려서 노드를 이리 저리 옮기는 게 많다. 마치 빨강 검정 나무의 동작을 다루는 것 같았다. 물론 둘은 개념과 성격은 서로 완전히 다르지만 말이다.
또한, 전산학 자료구조 시간에는 tree 노드를 표현할 때 sibling, child 등 다 중성 어휘를 쓰지만, 언어학에서는 sister, daughter 같은 여성형 어휘를 쓰더라.

프로그래밍 언어와 자연어를 설명하는 이론을 모두 마스터하고 싶다.

Posted by 사무엘

2014/07/28 08:33 2014/07/28 08:33
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/989

C/C++에서 구조체나 클래스는 통상적으로 global scope에서 선언되거나 기껏해야 다른 클래스 내지 namespace의 내부에서 선언된다. 즉, 어차피 비실행문들만 있는 곳에서 선언되는 편이다.
그러나 실행문으로 이뤄져 있는 함수 안에서 이들을 새로 선언하는 것도 문법적으로 가능하다. 다시 말해, 변수를 선언하는 것뿐만 아니라 그 변수들의 type을 결정하는 구조체나 클래스를 즉석에서 선언해 쓰는 것도 가능하다는 뜻이다.

다른 곳에서 두고두고 재사용하는 구조체가 아니라 함수 한 곳에서 튜플 같은 형태로 잠깐만 사용하고 마는 구조체라면, 이런 식으로 함수 안에서 간단히 선언해서 사용하면 좋다. 하긴, 그러고 보니 struct, class, union, enum뿐만 아니라 typedef도 실행문과 비실행문 문맥에서 모두 사용 가능한 물건이다.

함수 안에서 클래스 같은 걸 따로 선언하는 건 C#에서는 가능하지 않으며 C++만의 전유물인 듯하다.
이렇게 함수에서 선언된 자료형은 유효 범위도 마치 지역변수처럼 그 함수 안으로 완전 local하게 한정된다. 그래서 각종 IDE 같은 데에 명칭이 뜨지도 않는다. 지금부터는 이와 관련해서 좀 더 기괴한 이야기들을 풀어 보겠다.

1. 무명 자료형

C/C++에는 '이름 없는' 구조체/클래스/공용체 따위의 개체가 있을 수 있다. '이름 없는' 함수 그 자체만의 선언은 지원되지 않아서 함수형 프로그래밍 패러다임이 도입된 C++0x 이후에서야 람다와 함께 등장한 반면, 이름 없는 복합 자료형이라는 개념은 있었던 것이다.

class {
public:
    int x,y,z;
} obj;

C#이나 자바 스타일이라면 상상도 할 수 없는 일이겠지만, C/C++은 자료형의 선언과 해당 자료형에 속하는 변수의 선언을 동시에 할 수 있다. class OBJ { ... } a,b,c; 도 OBJ a,b,c;나 심지어 int a,b,c;와 개념적으로 같다.
class, struct, enum, union 등을 선언한 뒤에는 닫는 중괄호 다음에 세미콜론을 반드시 붙여야 하는 이유가 바로 이 때문이다.

그런데 이름 없는 자료형은 자료형의 선언과 함께 변수 선언도 같이 해 주는 게 선택이 아닌 '필수'라는 차이가 있다. 그도 그럴 것이, 얘는 이름이 없는 일회용 자료형인 고로 그 자료형을 선언하는 구문이 끝난 뒤에는 그걸 지칭할 방법이 없기 때문이다. 변수가 단 하나라도 같이 선언돼 있어야 나중에 C++11의 auto 라도 써서 그것과 동일한 자료형의 변수를 추가로 만들 수 있을 것이다.

이런 무명 자료형이라는 개념은 대개 한 자료구조 내부에서 구조체와 공용체를 섞어 가며 쓸 때 유용하지만, 그렇잖아도 일회용 성격이 강한 local 자료형에서도 더욱 의미가 있다. 굳이 이름을 생각할 필요 없이 내가 생각하는 복합 자료형을 간단하게 만들어서 쓰게 해 주기 때문이다.
물론 local뿐만 아니라 global scope에서도 무명 자료형을 얼마든지 선언해서 쓸 수 있다. C/C++의 오묘한 면모 중 하나이다.

2. 함수 안에 함수

C/C++은 복합 자료형은 앞서 살펴보았듯이 무명으로 선언할 수 있고, 그 안에 또 다른 복합 자료형을 nested된 형태로 선언하고 집어넣을 수 있다. 그러나 실행되는 코드의 집합인 함수를 그렇게 일종의 값처럼 자유자재로 다룰 수 있지는 않았다.

함수 자체를 다른 함수에다가 전달하는 것은 그나마 함수 포인터가 있으니 불가능하지는 않지만, 그건 자료형, 함수명 등에 대한 작명이 필요하며 기계 중심적이고 융통성이 부족했다. 또한 함수 안에다가 또 일회용으로 간단히 쓰고 마는 함수를 잠깐 선언하는 것도 가능치 않아서 global/class scope 차원에서의 선언이 필요했다. 남는 건 매크로 함수밖에 없지만 이게 얼마나 구조적으로 허접한 물건인지는 역시 설명이 필요하지 않는 수준이고.

void func()
{
    void simple_func(int x) { }

    simple_func(0);
    simple_func(1);
}

nested function은 C와 파스칼의 큰 차이 중 하나이기도 했다. 파스칼은 지원하지만 C/C++ 계열은 지원하지 않았기 때문이다. 마치 가변 길이 배열만큼이나 언어 차원에서 결코 지원되지 않을 금기 봉인 사항이기라도 한가 궁금하다. 다만, 옛날에 gcc던가 극소수 C 컴파일러에서 확장 옵션을 통해서 nested function을 지원하는 걸 본 것 같다.

물론, 중첩 함수를 써서 할 수 있는 일은 중첩 함수라는 개념이 없이도 완전히 똑같이 할 수 있기 때문에 상호 등가 교환이 가능하다. 마치 클래스에서 public과 private 구분을 해 주든, 아니면 전부 싸잡아 public인 struct로 코드를 작성하든.. 이것은 코드의 유지 관리의 편의성 내지 정보 은닉하고만 관계가 있지 프로그래밍 언어의 구조적인 계산 능력과는 무관한 것하고 같은 맥락이다. 그래서 C/C++은 nested 함수라는 개념을 도입하지 않은 듯하다. 정수 타입에 subrange 같은 개념도 없을 정도이니 뭐~

지금이야 람다 덕분에 함수 안에 함수의 선언이 사실상 가능해졌다. 캡처 같은 새로운 개념도 같이 도입됐다. 하지만 이건 일반적인 함수와 개념적으로 같은 물건은 아니다.
C++에서는 (1) 중첩 namespace 안에 들어있는 함수가 얼추 비슷한 개념일 수 있으며, 이것 말고도 좀 더 직접적으로 함수 안에 함수를 만드는 것이 편법 우회 경로로 가능하다. (2) 바로 함수 안에 클래스를 선언하고 멤버 함수를 정의하는 것이다. 이런 식으로.

int main(int argc, char* argv[])
{
    class A {
    public:
        static void Func() { puts("function inside function"); }
    };
    A::Func();
    return 0;
}

특히 static 함수는 this 포인터를 사용하지도 않으니 진짜로 일반 함수와 다를 바가 없다.
함수 안에다 구조체를 정의하는 것으로도 모자라서 완전한 형태의 클래스를 정의하고 멤버 함수를 정의하는 것까지도 가능하다니 놀랍지 않은지?

단, 이런 지역 클래스에서 멤버 함수를 선언할 때는 논리적으로 당연한 제약이 하나 걸린다. 함수의 몸체는 반드시 그 클래스 안에서 저렇게 정의되어야 한다. 안 그러면 아까 무명 자료형에서 변수 선언을 바로 안 해 줄 때처럼 경고가 뜬다.

비주얼 C++의 경우 일단 C4822 경고만 뜨고 그걸 실제로 호출까지 한 경우 링크 에러가 났지만, 요즘은 그 즉시 C3640 에러도 같이 나오는 듯. 링크 에러가 더 친절하게 컴파일 에러로 바뀌었다.
클래스의 밖인 그 함수 몸체 안에서 또 void A::Func() { } 이런 식으로 함수 몸체를 따로 정의하는 건 문법적으로 허용되지 않기 때문이다.

또한, 이런 이유로 인해, 지역 클래스는 static 멤버 함수는 가질 수 있는 반면 static 멤버 변수(=데이터)는 가질 수 없다.
그건 함수 안의 일반 static 변수와 같은 취급을 받으려나 궁금했는데, 만들어 보니 그건 언어 문법 차원에서 허용되지 않으며 곧바로 컴파일 에러가 난다. static const도 허용되지 않는다.

그러고 보니 이름 없는 클래스도 static 멤버 변수를 사실상 가질 수 없을 듯하다. 사실, 이름 없는 클래스에다가 그런 것까지 바라는 것 자체가 변태 도둑놈 심보이긴 하다. ㅎㅎ
멤버 함수야 몸체를 클래스의 선언부 안에다 강제로 집어넣는 식으로 정의할 수 있지만 static 변수는 결국 클래스 밖에서 따로 정의를 해야 하는데, 클래스 이름이 없으니 정의를 할 수 없어서 링크 에러가 나기 때문이다.
이거 정말 복잡한 문제다. C++이 C#/Java하고는 다른 독특 기괴한 면모가 이런 데서 또 발견된다.

Posted by 사무엘

2014/07/25 08:32 2014/07/25 08:32
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/988

라벨, 에디트, 리스트 박스, 콤보 박스, 일반 버튼, 라디오/체크 박스.
이름만 들어도 친숙한 이 물건들은 응용 프로그램의 대화상자에 들어가는 그야말로 필수 GUI 구성요소들이다.

여기에다 리스트 컨트롤, 트리 컨트롤 같은 더 고급스러운 공용 컨트롤까지 있으니 실무에서는 사실 99%에 가까운 일은 기존 컨트롤만으로 다 해치울 수 있다.
사소하게 마음에 안 들고 동작 방식을 좀 고쳐야 하는 건, (1) 이들이 제공하는 owner-draw 옵션(주로 외형 외주) 내지 일부 메시지에 대한 (2) 윈도우 프로시저의 서브클래싱으로 customize 가능하다.

그러나 아주 가끔은, 기존 컨트롤과는 동작 방식이 완전히 다른 새로운 윈도우를 내가 머리부터 발끝까지 직접 구현하여 대화상자에다 집어넣어야 하는 경우가 있다. 기존 컨트롤을 약간만 고치는 정도로는 성이 안 찬다.

본인이 개발한 <날개셋> 한글 입력기의 GUI를 보면 이런 custom 컨트롤들이 적지 않게 보인다. 당장 비트맵 글꼴을 사용하는 자체 에디트 컨트롤은 편집기의 클라이언트 영역뿐만 아니라 대화상자에서 입력란으로도 쓰인다. 그리고 한글 낱자를 뽑아 오는 독특한 스타일의 콤보 박스라든가 문자표 리스트, 글쇠배열 편집 윈도우도 다 자체 개발한 custom 컨트롤이다.

꼭 그렇게까지 특이한 용도의 윈도우가 아니더라도.. 이걸 생각해 보자.
자체적인 키보드 포커스를 받으며, 화살표 key나 마우스 휠로 2차원적인 스크롤이 가능한 형태로 그림 같은 owner-draw 컨텐츠를 출력하는 컨트롤.
쉽게 말해 MFC로 치면 CScrollView에 해당하는 컨트롤이다. 특이할 것 없는 아주 기본적인 기능임에도 불구하고 기존 컨트롤의 서브클래싱만으로는 구현 가능하지 않다.

키보드 포커스를 받지 않으며 스크롤 같은 것도 없이 고정적인 owner draw 그림만을 화면에 달랑 그리는 거라면 쉽다. 기성 컨트롤인 STATIC 윈도우에다 owner draw 스타일을 줘서 WM_DRAWITEM 메시지를 처리하거나, 아예 서브클래싱을 해서 WM_PAINT 메시지를 통째로 가로채면 된다.

그러나 스스로 키보드와 마우스 입력을 처리하는 윈도우라면 독립된 형태로 자체 구현이 불가피하다. 여러 메시지들에 대한 동작을 인위로 고쳐야 하기 때문이다.
<날개셋> 편집기에서 화면 인쇄를 시켰을 때 화면 인쇄 결과를 출력하는 윈도우가 바로 이런 용도로 만들어진 custom control이다.

custom control을 MFC를 써서 개발한다면 응당 CWnd에서 파생된 새로운 클래스를 떠올리게 될 것이다. 이 C++ 클래스는 자신만의 윈도우 클래스 이름을 갖고 있을 것이고 자신을 운영체제에다 등록하는(RegisterClass 호출) 함수도 static 형태로 갖추고 있어야 할 것이다. (응용 프로그램이 실행 초기에 호출함)
그리고 대화상자 리소스에는 그 클래스 이름을 지정해 놓은 custom 컨트롤을 마음껏 생성해 놓는다.

그런데 대화상자의 컨트롤들은 내가 CWnd::Create를 호출하여 만드는 게 아니라 운영체제의 대화상자 관련 함수들이 CreateWindowEx에다가 윈도우 클래스 이름만 달랑 줘서 만들어진다. 이렇게 C++ 언어 체계의 통제를 받지 않고 밖에서 만들어진 HWND에다가 MFC 기반의 C++ 개체는 어떤 메커니즘을 통해 상호 연결시켜야 할까?

단순히 CWnd::FromHandle 함수 같은 걸로 임시 CWnd만 달랑 생성해서 연결하는 게 아니다. 이 custom 컨트롤은 구현체 클래스가 존재하니, 그냥 CWnd가 아니라 내가 만든 전용 CWnd 파생 클래스가 정확히 연결돼야 한다. 그렇게 해야 응용 프로그램 해당 윈도우끼리는 type-safe하지 않고 불편한 메시지를 쓸 필요 없이 C++ 멤버 함수/변수에 직통으로 가능해지니 좋다.

연결하는 방법은 크게 두 가지가 있다. 먼저, C++ 클래스의 생명 주기를 윈도우 자체의 생명 주기와 일치시키는 것이다. 만드는 윈도우 클래스에다가 윈도우 프로시저를 아래와 같이.. MFC의 AfxWndProc와 거의 똑같은 형태로 작성해 준다.

LRESULT CALLBACK CHeapCtrl::_MyWndProc(HWND hWnd, UINT msg, WPARAM w, LPARAM l)
{
    CWnd *pWnd = CWnd::FromHandlePermanent(hWnd);
    if (pWnd==NULL) { pWnd=new CHeapCtrl; pWnd->Attach(hWnd); }
    return AfxCallWndProc(pWnd, hWnd, msg, w, l);
}

이 개체는 언제나 new 연산자를 통해 heap에만 생성된다. 그러므로 PostNcDestroy 함수를 다음과 같이 구현하여 윈도우와 함께 C++ 개체도 같이 소멸되게 해 줘야 한다.

void CHeapCtrl::PostNcDestroy()
{
    delete this;
}

또한 대화상자 내부에서는 CHeapCtrl을 언제나 포인터를 통해 접근해야 한다.

CHeapCtrl *p = DYNAMIC_DOWNCAST(CHeapCtrl, GetDlgItem(IDC_MYCONTROL));

둘째 방법은.. 대화상자이니까 가능한 더 간단한 방법이다. 대화상자 클래스에다가 해당 컨트롤 클래스의 개체를 선언한다. 윈도우 클래스를 등록할 때 WNDCLASS 구조체에다가 윈도우 프로시저는 그냥 쿨하게 DefWindowProc이라고 주면 된다.

CStackCtrl m_wndCustomCtrl;

그리고 윈도우와 C++ 개체를 그냥 이렇게 컨트롤 변수로 연결해 버린다.

void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_MYCONTROL, m_wndCustomCtrl);
}

이 경우, CStackCtrl은 스택, 아니 최소한 대화상자 클래스와 동일한 메모리에 생성된다. 그래서 개체를 포인터를 거치지 않고 더 간편하게 접근할 수 있을 뿐만 아니라, 대화상자 같은 윈도우 개체가 사라진 뒤에도 C++ 객체가 갖고 있던 내부 정보에 접근을 할 수 있다. PostNcDestroy 처리가 필요하지 않음은 두 말할 나위도 없고. 여러 모로 더 속 편하다.

윈도우 클래스상으로 이 윈도우는 프로시저가 아무 특수한 일도 하지 않는 DefWindowProc로 지정되어 있지만, DDX_Control 함수는 윈도우 프로시저를 서브클래싱하고 저 윈도우의 핸들을 우리가 준 CStackCtrl에다가 연결해 준다. 그래서 각종 메시지가 발생했을 때 CStackCtrl의 메시지 맵에 등록된 핸들러 함수가 호출되는 것이다.

윈도우 클래스와 MFC 클래스를 연결한 건 시작일 뿐이고 본격적인 코딩은 지금부터이다. 그야말로 산 넘어 산이다.
WM_PAINT와 필요하다면 WM_SIZE를 처리해야 할 것이고, 키보드 포커스를 받는 물건으로 계획을 했으니 WM_GETDLGCODE를 잡아서 스크롤을 위해 최소한 DLGC_WANTARROWS 정도는 되돌려야 할 것이다. 다른 단축키 같은 걸 추가로 인식하려면 DLGC_WANTCHARS도 필요할 테고.

스크롤이 되는 윈도우라면 요즘은 마우스 휠 인식은 필수다. WM_MOUSEWHEEL을 처리해야 한다. SystemParametersInfo(SPI_GETWHEELSCROLLLINES, ...) 을 호출하여 마우스 휠의 움직임 단위를 감지하여 동작할 필요가 있다. 특히 n줄이냐, 아예 페이지 단위냐(WHEEL_PAGESCROLL)를 잘 판단해야 한다.

만드는 컨트롤에 확대/축소 배율이 존재한다면 Ctrl+휠로 배율 조절을 시키는 게 요즘 트렌드다.
또한, 전통적인 세로 스크롤용 마우스 휠뿐만이 아니라 가로 마우스 휠 WM_MOUSEHWHEEL (wheel 앞에 H 추가)이라는 게 있다는 것도 알아 두면 좋다. 마우스 현물보다는 손가락 제스처를 이용한 스크롤 기능이 있는 노트북 터치패드로 생성되는 듯하다.

어디 그 뿐이랴? 표준 인터페이스에는 아예 마우스 휠을 눌러서 구동하는 '자동 스크롤' 모드도 있다. WM_MBUTTONDOWN(마우스 가운데 버튼) 되시겠다. 이건 아까와는 반대로 마우스 실물이 아닌 노트북 터치패드로는 구경하기 힘든 모드이다.

.마우스로 화면을 끌어서 스크롤이 되게 하려면 WM_LBUTTONDOWN, WM_MOUSEMOVE 같은 메시지들을 처리하면 될 것이고.
아, 이런 것보다 더 중요하면서도 스크롤 윈도우에서 상당히 번거로운 작업이 하나 있는데 그건 바로 WM_HSCROLL 및 WM_VSCROLL 메시지이다. 우리의 편견과는 달리,처음에 SetScrollInfo 함수 하나로 전체 스크롤 크기와 영역만 지정해 준다고 해서 그 다음부터 모든 스크롤 처리가 자동으로 되는 게 아니기 때문이다.

스크롤 메시지는 사용자가 스크롤 바의 화살표(한 칸씩)를 눌렀을 때, 스크롤 바를 드래그하고 있을 때, 스크롤 바 옆을 눌렀을 때(한 페이지 씩) 등등의 상황별로 서로 다른 정보가 담긴 채로 전달된다. 그리고 이때 실제로 화면을 얼마만치 스크롤시키고 어떤 처리를 할지는 전적으로 해당 응용 프로그램에 달려 있다. 운영체제가 자동으로 해 주는 일은 없다. 이걸 처리하지 않으면 사용자가 스크롤 바를 눌러도 다른 반응이 발생하지 않는다.

바로 이런 특성 때문에 사용자가 스크롤 바를 끌고 있는 동안 화면이 바로 갱신될지, 혹은 곧장은 아니고 스크롤 바의 드래그가 끝난 뒤에야 화면을 갱신할지 같은 것도 응용 프로그램마다 달리 동작할 수 있다. 물론 반응성이 좋은 프로그램이라면 어지간하면 즉각 화면이 갱신되는 게 좋겠지만 말이다.

그리고 요즘은 화면 캡처 유틸리티들이 스크롤 캡처를 지원하는데, 이 역시 생각보다 꼼수를 써서 구현돼 있다. 화면에다 별도의 표식을 그려 넣은 뒤, 캡처 대상 윈도우에다 스크롤 메시지를 보내고 그 표시가 얼마나 이동했는지를 수동으로 점검한다. 위와 같은 높은 자유도로 인해, 저렇게 하지 않으면 그 스크롤 분량을 정량적으로 알아낼 수 없기 때문이다. (스크롤 바가 이동한 양과 그에 따라 지금 화면이 실제로 이동한 픽셀수 사이의 인과관계)

화면이 스크롤되면 ScrollWindowEx 함수를 호출해서 이미 그려진 화면은 운영체제가 제공하는 스크롤 기능으로 넘기고 새로 칠해져야 하는 최소한의 부분만 새로 칠하는 '평범한 프로그램'이라면.. 저 꼼수가 통한다.
그러나 스크롤 될 때마다 화면을 몽땅 지우고 새로 그리는 프로그램이라면, 저 표식도 지워지기 때문에 스크롤 캡처를 할 수 없게 된다.

이상이다. 윈도우의 스크롤 기능까지 얘기하다 보니 말이 또 길어졌다. 여기에도 뭔가 정량적인 동작 패턴이 분명 있는 듯한데, 그것만 추려내기는 쉽지 않아 보인다.
공통된 기능을 운영체제가 API 함수로든, 공용 컨트롤 윈도우로든 뭘로든 좀 제공을 하지 않는다면 역시나 프로그래머들이 비슷한 기능을 여전히 자체적으로 중복 구현할 수밖에 없을 것이다.

Posted by 사무엘

2014/07/22 08:36 2014/07/22 08:36
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/987

내가 컴퓨터 프로그래밍을 시작하기 전에, 심지어 철도를 빨기 전에.. 정말 까마득한 먼 옛날엔
자동차를 머리부터 발끝까지 얼마나 심취해서 진심으로 쪽쪽 빨고 지냈는지...;;

1991년, 지금으로부터 거의 23년 전에 혼자 다른 책을 베끼고 온갖 사견을 덧붙여서 만들었던 자동차 화보-_-;;가 고향집에서 뒤늦게 발견되었다. 나의 초딩 3학년 시절의 작품이다.
물론 사진 찍는 건 어머니께서 도와 주셨다. 디카가 없던 시절이니 당연히 필카로 찍고 현상해서 찾아 와서 붙였다.

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

어린 눈에는 자동차별로 타코미터의 존재 여부, 파워윈도우의 존재 여부, 그리고 뒷좌석 중앙에 팔걸이의 존재 여부가 특별하게 다가왔던가 보다.

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

르망, 각그랜저, 그리고 콩코드, 로얄 시리즈 등.. 정말 추억의 올드카들이다. 지금은 저 차 번호들은 존재할 가능성이 0에 한없이 수렴하므로 번호판을 굳이 가리지는 않았다.

사용자 삽입 이미지사용자 삽입 이미지
그 당시 우리집 자가용이었던 엑셀의 카탈로그 내지 취급 설명서를 베껴서 그린 거지 싶다. ㅎㅎ

사용자 삽입 이미지사용자 삽입 이미지
그 당시 액정 디지털 계기판, 그리고 헤드라이트에까지 와이퍼가 달려 있던 임페리얼을 무척 신기하게 여기고 인상깊게 관찰했었다.

사용자 삽입 이미지사용자 삽입 이미지
대략 이런 내용.
걍 이 취미를 그대로 유지해서 현대 자동차에라도 입사했으면 돈은 더 많이 벌었겠다는 생각이 폭풍처럼 든다. ㅠ.ㅠ

Posted by 사무엘

2014/07/19 08:22 2014/07/19 08:22
Response
No Trackback , 5 Comments
RSS :
http://moogi.new21.org/tc/rss/response/986

1. 안톤 드보르작

음악가 드보르작. 알면 알수록 대단하며 우리 같은 후대의 철덕들의 귀감이 되는 존경스러운 철덕이다. 우리 모두 드보르작을 본받자. 불끈!

진 회숙의 <클래식 오딧세이> 중에서

드보르작은 사실 상당히 괴팍한 성격의 소유자였다고 한다. 그는 한 가지 것에 거의 편집증에 가까운 집착을 가지고 있었는데 그것은 기관차와 역, 철도에 관한 것이었다. 프라하에 살고 있을 때, 그는 매일같이 프란츠 요제프 역으로 가 입장권을 사서 구내로 들어간 다음 역 구석구석을 자세히 살펴보고, 개찰구의 역원이나 짐꾼, 감시원, 기관사들과 농담을 하기도 하고 기차에 관한 궁금한 사항을 물어보며 시간을 보내곤 했다.

기차 시간표를 줄줄이 암기하고 있었던 그는 열차가 가끔 늦기라도 하면 마치 큰일이나 난 것처럼 지나가는 역원이나 승객에게 그 이유를 물으며 야단법석을 피웠다고 한다.

그렇다고 음악원 선생 노릇을 전폐하고 역에 머물 수는 없는 노릇. 하지만 음악원에서 학생을 가르치고 있을 때에도 그는 틈틈이 역장이 가지고 있는 것과 똑같은 고풍스러운 시계를 신경질적으로 꺼내보며 열차의 도착 시간을 점검하곤 했다. 그러다가 정 궁금하면 학생을 역으로 보내 11시 20분 도착 예정인 브륀-프라하 행 급행열차 158호가 정시에 도착했는지, 기관사인 야로슬라브 보트루바가 무슨 재미있는 경험을 하지나 않았는지 알아오게 했다.

이 때문에 노바크, 수크, 피비히, 레하르와 같은 미래의 대음악가들이 종종 수업을 중단하고 스승의 취미에 봉사하기 위해 요제프 역으로 달려가야만 했다. 그러다가 언젠가 꾀가 난 제자가 역에 다녀오지도 않고 다녀온 것처럼 꾸며서 애기한 적이 있었다. 하지만 거짓말이 곧 들통이 나 그 제자는 드보르작으로부터 무척 호되게 야단을 맞았는데 그 제자의 표현을 빌면 당시 드보르작의 모습이 마치 ‘기차 화통을 삶아먹은 것’ 같았다고 한다.

어느 날 드보르작의 딸과 약혼한 제자 요제프 수크가 고향에서 돌아왔다. “그래, 여행은 어땠는가?”하고 드보르작이 물었다. “모든 것이 즐거웠습니다. 우리는 2시 34분 정각에 크로세비치를 출발해 3시 13분에 벤샤우에 도착, 거기서 물을 공급 받고, 3시 28분에 발차하여 5시 46분에 프라하에 도착했습니다. 아, 그리고 저희가 탄 열차의 번호는 10726번이었습니다.”

이렇게 얘기하고 나서 수크는 ‘이 정도면 스승이 만족했겠지’ 하고 생각했다. 그런데 웬걸? 그의 대답을 들은 스승의 표정이 그다지 밝지 않은 것이었다. 씁쓸한 표정을 지으며 드보르작이 하는 말,

“자네 정말 한심하군. 10726번은 열차번호가 아니라 기관차의 제작번호란 말일세. 이 벤샤우 열차의 번호는 187이야.”
이렇게 제자를 향해 쏘아붙인 드보르작은 다시 딸에게로 고개를 돌려 이렇게 물었다. “그래 너는 이런 남자와 결혼하겠다는 거냐?”

철도 차량의 번호와 운행 번호는 정말로 혼동하기 쉬운 개념이긴 하다만.. ㅋㅋㅋ
이것 말고도 드보르작의 놀라운 언행을 살펴보자. 이 사람이 오늘날 지하철이나 전기 기관차의 VVVF 구동음이라도 들었으면 정말 뿅 갔지 싶다..

“이것은 많은 부품으로 되어 있어. 너무 너무 다양한 부품들이지. 모두 각자가 모두 중요성을 가지고 있고, 각자 그 역할이 있어. 심지어 가장 작은 스크류 부속조차 제 위치에 있으며 무언가를 연결하고 있어. 모든 부품이 그 목적이 있고, 그 역할이 있어. 그 결과는 말이야 놀라워!!”

“오오 그런 기관차가 트랙에 있다니!! 그들이 석탄과 물을 넣으면 말이지, 단 한 사람이 작은 레버를 누르면, 큰 레버가 작동되기 시작해, 비록 기차는 수천 톤의 금속덩어리 인데도, 기관차는 토끼처럼 움직인다고! 내가 만약 기관차를 발명한 사람이 될 수 있다면, 나는 내가 작곡한 모든 교향곡이라도 포기할 수 있어!!


2. 유 병언

그러나 참으로 안타깝고 애석한 것은... 우리나라엔 정말 뜻밖의 인물도 완전 뼛속까지 철덕이라는 점이다.
정말 욕 나오고 분노가 치민다. 내가 다 민망하고 얼굴이 화끈거린다.

유 병언 전 세모그룹 회장이 취미생활로 알려진 각종 폐열차 구입에 약 20억 원에 이르는 거액을 쓴 것으로 밝혀졌다.
기독교 복음 침례회(구원파)가 소유한 전국 폐교, 연수원 등 4곳에 있는 각종 폐열차는 120여 량. 전남 곡성군 삼기면의 한 폐교에는 ㈜아해 소유의 폐열차 4량, 화차 58량이 있다. 벌크라고 불리는 화차는 유류, 시멘트 원료 등 각종 화물을 운송하는 데 쓰인다.


게다가 저 아저씨는 내가 재벌 갑부가 됐으면 하고 싶었던 취미생활을 정확하게 똑같이 하고 있었다.. ㅠㅠㅠㅠㅠ
금수원 사진에 웬 서울 지하철 열차가 있는 걸 보고는 깜짝 놀랐었는데, 실상은 이거 뭐 상상 이상이었다.

자고로 철덕이라면 철도를 이해하는 데 도움이 되는 문과 무와 예체능 일체의 학문을 분야 불문하고 두루 겸비한 인재로서
나라사랑 국토사랑 정신으로 국가와 사회에 기여하고 한국 철도의 향기를 좋은 간증으로 남겨 방방곡곡에 전해야 하거늘!
만나면 철덕의 명예를 걸고 한 대 패 주고 싶다.
구원파라고 해서 일반적인 기독교회까지 함께 싸잡아 팀킬시킨 게 다가 아니었구나.

서울메트로 관계자는 "유 전 회장이 폐전동차를 구입한 것이 밝혀지면서 회사 이미지에 부정적 영향을 끼칠 수 있어 앞으로는 객실을 해체해 고철로만 파는 방안을 검토 중"이라고 말했다.

"이것은 기록된바, 철도의 이름이 너희로 말미암아 일반인들 가운데서 모독을 받는도다, 함과 같으니라." 꼴이 됐다.

Posted by 사무엘

2014/07/13 19:37 2014/07/13 19:37
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/984

<날개셋> 한글 입력기는 도대체 왜 만들어도 만들어도 또 만들 게 끝도 없는 걸까? 미치겠다. ㅠ.ㅠ 이거 좀 진지한 고민거리이다. 누가 이 마약 같은 코딩의 노예계약으로부터 나를 좀 해방시켜 줄꼬.

난 기술을 매우 긍정적으로 보지만, 한편으로 IT 쪽은.. 이제 어지간히 만들어질 거, 나올 건 다 나오고 한계에 도달하지 않았나 하는 회의감도 어느 정도 갖고 있다. Windows도 그렇고 Office도 그렇고.. 소프트웨어 업그레이드의 여파가 이제 예전만 하지 않다.

그러나 <날개셋> 한글 입력기에 관한 한은 상황이 다르다. 여전히 만들어 질 거, 나올 것들이 다 완성된 상태가 아니다. 이건 긍정적인 상태라고 해석해도 될 것 같다.
이 글에서는 <날개셋> 한글 입력기 7.4에 대해서 미처 제대로 공지가 안 되었던 변화 사항과 다음 버전 개발 근황 등을 전하도록 하겠다.

※ 7.4 버전의 잠수함 패치 내역

1.  고급 입력기의 사용자 정의 조합은 이제 언제나 '일반 문자' 타입의 날개셋문자만을 인식한다. 한글 타입(두벌식, 세벌식 등)의 날개셋문자를 0x11??대의 글쇠로 자동으로 인식하는 기능은 없어졌다.
이것은 동작 방식의 간결화를 위해 취해진 조치이다. 한글 자모나 글자를 이용하여 사용자 정의 조합을 만드는 것은 7.4의 고급 입력기에서 추가된 '한글 출력 치환' 기능으로 구현해도 된다. 더 전문적인 대체 기능이 생겼다.

2. 기본 입력기의 글쇠배열 수식에서 이제는 A와 C 변수가 제공되지 않는다. 이것은 사실 디자인상의 실수에 가까운 잉여일 뿐이었다.
글쇠가 눌러질 때 Alt 키가 눌러져 있으면 A에, Ctrl 키가 눌러져 있으면 C에 nonzero 값이 들어왔다. 그러나 7.4부터는 기본 입력 스키마의 기본 94개 글쇠는 언제나 Alt, Ctrl이 눌러져 있지 않을 때만 동작하게 동작이 바뀌었으며 해당 변수는 제공하지 않는다. Alt/Ctrl까지 동원해야 하는 복잡한 글쇠 조합은 고급 입력 스키마로 구현하면 되고, 또 기본 입력 스키마에도 그런 사용자 정의 글쇠 배당 기능이 6.5 버전에서부터 따로 추가되었으므로 이를 활용하면 된다.

지난 7.4 버전은 긴 시간 동안 한글 입력 관련 기존 코드들을 싹 정리하면서 각 개체와 계층들의 역할을 재정리하고 프로토콜도 다시 설계했다. 즉, 리팩터링의 비중이 컸다. 그러면서 이런 미세한 기능들은 좀 예고 없이 변경해도 여파가 별로 없겠다 싶은 것들을 과감하게 뜯어고쳤다. 그랬는데 본인은 <날개셋> 한글 입력기 헤비 유저들의 창의성을 너무 얕잡아 봤던 듯하다.

7.4의 공개 직후에, 잠수함 패치 때문에 예전에 되던 기능이 갑자기 안 되어서 불편을 겪으신 분께 사과드리며, 앞으로는 이런 변화는 충분히 예고를 드리도록 노력하겠다.
특히 고급 입력 스키마의 새 기능을 적극 사용하면서 여러 질문과 버그 신고를 해 주신 김 기윤 님께 감사드린다.

※ 다음 버전 개발 근황

7.4는 큰 버그 없이 잘 만들어진 듯하다. 딱 이 정도 만들어졌을 때 커트를 하여 새 버전을 내놓은 것도 매우 적절했다.
새로 추가된 고급 입력 스키마에서 이것저것 미흡한 점과 개선할 점들이 보이지만 그건 시간과 여유의 부족으로 인해 애초에 고려를 못 했던 부분들이다. 새 기능으로 인해 기존 기능에서 예기치 못한 오동작이 발생한다거나 프로그램의 안정성과 관련된 치명적인 버그가 생긴 것은 없다.

7.4의 다음 버전은 7.5로 계획하고 있다. 7.4를 마무리 지으면서 나머지 잔여 기능들은 하반기에 나올 다음 버전을 기약하기로 그때부터 이미 결정을 해 놨다.
다음 버전의 개발 방향은 (1) 7.4에서 미처 못 끝낸 입력 엔진 리팩터링과 개선, (2) 고급 입력 스키마의 기능 마저 구현, (3) 기타..로 나뉜다. 다만, 이것저것 넣고 싶은 기능들을 다 구현한다면 변화량이 7.5 수준을 초과할지도 모른다.

(1) 고급 입력기의 기능을 활용하고 나면, 두 글자 이상 길이의 조합을 만드는 게 가능해진다. 그러나 조합 중에 C0|0xD (앞쪽으로 조합 중단) 같은 특수글쇠를 집어넣어 보면, 현재는 정확하게 조합의 앞에 cursor가 놓이는 게 아니라 조합의 뒤에서 한 글자 앞에 cursor가 놓이게 된다. 기본 입력기의 기능만 활용해서 한글 한 글자만 조합 중일 때에야 이런 동작 방식이 문제가 없지만, 범용성 면에서 이런 동작 방식은 문제가 있다.

이렇게밖에 할 수 없는 것은 내부적으로 여러 한계가 있기 때문인데, 이 한계를 극복하는 작업을 7.4에서 완전히 마무리 짓지 못했다. 그래서 다음 버전에서는 이를 지체없이 이어서 진행할 예정이다.

또한, 한글로부터 입력 순서를 찾아내는 알고리즘, 인접한 두 한글의 연속 입력 가능 여부를 판별하는 알고리즘을 처음부터 깔끔하게 다시 작성할 예정이다. 지금 작성된 코드는 긴 세월이 흐르면서 나중에 추가된 특수 도깨비불 규칙, 종성 지향 두벌식, 오토마타의 O 변수 같은 복잡한 기능들을 정확하게 반영하여 처리하지 못하며 구조적으로 확장하기도 어려운 형태이다.

주어진 입력 설정으로부터 최적의 한글 입력 sequence를 찾는 알고리즘은 한글 입력기가 제공할 수 있는 가장 유용하고 멋진 자동화 기능이므로 올여름에 심혈을 기울여 재개발을 할 것이다.
뭐, 이것 말고도 지금 모든 내역을 밝힐 수 없는 다른 리팩터링 작업들도 남아 있음.

(2) 고급 입력 스키마에 아직 숙제로 남은 여러 미흡한 기능들을 개선하고, 본격적으로 한글 동시치기와 관련된 추가 옵션들을 구현할 예정이다. 세벌식 자판도 직전 한두 타를 아주 빠르게 누른다거나 하면 조합 중이던 자모가 다음 글자로 넘어가는 '도깨비불' 현상이 발생할 수 있게 된다.

(3) 이것도 모든 내역을 당장 밝힐 수는 없는 여러 이슈들이 현재 존재한다. 아, 입력 패드와 관련된 내용을 잠시 후에 언급하도록 하겠다.

한편, 타자연습은 다른 특이점은 없고 새 연습글들을 허겁지겁 추가하다 보니 오타가 생각보다 많이 발견되어 이걸 고쳤다. 버전 번호를 바꾸지는 않을 것이고, 입력기 새 버전이 나오는 날에 타자연습을 동일 버전으로 다시 배포할 생각이다. 그렇기 때문에 기존 3.4 사용자라면 지금 쓰는 타자연습을 제거한 후, 새 3.4를 받아서 다시 설치해서 사용하시면 될 듯하다. 연습글의 오타 신고는 지금까지 박 철현 님께서 가장 열성적으로 해 주셨다.

※ 입력 패드의 버그 수정

<날개셋> 한글 입력기의 구현체들 중 입력 패드는 7.11에서 7.4로 넘어가는 그 격변기에도 큰 변화가 없었으며, 얘는 앞으로 거의 건드릴 일이 없을 거라 여겨졌다.
그러나 다음 7.5 버전에서는 이 프로그램과 내부 hook DLL이 정말 오랜만에 좀 고쳐질 예정이다.

예전에 외부 모듈은 Excel이나 Paint.NET 같은 프로그램에서 한글을 처음 입력할 때 첫 타 조합이 끊어지거나 덧나는 등 자잘한 문제가 있었다. 엄밀히 말하면 내 프로그램의 로직에 문제가 있긴 하지만, 그 프로그램들이 또 좀 특이하게 동작을 해서... 서로 조심을 안 해서 발생하는 문제였다.

그래서 외부 모듈은 한동안 특정 프로그램에서만 인위적으로 다르게 동작하는 예외 로직을 넣어서 문제를 피해 갔었으나, 지난 7.4에서는 오랜 연구 끝에 그 문제를 드디어 완벽하게 해결했다.
그러나 그 방법이 외부 모듈 말고 입력 패드에는 적용되지 않아 있었다. 여전히 Excel에만 예외 로직이 적용되어 있었으며, Paint.NET 같은 다른 프로그램에서는 글자가 덧나는 문제가 남아 있었다. 단지 입력 패드는 편집기나 외부 모듈보다 훨씬 덜 쓰이는 잉여 프로그램이기 때문에 문제의 심각성이 덜 부각되었을 뿐이다.

그러다가 이번에 입력 패드에까지 새로운 해결책을 적용하여 그 문제를 해결했다.
원래 입력 패드는 hooking을 이용하여 변칙적인 꼼수를 부리며 동작하는 위험한 프로그램이기 때문에 이 문제는 외부 모듈만치 깔끔하게 해결하기가 어려웠다. 이 문제를 해결하느라 다른 부작용(side effect)이 생기지는 않았는지를 아주 꼼꼼하게 테스트해야 했으며 이 과정이 대단히 힘들었다.

또한, '한손 입력기'처럼 내부적으로 독자적인 문자 생성기를 가진 도구를 사용했다가 닫은 뒤에 '화면 키보드' 같은 다른 도구로 문자 입력을 시도했을 때 프로그램이 죽는 문제를 해결했다. 늘 나타나는 현상이 아니어서 그저 그러려니 싶었는데.. 확실하게 코딩 실수가 있는 걸 발견했다. 이 두 가지 버그가 현재 해결되었다.

system hook 프로그래밍을 해 본 분들은 아시겠지만, hook을 사용하는 프로그램이 hook을 해제하고 종료한 뒤에도 어찌 된 일인지 그 hook DLL은 오랫동안 메모리에서 완전히 제거되지 않고 남아 있는 경우가 많다. 그래서 그 hook DLL의 소스를 고쳐서 다시 빌드를 하려 해도 덮어쓰기가 안 되고 진행이 안 되는 경우가 많다.

자잘하게 코드 한두 군데만 고친 뒤 결과를 확인하려고 하는데 매번 운영체제 로그인을 다시 해서 상태를 초기화해야 하니, hook 개발은 IME 개발보다도 더 불편한 애로사항이 있었다. IME는 그래도 프로그램만 확실히 종료하고 나면 업데이트가 안 되는 불편은 없기 때문에 말이다. 예전에 XP/Vista 시절에는 명령 프롬프트 디버깅을 한 뒤에는 conime.exe를 매번 죽여 줘야 해서 불편했지만, 그건 7부터는 개선됐다.

아무튼 본인으로서는 문제를 최대한 해결하여, 지금 동작이 예전의 문제를 해결하면 해결해서 더 낫게 만들면 만들었지, 최소한 예전보다 상태를 더 '나쁘게' 만든 것은 없음을 확인했다. 말은 이렇게 했는데 또 버그가 발견되면 그러면 할 말이 없긴 하지만...

여담인데, Internet Explorer의 웹페이지 내부에 있는 폼에다가 입력 패드로 문자를 입력해 보면,
IE 10까지는 조합이 일반적인 IME로 한글을 입력할 때와 마찬가지로 본문에다 네모 사각형 모양으로 제대로 생긴다. 그러나 유독 IE 11부터는 조합이 그렇게 처리가 안 되고 마치 IME-aware하지 않은 프로그램처럼 프로그램 밖의 조합 창에 따로 생기는 형태로.. 시각적인 피드백이 좀 불편해졌다. 어째 이런 것에도 미세한 변화가 생겼다.

※ 기타 공지

1. 다음 버전에서는 문자 생성기를 가리키는 명칭에 날개셋이라는 단어가 빠지고, 입력 스키마의 명칭과 마찬가지로 그냥 빈 / 기본 / 고급이라는 3단계 수식어만 붙을 예정이다. '빈 입력기, 기본 입력기, 고급 입력기'.
날개셋이라는 이름이 왜 지금까지 있었느냐 하면, 오토마타라든가 각종 특수글쇠처럼 <날개셋> 한글 입력기의 가장 중요한 핵심 기능들이 구현된 계층이 문자 생성기이기 때문이다. 쉽게 말해 상징적인 의미 때문이다. 하지만 그건 군더더기라 여겨져서 다음 버전부터는 빼게 되었다.

2. <날개셋> 한글 입력기가 버전을 매기는 방식은 통상적인 소수점과 완전히 동일하다. 가령, 버전 7.4와 7.40은 동일한 표기이며 7.4는 7.11보다 더 높은 버전이다. 다만, 공식적으로는 끝의 0은 생략하여 7.40 대신 7.4라고 표기하는 것을 권장· 선호한다.

3. 그리고 이것도 한 번쯤 언급할 필요가 있어서 입장을 확실히 밝히고자 한다. <날개셋> 한글 입력기는 브랜드 이름을 지금까지 less than과 greater than 부호로 감싸는 이상한(?) 관행이 존재해 왔다. 이것은 세벌식 최종 글자판이 ()와 <>를 아랫글쇠로 입력할 수 있다는 것에 착안하여 별 생각 없이 붙이게 됐는데..
반드시 강요는 안 한다. 파일 이름이나 HTML 태그 같은 상황에서 <>를 붙이기가 대략 난감할 때는 얼마든지 생략해도 된다. 영문 표기에서도 당연히 생략.
하지만 한글로 full name을 공식 표기할 때는 <>를 넣는 것을 원칙으로 삼고자 한다. ㅎㅎ 여기에도 이런 내력이 있는 셈이다.

4. 진짜 마지막으로... 간단한 내용이지만 최근에 문의를 한 분이 있어서 또 공지하도록 하겠다.
입력기와 타자연습을 모두 사용하시는 분이라면, 두 프로그램을 모두 최신 버전으로 업데이트할 것을 강력하게 권한다.

타자연습도 실행에 필요한 최소한의 파일들은 자체적으로 모두 갖추고 있으며 입력기와 독립적으로 동작하는 게 가능은 하다. 그러나 입력기와 타자연습이 서로 API 호환이 되지 않으면 타자연습은 입력기의 모든 기능을 100% 활용할 수가 없게 된다. 특히 플러그 인을 사용할 수 없게 되기 때문에 고급 입력 스키마나 고급 입력기 같은 기능을 사용한 입력 설정이 무용지물이 된다. 반드시 업데이트를 해야 한다.
이번에 타자연습은 3.31 이후 거의 1년 반 가까이 버전업이 없다가 3.4로 업데이트됐기 때문에 문의가 들어올 만도 했던 것 같다.

Posted by 사무엘

2014/07/10 19:32 2014/07/10 19:32
Response
No Trackback , 5 Comments
RSS :
http://moogi.new21.org/tc/rss/response/983

노트북 PC에서 가장 흔하게 발생하는 잔고장의 양대 산맥은 (1) 액정 화면 접촉불량과 (2) 키캡 빠짐이라 해도 과언이 아닐 것이다.

본인이 개인용 노트북 PC를 사용한 지가 15년이 넘었고 지금 쓰는 맥북은 제 5대 컴퓨터인데,
예전에 쓰던 노트북들은 쓴 지 1년 남짓 되면 저런 잔고장이 어김없이 발생하곤 했다.
물론, 언제나 새 노트북만 쓴 게 아니라 중고나 준중고를 쓴 것도 있기 때문에, 품질이 원래부터 안 좋아서 그랬던 것일 수도 있다.

(1)은 화면을 편 각도에 따라서 붉은 세로줄이 나타났다가 사라진다거나, 화면이 아예 꺼진다거나 하는 등 굉장히 성가신 증상이다. 이것 때문에 서비스센터 들러서 수리 받느라 신경 쓰이고 시간· 돈이 낭비되는 것도 과거엔 상당한 수준이었다.

(2)도 노트북 키보드가 데스크톱 PC 키보드보다 약해서 발생하는 고질적인 문제인 듯.
자주 누르는 편인 화살표, 엔터, space, shift 같은 게 잘 빠졌으며 가끔은 문자 키가 빠지기도 했다. 키캡이 없어도 해당 키를 누르는 것 자체는 가능하지만, 빠른 타자와 원활한 컴퓨터 사용에는 애로사항이 꽃핀다.
빠진 키캡 한두 곳만 수리가 되지는 않는 경우가 많다. 그래서 키캡이 대여섯 개쯤 빠질 때까지 컴을 더 쓰다가, 나중에 한꺼번에 키보드 기판을 교체하곤 했던 것 같다. 돈 몇만 원 정도는 깨진다.

그런데.. 지금 쓰는 맥북은.. 쓴 지 2년이 넘었지만. 위의 잔고장이 지금까지 전혀 발생한 적이 없다. 엔터 키가 약~간 달랑달랑거리긴 하지만 그래도 키캡이 완전히 빠진 건 없다. 잡느님의 손길이 담긴 품질 덕분인 걸까? 놀랍다.
맥북은 한번 병원 치료를 받으면 일반 놋붉에 비해 수리비가 작살나게 깨진다는 게 겁나긴 했었지만, 아직까지 A/S를 받을 일 자체가 전혀 없었다.
(참고로 난.. 흔들리는 교통수단 안에서도 쓰고 몇 번 떨어뜨린 적도 있고, 노트북 PC를 시도 때도 없이 험악하게 혹시시키며 쓰는 편이다.)

따라서 애플케어를 구입 안 한 게 현재로서는 결과적으로 이익이었다.
난 비록 맥북으로도 95% 이상의 시간은 맥OS가 아닌 Windows만 쓰며 지내지만 맥북의 이런 품질은 만족스러우며 인정할 만하다.

다만, 세월 때문인지 배터리 용량은 예전보다 확실히 줄어들었다. 2시간 남짓이면 간당간당하다. 배터리는 아직 구경도 못 해 봤다.
그리고 얼마 전엔 맥북을 쓴 지 2년 3개월 만에 처음으로 기능 고장을 경험했다. 본체나 LCD 쪽은 아니고, 전원 어댑터가 문제가 발생했다.

여기저기서 전선의 피복이 슬슬 벗겨지면서 속의 금속선들이 드러나 보이기 시작했다. 벗겨진 부분에다가는 테이프를 감았으며, 노출 부위 때문에 감전이나 화재 사고가 날까 두려워서 잘 때나 부재 중일 때는 전원 플러그를 빼 두기 시작했다.
그러더니 나중엔 컴퓨터 본체와 연결하는 목덜미 부분이 너무 너덜너덜해진 나머지 거의 두 동강 나다시피하면서 결국 단선됐다. 전원을 연결해도 본체에 전원 공급이 되지 않기 시작했다. 컴퓨터 본체는 이제 2시간짜리 시한부 인생으로 전락했다.

어댑터 전선에 피복이 벗겨지면서 고장이 이런 식으로 발생하는 노트북 컴은 맥북이 처음이다. 그런데 다른 맥북/아이폰 사용자에게 물어 보니, 애플 제품은 그런 식으로 전원 어댑터 내지 충전기가 망가지는 건 생각보다 흔한 일이라고 한다.

어댑터 자체는 이상이 없고 전선만 망가진 건데 어째 물건을 재활용할 수는 없나 궁금하다. 물론, 콘센트 쪽 전선이 아니라 컴퓨터에다 꽂는 쪽의 전선이다 보니 어댑터와 분리가 가능하지도 않고 가능하지는 않을 것 같다.

지금은 어댑터를 새로 구입해서 상황을 마무리 지었지만, 약 3~4일 동안 내 컴을 못 쓰면서 좀 불편하게 시간을 보냈다. 다음부터는 좀 불편하더라도 컴퓨터 본체와 어댑터 선이 언제나 같은 높이와 평행한 각도가 유지되게 해 놓고 써야겠다. 목덜미 부근엔 수축 튜브를 감아 주고, 전선을 둘둘 감아서 가방에 넣는 것도 굉장히 조심해서 해야 할 것 같다.

* 다음은 여담.

1. 맥 OS가 의외로 굉장히 유용한 경우가 있는 걸 본인이 얘기한 적이 있나 모르겠다. 바로 학교 안에서이다.
Windows의 경우 컴퓨터의 속도를 한 반쯤 깎아내리는 엄청난 백신, 보안 솔루션 등을 강제로 설치해야만 교내 무선 인터넷 이용이 가능한 반면, 맥 OS는 그런 것 전혀 없이도 바로 인터넷 이용이 가능하기 때문이다. 아주 편리하고 좋다.

2. Windows 진영에서는 Metro라는 이름을 안 쓰는 게 마음에 안 들던데, 애플 진영에서는 매킨토시로도 모자라서 Mac이라는 이름까지 왜 빼 버렸나 궁금하다.. 자기 정체성을 가장 잘 분명하게 보여 주는 명칭을 빼 버리고 그냥 OS X...;;;

3. 그러고 보니, 플래시가 깔려 있지 않은 맥 OS의 사파리 브라우저에서 유튜브 동영상이 나오고 있어서 깜짝 놀라서 살펴봤더니.. 말로만 듣던 HTML5 기반 동영상이다. 우와~ 물론 비스타+IE9 구닥다리에서 실행해 보니 여전히 플래시다. 브라우저에 따라 기술을 알아서 판별하는 듯하다.

솔직히 플래시든 뭐든 인터넷으로부터 패킷을 받아서 하드웨어 가속으로 동영상을 틀어 준다는 기본 원리는 똑같다. 단지, 동영상을 해독하고 재생하는 기계어 코드가 예전에는 플래시 ActiveX라는 일종의 private한 영역에 있었던 반면, 이제는 그게 통일된 규격으로 웹브라우저에 있다는 차이만이 존재할 뿐이다. 웹 표준은 기술 자체가 아니라 기술을 담는 그릇의 규격에 대한 논쟁인 것이다.

Posted by 사무엘

2014/07/08 08:29 2014/07/08 08:29
,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/982

근황 + 컴퓨터 음악 이야기

올해 상반기는 박사 첫 학기 진학과 <날개셋> 한글 입력기 7.4 완성이라는 본인의 개인사와는 대조적으로, 분위기가 은근히 우울했다.
월드컵 축구 경기는 기대를 저버리는 졸전 끝에 별 재미를 못 봤고, 신촌 동성애 퍼레이드에다 어째 1993년에 벌어진 것과 비슷한 패턴의 사건· 사고가 두 건이나 벌어졌다. 세월호(1993년의 서해 페리호), 그리고 총기 난사+무장 탈영(1993년의 임 채성 무장 탈영 인질극) 말이다. 정치판에서 돌아가는 큼직한 사건들은 좌파와 우파 성향 모두에게 최악의 실망만을 안겼다.

뭐 어쨌거나.. 이번 학기에 본인은 아직 프로그램 개발에 더 전념하려고 수업은 2개만 들었다. 언어학 문법 수업은 배경 지식이 부족하다 보니 확실히 어려웠다. 그래도 다음 학기에 프로그래밍 언어와 관련된 수업을 들으면서 경험을 서로 대조해 보면, 자연어와 인공 프로그래밍 언어가 개념적으로 어떤 관계가 있는지를 조명할 수 있을 것 같다.

다음으로 학기 초부터 기대했던 컴음악은 어려운 한편으로 재미있었다.
다른 학생들이 텀 프로젝트를 발표한 걸 보니 모바일 또는 웹으로 간단한 미디 음악 시퀀서를 만든 경우도 있고, 미디 데이터를 일정 규칙대로 변조하거나 climax를 찾는 알고리즘을 연구하기도 했다.
그 담당 교수님의 연구실에 들어가 있는 학생들은 여러 이미지와 여러 음악들을 분위기에 맞는 것끼리 서로 짝지어 주는 솔루션을 주로 내놓았다.

나도 처음에는 멜로디로부터 chord를 자동으로 매긴다거나 다른 감정 같은 의미를 읽어 내는 프로그램을 생각하고 있었으나.. 곧 단념했다. 그런 것도 없는 곡을 새로 만드는 것만큼이나 엄청 어렵고 창의적인 일이라는 걸 곧 인지했기 때문이다.

결국, 컴음악 시간에는 이 기회를 이용해 죽이 되건 밥이 되건 자동 작곡을 하는 프로그램을 만드는 걸로 방향을 선회했다. 복잡한 요소들 싹 다 제끼고 오로지 초등학교 음악책에 나오는 동요 급의 간단하고 짤막한 단음 멜로디를 생성하는 것.

음악이라는 게 어느 정도는 무질서하고 무작위한 요소가 있지만, 그 뒤부터는 정말 인간의 언어만큼이나 극도로 예전 문맥에 의존적이고 체계와 질서가 있어야 한다. 그게 없이 그저 rand()의 결과대로 멜로디와 박자를 뿌리면 그건 음악이 되지 않고 아무 호소력이나 메시지, 시너지 효과가 안 나오는 횡설수설 노이즈밖에 만들어지지 않는다.

연구하면 할수록 음악도 마치 문장을 구문 분석하듯이 계층이 존재한다는 걸 알 수 있었다. 각 마디 안의 음표들은 일정 chord 범위에 드는 한도에서 생성되는데, 그 chord가 바뀌는 규칙은 또 다른 상위 계층에 따라 정해지고, 그 계층의 상위 계층은 또??

맨 처음엔 박자를 재귀적으로 무작위 생성하는 것부터 해 봤다. 음색이 들어간 음악이 말 그대로 색, 컬러 그림이라면, 박자는.. 그냥 흑백 그림에 가깝다고 하겠다. 박자도 생각보다 가짓수가 많았으며, 취사선택을 잘 해야 했다.

2 1 1 2 1 1
8
3 1 4
4 2 2
3 1 4
8
2 1 1 2 2
6 2
4 2 2
3 1 2 2
2 2 3 1
8
2 2 2 2
3 1 2 1 1
6 2
3 1 4
8
4 2 1 1
4 2 2
6 2
1 1 1 1 2 2
1 1 1 1 4

아주 공교롭게도, 마지막 두 박자는.. 바로 “노래 시작했다 노래 끝났다 / 그런 노래 없다 다시 불러라”의 박자와 동일하다.. ㅋㅋㅋㅋ 컴퓨터가 그런 박자도 만들어 냈다. 하지만 박자에다가 음색을 입히는 건 훨씬 더 어려운 일이었다.

처음엔 1도 화음에서 시작하고 끝은 언제나 '도'로 끝나고, 주어진 chord와 어긋나는 음은 약박에만 들어가게 하고,
같은 음 반복, 1도씩 증가, 1도씩 감소 같은 인위적인 규칙을 일정 확률로 준 결과, 노이즈보다는 듣기 좋은 멜로디가 종종 생성되었다. 아 물론, Looking for you나 Let it go 같은 퀄리티를 기대해서는 안 되지만..ㅎㅎ

텀 프로젝트를 발표한 후 교수님의 반응은 “(1) 마디별 코드 전환이 부드럽고 꽤 그럴싸하게 작곡이 잘 됐다. (2) 단, 당초 계획만치 참고문헌 내용이나 관련 기존 연구 성과가 반영되지 못한 것은 아쉬움”이었다.

내가 목표했던 퀄리티는 그냥 별 생각 없이 콧노래를 흥얼거리는 수준의 작곡이었다. 그런데 사람은 무작위로 흥얼거린다 해도 결국 예전에 자기가 들어서 기억하고 있는 음악을 변형하는 형태인 반면, 컴퓨터는 그런 경험 데이터가 없이 난수 생성만으로 음악을 만든다. 결국 컴퓨터도 사람에게 듣기 좋은 음악을 만들려면 모방이 필요하며 기존 음악 패턴에 대한 데이터가 아주 없어서는 안 될 것 같다.

이래저래 음악은 탁월한 수리 능력에다가 창의성까지 갖춘 괴수 천재들을 매혹시키기 충분한 분야이다. 리서치 과정에서 Musimathics라는 책을 참고했는데, 저자는 이건 뭐 수학, 전산학에 소리를 물리· 전자공학적으로 분석하는 것까지 완전 다 통달한 천재다. 전자공학의 푸리에 변환도 나오고, 작곡 방법론에서 난수 생성 얘기를 하는 데서는 ACM 논문까지 소개한다.

나 또한 딴 건 몰라도 음악의 위력에 대해서는 Looking for you와 관련하여 할 말이 무진장 많다. 이런 음악 한 곡도 지적 설계자의 치밀한 설계 없이는 만들어질 수 없는데 하물며 이 세상 만물이겠는가 하는 생각도 응당 들었다.

사실, 과거에 마소에서는 미래엔 이렇게 실시간으로 작· 편곡을 하여 분위기에 맞는 음악을 만들어 내는 게임이 등장할 걸로 예상하고 DirectX에다가 미디 기반의 DirectMusic이라는 컴포넌트도 만들었다. 1990년대 말이면 아직 소프트웨어 기반 미디 신시사이저도 흔치 않던 시절이어서.. 하지만 게임 음악 기술은 음표 신호 방식이 아닌 waveform 방식으로 대세가 완전히 바뀌었으며, DirectMusic도 개발이 중단된 흑역사로 전락했다. 아쉽다면 아쉬운 점이다.

다음은 여담이다.

1.
어떤 학생이 자기 결과물을 발표하고 시연하는 중이었는데, 파일을 기록했다는 영문 메시지가 writed라고 뜨고 있었다. 본인은 그걸 보면서 속으로 뿜었다. 우와, 정말 생각도 못 했던 단어다.
그런데 집에 돌아와서 내가 발표했던 PPT 자료를 다시 보니, 나도 ‘결론’을 Conclusition이라고 써 놔 있었다. ㅋㅋㅋㅋㅋ

다들 시간에 쫓기는 상태로, 혹은 밤샘 하고 나서 머리 가동률이 70% 이하로 떨어진 상태로 비몽사몽 발로 영작을 하다 보니 저런 오타들이 나왔는가 보다.

2.
본인은 작곡한 멜로디를 출력할 때 미디 API를 쓴 게 아니라, 과거 BASIC에 존재하던 PLAY문과 비슷한 기능을 하는 함수를 직접 만들어서 썼다. 지정된 음의 주파수에 해당하는 sine wave를 생성해서 오디오로 출력하는 것이다. 난생 처음으로 waveOut*** 어쩌구 하는 함수들을 직접 공부해서 써 봤다.

난 간단한 콘솔 프로그램을 만드는 게 목적이기 때문에 파일 재생 관련 통지를 그냥 callback 함수 호출로 받게 했는데... 뭐가 이상하게 꼬이면서 잘 되질 않았다.
한참을 디버깅 하다가 시간도 부족하고 설마 하는 심정으로 message-only 윈도우를 만들고 통지를 메시지로 받게 했더니.. 모든 문제가 싹 해결되었다. 메시지가 진리. 두 메커니즘의 차이가 뭔지는 잘 모르겠다.

그래픽 애니메이션 출력 때 더블 버퍼링을 하는 것만큼이나, 실시간으로 오디오 데이터를 보내는 것도 더블 버퍼링 기법이 쓰인다는 게 흥미롭다. 관련 코딩을 해 보신 분이라면 이미 잘 아실 것이다.
버퍼 A의 내용이 사운드 카드로 가서 재생되는 동안 미리 버퍼 B의 내용을 보내 놓고 기다리고, 다음으로 버퍼 B가 처리되었으면 다시 버퍼 A에다가 다음 내용을 보내는 것이다.

Posted by 사무엘

2014/07/05 08:36 2014/07/05 08:36
, ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/981

컴퓨터 화면에 그려진 어떤 물건(주로 사각형 모양)의 경계 테두리에다 마우스를 갖다대면 포인터가 그 경계 테두리와 수직인 방향의 화살표로 변한다. 그 상태에서 마우스를 클릭하여 끌면, 그 물건의 크기를 마우스 포인터가 움직이는 방향으로 변경할 수 있다.
이것은 GUI에서 매우 흔히 볼 수 있는 기능이다. 특히 크기 조절 가능한 창--운영체제가 정식으로 제공하는 GUI 구성요소--의 경우 이런 기능은 운영체제가 non-client 영역에서 알아서 자동으로 처리해 준다.

그런 창이야 운영체제가 처리를 알아서 해 주지만, 대화상자 내부의 임의의 영역에 대해서, 혹은 클라이언트 영역에 내가 그려 주는 임의의 객체에 대해서 이런 처리를 구현하려면 어떻해야 할까? 뭔가 공통된 패턴의 알고리즘을 처리해야 할 텐데 코딩량이 적지는 않으며 왠지 귀찮고 번거로워 보인다.

크기 조절 내지 화면 분할 UI와 관련해서는 다음과 같은 여러 상황을 생각할 수 있다.

1. 한 윈도우 내부에 그려지는 개체의 크기 조절

2차원 벡터 그래픽 프로그램 내지 RAD 툴의 폼 에디터가 정확하게 여기에 속한다. 요즘은 워드 프로세서도 자체적인 벡터 그래픽이나 하다못해 OLE 개체라도 취급하니 마우스를 이용한 크기 조절 기능을 제공해야 한다. 개체를 클릭하면 8군데의 앵커 사각형이 생기며, 경계 아무 곳이라기보다는 그 앵커 사각형을 드래그했을 때 크기 조절이 된다. 다른 곳을 드래그하면 크기 조절이 아니라 이동이 되고. 그리고 요즘 MS 오피스 제품은 회전용 앵커까지 덤으로 제공한다.

비주얼 C++에는 CRectTracker라는 고전적인 클래스가 있어서 마우스 드래그로 임의의 사각형 영역을 화면에다 그리고 마우스가 클릭됐을 때의 일체의 처리를 알아서 해 준다. 이것만 쓸 줄 알아도 상당히 편리한데, 본인은 지금까지 그런 분야의 프로그램을 개발할 일이 없다 보니 실제로 써 본 적은 전혀 없다. 이런 게 있다는 것만 안다.

그리고, 지금도 있나 모르겠다만, 비주얼 C++에는 DrawCli라고 딱 개체 기반 벡터 드로잉과 드래그 드롭, 크기 조절과 이동을 모두 시연해 놓은 걸출한 예제 프로그램이 있다.
학창 시절 나의 친구였던 <비주얼 C++ 완벽 가이드>(김 용성)에도 '트래커'라는 예제 프로그램이 있으니 참고할 것.

사용자 삽입 이미지

참고로, 저렇게 테두리를 그리고 사각형 안의 내용물은 대각선 사선으로 칠하는 건.. embed된 OLE 개체를 외부 프로그램이 수정 중일 때 클라이언트 프로그램이 표시하는 표준 모양이기도 하다.

사용자 삽입 이미지

2. 부모 윈도우가 자식 윈도우를 동적으로 분할하고 관리

규모깨나 좀 있는 문서 편집 프로그램을 보면, view를 분할하는 기능이 있다. 이것은 한 문서 컨텐츠에 대해 뷰 윈도우를 여러 개 둬서 한 컨텐츠를 여러 가지 다른 방식, 다른 위치를 표시할 수 있게 한다. 사용자의 입장에서 매우 편리한 기능이지만, 컨텐츠와 뷰 윈도우가 완전히 일심동체라고 전제하고 만들어져 버린 프로그램이라면 추후에 이러 기능을 추가하기가 쉽지 않을 것이다. MDI 프로그램이라면 아예 별도의 독립된 창을 만드는 기능도 있겠지만 SDI에서는 한 창을 분할하는 것만 가능하다.

이건.. 생각보다 만들기 어려운 기능이다. 경계(splitter) 부분을 마우스로 끌었을 때의 처리도 처리거니와--이를테면 XOR 연산으로 자취를 그렸다가 지우는 것도..--, 내가 무엇보다도 힘들겠다고 느끼는 건 스크롤 바를 자체적으로 따로 만들어서 관리하는 부분이다. 이게 무슨 뜻인지를 설명하자면 이렇다.

Windows 운영체제에는 어떤 윈도우가 자체적으로 스크롤 바를 가질 수 있고, 한편으로 스크롤 바 자체가 별도의 컨트롤로 존재할 수 있다.
윈도우가 자체적으로 갖는 native 스크롤 바는 당연히 운영체제가 모든 처리를 알아서 해 준다. 창의 크기가 바뀌어도 스크롤 바를 자동으로 우측(상하) 내지 하단(좌우)에 배치해 주고, 스크롤을 할 필요가 없어지면 알아서 스크롤 바가 없어지고 그 영역까지 클라이언트 영역이 확대된다.

그러나 splitter가 존재하는 view를 보면, 스크롤 바가 있던 자리의 구석 일부에 창을 분할시키는 앵커가 자리잡고 있다. 이건 native 스크롤 바로 구현 가능하지 않기 때문에 스크롤 바 컨트롤을 따로 만들어서 앵커 밑이나 옆에다 두고, 스크롤 바의 위치· 크기와 관련된 모든 처리를 수동으로 해야 한다. 이 얼마나 복잡하고 손이 많이 갈까? 그러니 MFC가 CSplitterWnd라는 클래스에다 전부 구현해 놨다.

사용자 삽입 이미지

MFC AppWizard에서 '뷰 분할 기능 사용'을 체크하면, 프레임 윈도우는 밑에다 저 splitter 윈도우를 생성하고 걔가 또 자기 밑에다 어떤 view를 생성할지를 따로 지정해 준다. 다시 말해 프레임 윈도우와 view 사이에 splitter라는 중간 계층 윈도우가 하나 또 생긴다는 것이다. 그리고 요놈이 스크롤 바와 앵커의 위치를 관리하고 앵커 드래그에 대한 처리도 담당한다.

사용자 삽입 이미지

내가 MFC의 splitter 윈도우에 대해 꽤 놀란 것은, 분할을 2개씩뿐만 아니라 그 이상도 얼마든지 할 수 있다는 점이다. 위의 그림처럼 가로 3개, 세로 3개를 분할해서 무려 9개나 되는 view 윈도우를 뻥튀기시킬 수도 있다. 가로와 세로의 splitter 중 어느 것 하나의 위치가 바뀌었을 때, 혹은 창 전체의 크기가 바뀌었을 때 splitter가 총체적으로 조율하고 해야 할 일의 양도 그에 비례해서 많아질 것이다.

그런데 각각의 창들이 다 독자적인 가로· 세로 스크롤 바를 갖는 건 아니고.. 위의 스크린샷에 보듯이 한 column에 해당하는 view들은 가로 스크롤 바를 하나 다같이 공유한다. 그리고 한 row에 해당하는 view들은 세로 스크롤 바를 다같이 공유한다. 특이한 점임.

3. 부모 윈도우가 자식 윈도우를 '정적'으로 분할하고 관리

위의 2번과 마찬가지로 부모 윈도우가 자식 윈도우의 분할을 관리하는 경우이긴 한데, 위처럼 성격이 비슷한 윈도우가 아니라 별개의 윈도우를 고정적으로 관리하는 경우를 추가적으로 생각할 수 있다. 처음엔 1개였다가 2개 이상으로 자유자재로 분할되는 건 아니기 때문에, 스크롤 바나 앵커 같은 건 없다.

왼쪽에 트리 컨트롤, 오른쪽에 리스트 컨트롤을 두고 가로로 크기 조절이 가능한 탐색기 같은 프로그램도 좋은 예이고, 그보다 좀 더 복잡한 경우로는 개발자들의 친구인 Dependency Walker가 있다.

사용자 삽입 이미지

Spy++ 같은 프로그램으로 들여다보면, 창 구조가 생각보다 복잡하다는 걸 알 수 있다.
가장 겉에 있는 창은 화면을 상하로 나눈다. 위에 있는 창은 화면을 또 좌우로 나눠서 왼쪽은 모듈 트리 구조가 나오며, 오른쪽은 또 상하로 나누어서 각각 이 모듈이 import하는 심벌들, 그리고 대상 모듈이 export 하는 전체 심벌들이 표시된다.
한편, 아래의 화면은 또 상하로 나뉘어서 위에는 전체 모듈 리스트가 있고 아래에는 메시지 log가 있다.

즉, 한 윈도우에 여러 개의 컨트롤들이 sibling 관계로 대등하게 늘어서 있는 게 아니라, 또 분할 윈도우가 있고 그 아래에 또 분할 윈도우가 자식 윈도우로 있는 형태다. 한 분할 윈도우는 언제나 좌우로든 상하로든 2개의 윈도우만을 담당한다.

이렇게 이분법적으로 접근하면, 제아무리 복잡하게 화면이 좌우 상하로 분할되어 있는 창이라 해도 전체 크기가 바뀌었다거나 할 때 자기가 맡은 두 개의 창만 비율 분배를 잘 하고 나머지는 자가반복적인 재귀 처리에 맡기면 되니 문제가 단순해진다는 장점이 있다.

저런 윈도우가 활용의 자유도가 더욱 올라간다면 아예 별도의 창으로 분리하거나 docking까지 가능해진다. Visual Studio의 각종 보조 윈도우들처럼 말이다. 그건 우리 같은 평범한 프로그래머가 밑바닥부터 할 짓이 못 되며, 이미 있는 GUI 라이브러리의 사용법을 익히는 것만으로도 충분할 것이다. MFC 없이 Windows API만으로 docking toolbar를 구현한 소스를 외국 사이트에서 본 적이 있는데, 가히 근성이 느껴졌다.

사용자 인터페이스라는 건 컴퓨터로 하여금 의미 있는 작업을 하게 만드는 실질적인 알고리즘이 아니다. 없던 걸 처음 시도하여 만드는 게 아닌 이상, 베끼기만 하느라 시간 낭비할 필요는 없을 것이다.

4. 대등한 위상의 윈도우끼리 분할 관리를 해야 하는 경우

어휴, 글을 이렇게 길게 쓸 생각은 없었는데... 마지막 아이템도 언급을 안 할 수가 없구나. -_-;;
자, 지금까지 얘기한 것들을 정리하자면 1번은 그냥 한 윈도우 내부에서 그리기를 하는 것뿐이며, 2번과 3번은 부모 윈도우가 자식 윈도우들의 공간 관리를 하는 경우를 특별히 MFC의 document-view 아키텍처의 예를 들어 소개한 것이다.

그러나 실무에서는 그것만이 전부가 아니다. 대화상자처럼 document-view 아키텍처가 적용되지 않는 창에 대해서도 수평/수직 splitter 같은 물건을 만들어야 할 때가 있다.
예를 들어, <날개셋> 제어판 같은 경우, '분야'를 나타내는 왼쪽의 트리 컨트롤과, 오른쪽의 여타 컨트롤들 사이에 수직 splitter를 둬서, 트리 컨트롤의 폭을 좀 더 넓힌다거나 반대로 좁히는 경우를 생각할 수 있다.
이 경우 splitter는 여타 컨트롤들과 마찬가지로 대화상자 안에 존재하는 여러 자식 윈도우의 하나일 뿐이지 나머지 컨트롤들을 모두 통솔하는 부모 윈도우의 지위는 아니게 된다. Spy++로 들여다보면, 가로나 세로로 길쭉한 고유한 splitter 윈도우가 잡히는 걸 볼 수 있다.

이런 상황에 대해서는 MFC는 딱히 제공해 주는 있는 클래스가 없다. codeguru 같은 데서 splitter dialog 정도로 검색해 보면 예제 소스나 관련 튜토리얼들이 쭉 나온다. 예전에 아주 괜찮은 코드를 하나 구해서 유용하게 쓴 적이 있었는데 지금 다시 검색하려니까 못 찾겠다.

이런 일을 하는 범용적인 클래스를 만들 때 염두에 둬야 하는 사항으로는, 좌우나 상하든 보장해 줘야 하는 최소 크기를 인자로 받아야 할 것이고, 좌우 상하 중 한쪽에다 뒀으면 하는 윈도우를 배열 같은 자료구조로 관리해야 한다. 윈도우 핸들이 아닌 ID로 받으면.. ID로부터 실제 핸들값(HWND)을 얻어야 하는 번거로움이 있지만, 그 컨트롤이 중간에 재생성된다거나 해도 여전히 식별이 가능하기 때문에 범용성이 좀 더 향상된다.

다른 splitter조차 자기의 크기 조절에 영향을 받게 하고 WM_SIZE 메시지에 반응한다면, 아까 Dependency Walker 같은 복잡다단 splitter도 얼마든지 구현 가능하다.
splitter를 구현하려면 당장 크기를 조절하는 것 처리는 둘째치고라도, 창의 크기가 바뀌었을 때 각 분할 화면들의 공간 배분을 어떻게 할지 같은 것도 생각해야 하니 여러 모로 골치가 아픈 건 사실이다.

끝으로 언급하고 싶은 이슈가 있다. 1~4번들은 다 마우스가 클릭되었을 때 캡처를 잡고 마우스 움직임을 추적하다가 버튼이 떼졌을 때 마무리 처리를 한다는 공통점이 있다.
이 경우, WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP을 모두 메시지 맵에다 등록하고 각 상황별 코드를 메시지 핸들러 함수에다 제각기 따로 작성하는 방법을 생각할 수 있지만..

좀 더 능숙한 프로그래머라면, 그런 드래그 드롭 처리 정도면 WM_LBUTTONDOWN에다가 아예 별도의 message loop을 만들어서 거기에다 WM_MOUSEMOVE와 WM_LBUTTONUP에 해당하는 코드를 다 집어넣는 방법을 선택한다. 한 함수에다가 한 기능에 대한 처리를 몰아서 넣는 게 훨씬 더 깔끔하기 때문이다.

Posted by 사무엘

2014/07/02 08:32 2014/07/02 08:32
, ,
Response
No Trackback , 6 Comments
RSS :
http://moogi.new21.org/tc/rss/response/980

* 에구. 나 완전 고전 게임 덕후 인증이다. ㅎㅎ

1.
옛날의 액션/아케이드 게임 중에는 주인공을 죽게 하는 각종 트랩들이 적(몬스터)에게도 동일하게 적용되는 게 있고, 그렇지 않은 게 있다. 동일하게 적용되는 게 더 공정하고 현실 고증에 더 충실한 시스템이겠지만, 그 경우 적의 AI를 더 똑똑하게 만들어야 게임성이 성립할 테니 개발자에게는 더 큰 난관이 된다. 안 그러면 적을 정당하게 싸워서 죽이는 게 아니라 AI 헛점을 이용해서 트랩으로 죽이는 꼼수가 너무 횡행하여 게임 밸런스가 무너질 것이다.

이런 한계로 인해 id에서 개발한 둠, 퀘이크 등의 몬스터는 용암이나 화학 용액 같은 데에 들어가도 체력을 잃지 않으며 물에 들어가도 익사하지 않는다. 그 대신 잘 알다시피 자기끼리 팀킬을 벌이는 몬스터 내전(monster infighting)이 있을 뿐이다. 둠에서는 몬스터들이 정말 무식한지라 내전이 정말 잘 벌어졌지만, 퀘이크에서는 몬스터와 주인공 사이에 다른 몬스터가 일직선으로 있을 때는 공격을 안 하게 일말의 AI 개선이 이뤄졌다.

위험한 데이브에서는 몬스터는 트랩 같은 건 쌈싸먹으며 잘만 날아다니고..
과거의 툼 레이더 게임들은 주인공이 혼자 트랩 퍼즐을 풀어야 하는 구역과 몬스터와 싸우는 구역을 완전히 철저하게 분리하여 논란의 여지를 차단한 사례에 가깝다.
Rick Dangerous 2는 이 점에서 독특하다. 몬스터도 미사일, 전깃줄 같은 트랩에 걸리면 얄짤없이 죽을 뿐만 아니라, 이렇게 죽이는 게 전기총(50점), 폭약(100점) 같은 주인공의 일반적인 공격으로 죽이는 것보다 점수도 더 높게 준다(150점).

자, 서론이 너무 길어졌는데.. 페르시아의 왕자는 잘 알다시피 로토스코핑 기법으로 스프라이트를 만들었을 정도로 극도의 현실성을 추구한 게임이다. 악당도 딱히 초현실적인 괴물이 아니라 그저 검을 든 똑같은 인간이며, 이들을 상대로도 가시와 톱날 같은 트랩은 똑같이 동작한다. 악당 역시 트랩에 걸리면 피투성이가 되어 끔살당한다.

악당을 죽이는 방법으로는 (1) 칼 공격으로 HP를 다 깎아서 죽이는 가장 평범한 방법 외에도,
(2) 2층 이상의 높이에서 추락사시키기. 주인공은 이 경우 HP 하나만 잃지만, 악당은 의외로 즉사한다. 그 대신 악당은 1층 높이에서 떨어졌을 때 주인공처럼 무릎을 굽혀 엎드리지 않으며 바로 자세를 잡는다. 일당일단? 그리고 악당이 지금보다 아래 화면으로 떨어진 경우, 시체가 남지 않는다.
(3) 가시 꼬챙이가 있는 곳으로 떨어뜨리기. 한 층 높이에서만 떨어져도 주인공이든 악당이든 다 죽는다.
(4) 허리 자르는 쇠톱날.. 더 이상의 자세한 설명은 생략한다.

악당을 죽이고 나면 '빰 빠밤 빰' 하면서 승전 멜로디(?)가 흘러나온다. 칼로 찌르든, 떨어뜨리든 어떻게 죽이든지 말이다.
그런데... 페르시아의 왕자에서 악당을 (3)번, 즉 가시에다 밀어넣어 죽인 뒤에 승전 멜로디를 들은 적이 있으신 분 손..??
난 한 번도 없다. 20년 전부터 지금까지 이걸 대수롭지 않게 여기고 넘어가곤 했는데, 갑자기 문득 이상한 점이 느껴진다.

페르시아의 왕자 레벨에서 악당을 (3)번처럼 죽이는 건 레벨 2, 4, 8, 9(두 번), 11에서 총 6군데가 가능하다. 특히 마지막 레벨 11의 경우, 아예 왼쪽과 오른쪽에 모두 가시 트랩과 절벽이 존재하니 악당을 재미있게 요리하는 맛이 더욱 쏠쏠하다.

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

악당을 정상적인 (1)번 방식으로 죽였을 때는 승전 멜로디가 무조건 나온다. (2)나 (4) 같은 트랩으로 죽였을 때는 아주 가끔 음악이 안 나오기도 했던 것 같다.
그 반면 (3)은... 지금까지 한 번도 나온 적이 없었다.
어떤 방식으로 죽든 악당의 HP가 0이 되어 전투가 끝나고 주인공이 칼을 집어넣을 때 승전 멜로디를 연주하면 될 텐데.. 로직을 상황별로 다 따로 처리했나? 그리고 저기서만 if문이 하나 실수로 빠진 건지? 진실은 그 당시 코딩을 했던 프로그래머만이 알 수 있을 것으로 보인다.

2.
자, 이제부터는 가시 말고 톱날 얘기가 줄곧 나올 것이다.
페르시아의 왕자의 일부 던전에는.. 톱날을 통과해 들어간 뒤에 곧바로 칼을 뽑고 악당과 싸워야 하는 곳이 있다. 레벨 4에서 처음으로 등장한다.
그런데 적과 싸우기 시작했을 때는 게임 진행이 조금 느려지고, 심지어 톱날의 톱질 주기도 약간 길어지는 것 같다.

이것은 시스템 성능 같은 다른 외부적인 문제일 뿐, 게임 메카닉 자체가 느려지는 건 아니다.
페르시아의 왕자는 참 좋은 게, ESC를 눌러서 게임을 일시 정지시키고 나서 계속 ESC를 누르면 게임을 한 프레임 단위로 천천히 진행시킬 수가 있다.
이걸로 측정을 해 보면, 톱날의 톱질 주기는 언제나 15프레임으로 동일하다. 전투 중일 때든, 평시든 말이다.

그리고 심지어 톱날이 여러 개 연달아 동작할 때도 동일하다.
각각의 톱날이 한 번씩 연달아 톱질한 뒤에 첫 톱날의 다음 주기가 시작되기 때문에 주기가 좀 길 것처럼 느껴지기 쉬운데, 그렇지 않다.

원작 게임에서야 톱날이 한 층에 가장 많이 등장하는 게 레벨 3의 대형 물약이 있는 방이다. 3개짜리가 최다이다.
유튜브 같은 데서 톱날이 5개가 넘게 있는 이상한 방을 가 보면, 모든 톱날들이 15프레임 안에 한 번씩 순회를 하는 게 불가능하다. 이런 데서는 뒤쪽의 톱날이 미처 톱질을 하기 전에 15프레임을 다 채운 앞쪽 톱날이 또 톱질을 시작한다.
게임 메카닉이 이러하다는 걸 알 수 있다.

3.
또한, 톱날은 주인공이 톱날과 같은 층에 진입한 경우 곧장 동작한다.
그런데 주인공이 그 층에서 다른 이유로 인해 죽어 버리면, 톱날은 일반적인 경우 동작을 멈춘다. 가령, 그 층에 있는 악당과의 전투에서 져서 죽거나, 칼을 뽑지 않은 상태에서 악당의 칼빵을 맞아 죽은 경우 말이다. 톱날이 있는 층에 주인공이 추락사를 했다면 톱날은 역시 더 동작하지 않는다.

하지만 주인공이든 악당이든 누군가가 그 톱날 자체에 찍혀 죽었다면 톱날은 계속 동작한다. 다시 말해 누군가를 한번 죽인 적이 있는 톱날은 그 층에서 주인공이 추락사하든 악당의 칼에 맞아 죽든, 주인공이 그 층에 어떤 형태로든 있다면 계속 동작한다.

사용자 삽입 이미지
내가 치트까지 써 가면서 다양한 상황에서 테스트를 해 보니 대략 그러하다. 알고리즘 차원에서 이런 미묘한 차이도 존재한다는 게 흥미롭지 않은가?

4.
그리고.. 주인공이 톱날이 존재하는 층에 있긴 한데, 추락하느라 잠시 지나는 형태라면 톱날은 어떻게 동작할까?
톱날은 이때도 반응한다. 레벨 9에서는 심지어는 주인공이 아니라 악당이 떨어지는데도 톱날이 한번 동작하니, 참 놀랍지 않을 수 없다. 아래 그림을 보시라.

사용자 삽입 이미지

그 반면, 주인공이라 해도 이렇게 지나갈 때는 톱날이 동작하지 않는다. megahit 치트를 써서 천천히 떨어지게 하는 낙하산 효과를 줬는데도 톱날이 동작하지 않는다.

사용자 삽입 이미지사용자 삽입 이미지
이런 걸 보면, 톱날의 동작 여부에는 우리가 흔히 생각하는 것보다 더 복잡한 로직이 들어간 건지도 모른다. 심지어 던전의 지형별로 톱날의 동작 여부를 제어하는 메타데이터가 수작업으로 들어간 건 아닌지?

5.
아까도 잠깐 얘기했던 레벨 3으로 돌아간다. 여기는 알다시피 최대 HP를 늘려 주는 대형 물약이 있지만, 그 길목을 저렇게 톱날이 무려 3개가 가로막고 있다.

사용자 삽입 이미지

페르시아의 왕자에서 레벨 3은 중간 waypoint가 존재하는 유일한 레벨이기도 하다. 죽으면 얄짤없이 처음부터 시작하는 게 아니라, 타임어택 절벽 관문은 통과한 뒤의 시점부터 시작한다는 뜻. 페르시아의 왕자 2야 각 레벨이 워낙 방대해진 관계로 waypoint가 존재하는 레벨이 더 생긴 편이지만, 1에서는 레벨 3이 유일했다.

레벨 12에도 그림자 인간과 합체하고 나서 Jafar를 만나기 직전 위치에 일종의 waypoint가 있긴 하지만, 이건 내부적으로는 완전 별도인 레벨 13으로 간주된다. 그렇기 때문에 레벨 3의 waypoint와는 약간 성격이 다르다.
관문을 통과하기 전에 대형 물약을 먹었다면, 그렇게 해서 늘어난 HP도 다음에 waypoint에서 게임을 다시 시작할 때 반영된다.

그런데, 여기에 약간의 버그 내지 exploit가 있다.
waypoint에서 게임을 다시 시작하면, 레벨 3의 모든 요소들이 원래대로 되돌아간다. 딱 하나, 그 타임어택 절벽 관문의 한쪽에서 우리가 도움닫기를 위해 밟아 떨어뜨려 없애던 발판만 계속 없는 채로 남아 있다.

그것만 빼고 나머지는 전부 원상복귀. 심지어 타임어택 절벽을 넘기 전에 먹었던 대형 물약도 다시 생긴다.
그렇기 때문에 다음 레벨로 넘어가는 관문을 열고 해골도 처지한 뒤, 다시 그 방으로 돌아와서 대형 물약을 먹으면.. 이론적으로 레벨 3에서 대형 물약을 두 번 먹어서 HP를 2개나 확장하는 게 가능해진다.

레벨 3 시작 → 대형 물약 → 타임어택 관문 통과 → 그 뒤 Ctrl+A 눌러서 waypoint 지점부터 게임 재시작. 예전에 대형 물약 먹은 게 반영됨 → 되돌아와서 대형 물약 또 먹음 → 클리어

물론 실제로 이렇게 하는 건 시간이 너무 많이 걸리는 노가다이기 때문에 현실성은 별로 없지만..
이건 아마 조던 메크너(오리지널 게임 설계자, Apple II용 개발자)와 랜스 그루디(도스용 게임 포팅 개발자)조차 그 당시 예상을 못 했던 꼼수일 것이다.
차라리 waypoint 이후에 도달하는 위치에다 대형 물약을 놔 뒀다면 이런 exploit가 틈탈 여지가 없었을 텐데.

자, 이런 글을 보니 나도 도스박스 깔아서 페르시아의 왕자를 실행해 보고 싶은 생각이 드시지는 않는가?

Posted by 사무엘

2014/06/29 08:21 2014/06/29 08:21
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/979

« Previous : 1 : ... 125 : 126 : 127 : 128 : 129 : 130 : 131 : 132 : 133 : ... 215 : Next »

블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2024/05   »
      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:
2688095
Today:
1187
Yesterday:
1383