드디어 다섯번째 까지 왔습니다.

얻으신 것이 있었나요? 없었다구요? ㅜㅜ;;

어쨌든 그건 상관 없습니다. 이번은 내용이 좀 깁니다. 그렇다고 할 게 많은 것은 아닙니다.

자세한건 나중에 뒤에 보시면 아실 테고.

생각보다 많은 분들이 좋아해 주셨습니다. 기분이 좋았습니다. 잠시 개인적인 얘기를 하려고 합니다. 저의 일과는
이렇습니다.

회사 퇴근해서 밥 먹고 ‘인어 아가씨’ 본 다음(이거 정말 재미 있습니다. 요즘은 이거 보는 낙으로 살죠 ^^)
바로 9시 뉴스 보면서 세상이 어떻게 돌아가나 봅니다.

그리고 요 며칠(4일 동안인가 봅니다.) 이 글 쓰느라 보통 3 ~ 4시간 보내고 새벽 1시나 3시쯤에 글
데브피아에 올린 다음 잡니다. 그래서 비몽사몽간에 글을 씁니다. 따라서 주절주절 앞뒤 안 맞는 얘기도 많았을 겁니다. 하루를 쉬니 머리가 조금은
맑아 졌습니다. 그래도 앞뒤가 잘 안 맞을 겁니다.(원래 이렇습니다. 비몽사몽 어쩌고는 다 핑계입니다. ^^) 오늘은 시간이 꽤 걸릴 것
같습니다. 잠 못 잘 지도 모릅니다. 회사에서 요즘 밤에 뭐 하느냐고 의심의 눈초리로 째려봅니다. [ㅡ ㅡ]

잡담 그만하고 빨리 하라구요? 넵! 알겠습니다.

 

 

그럼 시작하겠습니다.

 

오늘 설명 하지 않겠다. 설명은 #6에서 본격적으로 하겠다.

오늘은 그냥 흐름을 파악하면 된다. 그냥 이렇게 만들면 되는 구나 라고 생각만 하면 될 듯 싶다. (중간에 설명이
없더라도 섭섭하게 생각하지 마라.)

COM 컴포넌트를 만드는 방법은 다양하다. 사람이라는 것이 간사해서 한번 맛들인 방법을 끝까지 고집하게 된다.
그래서 ATL을 사용해본 사람은 절대 다른 방법으로 하려고 하지 않는다. 하지만, ATL은 COM을 완벽하게 지원하지 않는 다는 걸 알아야
한다. 그렇다고 ATL에서 지원 하는 것 이상 만들 자신도 없지만 말이다. 그래도 기분 나쁘잖아~~. 있어 보이는 척 하는 거 빼면 시체인
난데.. 그럴 순 없쥐~.

오늘 여러분은 개념 이해하느라 머리 쥐어 뜯을 일이 없다. 그냥 아무 생각 없이 따라 하기만 하면 된다. 그럼
COM 서버 한번 만들어 보자. (서버란 말이 나오니, 왠지 서버 프로그래머가 된 것 같다. ^^)

먼저 오늘 만들 놈에 대해서 미리 어떤 일을 하는 놈인지 알고 들어가면 쉬울 것이다. 이번에 만들 COM
컴포넌트가 하는 일은 두 숫자를 입력 받아서 더한 결과를 돌려준다. 간단하다. 더 이상 말이 필요없다. 대부분의 COM 예제들이 이런 걸로 알고
있다.

그리고 먼저 밝혀 둘 것이 있다. 이 소스는 codeguru 사이트의 COM 란에서 가져온 소스를 내 나름대로
조금 편집한 소스이다. 모든 기능을 죽이고 우리나라 정서(?)에 맞게 조금 수정했다. (아무래도 내가 만들면 사람들이 믿지 않을 것 같다.)

그럼 시작해보자.

먼저 새 프로젝트를 열자.

 

 

아주 친숙한 화면이 나타났다. COM을 만든다고 ATL COM AppWizard를 선택하면 안 된다.

Win32 DLL을 선택하고 프로젝트 명을 넣자. 다 끝났다면 OK 버튼을 누른다.

 

An empty DLL project를 선택하고 Finish 버튼을 누른다.

자 기본적인 준비는 끝났다.

그리고 이제 .idl 파일이 필요하다. 인터페이스를 정의하는 파일이다. 그러려면 GUID도 하나 필요하다.
GUID를 쉽게 만드는 방법이 있다.

