'분류 전체보기'에 해당되는 글 81건

  1. 2013.04.29 #20130429-01 :: PyDev :: "Python stdlib source files not found"
  2. 2013.04.12 새로운 시작을 위한 반성, 벤처는 도전이 아닌 욕심이었다.
  3. 2013.03.03 [Platinum UPnP] 101 :: (6) Device에서 Event 받기
  4. 2013.03.02 [Platinum UPnP] 101 :: (5) Device 연결하고 Action 호출하기
  5. 2013.03.02 [Platinum UPnP] 101 :: (4) 간단한 Control-Point 만들기
  6. 2013.03.02 옛날 아고라에 썼다가 메인에 뜨는 바람에 식겁해서 삭제한 글
  7. 2013.03.01 [Platinum UPnP] 101 :: (3) Service에 Action 추가하기
  8. 2013.03.01 [Platinum UPnP] 101 :: (2) Device에 Service 올리기 + StateVariable & Event
  9. 2013.03.01 [Platinum UPnP] 101 :: (1) 간단한 Device 만들기
  10. 2013.03.01 [Platinum UPnP] 101 :: (0) 소개

#20130429-01 :: PyDev :: "Python stdlib source files not found"

오늘 회사의 맥북프로(Mac OS X Moutain Lion)에서 Django 작업 환경을 꾸미기 위하여 Django와 Aptana를 설치하였습니다. 그런데 프로젝트를 생성하려는 순간 적절한 Interpreter를 찾을 수 없다고 하더군요. 그래서 "환경설정 → PyDev → Interpreter-Python"으로 가서 "Auto Config"를 돌렸습니다. 결과는 "Python stdlib source files not found"라는 경고가 나면서 Python을 인식하지 않습니다.


해결 방법은 이곳에서 최신 "Command line tools for Xcode"를 다운로드 받아 설치하시면 됩니다. 그리고 다시 "Auto Config"를 하면 정상적으로 인식이 됩니다.

새로운 시작을 위한 반성, 벤처는 도전이 아닌 욕심이었다.

벤처라는 이름의 패착

만용이라 적혀 있던 것을 도전이라 읽은 어리석음

어른들의 충고를 무시했던 오만함

30여년 간 갈고닦은 내 자신의 객관과 직감에 대한 배신

욕심에 눈이 먼 폭주

인정으로 공정을 눈감은 안일함


실패를 타인에게 시인하는 것은 용기이지만,

다수를 향해 SNS에 감정을 하소연하는 것은 

길바닥에 똥덩어리를 싸지르는 것과 

다름이 없다고 생각한다.

그래서 요즘에는 타인과의 소통 이외에는 

SNS를 최대한 하지 않으려고 노력한다.


하지만, 때로는 각오를 다지기 위해

똑간은 과오를 다시 저지르지 않기 위해

타인에게 치부와 부끄러움을 고백해야 할 때도 있다.


벤처에서의 생활을 중단하고 나온지 8개월을 채워간다.

인생에 있어서 처음으로 후회를 가슴에 새겼고

유래없이 건강이 악화되었으며

내 가족의 생활까지 위기에 몰아넣은

그 생활을 마감하면,

이전의 생활로 돌아갈 수는 없어도

적어도 악화되던 모든 것들이 중단될 줄 알았다.

하지만, 인생 그렇게 호락호락 한 것이 아니었다.


사실, 아직도 억울하다고 하소연하고 싶고

저놈이 나빴다고 그놈이 잘못했다고 

욕도 하고 싶기도 하다.

그러나 그 전에 사실 다 내 잘못이었음을 

먼저 시인해야 한다.


30년간의 내 삶에는 원칙이 있었다.

첫째, 도전에 경제적 리스크를 동반하지 않을 것...

적자를 볼 수는 있지만

적자가 삶을 위협하지 않는 수준으로 제한했었다.

둘째, 타인에게 의지하지 말 것...

가족이나 친구에게 도움을 요청할 수는 있다.

단, 그들의 삶에 피해를 주지 않아야 했다.

셋째, 직감을 신뢰할 것...

인간의 무의식은 이성과는 비교가 안되게 똑똑하다.

다만, 똑똑해지려면 많은 경험과 학습이 필요하다.

나는 내 자신의 직감을 전적으로 신뢰 할만큼 

열심히 살아왔다고 자부한다.

그래서 내 직감을 신뢰하고

직감은 나를 배신한 적이 없었다.

넷째, 다수의 의견을 경청할 것...

최소 셋 이상이 같은 목소리를 낸다면,

아무리 듣기 싫은 말도 신뢰해야 한다.

다섯째, 경험을 우대할 것...

내게 있어서 경험은 논리에 앞선다.

대부분의 논리는 경험에서 나왔기 때문이고

사람은 논리적으로 행동하는 동물이 아니기 때문이다.


그러나 나는 벤처라는 무모함에 있어서

앞의 중요한 다섯까지 원칙 중 단 하나도 지키지 않았다.

첫째, 어마어마한 수준의 연봉하락을 허락했다.

아울러 복지와 안정까지 합산하면 연간 억대 수준이었다.

둘째, 어머니의 노후자금을 끌어들였다.

평생을 두고 반성해야 할 일이다.

셋째, 직감이 주는 경고를 

벤처에서 얻을 수익에 대한 기대로 덮어버렸다.

전형적으로 돈에 눈이 먼 얼간이 짓이다.

넷째, 주위 어른들의 만류에 귀를 막았다.

다들 내가 존경하고 사회경제적으로 성공하신 분들이다.

그러나 나는 그분들이 21세기 벤처 시스템을

이해하지 못하고 계신다는 오만함을 갖고 있었다.

그러나 그분들은 벤처 이전에 나보다 인간을 더 잘 이해하고 계셨다.

다섯째, 위의 충고를 하신 분 중에는

삼성에서 부장까지 지내신 후

퇴직하시고 창업을 하신 사촌형님도 계셨다.

나는 그 분의 직장인이었다는 과정을 무시하고

창업 후 수백명의 직원을 거느린 성공만 보았다.

경험을 무시하고 결과만 존경했다.


그리고 결과는 처참했음을 고백한다.

2012년 벤처에서 받은 쥐꼬리만한 수입의 25%는 병원비로 지출했고,

아직도 대학병원에서 정기적으로 검사를 받는다.

어머니의 노후자금도 온전히 보전하지 못했으며,

그마저도 아직 돌려드릴 형편도 되지 않는다.

내 개인자산은 대폭 하락했고 현금 유동성은 0에 수렴한다.

돈이 없어 집안 기자재 팔아치우는 건

70~80년대 드라마나 소설에나 나오는 건 줄 알았는데

내가 벤처를 다니며 했던 짓이다.


내가 벤처에서 기대한 것은

도전과 모험과 존중과 이해와 협력이었다.

그러나 신기술 도입에 격렬한 반대와 싸워야 했고,

개발자의 호소는 무시당하기 일쑤고,

영업에게 부응하지 못한다며 비교와 폄하당하고,

디자이너가 부응하지 못한 책임도 개발자가 떠안아야 했다.

항상 술먹고 비웃던 우리나라 IT업계의 부조리가

그곳에서 나에게 그대로 쏟아지고 있었다.

혹자 내게 말했다.

그래서 벤처에서 네 맘대로 못한게 뭐가 있냐고...

유감스럽게도 삼성에서도 그 정도는 얼마든지 내 마음대로 했었다.

내가 원했던 것은 내 마음대로 하는게 아니라

내가 할 수 없는 것을 다함께 하는 거였다.

실리콘벨리의 문화를 따라잡고 

한국 기업문화를 뛰어 넘는 것이었다.

그 회사가 나쁜 것이 아니다.

