꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 알 수 없는 사용자
원래 게임기획 카테고리를 연재하다가 '회계 포스팅의 맛을 알아버린(!)' 스톰입니다. 포프님은 빨리 원래 연재하던 시스템 기획 관련 글을 올리길 내심 원하시는 것 같지만 일단 이번 편 마저 올리고 스케쥴대로 27일에 기획 포스팅을 올릴까 합니다.


예산계획의 시작 - 계정과목의 이해

여러분이 15명 정도 되는 개발팀의 예산을 관리하는 책임자라고 가정해봅시다. 그리고 이 팀을 1년 동안 꾸려나가는데 얼마나 많은 자금이 필요할지 예산계획을 수립해보도록 하죠.

예산계획을 수립하려면 우선 어디에 어떻게, 얼마나 많은 자금이 필요한지를 따져봐야 합니다. 회계에서는 이렇게 자금이 들어오고 나가는 항목을 '계정과목'이라는 이름으로 분류합니다.

네이버 사전 같은데서 계정과목이라는 용어를 찾아보면, 부기상의 계산단위가 어쩌고 저쩌고 하는데, 사실 그 설명이 더 어렵습니다. 그냥 간단하게 돈이 들락날락거리는 카테고리라고 생각하면 됩니다. 대표적인 예로, 직원들의 월급액수는 보통 '급여'라는 계정과목에 기입하고, 게임을 알리기 위해 돈을 들여 배너 광고를 걸었다면 '광고비'라는 계정과목에 액수를 써넣을 수 있겠죠.

그런데 계정과목을 어떻게 나누어야 할지에 대해서는 딱히 정해진 규칙이 없습니다. 각 회사마다 자유롭게 분류하고 아무 명칭을 붙여도 법적으로는 문제가 없는 거죠. 물론 일반적이지 않은 형식으로 계정과목을 나누고 이름을 붙이면, po세무소wer에서 싫어하기도 하고, 회사 내에서도 돈이 어떻게 쓰이고 있는지 알아보기 힘들기 때문에 보통은 보편적인 계정과목으로 분류합니다. 다만 계정과목을 얼마나 세세하게 분류하느냐는 회사마다 천차만별인데, 세세함의 정도는 각 회사마다 회계처리 및 경영적 판단에 맞게 선택하면 됩니다.



예를 들어, 임직원 급여, 일용직(비정규직) 급여, 상여금 등을 각각 별개의 계정과목으로 나누어서 관리할 수도 있지만, 이것을 그냥 묶어서 급여라는 한 계정으로 관리할 수도 있습니다. 아니면 아예 복리후생비, 직원식대, 야근식대, 축조의행사비, 용역비 등과 합쳐서 인건비라는 큰 계정 하나로 관리할 수도 있습니다. 하지만 다양한 지출요인을 하나의 큰 계정과목으로 묶어서 관리하면, 정확하게 어떻게 자금이 사용되는지 알아보기가 어렵기 때문에 회사 사정에 적합하게 어느 정도는 세분화를 시켜주는 편이 실무적으로는 더 바람직합니다. 여러분의 이해를 돕기 위해 계정과목 분류의 예시를 표로 정리해보았습니다. 아마 보시면 대략 어떤 건지 쉽게 이해하시리라 봅니다.




인건비, 급여액이 전부가 아니다!

그러면 인건비 예산부터 먼저 잡아봅시다. 15명의 평균 월급여액이 230 만원이라고 가정하면, 1년 동안 필요한 급여예산은 230만원에 15명을 곱한 다음 12개월을 다시 곱한 4억 1400 만원입니다. (사실 회계를 담당하는 사람은 이런 표현보다는 414백만원이나 414,000천원 같은 표현에 더 익숙하지만 이 글을 보시는 분들에겐 조금 생소할 것 같으니 그냥 일반인용(?) 표현 위주로 갑니다)

그러면 1년에 4억 1400만원만 있으면 15명의 직원을 고용할 수 있는 걸까요? 물론 아닙니다. 이것은 단지 '급여총액'일 뿐이죠. 직원을 고용해서 유지하려면 다음과 같은 비용이 더 추가됩니다.

  • 건강보험(요양보험 포함), 고용보험, 산재보험, 요양보험, 연금보험(국민연금) - 속칭 4대보험
  • 커피, 음료 등 간식비, 야근식대, 회식비
  • 직원 생일, 경조사 발생시 지급되는 비용
  • 체력단련비, 진료비, 보육비, 사원여행비 등

위와 같은 비용들을 통틀어서 '복리후생비'라고 합니다. 보통 복리후생비(福利厚生費)라 하면, 종업원의 작업능률을 향상시키고 복리를 증진시키기 위하여 법인이 부담하는 시설이나 일반관리비, 제조경비를 말한다고 뇌입어 백과사전에 써있는데요. 쉽게 말하면 급여 외에 회사가 직원을 위해 지원해주는 비용이라고 생각하시면 됩니다. 이 가운데에서도 특히 4대보험의 경우 일정금액을 회사에서 부담하는 것이 의무이므로 반드시 예산에 편성되어야만 합니다.

여러분의 이해를 돕고자 4대보험의 2012년 기준 요율과 부담율을 표로 만들어보았습니다. 단, 산재보험의 징수비율(요율)은 업종에 따라 다른데(위험도가 높은 직종일 수록 요율이 높음) 여기서는 게임업계에 해당되는 요율을 적용하였고, 국민연금도 상시근로자 150명 이상인 경우 요율이 올라가지만 소규모 기업을 기준으로 하였습니다.


위 표와 같이 급여액을 기준으로 회사가 부담해야 하는 4대보험료 납부액은 9.53%입니다. 그래서 월급이 230만원인 직원이 있다면, 본인 부담금이 약 19만원이고, 회사부담금은 약 22만원인 셈이죠. 즉 예산을 편성할 때에는 이 부담금을 급여액의 10% 이상 여유있게 잡아놓아야 합니다. 따라서 15명의 1년치 급여총액 4억 1400만원의 10%인 4,140만원을 이와 같은 4대보험료 회사 부담금으로 준비해놓아야만 하는 것이죠.

여기서 잠깐!!!          
  • 실무적으로는 직원에게 급여를 지급할 때 근로자부담 금액만큼을 공제한 후 남은 금액을 지급하고 공제한 금액은 보통 '예수금'이라는 계정과목으로 보관해둡니다. 예수금이란 '미리 받아둔 돈'이란 뜻으로, 장차 누군가에게 지급할 돈을 일시적으로 보관하는 경우에 사용하는 계정입니다. 즉 지불 의무가 있기 때문에 이 또한 부채인 셈이죠.

  • 4대보험료는 급여를 지급한 월의 다음 달 10일까지 납부해야 합니다. 즉 1월에 직원들에게 지급한 급여에 대한 4대보험 및 국민연금 납부는 2월 10일 납기마감인 거죠. 그리고 납부할 때에는 직원 급여에서 공제해놓은 예수금에 회사부담금을 합쳐서 납부하게 됩니다. 예를 들어 납부할 금액이 150만원이고, 예수금이 70만원(근로자 부담금)이라면 회사 입장에서는 80만원의 비용이 들어가는 셈이죠.




방심하면 쓰나미가 되는 복리후생비 

사실 소규모 개발팀에서 예산계획이라고 해봐야 보통은 뻔합니다. 위에 올려놓은 계정과목의 예시를 보면, 대부분 큰 돈이 나가는 계정은 고정비이거나 '고정비 성격이 강한 비용'이기 때문이죠. 

고정비(固定費)
일정한 기간 동안 업무의 양이나 생산규모에 관계 없이 항상 일정액으로 발생하는 원가(비용). 반대의 개념으로는 변동비(變動費)가 있다.

예를 들어, 매달 꼬박꼬박 나가는 비용 가운데에서 급여, 사무실 임대료, 각종 렌탈비, 관리비 등은 거의 일정하거나 변동이 있더라도 고만고만 합니다. 이런 비용은 낭비할 여지가 별로 없고 절감하기에도 한계가 있는 비용이죠. (굳이 절감한다면 직원수를 줄이고 사무실을 더 저렴한 곳으로 이전하는 등의 방법이 있겠지만...) 그런데 초보 사장의 스타트업 개발사에서 의외로 많은 낭비가 발생하는 곳이 있으니, 그것이 바로 복리후생비입니다.

앞서 알아본 4대보험과 국민연금의 회사부담금은 통상 복리후생비 - 공과금 계정으로 관리합니다. 이 비용이야 뭐 급여총액에 비례해서 자동으로 결정되기 때문에 직원을 필요 이상으로 많이 고용하지 않는 한 낭비든 절감이든 마음대로 할 수가 없죠. 문제는 공과금을 제외한 나머지 복리후생비에서 발생합니다.

예전에 이런 신생 개발사가 하나 있었습니다. 20대의 아주 젊은 대표가 투자를 받아 차린 회사였는데, 회사 홈페이지에서 소개한 복리후생이 대략 이랬습니다. ▲아침, 점심, 저녁 식사 무료 제공 ▲각종 스낵, 음료 등 풍부한 간식거리 자유이용 ▲전직원 한약 제공 ▲체력단련비(헬스장) 지원 ▲해외여행비 지원 ▲빈번한 회식(-_-) ▲도서구매비 지원 기타 등등 ...

자, 어떻습니까? 복리후생만 보면 국내 톱클래스 수준이죠? 뭐 물론, 좋은 개발환경을 제공하자는 취지는 좋습니다. 시대가 어느 시대인데 헝그리 정신만 고집할 수는 없는 거니까요. 그러면 한 번 봅시다. 저 정도 복리후생을 제공하려면 비용이 얼마나 드는지 말이죠.

  • 1일 3식 제공: 끼니당 6천원 x 3식 x 15명 x 22일(월간 근무일수) = 594만원 / 월
  • 각종 간식거리: 1인 하루 5천원 x 15명 x 22일 = 165만원 / 월
  • 전직원 한약제공: 1인 하루 3천원 x 15명 x 22일 = 99만원 / 월
  • 체력단련비 지원: 1인당 월 5만원 x 15명 = 75만원 / 월
  • 해외여행비 지원: 1인당 연간 50만원 x 15명 = 62만 5천원 / 월
  • 빈번한 회식: 1인 예산 5만원 x 15명 x 월2회 = 150만원 / 월
  • 도서구매비: 1인 10만원 x 15명 =  150만원 / 월
