2016년 7월 28일 목요일

백준 알고리즘 11053 가장 긴 증가하는 부분 수열

안녕하세요. 전상수 입니다.

오늘도 DP문제 중에서 쉬운 문제에 속하는 문제를 풀어보겠습니다.


문제를 보면 수열 A가 있을때 가장긴 증가하는 수열의 길이를 구하는 문제입니다.

예를 들어 10, 20, 30, 40, 5, 6, 7, 8, 9, 10 이라는 수열이 있을 때 5,6,7,8,9,10 이 가장긴 증가하는 수열이 됩니다. 그래서 답은 6이 됩니다. 왜그런지 따로 설명은 필요없겠죠?

그럼 문제를 풀어보겠습니다.

예를 들어 풀어보면 10, 20, 10, 30, 20, 50 수열의 경우 50의 가장긴 증가하는 수열의 길이는

10, 20, 10, 30, 20( 50보다 작은 값 중에서 ) 중 가장 긴 증가하는 수열에서 + 1 을 한값과 같습니다.

수열을 arr 로 저장하고, 해당하는 인덱스에 가장긴 증가하는 수열의 길이를 DP로 저장하겠습니다.

이걸 식으로 나타내면 DP[n] = max(DP[n - 1] , DP[n - 2], DP[n - 3] , ..... , DP[1]) + 1 입니다.( arr[n-1] < arr[n] ,  arr[n-2] < arr[n], ... ,arr[1] < arr[n] 와 같이 수열의 값이 arr[n] 보다 작을때 입니다.)

코드는 아래와 같습니다.


주의 할 점을 보면
1. 수열의 마지막 수가 가장 긴 길이를 갖지 않는다.
2. 10, 20, 10, 30, 20, 50 에서 3번째 10처럼 이전 인덱스에 더 작은 값이 없는 경우 DP[인덱스]는 1을 가진다.

입니다.

감사합니다.



2016년 7월 26일 화요일

백준 알고리즘 11057 오르막수

안녕하세요. 전상수 입니다.

DP 알고리즘중 쉬운 문제에 속하는 문제를 풀어보겠습니다.

왼쪽에서 오른쪽으로 오름차순으로 올라가는 수를 오르막수라고 합니다.

12345 이런식으로 되는 수를 얘기하는데 이 문제는 특이하게도 같은 수가 연속해도 오르막수라고 합니다.

2234 의 경우 2->2(같은수라 오르막) 2-> 3(수가 증가하여 오르막) 3 -> 4(수가 증가하여 오르막)

왼쪽에서 오른쪽으로 증가하면서 지나가서 오르막 수 입니다.

3676의 경우 367까지는 오르막 이지만 7 -> 6 으로 내려가면서 오르막 수가 되지 않습니다.

DP배열을 만들때는 현재 상태를 저장해야 하는데 현재 상태를 저장하는데 필요한 변수를 보면 아래와 같습니다.

1. 현재 상태의 인덱스 (11119 에서 현재 상태의 숫자가 9라면 5가 되겠죠)
2. 현재 상태의 숫자 (11119 에서는 1 또는 9가 되겠죠)

1번을 i, 2번을 v로 하겠습니다.

현재 상태를 dp[i][v]라고 할때 이전에 올수 있는 수는 dp[i - 1][v와 같거나 작은수] 가 됩니다.

이걸 식으로 나타내면 dp[i][v] = dp[i - 1][v] + dp[i - 1][v - 1] .... + dp[i - 1][0] 이 될수 있습니다.

이런식을 제귀적으로 풀면 될 것 같습니다.

코드를 보면 아래와 같습니다.



2016년 7월 20일 수요일

서버란 무엇인가? - 물리 서버 / 논리 서버

안녕하세요. 유결입니다.

이번 포스트에서는 서버란 무엇인가에 대해 이야기하고자 합니다.
흔히 말하는 서버(Server) 여러분은 한마디로 딱 정의해서 말할 수 있나요?
서버의 사전적 의미로는 아래와 같습니다. [네이버 사전 인용]

1. In computing, a server is part of a computer network which does a particular task, for example storing or processing information, for all or part of the network.

2. In tennis and badminton, the server is the player whose turn it is to hit the ball or shuttlecock to start play.

3. A server is something such as a fork or spoon that is used for serving foodsalad servers.

서버는 특정 역할을 하는 컴퓨터 네트워크의 한 부분이기도 하며, 테니스나 베드민턴에서 서브를 시작할 차례인 사람을 서버라고도 하고, 음식점에서 서빙하는 사람을 서버라고도 합니다.

여기서 제가 이야기하고자 하는 서버는"특정 역할을 하는 컴퓨터 네트워크의 한 부분" 입니다.


- Physical/Logical Server


서버는 크게 물리적인 서버와 논리적인 서버로 나눌 수 있습니다.
위 그림에서 보듯, 사용자의 입력 및 HTML 생성을 담당하는 소프트웨어적인 'Web Server'와 요청에 따라 데이터를 제공하는 데이터베이스 기능을 제공하는 'DB Server'는 '논리적인 서버'라고 합니다. Web Server와 DB Server를 동작 시키는 물리적인 컴퓨터 자체는 '물리적인 서버'라고 합니다.


이 밖에도 Hypervisor 위에 올라가는 VM(Virtual Machine)도 서버 역할을 하며, 서버 역할을 하는 VM을 서버라고도 부르기도 합니다.

논리적인 서버는 IT전공자나 관심이 있는 분이면 대부분은 어느정도 알고 계실 것이라 생각합니다. 그렇다면, 물리적인 서버는 도대체 어떻게 생겼을까요?


- Physical Server Type

물리 서버는 크게 Tower, Rack, Blade 타입으로 분류 할 수 있습니다. Tower 타입은 대부분 익숙한 모양이죠? Rack 타입은 *Data Center에서 일반적으로 사용되며, Blade 타입은 분산처리, 클러스터링, 고속 연결 등을 고려하여 만들어진 서버입니다.
일반적으로 각각의 타입에 따라 용도가 다르고 가격도 천차만별 입니다.


- Data Center


