꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 알 수 없는 사용자

도플광어님의 '유니티엔진의 coroutine & yield 1편. FSM의 습격' 을 보면 코루틴에 대한 자세한 설명이 나옵니다.

그렇다면!! 이렇게 편한 코루틴을!! C++에서 쓸 수는 없을까?!

(제한적이긴 하지만)가능합니다!!


사실 C++ 자체로만은 만들기가 어렵고요.. goto문으로 만드는건 자체 스택을 가지지 않기 때문에 위험성도 있고 여러가지 문제가 있죠

그런데 Win32 API에는 Fiber라는 녀석이 존재합니다. Fiber는 유저 레벨 쓰레드 라고도 불리는 녀석인데요.

동작하는 방식이 코루틴과 거의 흡사합니다. 그럼 이녀석을 이용해서 코루틴처럼 만듭시다!! 

라고 하면 너무 힘들겠죠.. 사실 이미 그렇게 만들어서 공개된 라이브러리들이 존재합니다!!

그 중에서 제가 소개하려고 하는 것은 boost.coroutine입니다.


일단 다운로드부터 받아봅시다.

https://github.com/boost-vault/Concurrent-Programming/blob/master/boost-coroutine-2009-12-01.tar.gz

boost 코루틴은 boost 공식 라이브러리에 포함된 라이브러리가 아니어서 따로 받아주어야 합니다.

boost의 폴더구조를 그대로 사용하고 있기 때문에 받은 후에 boost 폴더에 덮어씌워주면 됩니다.


사용도 상당히 간단합니다. 코루틴 객체를 만들고 코루틴 객체를 실행해주고 코루틴객체에서 self.yield를 호출해줄 수 있습니다. Fiber니 뭐니 그런건 그닥 신경안써도 되죠. 사실 windows에서만 Fiber를 쓰고 다른 OS에서는 다른 API로 작동하게 구현이 되어있습니다. 물론 모든 OS 버전을 지원하진 않습니다.


void TestFunction( boost::coroutines::coroutine<void ()>::self& self )
{
printf( "첫번째\n" );
self.yield();
printf( "두번째\n" );
self.yield();
printf( "세번째\n" );
self.yield();
printf( "네번째\n" );
self.yield();
printf( "다섯번째\n" );
}

int _tmain(int argc, _TCHAR* argv[])
{
boost::coroutines::coroutine<void ()> coro( TestFunction );
printf( "첫번째 실행\n" );
coro();

printf( "두번째 실행\n" );
coro();

printf( "세번째 실행\n" );
coro();

printf( "네번째 실행\n" );
coro();

printf( "다섯번째 실행\n" );
coro();

if( coro.exited() )
{
printf( "끝!\n" );
}
}

위 코드의 결과는 아래처럼 나오게 됩니다.


self.yield()를 호출할때마다 코루틴을 실행해준 위치로 빠져나오고 다시 코루틴을 실행해주면 마지막으로self.yield()를 호출해준 다음 명령부터 실행되는 것을 확인할 수 있습니다.


리턴값이 있는 코루틴을 사용하는 경우에는 다음과 같이 할 수 있습니다.


typedef boost::coroutines::coroutine<int ()> coro_type;

int TestFunction( coro_type::self& self )
{
self.yield( 2 );
self.yield( 4 );
self.yield( 6 );
self.yield( 8 );
return 10;
}

int _tmain(int argc, _TCHAR* argv[])
{
coro_type coro( TestFunction );
printf( "%d\n", coro() );
printf( "%d\n", coro() );
printf( "%d\n", coro() );
printf( "%d\n", coro() );
printf( "%d\n", coro() );

if( coro.exited() )
{
printf( "끝!\n" );
}
}

역시 이 코드의 결과는 다음과 같습니다.


self.yield( 리턴값 ) 을 호출해주면 코루틴을 호출한 곳에서 리턴값을 받아서 사용할 수 있습니다. 


코루틴이 처음 생성될때 추가로 인자값을 넣어줄 수도 있습니다.

typedef boost::coroutines::coroutine<int ( int )> coro_type;

int TestFunction( coro_type::self& self, int number )
{
number += 2;
self.yield( number );
number += 2;
self.yield( number );
number += 2;
self.yield( number );
number += 2;
self.yield( number );
number += 2;
return number;
}

int _tmain(int argc, _TCHAR* argv[])
{
coro_type coro( boost::bind( TestFunction, _1, 10 ) );
printf( "%d\n", coro() );
printf( "%d\n", coro() );
printf( "%d\n", coro() );
printf( "%d\n", coro() );
printf( "%d\n", coro() );

if( coro.exited() )
{
printf( "끝!\n" );
}
}

역시 마찬가지로 다음과 같은 결과가 나올 것입니다.


물론 처음 말한것처럼 다른 언어에서 보여주는 정도의 완벽한 코루틴을 구현할 수는 없습니다. 그리고 제한적입니다. 

특히 boost.coroutine은 윈도우즈 서버 2008 이상의 OS나 윈도우즈 비스타 이상의 OS에서만 정상적으로 동작합니다.

그래서 클라이언트에서는 사용하기 어렵습니다..하지만 서버에서라면 쓸 수 있습니다. (네 저희는 윈도우 서버 2008을 사용합니다..) 그리고 사실 클라이언트에서보다 서버에서 더 효과적이라고 생각됩니다.



사실 이 뒤에 더 이어서 이것저것 써보려고 했지만.. 생략... 죄송합니다 노느라..

반응형
,
Posted by 알 수 없는 사용자

너무 오랜만에 글을 올려서 죄송합니다.orz

(제가 기타 항목에 올리는 글은 잡담 수준의 망상 글로서 아마추어적인 지식습득으로 인해 잘못된 정보가 섞여 있을 수 있습니다. 그런 부분에 대해서는 가차없이 지적해주시면 감사하겠습니다.)



몇 주전에 인터넷에서 본 글 중에 이런 내용이 있었습니다.

'온라인 MMO에서 발생하는 통화량으로 인해 하나의 MMO는 중앙은행과 같은 역할을 하게 되고, 결국 중앙은행은 그들을 고려해야 할 것이다.'