실행에서 다음과 같이 명령어를 입력해 보자.

 

 

그럼 다음과 같은 창이 하나 뜬다.

 

 

말 그대로 GUID를 랜덤하게 계속 만들 수 있다.

여기서 하나를 만들어서 Copy 버튼을 누른다.

그리고 소스에서 GUID가 필요한 부분에서 Paste 하면 나타난다.

새 파일을 하나 열고 다음과 같이 타이핑한다. 아니다. Copy & Paste 하면 되겠다.

import “unknwn.idl”;

 

[

uuid(12D90058-C2A5-4950-8355-1DC0189FFD0D),

helpstring(“여기에 필요한 설명을 적는다.”)

]

interface IAdd : IUnknown

{

   HRESULT     SetFirstNum(long nFirst);

   HRESULT     SetSecondNum(long nSecond);    

   HRESULT     GetSum([out, retval] long *pBuffer);

}

 

[

uuid(9D807A19-9A1A-4879-A7C0-6D3AFD04F7B8),

helpstring(“라이브러리에 대한 설명을 적는다.”)

]

library IcoddyLib

{

importlib(“stdole32.tlb”);

importlib(“stdole2.tlb”);

 

   interface IAdd;    

}

여기서 잠시 IDL이 뭔지 알아보자.

IDL(Interface Definition Language) 해석하면 인터페이스 정의 언어란 말이다. 일반적으로
인터페이스는 C++ 언어로 표현이 가능했다. 즉, 순수가상함수로 만들 수도 있다. 하지만, IDL로 만드는 것이 여러모로 노가다 작업을 줄일 수
있다. 그리고 그렇게 어렵지도 않다.

이 언어는 MIDL(Microsoft Interface Definition Language) 컴파일러로 컴파일 할
수 있다.

도스창에서 midl IAdd.idl 이렇게 실행하면 컴파일 된다. 여기서 여러가지 부산물들을 얻을 수
있다. 우리가 사용할 수 있는 헤더파일도 만들어 준다. 그리고 가장 중요한 것은 나중에 설명하겠지만, Proxy 와 Stub 코드를 만들어
준다는 것이다. 그리고 또 한가지 타입라이브러리를 만들어 준다는 것이다.

대충 이렇게 알고 넘어가자. 이 부분도 하루종일 해야 하는 부분들이다. 이 쪽을 파다가는 오늘 실습은 그냥
포기해야 한다.

그럼 다시 노가다 작업으로 들어가자. IAdd.idl 로 파일이름을 저장하고 Project메뉴를 사용해서
프로젝트에 추가한다.

 

그럼 다음과 같이 나타날 것이다.

 

Project 메뉴에서 Settings.. 메뉴를 클릭한다.

다시 거기서 Iadd.idl 파일을 선택한다. 컴파일을 먼저 해보기 위해서다.

Always use custom build step 를 체크하고 다음 텝을 선택한다.

 

위의 그림과 같이 명령어를 입력한다. 이 과정이 귀찮으면 도스창에서 직접 ‘midl IAdd.idl’ 을 직접
입력해도 상관없다. 결과는 같은 테니깐 말이다.

그럼 다음과 같이 같은 폴더에 5개의 파일이 덤으로 생성된다.

그리고 추가로 COM 컴포넌트를 다 만들고 나서 레지스트리에 추가하는 것도 귀찮으니 그것도 설정을 미리
해버리자. 아래 그림과 같이 프로젝트를 선택하고 마지막 텝의 Post-build step를 선택하고 명령어를 입력한다. 그러면 컴파일이 끝나면
알아서 이 명령어를 실행시킨다.

 

그 다음 부터는 아래의 파일들을 전부 위의 주석에 나온 파일 이름대로 다 저장하자.

그리고 다 끝났다면 *.cpp 와 *.def 파일을 프로젝트에 추가한다.

자, 그럼 지금부터 노가다를 좀 해라.
나는 노가다 하는 동안 좀 쉬어야 겠다. 커피도 고프고 담배도 고프다.

 

~

~

~

~

~

~

~

////////////////////////////////////////////////////////////////////

// AddComObj.h 파일

////////////////////////////////////////////////////////////////////

#include    “IAdd.h”

 

extern long g_nComObjsInUse;

 

class CAddComObj : public IAdd

{

private:

   long m_nFirst , m_nSecond; //operands for addition
   

   long m_nRefCount;   //for managing the reference count
         

 

public:  