*Data Center - 데이터를 모아두는 시설로 서버를 적게는 수백 대, 많게는 수만 대 동시에 운영하며 온라인 사업에 필수적인 설비를 제공하며, 고객과 기업 정보를 보관하고 서비스를 가능하게하는 시설입니다. 전기 사용량이 많은 데다 계속 가동해야 하기 때문에 ‘전기 먹는 하마’라고도 불립니다.



물리 서버의 구조와 동작은 일반 PC와 크게 다르지는 않습니다.
이 포스트에서는 Rack 타입 서버에 대해 좀 더 알아보고자 합니다.

- Rack Type Server (HP ProLiant DL360 Gen9)

Rack 타입 서버에도 수많은 종류가 있지만, 그 중 HP ProLiant DL360 Gen9 모델에 대해 알아보겠습니다.

Front View















1. Access Panel 
   뚜껑 내지 덮개라고 보시면 됩니다. 이름이 access panel로 지어진 것은 Rack에 마운팅하는 부분이기 때문(?)이라고 생각합니다.

2. Serial Label Pull Tab

3. HPE Universal Media Bay or NVMe
   직접 모니터, 키보드, 마우스 등을 연결해서 서버를 관리하게 되는 부분입니다.

4. Power On/Standby button and system power LED button
5. Health LED
6. NIC Status LED
   (4.~6.) 전원 버튼과 서버의 전원, NIC 상태 등이 정상인지 확인하는 LED 입니다.
   (ex. 정상이면 초록색, 이상이 있으면 노란색)

7. USB 3.0 Connector

8. Unit Identification Button & LED
   UID라고 하며 단순히 켜고 끄는 LED이자 버튼입니다. (서버의 전면부/후면부에 모두 있어, 전면부에서 켜고 후면부에서 끌 수 있음)
서버를 관리할 때, Rack에 수많은 서버가 마운팅 되어있기 때문에, 전면부에서 관리하다가 후면부로 가면 어떤 서버인지 헷갈림을 방지 할 수 있습니다.
   
9. SAS/SATA/SSD/NVMe Drive Bays
   여러 Disk를 장착하는 Bays 입니다.


Rear View













1. PCIe 3.0 Slots 1-3
   PCI(Peripheral Component Interconnect) 주변기기 포트

2. HPE Flexible Slot Power Supply Bay 2
3. Power Supply 2 Status LED
4. Power Supply 2 C13 Connection
5. HPE Flexible Slot Power Supply Bay 1
6. Power Supply 1 Status LED
7. Power Supply 1 C13 Connection
   (2.~7.) Power Supply가 2개 장착되며 상태 LED와 C13 전원 2개가 연결됩니다.

8. Video Connector

9. Embedded 4x1GbE Network Adapter
   내장 Network Adapter입니다.

10. Dedicated iLO 4 connector
   iLO(Intergrated Lights-Out)는 HP에서 만든 독자적 임베디드 서버 관리 기술로서, Network Adapter와 독립된 네트워크를 갖고 HTTPS로 웹을 통해 서버를 원격 제어 가능하게 합니다. 로컬 화면과 동일하게 원격가능. 즉, OS없이 부팅과정에서도 원격 가능

11. Serial Port Connector (Optional)

12. USB 3.0 Connectors (2)

13. Unit Identification LED

14. FlexibleLOM bay (Optional)
   추가 Network Adapter 장착 가능





1. 5 Standard Fans Ship for 1P and 7 Standard Fans Ship for 2P
   최대 7개의 펜 장착 가능

2. HPE Smart Storage Battery (Optional)
   Smart Storage Battery는 서버가 갑작스럽게 전원이 끊겼을 때 Write Cache(휘발성)에 임시 저장된 데이터를 보호하기위해 전력을 공급하는 역할을 합니다.

3. 2 Processors with HPE Smart Socket Guide
   2개의 Processor 장착 가능

4. MicroSD card slot

5. Dual Internal USB 3.0 connector

6. HPE Flexible Smart Array or Smart HBA (Optional)
   HBA(Host Bus Adapter)를 장착하는 부분

7. 2 HPE Flexible Slot Power supplies

8. Secondary PCIe 3.0 riser for PCIe slot 3 (requires CPU 2)

9. Embedded 4x1Gbe NIC
내장 Network Adapter

10. Primary PCIe 3.0 riser for PCIe slots 1 & 2

11. FlexibleLOM Bay (Optional)
  추가 Network Adapter 장착 부분

12. Embedded SATA Controller ports
   내장 SATA 컨트롤러 포트

13. 24 DDR4 DIMM slots (12 per processor)
   Processor당 12개의 메모리 장착 가능 (총 24개)


이상, 서버란 무엇인가에 대해 이야기해봤습니다. 서버는 어떤 것이라고 명확하게 정의하기는 어려운 것 같습니다. IT에 종사하는 분야마다 의미하는 바가 다르기도 하고.. 예를들면, 네트워크/서버 엔지니어와 서버/클라이언트(SW) 개발자가 말하는 서버는 조금은 다른 의미를 갖을 수 있다고 생각합니다.

다음에는 시간이되면 서버의 동작 원리에 대해 포스팅해보려 합니다.
여기까지 읽어주셔서 감사합니다.^^



Unity Particle System 이용하기


안녕하세요. 이관호입니다.

이번 포스트에서는 게임의 뷰를 담당하는 파티클을 사용하는 방법에 대해서 알아보도록 하겠습니다.

가장 먼저 파티클이 무엇이냐 ... 를 생각하시는 분이 있을텐데요.

파티클은 불꽃, 먼지, 폭발 등의 이펙트라고 생각하시면 쉬울거 같애요!

많은 렌더링과 수학적 함수가 들어가서 소규모의 인원으로 진행하는 프로젝트에서는 하나하나 만들려고 하면 인력과 시간 소모가 굉장히 많이 일어날 것입니다.

그래서! 유니티에서는 파티클 시스템이라는 것을 제공해주고, 왠만한 이펙트 효과는 모두 해결할 수 있도록 제공해주고 있습니다.

또한, 이미 만들어진 파티클을 가져와서 사용하여도 큰 문제는 없으니 게임 개발 후에 화려한 효과를 덧붙이기 원하는 분들에게는 최고의 시스템이라고 할 수 있겠죠??