그 글을 읽고 저것이 과연 가능할까 생각해봤는데 결론은 '중앙은행은 온라인 MMO가 만들어내는 통화량에 신경쓸 필요가 없다'라는 결론이 나왔습니다.


온라인 MMO,MO의 재화(고급 아이템, 게임머니)들은 환금/거래가 쉽다는 점에서 통화량을 늘리고 있습니다.

그건 사실입니다.

하지만 이미 온라인 mmo 이전에도 통화량을 늘리고 있는 다른 매체들이 있습니다.

상품권이 그것이죠.


만약 온라인 MMO에서 생성되는 통화량에 중앙은행이 신경써야 한다면 마찬가지로 상품권이 발생시키는 통화량에 대해서도 신경을 썼어야 할 겁니다.

하지만 그렇지 않죠.

왜냐면 상품권으로 인해 발생하는 통화량은 전체 GDP규모에 비하면 신경쓰지 않을 정도로 작기 때문입니다.

그리고 대부분의 온라인 MMO에서 발생하는 통화량은 엔간한 백화점 상품권이 발생시킬 통화량보다 작을 겁니다.

(물론 WOW정도 되면 왠만한 소국의 중앙은행보다는 많은 통화량을 발생시킬 것 같긴 하지만 말입니다.;;)

그러므로 중앙은행이 상품권에 신경쓰지 않는 것처럼 온라인 MMO도 신경쓸 필요는 없다는 것이죠.


정리하겠습니다.

1. 온라인 MMO의 재화들은 통화량을 발생시킨다.(아이템매니아의 공이 큽니다.;;)

2. 하지만 그 통화량은 전체 경제에 비하면 매우 작으며 중앙은행이 신경쓸 정도가 아니다.


지금까지 이런 뻘글을 읽어주셔서 감사합니다.

다음에는 좀 더 유익한 내용으로 찾아뵙도록 하겠습니다.;;

반응형
,

아는 만큼 보인다

아트 2012. 6. 29. 17:35
Posted by 알 수 없는 사용자

때는 2003년 중순부터 2004년 2월 클로즈베타를 지나 오픈 직후 팡야 프로젝트의 개발이 한창일 때의 일입니다.

당시 팡야 AD로서 캐릭터 기본 체형 제작 뿐만 아니라 수 많은 의상들을 모델링 하고 텍스쳐를 손으로 그려야 했는데요. 나중에는 옷의 질감이나 주름 표현의 달인이 되어 있었습니다. 나름 생활의 달인이였던거지요. 작업을 하는 본인이 깜짝 놀랄 정도로 바디페인터 브러시가 슬쩍 지나가기만 해도 아름답고 섹시한 주름이 만들어지곤 했습니다. (이 부분에서 돌 던질 지인들이 좀 있을 것 같네요 ㅡ,.ㅡ)

당시에는 요즘처럼 쉐이더를 통해서 질감을 표현하는 방식은 아니였고 순수하게 디퓨즈 텍스쳐만으로 모든 표현을 했었습니다. 하루 종일 회사에서 옷의 주름, 질감, 그리고 재봉선과 씨름을 하다 보니 주변 개발자들은 물론이고 지하철을 타거나 길거리를 걸어갈 때에도 눈에 들어오는 것은 주름, 질감, 그리고 재봉선이였습니다. 그렇게 눈여겨 봐둔 특징들은 상당 부분 게임에 반영이 되었구요.

놀라운 것은 평소와 같은 지하철이지만 내 관심사가 달라지면서 완전히 새로운 공간이 되었다는 점입니다. 전에는 보이지 않던 것들이 보이게 된 것이죠(투시력이 생기거나 한건 아닙니다). 보려고 해서 보는게 아니라 세상이 달라보이는 경험이였습니다. 물론 10년 가까이 지난 지금은 다시 원래대로 되돌아오긴 했습니다... 실무 비중이 많이 줄어든 탓도 있구요.

짐작이긴 하지만 디즈니 3D 라푼젤의 기술팀에서 자연스러운 머리카락을 연구할 당시에는 아마 주변 사람들의 머리카락만 눈에 들어왔을겁니다.

이처럼 어떤 사람이 관심사가 명확할 때에 받아들이는 정보의 종류와 품질은 비교할 수 없을만큼 달라지게 됩니다. 다시 말해서 목표가 명확한 사람은 잠을 자는 시간 외에는 모두 성장하는 시간이라고 봐도 됩니다.

극장에서 영화를 볼 때에 오디오 감독을 목표로 하는 사람의 경험과 시나리오 작가를 목표로 하는 사람의 경험은 완전히 다른 경험일 것입니다. 물론 극장에 영화를 보러 갈 때에 '나는 이번 영화를 통해서 성장하고야 말겠어' 라고 다짐하고 가는 사람은 없겠지만 목표가 명확한 사람은 평범한 생활 자체가 감각을 키우는 훈련인거죠.

반대로 무엇을 해야할지 목표가 불명확한 사람은 그 기간 동안은 분명히 정체되어 있습니다. 뭔가 배워야 할 것 같고 뭔가 해야할 것 같아서 이것 저것 해보더라도 목표가 없는 노력은 비효율의 극치입니다.

목적지가 명확해야 중간 경로가 확실해지고 당장 무언가를 시작할 수 있습니다.
그리고 무언가에 몰입하는 순간 성장이 시작됩니다.

반응형
,
Posted by 알 수 없는 사용자

한 달만에 글을 올리게 되는군요. 요즘에 조금 슬럼프(?)가 와서 귀차니즘 발동에 아무것도 하지 않고 있다가 정신이 좀 들어서 다시 정신줄 놓기 전에 글을 써볼까 합니다.

얼마 전 엄청 E3 2012에서 많은 게임 영상들이 공개되었는데요. 그 중에 눈에 띄는 영상이 있었으니, 바로 Unreal4 Engine과 스퀘어-에닉스의 차세대 엔진 Luminous의 테크 데모 동영상이었습니다.