= 월 평균 약 1300만원 (1년 기준: 1억 5546만원)

자 대충 이정도만 해줘도 매 달 1300만원씩 꼬박꼬박 돈이 나갑니다. 그런데 사실 여기에는 경조사행사비는 포함이 안 되어 있죠. 명절 선물비용이나 귀향비, 생일축하금 등등까지 감안하면 월평균 1400만원 이상은 될 겁니다. 직원 15명을 기준으로 계산한거니까 1인당 매월 100만원에 가까운 비용이 더 들어가는 셈이죠. 평균 월급여 230만원이라고 가정했으니까 대략 급여의 40% 정도 됩니다.

자 그러면, 월급여총액(3450만원)에 4대보험 등 공과금(345만원)을 더하고 나머지 복리후생비(1400만원)를 합하면 대략 매월 5200만원 쯤 되는군요. 연간으로 치면 벌써 6억이 훌쩍 넘어갑니다. (위에 예로 든 저 개발사도 결국 게임 못나오고 돈 펑펑 쓰다가 풍비박산났죠)

여기에 사무실 임대료, 관리비, PC 등 각종 비품, 소프트웨어 등등도 다 구매해야 한다고 가정하면 첫 해에 드는 비용은 금세 10억을 넘길 수 있습니다. 무섭죠? PC 클라이언트/서버 기반(C/S기반)의 온라인 게임을 연인원 15명 규모로 2년 동안 개발한다치면 딱히 낭비를 하지 않아도 그냥 15억 20억 까먹는 겁니다. 그런데 아시다시피 15명 규모면 C/S 기반의 게임 개발에서는 아주 작은 사이즈죠. 그러니까 게임 사이즈가 조금만 커지면 50억 100억 가는 게 순식간이라는 뜻입니다.

뭐 물론, 복리후생비 문제는 논란의 여지가 있습니다. 경영자 입장에서는 소규모 신생 개발사로서 좋은 인재를 채용하려면 대기업 못지 않은 직원 처우가 필요하다고 생각할 수 있고, 직원 입장에서도 마찬가지일테니까요. 하지만 (이후 포스팅에서 자세히 이야기 하겠지만) 게임이 출시될 때까지 돈 한푼 벌지 못하는 것이 개발사의 자금사정이라는 사실을 감안한다면, 예산 관리자는 이러한 비용들이 현재의 상황에서 감당할 수 있는지를 체크해야하고 만약 자금의 부족이 예상된다면 미리미리 그에 대비할 필요가 있습니다.

* * * * * * * * * * * * * * * * * * * *
 
이번 회에서는 예산계획의 기초인 계정과목과 더불어 급여 및 복리후생비에 대해서 알아보았습니다. 이어서 다음 회에서는 원래는 예산관리 이야기가 끝나면 자금조달, 투자유치 등을 다루려고 했으나, 야치주님의 포스팅과 겹치는 부분이 있을 것 같아 예산 이야기가 끝나면 게임회사의 재무제표 읽는법을 써볼까 합니다. 그럼 여러분들 27일 금요일에 기획 포스팅으로 다시 만나요~
반응형
,
Posted by ozlael

들어가며 

앞서서 입체 영상의 휴먼 팩터에 대하여 말씀 드렸습니다.이 휴먼 팩터는 영상 왜곡과 합쳐지면 더 많은 부작용을일으키게 된다고 말씀 드렸었는데요, 이번에는 그 영상 왜곡에 대하여 말씀 드리고자 합니다.

아시다시피 입체 영상은 좌안의 영상과 우안의 영상을 따로 만들어 내서 각각의 눈의 망막에 각각의 영상을 보여 주는 방법으로 구현되지요. (보여주는 방법은 이 글에서는 다루지 않겠습니다. 무안경 방식은 이 글을 참고하세요.)

이 좌안 우안을 신경쓰지 않고 만들다보면 많은 영상 왜곡(Image Distortion) 현상이 일어나기도 합니다. 이러한 왜곡은 심하게는 좌우 영상이 합쳐지지 않고 깨져보이는 지경까지도 이르게 됩니다. 이 글에서는 몇 가지의 영상 왜곡에 대해 다루어 보도록 하겠습니다. 


예측 불가능한 제시 조건에 의한 왜곡

모니터(스크린)의 크기, 시청자의 위치, 시청 각도 등 예측 할 수 없는 제시 조건에 의한 왜곡들도 발생을 합니다. 컨텐츠 제작시 예측했던 스크린의 크기나 시청 거리에서 어긋나게 되면, 상의 깊이감이 변하거나 폭이 왜곡 되는 것이죠.
3D 영화관의 경우 극장마다 상영관마다 스크린의 크기가 다릅니다. 상영관마다 입체 영상의 폭이 다르게 느껴진다는 것이지요. 또한 같은 오브젝트라도 앞좌석에서 보는 깊이감과 뒷좌석에서 보는 깊이감이 다르게 느껴지게 됩니다.


또한, 정면이 아닌 측면에서 관람하게 되면 상이 정면을 향하는 것이 아니라 관찰자가 있는 측면을 향해 있는 것 처럼 느껴지게 됩니다. 영화 상영관 내에서도 좌석의 위치에 따라서도 보여지는 상이 왜곡 될 수 있는 것이죠. 이러한 현상을 전단왜곡(shear distortion)이라 부릅니다.


 흔히들 영화에 따른 영화관의 명당이 있다고들 하는데, 괜히 나온 말이 아니라 이러한 이유들 때문에 정말 명당이 존재하는 것이죠. 영화의 경우는 영화관의 크기 차이가 그렇게 다이나믹 하지는 않습니다. 시야 각도 허용할 만한 수준 내에서 구성이 되도록 자리가 배치되어 있지요.
 하지만 이러한 왜곡 현상은 게임에서는 심하게 나타납니다. 게임을 즐기는 유저의 모니터 크기는 천차만별입니다. 방에서 22인치 모니터로 게임을 즐길 수 도 있고, 거실에서 55인치 대형 3DTV로 게임을 즐길 수도 있는 것이죠. 시야각 또한 마찬가지입니다. 혼자 게임을 즐긴다면 당연히 정면의 시야각으로 즐기게 되겠지만, 여러명이 모여서 게임을 하고 구경도 하고 한다면 시야각은 다양해지게 될 것입니다. 
 하지만 사실 게임을 제작하면서 이러한 유저들의 모든 시청 스펙을 예측하기란 불가능하지요. 다만, 평균적인 구성을 예측하고 그에 맞는 기본 환경 설정이 되어있어야 할 것입니다.


망막경합 (Retinal Rivalry)

앞서 말씀 드렸듯이 좌우 영상을 좌우 망막에 각각 따로 받아들이고 뇌가 이를 합성하여 입체정보로 만들어 내면서 입체영상이 만들어지게 됩니다. 그런데, 좌측 영상에는 있는 물체가 우측 영상에는 존재하지 않거나 좌측과 우측의 모습이 과도하게 다르다면, 뇌는 이 물체에 대해 제대로 지각하지 못하게 됩니다. 물체가 깜빡이게 보이거나 엉뚱한 깊이에 있게 느껴지게 되지요. 이러한 상황을 망막경합 (Retinal rivalry)  또는 양안경합 (Binocular rivalry) 이라 부릅니다. 예를 들어 아래 이미지에서는 여성과 석상이 망막경합을 일으킵니다.

이미지 출처 :  http://www.ray3dzone.com/Glimmer.html  

이러한 현상은 소위 적청안경이라 불리우는 애너글리프 방식에서는 쉽게 일어날 수 있습니다. 애너글리프 안경의 색깔에 가까운 색으로 이루어진 오브젝트는 한 쪽의 눈에만 전달될 수 있는 것이죠. 만일 게임을 적청안경으로 시연해야 하는 상황이라면, 오브젝트의 색은 완전 적색이나 완전 청색 등은 최대한 피해야 할 것입니다.(근데 그런 시연 상황이면 그냥 입체영상으로 보여주지 말아요)


키스톤 왜곡(Keystone Distortion)

오브젝트가 카메라에 상당히 가까이 있을 시, 그 상의 좌측 영상과 우측 영상의 높이 차가 발생하기도 합니다. 이러한 경우는 두 이미지를 뇌가 하나의 상으로 합치는데 어려움이 따르게 됩니다. 이러한 상황을 키스톤 왜곡이라 부릅니다.

 특히, FOV가 과도하게 설정 되어 있는 경우는 이러한 왜곡이 심하게 발생하게 됩니다.


윈도우 위반 (Window Violation)

좌측 영상의 카메라와 우측 영상의 카메라는 각각의 뷰 프러스텀을 가지게 됩니다. 그러다보니 좌우 카메라의 프러스텀이 공통적으로 속하지 않는 영역이 존재하게 되지요.


 그 부분은 투영된 영상으로 치면 화면의 좌우 각각의 끝부분이 이에 해당합니다. 그래서 화면의 좌우 끝에서는 망막 경합이 일어나게 됩니다. 다음 이미지를 보면 좌측 영상에는 있는 A1 파란 점과 I1 주황색 점이 우측 영상에는 존재하지 않습니다.


이러한 현상을  윈도우 위반(Window Violation)  또는 프레임 위반(Frame Violation)이라 부릅니다. 극장같은 큰 상영관에서는 이러한 현상이 있다 하더라도 보통은 이를 알아채지 못하고 넘어가게 됩니다. 하지만 커봤자 40인치 정도인 모니터에서 즐기게 되는 게임은 이러한 윈도우 위반이 쉽게 인지됩니다.
 nVidia 3D Vision, iZ3D, DDD 등의 입체 영상 미들웨어들은 윈도우 위반을 해결하는 방법을 제공해주기도 합니다. 보통은 프러스템이 겹치지 않는 영상의 끝부분은 잘라내는 극악의 방법이지요. 하지만 이러한 방식은 화면 밖으로 튀어나오는 오브젝트까지 완벽히 커버하지는 못합니다. 그렇기 때문에 화면 안에 완전히 투영되지 못하는 큰 오브젝트가 화면 밖으로 튀어나오면 윈도우 바이얼레이션 문제가 발생을 합니다. 
 컷씬을 제작하다보면 입체 영상에서 극적인 장면을 연출하기 위해 오브젝트를 화면 밖으로 튀어나오게 하고싶은 경우가 있을것입니다. 그러한 경우는 오브젝트를 반드시 화면 가운데 위치시켜야 하고, 화면 영역을 벗어날 정도로 큰 오브젝트는 금지하여야 할 것입니다. 