본 포스트에서는 간단하게 사용하는 방법 정도만 적을 것입니다. 자신에게 맞는 심화된 효과를 원하시는 분은 독스를 참고하여서 효과를 만드시거나, 이미 만들어진 효과를 가져다 사용하시는 것을 추천드립니다. (뭐 난 코딩과 수학의 천재여서 직접 만들겠다 하시면 말리지는 않겠습니다 ..)

자 그럼 시작합니다!

1. 파티클을 만들어보자!

가장 먼저 파티클을 만들어 보겠습니다. 굉장히 간단해요!



메뉴 - GameObject - Particle System 을 선택하게 되면




위와 같은 가장 기본적인 파티클 시스템이 생성되게 됩니다.

하얀 먼지들이 위로 올라가는 것을 보실 수가 있어요!

그리고 오른쪽에 보시면 여러 메뉴가 있는 것을 보실 수 있습니다.

위로 올라가는 하얀 먼지를 저 메뉴의 값 설정을 통해 바꾸는 것으로 왠만한 효과는 만들 수 있습니다.

예를 들어



간단하게 Shape 의 속성을 살짝 바꿔보게 되면

위로만 올라가던 하얀 먼지들이 사방으로 뻗혀 나가는 것을 보실 수 있습니다!




좀더 이것저것 건들여 보게 되면 위와같이 흙먼지가 튀기는 모습을 볼 수도 있네요!

파티클 시스템의 각 속성이 무엇을 의미하는지는

http://docs.unity3d.com/kr/current/Manual/PartSysMainModule.html

위 유니티 독스를 참고하면서 개발을 하시면 될 것 같습니다!




2. 만들어진 파티클을 가져와 보자!

위에서 파티클 시스템을 이용하여 간단한 효과들을 만들 수 있다는 것을 살펴보았습니다.

근데도 이마저도 귀찮다 하는 분들이 분명히 계시리라 생각합니다 ... ㅠㅠ

우리의 훌륭한 유니티는 그런 분들을 위하여서도 기본 패키지를 제공해 줍니다!!



Asset - Import Package - ParticleSystem 을 클릭하시고 잠시만 기다리시게 되면




위와 같은 작은 창이 하나 생깁니다.

저는 싹다 가져오기 위해 모두 체크된 상태에서 Import를 눌러 봤습니다.





자! Import를 누르시고 잠시 기다리시면 위와 같이 프로젝트 폴터 2개가 생성되신 것을 볼 수 있습니다.

그중 Standard Assets - ParticleSystems - Prefabs로 들어가시면 완성된 파티클을 몇 가지 보실수 있습니다!




원하는 파티클을 드래그하시게 되면




반짝반짝 예쁜 구슬을 보실 수 있어요!!




그리고 이 구슬은 Particle System을 사용했는지 인스펙터창을 보시면 Particle System을 보실 수 있는데, 여기서 살짝 조작을 해주시면 구슬에서 빨간 먼지가 튀어 오르는 것을 보실 수 있습니다!



Flair라는 파티클을 넣어 보았는데 불꽃이 예쁘게 튀기네요. 불꽃놀이 등에 이 효과를 사용하면 좋을거 같네요!


근데 난 이 정도로도 만족하지 못한다... 난 지갑전사여서 돈으로 해결하겠다..!! 하시는 분들도 ... 있으실 겁니다 ...

그런 분들을 위해 전 세계의 유니티 개발자들이 만든 훌륭한 파티클 ... 에셋 스토어가 있죠

에셋 스토어에서 particle 을 검색하시면 다양한 파티클이 나오는데 무료는 얼마 없더라구요 .. 전 무료여도 효과가 굉장히 좋다고 느꼈는데 이로도 만족하지 못하시는 분들은 유료 효과를 구매하셔서 사용하시면 보다 나은 파티클을 감상하실 수 있습니다!!




 3. 파티클을 사용해보자!

이때까지는 파티클을 불러와서 속성 조정 등만을 해보았습니다.

그런데 파티클이라함은 무언가 폭발하거나, 캐릭터가 움직이거나 할 때 나오는 것인데 ... 그럴때는 어떻게 사용하냐는 생각을 하실 수 있습니다.

여기서는 방법이 여러가지가 있고, 그중 한개만 간단히 소개해 드리겠습니다.

우선 유니티 스크립트 개발을 어느 정도 해보신 분이라고 가정하겠습니다.

C#을 기준으로 스크립트에

public GameObject explosion;

를 만들게 되면 해당 스크립트가 부착된 오브젝트에 하나의 게임 오브젝트를 삽입할 수 있습니다.

감이 오시나요? 그렇습니다. 만들어둔 파티클을 게임 오브젝트로 부착해주시면 됩니다.

그리고 콜리전, 무브 등의 각종 상태에 대해 해당 파티클을 on/off나 Instantiate/Destroy를 이용해서 구현해주시면 됩니다.




2016년 7월 15일 금요일

Game State Machine 기반 게임 프로젝트에 디자인 패턴 적용하기

안녕하세요! 이주찬입니다.
이번 포스트에서는 Unity 게임 제작에 많이 쓰이는 Game State를 알아보고 리팩토링하는 과정을 함께하는 시간을 갖겠습니다.


1. Game State Machine

게임에는 다양한 등장인물이 있습니다. 그리고 각 인물들은 자신들의 고유한 동작을 합니다. 프로그래머는 이러한 동작을 코드로 구현하게 되는데요. 직접 게임을 만들어 본 독자들은 알겠지만 등장인물의 수와 동작들이 늘어날 수록 프로젝트는 엉망이 되기 쉽습니다.

예를 들어, 괴물을 무찌르는 용사 게임을 만든다고 생각해봅시다.
적어도 용사는 무찌를 괴물을 만나러 갈 능력은 있어야 합니다. 용사의 위치가 실제로 이동해야함은 물론이고, 게이머가 재미 비슷한 것을 느끼려면 팔다리를 앞뒤로 휘두르는 애니메이션과 저벅저벅 소리정도는 나줘야 합니다. 좀 더 현실적인 게임을 구현하고자 한다면 걷기/뛰기 기능을 만들 수도 있는데요. 게이머가 걷기 기능의 존재 이유를 궁굼해하지 않도록 하려면, 용사가 일정 시간만 뛸 수 있도록 스테미너 기능도 만들어야 할 것입니다. 물론 걷는 애니메이션과 뛰는 애니메이션은 달라야하고, 소리도 달라야 하겠지요.

