너 “또라이”지?

오늘 문제의 원인을 알고 내가 나한테 던진 말이다.

“너.. 또라이지?” ㅋㅋ

문제는 MySQL에서 BLOB Type으로 데이터를 저장한 후에 읽어보니 전체 데이터가 아닌 일부만 읽혀지는 것이였다. 테이블에 저장한 그림파일의 크기는 195,881KB. 그런데 읽혀지는 데이터 길이는 65,535KB. 저장할때 잘못된 건지.. 읽어올때 잘못된 것인지 고민하다가 이것저것.. MySQL 환경설정 파일도 건드려 보고 말이다. 환경설정 파일을 보고 MySQL… 이거 제대로 쓸라면 공부할거 너무 많은것 같다.. 라는 부담스런 생각도 들고… 뭐 여튼… 아무리 인터넷을 찾아봐도 BLOB 데이터가 전부 오지 않는 이유는 찾을 수가 없었다. 구글신에게 도움을 요청해도 없다는건…. 뭔가 이유가 “얼토당토” 않은 것이라는 확율이 90%이상이란 말인데… 하지만 여전이 나는 “MySQL 이놈이 분명이 BLOB 데이터의 크기를 제한하고 있는게 분명해. 봐… 65,535KB만 보내주잔아? 즉, 64K만 보내주고 있다구.. 분명 환경설정에서 전송 크기를 늘려주는 환경변수가 있을 것이고 그것만 늘려주면 될것야..”라고 확신하며 그다지 흥미롭지 않은 MySQL 환경 변수만을 뒤지기 시작했으나.. 도대체 이놈이다 싶은 놈이 없었다. 음… 혹시 BLOB의 최대 크기가 64KB아냐? 라는 생각에 찾아보니 그냥 BLOB만 있는게 아니라 TINYBLOB, MEDIUMBLOB, LONGBLOB 라는 Type도 있는게 아닌가.. 각각의 최대 크기를 보니까

  • BLOB는 2^16-1KB
  • TINYBLOB는 2^8-1KB
  • MEDIUMBLOB는 2^24-1KB
  • LONGBLOB는 2^32-1KB

였다. 봐.. 다 “65,535KB보다 크잔아..”라고 자신있게 확신한 후에, 역시 원인은 MySQL의 환경변수에 있어 하고 있는데..  근데 2^16이 몇이지? 라는 생각에 계산기를 뚜드려보니.. 2^16 = 65536 !! 으아~~ ㅜ_ㅜ 저 또라이 맞죠? ㅠ_ㅜ 그데 쓰고보니 또라이는 좀 심했다는 생각에 또 다시 ㅠ_ㅠ

나 이제 퇴근할래.. 지하철 끊기기 전에~

[C++] LPCSTR Type을 LPOLESTR Type으로 변환

HRESULT __fastcall AnsiToUnicode(LPCSTR pszA, LPOLESTR* ppszW) { 
    ULONG cCharacters;
    DWORD dwError;

    // If input is null then just return the same.
    if (NULL == pszA)
    {
        *ppszW = NULL;
        return NOERROR;
    }

    // Determine number of wide characters to be allocated for the
    // Unicode string.
    cCharacters =  strlen(pszA)+1;

    // Use of the OLE allocator is required if the resultant Unicode
    // string will be passed to another COM component and if that
    // component will free it. Otherwise you can use your own allocator.
    *ppszW = (LPOLESTR) CoTaskMemAlloc(cCharacters*2);
    if (NULL == *ppszW)
        return E_OUTOFMEMORY;

    // Covert to Unicode.
    if (0 == MultiByteToWideChar(CP_ACP, 0, pszA, cCharacters,
                  *ppszW, cCharacters))
    {
        dwError = GetLastError();
        CoTaskMemFree(*ppszW);
        *ppszW = NULL;
        return HRESULT_FROM_WIN32(dwError);
    }

    return NOERROR;
}

SOAP, SOAP, SOAP, SOAP.. 지금 내 머리속엔 비누방울이 아른 아른~ 아침 출근할때 감기 바이러스가 몸에 침투를 했나부다. 몸에 열… 점심때 후식으로 먹은 빵이 불량식품이였나부다. 속이 부글부글.. 뼈를 싹인다는 Coco Cola로 일단 속을 달래는 중….

으.. 아까 콜라 사러 1층 편의점에 가려고 엘리베이터타려고 기다리는데 (사무실은 11층).. 이 놈의 건물의 엘리베이터의 움직이는 속도가 정말 장난이 아니다.. 가끔 엘리베이터를 기다리는 건지… 한없이 감감 무소식으로 오지 않는 지하철을 기다리는건지….. 성질 급한 사람은 가끔 화가 날 법도 하다.. 나같이 오늘처럼 말이다..