마치며

이 글에서 설명 드린 왜곡 외에도 수 많은 입체 영상 왜곡 현상들이 존재합니다. 영화나 에니메이션은 이러한 왜곡 현상들을 발생시키지 않기 위해 많은 노력을 하면서 제작을 하고 있습니다. 항상 고정된 영상의 컨텐츠이기 때문에 왜곡 현상을 거의 없앨 수 있지요.
 하지만 게임은 실시간으로 영상을 만들어 내는 컨텐츠이기 때문에 모든 왜곡 현상을 막는다는 것은 불가능에 가까울 것입니다. 다만, 이러한 왜곡 현상에 대해 잘 숙지하고 최대한 예방할 수 있는 방향으로 개발을 해야 할 것입니다. 
 이전 글에도 말씀드렸지만, 게임은 휴먼 팩터에 대한 영향이 크고 입체영상으로 즐기는 것에 대한 부담이 큰 컨텐츠입니다. 그러한 부담을 유저가 조금이라도 덜 느끼고 장시간 쾌적하게 즐길 수 있도록 제작을 해야 할 것입니다.
 

P.S. 글이 하루 늦어져서 죄송합니다. 모두들 새해 복 많이 받으시고 로또맞으세요. 대신 연금복권만 제게 양보해줘요 :-) 
반응형
,
Posted by 알 수 없는 사용자
본의 아니게 업계에 알려진 Rhea君의 건강은 지금은 꽤나 좋아졌습니다.
각종 디스크 상담이 쇄도하고 있습니다.
그러나 아직 아래 사진과 같은 선물을 줄 여자친구를 만들지 못했으므로 조금은 더 쉬어야합니다.
진정한 남자로 선택을 받는 시간이 한달도 채 남지 않았습니다.

진정한 남자로 선택을 받는 시간이 한달도 채 남지 않았습니다.

많은 게임 업체분들이 백수인 저를 위해 사심없고 우정 어린, 밥과 술을 사주셨는데,
현재 삼성동 사옥 돋는 N모사만 밥과 술과 향응을 제공하지 않았습니다.
여전히 성장기라 배가 고픕니다. 참고로 저는 소고기보다 물고기류를 더 좋아합니다(삼성동 오징어세상에서 오징어 통찜이나.).
그리고 백수를 갈취한 선릉역 N모사와 오리역 N모사 직원느님을 고발합니다(...뻥).

1. 네트워크 아키텍쳐
익히 알려져 있는대로, 소켓 속에는 주요 함수가 몇개 없습니다.
클라이언트를 기준으로 하면 연결을 위한 connect(), 주고받는 send()/recv(), 연결해제를 위한 close()뿐입니다.
그러나 많은 게임들은 이 몇안되는 단순한 함수들로 클라이언트/서버(이하 C/S)과 Peer-To-Peer(이하 P2P)를 오가는
마법과 같은 네트워크들을 보여줍니다.

1.1 모든 것은 서버로 C/S
C/S 모델은 100% 서버 지향 네트워크 아키텍쳐입니다.
사용자는 게임 클라이언트 실행시 서버에 의해 게임 플레이가 결정됩니다.

가장 좋은 예는 MMORPG와 맞고/포커같은 카드류 게임입니다.
C/S 모델은 MMORPG가 발전한 우리 나라에서 크게 발전된 모델입니다(기술뿐만 아니라 운영도 포함해서).

C/S 모델의 장점은 보안에 강력하다는 점입니다.
모든 결정이 서버에 의해 판가름나므로 해킹에 강합니다.
만약 해킹을 통해 어떤 짓을 했다고 하더라도 다음날 아침이면 들통납니다.
대부분의 온라인 게임 회사들일 경우, 출근하자마자 볼수 있는 것이 어제 급격히 레벨이 오른 유저의 전적입니다.
당장 제재가 가해지지는 않지만 어김없이 들키고 맙니다.
...나머지는 서버개발자가 아닌, 이름은 친절하지만 사실은 무서운 아저씨들인 고객만족팀에서 해결해주시곘죠.

여기서 한가지 명확하게 짚고 넘어갈 것이 있습니다.

우선 "클라이언트/호스트"와 "클라이언트/서버"의 개념입니다.
우선 우리가 익숙한 PC통신이나 UNIX를 생각해봅시다.
PC통신화면이나 텔넷으로 접속한 UNIX 화면은 100% 서버에서 던져주는 TEXT정보입니다.
이 당시에는 "서버"라는 용어 대신 "호스트(host)"라는 용어를 썼으며 이때는 반드시 로그인(Log in)이라는 용어를 썼습니다.

이 의미는 클라이언트에는 0.1%의 할일도 없으며 모든 것을 서버에서 전해주는 것을 받아 그 안에서 작업한다는 것입니다.
돌이켜보면 PC통신에 사용된 프로그램을 가르켜 단말기 예뮬레이터(Terminal Emulator)라고 불렀습니다.
PC가 아닙니다. 단말기라는 것은 PC가 아니라 Host에 접속해서 Host가 보내주는 화면과 텍스트 기반의 요청을 보내는 것 뿐입니다.

따라서 클라이언트는 Host 속으로 들어간다는 의미에서 로그인(Log in)이라 불렀습니다.

저승 로긴하시는 마미성님

저승 로긴하시는 마미성님


그리고 단말기는 사라지고 PC가 흔해진 시대에 들어서며 클라이언트/서버의 시대가 왔습니다.
클라이언트/서버의 의미는 클라이언트와 서버가 "평등"하다는 의미이며 하나의 작업을 처리하기 위해
클라이언트 컴퓨터와 서버 컴퓨터가 협력해서 작업을 한다는 의미입니다.

그래서 로그온(Log on)이라 부릅니다.

윈도우 역시 Log on/Log off 라는 표현을 씁니다.
이는 커널이라는 서버와 애플리케이션이라는 클라이언트가 협업한다는 의미입니다.
오래전 윈도우95가 등장할때, "본격 32비트 클라이언트/서버 OS"라고 했는데
일부 사람들은 윈도우95에 소켓이 내장되어 그렇게 부른다고 했지만 그게 아니라 커널과 애플리케이션이 통신한다는 의미입니다.

네트워크에서 클라이언트/서버 역시 마찬가지입니다.
웹페이지를 볼까요?
흔히보는 홈페이지 가입할때, 특정 항목을 제대로 적지 않았으면 다시 적으라고 알려줍니다.
이는 서버에서 검사할 수도 있고, 클라이언트에서 자바스크립트로 검사할 수도 있습니다.
어느 쪽이든 가능합니다.

하지만 서버 측에서 검사할려면 일단 데이터를 서버로 넘겨야 합니다.
트래픽 측면을 본다면 자바스크립트로 처리하는게 효율성에서 맞습니다.
그런데 또 문제가 있습니다.

비쥬얼 스튜디오같은 자바스크립트 디버깅 툴만 있으면 자바스크립트에서 처리하는 양식 검사 따위는 아주 쉽게 무시해 버릴수 있습니다!
이렇게 되면 효율성을 무시하고 서버에서 검사를 해야 합니다.
도대체 뭐가 맞는 것일까요?

홈페이지 이야기를 하니 헛갈린다면 게임으로 넘어옵시다.
C/S기반의 슈팅 게임을 있다고 할때 공격 성공 판정을 서버에서 해야할까요, 클라이언트에서 해야 할까요?
서버에서 하자니 매 키입력마다 네트워크를 타야 합니다.
클라이언트에서 하자니 검증이 불가능합니다.

정답은 그때그때 다르다이고 서버에서 처리할 것과 클라이언트에서 처리할 것을 알맞게 나눠야 한다는 것입니다.
앞으로 게임을 만들며 이 문제를 실전에서 다시 짚을 것입니다.

1.2 우리끼리 놀자 P2P
P2P는 유저들끼리 데이터를 주고 받으며 게임을 진행하는 방식입니다. P2P의 대표적인 게임이라면 스타크래프트와 FPS게임들일 것입니다.
그러나 MMORPG 위주의 우리 나라에서는 꽤나 오랜 시간 P2P 기술이 홀대를 받았습니다.
보안에 취약하고 지금처럼 FPS가 유행하기 전까지는 P2P에 어울리는 게임이 없었기 때문입니다.

P2P의 장점은 로비를 제외하면 대용량 서버를 구축할 일이 없다는 것입니다.
단점은 클라이언트가 결국 서버와 클라이언트, 모두를 맡아야 하므로 네트워크 측면에서 만들기가 좀더 어렵다는 점입니다.
특히 P2P는 (플레이어수-1)((플레이어수-1)-1) /2 만큼 연결을 가져야 하므로 사용자가 늘어날때마다 디버깅이 힘들어집니다.
그리고 절대 잊을수 없는 문제가 있지요, 바로 NAT(Network Address Translator)이라고 불리는 공유기 문제입니다.
공유기 아래에 있는 Peer는 서버로 작동할 수 없습니다. 그래서 홀펀칭(hole punching)이라는 방법이 거론됩니다.

그러나 현실적으로 P2P만으로 게임 서비스를 만들기는 불가능합니다.
친구들을 모으고 채팅을 하며 어떤 방이 있는지 목록을 보기 위해서는 반드시 중앙에 서버가 있어야 합니다.
스타크래프트로 치면 바로 배틀넷(battle.net)이죠.