이렇게 게임 엔진하면, 가장 먼저 떠오르는 것이 바로 높은 수준의 그래픽 퀄리티와 성능을 보장하는 렌더링 능력입니다. 그렇다면, 이렇게 게임엔진에서 이런 높은 수준의 렌더러을 만들어내는 사람들은 누구일까요? 

여러 직군 중에서 이런 업무를 주로 담당하는 직군을 "그래픽스 프로그래머(Graphics Programmers)"합니다. 오늘은 이 그래픽스 프로그래머에 대해서 소개를 해볼까 힙니다. 


그래픽스 프로그래머!?

그래픽스 프로그래머(Graphics Programmer)는 단어 그대로 해석하면 게임의 그래픽에 대한 프로그래밍을 하는 사람들입니다. 통상적으로 3D 프로그래머, 엔진 프로그래머, 렌더링 프로그래머, 비쥬얼 프로그래머 등 회사마다 편한데로 다양하게 불리우고 있고, 엄밀하게 말하면 다른 의미가 되겠지만 거의 같은 말이라고 보면 될 듯합니다.
(앞으로 내용 중에 그래픽 프로그래머와 엔진 프로그래머를 혼용해서 사용할텐데, 그냥 같은 뜻으로 보시면 됩니다. ㅎ)

그럼 그래픽스 프로그래머는 구체적으로 무슨 일을 할까요? 

요즘에는 게임 그래픽 대한 작업을, 아티스트분들이 거의 전담하게 됩니다. 특히, 테크니컬 아티스트(TA)분들이 엔진의 매터리얼 에디터를 이용하거나 직접 셰이더 코드를 짜면서까지도 셰이더 작업을 많이 하고 있습니다. 그래서 가끔은 테크니컬 아티스트와 그래픽스 프로그래머의 구분이이 명확하게 되지 않는 것도 사실입니다. 두 직군에서 처리하는 업무의 테크니컬한 주제로 보자면, 그래픽스 프로그래머와 테크니컬 아티스트는 같은 주제를 다루게 됩니다. 단지, 그것을 구현하는 데에 있어서의 역할이 조금 다를 뿐입니다.

얼마전에 핑속님의 테크니컬 아티스트 아티클을 보시면 테크니컬 아티스트가 어떤 일을 하는가를 잘 설명해주셨으니, 테크니컬 아티스트에 대해서는 핑속님에게~ ㅎ

그래픽스 프로그래머는 TA와 마찬가지로 직접 셰이더를 작성해서 결과물을 만들기도 하지만, 가장 큰 차이점은 직접적으로 게임 엔진을 다루는 일을 하게 되는 것입니다. 간단하게 예를 들어보면, 언리얼의 머터리얼에 대해서는 "머터리얼 에디터"를 이용해서 셰이더를 작성할 수 있습니다. 하지만, 렌더링 파이프라인을 조작해야 한다거나, 셰이더 조합에 대한 내부적인 처리까지 조작을 허용하지는 않습니다. 그 영역은 프로그래머가 다루어야 할 내용입니다. 

그래서, 사실 좀 더 정확하게 말하자면,  "그래픽스 엔진 프로그래머(Graphics Engine Programmer)"라고 부르는 것이 맞습니다. 이는 게임 엔진 부분에서 그래픽적인 내용. 즉, 그래픽스 렌더러를 개발하고 수정하는데 특화된 프로그래머를 의미합니다. 그렇기 때문에, 그래픽스 프로그래머가 하는 업무의 범위는 상당히 넓습니다. 크게 생각하면, 3D를 다루는 모든 범위를 다룬다도 할 수 있을 것 같군요. (3D 컴퓨터 그래픽스라는 범위로 봤을 때에 애니메이션도 포함된다구욧~) 


그래픽 프로그래머가 하는 업무는 크게 나누면 두 가지로 나눌 수 있을 것 같습니다. 하나는 게임 엔진의 그래픽에 관련된 기능들을 구현하는 것이고, 또 하나는 그 구현된 그래픽 기능들이 게임에서 잘 돌아갈 수 있도록 최적화하는 것입니다. 게임에 맞는 가장 적절한 그래픽을 보여줄 수 있는 가장 빠른 방법을 만들어내는 것이 중요합니다. 사실 어떻게 보면, 기능을 추가하는 것 자체가 그렇게 어렵지 않을 수도 있습니다. 하지만, 이 기능들을 게임에 녹아들게 해서 실제로 게임에서 빛을 보게 하는 일은 그리 쉬운 일이 아닙니다. 

그래픽스 프로그래머(엔진 프로그래머)는 기술적인 이슈를 다루기 때문에, 가끔은 최신 기술이나 화려한 기술들에 대해서 집착하는 경향이 있기는 합니다만, 궁극적으로 (테크니컬) 아티스트와 동일하게, 게임에 맞는 가장 좋은 퀄리티의 그래픽을 만들어 내는 것입니다. (Graphics VS Aesthetics을 보시는 것을 추천드려요~) 

그렇기 때문에, 그래픽 프로그래머는 프로그래머이지만, 좋은 그래픽이 무엇인지 좋은 그래픽을 만들기 위해서는 어떻게 해야 하는지에 대한 많은 고민을 해야 합니다. 그러기 위해서는 아티스트들과도 많이 이야기를 해야 하고, 어느 정도 그래픽(아트)에 대해서도 공부를 할 필요가 있다고 생각이 듭니다. (제가 이런 감각이 없어서 엄청 고생 중이에요.. ㅜㅜ)


그래픽스 프로그래머의 작업 결과물은 툴 작업을 거쳐서 아티스트들에게 제공됩니다. 즉, 아티스트들이 고객이라는 이야기입니다. 엔진 개발자(그래픽스 프로그래머)는 같이 일하는 툴 사용자들에 대한 서비스 마인드가 가져야 한다는 것은 매우 중요한 이야기입니다. 사실 3D 엔진 개발자들이 회사에서 가지는 영향력이 상당히 컸었기 때문에, 개발팀 내부에서 콧대 높은 엔진 개발자들에 대해서 안 좋은 이미지를 가지고 있는 경우도 참 많습니다. 


(지금은 그렇지 않아요~ ㅎㅎ)

그래픽스 프로그래머가 되려면?

