괴짜 프로그래머의 일상사~@@
프로그래밍, 컴퓨터, 그리고 일상에 관한 소소한 이야기들...
Blog | Guestbook
Keyword | Local | Tag
T 592 / Y 754 / Total 3471317
Catergories
Calendar
«   2007/08   »
      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  
Tag
홍진호, 취약점, 방문자, Sparse, 디바이스 드라이버, 살아 있는 것은 다 행복하라, 강컴, 백신, 찾기, 주식, 상대 평가, 수표, 윤년, 타입 시스템, 애쉬튼 커처, 예비군, 모임, 물놀이형 인간, 쓰레드, 원고,
Archive
Link
Search
  submit
Recent Articles
Recent Comments
Recent Trackbacks
해당되는 게시물 26건
2007/08/29 21:50
아인슈타인의 성공 방정식
[GGG]
오래전 메일함을 뒤적이다가 누군가한테 보낸 메일 중에 재미난 내용이 있어서 소개해 봅니다. 저도 다른 분께 메일로 받은 내용 중 일부를 발췌해서 보낸 건데, 제목은 제가 임의로 붙인 겁니다. 그때도 덧으로 많은 생각이 들게 하는 글이라고 적었는데 지금 봐도 많은 생각을 하게 하는 글이네요.
제자들이 아인슈타인에게 선생님은 어떻게 학문에 성공하셨는지 그 비결을 물었답니다.
그러자 아인슈타인 박사가 s=x+y+z 라고 쓰시더랍니다.

s는 물론 성공(success)의 머릿글자지요.

x는 말을 많이 하지 말 것,
y는 생활을 즐길 것,
z는 한가한 시간을 가지라는 뜻이라고

박사님은 설명을 해주시더랍니다.

성공의 비결,쉽지요.
말을 많이 하지 않고, 생활을 즐기고, 한가한 시간을 가질 것.

- 2000년 12월의 어느 메일 함에서
세 가지 항목을 따로 때놓고 보면 성공이랑 그닥 관련 없는 팩터들 같아 보이지 않습니까?
그런데 또 붙여놓고 생각해보면 성공의 요소인 것도 같아보이네요.
오묘합니다. ㅎㅎ

성공 방정식의 세 가지 항목 중에서 한 가지도 못지키는 요즘인 것 같습니다.
좀 여유를 가지도록 노력해야 겠습니다.

그나저나 메일함을 보니 참 유치한 메일들이 많네요. 철없던 시절 철없이 휘갈겨 쓴 말도 안되는 메일들. 보면서 웃기도 하고, 그때가 그립기도 하고 그렇네요. 언젠가 먼 훗날엔 지금 쓴 글들을 보면서 그런 생각을 하고 있겠죠? ㅋㅋ 그때의 순수를 잃은 것 같아 조금은 아쉽습니다. 나이가 든다는 건 점점 순수를 잃어가는거라고 말했던 어느 작가분이 생각납니다. 진짜 가을이 오려나???? ㅋㅋㅋ



2007/08/29 18:46
운영체제 버전 구하기
[GGG]
정확한 OS 종류를 판단하는 것은 무척 중요한 일 입니다. 이 문서에 포함된 함수들은 VerifyVersion과 GetVersionEx함수를 사용하여 정확한 OS 종류를 판단합니다. 제공되는 함수는 아래와 같습니다. 각각의 함수가 판별하는 OS는 주석을 참고 하십시요.
[CPP]BOOL WINAPI IsWin95()          // 윈도우 95
BOOL WINAPI IsWin95OSR2() // 윈도우 95 OSR2
BOOL WINAPI IsWin98() // 윈도우 98
BOOL WINAPI IsWin98SE() // 윈도우 98 SE
BOOL WINAPI IsWinME() // 윈도우 ME
BOOL WINAPI IsWin9X() // 모든 윈도우 9x 시스템
BOOL WINAPI IsWinNT4() // 윈도우 NT4
BOOL WINAPI IsWin2K() // 윈도우 2000
BOOL WINAPI IsWinXP() // 윈도우 XP
BOOL WINAPI IsWinNET() // 윈도우 .Net
BOOL WINAPI IsWinNT() // 모든 윈도우 NT계열 시스템[/CPP]
[CPP]#ifndef GETPROC
#define GETPROC(dll, type, name) (type)GetProcAddress(dll, name)
#endif

typedef BOOL
(WINAPI *fnVerifyVersionInfo)(LPOSVERSIONINFOEX, DWORD, DWORDLONG);

typedef ULONGLONG
(WINAPI *fnVerSetConditionMask)(ULONGLONG, DWORD, BYTE);

BOOL _VerifyVersionInfo(LPOSVERSIONINFOEX lpVersionInfo
                        , DWORD dwTypeMask
                        , DWORDLONG dwConditionMask
                        , BOOL *bVerified)
{
    BOOL bRet = FALSE;
    HINSTANCE hDll = NULL;
    fnVerifyVersionInfo FVerifyVersionInfo;
    fnVerSetConditionMask FVerSetConditionMask;

    hDll = LoadLibrary("kernel32.dll");
    if(hDll != NULL)
    {
        FVerifyVersionInfo = GETPROC(hDll
                                    , fnVerifyVersionInfo
                                    , "VerifyVersionInfoA");

        FVerSetConditionMask = GETPROC(hDll
                                    , fnVerSetConditionMask
                                    , "VerSetConditionMask");

        if(FVerifyVersionInfo != NULL && FVerSetConditionMask != NULL)
        {
            dwConditionMask = 0;
            if(dwTypeMask & VER_MAJORVERSION)
                dwConditionMask = FVerSetConditionMask(dwConditionMask
                                                    , VER_MAJORVERSION
                                                    , VER_EQUAL);

            if(dwTypeMask & VER_MINORVERSION)
                dwConditionMask = FVerSetConditionMask(dwConditionMask
                                                    , VER_MINORVERSION
                                                    , VER_EQUAL);

            if(dwTypeMask & VER_PLATFORMID)
                dwConditionMask = FVerSetConditionMask(dwConditionMask
                                                    , VER_PLATFORMID
                                                    , VER_EQUAL);

            if(dwTypeMask & VER_PRODUCT_TYPE)
                dwConditionMask = FVerSetConditionMask(dwConditionMask
                                                        , VER_PRODUCT_TYPE
                                                        , VER_EQUAL);


            *bVerified = FVerifyVersionInfo(lpVersionInfo
                                            , dwTypeMask
                                            , dwConditionMask);
            bRet = TRUE;
        }
        FreeLibrary(hDll);
    }

    return bRet;
}

