2014년 4월 17일 목요일

Tizen Multi Proc Service/UI App sample

아래 내용은 Tizen 2.2 기반이라 상위 버전에서는 별 의미 없는 내용입니다.
--------------------------------------------------------------------------- : https://developer.tizen.org/dev-guide/2.2.1/org.tizen.native.appprogramming/html/sample_descriptions/multiprocserviceapp.htm

: https://developer.tizen.org/dev-guide/2.2.1/org.tizen.native.appprogramming/html/sample_descriptions/multiprocserviceapp.htm

Tizen에서 ServiceApp과 UiApp간의 interaction을 알 수 있는 샘플
Timer event를 발생 시키는 ServiceApp과 이 ServiceApp에서 event를 받아 Ui에 표시해주는 application sample.

Service와 app간 통신은 IMessagePortListener를 상속받아서 메세지를 주고 받으며 처리한다.

실행해 보려면 Documentation에 나와 있다 시피
service app의 id와 service name을 manifest.xml에서 확인한 뒤
ui app의 SampleUiApp에서 아래 부분을 수정해 줘야 한다.


String serviceName(L".multiprocserviceapp");
String repAppId(15);

repAppId = L"LVjRUjlONC";

AppId serviceId(repAppId+serviceName);


[Tizen::Io::IMessagePortListener]

: https://developer.tizen.org/dev-guide/2.2.1/org.tizen.native.apireference/classTizen_1_1Io_1_1IMessagePortListener.html


이것 저것 다 빼고 발로 그린 class diagram


[MultiProcServiceApp]


[SampleServiceApp]
Tizen에서 UI feature를 가지지 않고 background에서 실행될 수 있는 ServiceApp을 상속 받는 class이며 background로 실행되어 Timer를 제어하여 상황 변경 시 event를 app으로 보내주는 것임.

초기화 시점에 App과의 통신을 위한 SampleMessagePort class와 Timer를 제어, event를 발생하는 SampleTimer를 생성하며 SampleMessagePort 생성(construct)시 port name으로 사용할 문자열을 전달하여 port를 생성하도록 한다.


[Message Port Communication]

: https://developer.tizen.org/dev-guide/2.2.0/org.tizen.native.appprogramming/html/guide/io/messageport.htm





bool
SampleServiceApp::OnAppInitializing(AppRegistry& appRegistry)
{
AppLog("SampleServiceApp::OnAppInitializing is called.");
result r = E_SUCCESS;

// Initialize ServerChannel
__pMessagePort = new (std::nothrow) SampleMessagePort();
TryReturn(__pMessagePort != null, false, "SampleServiceApp : [E_FAILURE] Failed to create __pMessagePort.");
AppLog("SampleServiceApp : __pMessagePort is created.");

r = __pMessagePort->Construct(LOCAL_MESSAGE_PORT_NAME);
TryReturn(IsFailed(r) != true, r, "SampleServiceApp : [%s] Failed to construct __pMessagePort", GetErrorMessage(r));
AppLog("SampleServiceApp : __pMessagePort is constructed.");

// Initialize Timer
__pTimer = new (std::nothrow) SampleTimer;
TryReturn(__pTimer != null, false, "SampleServiceApp : [E_FAILURE] Failed to create __pTimer.");
AppLog("SampleServiceApp : __pTimer is created.");

r = __pTimer->Construct();
TryReturn(IsFailed(r) != true, r, "SampleServiceApp : [%s] Failed to construct __pTimer", GetErrorMessage(r));
AppLog("SampleServiceApp : __pTimer is constructed.");

return true;
}



아래의 Tizen::Ui::Control의 가상함수인 OnUserEventReceivedN()를 구현 하여
SampleTimer로 부터 전달 받은 event를 App으로 전달한다.

void
SampleServiceApp::OnUserEventReceivedN(RequestId requestId, IList* pArgs)
{
switch (requestId)
{
case TIMER_START :
if (__pTimer != null)
{
__pTimer->Start();
}
break;
case TIMER_STOP :
if (__pTimer != null)
{
__pTimer->Stop();
}
Terminate();
break;
case TIMER_EXPIRED :
if (__pMessagePort != null)
{
HashMap *pMap = new HashMap(SingleObjectDeleter);
pMap->Construct();
pMap->Add(new String(L"ServiceApp"), new String(L"timer expired"));

__pMessagePort->SendMessage(pMap);

delete pMap;
}
break;
case TIMER_EXIT :
Terminate();
break;
default:
break;
}
}