그래픽스 프로그래머가 되려면 어떤 공부들을 해야 할까요? 음.... 제가 어떤 것이 필요하다고 하기 전에 구직사이트에서 그래픽스 프로그래머에 대한 요구스펙에 대해서 한번 보는게 좋을 것 같군요. 

몇 가지 공통적인 요구사항들이 있어서 정리해보도록 하겠습니다.

1. C/C++

렌더링 엔진 Core는 성능이 가장 중요하게 요구되기 때문에, 대부분의 경우에는 C++을 이용해서 구현을 하게 됩니다. 그렇기 때문에, 엔진 안에서 그래픽스 기능들을 구현하기 위해서는 당연히 C++를 이용하게 됩니다. 그러다보니, C++에 익숙한 것은 당연한 요구사항입니다. 또한 엔진의 성능이 매우 중요하기 때문에, 언어를 잘 다룰 줄 아는 것이 매우 중요합니다. (ex : std list의 size()을 남발하는 것...)

2. 3D API (DirectX / OpenGL)

렌더링 엔진은 3D 그래픽스를 처리하기 위해서 DirectX / OpenGL 과 같은 3D API를 사용합니다. 그렇기 때문에, 당연히 게임 엔진에 맞는 3D API를 잘 사용할 줄 아는 것은 매우 중요합니다. 현재에는 국내의 PC 온라인 게임의 경우에는 Direct3D9를 가장 많이 사용하지만, 차세대 게임 엔진들은 Direct3D11을 사용하고 있고, 요즘과 같이 모바일이나 웹 게임의 경우에는 OpenGLES을 사용하고 있기 때문에 프로젝트에 맞는 API를 공부해서 익숙해져야 합니다.  

3. Shader

기본적으로 HLSL을 사용하기 때문에, HLSL 문법을 익히면 됩니다. 문법적으로 봤을 때, 셰이더 프로그래밍 자체는 그렇게 어렵지 않습니다. RenderMonkey등의 에디터를 이용해서, 쉽게 공부해볼 수도 있구요. 하지만, 이미 모든 고수준 그래픽 기술들은 셰이더를 통해서 작성되기 때문에, 많은 연습을 해두는 편이 좋습니다. DirectX11의 경우, Shader Model 5.0을 사용하고 있기 때문에, DirectX11을 사용한다면 공부할 필요가 있겠구요. 요즘 모바일 기기들도 OpenGLES 2.0을 거의 기본적으로 적용하고 있기 때문에, GLSL 문법을 통해서 셰이더 처리를 하고 있습니다. 

4. 그 밖에

3D를 다루기 때문에, 당연히 수학적 지식은 굉장히 중요합니다. 회사에서 딱히 수학을 잘하냐? 라고 물어보지는 않지만, 수학적인 지식이 없다면, 분명히 한계에 부딪힐 수 밖에 없기 때문에 시간이 날 때 미리 미리 공부를 해두는 편이 좋습니다. 

또, 좋은 그래픽을 볼 줄 아는 눈도 연습을 통해서 키워나가는 것이 중요합니다. 저도 이 부분에 대해서 고민이 많았는데, 사진을 통해서 많은 도움을 받을 수 있다고 합니다. 좋은 그림, 좋은 영상과 주변 배경 등을 평소에 잘 관찰하는 것이 많은 도움을 줄 수 있습니다. 요즘에는 Pinterest와  유투브 같은 서비스를 이용해서 참고할만한 소스들을 스크랩해두는 것도 많은 도움이 되네요. 또, 주변에 아티스트분들과 친분을 쌓아두는 것도 매우 좋습니다. (아트님들 굽신~ 굽신~ 저 점 잘 봐주세요~ 헤~)

그래픽스 프로그래머는 사실 진입 장벽이 꽤 높습니다. 요즘과 같이 게임의 그래픽 기술들이 발전하면서, 알아야 할 것들도 많아졌고, 공부해야 할 것들도 많습니다. 거기에 발전속도 또한 상당히 빠릅니다. 그렇기 때문에, 꾸준히 공부를 하고, 만들어보고 관심을 가지는 방법 말고는 특별히 방법이 없군요. 그래픽스 프로그래머를 하고자 하시는 분들이시라면, 대학원에서 그래픽스과정을 공부해보시는 것도 추천해드립니다. 