그냥 평범한 거다.

전에 산업기능요원으로 다니던 회사들과 똑같이...


인생은 아이러니 하다.


LG전자에 입사한건 지인의 도움이 매우 컸다.

그는 매우 열정적이고 도전적이지만 

조직에서 부드러움이 부족한 부분이 있었다.

하지만, 그 때문에 실리콘벨리 문화를 향해 

동료들과 함께 빠르게 전진하고 있었다.

방해와 타협할 시간 없이 뚫고 전진해 나갔다.

그런 그의 추천이었기에 

스펙상 부족한 내가 연구소에 이직할 수 있었다.

내가 벤처에서 기대했던 것들이 지금 현재 그곳에 있다.

동료들은 모두 친구처럼 다정하고 스스럼 없고 유쾌하지만,

나이를 떠나 존대말을 쓰며 서로 존중하고 배려한다.

각자가 새로운 기술을 배우고 도입하는데 매우 적극적이며

타인의 노력을 나누고 기꺼이 동참하기를 꺼리지 않는다.

그래서 내가 수년간 얻고자 했던 것을 몇 개월만에 다함께 얻을 수 있었다.


절망으로 떨어지기 직전 행운이 왔다고 할까?

삼성에 없었기에 벤처에서 꿈꾸었던 많은 것들이

실제로는 LG 안에서 존재하고 있었다.

(LG가 삼성보다 좋다기 보다는 

사업부와 연구소의 차이가 크다고 생각한다.)


생활은 안정되기 시작했고

건강이 더이상 악화되는 것도 멈추었으며

재무 시스템도 서서히 제자리를 찾아가도록 

다시 준비를 할 수 있는 여건이 마련되고 있다.


아마 이 어마어마한 경제적 실패는

LG의 연봉과 복지만으로는 절대 복구되지 않을 것이다.

하지만, 당분간 안정적인 인생, 시간적인 여유,

프로그래머로써 발전하는 기회는 보장 받았다.

좋은 동료들도 얻었다.

도전할 목표들도 생겼다.


삼성에 있을 때까지만 해도 앞의 다섯 가지 원칙이 

더 큰 성장을 제한하는 것이 아닌가 의구심이 들기도 했었다.

더 큰 리스크를 통해 더 큰 무엇인가를 얻을 수 있는 기회를

내 스스로 제한하는 것이 아닌가 하는 두려움이었다.

그러나 나는 틀리지 않았다.


타인을 신뢰하기에 앞서 

내 자신을 신뢰해야 한다는

절실히 깨달았다.


나는 그래서 오늘 고백할 수 있다.

내게 있어 벤처라는 것은

열정적인 도전이 아니라

욕심에 눈이 먼 도박이었다는 것을...


그리고 다짐한다.

이제 앞으로 무엇을 하던지 

나는 내 인생의 원칙을 소중히 할 것임을...


마지막으로 내 소중한 사람들이 벤처에 도전하겠다고 한다면,

단 한가지만 명심하라고 말해주고 싶다.

"사람을 보라."

모든 성공과 실패의 핵심은 사람이 쥐고 있다.

절친한 친구라고 해서

좋은 사업 파트너나 보스 또는 직원이 될 수 없다.

그 사람이 그 장소와 역할에 맡는 사람인지 보라.

친하고 안친한 것은 절대 중요하게 고려하면 안되는 사항이다.


오늘 이 글을 통해 실패에 대한 공개적인 시인과 반성을 함으로써

모든 원망과 부끄러움을 털어버리려 한다.

이미 내게는 새로운 도전들이 시작되어 있기 때문에...

[Platinum UPnP] 101 :: (6) Device에서 Event 받기




파일: tutorial06.cpp


이제 Platinum UPnP 101 Tutorial도 막바지에 왔습니다. 이번에는 끝으로 Control-Point에서 Device의 Event를 받는 기능을 구현하도록 하겠습니다.

    하지만, 기능 구현에 앞서 약간의 리팩토링을 하고 진행하도록 하겠습니다.

    이전 단계에서 우리는 Action을 호출하기 위하여 TutorialListener 객체에서 Device 정보를 받아 PLT_CtrlPoint 객체에서 이 정보를 이용해 Action을 호출하였습니다. Event에서도 이와 비슷한 형태가 필요합니다. 그래서 이렇게 서로 다른 객체사이에 정보를 주고 받기 위한 복잡한 인터페이스를 줄이기 위헤, TutorialListener 클래스가 PLT_CtrlPoint 클래스와 PLT_CtrlPointListener 클래스를 다중 상속 받도록 변경하겠습니다. 그리고 TutorialListener 클래스의 이름을 TutorialCP로 변경합니다. 변경된 코드는 아래와 같습니다.


class TutorialCP : public PLT_CtrlPoint, public PLT_CtrlPointListener
{
public:
	TutorialCP(void)
	{
		AddListener(this);
	}
	~TutorialCP(void)
	{
	}

	// ...

	void Print(const char* str)
	{
		if (!device_.IsNull()) {
			PLT_Service* service;
			device_->FindServiceById("urn:upnp-org:serviceId:Tutorial.001", service);
			PLT_ActionDesc* desc = service->FindActionDesc("Print");
			PLT_ActionReference action(new PLT_Action(*desc));
			action->SetArgumentValue("String", str);
			InvokeAction(action);
		}
	}

	// ...
};


    클래스의 형태를 변경하면서 Listener 객체(여기에서는 자기 자신)를 생성자에서 바로 등록하도록 하였습니다. 그리고 device() 함수를 제거하고, main() 함수에서 콘솔의 입력을 받아 "Print" Action을 실행하던 부분을 Print() 함수로 묶었습니다.

    클래스 형태가 변하였으므로 main() 함수에서 TutorialCP(구 TutorialListener) 객체를 사용하는 코드에도 변경이 일어났습니다. 우선 Control-Point를 만들고 등록하는 부분은 아래와 같습니다.


TutorialCP* ctrlpoint = new TutorialCP;
PLT_CtrlPointReference ref(ctrlpoint);
upnp.AddCtrlPoint(ref);


    콘솔의 입력을 받아 "Print" Action을 호출하는 부분은 아래와 같이 변경되었습니다.


ctrlpoint->Print(input.c_str());


    빌드 후 실행하면 이전 단계와 동일하게 동작합니다. 이제 여기에서부터 Event에 대한 구현을 다시 시작합니다.

    Control-Point에서 Event를 받기 위해서는 Device Spy 프로그램에서 "Subscribe to Events"를 했듯이, PLT_CtrlPoint 객체서 Event를 받기 원하는 Service에 대해 Subscribe() 함수를 호출해야 합니다. 이 동작은 OnDeviceAdded() 함수에서 수행합니다. Service는 Action을 호출할 때 Print() 함수에서도 쓰이므로 멤버변수로 저장해 놓습니다. TutorialCP 클래스에서 변경된 코드들은 아래와 같습니다.


class TutorialCP : public PLT_CtrlPoint, public PLT_CtrlPointListener
{
public:
	// ...

	virtual NPT_Result OnDeviceAdded(PLT_DeviceDataReference& device)
	{
		if (device->GetFriendlyName() == "Tutorial Device") {
			printf("[Tutorial Device] added\n");
			device->FindServiceById("urn:upnp-org:serviceId:Tutorial.001", service_);
			Subscribe(service_);
			device_ = device;
		}
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnDeviceRemoved(PLT_DeviceDataReference& device)
	{
		if (device->GetFriendlyName() == "Tutorial Device") {
			printf("[Tutorial Device] removed\n");
			service_ = 0;
			device_ = 0;
		}
		return NPT_SUCCESS;
	}

	// ...

	void Print(const char* str)
	{
		if (!device_.IsNull()) {
			PLT_ActionDesc* desc = service_->FindActionDesc("Print");
			// ...
		}
	}

private:
	// ...
	PLT_Service* service_;
};