[SampleMessagePort]

SampleMessagePort는 message를 수신받기위해 Tizen::Io::IMessagePortListener를 구현하였음.
위 그림에서와 같이 수신받는 port인 local port, 전달하는 port인 remote port를 멤버 변수로 가지고 있어 메시지 수신 및 발송 까지 처리하는 class 임

local port는 MessagePortManager를 통해서 생성하고 remote port는 listener에서 connect가 되었을때 전달 받음.

result
SampleMessagePort::Construct(const String& localPortName)
{
result r = E_SUCCESS;

__pLocalMessagePort = MessagePortManager::RequestLocalMessagePort(localPortName);
__pLocalMessagePort->AddMessagePortListener(*this);
...
return r;
}

void
SampleMessagePort::OnMessageReceivedN(RemoteMessagePort* pRemoteMessagePort, IMap* pMessage)
{
String *pData = static_cast<String *>(pMessage->GetValue(String(L"UiApp")));

AppLog("SampleServiceApp : Received data : %ls", pData->GetPointer());

HashMap *pMap = new HashMap(SingleObjectDeleter);
pMap->Construct();

if (pData->CompareTo(L"connect") == 0)
{
__pRemoteMessagePort = pRemoteMessagePort;
pMap->Add(new String(L"ServiceApp"), new String(L"ready"));
}
...
}


result
SampleMessagePort::SendMessage(const IMap* pMessage)
{
...
r = __pRemoteMessagePort->SendMessage(__pLocalMessagePort, pMessage);
...
}


[SampleTimer]

Timer event 수신을 위해서 Tizen::Base::Runtime::ITimerEventListener 를 상속 받음.
Timer가 expire 될 경우 SampleServiceApp으로 TIMER_EXPIRED event를 전달함.

void
SampleTimer::OnTimerExpired(Timer& timer)
{
AppLog("SampleServiceApp : __pTimer is expired.");

if (__isRunning)
{
App* pApp = App::GetInstance();
if (pApp != null)
{
ArrayList messageList;
messageList.Construct();

pApp->SendUserEvent(TIMER_EXPIRED, &messageList);

messageList.RemoveAll(true);
}

if (__isRepeatable)
{
timer.Start(TIMER_INTERVAL_RUN);
}
}
else
{
timer.Start(TIMER_INTERVAL_READY);
}
}


-----------------------------------------------------------------------------------------------------

[MultiProcUiApp]

이것 저것 다 빼고 발로 그린 class diagram


[SampleUiApp]

SampleUiApp에서는 GUI 구성에 필요한 frame과 service와 통신을 위해 사용하는 SampleServiceProxy를 생성한다.

AppId(String일 뿐이다.)를 생성한뒤 SampleServiceProxy 생성 후 초기화 때 인자로 넘겨 준다.

bool
SampleUiApp::OnAppInitialized(void)
{
...
String serviceName(L".MultiProcServiceApp");
String repAppId(15);

repAppId = L"qwertzxcvb";

AppId serviceId(repAppId+serviceName);

AppLog("SampleUiApp : Service Id is %ls", serviceId.GetPointer());

// Initialize ServiceProxy.
result r = E_SUCCESS;
__pService = new (std::nothrow) SampleServiceProxy();
r = __pService->Construct(serviceId, REMOTE_PORT_NAME);
if (IsFailed(r))
{
AppLog("SampleUiApp : [%s] SeviceProxy creation is failed.", GetErrorMessage(r));
__pForm->SendUserEvent(STATE_FAIL, null);
}
else
{
__isReady = true;
}

return true;
}


Service로부터 메시지를 수신 받거나 전송하기 위해
Tizen::Base::Runtime::IEventListener를 구현하였음.
requestId에 따라서 명령을 처리하며 위에서 초기화 때 생성한 SampleServiceProxy를 사용하여
메시지 전송