   //IUnknown 인터페이스의 메서드를 구현한다.

   HRESULT __stdcall QueryInterface(REFIID riid, void
**ppObj);

            ULONG   __stdcall AddRef();

            ULONG   __stdcall Release();

           

   //IAdd 인터페이스의 메서드들…

   HRESULT __stdcall SetFirstNum( long nFirst);

   HRESULT __stdcall SetSecondNum( long nSecond);

   HRESULT __stdcall GetSum( long *pBuffer);

                       

   CAddComObj()

            {

       m_nRefCount=0;        

       InterlockedIncrement(&g_nComObjsInUse);      

            }            

           

   ~CAddComObj()

            {

       InterlockedDecrement(&g_nComObjsInUse);
                       

            }                

};

 

////////////////////////////////////////////////////////////////////

// AddComObj.cpp 파일

////////////////////////////////////////////////////////////////////

#include    <objbase.h>

#include    “AddComObj.h”

#include    “IAdd_i.c”

 

/////////////////////////////////////////////////////////////////////

// IAdd 인터페이스의 메서드들을 구현한다.

//

// SetFirstNum : 더할 숫자들 중 첫번째 수를 지정한다.

// SetSecondNum : 더할 숫자들 중 두번째 수를 지정한다.

// GetSum : 두 숫자의 합을 얻어 온다.

/////////////////////////////////////////////////////////////////////

HRESULT __stdcall CAddComObj::SetFirstNum(long nFirst)

{

   m_nFirst = nFirst;

   return S_OK;

}

 

HRESULT __stdcall CAddComObj::SetSecondNum(long nSecond)

{

   m_nSecond = nSecond;

   return S_OK;

}

 

HRESULT __stdcall CAddComObj::GetSum(long *pBuffer)

{

   *pBuffer = m_nFirst + m_nSecond;      

   return S_OK;

}

 

/////////////////////////////////////////////////////////////////////

// IUnknown 인터페이스의 메서드들을 구현한다.

// 다음의 3개 메서드가 기본이쥐~~~~~

// AddRef()

// Release()

// QueryInterface(REFIID riid, void **ppObj)

/////////////////////////////////////////////////////////////////////

ULONG   __stdcall CAddComObj::AddRef()

{

            return InterlockedIncrement(&m_nRefCount);

}

 

ULONG   __stdcall CAddComObj::Release()

{    

            long nRefCount = 0;

            nRefCount =
InterlockedDecrement(&m_nRefCount);

           

            // 참조 카운트가 없으면 스스로 해제한다.

            if (nRefCount == 0) delete this;

 

            return nRefCount;

}

 

HRESULT __stdcall CAddComObj::QueryInterface(REFIID riid, void
**ppObj)

{

   if (riid == IID_IUnknown)

            {

                          *ppObj =
static_cast<IAdd*>(this);

       AddRef();

       return S_OK;

            }

           

   if (riid == IID_IAdd)

            {

                          *ppObj =
static_cast<IAdd*>(this);

       AddRef();

       return S_OK;

            }

           

   

   *ppObj = NULL;

   return E_NOINTERFACE;

}

 

 

////////////////////////////////////////////////////////////////////

// AddComObjFactory.h 파일

////////////////////////////////////////////////////////////////////

extern long g_nComObjsInUse;

 

class CAddComObjFactory : public IClassFactory

{

private:

   long m_nRefCount;

 

public:

   CAddComObjFactory()

            {

       m_nRefCount=0;

       InterlockedIncrement(&g_nComObjsInUse);

            }

           

   ~CAddComObjFactory()

            {

       InterlockedDecrement(&g_nComObjsInUse);

            }

           

           

   // IUnknown 인터페이스의 메서드들…

   HRESULT __stdcall QueryInterface(REFIID riid, void
**ppObj);      

            ULONG   __stdcall AddRef();

            ULONG   __stdcall Release();    

           

           

            // IClassFactory 인터페이스의 메서드들…

            virtual HRESULT __stdcall CreateInstance(IUnknown*
pUnknownOuter,

                          const IID& iid,

                          void** ppv) ;

            virtual HRESULT __stdcall LockServer(BOOL bLock) ;

};

 

 

////////////////////////////////////////////////////////////////////

// AddComObjFactory.cpp 파일

////////////////////////////////////////////////////////////////////

#include    <objbase.h>

#include    “AddComObjFactory.h”

#include    “AddComObj.h”

 

/////////////////////////////////////////////////////////////////////