    마지막으로 OnEventNotify() 함수에서 Event를 받아서 변경된 "PrintBuffer"의 값을 콘솔에 출력합니다. 코드는 아래와 같습니다.


virtual NPT_Result OnEventNotify(PLT_Service* service, NPT_List<plt_statevariable*>* vars)
{
	const PLT_DeviceData* device = service->GetDevice();
	if (device->GetFriendlyName() == "Tutorial Device") {
		NPT_List<plt_statevariable*>::Iterator itr = vars->GetFirstItem();
		for (bool stop = false ; !stop ; ++itr) {
			const PLT_StateVariable* state_variable = *itr;
			if (state_variable->GetName() == "PrintBuffer") {
				const NPT_String& value = state_variable->GetValue();
				printf("[PrintBuffer] changed: %s\n", value);
				break;
			}
			stop = (itr == vars->GetLastItem());
		}
	}
	return NPT_SUCCESS;
}


    빌드 후 실행시킨 후 Device의 콘솔에서 직접 값을 변경하거나 Control-Point의 Action 호출을 통해 값을 변경하였을 때, Event에 의해 Control-Point의 콘솔에 변경된 값이 출력되는 것을 볼 수 있습니다.


    이것으로 Platinum UPnP를 이용하여 UPnP의 기본 구성 요소인 Device와 Control-Point를 간단하게 구현해 보았습니다. Platinum UPnP는 매우 잘 설계된 강력한 라이브러리 이므로, 라이선스 문제가 있기 때문에 무조건 쓸 수 없겠지만, 사용할 수 있는 조건이 된다면 UPnP/DLNA 서비스를 만드는데 적극적으로 사용해 보기를 권장합니다.

[Platinum UPnP] 101 :: (5) Device 연결하고 Action 호출하기




파일: tutorial05.cpp


이번에는 전에 만들어 두었던 "Tutorial Device"를 찾아내어 "Print" Action을 호출하는 기능을 추가합니다. 호출하는 방법은 Control-Point의 콘솔에 문자열을 입력하면 입력된 문자열을 인자로 하여 Action을 호출하는 것으로 해보겠습니다.

    미리 추가해 두었던 TutorialListener 클래스에는 OnDeviceAdded와 OnDeviceRemoved라는 한 쌍의 콜백 함수가 있습니다. OnDeviceAdded 함수는 Control-Point를 실행 했을 때 Network에 이미 존재하는 UPnP Device들 또는 Control-Point가 실행된 후 Network에 새롭게 등장한 Device들을 알려줍니다. 반면에 OnDeviceRemoved 함수는 Network에서 기존의 Device들이 사라지게 되면 알려줍니다.

    이 두 함수에 "Tutorial Device"를 찾으면 멤버변수로 저장해 두는 기능을 추가해 보겠습니다. 코드는 아래와 같습니다.


class TutorialListener : public PLT_CtrlPointListener
{
public:
	virtual NPT_Result OnDeviceAdded(PLT_DeviceDataReference& device)
	{
		if (device->GetFriendlyName() == "Tutorial Device") {
			printf("[Tutorial Device] added\n");
			device_ = device;
		}
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnDeviceRemoved(PLT_DeviceDataReference& device)
	{
		if (device->GetFriendlyName() == "Tutorial Device") {
			printf("[Tutorial Device] removed\n");
			device_ = 0;
		}
		return NPT_SUCCESS;
	}

	// ...

private:
	PLT_DeviceDataReference device_;
};


    먼저 Device의 정보가 담겨있는 PLT_DeviceData 객체를 저장할 멤버변수를 추가합니다. OnDeviceAdded() 함수에서는 추가된 Device가 "Tutorial Device"인지 확인하고, 맞다면 멤버변수에 저장합니다. OnDeviceRemoved() 함수에서는 사라진 Device가 "Tutorial Device"라면 멤버변수를 NULL 값으로 초기화 합니다.

    이제 콘솔의 입력을 받아 Device의 Action을 호출하는 기능을 구현하려고 합니다. 이에 앞서 콘솔의 입력을 받고 Control-Point의 조작은 main 함수에서 일어나는데 반해 Device의 정보는 TutorialListener 객체가 갖고 있습니다. 따라서 TutorialListener 객체로부터 Device 정보를 얻어오는 함수를 아래와 같이 TutorialListener 클래스에 추가합니다.


PLT_DeviceData* device(void) const
{
	return device_.AsPointer();
}


    마지막으로 main() 함수에 아래와 같이 콘솔에서 입력된 값으로 Action을 호출하는 코드를 작성합니다.


PLT_DeviceData* device = listener.device();
if (device != 0) {
	PLT_Service* service;
	device->FindServiceById("urn:upnp-org:serviceId:Tutorial.001", service);
	PLT_ActionDesc* desc = service->FindActionDesc("Print");
	PLT_ActionReference action(new PLT_Action(*desc));
	action->SetArgumentValue("String", input.c_str());
	ctrlpoint->InvokeAction(action);
}


    지금까지 작성한 코드를 빌드한 후 실행시켜 봅니다. 앞서 만든 "Tutorial Device"가 이미 실행되고 있거나, 지금 만든 Control-Point를 실행시킨 후 실행한다면, 콘솔에 "[Tutorial Device] Added"가 출력되는 것을 볼 수 있습니다. 이제 콘솔에 문자열을 입력하면, Device의 콘솔 창에 Contorol-Point에서 입력한 문자열을 입력 값으로 한 Action이 호출되었다는 정보가 출력될 것입니다.

[Platinum UPnP] 101 :: (4) 간단한 Control-Point 만들기




파일: tutorial04.cpp


앞서 우리는 간단하지만 기본적인 기능을 모두 갖춘 하나의 Device를 만들어 보았습니다. 이번에는 UPnP의 또 다른 구성 요소인 Control-Point를 만들어 보겠습니다.

    이에 앞서 Control-Point에 대해 간략히 설명하자면, Device를 검증하는데 사용했던 Device Spy가 대표적인 Control-Point라고 할 수 있습니다. 즉, Device를 찾고 Device의 정보를 보고 올바른 Action을 날리고 Event를 받는 등의 일을 하는 것이 Control-Point의 역할입니다.

    이제 가장 간단한 Control-Point 프로그램을 구성해 보겠습니다. 코드는 아래와 같습니다.


#include <string>
#include <iostream>
#include "Platinum.h"

int main()
{
	PLT_UPnP upnp;

	PLT_CtrlPointReference ctrlpoint(new PLT_CtrlPoint);
	upnp.AddCtrlPoint(ctrlpoint);
	upnp.Start();

	bool stop = false;
	do {
		std::string input;
		std::cin >> input;
		if (input == "q" || input == "Q") {
			stop = true;
		} else {
			// nothing
		}
	} while(!stop);

	upnp.Stop();
	return 0;
}


    Control-Point의 기본적인 기능을 담당하는 PLT_CtrlPoint 클래스는 PLT_DeviceHost 클래스가 가상 클래스였던 것과는 다르게 독립적으로 객체를 생성할 수 있습니다. 다만, Device에 Service를 올리기 전까지 아무것도 할 수 없었던 것처럼 지금도 할 수 있는 것이 아무것도 없다는 것이 문제 입니다.

    Device의 경우에는 PLT_DeviceHost 클래스를 상속 받아 객체 생성이 가능한 자식 클래스를 구성했습니다. 그러나 Control-Point는 Listener 객체를 등록받아 Listener 객체들에게 콜백을 호출합니다. 따라서 이번에는 PLT_CtrlPoint 클래스를 상속받는 것이 아니라 PLT_CtrlPointListener 클래스라는 순수 가상함수를 상속 받아 PLT_CtrlPoint 객체에게 등록해 줍니다.