구분 장단점 주 프로토콜 게임 스타일
C/S 장점 : 보안에 유리
단점 : 높은 트래픽, 대용량 서버 기술 필요
TCP가 주로 사용되나 UDP도 사용 온라인 게임
P2P 장점 : 대용량서버 필요없음
단점 : 복잡성 증가, NAT 홀펀칭 필요
서버연결은 TCP,
P2P에는 UDP가 주로 사용
네트워크 게임
<표1. C/S와 P2P 정리>

온라인 게임과 네트워크게임의 차이도 이번에 짚고 넘어가죠.
온라인 게임은 사용자의 대부분의 MMORPG처럼 접속이 없어도 서버 시간이 흐르는 게임을 의미합니다. 내가 서버에 접속하지 않아도 서버는 이벤트가 진행되고 밤낮이 바뀝니다. 네트워크 게임은 방을 만들어 진행하는 단판승부 위주의 게임을 의미합니다. 스타크래프트, 맞고 같은 게임이 대표적이죠.
이 강좌의 제목은 아무 생각없이 네트워크 게임 튜터리얼이라 지었는데, 어떤 예제가 나오더라도 그냥 무시해주시길 바랍니다. ㅠㅠ

2. 네트워크 아키텍쳐의 실전 사례 분석

주의!
이 포스트에 언급된 게임들은 반드시 그렇게 작동된다고 책임지지 않습니다.
특히 해당 IP와 Port로 접속을 거는 것도 결코 책임지지 않습니다.

오래 전부터 네트워크를 분석하는 툴은 많았습니다.
기본 프로그램인 netstat도 훌륭한 도구이며(그 유명한 TCP/IP Illustrated 도 netstat로 실습하고 있습니다.)
약간의 관심만 가진다면 어떤 유명 게임이라도 클라이언트 상에서 어떤 과정을 거치는지 쉽게 확인할수 있습니다.
악용하지 않는다면 정말 좋은 공부가 됩니다.

이런 도구들이 있는데 유명 게임들의 네트워크 아키텍쳐를 전혀 모른다는 말은 개발자로써 관심이 없다는 말일 겁니다.
가끔 면접 볼때 무슨 게임에 몇년을 투자했다는 열정에 대한 자랑을 듣지만,
그 게임의 라이팅이나 네트워크를 짐작도 못하고 있으면 공돌이로썬 안타깝게 느껴집니다.

진짜 울고 싶은건 질문하는 사람이라구!!

진짜 울고 싶은건 질문하는 사람이라구!!


이 강좌에서는 비쥬얼스튜디오 이외에 앞으로 두가지 툴을 자주 쓰겠습니다.

1) TCPView
http://technet.microsoft.com/en-us/sysinternals/bb897437
오래전 MS에게 인수된 sysinternals의 TCPView입니다.
netstat과 거의 흡사하지만 GUI라서 보다 편리합니다.
예전에 날잡아 서버 개발자 PC를 검사하며 이것이 안 설치되어 있으면 갈구시던 어떤 실장님이 생각나네요(제가 아닙니다.).
그외 FileMon, RegMon도 클라이언트 플랫폼 개발자들에게는 필수 도구입니다.

2) WireShark
http://www.wireshark.org/
pcap 라이브러리를 사용하는 패킷 검사툴입니다.
사실 이것과 노가다만 있으면 어떤 게임이라도 프리서버를 만들어 내는 일은 가능합니다.
불가능 하다고요?
우리가 게임을 만들듯, 수백명이 한사무실에 모여 각자 팀별로 패킷만 분석한다면 어떨까요?
대륙의 판타지는 이렇게 만들어집니다.

2.1 C/S 모델
여기서 말하고픈 것은 C/S 아키텍쳐라도 절대로 서버 1대로만 접속하지 않는다는 점입니다.
MMORPG도 그 수많은 종류만큼 다양한 네트워크 아키텍쳐가 있습니다만, 간단한 카드류 게임으로 서버에 어떻게 접속하는지 보겠습니다.

로비에 접속

로비에 접속


로비서버로 짐작되는 XXX.XXX.XXX.214에 접속

로비서버로 짐작되는 XXX.XXX.XXX.214에 접속


로비에서 게임룸으로 들어왔습니다.

로비에서 게임룸으로 들어왔습니다.


게임서버로 짐작되는 XXX.XXX.XXX.172에 접속

게임서버로 짐작되는 XXX.XXX.XXX.172에 접속


이렇듯 C/S 모델이라도 최소한 두개의 서버를 번갈아 접속했다는 사실을 알수 있습니다.
그리고 TCP 이외에 UDP도 사용하고 있고요.
UDP는 연결지향이 아니니까 책에서 본대로 접속은 이뤄지지 않았습니다.

물론 서버는 이 두개 뿐만이 아닙니다.
직접 해보시면 서버 주소가 다를 것이고 테스트해볼때마다 다를 수도 있습니다.
이 말은 실제 각 로비서버와 각 게임서버는 여러 대가 있다는 의미입니다.
그리고 다른 서버로 접속했다 하더라도 같은 방에 있어야 합니다.

이를 위해서는

1) 서버간 통신을 해주는 서버가 필요
2) 어떤 서버로 연결할 것인지 알려주는 서버가 필요

라는 결론이 나옵니다. 그 구조를 클라이언트에서 알수는 없습니다만, 아키텍쳐는 나온 것이죠.
이러한 유추를 통해 각기 다른 월드나 지역으로 구분하는데도 유용하게 사용할수 있습니다.

2.2 P2P 모델

P2P의 대표 모델인 스타크래프트 배틀넷을 분석해보죠.
스타크래프트의 P2P 모델은 DirectPlay의 P2P 모델과 일치합니다.
DirectPlay를 그대로 가져다 쓴 것이라 여겨집니다.

P2P에서는 크게 자신이 세션(방)을 만드는 것과 다른 사람이 만든 세션을 검색해서 들어가는 두가지로 나뉩니다.
각 단계에서 소켓에서는 어떤 함수가 불려지는지 정리해보지요.

1) 내가 방을 만들어 접속하는 경우
1-1) TCP 소켓 생성

Winsock은 TCP/IP 이외에 다른 프로토콜도 사용가능합니다. Battle.net을 선택하는 순간 TCP/IP를 위한 준비를 합니다.

Winsock은 TCP/IP 이외에 다른 프로토콜도 사용가능합니다. Battle.net을 선택하는 순간 TCP/IP를 위한 준비를 합니다.

pSocket->Create(TCP); 와 같은 형태로 함수가 호출될 겁니다.

1-2) Connect()
로긴

로긴

버전업 체크 서버와 버전을 확인을 한후 로긴을 합니다.
이때 pSocket->Connect(배틀넷, 스타크래프트_확장팩); 형식으로 접속을 하게 됩니다.

1-3) recv()

배틀넷 서버에 접속

배틀넷 서버에 접속


배틀넷 서버에 접속했습니다.
recv() 함수가 작동되며 공지사항이 날라옵니다.

1-4) 로컬에 서버 소켓 생성
방을 만들면...

방을 만들면...


P2P를 위해 로컬에 서버가 만들어집니다.

P2P를 위해 로컬에 서버가 만들어집니다.

세션이 만들어졌습니다.
이때가 무척 중요합니다.
내가 세션을 만드는 것도 중요하지만 다른 유저들이 내가 만든 방을 검색하기 위해서
배틀넷 서버에게 내 세션의 정보를 알려줘야 합니다.
가장 중요한 것은 "서버인 나의 IP"입니다.

1-5) 세션 호스트로써 게임 시작
게임을 시작합니다.
이때 배틀넷과 접속을 끊습니다.
P2P답게 다른 유저들과 접속이 이뤄지고 있을 뿐 배틀넷과의 접속은 의미가 없기 때문이죠.
UDP 막 날라다님

UDP 막 날라다님


만약 세션 호스트(방장)이 게임에서 나가게 되면 세션 마이그레이션(session migration)이라는 과정을 거치며
P2P 세션 호스트의 자리를 다른 사용자에게 물려주게 됩니다.
안정적으로 이것을 수행하는 것이 꽤나 난이도 있는 일이며 홀펀칭과 함께 P2P 라이브러리가 해주는 중요한 역활입니다.
스타크래프트에서 일반 유저보다 방장이 나갈때 잠시 랙이 걸리는 이유입니다.

1-6) P2P close(), 배틀넷 connect()
방을 나간 사람은 P2P 세션에서 나가며 다시 배틀넷에 Connect를 하게 됩니다.
이때 이겼다, 졌다의 정보를 갖고 배틀넷에 send()를 합니다.
이때가 P2P가 보안에 취약할 때입니다. 실제 이겼는지 졌는지 배틀넷은 모릅니다.
져놓고서 이겼다는 패킷을 날려도 배틀넷은 알지 못합니다.

2) 다른 사람의 만든 방에 접속할 경우
2-1) 세션 목록 recv()
1-3) 까지는 똑같습니다.
가장 중요한 것은 배틀넷이 현재 접속을 요청하는 세션 목록을 나타내는 것입니다.
P2P 서버가 해주는 가장 중요한 일

P2P 서버가 해주는 가장 중요한 일


2-2) 세션에 참여 P2P connect
배틀넷과 접속을 끊고 해당 세션 호스트의 IP로 접속을 하게 됩니다.
배틀넷과 접속을 끊고 P2P 세션에 connect()

배틀넷과 접속을 끊고 P2P 세션에 connect()


2-3) 게임 시작
세션 호스트라는 것이 아니면 네트워크 상으로는 똑같습니다.
마땅한 짤이 없어...지만 이건 스타2인데...

마땅한 짤이 없어...지만 이건 스타2인데...



2-4) P2P close(), 배틀넷 connect()
게임을 나가게 되면 P2P 세션을 닫고 배틀넷으로 접속합니다.
이 역시 1-6)과 같습니다.

3. 홀펀칭
P2P는 세션 마이그레이션 이외에도 NAT이 가장 골치꺼리입니다.
NAT 뒤에 있는 IP는 실제 IP가 아니기 때문에 밖에서 들어올 수 없는 것이죠.
공유기에 따라 특정 IP를 밖으로 노출 시키는 경우도 있고 아닌 경우도 있습니다.
어찌되었건 게임 서비스를 하는 입장에서는 NAT 특성에 관계없이 연결을 만들어줘야 합니다.