BOOL WINAPI IsWin95()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwMajorVersion = 4;
    osi.dwMinorVersion = 0;
    osi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;

    bRet = _VerifyVersionInfo(&osi
                        , VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID
                        , dwlConditionMask
                        , &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }

    return (osi.dwMajorVersion == 4)
            && (osi.dwMinorVersion == 0)
            && (osi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
            && (osi.szCSDVersion[1] != 'C');
}

BOOL WINAPI IsWin95OSR2()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwMajorVersion = 4;
    osi.dwMinorVersion = 0;
    osi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;

    bRet = _VerifyVersionInfo(&osi
                                , VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID
                                , dwlConditionMask
                                , &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       
    
    return (osi.dwMajorVersion == 4)
            && (osi.dwMinorVersion == 0)
            && (osi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
            && (osi.szCSDVersion[1] == 'C');
}

BOOL WINAPI IsWin98()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwMajorVersion = 4;
    osi.dwMinorVersion = 10;
    osi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;

    bRet = _VerifyVersionInfo(&osi
                            , VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID
                            , dwlConditionMask
                            , &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       
    
    return (osi.dwMajorVersion == 4)
            && (osi.dwMinorVersion == 10)
            && (osi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
            && (osi.szCSDVersion[1] != 'A');
}

BOOL WINAPI IsWin98SE()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwMajorVersion = 4;
    osi.dwMinorVersion = 10;
    osi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;

    bRet = _VerifyVersionInfo(&osi
                            , VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID
                            , dwlConditionMask
                            , &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       
    
    return (osi.dwMajorVersion == 4)
            && (osi.dwMinorVersion == 10)
            && (osi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
            && (osi.szCSDVersion[1] == 'A');
}

BOOL WINAPI IsWinME()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwMajorVersion = 4;
    osi.dwMinorVersion = 90;
    osi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;

    bRet = _VerifyVersionInfo(&osi
                            , VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID
                            , dwlConditionMask
                            , &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       

    return (osi.dwMajorVersion == 4)
            && (osi.dwMinorVersion == 90)
            && (osi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
}

BOOL WINAPI IsWin9X()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;

    bRet = _VerifyVersionInfo(&osi, VER_PLATFORMID, dwlConditionMask, &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       

    return osi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS;
}

BOOL WINAPI IsWinNT4()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwMajorVersion = 4;
    osi.dwMinorVersion = 0;
    osi.dwPlatformId = VER_PLATFORM_WIN32_NT;

    bRet = _VerifyVersionInfo(&osi
                        , VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID
                        , dwlConditionMask
                        , &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       
    
    return (osi.dwMajorVersion == 4)
            && (osi.dwMinorVersion == 0)
            && (osi.dwPlatformId == VER_PLATFORM_WIN32_NT);
}

BOOL WINAPI IsWin2K()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwMajorVersion = 5;
    osi.dwMinorVersion = 0;
    osi.dwPlatformId = VER_PLATFORM_WIN32_NT;

    bRet = _VerifyVersionInfo(&osi
                        , VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID
                        , dwlConditionMask
                        , &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       
    
    return (osi.dwMajorVersion == 5)
            && (osi.dwMinorVersion == 0)
            && (osi.dwPlatformId == VER_PLATFORM_WIN32_NT);
}

BOOL WINAPI IsWinXP()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwMajorVersion = 5;
    osi.dwMinorVersion = 1;
    osi.dwPlatformId = VER_PLATFORM_WIN32_NT;

    bRet = _VerifyVersionInfo(&osi
                        , VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID
                        , dwlConditionMask
                        , &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       
    
    return  (osi.dwMajorVersion == 5)
            && (osi.dwMinorVersion == 1)
            && (osi.dwPlatformId == VER_PLATFORM_WIN32_NT)
            && (osi.wProductType == VER_NT_WORKSTATION);
}

BOOL WINAPI IsWinNET()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwMajorVersion = 5;
    osi.dwMinorVersion = 1;
    osi.wProductType = VER_NT_WORKSTATION;
    osi.dwPlatformId = VER_PLATFORM_WIN32_NT;

    DWORD type = VER_MAJORVERSION
                | VER_MINORVERSION
                | VER_PRODUCT_TYPE
                | VER_PLATFORMID;

    bRet = _VerifyVersionInfo(&osi, type, dwlConditionMask, &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       
    
    return (osi.dwMajorVersion == 5)
            && (osi.dwMinorVersion == 1)
            && (osi.dwPlatformId == VER_PLATFORM_WIN32_NT)
            && (osi.wProductType != VER_NT_WORKSTATION);
}

BOOL WINAPI IsWinNT()
{
    OSVERSIONINFOEX osi;
    DWORDLONG dwlConditionMask = 0;
    BOOL bRet = FALSE;
    BOOL bVerified = FALSE;

    ZeroMemory(&osi, sizeof(OSVERSIONINFOEX));
    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osi.dwPlatformId = VER_PLATFORM_WIN32_NT;

    bRet = _VerifyVersionInfo(&osi
                            , VER_PLATFORMID
                            , dwlConditionMask
                            , &bVerified);
    if(bRet == TRUE)
        return bVerified;

    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
    {
        osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if(GetVersionEx((OSVERSIONINFO *) &osi) == 0)
            return 0;
    }       

    return osi.dwPlatformId == VER_PLATFORM_WIN32_NT;
}[/CPP]
2007/08/28 12:08
이슈 되겠어? 적용 되겠어? : 메모리 해킹
[GGG]
어젠가 풀리비님의 글을 보다가 관련 내용을 보게 되었다. 그 내용을 처음 보았을 때만 해도 "그냥 뉴스에 나왔나 보다"라고 생각을 했다. 왜냐면 그만큼 해묵은 방법이었기 때문이다. 그런데 예상외로 여기저기서 이슈가 되고 있는 모습이 눈에 띈다. "그 방법이 통하기 때문에 그런게 아닐까?"란 생각을 조심스럽게 해본다.

메모리 해킹이란 방법은 십 수년 아니 컴퓨터가 생기던 시절부터 있었던 메카니즘이다. 고전적인 수법인만큼 그 방법도 여러 수천 가지에 이른다(실제적으로 여러 수천 가지는 아니다). 따라서 기존에 나와있는 방법에 대해서 대응력을 가지는 것은 가능하겠지만 전혀 새로운 형태로 다른 프로세스의 메모리에 접근하는 것을 원칙적으로 차단하는 것은 매우 힘든 일이다.

그렇다면 메모리 해킹을 막을 방법은 없을까? 물론 방법이 없는 것은 아니다. 과거 컴퓨터의 역사와 같이한 방법인 만큼 그것을 막는 방법도 다양하게 연구되었다. 그 중 최종 결론은 데이터가 메모리에 존재하는 구간을 최소화 시켜야 한다는 것이었다. 궁극적으로 데이터가 메모리에 존재하는 시간이 0이 되면 메모리 해킹은 무용지물이 된다. 메모리에 데이터가 없다면 조작할 것이 없는 것이고, 결국은 메모리 해킹은 무위로 돌아갈 수 밖에 없기 때문이다. 이를 인터넷 뱅킹에 적용해서 생각해 보면 다음과 같이 된다.

인터넷 뱅킹을 하기 위해서 사용자가 입력해야 하는 아주 중요한 자료는 두 가지다. 이체 대상 계좌번호와 이체 금액이다. 이 둘은 사용자가 직접 입력하고 화면에 표시된다. 화면에 표시된다는 말은 이미 메모리 상에 존재한다는 말이다. 그래서 지금 메모리 해킹이 문제가 되고 있다. 이 두 데이터가 메모리 상에 존재하지 않는다면 안전하다는 것은 앞서 설명했다. 이 둘을 표시하지 않고 인터넷 뱅킹을 할 수 있을까? 물론 없다. 사용자가 실수로 잘못된 정보를 입력할 수 있기 때문에 보여주는 과정은 절대적으로 필요하다. 그렇다면 인터넷 뱅킹의 메모리 해킹을 막는것은 불가능 한가? 그것도 아니다.

과거 MMORPG 게임에 처음 메모리 해킹이 적용됐을때 그 파급효과는 정말 컸다. 거의 모든 데이터가 클라이언트에 저장되었기 때문에 고치기만 하면 적용되는 그런 세상이었다. 하지만 지금은 많이 달라졌다. 메모리 해킹 툴을 사용해서 클라이언트를 조작해도 데이터가 변경되진 않는다. 어떻게 그렇게 됐을까? 원리는 간단하다. 클라이언트에서 표시되는 데이터는 단지 보여지는 역할만 하기 때문이다. 모든 데이터 생성은 서버에서 이루어지는 것이다. 주사위를 굴린 값도 서버에서 생성되어 클라이언트로 전송된다. 그래서 요즘의 MMORPG 게임은 메모리 해킹 보다는 봇을 막는것이 좀 더 중심이 되고 있다.

다시 인터넷 뱅킹으로 돌아가보자. 우리는 클라이언트에 데이터가 저장될 수 밖에 없다면 단지 보여주는 기능만을 하도록 그것을 제한하면 메모리 해킹에 효율적으로 대응할 수 있다는 것을 게임에서 배웠다. 어떠한 방법을 통해서 이것을 인터넷 뱅킹에 적용할 수 있을까? 가장 기본적인 원리는 모든 것을 서버가 결정한다는데 있다. 다음과 같은 예를 한번 생각해보자.

사용자가 로그인을 한다. 이제 계좌번호를 입력할 순간이 되면 서버에서 그림을 열 조각 보내 준다. 이 그림은 0부터 9까지에 대응하는 그림이다. 하지만 각 그림에는 고유 키번호가 맵핑되어 있다. 즉 소프트웨어적으로 0번 그림이 0이라는 것을 알아낼 방법은 없다. 사람이 0을보고 누르면 그에 대응하는 키 값이 들어가는 것이다. 사용자가 1,2,3,4를 눌렀고 서버로 c,d,e,x가 전송되었다고 해보자. 서버는 받은 데이터를 자신이 보내준 그림 데이터에 근거해서 원복한다. 그럼 1,2,3,4가 나온다. 그럼 서버에서 그걸 다시 클라이언트로 전송한다. 화면에는 사용자가 입력한 계좌 번호가 나올 것이다. 메모리 해킹을 하는 프로그램이 그것을 조작해본들 그건 단지 뷰일 뿐이다. 서버로 전송되는 값이 아니란 의미다.

물론 이 경우에도 서버에서 발생시킨 그림 조각을 해킹 프로그램이 알아낸다면 해킹을 할 수가 있다. 하지만 이는 자동화하기가 매우 난해하다. 해커라면 리버싱을 통해서 그림과 의미를 매칭시킬 수 있지만 프로그램은 그것을 하기가 무척 어렵다. 결론적으로 0번 그림을 보고 그것이 0이라는 것을 인식하는 문제로 귀결되는데 이는 아직까지는 불가능하기 때문이다. 또한 그 이미지 자체도 동적으로 바뀐다면 더욱 프로그램으로 인식하기 어려워진다.

이때까지 말한 이 솔루션은 사실 새로운 방식도 아니고 몇 해 전에나 제안되던 구식 방법이다. 이 방식이 적용되지 않았던 이유는 단 한가지였다. 사용성이 떨어진다는 점이었다. 유저 입장에서도 골치아프고, 적용하는 업체 입장에서도 고칠 곳이 많아진다. 결과론적으로 비용이 올라간다는 것이었다. 맞는 말이긴 하다. ㅎㅎ

바이러스가 나오면 백신 업체가 돈을 벌고, 해킹 소식이 나오면 보안 업체가 돈을 버는 것은 초등학생도 알 만한 일이다. 이미 솔루션을 구축하고 이 뉴스가 일파만파 퍼지기를 기다리고 있는 보안업체가 있을지도 모른다. 물론 그들이 잘못한건 없다. 이슈가 되지 않으면 적용하지 않는 세태가 더 잘못된게 아닐까?

기사 말미에 나오는 휴대폰 인증도 좋은 방법이다. 그런데 휴대폰 방법을 적용하려면 이런 말을 하는 사람들이 꼭 있다. "그럼 휴대폰없는 사람은 인터넷 뱅킹도 하지 말라는 말이냐?" 그래서 집전화까지 지원하게 된다. ㅋㅋ


2007/08/25 12:13
윈도우 타입 시스템

[GGG]
처음으로 C언어를 익히고 Windows 프로그래밍을 하는 사람들이 겪는 가장 큰 어려움은 복잡한 타입 시스템이다. 대부분의 사람들이 PVOID, LPCTSTR, TCHAR, LPTSTR등으로 전혀 듣지도 못했던 타입 명들 앞에서 당황한다.

하지만 이렇게 괴상하게 보이는 것도 다 몇 가지 기본적인 규칙에 의거해서 만들어져 있기 때문에 그러한 규칙들을 알게 된다면 누구나 쉽게 이해할 수 있고 편하게 사용할 수 있다. 그 규칙이란 것도 특별한 것이 아닌 글자를 통한 축약 형태이다. 여기서는 자주 등장하는 글자들이 의미하는 바를 살펴보고 그것들이 실제로 자주 사용되는 타입 명에서 어떻게 적용되는지 알아보도록 하자. 

글자

의미

T

문자를 나타내는 타입에 주로 등장하는 글자다. T가 의미하는 바는 유니코드와 ANSI의 컴파일 환경에 따라 적절히 변경된다는 것을 나타낸다. 유니코드 빌드라면 wchar_t로 변환되고, ANSI 빌드라면 char로 변환됨을 나타낸다.

L

long을 나타낸다. 주로 포인터 타입에 많이 등장한다. 16비트 시절은 포인터가 near, far등으로 구분해서 사용했다. 그 시절 관습에 따라 붙여진 것이다. 32비트 환경에서는 그러한 포인터 사이에 구분이 없기 때문이 L이 붙은 것과 붙지 않은 것 모두 같은 의미를 가진다.

C

const를 나타낸다. C가 들어간 자료형은 값을 수정할 수 없는 const로 선언된 것이라 이해하면 된다.

D

double을 나타낸다. 크기가 두 배라는 의미다.

U

unsigned를 나타낸다.

H

핸들을 나타낸다. 대부분 이 타입은 void *로 변환된다.

그럼 이제 자주 사용되는 타입을 살펴보자. 

타입 명

설명

TCHAR

char형을 표현하는 타입이다. T가 붙었기 때문에 유니코드에선 wchar_t, ANSI에서는 char로 변환된다.

PTSTR,

LPSTR

문자열에 대한 포인터를 나타낸다. 유니코드에선 wchar_t *, ANSI에서는 char *로 변환된다.

PCTSTR,

LPCTSTR

수정이 불가능한 문자열 포인터를 나타낸다. 유니코드에선 const wchar_t *, ANSI에서는 const char *로 변환된다.

PVOID

void 포인터를 나타낸다. void *로 변환된다.

PCVOID

상수 void 포인터를 나타낸다. const void *로 변환된다.

BOOL

TRUE/FALSE를 나타내는데 사용됨을 의미한다. 실제로는 int로 변환된다.

BYTE

8비트 무부호 정수를 나타낸다. unsigned char로 변환된다.

WORD

16비트 무부호 정수를 나타낸다. unsigned short로 변환된다.

DWORD

32비트 무부호 정수를 나타낸다. unsigned long으로 변환된다.

UINT

unsigned int로 변환된다.

USHORT

unsigned short로 변환된다.

ULONG

unsigned long으로 변환된다.

PWORD,

LPWORD,

PUSHORT,

LPUSHORT

unsigned short * 변환된다.

HANDLE

파일, 뮤텍스, 세마포어, 이벤트등의 핸들을 나타낸다. void *로 변환된다.

HPEN

GDI PEN 핸들이다. void *로 변환된다.

HBITMAP

GDI BITMAP 핸들이다. void *로 변환된다.

보다 많은 타입에 대한 더욱 상세한 설명은 아래 페이지에 나와있다.
http://msdn2.microsoft.com/en-us/library/aa505945.aspx

[GGG]
얼마전 소리통을 만들고 거기에 음악방송 기능을 추가하면 좋겠다는 생각이 들었습니다. 그래서 ShoutCAST 소스 필터를 찾아보았는데 찾기가 쉽지 않더군요. 물론 코덱팩에 들어있는 필터는 많은데 저는 별도의 코덱을 설치하지 않아도 들을 수 있게 하고 싶었습니다. 그냥 exe에 집어넣을 수 있게 소스가 있으면 좋겠다 싶었죠.

네트를 방황하던 끝에 ShoutCAST 소스 필터를 찾을 수 있었습니다. MPEG-1 Stream Splitter 필터에 연결해서 사용할 수 있는 소스 필터였습니다. 필터 소스를 분리해서 연결하니 음악 방송이 솔솔 흘러오더군요. 음. 다했네 라고 잠시 생각했습니다. 그런데 헉.. 아주 사소한 문제가 있었습니다. 바로 탐색이죠. 로컬 파일 같은 경우는 앞뒤로 이동이 되는데 음악 방송같은 경우는 원칙적으로 탐색이 불가능합니다. 그런데 이 놈은 MPEG-1 Stream Splitter에 연결해서 쓰는 놈이라 그런지 탐색이 되는 것이었죠. 탐색이 되는건 아니고 단지 되는 척만 하는 것이었죠. 물론 UI에서 체크해서 비활성화 시키면 되지만 비겁한 방법이란 생각이 들더군요.

그래서 MP3를 분석해서 직접 ShoutCAST 푸시 소스 필터를 만들어야 겠다고 생각을 했습니다. MP3 프레임 헤더는 정말 간단한 4바이트 구조로 이루어져 있습니다. 프레임 헤더 구조가 궁금하신 분은 다음 페이지를 참고하세요. http://www.dv.co.yu/mpgscript/mpeghdr.htm

4바이트 심플한데 비트가 홀수로 쪼개져서 조금 귀찮아 보이죠. 전 저걸 보고 겁없이 비트필드를 만들었답니다. 보는 순간 이건 나에게 비트필드를 만드라고 지시하는 것 같았죠. 그 날 전 하루 죈장 삽질했습니다. 제가 삽질했던 이유는 비트필드의 메모리 레이아웃을 제대로 몰랐고, 엔디안을 정확하게 판단하지 않았던 것 입니다. 파일에 저장된 4바이트는 빅엔디안 순서로 저장되어 있고, VC++의 비트필드는 리틀 엔디안과 같이 뒤에서 부터 비트를 채우는 형태더군요(http://msdn2.microsoft.com/en-us/library/ms858673.aspx 참고). 결국 작업을 하긴 했지만 굉장히 힘든 과정이었습니다. 단지 4바이트 분석하는것이. ㅠㅠ

아래는 비트필드에 관한 자료를 찾던 중 발견한 재미난 내용입니다.

첫 번째 링크는 뉴스그룹에서 비트필드에 관한 논쟁입니다.
베커 아저씨와 mlimber 아저씨의 논쟁인데 둘의 입장이 첨예하게 대립됩니다.
mlimber 아저씨는 비트필드 따위를 버려라.
임플멘테이션 디펜던트하기 때문에 플랫폼이 바뀌면 쪼다된다.
비트스트림을 추상화한 클래스를 사용해라. 라는게 주된 주장입니다.
베커 아저씨는 좀 더 현실적인데요.
비트필드가 어울리는데는 그걸 사용하는게 옳다.
사실 템플릿메타 프로그래밍으로 비트 스트림 클래스를 만드는 것보다,
다른 플랫폼에서는 거기 알맞은 비트필드로 고쳐주는 것이 옳다. 라는게 주장입니다.
두 번째 링크는 mlimber 아저씨가 참고하라고 올려준 비트 스트림 클래스입니다.
자세히 읽지는 않았지만 재미난 내용인 것 같아요.

뉴스 그룹 스레드
비트필드를 예술로 승화시킨 사나이

재미난 논쟁인데 여러분은 어떤 입장인가요? 이번달 마소 플러스에도 이와 관련된 내용을 하나 연재했는데, 저는 베커 아저씨한테 손을 들어주고 싶습니다. 왜냐하면 일반적으로 회사에서 개발하는 제품은 특정 플랫폼에서 동작하는 경우가 대부분이기 때문이죠. 멀티플랫폼을 생각하더라도 베커 아저씨 말처럼 비트필드를 스왑하는 작업은 그다지 큰 힘도 어려운 일도 아닙니다. 과거 수많은 소프트웨어들이 그런 식으로 포팅되어 왔구요. 물론 잘 만들어진 비트스트림 클래스가 있다면 사용하는 것이 바람직하겠지만 없는걸 굳이 만들어가면서 까지 시간을 소비할 필요는 없을것 같다는게 제 생각입니다. 또한 템플릿 메타 프로그래밍을 해본 사람이라면 알겠지만 쉬이 접근할 곳이 아니죠.

---
한참 전에 썼던 글인데 아직도 ShoutCAST 푸시 소스 필터는 만들지 못했습니다. 요즘은 회사일이 조금 바빠서 그 코드 보는 일만으로도 힘이 드는군요. 저런건 또 재미 붙었을때 만들어야 하는데 타이밍 놓치면 다시 만들라면 좀 그렇더라고요. 하지만 언젠가는 ShoutCAST 기능을 붙일 겁니다. *^^* 언젠가는 다시 재밌어 지겠죠. ~~

2007/08/24 18:03
마이크로소프트웨어
[GGG]
저는 매달 마이크로소프트웨어를 읽습니다. 예전엔 정기 구독을했고, 지금은 필자라서 한 권씩 보내주는 걸 받아서 보고 있죠.

특히 서광렬님, 최재훈님, 안윤호 아저씨의 칼럼을 좋아합니다. 세 분의 칼럼을 좋아하는 이유는 사뭇 다릅니다. 서광렬님의 칼럼은 늘 제가 궁금했던 부분이나 신기한 것에 관한 것을 담고 있어서 좋아합니다. 최재훈님의 칼럼은 재미있고 유익하기 때문에 좋아하죠. 아마 제 생각에 마소를 보는 독자중에 가장 많은 사람들이 보는 내용이 최재훈님의 칼럼이 아닐까 싶습니다. 그만큼 재밌고 유익합니다. 안윤호 아저씨의 칼럼은 그 분의 박식함에 매료되는 경우가 많습니다. 그 분의 글에서 뿜어져 나오는 포스는 정말 감당이 완되죠. 글을 읽을 때마다 전 늘 이렇게 생각합니다.

"어떻게 이렇게 많은 것을 알 수 있을까? 사람이 아닐꺼야."

이번 달에 연재된 안윤호 아저씨의 칼럼 중에서 공감가는 내용이 있어서 소개해 봅니다.
사람들은 자기가 파악할 수 있을 정도의 복잡성을 좋아하고 그 이상은 사실 효율적이기 보다는 머리를 아프게 할 때가 많다. 하지만 아무리 복잡한 일도 자기가 관리할 수 있는 복잡성 이상으로 운영할 수는 없다.
토할만큼 공감가는 말이죠.
돌려보면 아는 만큼 보인다라는 말과도 유사합니다.

이번 달 안윤호 아저씨의 칼럼이 저에겐 저 말의 증명이었습니다.
그간에 연재된 칼럼의 경우는 너무 어려워서 이해하기 힘든 경우가 많았죠.
자연스럽게 제가 처리할 수 있는 복잡도를 넘어가니 흥미가 없었습니다.
그런데 조금 쉬운 내용을 담고 있는 이번 달 칼럼은 무척 재밌었습니다.

앞으로는 디지털 토이 제작과 관련된 내용을 쓰신다고 합니다.
한 때 관심 분야 였기에 더욱 기대가 됩니다.
재미난 토이들이 많이 나왔으면 좋겠습니다.
인두기랑 납을 준비해야 되는 건 아닌지 모르겠습니다.
인두기랑 납하니까 옛날 생각이 나는군요.

CodeWiz가 임베디드를 떠난 까닭은?
저의 첫 병역특례 회사는 임베디드 장비를 만드는 회사였습니다. 그 때 제가 처음 맡았던 프로젝트는 IP540이라는 장비를 운용하는 프로그램을 제작하는 것이었죠. IP540이란 기계는 196kc 마이크로컨트롤러가 있었고, lcd, 랜카드, 몇 개의 버튼, 바코드 리더기 정도가 달려있는 생산 자동화 장비입니다. 그 때 전 윈도우 프로그래밍이란 일에 한참 염증을 느꼈던 때라 임베디드 프로그래밍이 굉장히 신선했습니다. 회로도 보고, 레지스터를 조작하고, 모든 것을 직접 만들기 때문이었죠. 코머 아저씨의 책을 벗삼아 열심히 프로토콜도 공부하고 했습니다. 그렇게 저의 첫 작품은 모 업체에 납품되어서 성공적으로 사용되었습니다. 랜카드 칩셋과 씨름한 무수한 날들이 눈녹듯 사라지는 그런 날이었죠.



제가 다음으로 맡게된 프로젝트는 굉장히 위험한 것이었습니다. 전력선 통신 모듈을 개발하는 것이었죠. 이게 왜 위험한고 하니 전력선 통신이라는 것이 220볼트를 그대로 통신 선로로 사용하는 것 입니다. 따라서 테스트 장비또한 220볼트가 그대로 들어오죠. 회로에 결함이 있다면? 내지는 프로그램의 결함이 있다면 골로 가는 겁니다. 5볼트로 장난치는 디지털 세상과는 조금 다르죠. 이 때 사용한 칩은 PIC이었습니다. 8비트 칩이었죠. 단지 전등과 스위치를 연동시켜서 통신으로 켜고 끄는 간단한 일이었습니다. 이 간단한 일이 얼마나 힘든지를 뼈져리게 느꼈죠. 그 때 당시 결국 스위치를 연동해서 사용하는 것 까지 구현은 했으나 실용성은 제로 였습니다. 왜냐하면 노이즈가 조금만타도 전혀 동작되지 않았기 때문이었죠. 패리티나 CRC 같은 것들은 완전 쓸모없는 환경이었습니다. 하여튼 그 때 저는 실험 도중 그놈의 스위치가 폭발하는 것을 두번이나 보았고, 그 다음 부터는 사실 칩을 새로 구워서 테스트 하는 것도 두려웠습니다. 그렇게 저의 두번째 프로젝트는 대실패로 돌아갔고, 저는 결국 임베디드를 떠나 다시 윈도우 프로그래밍으로 돌아왔습니다.

그 때 저에게 김실장님이 하신 말이 아직도 생각나는 군요.
김: "왼손 잽이는 이쪽에서 일하면 안돼."
신: "왜 그런가요?·
김: "왼손있는데 바로 심장이 있잖아. 까딱하면 골로 가는거야...ㅠㅠ"

[GGG]
어제 아는 형이 이런걸 물어보더군요.
"lua 스크립트를 쓰는데 그걸 VC++ 6.0에서 C++로 인식시키고 싶어. 방법이 없을까?·
그 형이 요구한건 lua 스크립트를 컴파일은 하지 않고, 단지 신택스 하이라이팅만 하고 싶다는 것이었습니다.

제가 알려준 페이지는 아래 페이지입니다.
http://support.microsoft.com/kb/181506

테스트를 해보지 않아서 정확하진 않지만 아마도 형이 원한 답이 아니었을 것 같습니다.
C++로 인식 시키면 컴파일 해버리겠죠. ㅠㅠ

혹시나 그런 경우가 아니라 커스텀 확장자를 사용하고 싶은 분들에게는 도움이 될 것 같습니다.
cpp가 꼭 C++이어야 할 필요는 없자나요. c++을 확장자로 쓰고 싶은 사람들도 있을 테니까요.

그런데 과거부터 지금까지 쭉 느낀거지만 VC++의 신택스 하이라이팅 기능은 발전할 필요가 있습니다.
새로운 문법에 대해서도 그렇고, 구분 단위에 대해서도 그렇고 말이죠.



[GGG]


얼마전에 소개했었던 신영복님의 "강의: 나의 동양 고전 독법"이라는 책을 요즘 틈틈이 읽고 있습니다. 동양 고전 종류별로 챕터가 있습니다. 저는 개인적으로 노자를 좋아하는 편이라 그 쪽을 먼저 읽었습니다. 노자편의 예시 문안중 하나가 아래 내용이었습니다.
가장 완전한 것은 마치 이지러진 것 같다. 그래서 사용하더라도 해지지 않는다.
가득 찬 것은 마치 비어 있는 듯하다. 그래서 퍼내더라도 다함이 없다.
가장 곧은 것은 마치 굽은 듯하고,
가장 뛰어난 기교는 마치 서툰 듯 하며,
가장 잘하는 말은 마치 더듬는 듯하다.
고요함은 조급함을 이기고, 추위는 더위를 이기는 법이다. 맑고 고요함이 천하의 올바름이다.
감동적이지 않나요. 정말 동양 철학을 잘 보여주는 글귀인 것도 같습니다.
가장 곧은 것은 마치 굽은 듯하고, 가장 뛰어난 기교는 마치 서툰 듯 하며,...
언뜻 보면 미친 말 같지만, 곰곰 생각해보면 심오한 생각이 그 바탕에 깔려있죠...

개발자들도 흔히 하는 말이 있자나요~
가장 완벽한 설계는 가장 단순한 것과 같다.
그래서 누가 보더라도 쉽게 이해하고 사용할 수 있다. ㅋㅋ

책 읽고 있으면 시종일관 이런 생각이 듭니다.
한문을 잘했으면 좀 더 책을 음미하면서 읽을 수 있었을텐데... ...
하지만 한문 공부가 쉽진 않죠. ㅎㅎ


[GGG]
2006년도였던가 부터 전 구글 통계 서비스를 사용했습니다. 사실 말이 사용이지 자주 들어가보진 않습니다. 머 통계가 저에게 큰 의미가 없기 때문이죠. 단지 심심할 때 한번씩 들어가 보지도 않죠. 하여튼 거의 사용을 하지 않았던것 같습니다. 그러다 몇 일 전에 애드센스 홈페이지를 들렀다가 통계 서비스를 간만에 아주 간만에 들어가게되었죠. 예전에도 구글 통계는 꽤나 괜찮은 서비스였습니다. 왠만한 유료 통계 서비스보다 더욱 미려한 화면과 상세한 자료를 제공해 주었죠. 그런데 그놈이 개편된 것이었습니다. 언제 개편됐는지는 모르겠으나 감동이었습니다. 후후~

아래는 방문자의 기록 개요를 보여주는 화면입니다. 크게 표시된 그래프는 플래시입니다. 마우스를 움직이면 포인트가 트래킹 되어서 그 날 얼마나 많은 방문자가 다녀갔는지를 표시해주고 있습니다. 이 UI를 보면서 제가 느낀 첫 감정은 "이거 웹이야?"라는 것이었습니다. 미려하고 잘 움직입니다. 2007년 중에 가장 많은 사람이 방문한 날은 5월 16일이군요. 713명이나 방문했습니다. 와우~ ㅋ. 찾아보니 뱅킹 ActiveX 글을 올려서 아주 잠깐 올블에 올랐던 날이었군요. 평균적으로는 대체적으로 200명 내외에 해당하는 방문자가 다녀갑니다.

사실 저건 시작에 불과합니다. 그 밑으로 더 재미난 정보들이 나열되어 있습니다. 올해 총 방문 횟수는 52786회 이고, 그 중 실제 방문자는 38709명 이네요. 이 데이터를 보면 이 사이트의 방문자 성향을 알 수 있습니다. 거의 한번 왔다 가는 사람이 대부분이라는 거죠. ㅠㅠ 새로운 방문 횟수가 72.81%라는 것은 얼마나 충성도가 낮은 사이트인지를 말해 줍니다. 방문당 평균 채류 시간, 평균 페이지뷰 같은 재미난 정보도 나와 있습니다. 각 정보 옆에 나온 작은 그래프는 기간 내의 데이터를 작게 줄여서 표시한 것 입니다. 각 데이터의 특징을 간략하게 볼 수 있죠. 정보 부분을 클릭하면 보다 자세한 내용이 나옵니다.

사용자 삽입 이미지

아래는 방문자의 충성도(로열티)를 보여주는 표 입니다. 보시면 앞서도 언급했듯이 지니야닷넷을 방문해주시는 분들은 거의 대부분이 새로운 방문자라는 것을 알 수 있습니다. 우연찮게 한 번 들리고 마는 것이죠. 굉장히 슬픈 현실입니다. 몇 안되는 방문자 중에서도 그나마도 우연찮게 한번 들리는 사람이 대부분이라는 사실 ㅠㅠ

사용자 삽입 이미지

아래는 제가 완전 감동먹은 기간 선택 UI입니다. 통계 정보를 볼 기간을 선택하는 부분입니다. 달력에서 클릭하면 날짜가 자동적으로 입력됩니다. 세달 이전이나 이후로는 옆에 있는 버튼을 누르면 이동됩니다. DateRange 밑에 파란 박스가 보이시나요? 그건 지금 달력에서 클릭하면 그 자리에 입력된다는 것을 나타내는 포커스 표시입니다. 대단합니다. 군더더기 없는 황홀한 UI에 놀랐습니다.

사용자 삽입 이미지

예전엔 유료 통계 서비스가 있었습니다. 지금도 있나요? 하여튼 그런데 참 이런 구글의 통계 서비스를 보고 있자면, "이런게 공짜라면 당췌 유료 통계 서비스를 기획하는 사람들은 어떻게 만들어야 돈받고 팔 수 있을까?"란 생각이 머릿속을 파고 듭니다. 통계 서비스는 더 이상 경쟁력이 없는 건가요? 사용자로서 행복하지만 다른 한편으로는 참 씁쓸했던 구글의 통계 서비스 체험기였습니다.

홈페이지 혹은 블로그를 운영하신다면 꼭 가입해서 사용해 보세요. 수준 높은 서비스를 체험할 수 있습니다. 정말 생각지도 못했던 통계 자료도 많습니다. 물론 그런 것들이 개인이 운영하는 페이지에는 큰 영향을 미치진 않겠지만 말이죠.

2007/08/22 12:54
철학콘서트
[GGG]


재미없을 것 같은 이 책. 보기보다 상당히 재밌습니다. 저자가 꽤나 코믹하게 이야기를 잘 풀어나가기 때문인 것 같습니다. 마치 할아버지한테 옛날 이야기를 듣는 느낌입니다. 여러 명의 철학자에 관한 이야기가 하나씩 나오는데 저마다 다들 인상깊고 감동적입니다. 그 중에서도 저는 특히 퇴계편에 정말 큰 감동을 받았습니다. 진리 앞에 겸허한 학자라는 말이 정말 어울리더군요. 아래는 제가 그런 생각을 갖게된 책의 중요 부분들입니다.
조선 선비의 사부인 퇴계는 26년 연하인 고봉에게 깍듯이 존대한다. 나이 60, 이순을 바라보는 대학자가 이제 나이 이립을 갓 넘어선 젊은 후학에게 깍듯한 예를 다하여 존대하는 장면은 우리에게 충격적이다. 퇴계는 고봉을 존중했고, 그에 따르는 예의를 다한 것이다. 장장 13년에 걸친 사단칠정의 논변. 안동에 사는 스승과 광주에 사는 제자가 그것도 편지로 논변을 이은 것이다.

"<<성학십도>>를 보냅니다. 잘못된 곳이 있으면 가리켜 논박한 뒤 돌려주시면 어떻겠습니까? 일단은 다른 사람 눈에 띄게 하지 마십시오."
"영공에게 절하며 사례합니다. 그대의 가르침을 받으니 감격스럽고 다행스러웠습니다. 고증이 소홀했던 부분들을 깨우쳐주시니 저로서는 깊이 다행스럽다고 여겼습니다."

퇴계는 마음을 다 열어 제자들의 조언을 구한 분이었다. 참으로 진리 앞에 겸허한 대학자이다. <<성학십도>>는 퇴계가 평생 걸려 완성하는 성리학 작품. 왕에게 바치는 지성으로 작성한 이 <<성학십도>>의 초고에 대해 퇴계는 제자 고봉에게 정정을 부탁한 것이다.
그렇다면 문제의 사단칠정 논변의 결말은 어떻게 되었을까? 퇴계는 제자의 논변이 논리적으로 옳다는 것을 이해했을 것이다. 그러나 고봉의 논리를 있는 그대로 다 수용하면 성리학의 토대가 무너진다는 것 또한 잘 알고 있었다. 고봉이 논리적으로 우수하나, 세상을 아우르는 지혜가 부족함을 스승은 안타까이 생각했을 것이다. 퇴계는 고봉이 진정한 인격자, 원숙한 정치가로 성장하길 바랐을 것이다. 72세의 나이로 퇴계가 세상을 뜨자, 아직 살날이 많은 고봉도 이어 숨을 거둔다.

"지금까지 '사물의 이치에 이른다.'와 '무극이면서 태극이다.'에 대한 저의 견해는 모두 잘못되었습니다."
퇴계가 죽기 전 고봉에게 보낸 편지이다. 우리로서는 도무지 다가설 수 없는 경지이다.
자신보다 어린 사람을 깎듯이 대하는 것이나, 어린 사람임에도 불구하고 그 사람의 지식에 대해서 존중하는 태도를 가지는 것이나, 자신의 틀린점을 상대에게 고백하는 것이나 한 가지도 쉬운게 없습니다. 특히나 자신이 소위 잘나가는 권위자인 경우에는 정말 낙타가 바늘 구멍에 들어가는 것보다 어렵습니다. 하지만 이 분. 퇴계 선생님은 모든 것을 하셨습니다. 정말 마지막에 잘못되었습니다. 저 문장을 읽을때는 가슴에 전율이 오더군요. 정말 범인인 우리로서는 도무지 다가설 수 없는 경지라는 말에 백번 공감이 갑니다.

[GGG]
제가 비스타를 시스템에 설치해서 사용한지는 꽤 되었습니다. 64비트 컴퓨터를 산지가 꽤 됐으니까 말이죠. ㅋ 그런데 비스타를 처음 깔아서 쓸때부터 지금까지 느꼈던 엄청난 단점이 있었습니다. 바로 엄청난 양의 디스크 I/O 였습니다. 제 컴퓨터는 빠방한 AMD 64비트 듀얼 코어에, 2기가 램을 가지고 있습니다. 그런데도 비스타는 저의 하드를 엄청나게 긁어 대는 것이었습니다. 상식적으론 도저히 납득이 되질 않죠. 무엇이 부족해서 그리도 디스크를 읽는단 말이냐? 하고 늘 생각했습니다.

그래서 처음 설치하고 쉽게 떠오르는 인덱싱 서비스나 디펜더 서비스를 중지 시켰습니다. 당연히 클린 시스템이니 자동 실행 프로그램이 많을리 없습니다. 이제는 조용하겠지? 하고는 재부팅을 했는데 소용없는 노릇이었습니다. 그렇게 몇 달간을 포기하고 지냈습니다. 회사 컴퓨터였으면 어떻게든 고쳤을텐데 집 컴퓨터라 할 시간이 많지 않아 그냥 두고 있었죠. 오늘 집에 와서 부팅을 시켰는데 이 놈 또 부팅하자마자 20분을 혼자 디스크를 긁고 있습니다. 조용해졌다 싶어서 작업을 좀 해볼까 하면 또 연나 긁어댑니다. 주기도 없고, 규칙도 없고 마음대로 긁어대는 거죠.

그래서 오늘 안되겠다 싶어서 구글님과 집중 면담을 했습니다. 그 결과 원인이 무엇인지 알아냈습니다. 휴우 ㅋㅋ 원인은 바로 비스타에서 새로 생긴 수퍼페치(Superfetch)라는 기능 때문이었습니다. 수퍼라... 먼가 좋아보이지 않나요? ㅎㅎ

수퍼페치가 하는 일을 아주 간단히 설명하면 메모리에 자주 사용하는 것들을 미리 올려두어서 사용하자 필요하는 시점에 그것들을 바로바로 제공해 주는 것입니다. 메모리를 캐시로 사용하겠다는 생각이죠. I/O 병목 지점에서 미리 읽어둔다면 실행 속도가 엄청 빨라질겁니다. 와우. 훌륭한 기능이죠. 물론 아이디어는 좋습니다. 하지만 수퍼페치의 기능을 몇 달간 체험해 본 바로 제가 느낀 점은 "너무 공격적이다"라는 것 입니다. 메모리를 아낌없이 사용하며, 디스크 I/O는 시도때도 해버리는 그런 존재인 것이죠. 제가 봤을때 수퍼페치를 설계한 사람들이 결정적으로 간과한 한 가지 사실은 하드디스크 소음입니다. 그 엄청난 I/O에서 발생하는 소음 공해들. 작업에 집중할 수 없는 환경을 만들어 줍니다. 이런 연유로 저는 오늘 당장 수퍼페치를 중지 시켰고 매우 만족스럽게 사용하고 있습니다. 이제야 조용한 컴퓨터가 되었네요.

그렇다면 수퍼페치는 어떻게 중지시키나요?

사용자 삽입 이미지

우선 제일 중요한 작업은 수퍼페치 서비스를 중지하는 것 입니다. 서비스 관리에 들어가서 Superfetch를 찾은 다음 속성을 누릅니다. 그리고는 과감하게 사용안함으로 바꾼다음 확인을 누르면 됩니다. 위와같이 표시된다면 정상적으로 중지된 것 입니다.

사용자 삽입 이미지

서비스만 중지해도 되는데 전 조금 찜찜해서 레지까지 수정했습니다.
HKLM\CurrentControlSet\Control\Session Manager\Memory Management\PrefetchParameters에 들어갑니다. 거기서 오른쪽에 표시되는 항목 중에 EnableSuperfetch 값을 0으로 변경해 줍니다. 참고로 값이 의미하는 바는 0은 중지, 1은 부팅 과정 수행, 2는 응용프로그램 수행, 3은 둘 다 수행입니다.

조용한 비스타를 원하시는 분들은 한번쯤 중지시켜 보세요.
분명 만족하실 겁니다.