역사상 최고의 노잼 게임 "ET" 마저도 주인공이 이동할 때 다리가 움직인다.
https://youtu.be/EFt-La3UUu0

비슷한 기능은 용사뿐만 아니라 괴물에게도 필요합니다. 괴물의 종류가 한 가지가 아니라면 종류별로 따로 구현해줘야겠지요. 이렇게까지 했는데도 용사와 괴물은 서로 마주칠 뿐 아직은 서로 싸우지도 못합니다. 개발이 진행될수록 개발이 아니라 노가다가 되는 이유입니다.


-개발 할 수록 늘어나는 것
  - 코드의 라인수가 늘어납니다.
  - 복사/붙여넣기의 횟수가 늘어납니다.

- 개발 할 수록 줄어드는 것
  - 코드를 자랑하고 싶은 마음이 줄어듭니다.
  - 게임이 완성될 가망이 줄어듭니다.



프로그래머들은 점점 복잡해지는 코드를 정리하기 위해 State(상태)를 도입했습니다.
위 예에서 용사와 괴물은 걷기/뛰기라는 공통 된 "상태"를 가지는데요. 그리고 충분히 뛰기를 하고나면 걷기로 "상태를 변경"하는 기능을 가집니다. 코드를 잘 보면 등장인물은 상태를 기준으로 각각에 맞는 애니메이션과 소리를 동일하게 구현해야 합니다.


- 용사에서 구현해야 할 것
  - 애니메이션
    - 걷기 상태 : 팔은 흔들지 않고 발만 움직인다.
    - 뛰기 상태 : 팔과 다리를 힘차게 흔든다.
  - 소리
    - 걷기 상태 : "저벅 저벅" 소리가 난다.
    - 뛰기 상태 : "타박 타박" 소리가 난다.

- 괴물에서 구현해야 할 것
  - 애니메이션
    - 걷기 상태 : 한 쪽 다리를 절면서 움직인다.
    - 뛰기 상태 : 앞다리까지 네 발로 움직인다.
  - 소리
    - 걷기 상태 : "그르렁" 소리가 난다.
    - 뛰기 상태 : "컹컹!" 소리가 난다.


따라서 각 상태에 맞게 필요한 것들을 차근차근 구현하다보면, 걷는 애니메이션을 빼먹고 구현하지 않는다던지 뛰고 있는데 걷는 소리가 난다던지 하는 실수를 줄일 수 있습니다. 또 단순히 스테미너가 다 닳았을 때 상태를 뛰기에서 걷기로 바꾸는 기능만 구현만 하면, 모든 구현을 일일이 바꾸지 않아도 되는데요. 다음 항목에서 보여드릴 Switch문을 이용하면 코드가 제법 깔끔하게 정리되어 멀리서 보면 멋지게 보이기까지 합니다.

2. Enum과 Switch를 이용한 구현

보통 Game State는 Enum과 Switch를 이용해서 구현합니다.

2.1. 구현

enum CharactorState {
  WALKING,
  RUNNING
}
Enum을 이용해서 위와 같이 상태를 정의합니다. 각 상태에 맞는 구현은 아래와 같겠지요?
class Warrior {
  private CharactorState state;

  public Warrior(CharactorState state) {
    this.state = state;
  }

  public void move() {
    switch (state) {
      case WALKING :
        // 용사가 한번에 한 걸음씩 걷는 코드
      case RUNNING :
        // 용사가 한번에 열 걸음씩 걷는 코드
    }
  }

  public void animate() {
    switch (state) {
      case WALKING :
        // 용사가 팔은 흔들지 않고 다리만 움직이는 코드
      case RUNNING :
        // 용사가 팔과 다리를 힘차게 움직이는 코드
    }
  }

  public void sound() {
    switch (state) {
      case WALKING :
        // 용사가 "저벅 저벅"거리는 코드
      case RUNNING :
        // 용사가 "타박 타박"거리는 코드
    }
  }
}
괴물도 똑같습니다.

2.2. 문제점

우리는 기존의 엉망진창 코드를 Enum과 Switch를 이용해 정리했습니다. 그 결과 프로그래머가 "실수 할 가능성"을 줄일 수 있었고, "가독성"있는 코드를 얻게 되었는데요. 그럼에도 불구하고 아직 부족한 점이 남아있습니다.

public void move() {
  switch (state) {
    case WALKING :
      // 용사가 한번에 한 걸음씩 걷는 코드
    case RUNNING :
      // 용사가 한번에 열 걸음씩 걷는 코드
  }
}
위 메서드를 보면, 아직 우리가 없애지 못한 실수의 가능성을 볼 수 있습니다.


- state를 바꿔적는 실수
  이런 멍청한 짓을 하는 프로그래머가 있을까 싶지만, 종종 이런 일을 벌이고 자책해본 경험이 한번씩은 있을 것입니다. 이 실수는 간단하면서도 짜증나는 결과를 가져오는데, 컴파일러가 아무런 불만도 하지 않고 넘어가기 때문에 좀처럼 찾아내기가 어렵기 때문입니다.

- 특정 state의 case를 구현하지 않는 실수
  이 실수는 앞선 것보다 더 하기 쉽습니다. 지금은 state가 두 가지밖에 안되지만 수십가지로 늘어나면 어떤 state를 구현했고 하지 않았는지 알기 어렵습니다. 팀원 중 하나가 말도없이 state를 추가하기까지 한다면, 나는 무엇이 잘못되었는지 조차 알지 못하는 상태에 이르게 됩니다. 결국 프로젝트를 마감을 앞두고 "어 왜 아무것도 안하지?"하는 팀원의 모습을 볼 수 있습니다.