다행히도 이러한 홀펀칭에 대해서는 denoil님께서 멋지게 정리를 해주셔서 (http://gamedevforever.com/47)
해당 포스트를 참조하시면 될 것 같습니다. ^^

홀펀칭 역시 제 블로그(http://rhea.pe.kr/)에서도 2-1 편으로 다시 정리하겠습니다.
UDP 예제도 이번 포스트 소스로 넣어야겠군요...

자, 이번 포스트를 통해 소켓 사용법 이외에 네트워크 게임 개발자로써 고민해야할 1차적인 아키텍쳐를 이야기해봤습니다.
강조하고픈 것은 아키텍쳐에 게임을 맞추지 말고,
게임에 따라 유연한 아키텍쳐를 생각해내고 업데이트에 따라 유연하고 쉽게 바뀔 수 있는 자세를 가져야 한다는 것입니다.
좋아하는 게임이 있으면 어떤 네트워크 아키텍쳐를 갖고 있는지 최대한 유추해보시길 바랍니다.
그리고 PC게임 뿐만 아니라 스마트폰 게임이나 콘솔게임기를 추적해보는 것도 좋은 공부가 됩니다.

다음 시간에는 서버와 절대로 떼어놓을 수 없는 Database를 살펴보겠습니다.
아직도 게임을 만들 시기는 아닙니다.

4. 더 생각해볼꺼리
1) 오늘 예제로 등장한 C/S게임과 스타크래프트의 네트워크 연결 순서도를 그려보세요.
물론 소켓 함수도 함께 적어보세요.

2) 자세히 보시면 스타크래프트1의 배틀넷은 DirectDraw surface가 아니라 일반 Dialog입니다.
(커서가 윈도우 커서로 바뀌죠?)
저도 그랬지만 Surface 위에 Dialog 나 윈도우 stuff들을 올리는 것은 쉽지 않았습니다.
이는 지금도 마찬가지인데 각자 UI 컨트롤을 위해 노가다 헀던 경험담들을 풀어봅시다.
선수끼리 숨기지말고요~

내용은 맘대로 퍼갈수 있지만 동의없는 수정은 안되며 출처(http://www.gamedevforever.com/ , http://rhea.pe.kr/ )를 명시해주세요.

반응형
,
Posted by 친절한티스

벌써 두번째 연재네요. 재미없는 첫 번째 글에 많은 관심을 가져주신 분들께 감사드립니다. 여러 네임드 능력자분들과 같이 글을 쓰려니 괜히 쩌렙 인증하는 거 같고, 많이 후달립니다.

앞으로 올릴 글은 제가 최근 관심을 갖고 있는 주제이기도 한 병렬 프로그래밍에 대해 써볼까합니다. 분량이 좀 많다보니 연재식으로 될거 같습니다. 그리고 창희님과 번갈아가며 쓰게 될지도... ( 일단 떠밀어보기 )


바야흐로 세상은 대 멀티코어 시대
골드 D 인텔이 멀티코어로 세상을 평정하고 프로그래머들이 병렬 프로그래밍의 세계로 뛰어들어 세상은 대 멀티코어 시대에 돌입하였다!! ... 라는 되도 않는 드립을 쳐보려고 했지만 재미없네요.

병렬 프로그래밍을 찾아 떠나는 프로그래머 해적들

이제는 흔하다 못해 스마트폰에서까지도 멀티코어를 쓰는 시대입니다. 그에 맞춰 여기저기서 병렬 프로그래밍 얘기들이 흘러나오고 왠지 나도 병렬 프로그래밍 안하면 안될거 같은 기분이 마구마구 듭니다. 게다가 게임도 갈수록 고사양화 되고 있고 싱글 코어 활용만으로는 도저히 퍼포먼스도 안나옵니다. 멋지게 게임 만들어놨는데 CPU 사용률이 25%만 올라가있으면 왠지 손해보는 느낌까지 듭니다.

싱글 코어로 공짜밥 먹던 시절
멀티코어 시대 이전에 병렬 프로그래밍이 없었던 것은 아닙니다. 백그라운드 로딩이라든가 네트웤 통신에 많이 사용되고 있었고, 지금도 사용 되고 있습니다. 하지만 메인인 게임 로직부분은 거의 직렬 방식이었습니다. 과거에는 이 것 만으로도 충분했습니다. 게다가 CPU 제조사에서 속도를 업그레이드 해줄때 마다 자연스레 게임 퍼포먼스도 올라 갔습니다( CPU 제조사에 감사합니다~ ).

그러나 CPU 제조사의 이런저런 불편한 진실로 인해 속도 업은 미비해주고, 대신 코어 수로 떼우기 시작하게 됩니다. 게임은 갈수록 고사양이 되가는데 CPU 속도 업은 거의 없어지니 게임이 느려지기 시작합니다 ( 게임만이 아니지만 우린 게임 프로그래머이니 게임이라고 합시다 ) . 이를 두고 향간에서는 더 이상 공짜 점심은 없다!! ...라고 말하기 시작합니다.

누가 내 점심을 없앴는가?

멀티 코어 어떻게 써요? 
멀티 코어를 활용해야겠다고 다짐 했습니다. 어떻게 해야 멀티 코어 CPU를 활용할수 있을까? 옵션하나 탁~ 켜주면 내 프로그램이 멀티 코어로 작동했으면 좋겠습니다. ( 실제 멀티 코어 CPU가 어떤식으로 동작하는지 알고 싶으신 분은 [김민장님 저서인 프로그래머가 몰랐던 멀티코어 CPU 이야기] 한번 읽어보시기 바랍니다. ) 하지만 현실은 그렇게 쉽지 않습니다. 내가 만든 프로그램이 멀티 코어로 동작하기 위해서는, 병렬적인 프로그램을 만들어야지만, 멀티 코어 환경에서 병렬로 작동 됩니다.

말 장난 같지만 사실 입니다...


병렬 프로그램 어떻게 만들어요? 
멀티 코어 CPU의 경우 프로그램이 실행될때 병렬성을 체크해서 각 코어에 일을 할당 해줍니다. 만약 그런게 전혀 없다. 그러면 코어 하나에만 작업이 할당되서 프로그램이 작동하게 되는 것이죠. 몇몇 게임을 실행할때 4코어인데도 불구하고 CPU 사용률이 25%만 올라가는 이유가 바로 이 것 입니다. 그렇다면 어떻게 병렬성을 높일수 있을까요? 대표적인게 바로 멀티 스레딩입니다. 여러개의 스레드가 생성된 프로그램이라면 1번 코어에서 1번 스레드를, 2번 코어에서 2번 스레드 처리하는 식으로 병렬 처리를 하게 됩니다.

이것이 병렬 처리다!!

스레드만 추가 생성하면 끝? 
위에 적은 데로라면 스레드만 추가하면 CPU에서 알아서 병렬 처리를 해주겠네요. 스레드를 마구마구 생성해서 프로그램을 만들어봅니다. 아마 조만간 ㅅㅂ... 소리가 절로 나올겁니다. 정상 작동하는게 하나도 없어 보입니다. 왜그럴까요?

멀티 스레드를 이용한 프로그래밍을 할 때에는 많은 주의 사항이 있습니다. 대표적인 문제 중 하나로 꼽히는 것이 데이터 레이싱 입니다. 일반적으로 멀티 스레드는 동일 메모리 공간에서 작동합니다. 즉, 여러 스레드가 하나의 데이터에 동시 접근이 가능하다는 이야기입니다. 그렇게 되면 여러 스레드가 동일한 데이터를 읽고, 쓰다가 원치 않는 값  변조가 일어나게 됩니다. 밑의 그림은 그 상황 예시입니다.

 
1번과 2번 스레드가 x값을 참조하고, 값을 바꿔 써넣는 코드입니다. 1번과 2번 스레드는 비동기로 동작하기 때문에 어떤 스레드가 먼저 x값을 참조하고 값을 바꿀지 모릅니다. 그래서 x의 최종 결과 값이 #1, #2, #3중 어느 결과로 나올지 알 수 없습니다.

문제는 이것만이 아닙니다. 데드락 문제도 있고, 코어 수만큼 스레드를 어떻게 분배할 것인지도 문제고, 생성된 스레드들은 또 어떻게 관리 할지도 문제입니다. 병렬 프로그래밍을 해보려다 그 앞에 산재해 있는 문제들때문에 제대로 해보지도 못하고 포기하게 생겼습니다.

쉬운 방법 없을까? 
좀더 쉬운 방법이 없을까? 눈을 돌려봅니다. 세상에는 똑똑한 분들이 많습니다. 멀티 코어 CPU가 발전하는 만큼 이를 쉽게 사용할 수 있게 해주는 라이브러리들도 발전하고 있습니다. OpenMP, TBB, PPL등이 그것입니다. 이들 라이브러리를 이용하면 직접 스레드를 관리하거나 분배하는 수고 없이 수월하게 병렬 처리를 할수 있도록 도와 줍니다.

간단하게 배열에 있는 수를 더하는 로직을 병렬로 처리 해보도록 하겠습니다. 직렬 처리 방식으로 로직을 구성 한다면 이런 모양 일겁니다.
for( int i = 0; i < 5000; ++i )
	fNumSum += fNum[ i ];
참으로 단순한 로직입니다. 5000개의 배열을 가지고 있는 fNum의 임의수들을 전부 다 더하는겁니다. 이걸 병렬로 처리 한다고 생각해봅니다. 그전에 위에서 얘기한 멀티 스레드의 문제점을 다시 떠올려봅니다. 그냥 스레드를 여러개 생성해서 위의 로직을 수행한다고 하면, 데이터 레이싱이 일어나 엉뚱한 합산 값이 나올겁니다. 이를 방지하려면 스레드에 맞게 배열을 분배 해주어야 합니다. 스레드를 2개 생성 한다면 1번 스레드는 0~2499, 2번 스레드는 2500~4999 식으로 말이죠. 그리고 작업을 수행 후 서로 합산 값을 더하면 원하는 결과가 나오게됩니다.

그런데 이것을 직접 수동으로 한다면, 스레드도 직접 생성해주고, 모든 스레드가 작업을 수행할때까지 대기했다가 작업이 끝나면 Join 시켜주고, 값 합산 해야하고, 만약 CPU 코어 수에 맞게 스레드 생성도 해주겠다 하면... 손이 가는게 이만 저만이 아닙니다. 단순한 합산 로직 하나 만드려고 들이는 정성이 배 보다 배꼽이 더 큰격입니다.

정말 이런 짓을 매번 하라고??

그럼 TBB가 출동 한다면 어떨까요??
// 병렬 처리를 위한 바디 클래스
// TBB 쓰면 그냥 이런 식으로 구성된다라는 것을 알리기 위함이므로 코드 설명은 생략
class CFoo 
{
private:
	float* m_fElement;

public:
	float m_fSum;
	void operator()( const blocked_range<size_t>& r )
	{
		size_t end = r.end();
		for( size_t i = r.begin(); i != end; ++i )
			m_fSum += m_fElement[ i ];
	}
	CFoo( CFoo& pSplitFoo, split ) : m_fElement( pSplitFoo.m_fElement ), m_fSum( 0 ) {}
	CFoo( int a[] ) : m_fElement( a ), m_fSum( 0 ) {}
	void join( const CFoo& pSplitFoo )
	{
		m_fSum += pSplitFoo.m_fSum;
	}
};

// TBB의 parallel_reduce를 이용해 fNum의 5000개 배열의 값들을 합산
CFoo foo(fNum);
parallel_reduce( blocked_range<size_t>( 0, 5000 ), foo );
코드 어디를 봐도 스레드를 생성하고, 작업을 분배 해주는 곳이 없습니다. 다~ 자동입니다. 알아서 코어수 만큼 스레드를 할당해주고, 각 스레드에 알맞은 작업량을 할당해줍니다. 작업자는 스레드 관리에 대해 전혀 신경쓸게 없습니다. 완전 좋죠~!! 짱입니다!! ( 물론 세세한 옵션등이 있지만 일단은 신경끕니다 ).

병렬 프로그래밍. 참 쉽죠?
TBB를 사용하니 병렬 프로그래밍이 엄청 쉬워진 것 같습니다. 하지만 안쉽습니다. 절대 안쉬워요. 조금이라도 병렬 프로그래밍 해보신 분이라면... 이거 참 병맛이다. 라고 말씀하실 겁니다. 아무리 툴이 좋아지고, 라이브러리가 좋아졌어도, 그걸 사용하는 사람은 기본적으로 직렬적으로 생각합니다. 동시에 처리될 부분을 모두 염두해가며 프로그래밍을 할 수 있는 사람은 극히 드뭅니다. 왜 난 이런 것도 못하지? 자책하지 마세요. 그게 정상입니다. 할수 있는 사람들이 특이한겁니다.

안 쉬워요.

To Be Continued....
다음 강에는 게임 프로그래밍에 적용하는 병렬 프로그래밍에 대해 좀더 다뤄볼게욤~
아마도... 
반응형
,
Posted by 대마왕J

안녕하십니까 남들은 조낸 멋진거 막막 쓰고 인기높고 막 그러는데
저만 괜히 정말 아무것도 모르는 초보를 위한다고 너무 수준낮게 쓰느라
팀블로그 그래픽 반의 질을 떨어뜨리는건 아닐까 하고 걱정하고 있는 대마왕J 입니다.


나... 나도 쓸꺼야! 실버치매님처럼 멋진 글을!!!!





근데 생각해 보니까 하지만 난 어차피 수준이 초딩이잖아?
컨트롤 C+V 밖에 못하는


그러니 닥치고 저는 이 사이트의 낮은 레벨을 담당하는 것이 낫습니다.




시작합니다. 이히히 ~
==================================================================================================


1. 텍스쳐를 넣어 봅시다.

자 드디어 텍스쳐를 넣을 시간입니다! 텍스쳐를 넣는 것도 지극히 간단하지요.
아까 만든 것처럼 빈 화면에서 오른쪽 클릭을 해서 Maps로 간 다음,
이번엔 Color가 아니라 TextureMap을 선택합니다.

텍스쳐를 넣어야 하니까, 당연한 선택이겠지요?


자, 이렇게 TextureMap이 생겨났습니다.
아직 아무 그림도 없지요.

역시 이 것을 선택하면, 오른쪽에 인터페이스가 좌아악 펼쳐집니다...
이 옵션들은 사실 굉장히 기능이 많습니다. 각종 시멘틱(Semantic) 설정에, 텍스쳐 UV 채널, 샘플러 셋팅, 밉맵의 개수, 심지어 감마 코렉션(Gamma correction) 을 위한 기능까지 들어 있습니다.
간편하게 다루는 툴인데 비해, 상당히 많은 기능이 들어 있지요.


뭐 그치만 그딴건 아직 안봅니다. 신경쓰지 마세요



어쨌건 텍스쳐를 하나 넣어 봅시다.
아래 그림처럼, UI Name 아래 있는 버튼을 눌러 볼까요.


저는 D_grass.dds 라고 하는 풀 그림이 그려진 그림을 선택했습니다. [각주:1]
드디어 그림이 보이는 군요.


자, 이제 이 TextureMap을 왼쪽의 셰이더 루트의 Ambient Color에 연결해 주면 될텐데...
아까 칼라를 연결할 때에는 하나밖에 없었는데, 이젠 다양한 색깔로 여러 종류가 있습니다. 분홍색의 RGBA , 녹색의 RGB, 그리고 파랑색의 R,G,B,A ...

일단 결과를 보는게 급하지요! 답을 알려 드리겠습니다. 여기서는 색이 중요합니다! RGB라고 씌인 녹색 공과 Ambient color를 연결해 주세요! [각주:2]

그리고 이 쉐이더를 주전자와 연결시켜 주시면, [각주:3] 드디어 주전자에 텍스쳐가 입혀집니다!!!


축하드립니다. 드디어 텍스쳐를 연결하셨습니다. 이제 이곳 저곳에 연결해 보시고 끊어보시기도 하면서 가지고 놀아보세요. 잘 작동하는 것도 있고 잘 작동하지 않는 것도 있을 겁니다.

그리고 특히, 아래 그림의 이 부분을 제일 주목해 주세요.[각주:4]
아래 그림의 붉은 박스 영역과 같이
RGBA 들이 같이 있기도 하고 , 떨어져 있기도 한 이 부분 말입니다.

우선 생각하기에 RGB 값이 연결되는 것은 당연하게 보이긴 합니다. 이미지는 보통 RGB로 구성되니까요.
하지만 알파가 포함된 RGBA , 그리고 r,g,b,a 가 각각 떨어져 있는 것도 있습니다.
이것을 어떻게 사용해야 할까요?

이걸 알기 위해서는 색상의 각 요소가 프로그램에서 어떻게 사용되는지를 알 필요가 있습니다.




2. Float 의 비밀

이전에 빨강 공을 만들때, float3라는 말을 언급했었지요?


자... 이게 무슨 소리인지 이제 알아볼 시간입니다. :)
원래대로 풀버전으로 설명하면 칼라의 bit 부터 시작해서 너무 길어지므로 최대한 축약해 보도록 하겠습니다.
풀버전 설명은 다음기회에 따로 준비하도록 하지요. 오늘은 간략하게 간락하게 쉽게 쉽게 ...