마지막으로, 여기 그래픽스 프로그래머가 된 사람의 인터뷰를 보시는 것도 도움이 될 듯 하네요. 
- "위대한 게임의 탄생 2"에 김포프님의 "생생한 그래픽스 프로그래머 인터뷰"가 담겨 있으니, 꼭 한번 읽어보시기 바랍니다. 
- So You Want to be a Graphics Programmer (http://www.altdevblogaday.com/2011/05/10/so-you-want-to-be-a-graphics-programmer/)


맺음말

얼마 전부터 주변에서 "엔진 프로그래머 좀 구해줘~"라는 이야기를 참 많이 듣습니다. 어느 순간부터 주변에서 엔진을 개발하거나 엔진 관련한 일을 하시던 분들이 많이 줄어들고 있습니다. 반대로, 게임 엔진을 개발하고 싶거나 그래픽스 관련한 일을 하고 싶어하는 개발자들이 자신이 좋아하는 업무를 선택할 수 있는 기회도 예전에 비해서는 많이 줄어들었습니다.

그렇지만, 아직도 많은 곳 (IMC Games, 마이에트, 스쿼드플로우, 마비노기2 등)에서 자체 엔진을 개발하고 있고, 수 많은 그래픽스 프로그래머와 아티스트들이 좋은 그래픽을 만들기 위해서 많은 노력을 하고 있습니다. 그리고, 국내에는 생각하신 것보다 매우 뛰어난 그래픽스 프로그래머들이 많이 계십니다. (외부로 많이 드러나지 않아서 그렇지요... 티스?! ㅎㅎ)

상용엔진을 사용한다고 하더라도 엔진 내부를 자신의 게임에 맞는 결과를 만들기 위해서 그래픽스 프로그래머들과 아티스트들이 엔진 수정과 기능 추가등을 진행해야 합니다. 아직도 그래픽스(엔진) 프로그래머를 필요로 하는 곳이 많습니다. 

게임에서 뛰어난 그래픽을 만들어 낸다는 것은 굉장히 흥분되는 일이고, 매우 즐거운 일입니다. 저도 언젠가는 언리얼 엔진에 버금가는 결과물을 한번 만들어보고 싶군요!!! 그 날이 오겠죠?! ㅎㅎ

이런 흥분되는 일에 관심이 있으신가요?! 도전하세요!  많은 곳에서 여러분들을 기다리고 있다니깐요~!!! 우힛~!


이 글은 사실 핑속님의 "테크니컬 아티스트란?" 을 읽고, 전반적으로 업계 직군에 대해서 한번 정도 싹 소개해보면 어떨까? 라는 생각에 비록 제가 아직 직군을 소개할 정도가 되는 것은 아니지만, 용기내어 적어보았습니다. 하고 싶은 말이 많았는데, 두서 없이 주절거렸군요. 혹시 궁금한 것들이 있으시면 질문하시면 포프님이 최대한 답변해드릴거에요~ ㅎㅎ 그럼 꾸벅~


반응형
,
Posted by 친절한티스



지난 글에서 WPF에 프로퍼티 그리드를 적용하는 방법을 살펴 보았습니다. 이 것으로 다양한 속성 값들을 툴에서 컨트롤 할수 있게 되었죠. 그런데 앞서 언급했던 것처럼 여러 속성 값들의 타입이 각자 제각각입니다. 그 중 많이 쓰이는 타입 중 하나가 벡터3 ( Vector3 )이 있습니다. 위치를 나타낼때 많이 쓰이는 이 타입의 값을 프로퍼티 그리드에서 어떻게 표시해야 될까요? 아마 밑의 그림과 같이 표시할 수도 있을 겁니다.



벡터3의 x, y, z 성분들을 각각 하나의 값으로 보고 값을 입력 받을수 있도록 하는 것이죠. 하지만 벡터3의 타입이 많이 쓰이게 된다면 관리 해야될 속성값이 기하급수적으로 늘어나게 될 것입니다. 다른 방법은 없을까요?


보통 엔진단에서 보면 벡터를 하나의 타입으로 간주하고 사용을 합니다. 툴에서도 같은 식으로 써야 논리적으로 맞을것 같습니다. 벡터 클래스를 하나 만들기로 하죠.

// 간단한 벡터3 타입 클래스
public class Vector3
{
    public float m_fX;
    public float m_fY;
    public float m_fZ;

    public Vector3()
    {
        m_fX = 0;
        m_fY = 0;
        m_fZ = 0;
    }
}

그리고 이 클래스를 프로퍼티 그리드에 등록하겠습니다.

// 중복 코드는 생략
public class Person
{
    private Vector3 m_vLocate;

    [Category("기타등등")]
    [DisplayName("위치")]
    public Vector3 locProp
    {
        set { m_vLocate = value; }
        get { return m_vLocate; }
    }
}

한번 실행해 보죠.



프로퍼티 그리드에 등록은 되는 것 같습니다. 하지만 값을 입력할라 치면, 아래와 같은 오류 메시지가 뜹니다.



이유는 입력한 값을 Vector3 타입으로 변환 할수 없기 때문입니다. 즉, 입력한 값 ( 보통 문자열 )에서 Vector3 타입으로 변환해 줄수 있는 변환기가 필요하다는 것 입니다. 그렇다면 변환기를 만들어주도록 하죠.

// Vector3 변환기
public class Vector3Convter : TypeConverter
{
    // string 으로 부터 변환이 가능한가?
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(string))
        {
            return true;
        }
        return base.CanConvertFrom(context, sourceType);
    }

    // string 으로 부터 vector3로 변환
    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        if (value is string)
        {
            string[] v = ((string)value).Split(new char[] { ',' });
            return new Vector3(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]));
        }
        return base.ConvertFrom(context, culture, value);
    }

    // vector3 에서 string으로 변환
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string))
        {
            return ((Vector3)value).m_fX + "," + ((Vector3)value).m_fY + "," + ((Vector3)value).m_fZ;
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
}

변환기를 만들어 주기 위해서는 TypeConver 클래스를 상속 받아 CanConvertFrom, CanvertFrom, CanConvertTo, ConvertTo 4가지 함수를 재정의 해주어야 합니다. 여기서는 CanConvertTo가 그닥 필요치 않아 생략 되었습니다( 자세한 내용은 MSDN 참조 ). 변환기 클래스를 만들었으면 이제 벡터3 프로퍼티에 변환기를 등록해줍니다.

// 중복 코드는 생략
public class Person
{
    private Vector3 m_vLocate;

    [Category("기타등등")]
    [DisplayName("위치")]
    [TypeConverter(typeof(Vector3Convter))] // 변환기 등록
    public Vector3 locProp
    {
        set { m_vLocate = value; }
        get { return m_vLocate; }
    }
}

이제 프로퍼티 그리드에서 벡터3 타입이 제대로 입출력 되는지 확인해보도록 하죠.



실행을 해보니 벡터3의 기본값인 0, 0, 0이 출력됩니다. 그리고 10, 55, 38을 입력하면, 아까와 같은 오류 없이 제대로 입력이 됩니다. ( 그외 맞지 않는 문자열등이 입력될 때를 대비해 변환기에서 예외처리를 해주면 좋습니다 )


이처럼 변환기를 통해 기본 타입 이외의 타입들을 관리 할수 있는 방법을 알아봤습니다. 하지만 여기서 좀더 유저 편의성을 높일수 있는 방법이 있습니다. 예로 특정 정수값이 있는데, 이 값은 0 ~ 100까지 값을 가집니다. 그런데 이 값을 직접 입력받는 것이 아니라 슬라이더 컨트롤러(Slider)를 이용해 값을 정할수 있다면 어떨까요? 사용자 입장에서도 꽤나 편리 할것 같습니다.


프로퍼티 그리드에 기본적으로 제공되는 기본 컨틀롤러 이외의 커스텀 컨트롤러를 추가하는 방법을 알아보겠습니다. 위에 이야기한 슬라이더 컨트롤러를 추가해보도록 하죠.