- 존재하지 않는 state가 주어지는 경우
  Enum에 정의하지 않은 state가 있을 수 있는가 싶지만 실재로 가능합니다. Enum은 WALKING, RUNNING과 같이 특수한 상태를 가지는 것 처럼 보이지만 실재로는 0, 1, 2, 3 따위의 정수 값입니다. 별도의 코드를 작성하지 않으면 CharactorState는 아래 코드와 같습니다.

  enum CharactorState {
    WALKING = 0,
    RUNNING = 1
  }
  이는 CharactorState이 int값을 가짐은 물론이고, CharactorState에 int값을 캐스팅해서 넣을 수도 있다는것을 의미하는데요. 이를 이용하면 아래처럼 멍청한 코드도 가능합니다.
CharactorState state0 = (CharactorState)0; // 0이 캐스팅되어 WALKING이 되었습니다.
CharactorState state1 = (CharactorState)1; // 1이 캐스팅되어 RUNNING이 되었습니다.
CharactorState state2 = (CharactorState)2; // 2가 캐스팅되어 ???????이 되었습니다?
  안타깝게도 대부분의 컴파일러는 state2에서 아무런 불만도 하지 않습니다. 따라서 Warrior의 state가 항상 유효하다는 보장을 할 수 없게 되어버렸습니다. 역시나 프로젝트 마감을 앞두고 "어 왜 아무것도 안하지?"하는 팀원의 모습을 볼 수 있습니다.

- 여러 case가 공유하는 변수를 두고 싶은 유혹
  public void move() {
    int speed = 1;

    switch (state) {
      case WALKING :
        // 용사가 한번에 한 걸음씩 걷는 코드, speed는 1이다
      case RUNNING :
        // 용사가 한번에 열 걸음씩 걷는 코드
    }
  }
  충분히 switch문 밖에 변수를 둘 수 있습니다. 만약 걷는 속도가 너무 느리다고 생각돼서 speed를 2로 올리면 뛰는 속도는 어떻게 될까요? 뛰는 속도는 10을 곱한 20이 될까요, 아니면 9를 더한 11이 될까요? 단순히 여러 case가 변수를 공유하는 것 만으로도 코드는 불확실해졌습니다. WALKING을 바꾸고 싶었을 뿐인데 원치 않는 변화가 RUNNING까지 퍼져나갔기 때문이지요. 이러한 코드를 "산탄총 수술"이라고 합니다. 한 발의 총알로도 수술해야할 부위는 수십군데가 되버리기 때문입니다.


위 문제점 중 하나만 어기더라도 용사는 제 구실을 하지 못할 것입니다. 이러한 문제는 Warrior 클래스가 관리해야할 CharactorState가 늘어날수록 심각해지며, 어떠한 실수도 놓치지 않기 위해 들여햐 하는 노력도 코드의 양 만큼 늘어납니다. 결국 코드가 늘어날수록 엉망이 되는 것은 같네요.

객체지향 5원칙에 따르면 클래스와 메서드는 한 가지 기능만 책임져야 하며(SRP), 기능을 추가하기는 쉽고 이로인한 부작용은 없어야 합니다(OCP). 왜 그래야만 하는지 밝히는 것은 이 글의 범위를 벗어나서 생략하겠습니다. 하지만 CharactorState가 추가될수록 스크롤과 복사/붙여넣기 횟수는 증가하고, 원치 않는 기능의 변화들이 발생함은 분명하지요?

2.3. 파국으로 치닫는 코드

이 게임을 망치는 한가지 경우가 더 있습니다. 만약 CharactorState외에 GameState가 또 있다면 어떻게 될까요?
한참 유행했던 "OO런"류의 러닝 액션 게임을 보면, 특정 아이템을 먹었을 때 캐릭터가 빨라지면서 반짝거리고 다른 소리가 나는 것을 보셨을 것입니다. 이러한 게임 전역적인 상태를 관리하기 위해 아래와 같은 Enum을 정의할 수 있겠네요.
enum GameState {
  NORMAL,
  BONUS     // 캐릭터가 빨라지고 반짝거리며 다른 소리가 납니다. 물론 괴물이 그럴수도 있습니다.
}
GameState가 BONUS로 바뀜에 따라 우리 용사도 변해야 할 것입니다.
class Warrior {
  // ...

  public void animate() {
    switch (gameState) {
      case NORMAL :
        switch (state) {
          case WALKING :
            // 용사가 팔은 흔들지 않고 다리만 움직이는 코드
          case RUNNING :
            // 용사가 팔과 다리를 힘차게 움직이는 코드
        }
      case BONUS :
        case NORMAL :
          switch (state) {
            case WALKING :
              // 용사가 팔은 흔들지 않고 다리만 움직이지만 반짝거리는 코드
            case RUNNING :
              // 용사가 팔과 다리를 힘차게 움직이지만 반짝거리는 코드
          }
    }
  }

  // ...
}
우리가 원하던 "정리된" 코드는 아닙니다. 더 심각한 것은, GameState는 Warrior 클래스 뿐만 아니라 각종 괴물들 클래스에 뿔뿔이 흩어져 있습니다. Warrior 클래스는 메서드들을 서로 비교해가면서 실수를 줄여나갈 수 있었지만, 이제는 클래스와 파일을 넘나들며 확인해야만 합니다.
실수하는 것이 아주 쉬워졌네요.

3. 리팩토링

이 글의 제목을 보면 디자인패턴과 리팩토링을 통해 문제를 해결할 수 있다고 합니다. 바로 시작해봅시다.

3.1. 단계 1 - 함수로 추출하기

위 문제의 시작은, 하나의 메소드가 여러 기능을 책임지고 있는 것에서 시작됐습니다. 따라서 하나의 메소드가 하나의 기능만 하도록 해줍시다.
public void animate() {
  switch (state) {
    case WALKING :
      walkAnimate();
      break;
    case RUNNING :
      runAnimate();
      break;
  }
}

private walkAnimate() {
  // 용사가 팔은 흔들지 않고 다리만 움직이는 코드
}

private runAnimate() {
  // 용사가 팔과 다리를 힘차게 움직이는 코드
}
아직 animate()는 두 개의 기능을 책임지고 있지만, 적어도 walkAnimate()와 runAnimate()가 변수를 공유하지는 않게 됐습니다. 두 함수는 분리되어있기 때문에 한 쪽의 변화가 다른 쪽에 영향을 미칠 가능성이 낮아졌습니다.