Template을 이용한 Observer 패턴 – 2단계

이제 여기서부터는 개선의 여지를 찾아보겠습니다. 어떻게 하면 EventSrc 클래스가 담당하고 있는 Fire 함수를 Observed에게 전가시킬 수 있을까요? 그래서 EventSrc 클래스를 없앨 수 있겠는가? 그 해답은 함수자(Functor)에 있습니다.

먼저 연습겸해서 간단하게 함수자를 사용해서 1단계의 코드를 수정해 보도록 하겠습니다. 아래는 새롭게 추가한 함수자에 대한 클래스입니다.

class Functor {
private:
    int arg_;

public:
    Functor(int arg) {
        arg_ = arg;
    }

    void operator()(Observable *pOb) const {
        pOb->OnEvent(arg_);
    }
};

이 함수자를 사용하면, EventSrc 클래스의 Fire 함수가 아래처럼 무척 깔끔하게 작성됩니다.

void Fire(int a)
{
    std::for_each(m_listObserver.begin(), m_listObserver.end(), Functor(a));
}

하지만 이런 방식은 Fire 함수를 깔끔하게 만들어준다는 사소한 장점은 있지만, 다른 어플리케이션에 적용할때 매번 함수자를 각각의 경우에 맞게 새롭게 코딩해줘야 하는 수고로움이 더욱 많습니다. 또한 여전이 Observed에서는 자신이 관리하고 있는 Observer 객체들이 호출해야할 함수가 무엇인지를 알 길이 없습니다.

하지만 여기서 곰곰이 생각해 보면 이 함수자를 Observed 클래스의 inner class로 정의해보는 것에 대한 아이디어가 떠오릅니다. 게다가 이 함수자 역시 template로 정의해서 Observed 클래스가 관리하고 있는 Observer 객체들이 호출해야할 함수를 타입으로 받아 버린다면 Observed가 알아야할 Observer의 정보를 모두 Observed에게 넘겨줄 수 있게되어, Fire의 책임을 Observed가 맡을 수 있게 됩니다. 그러면 더 이상 EventSrc는 필요치 않게 되고요. 이러한 아이디어에 착안해서 새롭게 구현된 Observed 클래스는 다음과 같습니다.

template 
class Observed
{
public:
    class Firer1 {
    public:
        explicit Firer1(T_result (T::*pMember)(T_arg1), T_arg1 arg1) {
            m_pMemFunc = pMember;
            m_arg1 = arg1;
        }

        T_result operator()(T* pClass) const {
            return ((pClass->*m_pMemFunc)(m_arg1));
        }

    private:
        T_result (T::*m_pMemFunc)(T_arg1);
        T_arg1 m_arg1;
    };

public: // type define
    typedef std::list typeObservers;

protected: // private attribute
    typeObservers m_listObserver;
    T_result (T::*m_pMemFunc)(T_arg1);
}

public: // ctr & dtr
    Observed(T_result (T::*pMember)(T_arg1)) {
        m_pMemFunc = pMember;
    }
    virtual ~Observed() {}

public: // operator
    void RegisterObserver(T *pOb)
    {
        m_listObserver.push_back( pOb );
    }

    void UnRegisterObserver(T *pOb)
    {
        m_listObserver.remove(pOb);
    }

public: // Fire!
    void Fire(T_arg1 arg1)
    {
        std::for_each(m_listObserver.begin(), m_listObserver.end(), 
            Firer1(m_pMemFunc, arg1));
    }
};

굵은 청색 폰트로 된 것이 수정되거나 새롭게 추가된 것입니다. Observed가 관리하고 있는 Observer의 호출함수의 반환값과 인자, 그리고 호출함수에 대한 타입을 template 인자인 T_result와 T_arg1으로 받고, 호출함수는 생성자에서 받아 맴버변수인 m_pMemFunc에 저장하도록 하였습니다.

이렇게 새롭게 구현된 Observed를 이용해서 Client 측에서 사용하는 코드는 다음과 같습니다.

int _tmain(int argc, _TCHAR* argv[])
{
    Observed *pES = 
        new Observed(&Observable::OnEvent);

    Observable_A *pOA = new Observable_A();
    Observable_B *pOB = new Observable_B();

    pES->RegisterObserver(pOA);
    pES->RegisterObserver(pOB);

    pES->Fire(99);

    delete pOA;
    delete pOB;
    delete pES;

    return 0;
}