public class CustomSlider : ExtendedPropertyValueEditor
{
    public CustomSlider()
    {
        // Template for normal view
        string template1 = @"
            <DataTemplate
                xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
                xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
                xmlns:pe='clr-namespace:System.Activities.Presentation.PropertyEditing;assembly=System.Activities.Presentation' 
                xmlns:wpg='clr-namespace:PropertyGrid;assembly=PropertyGrid' > 
                <DockPanel LastChildFill='True'>
                        <TextBox Text='{Binding Path=Value.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}' Width='40' TextAlignment='Center' />
                        <Slider x:Name='slider1' Value='{Binding Path=Value.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}' Margin='2,0,0,0' Minimum='{Binding Value.Min}' Maximum='{Binding Value.Max}' />
                </DockPanel>
            </DataTemplate>";

        // Load templates
        using( var sr = new MemoryStream( Encoding.UTF8.GetBytes( template1 ) ) )
        {
            this.InlineEditorTemplate = XamlReader.Load( sr ) as DataTemplate;
        }
    }
}

먼저 ExtendedPropertyValueEditor 클래스를 상속 받아 CustomSlider 클래스를 만듭니다. 그 다음 XAML을 통해 슬라이더 컨트롤러 DataTemplate을 구성합니다. 여기서 끝이 아닙니다. 이 컨트롤러와 바인딩할 객체가 있어야합니다. 즉, 슬라이드를 움직이면 그 슬라이더 값이 저장될 곳이 있어야 합니다. 위 코드를 보면 미리 Binding Value.xxx 형태로 바인딩이 적용되어있습니다. 


슬라이더 컨트롤러의 경우 현재값, 최소값, 최대값, 단계값 총 4개의 값이 필요합니다. 이를 위해 따로 슬라이더 컨트롤러를 위한 클래스를 하나 정의해주도록 하겠습니다.

public class SliderValue<T>
{
    public SliderValue(T value, T min, T max, T step)
    {
        Value = value;
        Min = min;
        Max = max;
        Step = step;
    }

    public T Value { get; set; }
    public T Max { get; set; }
    public T Min { get; set; }
    public T Step { get; set; }
}

이제 실제로 슬라이더 컨트롤러를 프로퍼티 그리드에 사용해보도록 하겠습니다.

// 중복 코드 생략
public class Person
{
    private SliderValue<int> m_iAge;

    public Person()
    {
        m_iAge = new SliderValue<int>(22, 0, 100, 1);
    }

    [Category("인적사항")]
    [DisplayName("나이")]
    [Editor(typeof(CustomSlider), typeof(PropertyValueEditor))]
    public SliderValue<int> ageProp
    {
        set { m_iAge = value; }
        get { return m_iAge; }
    }
}



실제 슬라이더 컨트롤러가 적용된 스샷입니다. 슬라이더를 움직이면 0 부터 100까지 값을 조절할수 있고, 직접 값을 입력하면 슬라이더 컨트롤러도 같이 움직이는 것을 확인할 수 있습니다. 단, 직접 입력시에는 0~100까지 제한이라는 예외처리가 안되어있기 때문에 이를 위해서는 추가 작업을 해주어야 합니다.


위의 슬라이더 컨트롤러 XAML을 살펴보면 UpdateSourceTrigger=PropertyChanged 라는 구문이 있습니다. 이는 값이 변경 되었을때 PropertyChanged 이벤트를 날려주기 위함이지요. 이 이벤트를 받는 방법을 구현해보도록 하겠습니다. PropertyChanged 이벤트를 받기 위해서는 INotifyPropertyChanged 클래스를 상속 받아야 합니다. 앞서 구현한 SliderValue 클래스를 조금 수정하도록 하겠습니다.

// 중복 코드 생략
public class SliderValue<T> : INotifyPropertyChanged
{
    private T mValue;
    public T Value
    {
        get
        { 
            return mValue;
        }
        set
        {
            mValue = value;
            OnPropertyChanged("Value");
        }
    }

    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion
}

이제 슬라이더 컨트롤러를 통해 값이 변경 되면 PropertyChanged 이벤트를 받을수 있게 되었습니다. 슬라이더 컨트롤러를 사용하는 Person 클래스에 슬라이더 컨트롤러의 PropertyChanged 이벤트를 받을수 있는 콜백 함수를 등록 하면 슬라이더 컨트롤러의 값이 변경 되었을 때의 작업을 수행할 수 있습니다.

public class Person
{
    public Person()
    {
        m_iAge = new SliderValue<int>(22, 0, 100, 1);
        m_iAge.PropertyChanged += new PropertyChangedEventHandler(SliderPropChanged);
    }

    // CustomSlider에서 값이 변경 되었다~
    private void SliderPropChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e != null)
        {
        }
    }
}


반응형
,
Posted by 알 수 없는 사용자

해마다 상장기업이 반기 또는 연말 결산 재무제표를 공시하는 시즌이 되면 각 기업이 발표한 사업보고서와 재무제표 등을 토대로 여러 가지 뉴스들이 나옵니다. 이 가운데에서도 모든 업계인들의 어그로를 관심을 끄는 것이 바로 '평균연봉'인데요. 그래서 이번 포스팅에서는 상장기업의 공시자료를 토대로 산출되는 '평균연봉'의 진실에 대해서 이야기해 볼까합니다.

이런 분이 게임업계로 오시면 연봉이 좀 오르려나요?


도대체 '급여'가 뭐야?

얼마전 게임조선을 통해서 상장 게임사들의 공시자료를 통해 업체별 '평균연봉'을 비교하는 기사가 나온 적이 있었습니다. 그래서 연간 급여총액이 522억원이고 직원수가 1,055명인 넥슨은 평균연봉이 4,950만원으로 집계됐고, NHN은 물론 게임부문뿐만 아니라 네이버를 포함한 회사 전체의 급여액이긴 하지만 어쨌거나 7,405만원이라는 꿈의 수치가 나왔죠.

 