3.2. 단계 2 - 클래스로 추출하기

이제는 animate()도 하나의 기능만 책임지게 해줍시다. 그 시작은 walkAnimate()와 runAnimate()를 다른 클래스로 분리 시키는 것부터 시작합니다.
class WalkState {
  public animate() {
    // 용사가 팔은 흔들지 않고 다리만 움직이는 코드
  }
}

class RunState {
  public animate() {
    // 용사가 팔과 다리를 힘차게 움직이는 코드
  }
}
메서드가 클래스로 빠져나왔으니, Warrior 클래스는 이런식으로 되어야 원래 기능을 유지하겠지요?
class Warrior {
  private CharactorState state;
  private WalkState walkState;
  private RunState runState;

  public void animate() {
    switch (state) {
      case WALKING :
        walkState.animate();
      case RUNNING :
        runState.animate();
    }
  }

  // ...
}

3.3. 단계 3 - 상속 관계로 묶기

두 개의 클래스가 추가된 것이 오히려 역행하는 기분입니다. 조금만 참고 두 클래스를 상속관계로 묶어봅시다.
interface IState {
  void animate();
}
class WalkState : public IState {/* ... */}
class RunState : public IState {/* ... */}
클래스를 상속 관계로 묶으면 Warrior 클래스에 추가될 클래스를 하나로 줄일 수 있습니다.
class Warrior {
  private CharactorState state;
  private IState walkrunState;

  public void animate() {
    switch (state) {
      case WALKING :
        walkState.animate();
      case RUNNING :
        runState.animate();
    }
  }

  // ...
}

3.4. 단계 4 - Enum과 Switch 없애기

상속 관계로 묶인 클래스는 다형성을 가집니다. 따라서 CharactorState와 Switch를 이용한 분기 없이도 상황에 따른 다른 동작을 할 수 있습니다.
class Warrior {
  private IState state;

  public void animate() {
    state.animate();
  }

  // ...
}
만약 state의 타입이 WalkState라면 state.animate()는 팔은 흔들지 않고 다리만 움직일 것이고, RunState라면 state.animate()는 팔과 다리를 힘차게 움직일 것입니다.


3.5. 결과

결국 코드는 이렇게 변했습니다.
interface IState {
  void move();
  void animate();
  void sound();
}

class WalkState : public IState {
  public move() {
    // 용사가 한번에 한 걸음씩 걷는 코드
  }

  public animate() {
    // 용사가 팔은 흔들지 않고 다리만 움직이는 코드
  }

  public sound() {
    // 용사가 "저벅 저벅"거리는 코드
  }
}

class RunState : public IState {
  public move() {
    // 용사가 한번에 열 걸음씩 걷는 코드
  }

  public animate() {
    // 용사가 팔과 다리를 힘차게 움직이는 코드
  }

  public sound() {
    // 용사가 "타박 타박"거리는 코드
  }
}

class Warrior {
  private IState state;

  public Warrior(IState state) {
    this.state = state;
  }

  public void move() {
    state.move();
  }

  public void animate() {
    state.animate();
  }

  public void sound() {
    state.sound();
  }
}
어째 코드가 더 늘어나 버렸습니다. 하지만 이제 이런 걱정은 하지 않아도 됩니다.


- state를 바꿔적는 실수
  애초에 생성자로 전달되는 IState state가 잘못되지 않은 이상 바뀌는 경우는 없습니다. 잘못 전달되더라도 일관성있게 변하기 때문에 찾아내기도 쉽습니다.

- 특정 state의 case를 구현하지 않는 실수
  마찬가지로 특정 state만 누락될 수 없습니다. 누락되더라도 모든 state가 누락되는 경우 외에는 일어날 수 없으며, 이마저도 state가 생성자를 통해 전달되는 경우에는 누락 자체가 일어날 수 없습니다.

- 존재하지 않는 state가 주어지는 경우
  IState state는 int로 캐스팅 되거나 하지 않습니다. 따라서 존재하지 않는 state가 있을 수 없습니다.

- 여러 case가 공유하는 변수를 두고 싶은 유혹
  각 case가 다른 객체로 분리되어있기 때문에 그런 코드를 작성할 수 없습니다.


이러한 코드는 확장도 쉽습니다.


- 새로운 state가 필요할 경우
  IState를 상속하는 새로운 클래스 정의하면 됩니다. 자연스럽게 IState가 요구하는 메서드도 함께 구현하게 됩니다. 그러지 않으면 컴파일이 안되니까요.

- 새로운 기능이 필요한 경우
  IState에 해당 기능의 메소드를 추가하면 됩니다. 자연스럽게 IState를 상속하는 모든 클래스는 해당 메소드를 반드시 구현해야만 합니다. 그러지 않으면 컴파일이 안되니까요.


4. State Pattern

위 리팩토링 과정을 거쳐 구현한 패턴은 "State Pattern"입니다. Gang Of Four(GoF)의 디자인 패턴을 참고하면 더 자세한 내용을 알 수 있습니다.


2016년 7월 14일 목요일