// IUnknown 인터페이스의 메서드들을 구현한다.

//

// AddRef

// Release

// QueryInterface  

/////////////////////////////////////////////////////////////////////

ULONG   __stdcall CAddComObjFactory::AddRef()

{

            return InterlockedIncrement(&m_nRefCount) ;

}

 

ULONG   __stdcall CAddComObjFactory::Release()

{

            long nRefCount = 0;

            nRefCount =
InterlockedDecrement(&m_nRefCount);

            if (nRefCount == 0) delete this;

            return nRefCount;

}

 

HRESULT __stdcall CAddComObjFactory::QueryInterface(const
IID& iid, void** ppv)

{    

            if ((iid == IID_IUnknown) || (iid ==
IID_IClassFactory))

            {

                          *ppv =
static_cast<IClassFactory*>(this);

            }

            else

            {

                          *ppv = NULL;

                          return E_NOINTERFACE;

            }

 

            reinterpret_cast<IUnknown*>(*ppv)->AddRef();

            return S_OK;

}

 

/////////////////////////////////////////////////////////////////////

// IClassFactory 인터페이스의 메서드들을 구현한다.

//

// LockServer

// CreateInstance

/////////////////////////////////////////////////////////////////////

HRESULT __stdcall CAddComObjFactory::CreateInstance(IUnknown*
pUnknownOuter,               const IID& iid,
                                                                                                             void**
ppv)

{

   // Aggregation을 사용하지 않는다.

            if (pUnknownOuter != NULL)

            {

                          return CLASS_E_NOAGGREGATION;

            }

           

   CAddComObj* pObject = new CAddComObj;

            if (pObject == NULL)

            {

                          return E_OUTOFMEMORY;

            }

           

   return pObject->QueryInterface(iid, ppv);

}

 

HRESULT __stdcall CAddComObjFactory::LockServer(BOOL bLock)

{

            return E_NOTIMPL;

}

////////////////////////////////////////////////////////////////////

// AddComObjGuid.h 파일

////////////////////////////////////////////////////////////////////

#ifndef __AddObjGuid_h__

#define __AddObjGuid_h__

 

// // {4EE0DC95-64F3-4ad6-A1FC-191A9FAEA849}

static const GUID CLSID_AddObject =

{ 0x4ee0dc95, 0x64f3, 0x4ad6, { 0xa1, 0xfc, 0x19, 0x1a, 0x9f,
0xae, 0xa8, 0x49 } };

 

#endif

 

 

////////////////////////////////////////////////////////////////////

// Exports.cpp 파일

////////////////////////////////////////////////////////////////////

#include    <objbase.h>

#include    “AddComObj.h”

#include    “AddComObjFactory.h”

#include    “AddComObjGuid.h”

 

HMODULE g_hModule = NULL;

long g_nComObjsInUse = 0;

 

///////////////////////////////////////////////////////////////////////////////

// 여기가 DllMain 이다.

// DLL_PROCESS_ATTACH : DLL이 프로세스의 주소 영역에 맵핑된다.

///////////////////////////////////////////////////////////////////////////////

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void*
lpReserved)

{

            if (dwReason == DLL_PROCESS_ATTACH)

            {

                          g_hModule = (HMODULE)hModule ;

            }

 

            return TRUE ;

}

 

///////////////////////////////////////////////////////////////////////////////

// COM 라이브러리의 CoGetClassObject 함수에서 DLL의 DllGetClassObject함수를
호출하고,

// 이 함수에서 실제로 클래스팩토리 COM 개체를 생성하게 된다.

///////////////////////////////////////////////////////////////////////////////

STDAPI DllGetClassObject(const CLSID& clsid, const IID&
iid, void** ppv)

{

   if (clsid == CLSID_AddObject)

            {

                          // CAddComObjFactory 를 생성한다.

       CAddComObjFactory *pAddFact = new CAddComObjFactory;

 

       if (pAddFact == NULL)

                          {

           return E_OUTOFMEMORY;

                          }

       else

                          {

           return pAddFact->QueryInterface(iid , ppv);

                          }

            }    

           

   return CLASS_E_CLASSNOTAVAILABLE;

}

 

///////////////////////////////////////////////////////////////////////////////

// COM 라이브러리의 CoFreeUnusedLibraries 함수는 DLL의 DllCanUnloadNow
함수를 호출한다.

// 즉, DLL을 해제 시켜도 좋은지 물어본다.