하지만 언뜻 그럴듯 해보이는 이 평균 계산법에는 회계적인 관점에서 볼 때 많은 오류를 안고 있습니다. 흔히들 급여액이라고 하면 그냥 '월급'이나 '연봉'을 떠올릴 것입니다. 하지만 2011년부터 상장기업에게 한국채택 국제회계기준(K-IFRS) 적용이 의무화되면서 '급여'를 회계적으로 처리하는 기준이 예전에 비해 크게 변경되었습니다. 어차피 논란이 되고 있는 급여 자료가 상장기업의 2011년도 기준이니까 K-IFRS에서 규정한 '종업원급여'에 대해서 간단히 짚고 넘어가보죠.

한국채택국제회계기준 (K-IFRS)
국제회계기준(IFRS: International Financial Reporting Standards)에 맞춰 제정된 새로운 회계기준으로, 우리나라에서는 2011년부터 모든 상장기업이 의무적으로 K-IFRS를 적용해야 한다. 비상장기업 등 K-IFRS를 적용하지 않는 기업은 현행 일반기업회계기준(GAAP: Generally Accepted Accounting Principles)에 따른다.

K-IFRS 1019호에서 규정하고 있는 종업원급여에서는 단기종업원급여, 퇴직급여, 기타장기종업원급여, 해고급여 등 총 네 가지 범주로 직원의 급여를 구분합니다. 이 가운데에서 퇴직급여해고급여는 그 명칭만 봐도 뭔지 대충 알 수 있겠죠? 그런데 단기종업원급여는 명칭에서 느껴지는 것처럼 '잠깐 다니는 계약직'에게 주는 급여라는 뜻이 아니라, 회계기간 말(보통 12월 31일)을 기준으로 12개월 이내에(즉 내년 연말 이내) 지급기일이 도래하는 종업원급여(해고급여 제외)를 의미합니다. 회계기준서에 나와있는 표현이다보니 이거 참 말이 어렵죠.

통상적으로 단기종업원급여란 그냥 보통의 월급이나 상여금, 그리고 유급휴가(를 금액으로 환산한 액수) 등을 생각하시면 됩니다. 반면에 기타장기종업원급여는 장기근속급여 및 장기근속휴가, 그리고 1년 이후에 지급할 상여금 등을 말합니다. 즉 한국채택국제회계기준의 종업원급여에 나오는 단기 또는 장기라는 표현은, 직원의 근무기간이나 근속기간이 아니라 급여의 지급기일이 단기(향후 1년 이내)냐 장기(향후 1년 이후)를 뜻하는 것입니다.

그런데 문제는 상장기업의 사업보고서에 나오는 직원 급여액은 위에서 언급한 네 가지 종업원급여 항목에서 어떤 것이 포함되었는지를 정확히 알 수가 없다는 점입니다. 예를 들어 엔씨소프트의 사업보고서를 보면, 연간급여총액은 급여, 상여금 등을 포함하고 있다고 나와있는 반면에, 네오위즈게임즈와 컴투스는 퇴직급여 또는 퇴직급여충당금이 포함되어 있다고 되어 있으며, 엠게임은 '현재 재직중인 직원'에 대한 지급총액이라고 말하고 있습니다. 각 상장 게임사별 연간급여총액에 대한 부연설명을 아래에 표로 정리하였으니 한 번 보시죠. (2011년 사업보고서 기준)

 기업명

 연간급여총액에 대한 부연설명 
 엔씨소프트

 급여총액은 위 인원에 대한 급여 및 상여금 등으로 구성되어 있음.

 NHN

 연간급여 총액에는 급여 외에 상하반기 인센티브 등의 수당이 포함 

 네오위즈게임즈

 연간급여 총액에는 당기 중 지급된 각종 수당, 급여, 상여금 및 퇴직급여 충당금이 포함된 금액입니다.

 엠게임

 연간급여총액은 공시작성기준일 현재 재직중인 직원에 대한 지급총액입니다

 한빛소프트

 자세한 설명은 생략한다

 게임빌

 급여작성기준: 급여와 상여, 퇴직급여를 포함 

 컴투스

 급여와 임금, 제수당, 퇴직급여를 포함한 금액입니다.

 위메이드

 연간급여 총액은 2011년 1월부터 12월까지의 급여총액입니다.

 와이디온라인

 연간급여총액은 2011년 12월까지 집행된 직원 급여 및 상여 등으로 구성되어 있음.


물론, 위의 부연설명에 퇴직급여가 명시되어 있지 않다고 해서 실제로 연간급여총액에 퇴직급여가 빠졌다고 100% 확신할 수는 없습니다. 그리고 수당이 명시되어 있지 않다고 해서 수당이 정말로 빠진 금액인지도 확실하지 않구요. 그래서 이게 참 애매~ 합니다잉.

일단 게임조선의 기사에서 계산한 '평균연봉'이 높은 기업들은 아마도 연간급여액에 상여금, 수당, 그리고 퇴직급여까지 모두 포함되었을 가능성이 매우 높습니다. 게다가 일부 기업의 경우 미사용 유급휴가도 금액으로 환산하여 포함시켰을 수도 있겠지요. (예: 연간 15일의 유급휴가가 있는 직원이 휴가를 5일만 사용했을 경우 사용하지 않은 10일분의 유급휴가를 금액으로 환산)

하지만 급여를 받는 사람의 입장에서 생각하는 '연봉'은 회계기준상의 '급여'와 차이가 있습니다. 국어사전에서 '연봉'이라고 하면, '일 년 동안에 받는 봉급의 총액'이라고 설명하고 있죠. 그러면 '봉급'은 또 뭘까요? 봉급을 사전에서 찾아보면, '어떤 직장에서 계속적으로 일하는 사람이 그 일의 대가로 정기적으로 받는 일정한 보수'라고 되어 있습니다. 즉, 연봉이란, 일단 '정기적'으로 받아야 하며 금액이 '일정'해야 하는 것이죠. 그래서 이 기준을 적용한다면, 통상적인 급여와는 별도로 받는 '상여금(보너스)'의 경우, 그 금액이 일정하게 보장되어 있고, 또 정기적으로 지급된다면 연봉에 포함되는 것으로 볼 수 있지만, 회사, 또는 직원 개인의 성과에 따라 받는 인센티브 같은 경우는 근로자에게 보장된 금액이 아니라 회사에서 지급여부를 마음대로 결정할 수 있으므로 연봉에 포함되지 않는 것입니다.

 