우선, 컴퓨터 그래픽에서의 '색' 에 대해서 생각해 보도록 하지요.
학교 수업시간에 배운바에 의하면, TV나 모니터의 빛은 '가산혼합법' 이라고 해서, 다 더하면 하얗게 되는 색이라고 합니다.


이런 그림 기억나시죠? 빨강색 , 녹색, 파랑색이 있고, 이 세 가지의 색을 모두 더한 색은 흰색이고, 세 가지의 색이 전혀 없는 색은 검은 색이다 ... 라는 거 말이죠.

이거와는 반대로, 잉크로 인쇄하는건 달라서, 섞으면 섞을수록 어두워지고... 다 더해도 진짜 검은색이 사실 안나오기 때문에 검은색을 따로 추가하고 어쩌고 해서 CMYK[각주:5]다 ... 라는거 말이죠.

네, 그렇지만 우리들의 모니터는 가산혼합법으로 이루어지는 녀석이라, 3가지의 색 (Red,Green,Blue) 이 모여서 더해지는 색 구조를 따르고 있습니다. 그리고 색을 모두 더하면 흰색이 되구요.

만일 빨강색이 있다면, 우리 그래픽 디자이너들이 익숙한 포토샵에서는 이렇게 보이겠지요?

빨강색을 선택했더니, (R,G,B) 가 (255,0,0) 이라고 합니다.
이건 그래픽 디자이너분들한테는 매우 익숙한 광경이지요.
그렇다면 흰색은 무엇이겠습니까?
흰 색은 RGB가 모두 최고 밝은 상태이므로 (255,255,255) 겠지요?

그렇다면 검은색은 ?
검은색은 RGB에 아무런 값도 없는 상태를 의미하니 (0,0,0) 일겁니다.

자, 그럼 회색일때 RGB 값은 어떤 값일까요?


... 네 그렇습니다. (128,128,128) 입니다. [각주:6]

이렇게, 우리는 일반적으로 0~255 까지의 수를 가지는 세 가지의 색(RGB) 의 조합으로 모든 색을 표현하고 있습니다.
이 때, R, G, B 라고 불리우는 각 색의 값을 '채널' 이라고 부릅니다. 즉 색을 만들기 위해서는 3개의 채널이 필요하고, 우리가 일반적으로 사용하는 채널당 8비트 이미지는 [각주:7] 0~255 까지의 값을 가진 R,G,B 채널들의 조합으로 만들어 진다는 것이지요.

자, 다시 한 번 정리해 봅시다. 채널당 8비트인 이미지에서 RGB 값은,
R G B
빨강색은 ------------ (255, 0, 0)
흰색은 -------------- (255, 255, 255)
검은색은 ------------ (0, 0, 0)
회색은 -------------- (128, 128, 128)

이란 말입니다. 이해가시겠지요?