    PLT_CtrlPointListener 클래스를 상속 받은 TutorialListener 클래스의 기본 구현 코드는 아래와 같습니다.


class TutorialListener : public PLT_CtrlPointListener
{
public:
	virtual NPT_Result OnDeviceAdded(PLT_DeviceDataReference& device)
	{
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnDeviceRemoved(PLT_DeviceDataReference& device)
	{
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnActionResponse(NPT_Result res, PLT_ActionReference& action, void* userdata)
	{
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnEventNotify(PLT_Service* service, NPT_List<PLT_StateVariable*>* vars)
	{
		return NPT_SUCCESS;
	}
};


    이제 PLT_CtrlPoint 객체에 TutorialListener 객체를 등록을 해줍니다. main() 함수의 코드가 아래와 같이 변경 됩니다.


TutorialListener listener;
PLT_CtrlPointReference ctrlpoint(new PLT_CtrlPoint);
ctrlpoint->AddListener(&listener);
upnp.AddCtrlPoint(ctrlpoint);


    이것으로 Control-Point의 기능을 만들기 위한 기본 코드가 완료되었습니다.

옛날 아고라에 썼다가 메인에 뜨는 바람에 식겁해서 삭제한 글

당분간 비공개 유지할 것!!




상상력이 풍부하시네요. 전 삼성전자 SW 퇴사한 사람입니다. [278]

길벗 (su1****)

주소복사 조회 53015 11.09.17 19:55

 

 

마지막 첨언 입니다.

사실 글 내릴까도 엄청나게 고민 했는데요.

사실 제가 삼성 욕한거도 아니고 해서 원문은 유지하기로 했습니다만,

너무 제자랑 인듯한 것도 장황하고 자꾸 애플VS삼성 하는 것 같아 

마지막으로 제 의도를 요약할까 합니다.


현재 애플은 삼성의 경쟁자 보다는 패스트팔로윙 롤모델 입니다.

경쟁구도는 마케팅 도구일 뿐이고,

삼성의 강점은 제품의 지역화를 통한 맞춤제품 등에 있는 것인데,

소프트웨어 기술력 문제와 애플의 강력함으로 

소니를 제치던 때와 같은 이점을 발휘하지 못하고 있습니다.


이를 개선하기 위해서는 삼성전자 내에서

소프트웨어 인력에 대한 인식과 처우가 획기적으로 바뀌고

특히나 제 좁은 사견으로 중간관리자들의 재교육 및 인선이 무엇보다 중요하다고 봅니다.


어짜피 시스템은 아무리 개선해도 구멍이 있게 마련이고,

결국 사람과 사람 사이의 관계가 조직을 건강하고 발전적으로 만든다고 봅니다.


제가 보아온 삼성전자의 수석연구원급 또는 임원분들은 

높은 자리에 올라갈만큼 충분히 날까롭고 훌륭한 분들입니다.

문제는 학력에 의한 획일적 공채와 고용안정에 의존해 소프트웨어 관리직에 앉으신 분들 중 몇몇은

자기 휘하의 소프트웨어 인력의 능력과 비전을 등한시 하고

자신이 임원들에게 어필하기 위한 아이디어 도구 및 문서 도구로 생각하는게 보입니다.

이런 사람들은 아무리 소프트웨어 능력이 뒤어나도 절대 경쟁력 있는 개발자라는 인정 안해줍니다.

왜냐?! 자기는 개발 잘 모르는데 얘들 추켜주면 나중에 자기 깔볼까바 무서운거죠.

그래서 무조건 깍아내리고 자기 자랑 합니다.

소프트웨어 결과물은 하청주거나 인도,중국 연구소에 과주 발제하면 될거라는 인식이 팽배합니다.

결국 아무리 뛰어난 소프트웨어 인재라도 평범한 회사원이 되거나 퇴사하게 됩니다.

물론 이건 제가 본 사업부의 시각이고, 연구소 들을 다를 수도 있음을 말씀 드립니다.


한가지 부탁드리자면,

제 글을 대기업의 부도덕성을 까거나 애플 알바글 뭐 이런식으로 몰아주지는 말아주십사 하는 겁니다.

전 그냥 삼성이 왜 애플을 쫒기에 버거운지 무엇이 문제고 바뀌어야 하는지를 말하는 겁니다.


==============================================================================


음... 어쩌다 제글이 이렇게 이슈가...

관심가져주셔서 감사합니다만... 

개인적으로는 좀 좌불안석 이네요. ㅋ

사실 크게 삼성을 까고 싶은 것도 아니었고 옹호하고싶은 것도 아니고,

삼성에 부푼 꿈이 있어 입사했다가 사람과 조직에 꿈이 꺽여 가슴쓰린 마음에

삼성도 현실을 직시하고 바뀌어야 하지 않을까 해서 적어본 글입니다.


댓글에 대해 몇가지 방어를 좀 하자면,


딱 신입사원 마인드라고 하셨는데

삼성 입사 전에도 소프트웨어 회사에서 다년간 일했던

삼성전자 공채 신입사원이지 경력이 없는 사람은 아니예요.

삼성전자는 학교 졸업하고 공채된겁니다.


그리고 제가 오지랍 넓고 사람 사귀는거 좋아해서 본글에 언급했다시피

유관부서의 왠만한 정보는 다 듣고 다녔습니다.

그래서 개발자들끼리 주고받는 사내 정보가 얼마나 제한적인지 잘 알아요.

솔직히 임원들에게 가는 진짜 절박한 위기+사탕발림 보다는

제가 지인들과 나눴던 대화가 훨씬 가감없고 적나라할걸요.


기획없는 개발은 코더일 뿐이다라고 했는데,

본문의 윗분이 말한 기획은 결국 자기 대신 아이템짜내서 PPT 만들어주는 것을 이야기 하는 겁니다.

저도 일하면서 협력업체 꾀부리는거 프로그래밍 실력으로 바로 잡으면 별거 없는데

임원에게  올라갈 PPT 중 제가 맡은 부분 잘 뽑아내면 '훌륭하다'며 엄청나게 칭찬 받았거든요.

기획이요...

저도 매우 중요하게 생각하고 그만큼 실력 갈고 닦았습니다.

신입사원 신제품 컨셉개발 때 임원 만장일치 1위 했다고 말씀 드렸지 않나요?

그 때 "UX에 관심있는 친구들이 많은 팀인가보네... 원하는 부서에 갈 수 있도록 신경쓰게"라는

임원분의 지시를 듣고 삼성전자에서 내 꿈을 이룰 수 있겠구나 라고 착각한게 엊그제 같네요.

아무튼 프로그래머로써 제품을 책임질만큼 생각없이 코딩하고 사는 코더는 아닙니다.


반도체는 제가 완제품이 있는 수원 디지털시티에 있었기 때문에 잘은 모릅니다.

이야기를 나눈 사람이 삼성전자 비메모리분야에 있고

삼성전자가 생산하는 다수의 비메모리칩의 시장상황 및 재고파악 등이 가능한 위치에 있어서

그냥 들은 이야기 중 글에 필요한 부분 언급한 겁니다.

자세하게 걸로 넘어지시면 전 그냥 '그렇게 들었다'라고 하겠습니다.


마지막으로 자조섞인 말을 하겠습니다.

전 정치적 경제적 관점에서 주변사람들 보기에 삼성을 좋아하지 않는다고 보여지는 사람입니다.

하지만, 삼성전자에 개발자로 입사한 이유는

애플이 보는 것과는 방향은 다르지만, 

우리를 풍요롭게할 디지털 세상을 열 수 있는 대등한 경쟁자가 삼성전자라고 생각했기 때문입니다.

하지만, 사람과 조직에 막혔죠.


삼성에는 저와 같은 사람들이 많습니다.

제 꿈과 비전과 그에  이르는 방법을 돌아다니며 지인들에게 침토하며 말하다보면

젊은 층 (대리, 사원급)에 저와 비슷한 생각을 하고 준비하는 사람들이 의외로 많은 것을 보게 됩니다.

문제는 책임과 수석급의 정치 논리에 많이 막힙니다.

우리 파트의 영역이 아니다.

그거 보고하면 옆파트가 임원에게 불호령 듣고 그럼 우리가 보복 똥침 맞는다.

게다가 몇달을 설명해도 이해도 못하고 이해하려 들지 않기도 일수입니다.

그럼 이런 사람들은 두 부류 중 하나가 됩니다.

자포자기하고 안정된 봉급과 빵빵한 PS를 바탕으로 결혼하여 평범한 인간적 행복에 몰두한다.

저처럼 퇴사한다.


얼마 전 삼성전자 전사가 소프트웨어 인력에 대하여 여러가지 혁신을 한다는 소식을 들었습니다.

사실 나오면서 당장 필요하지만 저런 조직 구조면 3~5년을 걸릴거라고 생각한 인사조치가

얼마전 파격적으로 이루어졌다는 소식도 들었습니다.

아마 미래전략실 주도로 이루어졌겠지만, 아주 충격적이었죠.

(내부 조직구성 및 인사이동은 대외비라 제가 언급하면 퇴직서약서에 위배됩니다.)

LG는 못하지만, 삼성은 할 수 있는 근본 이유가 저런 파격적인 부분 때문이 아닐까 합니다.


인사와 관리에 파격이 일어나기 시작 했으니

제발 꿈있고 열정있는 프로그래머 친구들이 능력을 제대로 발휘할 수 있기를 바라게 됩니다.

하지만, 그와 함께 문제가 있는 부분 도려내고 시정하는 작업도 좀 필요하다고 봅니다.


삼성전자 여러분...

애플을 이겨달라고는 이야기 하고 싶지 않습니다.

그냥 멋진 디지털 세계를 여는데 좀 앞장서 주세요.


==============================================================================


안녕하세요.

전 삼성전자 올해 퇴사한 사람입니다.

자세한건 신상털기 당할까봐 말씀드리기 뭐한데요...

SW 개발자였고,

스마트, 앱 뭐 이런 단어들과 직접적으로 연관있던 최전방에 있었습니다.

업무는 직접 개발, 협력업체 관리, 대외 행사 기술 지원까지

키워드는 스마트와 앱 이였죠.

오지랍이 넓어

임원이 아닌 직원급에서 들을 수 있는 유관 부서 정보는 대부분 다 듣고 다녔습니다.


삼성이 애플 못잡아서 안달인거 맞아요.

좀 더 솔직히 말하자면,

애플을 이긴다는 건 꿈도 못꾸고 

애플처럼 마진 많이 남기며 팍팍 팔았으면 좋겠다는게 맞겠죠.

항상 애플 뭐하나 귀 쫑긋 거리고...


상상력이 풍부하신거 같은데

말씀하신 부분에 대한 현실을 말씀드려 볼까 해요.


1. 삼성은 아이폰과 비슷한 갤럭시를 왜 지속적으로 출시하는가?


시장 트렌드에 부합하는 신제품 팔아야 돈벌죠.

LG 어떻게 됐는지 안보이세요?

LG 휴대폰 사업 쵸콜릿으로 흥했다고 스마트폰 신경 안쓰다가

모토로라처럼 한방에 훅 갔잖아요.

삼성은 마켓리더가 아니예요.

시장이 어떻게 변할지 뭐가 좋은지 잘 몰라요.

메이저 트렌드에 디자인이니 뭐니 껍데기 입히는 걸 잘하는 패스트 팔로워죠.

시장 트렌드가 스마트폰이고 스마트폰하면 아이폰이니

당연히 아이폰이랑 똑같은거 만들어야지 않겠어요?

그나마 바다OS 완전히 망했는데, 구글 덕분에 목숨 건진거예요.


어짜피 갤럭시 안내놔도 애플은 삼성에게 하청 안줘요.

폭스콘이 훨씬 싼데 왜 삼성에게 하청을 주나요.

애플이 단가후려치면 안팔면 되요.

기업도 손익분기점은 따져야죠.

다만, 삼성이 반도체나 LCD 라인이 타 업체에 비교도 안되게 대규모라

물량 때문에 단가가 싸서 애플이랑 거래 잘 하고 있는거예요.


장황한데, 결론을 말하자면,

삼성전자 무선사업부가 지배하는 휴대폰 시장의 주류가 피처폰에서 스마트폰으로 이동하면서

판매물량 및 수익을 이전 수준으로 지속시키자면 스마트폰으로 이동해야하는데,

블랙베리나 뭐 이런건 영향력이 미미해서 신경도 안쓰다가

아이폰이란 돌풍이 나타나서 확실하게 존재감을 보여주니까 베끼기 시작 한거죠.

그나마도 자체 기술로 여의치 않아서 바다OS 양산하기에 뭐 같아서 죽쓰는데 

구글이 안드로이드 만들어주니 정말 감사합니다가 된거죠.

삼성전자 무선사업부가 수원 디지털시티의 두번째로 큰 건물인 R3를 통째로 혼자 쓰고

가장 큰 건물인 R4의 20~30% 사용하고, 그 외 자잘한 건물들에도 무선사업부 및 유관부서들이 있죠.

삼성전자 완제품 사업부 나머지 전체보다도 큰 무선사업부를 호황이었던 피쳐폰 때와 같이 유지하려면

열심히 베껴서 열심히 판다 그 뿐인거예요.

애플 견제하고 압박하고 그럴 주제도 안되고 여력도 없어요.



2. 애플은 삼성을 견제할 이유가 있는가?


애플은 삼성에 전혀 신경쓰지 않아요.

애플에게 문제는 구글이죠.

삼성이 구글과 손잡고 시장을 넓히는 거에 관심 없어요.

구글 플랫폼 전체가 시장에 얼마나 퍼지느냐가 진짜 중요한거죠.

애플의 삼성 견제는 구글에 대한 간접 견제일 뿐이예요.

물량 가장 많이 찍으면서 SW로 가장 빈약하고

베끼기 전문인게 너무 티나는게 삼성인지라 압박하기 쉬운거죠.


국내 언론만 쉬쉬하지 갤럭시고 뭐고 삼성폰 북미/유럽에 완전 저가 뿌리는가 공공연한 사실이잖아요?

저가 시장에 구글 플랫폼이 너무 맘놓고 설치면서 퍼지면 안된다는게 애플 생각이예요.

그랬다간 그 옛날 맥과 IBM PC의 악몽이 재현될 테니까요.

비슷해보이지 않아요?

HW/SW 일체형인 고가의 럭셔리 애플 맥 & 아이폰

DOS/Windows 탑재한 IBM PC 연합 & 안드로이드 탑재한 삼성, HTC, LG 등등

애플이 스마트폰 시장의 구석구석에 충분히 영향력을 발휘하기 전까지

안드로이드가 너무 심하게 설치면 안된다는 겁니다.



3. 삼성의 소프트웨어 강화 지시는 애플에게 패배할 것이 뻔한 자충수인가?


지시는 훌륭합니다.

문제는 삼성의 DNA가 SW 인력을 다루기에 문제가 많다는 거죠.

매우 양질의 SW 인력은 좀 더 예술가 타입에 가깝습니다.

자유롭고 상상하고 내킬 때는 스스로 몇 날 밤을 새서 일하다가도

머리가 꽉막히면 손 쫙 놔버리고

소프트웨어 프로그램을 얼마나 짧고 정돈되고 아름답게 짜느냐에 감동받고

최신기술 기웃거리기 좋아하고

신경질적이고 날카롭고 방어적이고 개인주의적이고

이런 까다로운 사람을 관리의 삼성이 반길리가 없죠.


저는 입사했을 때 신입사원 신제품 기획 프로젝트에서

참석 임원 만장일치(9명)로 최우수 작품상을 받았습니다. 

(팀프로젝트 였고 제 기여가 매우 컸습니다.)

그런데 부서 배치 후 저의 메인 업무는 협력업체 관리였습니다.

부서는 소프트웨어 개발 부서였습니다.

결과물은 당연히 소프트웨어 였죠.

사실 수억의 돈을 들여 수개월간 작업한 결과물 치고는 좀 심하다 싶어

2개월간 야근하고 주말에 집에서 일하며 비밀리에 기존 제품의 카피를 만들었습니다.

물론 품질은 훨씬 업그레이드 됐죠.

(어떻게 그렇게 짧은 기간에 고품질의 소프트웨어를 만드냐? 뻥 아니냐 하시면...

그 물건이 원본의 90%가 오픈소스 였기에... 그래서 돈 아까웠던거죠.

차라리 오픈소스그룹에 기부를 하고 말지...

참고로 전 신입이지만 소프트웨서 회사 경력 3년에 

졸업 때까지 1년반 동안 학생 신분으로 삼성전자의 일을 했습니다.)

소파트 회의에서 윗분에게 자랑스럽게 깜짝 발표를 한 후 돌아온 말은

"껍데기는 아무나 만들어, 그 기업의 10년 노하우가 중요한거야." 였습니다.

삼성전자의 10억 예산을 아껴줄 소프트웨어가 그렇게 사라졌습니다.

구글TV가 언론에 처음 발표되었던 날 윗분께 어떻게 생각하시냐고 물었습니다.

"사원은 아무 생각말고 시키는 일이나 잘해. 

임원과 윗분들은 그런 정보 챙기는 전담 부서에서 훨씬 양질의 액기스만 뽑아서 올려 바치니까

네 선에서 그런거 생각하지마. 위에서 알아서 하실테니까."

이 외에도 기타등등 기타등등

그렇게 그렇게 보내다가 이건 아니다 싶어 퇴사했습니다.

퇴사 후 옆 파트의 정말 튀어난 개발자 선임이 제 빈자리를 매웠다는 소식을 들었습니다.

그 옛 윗분께 연락 드렸습니다.

저: "뛰어난 실력자 영입하셨다니 축하드립니다. 제 퇴사가 오히려 복이 됐네요."

윗분: "별로 뛰어나지 않아. 개발은 잘하는데, 코드만 짜는 사람은 3류야. 나처럼 기획을 잘해야지."

자뻑은 둘째치고 개발에 뛰어난 개발자가 기획을 못한다고 3류라니요.

그럼 기획이나 마케팅을 해야지 왜 개발그룹에서 개발자를 하나요.


제 경험담을 보면 아시겠나요?

개발의 중추를 맡고있는 실무자가 개발에 대해 이런 자세를 견지하고 있고,

아무도 문제라 생각하지 못하고 있습니다.

소프트웨어가 지시한다고 좋아질까요?

결국 노키아 꼴 나겠죠.


하지만, 지시만큼은 훌륭한 겁니다. 흠흠



4. 삼성은 애플과 비교하여 패배자인가?


패배자는 아니지만, 바게닝 파워 말씀하시면서 언급한 부분은 완전히 국내 언론에 놀아나신거네요.

연휴에 반도체 부문의 친한 형과 차를 마셨습니다.

실제 삼성의 반도체공정 기술력은 세계 5~6위 수준이며,

현재 거래선을 트고있는 TSMC(?)가 진짜 1위라고 하네요.

삼성의 강점은 생산라인이 크고 반도체를 덤핑으로 끼워팔기를 해주기 때문에

애플의 막대한 물량을 받아내면서 프로세서+메모리 세트 가격이 가장 싸기 때문이라고 합니다.

그나마 애플이 빠져나가면 수익이 크게 악화되기 때문에

윗분들이 애플 꼬시느라 똥줄 탄다네요.

애플 빠져나가서 수익 악화되면 임원들 LCD처럼 목숨줄 뎅겅뎅겅 되는 건 시간 문제겠죠?



5. 결론


삼성은 애플의 하청업체가 되는 것을 두려워하지도 않고,

애플도 삼성을 하청으로 쓸 생각이 없습니다.


현재 삼성은 애플이 기존 마켓 트렌드를 계속 파괴하면서 새로운 마켓을 형성하는데

그걸 미리 읽지 못해 전전긍긍하고 있는 상황에서

그나마 안드로이라도 있어서 기존의 패스트 팔로워 기질을 십분 발휘하여 

시장 지배력을 잃지 않기 위해 노력하는 상황입니다.

문제는 기존에는 패스프 팔로워 전략으로 지역과 소비자에 따라 맞춤 제품을 제공해서

기존 선두 업체들을 죄다 뭉갰는데,

이제는 소프트웨어 기술력이 없어서 따라가는것 자체가 안드로이드가 없으면 안된다는거죠.

삼성은 기존 시장 점유율 잃지 않기 위해 따라가기만도 힘들어요.


애플이 삼성을 견제하는 것은

구글 안드로이드를 막아야 하는데, 오픈소스 프로젝트를 저지할 방법이 없습니다.

그렇다면 안드로이드 탑제 제품을 막아야 하겠는데,

삼성이 물량도 제일 많고 애플 제품도 똑같이 따라해주고 

딴지 걸곳도 많고 효과도 확실하니까 삼성 멱살을 잡은거 뿐입니다.


삼성과 애플을 나이키와 루이비똥에 비교한건 비약이 심하신데요.

삼성과 애플은 지금 함께 스마트폰 시장에서 격돌 중이고,

곧 스마트 TV까지 불똥이 번질겁니다.

참고로 삼성전자 완제품의 최대 캐시카우는 휴대폰과 TV 입니다.

나이키는 스포츠웨어 회사고 루이비똥은 패션브랜드죠.

시장과 타겟 고객층이 전혀 달라요.


아고라 원문 http://bbs1.agora.media.daum.net/gaia/do/debate/read?bbsId=D115&articleId=1598812&RIGHT_DEBATE=R0


[Platinum UPnP] 101 :: (3) Service에 Action 추가하기




※ 주의: 앞으로 나오는 이상한 문자열이나 XML에 대해서 친절하게 설명하지 않습니다. 눈치것 의미를 알아차리시거나, 스펙문서를 통해 의미를 파악해 주시기 바랍니다. 이 문서의 목적은 Platinum UPnP를 이용해 잘 동작하는 Tutorial 프로그램을 작성하는 것이니까요.


파일: tutorial03.cpp


우리는 이제까지 Device를 만들어서 Service를 등록하고 Event가 발생하도록 StateVariable을 조작하는 것까지 해보았습니다.  이번에는 Device에 Action을 추가해 보겠습니다. Action의 기능은 입력 값을 문자열로 받아서 앞서 사용했던 "PrintBuffer" StateVariable의 값을 변경하고 콘솔에 변경된 값을 출력하는 것으로 하겠습니다. 이렇게 하면 Action이 정상적으로 동작 했는지에 대해 콘솔과 Event 양쪽에서 모두 확인할 수 있을 것입니다.


    우선 Service Description XML에 Action에 대한 내용을 추가합니다. 수정된 내용은 아래와 같습니다.


const char* SCPDXML_TUTORIAL =
	"<?xml version=\"1.0\" ?>"
	"  <scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
	"    <specVersion>"
	"      <major>1</major>"
	"	   <minor>0</minor>"
	"    </specVersion>"
	"    <actionList>"
	"      <action>"
	"        <name>Print</name>"
	"        <argumentList>"
	"          <argument>"
	"            <name>String</name>"
	"            <direction>in</direction>"
	"            <relatedStateVariable>PrintBuffer</relatedStateVariable>"
	"          </argument>"
	"        </argumentList>"
	"      </action>"
	"    </actionList>"
	"    <serviceStateTable>"
	"      <stateVariable sendEvents=\"yes\">"
	"        <name>PrintBuffer</name>"
	"        <dataType>string</dataType>"
	"        <defaultValue></defaultValue>"
	"      </stateVariable>"
	"    </serviceStateTable>"
	"  </scpd>";


    변경 내용을 간략히 살펴보자면, "ActionList" Tag가 추가되고 그 안에 "Print"라는 이름의 Action이 하나 있습니다. 이 Action은 "String"이라는 입력값을 갖으며, 값의 형태는 "PrintBuffer" StateVariable의 정의를 따라갑니다. 스펙을 보면 아시겠지만, UPnP에서 Action의 입출력 값들의 타입은 무조건 StateVariable 중 하나와 연결되어야 하는 것으로 보입니다.

    이 상태로 컴파일 후 실행하면, Device Spy에서 Action이 추가된 것을 확인할 수 있습니다. 하지만, 현재로써는 실제로 Action이 동작하지 않습니다. 그래서 이제 Action의 구현 코드를 넣어야 합니다.

    PLT_DeviceHost 클래스에는 OnAction()이라는 virtual 함수가 있습니다. 이 함수의 내용을 오버라이딩(Overriding)하여 원하는 Action이 동작하도록 하여야 합니다. Tutorial Device 클래스에 추가된 코드는 다음과 같습니다.


virtual NPT_Result OnAction(PLT_ActionReference& action, const PLT_HttpRequestContext& context)
{
	PLT_ActionDesc& desc = action->GetActionDesc();
	const NPT_String name = desc.GetName();
	if (name == "Print") {
		NPT_String value;
		action->GetArgumentValue("String", value);
		PLT_Service* service = desc.GetService();
		service->SetStateVariable("PrintBuffer", value);
		printf("[Action:%s] (String, %s)\n", name, value);
		return NPT_SUCCESS;
	}
	return PLT_DeviceHost::OnAction(action, context);
}


    위 코드의 내용은 다음과 같습니다. OnAction() 함수가 호출되면 Action의 이름이 "Print"가 맞는지 확인합니다. 맞지 않다면 부모 클래스의 기본 OnAction() 함수를 수행합니다. 맞다면 다음과 같이 "Print" Action을 수행합니다. 호출된 Action에서 "String"의 값을 읽어 옵니다. 그리고 읽어온 값을 Service의 "PrintBuffer" StateVariable에 적용합니다. 마지막으로 수행한 Action과 입력값을 콘솔에 출력합니다.


    이제 UPnP에서 정의하는 기본적인 동작을 모두 갖춘 하나의 Device가 완성되었습니다. 다음에는 간단한 Control-Point를 하나 만들어 보겠습니다.

[Platinum UPnP] 101 :: (2) Device에 Service 올리기 + StateVariable & Event




※ 주의: 앞으로 나오는 이상한 문자열이나 XML에 대해서 친절하게 설명하지 않습니다. 눈치것 의미를 알아차리시거나, 스펙문서를 통해 의미를 파악해 주시기 바랍니다. 이 문서의 목적은 Platinum UPnP를 이용해 잘 동작하는 Tutorial 프로그램을 작성하는 것이니까요.



앞에서 우리는 간단한 Device를 만들어서 확인해 보았습니다. 하지만, UPnP에서 Device만으로는 아무런 소용이 없습니다. UPnP는 Device가 소유하고 있는 Service를 통해서만 Device를 조작할 수 있습니다. 이러한 동작을 위해 Service는 다시 Action과 StateVariable, Event라는 개념을 갖고 있습니다. Action은 외부에서의 명령, StateVariable은 말 그대로 상태값(음량, 음소거 상태 등등)이며, Event는 StateVariable의 변경이 일어났을 때, 만약 그것을 외부에 알리도록 설정되어 있다면 변경을 공지합니다.


아마 Windows의 COM/DCOM/ActiveX 개념을 알고 있다면, 위의 내용을 이해하기 훨씬 쉬울 겁니다. 임의의 바이너리를 범용적으로 연결하여 동작하기 위한 기본 원리는 완벽하게 동일하다고 말할 수 있겠네요. 뭐, 모른다면 할 수 없구요 ㅋ


    이번 작업은 앞서 했던 파일(tutorial01.cpp)을 바탕으로 수정하도록 하겠습니다. 

    먼저, 성의가 없었던 Device의 생성자를 살짝 수정합니다. 수정된 코드는 아래와 같습니다.


TutorialDevice()
	: PLT_DeviceHost("/", "", "urn:schemas-upnp-org:device:Tutorial:1", "Tutorial Device")
{
}


    이제 Service를 생성해야 합니다. Service는 XML 형식의 Service Description에 의해 그 형태가 결정 됩니다. Service Description에는 그 Service가 갖는 Action과 StateVariable, Event에 대해 기술되어 있습니다.

    일단 복잡함을 피하기 위하여, StateVariable과 Event에 대한 동작을 먼저 만들어 보겠습니다. 지금 구현할 동작을 명세하자면, 콘솔 입력을 통해 문자열을 입력 받은 후 그것을 StateVariable에 저장하여 Event가 발생하는지를 확인합니다.

    우선 Service Description XML을 준비해야 합니다. 그래서 아래와 같은 변수를 정의합니다.


const char* SCPDXML_TUTORIAL =
	"<?xml version=\"1.0\" ?>"
	"  <scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
	"    <specversion>"
	"       <major>1</major>"
	"	    <minor>0</minor>"
	"	 </specversion>"
	"    <servicestatetable>"
	"      <statevariable sendevents=\"yes\">"
	"        <name>PrintBuffer</name>"
	"        <datatype>string</datatype>"
	"        <defaultvalue></defaultvalue>"
	"      </statevariable>"
	"    </servicestatetable>"
	"  </scpd>";


    위의 XML에서는 현재 Service 안에 문자열(string)로 취급되는 "PrintBuffer"라는 이름의 StateVariable이 하나 있으며, 변경시 Event가 발생한다는 것을 알려주고 있습니다. 이제 Device에 이 Service를 등록해야 합니다.

    전 단계에서 PLT_DeviceHost 클래스의 자식 클래스를 만들기 위해 정의했던 함수가 있습니다. 바로 SetupServices() 함수 였습니다. 기억하시는지요? Device에 Service를 등록하기 위해서는 이 함수 안에서 등록해 주어야 합니다. 그래서 아래와 같이 SetupServices() 함수에 내용물을 넣습니다.


virtual NPT_Result SetupServices(void)
{
	PLT_Service* service = new PLT_Service(
		this,
		"urn:schemas-upnp-org:service:Tutorial:1", 
		"urn:upnp-org:serviceId:Tutorial.001",
		"Tutorial");
	service->SetSCPDXML(SCPDXML_TUTORIAL);
	AddService(service);

	service->SetStateVariable("Status", "True");

	return NPT_SUCCESS;
}


    이렇게 함으로써 "urn:upnp-org:serviceId:Tutorial.001"이라는 아이디와 "Tutorial"이라는 이름을 갖는 Service가 Device에 등록 되었습니다.

    StateVariable과 Event가 정상 동작하는지 판별하기 위해서는 수동으로 값을 변경해 주어야 할 필요가 있습니다. 그래서 아래와 같이 외부에서 "PrintBuffer"를 조작하기 위한 함수를 하나 만들겠습니다.


void SetPrintBuffer(const char* str)
{
	PLT_Service* service;
	if (FindServiceByName("Tutorial", service) == NPT_SUCCESS) {
		service->SetStateVariable("PrintBuffer", str);
	}
}


    FindServiceByName() 함수는 PLT_DeviceHost의 부모 클래스인 PLT_DeviceData의 멤버함수 입니다. 위 함수 이외에도 다양한 방법으로 Device가 소유한 Service를 찾을 수 있습니다. Service를 찾고나면 원하는 StateVariable에 입력값을 저장합니다.

    마지막으로 콘솔에서 값을 입력 받아 위에서 정의한 SetPrintBuffer() 함수를 호출하도록 main() 함수의 while loop 구문을 아래와 같이 수정합니다.


bool stop = false;
do {
	std::string input;
	std::cin >> input;
	if (input == "q" || input == "Q") {
		stop = true;
	} else {
		tutorial_device->SetPrintBuffer(input.c_str());
	}
} while(!stop);


    이제 모든 작업이 완료 되었습니다. 코드를 빌드하여 실행시킨 후 StateVariable을 변경시켜 Event가 발생하는지 Device Spy로 확인할 차례입니다. 여기서 주의할 점은 Device Spy의 해당 서비스에서 마우스 오른쪽 버튼으로 컨텍스트 메뉴를 연 뒤 "Subscribe to Events" 항목을 클릭해야 해당 Service의 Event가 Device Spy에 나타나게 됩니다.

    확인해 본 결과 저는 아주 잘 동작하고 있습니다. 여러분은 어떠신가요?


[Platinum UPnP] 101 :: (1) 간단한 Device 만들기




파일: tutorial01.cpp


UPnP는 크게 Device와 Control-Point라는 개념으로 나뉘어 집니다. 우선 간단한 Device를 만들어 보겠습니다. Device를 먼저하는 이유는 Device Spy 프로그램으로 동작을 간단히 확인해 볼 수 있기 때문입니다. Control-Point의 기본적인 동작은 Device를 찾아내어 그 정보들을 확인하고 그에 맞게 Device를 조작하는 등의 복잡한 작업을 하기 때문에 Device를 먼저 알아보도록 하겠습니다.


    우선 main() 함수를 구성합니다. Platinum은 Network 통신을 위한 PLT_UPnP 객체를 제공합니다. 이 객체에 AddDevice() 함수를 통해 Device를 등록하면, UPnP 통신에 대한 것은 PLT_UPnP 객체가 담당해주고, Device는 자신의 임무만 열심히 하면 됩니다.


int main()
{
	PLT_UPnP upnp;
	upnp.Start();

	bool stop = false;
	do {
		const char c = fgetc(stdin);
		stop = (c == 'q' || c == 'Q');
	} while(!stop);

	upnp.Stop();
	return 0;
}


    위 코드를 통해 프로그램은 시작하면서 Network 통신을 준비하고, q 또는 Q 문자가 입력될 때까지 대기합니다. Platinum은 기본적으로 멀티 쓰레드로 동작하기 때문에, main() 함수에서 아무 동작을 하지 않아도 내부에서는 UPnP 통신을 위한 많은 동작을 하고 있습니다.


    이제 UPnP 통신은 준비를 했지만, 사용할 Device가 없습니다. 그래서 Device를 만들어 봅니다. Device는 PLT_DeviceHost 클래스를 상속을 받아서 구현을 합니다. 이 때, 주의할 점은 PLT_DeviceHost 클래스는 SetupServices() 라는 순수 가상함수를 포함하고 있습니다. 따라서 상속만 받아서는 객체가 생성되지 않습니다. SetupServices() 함수도 함께 구현을 해주어야 합니다. 코드는 아래와 같습니다.


class TutorialDevice :public PLT_DeviceHost
{
protected:
	virtual NPT_Result SetupServices(void)
	{
		return NPT_SUCCESS;
	}
};


    현재 위의 코드에서는 아무 일도 하지 않습니다. 왜냐하면 우리는 Service가 존재하지 않는 가장 단순한 Device를 만들기로 했기 때문 입니다. Service가 무엇인지는 다음 단계에서 알아보고 구현하겠습니다.

    Device가 만들어 졌으면 Device가 Network 상에서 동작할 수 있도록 PLT_UPnP 객체에 등록을 합니다. 등록 코드는 아래와 같습니다.


	...

	PLT_UPnP upnp;
	PLT_DeviceHostReference device(new TutorialDevice());
	upnp.AddDevice(device);
	upnp.Start();

	...


    이제 실행을 시켜 봅니다. 실행을 시키면 Device Spy 에 새로운 디바이스가 추가되는 것을 볼 수 있습니다. 하지만, 이름이 없는 Device 입니다. 너무 궁색해 보이니 TutorialDevice 클래스의 생성자에서 이름을 추가합니다.


	...

	TutorialDevice()
		: PLT_DeviceHost()
	{
		m_FriendlyName = "Tutorial Device";
	}

	...


    프로그램을 다시 실행 시킨 뒤에 Device Spy 프로그램의 Device 목록을 보면 "Tutorial Device"라는 이름의 새로운 Device를 볼 수 있습니다.

[Platinum UPnP] 101 :: (0) 소개




지난 1주일 간 Platinum UPnP라는 오픈소스를 만지작 거리고 있습니다. 가장 큰 이유는 회사 업무와 관련이 있기 때문입니다. 하지만, 그 이유만이 아니더라도 UPnP/DLNA 서비스는 더욱 확대 될 것으로 보이기 때문에 좀 더 제대로 알아두면 좋을 것 같습니다.

    다만, Platinum UPnP는 듀얼라이센스로 운영됩니다. 무료로 사용하시고자 하면 GPL이 적용되어 작업한 결과물의 소스를 모두 공개해야 합니다. GPL을 적용받기 싫다면, 유료 라이센스를 구매해서 사용하시면 됩니다. 언듯 듣기로 수천만원 한다고 하는 것 같던데, 잘은 모르겠네요.


    Platinum UPnP는 UPnP/DLNA 서비스를 만들기 위한 기반 코드를 제공합니다. 크로스 플랫폼 컴파일이 가능하며, 개인적으로 확인한 플랫폼은 Windows 7, Linux, Mac OS X 입니다. 소스를 다운받으면, Platinum UPnP의 라이브러리 빌드 환경과 몇몇 샘플들이 함께 포함되어 있습니다. 다만, 문서는 Doxygen 이외에는 보이지 않아서 약간 불친절 합니다. 하긴, 유료 라이센스에는 원래 기술지원 비용도 포함되니까 너무 친절한 문서를 제공해도 장사에 문제가 되려나요?


    일단 여기서는 UPnP/DLNA에 대한 자세한 내용은 다루지 않습니다. 저도 아직은 스펙을 보고 있는 중이기도 하고, 공식 홈페이지나 웹서핑을 통해 양질의 정보를 얻으실 수 있을거라 생각합니다. 


    이곳에서는 UPnP를 테스트 할 수 있는 툴이 제공됩니다. 무료입니다. 제공되는 프로그램들 중 Device Spy 라는 프로그램이 UPnP의 특징을 가장 잘 보여준다고 할 수 있겠네요. UPnP의 스펙문서들은 이곳에서 받아보실 수 있습니다.