NoSQL과 MongoDB의 기본 개념

 안녕하세요? 김성중입니다.

 거의 모든 웹 앱은 정보를 (영속적으로) 유지하기 위해 데이터베이스에 의존합니다. 여기서 데이터베이스는 주로 관계형이거나 NoSQL입니다. NoSQL 데이터베이스는 이름이 의미하듯 SQL을 사용하지 않으며, 쉬운 사용법과 빠른 속도 덕분에 최근에 많이 사용되고 있습니다. NoSQL 데이터베이스는 문서 저장소, 객체 저장소, Key-Value 저장소로 구분할 수 있고, Redis, Memcached, Cassandra, MongoDB, Couchbase 등이 있습니다. NoSQL 데이터베이스가 일반적으로 관계형 데이터베이스보다 더 빠른 이유는 엄격한 데이터 일관성을 강제하거나 정해진 스키마를 따르지 않기 때문입니다.

 MongoDB란?

 몽고디비는 NoSQL 데이터베이스의 Document 저장소로 분류됩니다. 몽고디비는 데이터를 BSON(Binary JSON) 형태의 문서로 저장합니다. JSON을 기본 형식으로 사용하기 때문에 자바스크립트 앱을 위해 간편하고 편리한 인터페이스를 제공합니다. 몽고디비는 또한 상호 대화식 Shell, 데이터 불러오기/내보내기, 통계 보고용 앱과 같은 여러 도구도 함께 설치하여 사용할 수 있습니다.

 MongoDB 명령어

 몽고디비를 지역적으로 사용하려면 먼저 몽고디비 데몬인 mongod를 시작해야 합니다. mongod는 mongo 명령에서 비롯된 연결을 받아들입니다. 기본적으로 mongo는 localhost의 포트 27017에 연결을 시도하지만, --port와 --host 플래그를 사용해서 다르게 구성할 수 있습니다. 


 mongo로 몽고디비에 연결하면 아래 그림처럼 db 명령을 사용해서 현재 데이터베이스 이름을 볼 수 있습니다. 디폴트로 설정된 test 데이터베이스에 연결된 것이 보이시죠?


 다른 데이터베이스로 변경하려면 use 명령 뒤에 전환하고 싶은 데이터베이스 이름을 붙이면 됩니다. 예를 들어, yamiyami 데이터베이스로 전환하려면 다음과 같은 명령을 내립니다. 이 명령은 아직 yamiyami 데이터베이스를 생성하지 않았음에도 불구하고 동작합니다.



 새로운 데이터 삽입

 db는 현재 DB 이름을 출력하는 기능뿐만 아니라 데이터를 조작할 수 있는 DB의 최상위 객체이기도 합니다. 예를 들어 yamiyami 디비에 'sungjung'을 추가하고 싶다면, 먼저 `use yamiyami` 명령을 내려 yamiyami 디비로 전환합니다. 그 다음 아래 그림처럼 `sungjung`을 삽입합니다. 이 명령은 객체 {name:sungjung`}을 yamiyami 디비의 people 컬렉션에 추가합니다. yamiyami 디비는 아직 존재하지 않지만, 몽고디비는 이 디비를 바로 생성해 줍니다. 또한 show dbs와 show collections 명령을 사용해서 현재 yamiyami 디비와 people 컬렉션이 존재하는지 확인할 수 있습니다.


 또한, 아래와 같이 객체 배열을 전달하는 방법을 사용하면 insert() 호출 하나로 여러 항목을 추가할 수 있습니다.



 데이터 검색

 데이터를 디비에 추가했으므로 다시 읽을 수단이 필요합니다. 이를 위해 컬렉션의 find() 함수를 사용하면 됩니다. 아래 그림은 yamiyami 디비에서 컬렉션의 모든 항목을 검색한 결과입니다. 검색 결과에 보이는 _id 필드는 몽고디비에서 부여하는 객체에 대한 유일한 식별자입니다.


 아래 예제는 이름이 'sungjung'인 데이터를 검색하는 방법입니다. 검색 기준으로 name 필드에 /^sungjung/ (sungjung로 시작하는 문자열)이라는 정규표현식을 사용하였습니다. 더 정확한 질의를 원한다면 `db.people.find({name: 'sungjung}) 을 사용할 수 있습니다.



 데이터 갱신

 데이터는 컬렉션의 update() 함수를 사용해서 갱신합니다. 
  • @param1 : 질의 객체. 이 객체는 갱신해야 할 항목을 명시하여 find()와 동일 한 구문으로 사용
  • @param2 : 갱신되어야 하는 값.
  • @param3 : 옵션 객체.
  • 옵션1 - upsert : 값이 true로 설정될 경우, update()는 질의 기준에 맞는 기존 항목이 없을 경우 새로운 문서를 생성한다.(default: false)
  • 옵션2 - multi : 값이 true로 설정될 경우, updates()는 질의 기준과 일치하는 모든 문서를 변경한다.(default: false)
  • 옵션3 - writeConcern : 갱신의 쓰기 동작 방식을 정의.
 아래 명령어를 통해 'sungjung'은 'jeremy'로 바꼈고, 나이를 명시하기 위해 문서에 age라는 새로운 필드도 추가했습니다. 이는 RDB와 비교해서 NoSQL의 유연성을 보여주는 예임. 실행 시점에 NoSQL 디비는 개별 데이터에 새로운 필드를 정의할 수 있어요. 
 


 데이터 삭제

 마지막 CRUD 연산은 삭제입니다. remove() 함수를 사용하면 컬렉션에서 데이터를 삭제할 수 있습니다. 
  • @param1 : find() 형태의 질의 객체.
  • @param2 : 옵션 객체
  • 옵션1 - justOne : true로 설정하면 삭제가 하나의 문서로 제한된다.(default: false)
  • 옵션2 - writeConcern : update()의 writeConcern과 동일한 방식.
 아래 명령어를 통해 이름이 'sungjung'인 사람 중 한 명을 삭제하였습니다. name 필드가 'sungjung'인 데이터가 여러개 임에도 불구하고 한 명만 삭제되었습니다.



 컬렉션 삭제

 컬렉션의 drop() 함수를 사용해서 디비에서 전체 컬렉션을 삭제할 수 있습니다. 아래 명령어를 통해 yamiyami 디비 안에 있는 people 컬렉션을 삭제하였습니다.


 show collections 명령으로 해당 컬렉션이 더 이상 존재하지 않음을 확인하시면 됩니다.



 데이터베이스 삭제

 dropDatabase() 함수를 사용해서 현재 디비와 여기에 속한 모든 데이터를 삭제할 수 있습니다. 아래 명령어를 통해 yamiyami 디비를 삭제하였습니다.



2016년 7월 12일 화요일

Unity 5로 간단한 2D 터치 게임 만들기

안녕하세요? 유결입니다.
 야미야미가 만들 게임의 프로토타입을 Unity로 간단하게 제작해 보도록 하겠습니다. 간단하게 연습삼아 만든 프로토타입이고 디테일은 생략된 부분이 있으니 참고해주세요^^;

 먼저 게임에 대해 설명드리면, 파란색과 검은색 블럭들이 랜덤하게 생성되고, 하단에 두개의 UI 버튼을 클릭해서 블럭을 없애는 게임입니다. 라인 가장 아래 블럭(1층) 중 파란색 블럭만 제거 할 수 있으며, 블럭을 제거하면 왼쪽과 오른쪽 가장 아래 1층 블럭이 양쪽 모두 사라집니다. 파란색 블럭을 제거하면 score 100점을 획득하고, 검은색 블럭일 때 버튼을 누르면 score 100점을 잃습니다.



 먼저 Unity 2D 프로젝트를 생성합니다. 저는 세로모드로 게임을 만들었기 때문에 아래 그림과 같이 Game 뷰에서 1440x2560으로 해상도를 바꿨습니다.





다음으로, Main Camera의 속성 중 Far을 100으로 설정했습니다. 해상도나 Main Camera 시야 거리는 제작하시는 분이 적절하게 설정하시면 됩니다.




UI Button을 만들어 보겠습니다. Hierarchy 에서 마우스 오른쪽 클릭으로 UI > Button을 클릭해서 생성합니다.




왼쪽과 오른쪽 버튼 두개를 만들고 위치를 적절하게 배치해줍니다.




 이번에는 Hierachy에서 마우스 오른쪽 클릭으로 2D Object > Sprite를 클릭해서 Box를 만들고 버튼을 만들고, 박스가 너무 작아서 Scale 속성을 (7,7,1)로 설정했습니다. Sprite Renderer 에서는 기본으로 제공되는 Sprite 이미지를 사용했습니다.
Box가 너무 많아서 생성 과정 캡쳐는 생략하겠습니다. Box를 모두 생성하고 적절히 배치해준 후에, Boxes_R과 Boxes_L를 parent object로하고 각각 오른쪽과 왼쪽 box object들을 child로 집어넣었습니다. (사실 여기서 하위 오브젝트로 집어넣은 부분은 큰 의미는 없고 위치 조절을 편하게 하기위해 묶었습니다^^;) Boxes_R과 Boxes_L을 다시 Boxes의 child로 설정했습니다.




이번에는 UI text를 만들어 보겠습니다. Hierachy에서 마우스 오른쪽 클릭으로 2D Object > Text를 선택합니다.




Text Object 두개를 생성해서 하나는 Score_Text, 다른 하나는 Score_Value로 이름을 변경하고 폰트와 색깔을 변경해 줍니다. (폰트는 기본 제공이 아니므로 .ttf 파일을 따로 다운 받아서 사용하세요! 저는 폰트를 Assets 폴더 하위에 Font에 저장해 두었습니다.)




이제 Score_Value에 Script를 추가해보겠습니다. 여기서 Script를 추가하는 이유는, 점수를 획득할 때 Score_Value 값이 변경되는 것을 Script로 구현하기 위해서 입니다. (Score_Value에 Script를 추가하는 방법 외에도, 다른 object으 script에서 Score_Value를 참조해서 변경해주는 방법도 가능합니다. 여기서는 Score_Value에 직접 Script를 추가해보겠습니다.)
Add Component로 Score 스크립트를 생성합니다. (아래 캡처 이미지에서는 미리 생성되어 있습니다.)




자 이제 별거 없지만 코딩해보겠습니다. Unity의 UI Text를 사용하기 위해 먼저 
using UnityEngine.UI 를 추가해줍니다.
public float type으로 value를 선언 및 초기화 해주고, private Text type으로 scoreValue를 선언합니다. Awake() 내에서 scoreValue에 GetComponent<Text>()의 반환 값을 저장합니다. GetComponent<Text>를 사용하면 현재 object의 component인 Text를 얻어온다는 의미가 되겠습니다. 
Update() 함수는 계속 호출되는 함수로, scoreValue를 지속적으로 업데이트 해줍니다.
scoreValue.text에 public으로 선언했던 value를 string.Format() 함수를 통해 string type으로 업데이트 해줍니다.




이제 UI 준비는 끝났습니다. 이 게임의 동작을 위한 script를 Boxes에 추가합니다.



추가한 스크립트 내용중 변수를 선언하는 일부분을 보겠습니다. 아래와 같이 public GameObject 배열로 bxs_R과 bxs_L을 선언 했습니다.



 위의 script에 public으로 변수를 선언하면 아래와 같이 Unity Inspector 뷰에서 속성이 나타납니다. 오른쪽 상단에 자물쇠 표시로 해당 Inspector를 고정하고, Hierarchy의 Boxes_R 하위 Box0~11 과 Boxes_L 하위 Box0~11를 드래그해서 Bxs_R과 Bxs_L에 각각 내려놓아 참조시켜줍니다.




이제 전체 Script를 보도록 하겠습니다. 가독성과 효율성을 고려안하고 연습삼아 작성한 코드라 부끄럽네요... ^^;

참고로 Score object를 생성하고, 아까 위에서 만들었던 Score script의 public으로 선언한 value를 변경해주는 부분을 잘 봐주세요. 

아래 코드 중 햇갈릴 수 있는 부분이 Box의 position과 참조를 swap하는 부분인데, 이 부분은 그림을 직접 그려보시면 쉽게 이해할 수 있으리라 생각합니다!



자 이제 코딩은 끝났습니다. 왼쪽 버튼(LButton)에  Button Component를 보시면 On Click () 속성을 보실 수 있습니다. On Click () 은 해당 버튼을 클릭시 발생하는 이벤트를 등록해주는 부분입니다. 여기에 Boxes object를 드래그해서 참조시킵니다.



참조가 되었으면, Function 부분을 클릭해서 boxes > LClicked() 를 선택해줍니다.
LClicked()는 위에서 다루었던 boxes script에서 만든 함수입니다. 해당 함수가 호출되면 Boxes_down() 함수가 호출되고, score가 변경되는 부분입니다.
마찬가지로 RButton에도 같은 작업을 해줍니다.




이제 플레이를 해주시면 됩니다~!




자료도 정리가 잘 안되어있고 복잡하지만 끝까지 읽어주셔서 감사합니다. (_ _)
깔끔하게 포스팅하는 법을 익혀, 앞으로 노력해보겠습니다.^^;