급여총액을 직원수로 나누면 평균연봉?

앞서서는 '급여'와 '연봉'이 무엇인지에 대해서 살펴보았습니다. 그러면 '평균연봉'의 진실을 한 번 알아볼까요? 위에서 살펴본 게임조선의 기사에 나온 평균연봉은 연간급여총액을 직원수로 나누어서 계산된 평균치입니다. 그래서 넥슨의 경우 급여총액이 522억원이고 직원수가 1,055이니까 1인 평균 4,950만원이라는 결과가 나온 것이죠. 하지만 이런 계산법에는 큰 문제가 있습니다. 왜냐하면, 계산의 근거가 된 2011년 사업보고서상의 직원수는 2011년 12월 31일을 기준으로 재직중인 직원수만 반영한 것인 반면, 연간급여총액은 연중에 퇴사한 직원의 급여까지 반영되어있기 때문이죠. 그렇기 때문에 이런 식으로 계산한다면, 연초에는 직원이 많았다가 구조조정 등으로 중도에 퇴사한 직원이 많은 회사라면 연간 총 급여액은 많은 반면 기말의 직원수는 적으므로 '평균 급여액'이 높게 나올 수 밖에 없습니다. 반대로 연초에는 직원이 적었는데 연말 가까이 와서 직원수가 늘어난 회사의 경우에는 '평균 급여액'이 적게 나오는 것이죠.

즉 이와 같은 계산법의 근본적인 문제는, 직원수는 특정 시점을 기준으로 한 반면에, 급여액은 전체 기간에 발생한 금액을 합산했다는 점입니다. 따라서 보다 정확한 평균을 구하려면 특정 시점의 직원수가 아닌 연간 직원수의 가중평균을 구해야만 합니다. 구체적인 예를 들면 이런 식이죠.

 

 기간중 총급여액

연중 근무일수

 일평균 급여액

 365일 기준 급여액

 직원A

45,000,000 

365일 

123,288 

45,000,000 

 직원B

27,000,000 

284일 

95,070 

34,700,704 

 직원C

36,400,000 

310일 

117,419 

42,858,065 

 직원D

5,720,000 

76일 

75,263 

27,471,053 

평균

37,507,456 


즉, 각 직원들마다 연중 근무일수가 다르고 급여는 근무일수에 비례하여 지급하는 것이므로 이렇게 각 직원별 근무일수를 감안한 평균을 계산해야만 보다 정확한 '평균연봉'이란 걸 구할 수 있는 것입니다. 사실 회계에서는 이와 같은 가중평균을 상당히 자주 사용합니다. 회계나 재무관리를 좀 공부해보신 분들은 알고 있겠지만, 자본화 차입원가라든지, 가중평균유통주식수, 가중평균 공헌이익률, 가중평균지본비용(WACC) 등등 뭐 어지간한 평균을 구하는 것들은 거의 가중평균을 사용하죠.

 

업계 평균연봉의 허상

지금까지 알아본 바와 같이, 소위 말하는 '업계 평균연봉'은 다음과 같은 두 가지 문제점을 가지고 있습니다. ① 각 기업에 발표하는 급여액'이 각 기업별로 계산하는 방법이 다르고, 또 통상적으로 '연봉'이라 할 수 없는 금액들까지 포함되어 있는 경우가 많다. ② 상장기업의 사업보고서에 나오는 평균연봉의 계산방식(연간총급여액÷연말직원수)은 정확한 평균연봉 계산법이 아니다.

그리고 또 하나 감안해야 할 점이 있습니다. 통상적으로 경력 연차가 높을수록 연봉도 높아지게 마련인데, 각 기업의 사정에 따라 평균적인 경력 연차가 다를 수 밖에 없겠죠. 쉽게 말하면, 10년차 이상의 베테랑 위주로 구성된 회사와 신입~5년차 미만 위주로 인력이 세팅된 회사와는 수치상의 평균연봉이 당연히 차이날 수 밖에 없는 것입니다.

단적인 예로, 얼마전에 보도됐던 '업종별 대표 중소기업 평균연봉'에서 저희 회사의 모기업인 오로라월드가 평균연봉 4600만원으로 동종업계 경쟁사인 ㅅㅇㄱ의 2755만원에 비해 무려 67%나 높다는 기사가 나온적이 있었는데요. 기업 내부 사정을 잘 모르는 사람은 단순히 이 수치만 보고 '오로라월드가 연봉을 많이 준다'라고 생각하겠지만(뭐 실제로 많이 주는 것일수도 있습니다), 오로라의 경우 비교적 급여액이 적은 생산직, 판매직 등은 별도의 해외법인이나 자회사 소속으로 되어 있고 본사에는 관리직과 전문 디자이너 위주로 근무하고 있어서 평균연봉이 높게 계산되는 것일 수도 있습니다. (관련기사 링크: http://www.asiatoday.co.kr/news/view.asp?seq=622670 )

즉 이와 같이 대중에게 공개된 정보만 가지고는 각 기업의 정확한 평균연봉을 알 수는 없으며, 설사 그 정보가 정확하다 하더라도 각 기업별로 인력 구성이 다르기 때문에 단순히 수치의 크고 작음만 가지고 어느 회사의 연봉이 더 짜거나 후하다고 단순 비교를 할 수는 없다는 점을 감안해야 합니다. 그러니까 뉴스나 공시자료에 나오는 회사별 평균연봉을 보고 자신의 연봉이 다른 회사 평균보다 작다고 해서 무조건 실망하실 필요는 없습니다. 그냥 그런 자료에 나오는 수치들은 어차피 정확한 평균연봉이 아니니까 그냥 단순 참고용으로만 보시고 자신의 실력으로 연봉을 높여가시기 바랍니다. 그럼 여러분의 연봉에 포스가 함께 하시길 바라며, 다음 번엔 더욱 유익한 포스팅으로 '자주' 찾아뵙도록 할게요~

 

반응형
,