이제 마지막으로 하나 더 짚고 정리를 하겠습니다. 여기서 한가지 큰 문제가 있는데 그것은 Observed가 관리하고 있는 Observer의 호출해야할 함수의 인자 개수에 관한 문제입니다. 지금가지의 경우는 단지 하나의 인자만을 받는 경우지만 두개 이상의 인자를 받는 경우에 대한 처리도 필요하지요. 하지만 이것 역시 그리 어렵지 않게 해결할 수 있습니다. Observed의 inner class인 함수자의 이름이 Firer1인 이유는 하나의 인자를 받는 함수자이기 때문에 Firer 뒤에 1을 붙인 것입니다. 그렇다면 이제 2개의 인자를 받는 Firer2 함수자를 정의하고 인자를 2개를 받는 Fire 맴버함수를 하나더 만들어 두면 됩니다.

이제 Client는 1단계처럼 관리하고자 하는 Observer에 대해 EventSrc와 Observable 클래스 모두를 정의할 필요가 없이, Observable 클래스 단하나만 신경 쓰면 되게 되었습니다. 그리고 Observer에 대한 모든 관리에 대한 책임을 오직 Observed가 맡게 되어 SRP를 지키게 되었습니다.

Template을 이용한 Observer 패턴 – 1단계

C++의 template을 이용해서 Observer를 구현하는 것에 대한 단계적 설명입니다. 원본은 데브피아에서 김태현(tipani)님이 올려 놓으신 글을 기반으로 작성했으며 한단계 더 업그레이드 했습니다. 제가 늘 느껴오는 것이지만 C++의 template은 기존의 클래스간 관계도에 한정해 볼적에 그 디자인을 획기적으로 개선한다는 점에서 그 판도를 확 바꿀 수 있는 강력한 개념이라고 생각합니다. 아무쪼록 제가 김태현님의 글을 보고 매우 재미있게 template에 한발짝 다가섯듯이 여러분도 제 글을 통해 template에 한발짝 다가 설수있다면 정말 기쁘겠습니다. 참고로 이 글을 읽기 전에 Observer 패턴이 무엇인지 패턴입문서를 살펴보시길 바랍니다. 또한 이 글의 진행은 단계 단계 개선해 나가는 흐름으로 진행된다는 점에 유의하시길 바랍니다.

먼저 1단계입니다. 아래의 코드는 Observer들의 관리에 대한 책임을 맡고 있는 Observed 클래스입니다.

template 
class Observed {
public:
    Observed() {}
    typedef std::list typeObservers;
    virtual ~Observed() {}

    void RegisterObserver(T *pOb) {
        m_listObserver.push_back( pOb );
    }

    void UnRegisterObserver(T *pOb) {
        m_listObserver.remove(pOb);
    }

protected:
    typeObservers m_listObserver;

};

Observed가 관리하는 Observer 클래스인 Observabe 입니다. 단순히 Observed가 호출할 Observer의 OnEvent 함수가 순수가상함수로 선언되어 있습니다.

class Observable {
    virtual void OnEvent(int a) = 0;
};

그리고 아래는 Obserable를 상속받아 구현한 클래스들입니다.

class Observable_A : public Observable
{
    virtual void OnEvent(int a) {
        std::cout << "Fire_A -> " << a << std::endl;
    }
};

class Observable_B : public Observable
{
    virtual void OnEvent(int a) {
        std::cout << "Fire_B -> " << a << std::endl;
    }
};

이제 마지막으로 Observed가 자신이 관리하고 있는 Observable의 OnEvent를 호출해 줘야 하는데, Observed 클래스는 자신이 관리하고 있는 Observable의 타입을 모르기 때문에 Observed와 Observable의 관계를 연결해 주기 위한, Observed로부터 상속받은 EventSrc 클래스가 필요합니다.

class EventSrc : public Observed
{
    void Fire(int a)
    {
        typeObservers::itrator it;
        for(it=m_listObserver.begint(); it!=m_listObserver.end; ++it) {
            (*it)->OnEvent(a);
        }
    }
};

드디어 1단계의 Observer 패턴의 구현이 완성되었습니다. 실제 사용하는 예는 다음과 같습니다.

int main() {
    EventSrc *pES = new EventSrc();
    Observable *pO_A = new Observale_A();
    Observable *pO_B = new Observale_B();

    pES->RegisterObserver(pOA);
    pES->RegisterObserver(pOB);

    pES->Fire(99);


    delete pO_B;
    delete pO_A;
    delete pES;

    return 0;
}

1단계에서 산출된 소스만으로도 충분할 수도 있겠지만, 큰 문제점이 하나 있습니다. 그것은 바로 SRP(Single Responsiblity Principle)를 위반한다는 사실입니다. 즉, Obserable를 관리하는 책임을 Observed와 EventSrc라는 두개의 클래스가 책임을 나눠서 지고 있다는 점입니다. 그럴수밖에 없는 이유는 본문에 언급을 했구요.

이제 다음 2단계 이후부터는 이러한 SRP의 원칙을 지켜나가는 것을 해결문제로 다뤄나가면서 점차적으로 개선된 Observer 패턴을 구현해보겠습니다.