근데 프로그래밍에서는 이런 숫자를 쓰지 않습니다. 그 이유에 대해서 제대로 쓰면 또 양이 많아지기 때문에, 간단히 말해서 '컴퓨터가 원래 그렇게 돌아가기 때문임' 이라고 우기도록 하겠습니다

이힝힝. 모, 몰라서 그러는 거 아니라능!! 믿어달라능!!!


프로그래밍에서는 그럼 색을 어떻게 쓸까요?
바로 포프 스승님 강의를 읽어보셨으면 알만한 내용인, "% 로 표시합니다" 라는 겁니다.


R G B
빨강색은 ------------ (100%, 0%, 0%)
흰색은 -------------- (100%, 100%, 100%)
검은색은 ------------ (0%, 0%, 0%)
회색은 -------------- (50%, 50%, 50%)



라는 거지요.
그리고 다시 이것을 소숫점으로 나타내면 Float(부동소수점)[각주:8] 이 되는데요.
그럼 이렇게 나타나게 됩니다.

R G B
빨강색은 ------------ (1, 0, 0)
흰색은 -------------- (1, 1, 1)
검은색은 ------------ (0, 0, 0)
회색은 -------------- (0.5, 0.5, 0.5)



즉, float 이란 부동소수점으로 표시한 채널을 의미하며, 이것이 RGB 일 경우에는 소수점 3개가 필요하니 float3 라고 표시하는 겁니다. 최종적으로는 이렇게 표시되겠지요.

R G B
빨강색은 ------------ float3 (1.0, 0.0, 0.0)
흰색은 -------------- float3 (1.0, 1.0, 1.0)
검은색은 ------------ float3 (0.0, 0.0, 0.0)
회색은 -------------- float3 (0.5, 0.5, 0.5)



그러므로 이 그림에서 float3란, '3개의 소수점으로 이루어진 값' 이라는 의미가 되는 겁니다.
그러면 아래 그림에서 Ambient Color로 넘어가는 값은 float3(1.0, 0.0, 0.0) 이라는게 이해 되시겠지요?

여기서 또 하나 재미있는 것은, float3의 값을 받는 Ambient color 도 녹색 박스라는 겁니다. 다시 말해서 float3만을 받을 수 있다는 말이지요. color 니까 당연하겠지요.


3. Float을 그림으로 보자

float3 는 float + float + float 3개가 모여서 만들어 졌다는걸 아시겠지요?


그럼 이 이미지가 슬슬 이해 되실 겁니다.
RGBA 란? A 란 '알파 채널' 을 의미하지요. 투명도를 나타내는 바로 그 값이 포함된것 말이지요.
투명도 역시 0~255 라는 단계를 가질 수 있습니다. 즉 RGB에다가 A 가 추가로 포함되어서, 투명도가 포함된 이미지를 의미한다는 말이지요. 그러므로 이건 float으로 나타내면? float+float+float+float 4개가 모여 4자리니까 float4 라고 말할 수 있겠지요? 아래처럼 말이지요.
R G B A
빨강색인데 불투명 ------------ float4 (1.0, 0.0, 0.0, 1.0)
빨강색인데 완전투명 ---------- float4 (1.0, 0.0, 0.0, 0.0)
빨강색인데 절반정도 투명 ---- - float4 (1.0, 0.0, 0.0, 0.5)




그렇다면 각각 떨어져 있는 R, G, B, A 의 단위는 무엇일까요?


.... 네 그렇습니다. 그냥 float이지요. 한 자리 소숫점 숫자일테니까요.
이런 식이란 말입니다.
빨강색인데 R채널 ------------ float (1.0)
빨강색인데 G채널 ------------ float (0.0)
빨강색인데 B채널 ------------ float (0.0)



이 부분이 가장 중요하면서 초반에 그래픽 디자이너분들이 가장 많이 포기하는 부분이므로 좀 더 자세하게 써 보겠습니다.


자, 익숙해 보이는 포토샵 화면입니다. 풀 그림이 있고요, 채널 탭이 보입니다.
알파 채널이 있다는 것도 보이시지요? 그리고 지금 '눈' 마크가 RGB가 켜 있어서, RGB채널이 모두 보이고 있는 상태라는 것도 알 수 있습니다.

자 이제 채널을 하나 하나 따로 따로 보겠습니다.

Red 채널입니다. Red 채널만 활성화 시키자, 그림이 흑백으로 변했습니다.
이미지를 잘 보시죠. 무슨 의미일까요?
이 이미지의 한 픽셀 한 픽셀은, float 값들을 의미합니다. Red 값의 강도만 0.0~1.0 사이로 나타내면 되기 때문에, 흑백으로 충분한 것입니다.

즉 흰색 점들은 float (1.0) 을 의미하고, 회색인 점들은 float(0.5)를 의미합니다. 검은색 점은? 당연히 float(0.0) 을 의미하겠지요. 각 점은 밝기에 따라, 다양한 한 자리의 소수점 값을 가지고 있습니다.


이번엔 녹색 채널입니다. 아까보다 밝아 보이죠? 당연합니다. 그림 전체가 녹색에 가깝기 때문에 녹색 값이 높은 겁니다. 그래서 녹색 채널만 보면 밝아 보이는 거구요

Blue 채널을 보니 좀 어둡습니다. 즉 이 이미지에는 파랑색이 별로 들어가 있지 않다는 얘기겠지요.

알파를 보겠습니다. 역시 다른 채널과 마찬가지로, 흑백으로 되어 있습니다.
또한 다른 채널과 완전히 마찬가지로, 0인 부분 (투명한 부분) 은 완전한 검은색으로 되어 있고, 1인 부분 (불투명한 부분) 은 흰색으로 되어 있으며, 0.5 (반만 투명한 부분) 은 흰색으로 되어 있습니다.

이렇게 4개의 채널을 봤습니다.

...무언가 발견하신 것이 있는지?
사실, R,G,B,A 내 개의 채널은 아무런 차이가 없습니다! 모두 똑같은 'float1' 구조를 가지고 있습니다!
모두 각 픽셀당 소수점 하나만을 가지는 숫자로 되어 있을 뿐입니다. 즉 언제건 Red와 Green을 바꿔도 상관없고 , Blue와 Alpha를 바꿔도 문제없이 정상 동작합니다. 단지 이미지의 색이 달라질 뿐이겠지요.

그래서 아래와 같이 , R,G,B,A가 각각 따로 동일하게 취급을 받을 수 있는 것입니다.
이것이 가장 중요한, 채널과 float에 대한 간략한 개념입니다. (말씀 드렸다시피, 풀버전은 나중에...)





4. Opacity를 만들자.

프로그래머들은 Opacity라고 하면 잘 못알아 듣습니다. Alpha나 Transparant 라고 하면 잘 알아 들어도...
어쨌건 여기까지 잘 하신 상태란 말입니다.


이제 Opacity에 값을 추가할 차례입니다.
잘 보세요. Opacity는 파란 색 값을 원하고 있습니다.

파란 색 값이란? float 값을 의미합니다. 한 자리의 숫자요. 위에서 말한.
하긴 투명도 결정하는데 RGB 세 채널은 필요없겠죠. 한 자리면 됩니다. 투명한 것은 '얼마나 투명하냐 ' 라는 한 자리의 수만 있으면 되니까요.

흔히 그래픽만 하시던 분들이 쉐이더를 공부할때 이 부분에서 혼동을 많이 하시는데, 3Dmax의 Opacity는 "흑백으로 된 RGB" 이미지를 따로 만들어서 Opacity에 넣지요. 이것은 그거랑 다릅니다. 그정도로 필요 없습니다.
사실 Opacity를 표현하기 위해서는 '채널 하나' 만으로도 충분하고, 그래서 알파 채널 하나만 있으면 투명도를 결정하는데 충분하게 사용할 수 있다는 말입니다.





자, 알파 채널 (A) 를 클릭해서 끌어내 봅니다. float 이 나오는 것을 알 수 있습니다!!

즉 지금 이 그림이 나오고 있는 것이지요?

이것을 Opacity 에다가 끌어 놓으면 되는 것입니다.

주의할 점은, float은 반드시 float에 넣어야 하고, float3는 반드시 float3에 넣어줘야 한다는 겁니다.
Opacity는 파란색 (float) 만을 받는 값이기 때문에, 반드시 float을 넣어줘야지,
여기다가 녹색 (RGB)데이터를 넣어봐야 들어가지도 않습니다.



자! 결과물이 드디어 나왔습니다. 원하던 모양이 나왔네요.[각주:9] 이것이 채널을 사용하는 법입니다.
RGB 채널은 Ambient Color에 연결했고, A 채널은 Opacity에 연결했습니다.

이번 강의는 여기서 끝입니다 . 잘 하셨어요.
그런데 퀴즈가 하나 있어요... 이해하셨나 테스트!

만약 Opacity에 A 채널을 넣지 않고, B나 G, R 채널을 끌어다 놓아도 될까요?






정답은








됩니다!!! B 채널을 Opacity로 끌어다 놓았습니다.
Alpha 채널과 똑같은 float 이기 때문에 당연히 됩니다. 그것만 맞으면 뭐든지 들어간다니깐요.
지금 현재 B채널은 Ambient Color에서도 쓰이고 있고, Opacity 에도 쓰이고 있군요.

대신 투명도가...


이 이미지의 강도에 맞게 결정되었다는건 이해하시겠지요? 자, 놀랍지요? 이것이 기존에 그래픽 디자이너들이 잘 모르던, 채널의 속사정입니다. 가장 중요하면서, 가장 기초가 되는 바로 그 부분이지요.

각각의 채널은 동등한 구조의 float으로 구성되어 있고, 이게 단지 순서에 따라 Red냐 Green이냐 Blue냐 Alpha 이냐를 결정하고 있으며, 이걸 서로 섞어도 일단 서로 호환은 된다는 말입니다.



이걸 잘 이해하셨다면, 이제 오늘 강의는 진짜로 끝입니다. 다음부터 재미있어 지겠군요.

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

- 다음 시간에는, 쉐이더를 가지고 놀아보는 시간입니다. 산수야 놀자! 입니다.
모든 색의 채널이 0.0~1.0 사이의 숫자라는 것을 알았으니, 이것을 더하고 곱하고 가지고 놀아봅시다.