///////////////////////////////////////////////////////////////////////////////

STDAPI DllCanUnloadNow()

{

   if (g_nComObjsInUse == 0)

            {

       return S_OK;

            }

   else

            {

       return S_FALSE;

            }

           

}

 

 

;Experts.def 파일이다.

DESCRIPTION     “Simple COM object”

 

EXPORTS

               DllGetClassObject             PRIVATE

               DllCanUnloadNow            PRIVATE

               DllRegisterServer              PRIVATE

               DllUnregisterServer            PRIVATE

 

 

 

////////////////////////////////////////////////////////////////////

// Registry.h 파일

////////////////////////////////////////////////////////////////////

#ifndef __Registry_H__

#define __Registry_H__

 

HRESULT RegisterServer(HMODULE hModule,

                      const CLSID& clsid,

                      const char* szFriendlyName,

                      const char* szVerIndProgID,

                      const char* szProgID) ;

 

HRESULT UnregisterServer(const CLSID& clsid,

                        const char* szVerIndProgID,

                        const char* szProgID) ;

 

#endif

 

 

////////////////////////////////////////////////////////////////////

// Registry.cpp 파일

////////////////////////////////////////////////////////////////////

#include    <windows.h>

#include    <objbase.h>

#include    “AddComObjGuid.h”

 

#define     AddObjProgId    “IcoddyLib.Sum”

 

extern HMODULE g_hModule;

 

 

BOOL   HelperWriteKey(HKEY roothk, const char *lpSubKey,
LPCTSTR val_name, DWORD dwType, void *lpvData, DWORD dwDataSize)

{

           

   HKEY hk;

   if (ERROR_SUCCESS != RegCreateKey(roothk,lpSubKey,&hk)
) return FALSE;

           

   if (ERROR_SUCCESS !=
RegSetValueEx(hk,val_name,0,dwType,(CONST BYTE *)lpvData,dwDataSize)) return
FALSE;

   

   if (ERROR_SUCCESS != RegCloseKey(hk))   return FALSE;

 

   return TRUE;

           

}

 

// COM 개체를 시스템 레지스트리에 등록할 때 Regsvr32.exe 에 의해 호출 된다.

HRESULT  __stdcall DllRegisterServer(void)

{

   WCHAR *lpwszClsid;

   char szBuff[MAX_PATH]=””;

   char szClsid[MAX_PATH]=””,
szInproc[MAX_PATH]=””,szProgId[MAX_PATH];

   char szDescriptionVal[256]=””;

           

   StringFromCLSID(CLSID_AddObject, &lpwszClsid);

   

   wsprintf(szClsid,”%S”,lpwszClsid);

   wsprintf(szInproc,”%s\\%s\\%s”,”clsid”,szClsid,”InprocServer32″);

   wsprintf(szProgId,”%s\\%s\\%s”,”clsid”,szClsid,”ProgId”);

           

           

   wsprintf(szBuff,”%s”,”icoddy’s sum”);

   wsprintf(szDescriptionVal,”%s\\%s”,”clsid”,szClsid);

           

   HelperWriteKey (HKEY_CLASSES_ROOT, szDescriptionVal, NULL,
REG_SZ, (void*)szBuff, lstrlen(szBuff));

           

           

   GetModuleFileName(g_hModule, szBuff, sizeof(szBuff));

   HelperWriteKey (HKEY_CLASSES_ROOT, szInproc, NULL, REG_SZ,
(void*)szBuff, lstrlen(szBuff));

           

   lstrcpy(szBuff,AddObjProgId);

   HelperWriteKey (HKEY_CLASSES_ROOT, szProgId, NULL, REG_SZ,
(void*)szBuff, lstrlen(szBuff));

           

           

   wsprintf(szBuff,”%s”,”icoddy’s sum”);

   HelperWriteKey (HKEY_CLASSES_ROOT, AddObjProgId, NULL,
REG_SZ, (void*)szBuff, lstrlen(szBuff));

           

           

   wsprintf(szProgId,”%s\\%s”,AddObjProgId,”CLSID”);

   HelperWriteKey (HKEY_CLASSES_ROOT, szProgId, NULL, REG_SZ,
(void*)szClsid, lstrlen(szClsid));

           

   return 1;        

}

 

// COM 개체를 시스템 레지스트리에 등록할 때 Regsvr32.exe 에 의해 호출 된다.

HRESULT  __stdcall DllUnregisterServer(void)