void
SampleUiApp::OnUserEventReceivedN(RequestId requestId, IList* pArgs)
{
AppLog("SampleUiApp : OnUserEventReceivedN is called. requestId is %d", requestId);

result r = E_SUCCESS;

switch (requestId)
{
case STATE_CONNECT_REQUEST :
if (__isReady)
{
HashMap *pMap = new HashMap(SingleObjectDeleter);
pMap->Construct();
pMap->Add(new String(L"UiApp"), new String(L"connect"));

r = __pService->SendMessage(pMap);

delete pMap;

TryReturnVoid(!IsFailed(r), "SampleUiApp : [%s] MessagePort Operation is Failed", GetErrorMessage(r));
}
break;
...
}


[SampleServiceProxy]

Tizen::Io::IMessagePortListener 를 상속 받아 port를 생성, 관리하는 class.

초반에는 Service가 동작하는지를 AppManager를 통해서 확인하고 없다면 Service를 실행한다.
그리고 Service에서는 local port를 반들고 remote port는 connect 때 만들어 졌는 것에 반해
여기서는 local port와 remote port를 명시적으로 생성한다.


result
SampleServiceProxy::Construct(const AppId& appId, const String& remotePortName)
{
...
AppManager* pAppManager = AppManager::GetInstance();
...
for (int i=0; i < 5; ++i)
{
if (pAppManager->IsRunning(__appId))
{
AppLog("SampleUiApp : Service is ready !!!");
break;
}
else
{
AppLog("SampleUiApp : Service is not ready !!! try to launch !!! ");
r = pAppManager->LaunchApplication(__appId, null);
TryReturn(!IsFailed(r), r, "SampleUiApp : [%s]", GetErrorMessage(r));
Thread::Sleep(CHECK_INTERVAL);
}
}

...
__pLocalMessagePort = MessagePortManager::RequestLocalMessagePort(LOCAL_MESSAGE_PORT_NAME);
__pLocalMessagePort->AddMessagePortListener(*this);

__pRemoteMessagePort = MessagePortManager::RequestRemoteMessagePort(appId, remotePortName);

...
}


Form이랑 Frame은 일반적이라 생략...

Tizen BasicApp sample

아래 내용은 Tizen 2.2 기반이라 상위 버전에서는 별 의미 없는 내용입니다.
--------------------------------------------------------------------------- : https://developer.tizen.org/dev-guide/2.2.1/org.tizen.native.appprogramming/html/sample_descriptions/basicapp.htm

https://developer.tizen.org/dev-guide/2.2.1/org.tizen.native.appprogramming/html/tutorials/ui_tutorial/task_basicapp_panels.htm

Tizen sample 중 가장 기본 앱인 basic app을 대충 훑어봄...
간단하게 Tab header를 통해 Button, Orientation, Image panel에 있는
 button, image, orientation 변경을 확인할 수 있음.


Button  Image

이것저것 다 생략하고 발로 그린 class diagram.


[BasicAppEntry.cpp]

Tizen에서는 memory에 필요 library들과 application executable을 load하고
application executable의 OspMain() 함수를 호출하여 실행.
OspMain()함수에서는 BasicApp의 instance 생성을 함

int
OspMain(int argc, char* pArgv[])
{
...
result r = Tizen::App::Application::Execute(BasicApp::CreateInstance, &args);
...
}


[BasicApp.cpp]

BasicApp에서는 app을 system에 등록하고 app UI 표시를 위해 BasicAppFram을 생성하여 등록함.
BasicApp은 Application(UiApp)을 상속 받고 Back key 처리를 위해서 IScreenEventListener를 구현함.


Tizen::App::Application(UiApp), Tizen::App::ServiceApp
: https://developer.tizen.org/dev-guide/2.2.1/org.tizen.native.appprogramming/html/guide/app/service_app_fundamentals.htm
문서에서 나와 있듯이 일반적인 application은 Application을 상속받아 구현하면 되며
UI를 가지지 않고 background로 동작하는 app을 ServiceApp을 상속받아서 만들 수 있음.


AppRegistry
: https://developer.tizen.org/dev-guide/2.2.1/org.tizen.native.apireference/classTizen_1_1App_1_1AppRegistry.html
: 설명에서는 app의 preference들을 저장하고 사용하기 위해 사용하는 class라고 하는데 아무래도 system에서 app 관리를 위해서도 사용되는 class일 것 같다... (tizen 소스를 보면 알겠지만...귀찮음)


bool
BasicApp::OnAppInitializing(AppRegistry& appRegistry)
{
int lastPanelId = 0;
String lastPanelName(L"");
String panelIDkey(L"AppLastPanelId");
String panelNamekey(L"AppLastPanelName");

result r = appRegistry.Get(panelIDkey, lastPanelId);
if (r == E_KEY_NOT_FOUND)
{
lastPanelId = 0;
appRegistry.Add(panelIDkey, lastPanelId);
}

r = appRegistry.Get(panelNamekey, lastPanelName);
if (r == E_KEY_NOT_FOUND)
{
appRegistry.Add(panelNamekey, L"Panel1");
}

// Create a Frame
BasicAppFrame* pBasicAppFrame = new (std::nothrow) BasicAppFrame();
pBasicAppFrame->Initialize(lastPanelId);
pBasicAppFrame->SetName(L"BasicApp");
AddFrame(*pBasicAppFrame);

return true;
}


[BasicAppFrame]

Tizen GUI 구성에 기본이 되는 Frame class을 상속 받은 class로 실제 App에서 사용될 MainForm을 생성&등록하는 역할을 한다.



Tizen User Interface
: https://developer.tizen.org/dev-guide/2.2.0/org.tizen.native.appprogramming/html/guide/ui/ui_namespace.htm


result
BasicAppFrame::OnInitializing(void)
{
// Create a form
MainForm* pMainForm = new (std::nothrow) MainForm();
result r = pMainForm->Initialize(__panelId);

// Add the form to the frame
AddControl(pMainForm);

// Set the current form
SetCurrentForm(pMainForm);

// Draw the form
pMainForm->Invalidate(true);

return r;
}


[MainForm]

BasicApp의 기본 GUI를 구성하는 class
기본으로 Tizen::Ui::Controls::Form을 상속 받고 Action event handling을 위해 IActionEventListener와
Back key 처리를 위해 IFormBackEventListener를 구현 한다.


class MainForm 
: public Tizen::Ui::Controls::Form
, public Tizen::Ui::IActionEventListener
, public Tizen::Ui::Controls::IFormBackEventListener
{
...

private:

Tizen::Ui::Controls::Panel* __pPanel[3];
Tizen::Ui::Controls::Button* __pButtonOrientation;
Tizen::Ui::Orientation __status;

int __panelId;
};


초기화 시점에서 GUI를 구성하는 위쪽 Tab들, ButtonPannel을 구성하고
tab들의 상태를 초기화 하기 위한 코드가 있음.
이전 실행 종료 시점에 저장해 둔 tab 위치(__panelId)에 따라서 보여지게 하기 위한 코드

좀 다른 얘기이지만 SceneManager를 사용하지 않지만 SceneManager에는 FormFactory와 PanelFactory를 등록하여 id를 사용해서 생성하게 하는 부분이 있음.. 좀 이대로 해보려고 했으나 Form 생성은 되지만 Panel 생성은 안되는 것 같다.. 나중에 좀 더 봐야 할듯.
https://developer.tizen.org/dev-guide/2.2.1/org.tizen.native.appprogramming/html/tutorials/ui_tutorial/registering_scenes.htm

result
MainForm::OnInitializing(void)
{
result r = E_SUCCESS;

Rectangle clientRect = GetClientAreaBounds();
Rectangle rect(0, 0, clientRect.width, clientRect.height);

// Create header
Header* pHeader = GetHeader();
if (pHeader != null)
{
pHeader->SetStyle(HEADER_STYLE_TAB);

HeaderItem headerItem1;
headerItem1.Construct(ID_HEADER_ITEM1);
headerItem1.SetText(L"Button");
pHeader->AddItem(headerItem1);

...

pHeader->AddActionEventListener(*this);
}

SetFormBackEventListener(this);

// Create the Button panel
ButtonPanel* pButtonPanel = new (std::nothrow) ButtonPanel();
pButtonPanel->Initialize(rect);
AddControl(pButtonPanel);
__pPanel[0] = pButtonPanel;

// Orientation panel was created with UI Builder,
// so only its button events must be defined here
__pPanel[1] = static_cast<Panel *>(GetControl(IDC_ORIENTATIONPANEL));
if (__pPanel[1] != null)
{
__pButtonOrientation = static_cast<Button *>(GetControl(IDC_BUTTON_ORIENTATION, true));

if (__pButtonOrientation != null)
{
__pButtonOrientation->SetActionId(ID_ORIENTATION);
__pButtonOrientation->AddActionEventListener(*this);
}
}

// Set the current panel as selected in the header and display it on the form
if (pHeader)
{
pHeader->SetItemSelected(__panelId);

if(__panelId == 2)
{
if (__pPanel[2] == null)
{
CreateImagePanel();
}
SetOrientation(ORIENTATION_AUTOMATIC);
}

if (__pPanel[0] != null)
{
__pPanel[0]->SetShowState(false);
}
if (__pPanel[1] != null)
{
__pPanel[1]->SetShowState(false);
}

__pPanel[__panelId]->SetShowState(true);
}

Invalidate(true);
return r;
}


Action event 처리를 위한 IActionEventListener 구현 부
Tab 처리를 위한 코드는 좀 노가다 같다..
사실 tab widget을 system에서 제공해 주면 이런 코드는 안봐도 될것 같은데..


void
MainForm::OnActionPerformed(const Tizen::Ui::Control& source, int actionId)
{
switch(actionId)
{
case ID_HEADER_ITEM1:
{
if (__pPanel[0] != null)
{
__pPanel[0]->SetShowState(true);
}
if (__pPanel[1] != null)
{
__pPanel[1]->SetShowState(false);
}
if (__pPanel[2] != null)
{
__pPanel[2]->SetShowState(false);
}
SetOrientation(ORIENTATION_PORTRAIT);
}
break;

case ID_HEADER_ITEM2:
...
case ID_HEADER_ITEM3:
...
case ID_ORIENTATION:
{
if (__pPanel[1]->GetShowState())
{
OrientationStatus status = GetOrientationStatus();
if (status == ORIENTATION_STATUS_PORTRAIT)
{
__status = ORIENTATION_LANDSCAPE;
}
else if (status == ORIENTATION_STATUS_LANDSCAPE)
{
__status = ORIENTATION_PORTRAIT;
}
SetOrientation(__status);
}
}
break;

default:
break;
}

Invalidate(true);
}

종료 시점에는 AppRegistry를 통해 선택된 tab index를 저장해 둔다..
근데 왜 panelIDkey와 panelNamekey 두개로 저장하는 걸까?

result
MainForm::OnTerminating(void)
{
AppRegistry *appRegistry = Application::GetInstance()->GetAppRegistry();
String panelIDkey(L"AppLastPanelId");
String panelNamekey(L"AppLastPanelName");

Header* pHeader = GetHeader();
result r = appRegistry->Set(panelIDkey,pHeader->GetSelectedItemIndex());
if (IsFailed(r))
{
//error condition
}

String panel;
panel.Format(50, L"Panel%d", pHeader->GetSelectedItemIndex());
r = appRegistry->Set(panelNamekey, panel);
if (IsFailed(r))
{
//error condition
}

r = appRegistry->Save();
if (IsFailed(r))
{
//failed to save data to registry.
}

return r;
}


그리고 아래 Tutorial 괜찮음
: http://tizenschools.com/?p=557


2014년 4월 15일 화요일

Ubuntu 글꼴 설정

Ubuntu에서 글꼴 설정 하는 법 정리..
아무리 Linux가 개인 데탑환경으로 쓰기에 좋다고 하지만 그래도 customizing이 필수 인듯 하다....


[글꼴 등록 방법]

다운로드 받은 글꼴을 /usr/share/fonts에 옮기고 font cache update


km@km:~/다운로드$ ls
naverdic.tgz  naverdic.ttf

km@km:~/다운로드$ sudo mkdir /usr/share/fonts/naverdic
km@km:~/다운로드$ sudo cp naverdic.ttf /usr/share/fonts/naverdic
km@km:~/다운로드$ sudo chmod 755 /usr/share/fonts/naverdic/naverdic.ttf
km@km:~/다운로드$ fc-cache -r


[글꼴 변경 방법]

여러 방법이 있겠지만 myunity 툴로 쉽게 바꿀 수 있음.




[Ubuntu에서 쓸만한 글꼴들]

개인적으로는 나눔고딕코딩 글꼴이 코딩시에는 좋고 shell에서는 ubuntu 기본글꼴이 익숙하다..

2014년 4월 9일 수요일

Ubuntu Chrome에 SSL 인증서 설치 방법(링크)

나중에 참고하고자 등록...

잘은 모르겠으나
Ubuntu chrome에서 방화벽을 경유하여 외부 사이트로 HTTPS 연결 시
chrome에서 SSL error를 보여주며 아래와 같이 연결을 중단 시킴.
아마도 방화벽에서 전달하는 인증서가 ubuntu에 등록되지 않아서 발생하는 것으로 판단하고 방법을 찾아서 해결함..

Chrome SSL error
http://www.pocketables.com/2014/02/override-chromes-new-ssl-error-connect-real.html

아래와 같이 인증서 설치로 문제 해결..가능..

[인증서 설치 방법]

Adding SSL certificates to Google Chrome Linux (Ubuntu)
Peter van der Does - February 3, 2010
: http://blog.avirtualhome.com/adding-ssl-certificates-to-google-chrome-linux-ubuntu/

Google Chrome in Linux doesn’t have a SSL certificate manager, it relies on the NSS Shared DB. In order to add SSL certificates to the database you will have to use the command line. I will explain how you can add the CAcert certificates and a very easy way to add self-signed certificates.
You will have to install some tools first:
sudo apt-get install libnss3-tools
sudo apt-get install curl

Adding CAcert certificates

Lets start with adding the CAcert certificates, this will help with a lot of sites
curl -k -o "cacert-root.crt"   "http://www.cacert.org/certs/root.crt"
curl -k -o "cacert-class3.crt" "http://www.cacert.org/certs/class3.crt"
certutil -d sql:$HOME/.pki/nssdb -A -t TC -n "CAcert.org" -i cacert-root.crt 
certutil -d sql:$HOME/.pki/nssdb -A -t TC -n "CAcert.org Class 3" -i cacert-class3.crt

Adding self-signed certficates

There are certain sites that use self-signed certificates and you need to add them individually to the database and there are two options to do this:
위 방법 수행이후 방화벽에서 전달하는 인증서를 설치하려면
마지막에 있는 command line 두줄 처럼 certutil을 사용하여 add 하면 됨.

sql:$HOME/.pki/nssdb 부분에서 절대 경로를 /home/xxxx 를 사용해보고
그래도 S E C_ERROR_BAD_DATABASE, bad database 에러 발생 하면
아래 처럼 DB init

http://serverfault.com/questions/414578/certutil-function-failed-security-library-bad-database


5down voteaccepted
If it is new system, your certificate database might not be initialized. To fix this, perform:
mkdir -p $HOME/.pki/nssdb
certutil -d $HOME/.pki/nssdb -N
share|improve this answer

2014년 4월 1일 화요일

프로그래머 그 다음 이야기

프로그래머 그 다음 이야기
임백준, 오병곤, 이춘식, 이주연, 박재성, 신재용 | 로드북

http://www.yes24.com/24/goods/5354328?scode=032&OzSrank=1



회사 도서관에서 책을 둘러보다 보게 눈에 띄어 된 책이다.
이 출판사 책 중 프로그래머 관련 책을 읽다가 이 책이 언급된 기억과
저자로 나오는 분들이 꽤나 유명하신 분들이시라 궁금해서 선택함.

책 뒷 표지에 나와 있듯이 "프로그래머 평균 정년 35세 당신은 무엇을 하고 계십니까?"
프로그래머라면 누구나 30-40대 사이에 고민을 할 때 이고 나 또한 마찬가지인 상황에서 갈길이 보이지 않고 되돌아본 나의 경력이 보잘 것 없는지라 남다른 의미로 다가온다.

글 중 이춘식님의 글이 와 닫는 내용이 많고 나 같은 경우 신앙을 가지고 있는지라 더욱 좋았음.
그외 재미 있는 것은 프로그래머로서 실무에서 멀어지고 나이가 들어 관리 보직을 맡게 될 경우 일반적인 목표가 자격증(기술사, 퍼실리테이터 등) 인가 라는 생각이 든다.. 저자중 3분이 기술사 합격 하신 지라..

암튼... 일단 보면서 나에게 중요하다고 생각한 것들을 대충 정리함.


임백준 - 시니어 프로그래머, 행복한 프로그래밍

나는 후배 프로그래머들에게 생존과 기술에 대해서 고민하기 이전에 우선 "프로그래밍을 진심으로 사랑할 것"을 당부하고 싶다. 지금까지 나를 여기까지 밀어온 것은 다른 무엇이 아니라 바로 프로그래밍을 통해서 얻게 되는 행복과 성취감이었기 때문이다.
=> 동감하며 나에게 다시 되묻는다.. 입사 초기의 그 열정은 어디로 갔는지?

새로운 장소에서 만나게 된 프로그래머 중에는 솔직히 말해서 내가 따라가기 어려울 정도로 높은 실력을 갖춘 프로그래머가 몇 명 있었다. 그것은 나에게 건강한 의미에서의 도전이었지만, ... 그런 깊은 충격을 나에게 전해주기도 했다.
=> 사실 나도 동일 조직에서 한가지 업무를 오랫동안 해왔던 터라 우물안 개구리 같았다.. 지금에서야 밖으로 기어나오려고 하지만 너무 늦은 듯한 생각에 후회 막급이다.


오병곤 - 제2의 인생, 컨설턴트의 길

개발자에게 요구되는 역량이 논리력과 창의적 사고라면 PM은 종합적인 관리 능력과 함께 리더십 역량이 요구되는 직무다. 그래서 어느 정도 연수가 차면 누구나 PM이 되는 것은 위험하다.
: 그래 PM는 개나 소나 되는 게 아니다..


이춘식 - 데이터아키텍트의 마스터로 살련다.

보고서 작성 방법
: 일반적인 처음 도입으로 시작해서 '승'-'전' 에서 peak를 두는 것 보다.
처음 문제점을 강렬하게 제시하여 '기', '승'-'전' 에서 peak를 두고 '결'에서 살짝 impact를 주는 것이 보고서를 보는 사람들의 흥미를 끌게 된다.

수확체증의 법칙 (Increasing Returns of Scale)
: 수확체증의 법칙이란 투입된 생산요소가 늘어나면 늘어날수록 산출량이 기하급수적으로 증가하는 현상을 말한다.
사람이기 때문에 잠재력이 있고 또한 다양한 경험을 통해 수집된 정보가 집약되어 일정한 수준을 넘어서면 이전에 증가하였던 상태보다 엄청난 속도로 그 성과 등이 나타난다는 것이다.
=> 학습에 대한 메모를 보여주며 이를 설명하고 있고 동감하는 부분이다. 공부나 경험이라는 것을 통해서 효과가 바로 나오는 것이 아니라 어느정도 밑받침이 되어야 효과가 나오는 것이라..

능력있는 리더
: 비전이 있으며 긍적적인 사고로 즐겁게 일하면서 강한 실행력을 발휘할 수 있으면 능력있는 리더

1. Goal - 일의 목표는 분명한가?
2. Value & Performance - 일에 가치가 부여되고 있고 일의 성과를 내고 있는가?
3. Communication - 다양한 성격의 사람과 대화를 잘할 수 있는가? 협상의 기술이 있는가?
4. Presentation Skill - 프리젠테이션 스킬이 우수한가?
5. Document Skill - 기획 문서를 쉽고 빠르게 작성할 수 있는가?
6. Time Management - 시간 관리를 하고 있는가?
7. Insight - 변화와 개선점을 파악할 수 있고 제시할 수 있는가?


진정한 일의 측정은 성과와 품질로 가능하다. 그 사람이 얼마나 많은 일을 했느냐에 초점을 맞추지 말고 그 사람이 무슨 일을 성취했느냐? 그리고 그 일이 가치 있는지 품질은 우수한지를 보아야 할 것이다.


대화의 기술도 여러 가지 전문적인 방법이 많이 있지만, 상대방의 이야기를 경청하고 핵심을 파악한 다음 다시 나의 의견을 논리적으로 이야기하는 것이 핵심이다.
... 효율적인 대화를 이끌기 위해서는 상황에 적합한 적절한 유머 감각이 아주 중요한 것 같다. 보통 회사에서 여러 사람이 대화하는 자리는 주제에 대해서만 집중 하다 보니 사람들의 표정도 쉽게 굳어지고 사무적인 대화만 오가면서 가벼운 내용도 심각하게 이야기되고 따라서 의사결정도 어려워지며 시간이 많이 소요되곤한다.
=> 나도 배워야할 것 중 하나.... 하지만 유머를 책으로 배울 수는 없으니..

직장 생활의 라이프 사이클
1기 - 시작/성장기 - Low Performer, Low Cost - 0~4년차, 사원
2기 - 발전기          - High Performer, Low Cost - 5-10년차, 대리, 과장
                                  . 인건비 대비 효과 좋은 시기
3기 - 유지기          - High Performer, High Cost - 11-16년차, 과장, 차장
                                  . 일은 잘하나 몸값이 올라가 서서히 기피함.
4기 - 준비기          - Middle performer, High Cost - 17년차 ~ 퇴임
                                  . 일에 대한 관록은 우수하나 노골적인 기피 대상