- 시간이 된다면, 조금 더 이미지를 조합해 봅시다. 재미있을겁니다.


  1. 이 텍스쳐 파일은 ShaderFX를 설치하면 자동으로 깔리는 텍스쳐 중 하나입니다. 저는 C:Program FilesAutodesk3ds Max 2009maps 안에 있네요. [본문으로]
  2. Ambient Color에만 연결하는게 지겨우신 분들은 Diffuse Color나 Self-illumination에도 연결해 보세요. 재미있는 변화가 일어날 겁니다. [본문으로]
  3. 쉐이더와 주전자를 동시에 선택한 채, Tools / Assign Material to selection을 해주면 됩니다. [본문으로]
  4. 어떻게 Texture Map 오브젝트가 저렇게 크게 보이게 할 수 있느냐고요? 마우스 휠을 굴려보세요. 확대, 축소가 됩니다. 마우스 가운데 버튼을 누른채 이동하면 패닝이 되구요. [본문으로]
  5. Cyan, Magenta, Yellow, Black 을 의미합니다. [본문으로]
  6. 사실, 모니터의 출력특성에 따른 색상보정으로 감마 코렉션(Gamma correction) 이라는게 적용되어서, 진짜 회색은 128,128,128 이 아닙니다만 여기서는 그 얘기를 무시하고 넘어가겠습니다. 분명 실버치매님이 써주실 거다에 한표. ㅋㅋㅋ [본문으로]
  7. 채널당 8비트는 R,G,B 채널들이 각각 2의 8승 = 256 단계 로 이루어져 있다는 말입니다. 물론 채널당 다른 비트도 존재합니다. [본문으로]
  8. 소수점이 있는 수를 표현할때 사용하는 방식이라고 생각하시면 편합니다. 물론 어느 정도 한계치가 있어서, 너무 큰 수나 너무 작은 수는 표현 불가능하지만 일반적인 셰이더 작업을 위해서는 충분하고 남습니다. [본문으로]
  9. 참고로, 맥스 2012에서는 잘 나오지 않습니다. 현재 사용하고 있는 맥스는 2009 버전이며, 2010 버전에서도 잘 나오던걸로 기억합니다.. [본문으로]
반응형
,
Posted by 알 수 없는 사용자

안녕하세요. 2일과 17일을 담당하고 있는 박성준입니다.
오늘은 문자열 인코딩에 대해서 이야기를 해보려고 합니다.

게임을 만들다보면 여러가지 형식으로 인코딩된 문자열들을 사용하게 됩니다. 그런데 C++에서는 기본적으로 문자열을 표현하는데 기본적으로 제공되는 것들이 C#이나 다른 최근에 만들어진 언어에 비해 많이 불편합니다..

그래서 저는 C#의 System.Text.Encoding 클래스를 베껴서 C++에서 구현하여 사용합니다..



이것이 문자열 인코딩

모두 아시다시피 컴퓨터는 원래 문자를 표현할 수 없습니다. 숫자만을 표현할 수 있고 저장할 수 있죠. 그래서 컴퓨터에서 문자를 표현하기 위해서는 숫자들로 이루어진 값(바이트)을 특정 문자로 매핑하여 어떤 숫자가 어떤 문자를 표현하는지를 미리 설정해두어야 합니다. 즉, 문자열 인코딩은 지원되는 문자 집합의 각 문자를 해당 문자를 나타내는 숫자 값과 연결하는 것을 말합니다.  char형을 사용하는 문자열 인코딩의 형식은 보통 코드페이지에 의해 구분되며 wchar_t를 사용하는 문자열 인코딩 형식은 기본적으로 Unicode 인코딩 형식입니다. Unicode는 UTF-8, UTF-16(UCS-2), UTF-32(UCS-4) 등이 있는데 윈도우즈에서는 기본적으로 UTF-16을 사용하기 때문에 윈도우즈에서 wchar_t형은 UTF-16이라고 생각하시면 됩니다. 그래서 wchar_t의 사이즈가 2바이트인 것입니다. (리눅스는 UTF-32를 사용한다고 들었습니다. 따라서 리눅스에서 wchar_t의 사이즈는 4바이트 입니다.)
유니코드에 대한 더 자세한 내용은 유인환님께서 쓰신 '2012/01/06 - [프로그래밍] - Unicode 사용하기'를 참고해주세요.


문자열 인코딩은 어디에 쓰지?!

문자열 인코딩은 여러가지 경우에 사용됩니다. 기본적으로 컴퓨터에서 문자를 표현할때는 모두 문자열 인코딩을 사용합니다. 하지만 직접 코드페이지를 입력해주어야 하거나 변환을 해야 하는 경우도 존재하죠. 예를 들면 윈도우즈의 언어설정이 한국어로 되어있는 경우 기본 코드페이지는 한국어 코드페이지(949)로 되어있기 때문에 특별히 신경써주지 않아도 한국어 표현이 됩니다. 하지만 언어 설정이 일본어로 되어 있는 경우 따로 문자열 인코딩을 해주지 않았다면 "안녕하세요" 라는 문자를 출력했을때 "セネウ酩マシシソ・"라는 이상한 일본 문자가 화면에 나타날 것입니다. 또는 유니코드 환경을 사용할 때에도 문자열 인코딩은 신경써야 합니다. 모든 라이브러리가 유니코드를 지원하는것이 아니기 때문에 또는 UTF-16을 사용하고 있지만 UTF-8로 입력이 들어오는 경우도 존재하기 때문에 사용하고자 하는 문자를 명확하게 지정할 수 있어야 제대로 된 문자열을 화면에 출력할 수 있겠죠. 사실 최근에 만들어진 언어들은 문자열 출력에 좀 더 신경써서 만들어졌습니다. C#만 봐도 char, wchar_t 이런것에 전혀 신경쓰지 않고 string이라는 타입에 모든 문자열 형태를 저장할 수 있고 Encoding 클래스를 사용하여 인코딩도 쉽게 설정할 수 있습니다.


C#에서는 어떻게 쓰길래

C#에서는 System.Text에 Encoding이라는 클래스가 존재합니다. 이 Encoding 클래스를 사용하면 문자열 바이트를 한국어로 인코딩을 해서 string을 얻어내거나 한국어 <-> UTF-8, 한국어 <-> UTF-16, UTF-8 <-> UTF-16 등 인코딩 변환도 간단하게 할 수 있도록 만들어져있습니다. 이런 클래스가 존재하기 때문에 문자열을 저장하거나 메시지버퍼에 넣을때에도 원하는 인코딩으로 편하게 변환시킬 수 있습니다.

문자열의 인코딩 형식의 변경도 조금 복잡하긴 하지만 그래도 쉽게 가능합니다. 원래 인코딩이라는게 인코더와 디코더로 이루어져야 하기 때문에 .Net Framework에 있는 Encoding 클래스들 역시 인코더와 디코더의 기능을 가지고 있고 이것으로 문자열 인코딩 형식 변환을 하게 됩니다. 인코더는 바이트 배열을 문자로 표현할 수 있는 특정 범위의 값으로 변경하는것이고 디코더는 인코딩된 형식의 값을 바이트 배열로 변경하는것을 말합니다.


C++에서의 구현

기본적으로 Encoding 클래스의 형태는 C#의 그것과 비슷하게 따라가려고 했습니다. 특별한 이유는 없고 그냥 베낀겁니다. -_-;
특히 C#에서 Encoding.Default, Encoding.ASCII, Encoding.UTF8 이런식으로 자주 사용하는 문자열 인코딩 형식을 쉽게 가져오는 부분과 Encoding.Convert( Encoding.Default, Encoding.UTF8, 바이트배열 ) 이런식의 사용에 중점을 두었습니다.

그래서 다음과 같은 코드를 작성할 수 있도록 하였습니다.
 회사에서 작업한 코드를 올릴 순 없으니 급하게 따로 작성한 코드를 첨부하도록 하겠습니다..
급하게 작업한거라 버그가 있을 수 있으니 그대로 사용하기엔 문제가 발생할 수 있습니다..
그냥 이런식으로도 사용하는구나 정도로 봐주시면 될 것 같습니다...

TextEncoding.zip





받아보시면 Encoding의 구성은 위 그림처럼 되어있을 것입니다. 하지만 외부에서 사용하는건 Encoding 클래스 하나입니다. 사실 ASCII니 DBCS니 SBCS니 해도 내부 구현은 WideCharToMultiByte()와 MultiByteToWideChar()를 사용하여 구현되어있습니다. 그리고 몇몇 부분들은 대충 구현한거라 최적화와는 거리가 먼 코드들이 존재합니다..
먼저 내부 구현부중에 위 코드는 자주 사용하는 인코딩 형식을 쉽게 사용할 수 있도록 만들어준 static 함수입니다. "Encoding::Default()" 이런 코드로 현재 윈도우즈의 코드페이지를 사용하는 인코딩 형식을 사용할 수 있도록 하기 위함입니다. 이 함수들은
내부적으로 이 함수를 사용합니다. GetEncoding() 함수는 코드페이지를 직접 입력하여 인코딩 형식을 사용할 수 있도록 하는 함수입니다. "Encoding::GetEncoding( 949 )" 이렇게 한국어 코드페이지 949를 사용하는 인코딩 형식을 가져올 수 있죠.
그리고 Convert함수를 사용하여 문자열의 인코딩 형식을 변환할 수 있습니다.

이외에도 Encoding 객체를 사용하여 wchar_t를 사용하여 표현되는 문자열을 char를 사용한 해당 인코딩형식으로 변환할 경우에 사용할 수 있는 여러가지 함수들이나 그 반대의 경우에 사용할 수 있는 함수들을 포함하고 있으면 코드페이지를 얻어오거나 인코딩 형식의 이름을 가져오는 함수 등 자주 사용되는 함수들이 포함되어 있습니다. 이 함수들에 대한 자세한 설명은 생략하도록 하겠습니다..


뭔가 급하게 끝나는것 같은 기분이 든다면 그건 착각입니다..


반응형
,