{

   charszKeyName[256]=””,szClsid[256]=””;

   WCHAR *lpwszClsid;  

   

            wsprintf(szKeyName,”%s\\%s”,AddObjProgId,”CLSID”);

   RegDeleteKey(HKEY_CLASSES_ROOT,szKeyName);

   RegDeleteKey(HKEY_CLASSES_ROOT,AddObjProgId);

           

           

   StringFromCLSID(CLSID_AddObject, &lpwszClsid);

   wsprintf(szClsid,”%S”,lpwszClsid);

   wsprintf(szKeyName,”%s\\%s\\%s”,”CLSID”,szClsid,”InprocServer32″);

   RegDeleteKey(HKEY_CLASSES_ROOT,szKeyName);

           

   wsprintf(szKeyName,”%s\\%s\\%s”,”CLSID”,szClsid,”ProgId”);

   RegDeleteKey(HKEY_CLASSES_ROOT,szKeyName);

           

   wsprintf(szKeyName,”%s\\%s”,”CLSID”,szClsid);

   RegDeleteKey(HKEY_CLASSES_ROOT,szKeyName);

           

   return 1;        

}

 

여기가 끝이다.

마지막으로 컴파일을 해보자.

이렇게 나오면 성공한 거다.

진심으로 축하한다. 드뎌 COM 컴포넌트를 만든 것이다.

그럼 이제 클라이언트 프로그램을 만들어서 실제로 잘 동작하는 지 확인 해야 한다. 내가 이 수고를 덜겠다.
해보니깐 잘된다. 밑에 그림 보이쥐? 그럼 됐다. 저거 조작한 거 아냐?라고 의심하는 사람은 잘 봐라. 결과가 읽기 속성으로 되어있다. 그래도
조작한 거라고?

우쒸~그래 조작했다. 어쩔 건데? 여긴 할 게 별로 없으니 소스나 한번 훑어 보고 지나가라.

 

 

결과 보기 버튼을 누르면 결과가 나온다.

void CAddComClientDlg::OnButtonSum()

{

            // TODO: Add your control notification handler
code here

            UpdateData(TRUE);

 

            HRESULT hr;

            hr = CoInitialize(NULL);

            if(FAILED(hr))

            {

                          AfxMessageBox(“COM 라이브러리를 초기화 하지
못했습니다.”);

                          SendMessage(WM_CLOSE);

            }

 

IcoddyLib::IAddPtr pIcoddyLib;    

pIcoddyLib.CreateInstance(“IcoddyLib.Sum”);

pIcoddyLib->SetFirstNum(m_nFirst);

          pIcoddyLib->SetSecondNum(m_nSecond);

          m_nSum = pIcoddyLib->GetSum();

 

            UpdateData(FALSE);

}

오늘은 따로 설명을 하지 않겠다. 오늘 이것을 나에게 요구한다면 나 그만 둘거다. 협박하는 거다. 절대 강요하지
마라. 기분 나쁘면 언제든지 그만둔다. 내 맘이다.(헛~~ 또 돌 날라오넹)

자, 여러분들 너무 지쳤을 것이다.

설명은 다음 경험담 #6에서 본격적으로 하겠다. 열심히 깊이 팔 준비를 하고 오면 고맙겠다.

오늘의 핵심은 이 과정을 기억하라는 것이다. 소스는 나중 일이다. 이 과정만 알면 다음에 언제든지 소스
참조 하면서 만들면 된다.

 

 

헐~~~ 날 다 샜다.

대충 예상은 했었지만, 정말 이렇게 될 줄이야.

TV 좀 보다가 씻고 출근해야 한다. 밥은 뭐 해먹지? 음.. 즉석 미역국이 있었네. 그거 해 먹어야 겠다.
800원인데 두개 들어 있다. 즉, 한 개 400원 꼴이니, 정말 싸고 맛있게 먹는 거다. 여러분도 라면으로 끼니 떼우지 말고 이런 거
사먹어라. 이건 3분도 안 걸린다. 몸에도 좋다. 아침 먹는 사람이 그렇지 않은 사람보다 수명이 10년이 더 길다고 한다. 진짜다.
믿어라.(사실은, 나도 귀찮아서 일주일에 한번도 아침 못 먹는다.)

그럼 날밤 새도 끄떡없는 건강한(?) 프로그래머 세계를 꿈꾸며 이만 끝내야 겠다.

Leave a Reply

Your email address will not be published. Required fields are marked *