프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf ·...

35
생명 게임 : Life game ( 프로그램 제작 실전 ) 20050511(Sunny Kwak) Endless Creation. (http://computer.snut.ac.kr/~ec) [email protected]

Upload: others

Post on 18-Sep-2019

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

생명 게임 : Life game (프로그램 제작 실전)

2005년 05월 11일

(Sunny Kwak)

Endless Creation. (http://computer.snut.ac.kr/~ec)

[email protected]

Page 2: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 2 / 35

Copyrights © 2005 Endless Creation, Inc. All rights reserved.

Other disclaimers The names of actual companies and products mentioned herein may be the trademarks of their respective owners.

Version Version 1.0 (draft)

History 2005년 5월 8일 일요일 First created.

2005년 5월 10일 화요일 Add framework, document view architecture

Page 3: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 3 / 35

Table of Contents

1. INTRODUCTION ..................................................................................................... 4

1.1 OBJECTIVES ............................................................................................................. 4

1.2 TARGET AUDIENCE ...................................................................................................... 4

1.3 선행 학습 ............................................................................................................... 4

2. 생명 게임(THE GAME OF LIFE)................................................................................... 4

3. 구현에 앞서서 ............................................................................................................ 7

3.1 먼저 구현하지 말라 ................................................................................................... 7

3.2 소프트웨어 생명주기(SOFTWARE LIFE CYCLE)...................................................................... 8

4. 생명 게임 제작........................................................................................................... 8

4.1 계획...................................................................................................................... 8

4.2 요구분석 ................................................................................................................ 8

4.3 설계...................................................................................................................... 9

4.3.1 개발 환경....................................................................................................................................... 9

4.3.2 소프트웨어 구조........................................................................................................................... 10

4.4 구현.....................................................................................................................12

4.4.1 MFC(Microsoft Foundation Classes).............................................................................................. 13

4.4.2 프레임워크(framework)................................................................................................................. 13

4.4.3 도큐먼트 / 뷰 구조 ...................................................................................................................... 15

4.4.4 SDI and MDI................................................................................................................................. 19

4.4.5 MFC Wizard ................................................................................................................................. 19

4.4.6 Visual C++ 실행 ........................................................................................................................... 20

4.4.7 CLifeGameDoc 구현 .................................................................................................................... 22

4.4.8 CLifeGameView 구현 ................................................................................................................... 28

4.5 테스트 및 유지보수 ..................................................................................................34

4.5.1 테스트.......................................................................................................................................... 34

4.5.2 유지보수 ...................................................................................................................................... 34

5. 정리 ...................................................................................................................... 35

Page 4: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 4 / 35

1. Introduction 생명(life) 게임은, 영국의 수학자 존 콘웨이가 만든 것으로, 간단한 규칙에 따라 패턴이 변해가는 것

을 감상하는, 일종의 장난감(?) 같은 것입니다. 비록 간단한 게임이지만 인공 생명에 대한 이론을 일반인이 이해하기 쉽도록 보여주는 좋은 사례입니다.

또한, 이 게임을 통해서 프로그램을 어떤 과정을 거쳐 개발하는지 상세한 개발 절차 및 다양한 개념들을 학습할 것입니다.

본 문서의 내용이 매우 다양한 기술에 대해 언급하고 있기 때문에 한번에 이해하기 힘든 부분도 많을 것이다. 그러나, 본 문서에서 언급된 기술에 대해서 인터넷이라거나 다양한 책자를 통해 학습한다면 일반적인 프로그램 개발 절차에 대해서 빠짐없이 정확하게 이해하게 될 것이라고 확신한다.

1.1 Objectives 인공 생명 이론에 대한 이해 소프트웨어 개발 절차에 대한 정확한 이해.

MS Visual C++ & MFC 개발 기초 Frame/Document/View 아키텍쳐 이해 윈도우 SDI(Single Document Interface) 어플리케이션 제작

1.2 Target Audience 프로그래밍을 배우는 학생 및 프로그램 개발 절차를 제대로 이해하고자 하는 모든 사람.

1.3 선행 학습 객체지향 개념 기초 이해 Visual C++ 언어 및 MFC 학습 (선행 학습 내용은 필수가 아니라 권장 사항이다.)

2. 생명 게임(The game of Life) John H. Conway에 의해 발명된 삶의 게임(The game of Life)은 출생과 제 1세대 생존, 죽음의 유전적 법칙의 모델을 가정한 것이다(1970년 10월, Scientific American. p.120참조). 가로와 세로로 25개씩의 정사각형을 갖는 판(총 625개의 정사각형)위에서 게임을 한다. 각 사각형은 비어 있거나, 생명체의

존재를 갖는 X 값을 가질 수 있다. 각 사각형(가장자리는 제외)은 8개의 이웃을 갖는다. 생명체의 다음 세대는 다음 기준에 따라 결정된다.

a. 출생 - 정확히 3개의 이웃을 갖는 빈 위치에서 태어날 수 있다.

b. 죽음 - 4개 이상의 이웃을 갖는 생명체는 과밀로 인해 죽게 된다. 둘 보다 적은 이웃을

갖는 생명체는 외로움으로 죽게 된다.

c. 생존 - 둘 또는 셋의 이웃을 갖는 생명체는 다음 세대까지 생존하게 된다.

게임판을 벗어난 곳은 생명체가 탄생할 수 없고, 생존할 수도 없는 곳으로 한다.

Page 5: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 5 / 35

[C로 배우는 컴퓨터 프로그래밍, 홍릉과학출판사, 429~430쪽] 아래는 또 다른 참고 글이다.

생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가 만든 것으로, 간단한 규칙에 따라 패턴이 변해 가는 것을 감상하는 일종의 장난감 같은 것입니다. 이 게임은 마틴 가드너가

자신의 칼럼 '수학적 게임(Mathematical Games, Scientific America, 1970년 10월 호)'에 소개하면서 유명해졌습니다. 생명은 무한히 넓은 바둑판과 같은 격자의 칸 몇 군데에 생명 하나씩을 심어 놓는 것으로 시작합니

다. 한 세대가 배치되어 있는 상황에 따라 다음 세대가 결정되는데, 그 규칙은 다음과 같습니다. 1. 한 생명의 주변 여덟 칸에 있는 생명의 숫자가 2 또는 3이면 그 생명은 다음 세대에 살아남고, 0

또는 1 이면 과소(過少)로, 4 이상이면 과밀(過密)로 죽습니다. 2. 비어 있는 한 칸의 주변 여덟 칸에 있는 생명의 숫자가 셋이면, 그 빈칸에는 다음 세대에 생명이 생긴다.

예를 들어, 깜빡이(Blinker)라고 불리는 다음의 생명을 봅시다(흰 네모는 빈칸을, 검은 네모는 생명을 나타냅니다).

□□□ □■□ □□□ □■□

■■■ → □■□ → ■■■ → □■□

□□□ □■□ □□□ □■□

설명을 위해 다음 그림처럼 번호를 붙입니다.

1 2 3 4 5 6

7 8 9 깜빡이는 처음에 4, 5, 6번에 생명이 있습니다. 한 세대가 지나도, 1번 칸은 아무 변화가 없습니다.

주변 생명이 둘 밖에 없기 때문입니다. 3, 7, 9번도 마찬가지입니다. 2번은 어떻게 될까요? 4, 5, 6번 세 곳에 생명이 있으니까, 2번 칸에는 다음 세대에 생명이 생깁니다. 8번도 같은 이유로 생명이 생깁니다. 4번 칸은, 주변 생명이 5번 한 군데밖에 없으니까, 다음 세대에 사망. 6번도 같습니다. 5번은

주변 생명이 4, 6번의 두 군데에 있으니까, 다음 세대에도 살아남습니다. 이렇게 해서 깜빡이의 두 번째 세대가 결정되고, 같은 방법으로 차례차례 다음 세대가 만들어집니다. 이 때의 모양이, 세 개의 연속된 생명이 한 번은 가로로, 한 번의 세로로 반복하기 때문에, 깜빡

이라는 이름이 붙여졌습니다. 이런 단순하기 그지없는 규칙에, 감상할 것이 뭐가 있나 하겠지만, 생명 게임에는 놀랄 만한 작품들이 많이 있습니다.

□■□□ □□□□ □□□□ □□□□ □□□□

□□■□ → ■□■□ → □□■□ → □■□□ → □□■□

■■■□ □■■□ ■□■□ □□■■ □□□■

□□□□ □■□□ □■■□ □■■□ □■■■

그림을 보면 알겠지만, 처음 모양이 세 세대가 지나면 다시 같은 모양이 되는데, 위치가 대각선으로 한 칸 이동합니다. 여러 세대에 걸쳐 보면 이 모양은 왼쪽 위에서 오른쪽 아래로 날아갑니다. 이 모양을 무어라 부를까요? 바로 글라이더(Glider)입니다.

Page 6: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 6 / 35

또 하나 생명 게임의 대표적이 작품 가운데 r-펜토미노(r-pentomino)가 있습니다. 다음 그림처럼 다섯 개의 생명이 영어 소문자 r과 비슷하게 생겨서 이런 이름이 붙었습니다('pento-'는 다섯을 뜻하는 말입니다).

□□□□□

□□■■□

□■■□□

□□■□□

□□□□□

고작 다섯 개의 생명으로 시작한 이 r-펜토미노는 폭발적으로 변화하다가, 69세대에 이르러 앞서 말한 글라이더를 발사합니다. 그러고도 계속 변화하면서 다섯 개의 글라이더를 더 발사하고 1103세대

가 되어서야 안정합니다. 생각보다 꽤 오래 가죠? (註. 현재 이 페이지에 올려져 있는 생명게임은 배열이 20x20의 크기이고, 여기에서 설명하는 생명게임은 크기가 무한히 넓습니다. 그래서 여기에 있는 프로그램으로 r-펜토미노를 시뮬레이션하면 가장자리에서 왜곡이 생겨서 글에서 설명하는 것처럼

의 모양은 나오지 않습니다.) 이 r-펜토미노는 컴퓨터가 지금처럼 막강하지 못했던 시절만 해도, '결과를 알 수 없는 생명'이었습니다. 1103세대에 이르러 깜빡이와 몇 개의 불변 형태, 그리고 날아가고 있는 여섯 개의 글라이더로

안정한다는 것이 밝혀진 것은 한참이 지난 후였습니다. 이제 당연한 질문이, "모든 모양은 결국 안정할 것인가" 하는 겁니다. 콘웨이 역시 그럴 것이라고 예상하면서도, 무한히 커지는 생명의 가능성을 몇 가지 제시했습니다. 첫째가 글라이더 총(Glider gun)

으로, 끊임없이 글라이더를 날려 보내는 생명입니다. 둘째가 칙칙폭폭 기차(Puffer train)로, 무한히 움직이면서 안정한 연기를 남기는 생명입니다. 실은 콘웨이가 예상한 이 두 생명이 모두 발견되어, '모든 생명이 안정할 것'이라는 추측은 부정되었

습니다. 다음 그림이 각각 '글라이더 총'과 '칙칙폭폭 기차'입니다. □□□□□□□□□□□□□□□□□□□□□□□□□□■□□□□□□□□□□□

□□□□□□□□□□□□□□□□□□□□□□□■■■■□□□□□□□□■■□

□□□□□□□□□□□□□□■□□□□□□□■■■■□□□□□□□□□■■□

□□□□□□□□□□□□□■□■□□□□□□■□□■□□□□□□□□□□□□

□■■□□□□□□□□□■□□□■■□□□□■■■■□□□□□□□□□□□□

□■■□□□□□□□□□■□□□■■□□□□□■■■■□□□□□□□□□□□

□□□□□□□□□□□□■□□□■■□□□□□□□□■□□□□□□□□□□□

□□□□□□□□□□□□□■□■□□□□□□□□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□■□□□□□□□□□□□□□□□□□□□□□□□

글라이더 총 □□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□

□□□□□□□□□□□■■■□□□□□□□□□□□■■■□□□□□□□□□□

□□□□□□□□□□■□□■□□□□□□□□□□■□□■□□□□□□□□□□

□□□□□□□□□□□□□■□□□□■■■□□□□□□■□□□□□□□□□□

□□□□□□□□□□□□□■□□□□■□□■□□□□□■□□□□□□□□□□

□□□□□□□□□□□□■□□□□■□□□□□□□□■□□□□□□□□□□□

□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□

칙칙폭폭 기차

Page 7: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 7 / 35

생명 게임의 특징 가운데 하나는, 미래가 결정되어 있으나 예측할 수 없다는 것입니다. 이것은 카오스 이론(Chaos theory)의 철학과도 닿아 있으며, 카오스 이론의 기본 원리인 나비 효과(Butterfly effect)쯤은 생명 게임에서는 아주 당연한 사실입니다. 한편, 단순한 규칙이 복잡한 양상을 낳는다는

점도 카오스 이론과 어느 정도 비슷한 점이 있습니다. 실제로, 카오스 이론이 나타나게 된 원인 중 하나가 비선형 편미분 방정식인데, 이것을 풀기 위한 방법으로 이 생명 게임의 원리를 응용한 셀룰러 오토마타(Cellular automata)이론이 있습니다.

콘웨이는 자기 책에서, 생명 게임의 세대가 지남에 따라 처음의 자기 모습을 계속 복제해 내는 생명이 있다고 주장했습니다. 즉, 단 하나뿐이던 객체가 무한한 시간이 지나면 똑같이 생긴 객체로 전 공간을 가득 채우게 된다는 말입니다. 이것은 실제가 아닌 컴퓨터에서만 존재한다는 것이 다를 뿐,

스스로를 복제 생산하는 생물의 정의에 꼭 들어맞는 것입니다. 생명 게임의 이 생명이야말로, 바로 인공생명이라 할 수 있는 것입니다.

[재미있는 영재들의 수학퍼즐, 박부성, (주)자음과 모음], 53∼55쪽에서 인용

3. 구현에 앞서서 3.1 먼저 구현하지 말라

Program을 만들기 위해서는 가장 먼저 문제해결능력(논리적 사고력, algorithm, logic, data structure

등)이 밑바탕이 되어야 하고, 둘째로 programming 원리와 개념(Object Oriented Program, Windows programming, Database, Web programming, Network, Software Engineering 개발방법론 등)이 확립되어야 하고 셋째로 기능적 테크닉(언어 문법, tool 사용, Coding 테크닉 등)을 구사할 수 있어야 합니

다. 그런데 Program을 공부하는 사람중 대다수는 빙산의 보이는 부분만을 보고 그것이 빙산의 전부인 것 같이 착각하듯이 program 하는데 당장 필요한 부분인 세 번째 기능적 테크닉만을 공부하기 때문

에 간단한 모방 program과 남의 source를 변형해서 사용하는 정도는 가능하지만 무에서 유를 창조하는 진정한 소프트웨어 엔지니어로서의 기초 소양은 부족한 것 같습니다.

본원의 SE과정에서는 소프트웨어의 개발회사에서 근무할 수 있는 초급 소프트웨어 엔지니어를 양성하는 것을 목적으로 하고 있습니다. 다양한 경험을 가지고 막힘없이 모든 일을 해결해 낼 수 있는 숙련된 프로그래머가 아니라 팀원으로서 본인에게 주어진 업무를 이해하고 모르는 부분이 있으면

스스로 해결하는 방법을 알아내서 적극적으로 대처할 수 있는 프로그래머지요. [박영만 전산전문학원 홈페이지 / FAQ에서 인용]

위 글에 본인은 매우 공감하는 바이다. 대부분의 개발자 혹은 개발 지망 학생들은 문제해결 능력이나 원리와 개념을 이해하기 보다는 테크닉 만을 공부한 후에 현장에 접근합니다. 물론 본인도 초보

시절 테크닉 만을 공부한 후 현장에 뛰어든 사람들 중에 한명 이었기에 굉장히 많은 기간을 밤샘과 고민으로 보냈고, 뒤늦게 잘못된 작업 방식을 깨달을 수 있었다.

우리의 목표는 ‘생명 게임’을 구현하는 것이지만, 결코 코드부터 작성하는 어리석음을 반복하지 않겠습니다. 다음 글을 읽기에 앞서 부디 앞의 인용을 다시 읽어 보시기 바란다. ‘무에서 유를 창조해내는 진정한 소프트웨어 엔지니어’. 이런 목표를 꿈꾸지 않는가?

Page 8: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 8 / 35

3.2 소프트웨어 생명주기(Software Life Cycle) SDLC(Software Development Life Cycle)는 소프트웨어 공학에서 가장 널리 쓰이는 개발방법론이며,

소프트웨어 개발 절차를 ‘계획’, ‘분석’, ‘설계’, ‘구현’, ‘테스트 및 유지보수’ 등 5 단계로 나눈다. 소프트웨어 개발방법론에 기초하여 ‘생명 게임’을 구현할 것이며, 이러한 절차는 소프트웨어 개발 현장에서 실제로 사용되고 있다는 점을 염두에 두기 바란다.

4. 생명 게임 제작 4.1 계획

‘생명 게임에 대한 개발 계획’이라 함은 소요 기간, 투입 인력, 팀원들의 역할 등을 서술하는 것이다.

그러나, 게임 자체의 기능이 단순하고 소요 기간이 매우 짧기 때문에 이를 생략하도록 하겠다.

4.2 요구분석 요구분석 단계에서는 구현하고자 하는 ‘목표’ (goal), 혹은 무엇을 구현하려는지 (what) 서술하는 작업이다. 이 단계에서는 ‘생명 게임’의 규칙(rule), 제약 조건 등을 서술한다.

생명 게임의 요구 분석 내용은 다음과 같다.

1. 가로와 세로로 25개씩의 정사각형을 갖는 판(총 625개의 정사각형)위에서 게임을 한다.

2. 각 사각형은 비어 있거나, 생명체의 존재를 갖는 X 값을 가질 수 있다. 3. 각 사각형(가장자리는 제외)은 8개의 이웃을 갖는다.

4. 생명체의 다음 세대는 다음 기준에 따라 결정된다. a. 출생 - 정확히 3개의 이웃을 갖는 빈 위치에서 태어날 수 있다.

b. 죽음 - 4개 이상의 이웃을 갖는 생명체는 과밀로 인해 죽게 된다. 둘 보다 적은 이웃을 갖는 생명체는 외로움으로 죽게 된다.

c. 생존 - 둘 또는 셋의 이웃을 갖는 생명체는 다음 세대까지 생존하게 된다.

5. 게임판을 벗어난 곳은 생명체가 탄생할 수 없고, 생존할 수도 없는 곳으로 한다.

요구 분석 단계에서는 게임을 어떠한 플랫폼(platform) –운영체제나 윈도우 환경인지 텍스트 환경인

지 등 프로그램이 동작하는 환경을 말한다-에서 동작하는지, 사용자 인터페이스는 어떤 특징을 가지는지, 프로그래밍 언어는 무엇을 사용하지는 정의하지 않는다. 즉, 개발을 위한 조건들이 아니라 오직 게임 제작을 의뢰하는 사용자 입장에서 최종 프로그램에 포함되어야 하는 조건(혹은 기능)들을

서술하는 것이다.

계획 분석 설계 구현 테스트 및

유지보수

Page 9: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 9 / 35

4.3 설계 설계라는 무엇인가? 한마디로 정의하자면 앞서 분석 단계에서 파악된 무엇을 ‘어떻게(how)’ 만들 것

인지 결정하는 단계이다. 또한, 개발자가 본인이 보유한 기술의 한계(limitation)를 명확히 하고, 구현할 수 있는 것과 구현할 수 없는 것을 가름해야 하는 단계이다. 개발자가 본인의 역량을 무시하고, 생명게임을 애니메이션 혹은 아바타 기술 등을 적용하고자 한다

면, 얼마나 많은 시간을 낭비하게 될까? ‘생명 게임’을 통해 우리가 얻고자 하는 목표는 화려한 그래픽이 아니라, 소프트웨어를 개발하는 과정을 이해하고, 간단한 ‘인공 생명’ 이론을 구현하고 시각적으로 확인하고자 하는 것이다.

개발자는 항상 자신의 한계를 명확히 이해해야 한다. 무한한 시간과 무한한 노력을 투입하면 인간이 하지 못하는 일은 없다. 그러나, 지금 유행하는 수준의 게임을 10년 걸쳐서 만든다면 10년 후에

누가 당신이 만들어낸 작품을 인정하거나 환영하겠는가? 자신이 지금 할 수 있는 일을 해야 한다. 그래야 여러분은 그 결과물로서 능력을 인정 받을 수 있다.

설계 단계에서는 다음과 같은 작업을 수행한다. 개발 환경 및 개발 언어 정의, 그리고 가장 중요한 소프트웨어 구조 설계이다. 소프트웨어 구조 설계는 다시 정적 요소와 동적 요소로 분류되며 이에 대한 상세한 절차는 이어지는 글에서 상세히 소개할 것이다.

4.3.1 개발 환경 우선 어떤 운영체제에서 실행하며, 그래픽 환경인지 텍스트 기반인지, 그래픽 환경이라면 2차원 그래픽인지 그리고 필요한 기술은 무엇인지 판단해야 한다.

이것이 언듯 아무것도 아닌 것으로 느껴질 수도 있다. 그리고, 보통 간단히 생각하고 넘어가게 된다. 그러나, 만약 여러분이 향후에 보다 복잡한 게임이나 어플리케이션을 개발해야 한다면 개발 환경을 결정하는 단계에서 여러분이 배워본적이 없거나 익숙하지 않은 기술, 운영체제를 고려해야 할지도

모른다. (Unix 운영체제라거나, DirectX 같은 고급 그래픽 기술, 3차원 그래픽, 그리고 P2P 통신 및 서버 운영, 데이터베이스 연결 등을 예로 들 수 있겠다.)

설계 단계에서 목표 시스템을 어떻게 구현할 지, 어떤 기술이 필요한지 정확히 파악하고 필요한 기술을 수용하는데 (혹은 습득하는데) 얼마나 걸릴지 가늠하지 못한다면 여러분은 90%의 확률로 프로그램 개발에 실패하게 된다. 이해하기 쉽게 말하자면, 이는 눈을 감고 100m 달리기를 하는 것과

같다. 아무리 사소한 프로그램을 개발하더라도 분석/설계를 건너뛰는 어리석음을 범하지 않기 바란다.

물론, 분석/설계를 하지 않아도 개발은 가능하다. 하지만, 프로그램 개발이라는 작업은 상당히 오랜 기간과 노력을 들여야 하는 과정이다. 누구의 시간이며, 누구의 노력인가? 여러분의 인생이다. 만약, 프로그램 개발을 자주 실패한다면 잃어버린 시간과 노력은 과연 무슨 의미가 있겠는가? 신중해야

한다. 자칫 여러분은 귀중한 시간을 허비하는 바람에 의도하지 않게도 선두권과 상당히 멀어져 버릴 수 있다. 그리고, 한번 벌어진 간격은 아주 좁히기 힘들다.

우리는 ‘생명 게임’을 구현하기 위한 환경을 다음과 같이 결정할 것이다.

운영체제 : MS Windows 계열 (XP, 2000 이상)

개발언어 : MS Visual C++ 6.0 & MFC library

프로그래밍 모델 : Object Oriented Programming

Page 10: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 10 / 35

어플리케이션 특성 : SDI application, Keyboard & mouse, simple menu, non-

networking, use Windows GDI API

위에서 SDI는 Single Document Interface이며, GDI는 Graphic Device Interface의 약자이다. 객체지향

프로그래밍 기법에 대해서는 다른 문서를 참고하기 바라며, SDI와 GDI는 추후 자세히 설명할 것이다.

4.3.2 소프트웨어 구조 소프트웨어는 단순히 정의하자면 명령문의 집합이지만, 복잡한 어플리케이션을 제작할 때는 이를

세분화 하고, 부품화, 모듈화하는 과정을 거친다. 소프트웨어를 구성하는 방법에 대한 이론들은 구조적 프로그래밍, 객체지향 프로그래밍 등 다양하게 나와 있다. 하지만, 이 문서는 소프트웨어 설계 이론을 해설하기 위한 목적으로 작성된 것이 아니므로 이에 대한 개념들을 이해하기 위해서는 ‘How

to program’이나 다른 문서들을 읽어보기 바란다. 소프트웨어는 크게 정적인 요소인 ‘데이터(data)’ 혹은 자료구조와 동적인 요소인 ‘명령문(statement)’

혹은 알고리즘으로 구성된다. 일반적으로 초보 개발자들은 이중에서 알고리즘을 먼저 생각하는(혹은 구상하는) 습관을 가지게 되는데 이는 본인의 오랜 경험 상 잘못된 접근 방식이다. 왜냐하면 소프트웨어가 동작하는 과정은 정적인 데이터를 동적인 명령문이 변경을 가하는 것이며, 그 결과를 사용

자들에게 보여주는 것이 최종적인 목표이다. 따라서, 변화의 대상인 데이터 없이 알고리즘을 먼저 생각한다는 것은 분명 앞뒤가 맞지 않다.

더욱 중요한 것은 아무리 좋은 알고리즘을 구상하더라도 자료구조가 변경되면 알고리즘 자체가 변경되어야 하는 사태가 발생한다. 좋은 소프트웨어는 좋은 자료구조에서 시작된다. 개발 경험을 어느 정도 갖춘 개발자들은 충분히 이해할 것이다. 데이터 구조가 바뀜으로서 급작스럽게 프로젝트 막판

에 밤샘하는 경험들을 한두번씩은 가지고 있을 것이다. 또한 자료구조가 지나치게 복잡해지면 덩달아 알고리즘 마저 복잡해지며, 심지어 프로그램 실행 성능까지 엄청나게 느려지는 사태가 벌어질 수도 있다.

사족을 달자면, 객체지향을 약간이라고 공부해본 사람이라면, 데이터와 명령문을 분리하여 분석하는 것이 옳지 않다고 여길 것이다. 게다가, 앞서 객체지향 프로그래밍 기법을 사용하겠다고 선언했다.

무엇이 잘못된 것일까? 다년간 소프트웨어 개발 현장에서 분석/설계/개발하는 과정을 거치는 동안 인간은 정적인 구조는 빠르게 분석할 수 있지만 동적인 요소를 분석하는 일에 대해서는 상당히 어려움을 느낀다는 것을 알게 되었다. 따라서, 객체지향 모델링을 하더라도 먼저 정적인 구조를 파악

한 후 여기에 동적인 요소를 추가하는 것이 더욱 빠른 분석 방식이라고 믿게 되었다. 따라서 우리는 먼저 정적인 구조를 파악한 후, 여기에 동적인 요소를 추가할 것이다. 물론 두가지는 하나의 구조로 결합되어 객체지향 모델링의 원칙을 충분히 따를 것이다. (여러분이 본인의 분석/설

계를 완벽히 따라할 필요는 없다.)

4.3.2.1 정적 구조 ‘생명 게임’의 정적인 요소는 무엇인가? 정적 구조는 데이터 혹은 자료구조라고 앞서 정의했고, 게임에서 나타나고 사라지는 ‘생명’들이 바로 게임의 데이터라고 할 수 있다. 앞으로는 표현의 모호함을

피하기 위해서 ‘생명’을 ‘벌레(worm)’이라고 부르겠다. 또한, 웜들은 임위로 존재하는 것이 아니라, 25 * 25의 격자내에서 존재하거나 사라진다. 따라서 격

자 역시 자료구조이다. 격자는 보드(board)라고 칭할 것이다. 일단, 요구분석 단계에서 나타난 조건에서는 2가지 자료구조가 나타나며, 현재 단계에서 이외의 것

Page 11: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 11 / 35

을 파악하기는 힘들기 때문에 2가지 요소에 대해 좀 더 상세히 설계하겠다. 또한 앞서 객체지향 프로그래밍 기법을 사용하겠다고 설명했기 때문에 각 데이터 구조가 객체인지 파악할 것이다.

웜(worm) 웜이 어떤 자료(data)를 포함해야 하는지 생각해보라. 웜은 움직이지 않기 때문에 속도라거나, 위치 정보를 가지지 않는다. 다양한 형태를 가지는 것이 아니기 때문에 그래픽 이미지나 에니메이션 효과도 필요없다. 단지 태어났다는 사실과 죽었다는 정보만을 표함한다. 즉, 2가지 상태를 나타내면 된

다. 2가지 상태를 나타내는 가장 작은 변수 타입은 boolean 타입이므로 웜은 부울린 타입 변수로 정의하면 된다.

자, 그렇다면 웜은 객체인가? 객체라는 것은 스스로 존재하는(의존적이지 않은), 다른 존재와 구분되는 모든 것을 의미한다. 따라서, 웜을 객체라고 정의해도 된다. 하지만, 생명 게임에서는 웜 스스로 무언가 행동하는 것이 전혀 없다. 그렇기 때문에, 객체가 아니라 변수로서 선언한다.

여러분이 객체 지향 기법을 공부하고, 클래스 개념를 이해하고 나면 간혹 모든 것을 객체로 만들어보려고 시도하게 된다. 하지만, 모든 것을 객체로 정의해야할 필요는 없다. 닭 잡는데 소잡는 칼을

쓰지 말라는 속담이 있다. 지나친 설계는 자원을 낭비하고, 프로그램 구조를 지나치게 복잡하게 만든다.

보드(board) 보드 혹은 게임판은 웜들이 저장되는 장소이다. 웜이 태어나고 죽는 공간이며, 웜을 포함하고 있다

라고 할 수 있다. 25 * 25의 격자를 프로그램에서는 어떻게 표현할 수 있을까? 프로그래밍 언어를 약간이라도 배운 사람이라면 쉽게 알 수 있을 것이다. ‘2차원 배열’로 선언하면 되겠다.

BOOL board[25][25]

보드는 객체로 간주할 수 있다. 25 * 25개의 웜을 포함하고 있으니 우선 데이터를 가지고 있으며, 개별 웜이 태어나고 소멸하기 위한 메소드와 전체 세대를 교체하기 위한 메소드들을 가져야 하기 때문이다. 구체적인 메소드들을 설계에서 정의하는 것이 원칙이기는 하나, 생명 게임은 단순하므로 구

현 단계에서 언급할 것이다.

외관 혹은 뷰(view) 실행되는 화면 모습은 아래와 같이, 단일 윈도우 내에 25 * 25개의 격자를 나타낼 것이다.

Page 12: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 12 / 35

4.3.2.2 동적 구조 정적 요소가 결정되었으니, 이를 이용해 무언가 변화하고, 보여지는 요소를 설계해야 한다. 동적 요소에는 프로세스 흐름, 사용자 인터페이스 (키보드, 마우스, 타이머 이벤트 처리) 그리고 알고리즘

등이 포함된다.

프로세스 흐름도 프로세스 흐름도는 프로그램이 시작하고, 게임이 진행되면, 종료하기 까지의 개략적인 과정을 요약한 것이다. 이를 통해 개발자는 자신이 만들하고 하는 프로그램의 개략적인 동작 과정을 정의할 수

있다. 프로세스 흐름도는 플로우 챠트(flow-chart) 형태로 작성하는게 편리하다.

사용자 인터페이스 우선 키보드 동작을 고려할 수 있다. 그런데, 생명게임을 매우 단순한 게임이므로 키보드 입력은 없

는 것으로 설계한다. 마우스 이벤트는 왼쪽 마우스 버튼과 오른쪽 마우스 버튼을 구분하여 정의한다. 좌측 마우스 버튼

을 클릭하면 마우스 버튼을 클릭한 좌표를 계산하여 해당 격자내에서 웜을 탄생시킨다. 우측 마우스 버튼을 클릭하면 게임이 정지된 상태라면 게임을 시작하고, 게임이 진행되는 중에 클릭하면 게임을 중단시킨다.

타이머 이벤트는 마우스 오른쪽 버튼을 클릭했을 때부터, 한번 더 클릭할 때까지 주기적으로 발생하게끔 한다. 즉, 타이머 이벤트가 발생할 때마다 웜들이 태어나고 죽는 세대 교체가 진행된다. 달

리 표현하자면 타이머에 의해 게임이 진행된다.

알고리즘 생명이 나타나고 사라지는 세대교체 알고리즘은 구현 단계에서 상세히 설명하겠다.

4.4 구현 소프트웨어 제작에 있어서, 구현(implementation) 혹은 코딩(coding)은 아주 뒤늦게 진행되는 작업이

다. 즉, 가장 우선순위가 낮은 일이라는 것이다. 그렇다고 코딩이 중요하지 않다라고 말하는 것은 아니다. 코딩을 얼마나 잘하느냐에 따라 최종 소프트웨어의 완성도가 결정되기 때문에 매우 중요한

프로그램 시작

프로그램 종료

웜 생성 (사용자 동작)

게임 진행

(일정 시간마다 세대 교체)

Page 13: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 13 / 35

과정이다. 그러나, 우리는 이제 눈을 돌려 건축이나, 전자제품 혹은 자동차 산업을 생각해보도록 하자. 세상에

어떤 회사에서도 제품(혹은 건물)을 기획하고 설계하는 과정을 거치지 않고 바로 제작하는 경우는 없다. 그리고, 공장에서 제품을 제작하는 과정은 아주 낮은 중요도를 가진 일일 뿐이다.

그런데, 소프트웨어 업계는 어떤가? 아직도 수공업을 벗어나지 못하고 있다. 장인의 능력이 제품의 성능을 좌우한다. 이런 모습이 10년, 20년 후에도 지속될 것인가? 아니다. 건축이나 의학, 기계공학, 자동차 산업들은 이미 수천년에서 수백년을 넘긴 산업들이다. 반면에 소프트웨어 산업은 이제 60년

을 조금 넘었을 뿐이다. 그러니, 소프트웨어 산업도 다른 산업과 유사한 형태로 전환될 것이다. 그렇다면, 소프트웨어 개발의 자동화 전환 시점은 언제인가? 소프트웨어의 발전 속도는 다른 산업의

발전에 비할 수 없기에 상당히 빠르게 다른 업종의 수준을 따라잡을 수 있을 것으로 예상한다. 아마도 우리 세대에서 볼 수 있으리라. 그렇다면 코딩하는 일은 사실 대단한 대우를 받지 못할 것이다. (물론, 분석/설계 기술을 익히기에 앞서 구현 기술을 익힐 필요는 있다. 그래야 소프트웨어 제작

과정 전체를 이해할 수 있기 때문이다.)

4.4.1 MFC(Microsoft Foundation Classes) 설계 단계에서 Visual C++ 6.0과 MFC library를 이용해 개발한다고 결정했다. 여기서 Visual C++은

마이크로소프트 사에서 판매하는 C++ 언어를 위한 컴파일러 이면서, 비주얼 소프트웨어 통합 개발 환경(IDE : Integrated Development Environment)이다. 통합 개발 환경이라는 말의 의미는 단지, 소스를 컴파일하여 실행 파일을 만들어 주는 기능 뿐만 아니라, 소스를 편집하고, 컴파일하고, 디버깅하

는 기능과 화면 디자인을 위한 도구 등 다양한 보조 수단을 한꺼번에 제공하고 있다는 뜻이다. (유사한 통합 개발환경으로는 델파이, 이클립스, 넷빈즈 등이 있다.)

MFC는 Microsoft Foundation Library의 약자이다. MFC는 객체지향 기법으로 설계/구현된 윈도우 개발을 위한 클래스 라이브러리이자 개발 프레임워크이다. 달리 표현하자면, 개발자들이 손쉽게 윈도우 프로그램을 구현할 수 있도록 미리 만들어진 다양한 부품들의 집합이라고 하겠다. 여러분을 MFC가

제공하는 클래스와 함수들을 사용하거나, 혹은 변형시켜서 각자의 고유한 프로그램을 개발할 수 있다. 일단, 프레임워크라는 용어의 의미에 대해서 이해하도록 하자.

4.4.2 프레임워크(framework) 프레임워크는 특정 도메인 또는 기능 군에 속한 응용 소프트웨어 개발에 공통적으로 사용되는 구성

요소와 이들의 아키텍처를 일반화해 부분적으로 구현한 소프트웨어 시스템이라 할 수 있다.

스트럭처? 아키텍처? 프레임워크! 스트럭처(Structure), 아키텍처(Architecture), 프레임워크(Framework)란 용어는 기술과 시대가 변하면서 조금씩 그 의미를 달리해가고 있습니다. 스트럭처(structure)가 트리(Tree)와 같은 계층적

(Hierarchical)인 기반 구조를 말하는 반면, 프레임워크는 다소 수평적인 의미를 갖는 하부 구조를 나타냅니다. 또한 아키텍처는 더 포괄적인 개념으로 이 두 부분을 모두 포함하는 체계적인 기반 구조를 의미한다.

이 때, 프레임워크란 용어는 스트럭처나 아키텍처보다 더 낮은 레벨의 의미를 갖습니다. 즉 프레임워크의 실체는 때론 API의 집합으로 나타나기도 한다는 것입니다. 그러나 최근에 와서 IBM의 '샌프

란시스코 프레임워크' 또는 MS의 '.NET 프레임워크'라는 용어가 등장하면서 '반 제품'의 의미를 강하게 띄고 있다.

Page 14: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 14 / 35

이러한 샌프란시스코 프레임워크와 .NET 프레임워크는 정형화된 업무를 위한 비즈니스 컴포넌트를 미리 만들어두고, 이를 조립함으로써 생산성을 극대화하자는 것이 요지입니다. 현재의 프레임워크란 것은 '기반,틀,구조' 라는 모호한 추상적인 개념 보다는 물리적인 실체이면서 반 제품 성격의 구체적

이고 체계화된 API를 제공하는 개념에 더 가깝다.

자동차 제작으로 알아본 프레임워크 자동차와 냉장고의 모든 각종 부품이 전부 분해돼 바닥에 서로 섞여 있는 모습을 상상해보자. 수많

은 볼트와 넛트·게스킷·파이프·심지어 팬치와 드라이버까지 마구 섞여 있다. 이러한 부품과 연장을 이용해 우리가 만들려는 것은 파란색 자동차이다.

물론 완전히 분해된 부품들을 하나하나 조립해 파란색 자동차를 만들어낼 수는 있다. 하지만 상당한 시간이 걸릴 것이다. 자동차를 만드는 데 전혀 쓸모없는 냉장고 부품은 걷어내고, 엔진조립부터 시작해 페인트칠까지 하다 보면, 갖가지 일이 발생할 것이다. 그렇게 처음부터 우왕좌왕 하다보면,

납기는 가까이 오고 제때 완제품을 못 만드는 경우가 발생하기 쉽다.

그러나 엔진과 기어 변속장치, 동력 전달장치 등 각종 단위 부품을 미리 조립해둔 반 제품을 이용

한다면 최종적으로 자동차를 만드는 기간은 엄청나게 단축시킬 수 있을 것이다. 완제품을 만드는 사람은 엔진구조 공학은 모르지만 단지 최종적인 조립을 위한 최소한의 '조립 공정'과 기술만 보유하고 있어도 된다.

만들려는 자동차를 SI(System Integration) 프로젝트를 통해 완성시켜야 할 최종적인 시스템에 비유한다면, 각종 부품들은 API(Application Programming Interface)에 해당될 수 있다. 이렇게 자동차를

더 빠르게 생산하기 위해 미리 조립해 제공하는 반 제품과 각종 부품에 해당하는 API를 일컬어 프레임워크라 할 수 있다.

이 정도 해설로 완벽히 이해하기는 힘들다. 프레임워크는 일단 써봐야 확실히 감이 오는 개념이다. 일단 어렴풋이 이해한 것으로도 충분하다 그리고, MFC는 프레임워크의 한 종류이다. 중요한 것은 프레임워크를 사용함으로써 API로 개발하는 작업에 비해서 개발 기간이 줄어든다는 점을 이해하고,

받아들여야 한다. 단점은 API를 직접 다루는 것보다 섬세한 제어를 하기 힘들다. 권장하는 학습 과정은 빠른 개발 속도가 필요한 곳에서는 프레임워크를 사용하며, 정교한 제어가 필요한 시점에서는

Page 15: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 15 / 35

API를 사용할 줄 알아야 한다. 물론 둘 다 혼합하여 개발하는 것도 가능하다.

4.4.3 도큐먼트 / 뷰 구조 프레임워크는 단위 부품을 조립하여 반제품 형태로 제작한 것이라고 했고, 따라서, 완성품은 많지

않은 반제품 몇 개를 조립하는 과정이라고 생각할 수 있다. 그렇다면, 완성품을 구성하기 위한 부품을 몇 개로 쪼갤 것이며 어떻게 쪼갤 것인지 결정해야 한다. 그래야, 반제품의 형태를 정의할 수 있을 것이다.

자동차를 만드는 프레임워크라면 자동차 엔진, 차량 하체, 그리고, 덮개(sash) 이런 식으로 반제품들을 분리하면 될 것이다. 윈도우 어플리케이션이라면 어떨까? 어떻게 반제품을 구성할 것인가? 이에

대한 마이크로소프트의 답안이 도큐먼트 / 뷰 아키텍쳐 이다. (도큐먼트/뷰 구조는 MS의 고유 기술은 아니다.)

도큐먼트 그리고 뷰 윈도우 어플리케이션의 주된 부품은 윈도우라는 것은 이미 다들 알고 있는 사실이다. 하지만, 모든

프로그램은 내부에 데이터와 알고리즘을 담고 있으니, 윈도우 어플리케이션은 데이터와 알고리즘을 담고 있는 도큐먼트라는 컴포넌트와 도큐먼트의 내용을 화면에 보여주는 윈도우라고 부르는 뷰(view)로 나뉠 수 있다. 이것을 그림으로 표현하자면 아래와 같다.

도큐먼트 뷰 구조의 기본 철학은 애플리케이션에서 관리하는 데이터와 그 데이터를 보여주는 방법을 분리시킨다는 것이다. 도큐먼트 / 뷰 구조를 사용하는 개념은 언듯 생각하기에는 소프트웨어 구조가 불필요하게 복잡해지는 것은 아닌지 하는 의구심을 불러일으킨다. 왜, 윈도우 프로그램은 윈도

우만으로 표현하면 안되는 것일까? 이런 기술이 도입된 이유는 대부분의 어플리케이션에서 동일한 데이터를 이용해서 챠트, 그래프, 표

그리고 프린터로 출력하는 등 각기 다른 형태의 뷰(혹은 출력)로 표현하는 경우가 많기 때문이다. 각기 다른 윈도우에서 동일한 데이터를 복사해서 가지고 있거나, 데이터를 특정 윈도우가 가지고 있는 것보다는 도큐먼트라는 모듈에서 한 번만 저장되고 수정되는 것이 편리하다. 그리고, 뷰는 도

큐먼트의 내용을 영화관의 스크린처럼 투영하여 보여주는 것라고 생각하면 이해하기 쉽다.

document View

Page 16: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 16 / 35

하나의 도큐먼트에 다양한 뷰를 연결함으로써 오른쪽 그림과 같이 다양한 방식으로 화면에 출

력할 수 있다. 우측 그림에서 첫번째 방식은 하나의 도큐먼트

를 두 가지 형태의 윈도우에서 표시하는 것이며, 두번째 방식은 하나의 문서를 하나의 윈도우에서 표시하되 분리 바(split bar)를 이용해 두 개

의 분할 창으로 표시하는 경우이다. 세번째 방식은 하나의 문서를 텍스트와 그래프 형태 두가지로 보여준다.

개념적으로는 도큐먼트 / 뷰 아키텍쳐를 어느 정도 이해했기를 바란다. 이해하지 못했다면, 미안하지만 위 글들을 다시 읽어보기 바란다. 이 부분을 이해하지 못하면 이후의 구현 단계 역시 이해하기 어렵기 때문이다.

도큐먼트 / 뷰 클래스 만약 개념을 이해했다면, 이제 도큐먼트 / 뷰 구조가 물리적으로 소스(혹은 클래스, 부품) 차원에서는 어떻게 구성되고 동작하는지 알아볼 차례이다.

docume

chart

graph, table

print

Page 17: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 17 / 35

MFC 에서는 기본적으로 Application, Document Template, Document, Frame, View 등 5가지 클래스을

이용해 어플리케이션을 구성한다. 갑자기 3개의 클래스가 추가되었다고 너무 당황하기 말기 바란다. 5개의 클래스는 모두 MFC 마법사에 의해서 자동으로 생성되거나 미리 만들어져 있다.

이제 5개의 클래스가 어떤 역할을 하는지 하나씩 설명하도록 하겠다. <Application 클래스>

C, C++ 언어 혹은 자바 언어를 배워본 사람들은 모든 어플리케이션들은 main() 함수에서부터 시작한다는 것을 알고 있을 것이다. MFC에서는 main() 함수를 대체하는 Application 클래스를 제공한다. MFC를 이용해 작성된 모든 어플리케이션은 CWinApp 클래스에서 상속된 Application 클래스를 가진

다. Application 클래스의 이름은 C + 어플리케이션 이름 + App 가 된다. 예를 들어, 프로그램 이름이 LifeGame이라면 어플리케이션 클래스 명칭은 CLifeGameApp가 된다.

사용자가 MFC로 개발된 어플리케이션을 실행시키면, 가장 먼저 Application 클래스의 객체가 생성되며, 어플리케이션을 초기화 하기 위해 Application 객체의 InitInstance() 메소드가 호출된다. InitInstance() 메소드 내에서 Document Template 클래스 객체를 생성한다.

아래는 InitInstance() 메소드에서 DocumentTemplate 객체를 생성하는 예제이며, 아래 소스는 MFC 마법사에 자동으로 생성된다.

// Register the application's document templates. Document templates

// serve as the connection between documents, frame windows and views.

CSingleDocTemplate* pDocTemplate;

pDocTemplate = new CSingleDocTemplate(

IDR_MAINFRAME,

RUNTIME_CLASS(CLifeGameDoc),

RUNTIME_CLASS(CMainFrame), // main SDI frame window

RUNTIME_CLASS(CLifeGameView));

AddDocTemplate(pDocTemplate);

<Document Template 클래스>

Page 18: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 18 / 35

위 예제 소스를 살펴보면 CSingleDocTemplate 객체가 CLifeGameDoc, CMainFrame, CLifeGameView 클래스를 감싸고 있다는 것을 알 수 있다. CSigngleDocTemplate 클래스는 DocumentTemplate 클래스의 하위 클래스이다. DocumentTemplate 클래스는 2가지 종류가 있으며, 이는 SDI와 MDI를 설명하는

다음 단락에서 설명할 것이다. DocumentTemplate 객체는 Mainframe, Document, View 클래스 객체를 생성한 후, 세 개의 객체가 상

호 협력할 수 있도록 중재자(coordinator) 역할을 수행한다. DocumentTemplate 객체의 역할은 의외로 단순하다고 할 수 있다.

<Document 클래스> 문서(Document) 객체는 DocumentTemplate 객체에 의해 자동으로 생성된다. 하지만, 문서 클래스 내부에 각종 자료 구조와 이를 가공하는 메소드를 만드는 작업은 개발자가 직접해야 한다. MFC에

의해 자동으로 생성되는 코드는 단지 껍데기에 불과하다. <Main Frame Window 클래스>

여러 윈도우 어플리케이션(메모장, MS 오피스, 익스플로러 등)들을 보면 타이틀바, 메뉴 그리고 툴바(toolbar)와 상태바들을 가지고 있다는 점에서 모두 공통된 인터페이스를 제공하고 있다는 것을 알 수 있을 것이다. MainFrame 클래스는 윈도우 어플리케이션 내에서 앞서 말한 타이틀바, 메뉴, 툴

바, 상태 바 요소들을 화면에 표시하고 제어하는 모듈이다. 따라서, 개발자는 이러한 요소들을 제어하기 위해서 프로그램 코드를 작성할 필요가 거의 없다.

<View 클래스> View 클래스는 화면 상에서 MainFrame 클래스에 의해 둘러싸여 있는 안쪽 영역이라고 할 수 있다. 개발자는 문서의 내용을 View 클래스에 출력함으로써 사용자에게 보여준다. 또한, 마우스와 키보드

입력을 받아 처리하는 기능도 대부분 View 클래스에 포함된다.

이제 정리해보자. MFC 어플리케이션이 실행되면 우선 Application 객체가 생성되며, 어플리케이션 객

체는 DocumentTemplate 객체를 생성한다. DocumentTemplate 객체는 MainFrame, View, Document 객체를 생성하게 되고, 3가지 객체가 상호 협력하면서 프로그램이 동작하게 되는 것이다.

CNetmsgView

CMainFrame

Page 19: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 19 / 35

CNetMsgAppCNetMsgApp

create main frame& view window

start process

CMainFrame / CNetMsgView

CLoginDlg

CSearchUserDlg

CNoticeDlg

CNetMsgDoc

create document

CNetMsgAppCNetMsgAppCNetMsgAppCNetMsgApp

create main frame& view window

start process

CMainFrame / CNetMsgView

CLoginDlg

CSearchUserDlg

CNoticeDlg

CNetMsgDoc

create document

위 그림은 메신저 프로그램에서 각각의 클래스가 어떤 상관관계를 가지는지 나타내는 그림이다.

4.4.4 SDI and MDI MFC는 도큐먼트 / 뷰 아키텍쳐 기반의 프레임워크는 다시 두 가지 유형으로 분리된다. SDI는 Single Document Interface 의 약자로서 한번에 하나의 문서만을 작업할 수 있는 어플리케이션을 의미한다. 메모장이나 그림판과 같은 응용프로그램이 좋은 예가 된다. 이들 프로그램은 한번에 한 가지 종류

의 문서 작업 만이 가능하다. SDI 응용 프로그램으로는 단 하나의 문서 작업 그리고, 한 가지의 타입의 도큐먼트 작업만이 가능하지만, MDI(Multi Document Interface)는 동시에 여러개의 도큐먼트 작업이 가능하며, 또한 각각의 도큐먼트가 같은 타입이 아니어도 가능하다.

생명 게임은 뷰나 도큐먼트가 하나로서 충분하므로 SDI를 선택하였다.

4.4.5 MFC Wizard 개발자가 MFC 프레임워크(framework)를 사용하기 쉽도록 하기 위해서, Visual C++에서는 MFC 마법

사라는 기능을 제공한다. MFC 마법사(wizard)는 여러분의 만들고자 하는 프로그램을 위한 몇가지 조건을 제시하면 자동으로 프로그램 틀(template)과 소스들을 자동으로 만들어준다.

건축에 비유하면 집 모양에 대한 기본 조건을 제시하면, 기둥과 지붕 등을 한 순간에 지어주는 마법사이라고 할 수 있다. 원하는 집의 뼈대가 순식간에 갖추어진 후, 집안의 인테리어를 꾸밀 수 있다면 짧은 시간내에 멋진 집을 얻을 수 있는 것처럼, MFC 마법사는 프로그램을 위한 대략적인 소스

를 자동으로 생성해준다. 또한, 마법사가 생성해준 소스를 컴파일하면 프로그램이 바로 실행된다.

Page 20: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 20 / 35

물론 아무 기능도 없겠지만 말이다. API로 직접 모든 코드를 작성하는 작업에 비해서 마법사를 사용하는 경우, 초기 개발을 위한 작업

시간이 대폭적으로 줄어준다. 대략 1~2일 분량이 줄어둔다고 할 수 있다.

4.4.6 Visual C++ 실행 이제 Visual C++ 프로그램을 실행해 보도록 하자.

막상 실행은 했지만 툴바(toolbar)와 메뉴, 그리고 하단의 상태창까지 대단한 압박이 아닐 수 없다. 무엇부터 선택해야 하는 것인지 혼란스럽다.

Visual C++을 이용해 윈도우 어플리케이션을 개발하기 위해서는 일단 기본적으로 Document View 아키텍쳐 개념과 SDI 및 MDI 개념을 이해해야 한다. 그리고 난 후, MFC App Wizard을 이용해 어클

리케이션을 개발하는 다음 절차를 이해할 수 있다.

SDI 어플케이션 제작 File → New 메뉴를 선택한다.

Page 21: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 21 / 35

Projects 탭에서 MFC Wizard를 선택한 후, Project Name 항목에 LifeGame을 입력한다. 또한, Location 을 적당한 디렉토리 위치로 지정한다. 그리고, OK 버튼을 클릭한다.

‘MFC AppWizard – Step 1’ 다이얼로그가 나타난다. Single document를 선택하여 SDI 어플리케이션을 제작할 수 있도록 한다. Document/View architecture support를 선택한다. ‘Finish’ 버튼을 클릭하면 자동으로 소스 파일들이 생성된다.

Page 22: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 22 / 35

이제 MFC 마법사에 의해 자동으로 소스들이 생성되며, 자동 생성되는 클래스들의 이름과 역할은 다

음과 같다. 클래스 이름들을 눈여겨 보기 바란다. CMainFrame 클래스이 이외에는 임의로 생성되는 것이 아니라, 어플리케이션 이름을 변형하여 생성된다. (CMainFrame 클래스 명칭은 모든 MFC 어플리케이션에서 항상 동일하게 생성된다.)

CLifeGameApp : 어플리케이션 클래스 CMainFrame : 메인 프레임 클래스

CLifeGameDoc : 문서 클래스 CLifeGameView : 뷰 클래스

4.4.7 CLifeGameDoc 구현 Document/View 아키텍쳐에서 document 클래스는 자료구조와 데이터를 변경하는 알고리즘을 포함

한다. 또한, MFC Wizard는 프로그램 명칭을 이용해 document 클래스의 이름을 자동으로 생성해준다. 따라서, 생명 게임의 document 클래스 이름은 CLifeGameDoc이다.

보드 크기 상수 정의 앞서 요구분석에서 생명 게임판의 크기를 25 * 25로 정의했지만, 향후 게임판의 크기를 변경할 경우

쉽게 변경할 수 있도록 게임판의 폭(width)과 높이(height)를 상수로 정의한다. 상수는 LifeGameDoc.h 파일 – CLifeGameDoc 클래스의 선언부-에 추가한다.

#define BOX_WIDTH 25

#define BOX_HEIGHT 25

Page 23: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 23 / 35

보드 선언 웜들을 포함할 보드 변수를 CLifeGameDoc 클래스의 멤버로 선언한다. 앞서 설계 시에 결정한 것처럼 부울린 타입의 배열을 선언하며, 변수의 이름은 웜들이 마치 벌집 속에 있는 듯하기 때문에 honycomb이라고 부여한다. 아래의 문장을 LifeGameDoc.h 파일에 추가한다.

private:

BOOL honeycomb[BOX_WIDTH][BOX_HEIGHT];

웜 탄생 및 소멸 함수 앞서, honycomb 변수를 CLifeGameDoc 클래스의 private 멤버로 선언했기 때문에 클래스의 외부에서는 웜을 직접 추가하거나 삭제할 수 없다. 따라서, 웜을 탄생 시키거나, 소멸시키는 함수를 추가해

야 한다. 또한, 특정 위치에 웜이 존재하는 여부를 확인하는 함수를 포함시키자. 먼저, 아래의 선언을 LifeGameDoc.h 파일에 추가한다.

// Implementation

public:

void destroyWorm( int nX, int nY );

void createWorm( int nX, int nY );

BOOL isExistWorm( int nX, int nY )

다음으로 아래의 함수 본체(body)를 LifeGameDoc.cpp 파일에 추가한다.

BOOL CLifeGameDoc::isExistWorm( int nX, int nY )

{

if( nX >= 0 && nX < HONEYCOMB_WIDTH &&

nY >= 0 && nY < HONEYCOMB_HEIGHT )

{

return honeycomb[nX][nY];

}

else

return FALSE;

}

void CLifeGameDoc::createWorm(int nX, int nY)

{

if( isExistWorm( nX, nY ) == FALSE )

honeycomb[nX][nY] = TRUE;

}

void CLifeGameDoc::destroyWorm(int nX, int nY)

{

if( isExistWorm( nX, nY ) == TRUE )

honeycomb[nX][nY] = FALSE;

}

Page 24: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 24 / 35

세대 교체 (change of generations) 이제 보드 상에 존재하는 생명들이 한 세대에서 다음 세대로 교체되는 과정을 구현해야 한다. 함수 이름은 chgOfGen()이라고 하자. 함수 이름을 정의했다면, 함수가 실행되는 과정을 가상 코드

(pseudo code)로 작성한다. 복잡한 알고리즘을 작성할 때는 가급적 가상 코드로 간단히 작성해본 다음 구체적인 코드를 작성하는 것을 권장한다.

가상 코드 작성 방식은 개발자가 문법이나 명령, 함수의 표현보다는 알고리즘 자체에 집중하게 되므로 프로그램의 논리적 오류 발생을 현저히 줄여준다.

void chgOfGen()

{

// ----- 2중 루프(loop)를 선언한다. -----

// 보드의 폭만큼 반복한다.

for( x )

// 보드의 높이만큼 반복한다.

for( y )

{

// 각각의 셀을 검사한다.

if( cell has worm )

{

// 웜이 존재한다면, 다음 세대에 생존하거나, 소멸할지 판단한다.

// 소멸한다면 소멸 함수(destryWorm)를 호출한다.

}

else

{

// 웜이 존재하지 않는다면, 해당 셀에 다음 세대의 웜이 탄생할지 검사한다.

// 탄생한다면, 탄생 함수(createWorm)를 호출한다.

}

}

}

세대 교체 함수를 가상 코드를 작성해 보면, ‘다음 세대 생존/소멸 여부’ 및 ‘탄생 여부’를 검사하는

함수가 필요하다는 것을 알 수 있다. 이 두가지 기능을 decideSurvival(), decideBirth() 함수라고 선언하자. 두 함수 모두 반환 값은 BOOL 타입이다. 생존하거나, 태어나면 TRUE를 그렇지 않다면 FALSE를 반환한다. 또한, 셀의 좌표(x,y)를 인자로 받아들인다. 그리고, 두 함수는 외부에서 호출될 필요가

없으므로 private 메소드가 된다. 아래 선언을 LifeGameDoc.h 파일에 추가한다.

private:

BOOL decideBirth( int nX, int nY );

BOOL decideSurvival( int nX, int nY );

int howManyNeighbor( int nX, int nY )

또한, 아래 코드를 LifeGameDoc.cpp 파일에 추가한다. 또한, 생존/소멸/탄생을 검사하는 코드를 작

Page 25: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 25 / 35

성해본다.

BOOL CLifeGameDoc::decideSurvival(int nX, int nY)

{

// 셀의 주변에 2개 미만 혹은

// 3개 초과의 셀들이 살아있으면 중앙 셀이 죽도록 한다.

return howManyNeighbor( nX, nY ) == 3

howManyNeighbor( nX, nY ) == 2;

}

BOOL CLifeGameDoc::decideBirth(int nX, int nY)

{

// 셀의 주변에 정확히 3개의 셀들이 살아 있으면

// 셀이 다시 살아나도록 한다.

return howManyNeighbor( nX, nY ) == 3;

}

int CLifeGameDoc::howManyNeighbor( int nX, int nY )

{

int nCnt = 0;

// 셀 주변에 몇개의 웜이 살아 있는지 검사한다.

// 주변 8개의 셀을 검사한다.

nCnt += isExistWorm( nX-1, nY-1 ) ? 1 : 0;

nCnt += isExistWorm( nX-1, nY ) ? 1 : 0;

nCnt += isExistWorm( nX-1, nY+1 ) ? 1 : 0;

nCnt += isExistWorm( nX, nY-1 ) ? 1 : 0;

nCnt += isExistWorm( nX, nY+1 ) ? 1 : 0;

nCnt += isExistWorm( nX+1, nY-1 ) ? 1 : 0;

nCnt += isExistWorm( nX+1, nY ) ? 1 : 0;

nCnt += isExistWorm( nX+1, nY+1 ) ? 1 : 0;

return nCnt;

}

위 가상 코드에 문제가 없다고 판단되면, void chgOfGen() 함수를 제대로 작성해볼 차례이다.

void CLifeGameDoc::chgOfGen()

{

// ----- 2중 루프(loop)를 선언한다. -----

// 보드의 폭만큼 반복한다.

for( int nX=0; nX<HONEYCOMB_WIDTH; nX++ )

{

// 보드의 높이만큼 반복한다.

for( int nY=0; nY<HONEYCOMB_HEIGHT; nY++ )

{

// 각각의 셀을 검사한다.

Page 26: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 26 / 35

if( isExistWorm( nX, nY ) == TRUE )

{

// 웜이 존재한다면, 다음 세대에 생존하거나, 소멸할지 판단한다.

// 소멸한다면 소멸 함수(destryWorm)를 호출한다.

if( decideSurvival( nX, nY ) == FALSE )

destroyWorm( nX, nY );

}

else

{

// 웜이 존재하지 않는다면,

// 해당 셀에 다음 세대의 웜이 탄생할지 검사한다.

// 탄생한다면, 탄생 함수(createWorm)를 호출한다.

if( decideBirth( nX, nY ) == TRUE )

createWorm( nX, nY );

}

}

}

}

그런데, 위에 작성한 코드에는 치명적인 문제가 존재한다. 세대가 교체되는 과정에서 구세대와 신세대가 동일한 보드에서 나타나고 사라지기 때문에 세대 교체 도중에 탄생한 새로운 개체가 구세대의 생존과 소멸 과정에 영향을 미쳐 생명 게임의 규칙을 어긋나게 만든다.

이런 문제를 어떻게 해결해야 할 것인가? 바로 이런 문제를 해결하는 능력이 진정한 프로그래머의 능력인 것이다. 이제 문제를 이중 버퍼링(double buffering)이라는 기법을 통해 해결할 것이다.

이중 버퍼링이라는 기법은 동일한 자료 구조를 2개 이상 복사해서 사용하는 기법이다. 생명게임에서는 세대교체 과정에서 구세대와 신세대를 구분하기 위해 앞서 선언한 honeycomb 배열을 하나 더

선언할 것이다. 새로운 배열의 이름은 honneycomb_nextgen이라고 정의한다. LifeGameDoc.h 파일에 추가한다.

private:

BOOL honeycomb_nextgen[BOX_WIDTH][BOX_HEIGHT];

이제 chgOfGen() 함수를 다음과 같이 변경한다. 또한 신세대 배열에 웜을 생성하기 위해 createWormNextGen 함수를 추가한다.

void CLifeGameDoc::createWormNextGen(int nX, int nY)

{

honeycomb_nextgen[nX][nY] = TRUE;

}

void CLifeGameDoc::chgOfGen()

{

// 신세대를 위한 버퍼를 clear 한다.

for( int nX=0; nX<HONEYCOMB_WIDTH; nX++ )

Page 27: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 27 / 35

{

for( int nY=0; nY<HONEYCOMB_HEIGHT; nY++ )

honeycomb_nextgen[nX][nY] = FALSE;

}

// ----- 2중 루프(loop)를 선언한다. -----

// 보드의 폭만큼 반복한다.

for( nX=0; nX<HONEYCOMB_WIDTH; nX++ )

{

// 보드의 높이만큼 반복한다.

for( int nY=0; nY<HONEYCOMB_HEIGHT; nY++ )

{

// 각각의 셀을 검사한다.

if( isExistWorm( nX, nY ) == TRUE )

{

// 웜이 존재한다면, 다음 세대에 생존하거나, 소멸할지 판단한다.

// 생존한다면, 신세대로 복사한다.

if( decideSurvival( nX, nY ) == TRUE )

createWormNextGen( nX, nY );

}

else

{

// 웜이 존재하지 않는다면,

// 해당 셀에 다음 세대의 웜이 탄생할지 검사한다.

// 탄생한다면, 탄생 함수(createWorm)를 호출한다.

if( decideBirth( nX, nY ) == TRUE )

createWormNextGen( nX, nY );

}

}

}

// 신세대를 위한 버퍼를 현재 세대로 복사한다.

for( nX=0; nX<HONEYCOMB_WIDTH; nX++ )

{

for( int nY=0; nY<HONEYCOMB_HEIGHT; nY++ )

honeycomb[nX][nY] = honeycomb_nextgen[nX][nY];

}

}

마지막으로 문서 객체가 초기화될 때 마다, 보드의 모든 생명을 소멸된 상태로 만들기 위해

OnNewDocument 메소드를 다음과 같이 변경한다.

BOOL CLifeGameDoc::OnNewDocument()

{

if (!CDocument::OnNewDocument())

return FALSE;

Page 28: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 28 / 35

// 보드를 clear 한다.

for( int nX=0; nX<HONEYCOMB_WIDTH; nX++ )

{

for( int nY=0; nY<HONEYCOMB_HEIGHT; nY++ )

honeycomb[nX][nY] = FALSE;

}

return TRUE;

}

4.4.8 CLifeGameView 구현 이제 생명 게임의 근간을 이루는 웜의 탄생과 소멸 알고리즘을 구현했으므로, 웜들의 세대 교체 모

습을 눈으로 확인해야 한다. 따라서, View 클래스를 구현할 것이며, 이제 여러분은 윈도우 그래픽 함수를 사용하게 될 것이다. 사실상 대부분의 개발자들은 이 부분부터 구현을 시작한다. 완전히 반대로 길을 걷고 있던 것이다.

보드 그리기 웜들을 어디에 그릴 것인가? 간단히 생각하자면, 프로그램 메인 창에 그리면 될 것이다. 자, 어디에 그릴지 결정했으니, MFC가 작성한 소스의 어느 부분을 수정해야할지 어떤 함수를 수정해야할지 고민해야 한다.

MFC는 자신이 제공한 Main Window의 내용을 그려야 할 때, 호출할 함수를 미리 준비해 두었다. 바로 OnDraw 메소드이다. 우리는 이 메소드 내부에서 우리가 원하는 그림을 그리면 된다. 달리 표현

하자면, 그리는 명령들을 호출하면 되는 것이다. 여기서 우리는 GDI(Graphice Device Interface) 함수들을 사용하게 된다. GDI는 쉽게 말해서 윈도우에

선, 도형, 이미지 등을 그리는 함수들의 집합이라고 설명할 수 있다. (그런데, GDI는 윈도우 뿐만 아니라 프린터로 출력하는데도 사용된다.) GDI 함수는 그 종류가 매우 방대하기 때문에 본 문서에서 모두 설명하기 힘들며, 단지 몇 개의 함수를 사용하는 예를 통해서 간단히 경험해보기 바란다.

OnDraw 메소드에서 사용되는 메소드들이 GDI 함수의 예이다. 그런데, 화면에서 그리자니 웜의 크기를 우선 결정해야 한다. 웜의 크기는 LifeGameView.h 파일에

선언한다.

#define CELL_SIZE 15

이제, OnDraw 메소드를 수정한다.

void CLifeGameView::OnDraw(CDC* pDC)

{

CLifeGameDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

// 붓과 펜을 준비한다.

// create and select a solid blue brush

CBrush brushBlue(RGB(0, 0, 255));

Page 29: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 29 / 35

CBrush* pOldBrush = pDC->SelectObject(&brushBlue);

// create and select a thick, black pen

CPen penBlack;

penBlack.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));

CPen* pOldPen = pDC->SelectObject(&penBlack);

// 격자를 그린다.

for( int nX=1; nX<=HONEYCOMB_WIDTH; nX++ )

{

pDC->MoveTo( nX*CELL_SIZE, 0 );

pDC->LineTo( nX*CELL_SIZE, HONEYCOMB_HEIGHT*CELL_SIZE );

}

for( int nY=0; nY<=HONEYCOMB_HEIGHT; nY++ )

{

pDC->MoveTo( 0, nY*CELL_SIZE );

pDC->LineTo( HONEYCOMB_WIDTH*CELL_SIZE, nY*CELL_SIZE );

}

// 살아있는 웜들을 그린다.

for( nX=0; nX<HONEYCOMB_WIDTH; nX++ )

{

for( nY=0; nY<HONEYCOMB_HEIGHT; nY++ )

{

if( pDoc->isExistWorm( nX, nY ) == TRUE )

{

// draw a thick black rectangle filled with blue

pDC->Rectangle( nX*CELL_SIZE, nY*CELL_SIZE,

(nX+1)*CELL_SIZE, (nY+1)*CELL_SIZE );

}

}

}

// put back the old objects

pDC->SelectObject(pOldBrush);

pDC->SelectObject(pOldPen);

}

위 소스에서 OnDraw 함수의 처음과 끝 부분에서 SelectObject를 호출하는 것을 볼 수 있다. 그리고, 붓과 펜을 생성해 사용한 후, 이전(old) 붓과 펜을 디바이스 컨텍스트(Device Context 이하 DC)에 복

구한다는 주석(comment)을 읽을 수 있다. 이것은 무슨 의미인가? DC는 여러분이 GDI 함수를 이용해 그림을 그릴 수 있는 영역(area)와 붓, 펜 등 그림 도구의 집합

이다. 화가가 그림을 그리는 작업을 떠올려 보자. 화가는 한 손에 붓을 들고 그림을 그린다. 동시에 두가지 색상을 칠하는 경우는 없을 것이다. 이처럼, DC는 한번에 하나의 붓, 하나의 펜을 이용해 그림을 그리게 된다. 따라서 우리는 SelectObject 메소드를 이용해 DC에 그림 도구를 들려준다.

컴퓨터 스크린은 여러 프로그램들이 공유하는 영역이지 않은가? 따라서, DC는 프로그램들 간에 공

Page 30: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 30 / 35

유하는 것이라고 생각하면 되겠다. 그래서 OnDraw 시작 시에 붓과 펜을 선택한 후에 이전에 설정되어 있던 펜을 OnDraw가 종료할 때 복원 시킨다.

이렇게 구현한 후, 실행하면 다음과 같은 결과를 볼 수 있다. 단지 격자만이 나타나게 된다. 이제 웜을 사용자가 마우스를 클릭해 생성하는 기능을 추가할 것이다. 그리고 난 후, 일정 세대 마다 세대가 교체되는 기능을 구현할 것이다.

마우스 이벤트 처리 사용자가 특정 위치에 웜을 생성하기 위한 가장 간편한 방법은 격자위에서 마우스를 클릭하는 것이다. 따라서, 사용자가 뷰(view) 윈도우 위에서 마우스를 클릭하는 순간, 해당 좌표를 계산한 후 문서 객체에 웜을 추가(생성)하는 메소드를 구현해야 할 것이다.

마우스가 클릭되는 이벤트를 받아들이기 위해서는 뷰 윈도우 클래스에 메시지 핸들러(message handler) 메소드를 추가해야 한다. 이를 위해서는 클래스 위자드(Class Wizard)를 사용하는 것이 간

편하며, Visual C++ 개발환경에서 ctrl-W key를 누르면 클래스 위자드가 호출된다. ‘Class name :’ 선택 상자에서 CLifeGameView 클래스를 선택한 후 ‘Messages :’ 목록에서

WM_LBUTTONUP 메시지를 더블 클릭한다. (WM_LBUTTONUP 이벤트는 왼쪽 마우스 버튼을 누르고 떼는 순간 발생한다.) 그러면 다이얼로그 하단의 ‘Member functions ‘ 목록에 OnLButtonUp 메소드가 추가 되었음을 확인할 수 있다.

Page 31: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 31 / 35

이제 뷰 윈도우 상에서 마우스 좌측 버튼을 클릭하면 호출되는 OnLButtonUp 메소드의 내용을 채울 차례이다.

void CLifeGameView::OnLButtonUp(UINT nFlags, CPoint point)

{

// 문서 객체를 얻어낸다.

CLifeGameDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

// 웜을 생성하거나 소멸시킨다..

int x = point.x / CELL_SIZE;

int y = point.y / CELL_SIZE;

if( pDoc->isExistWorm( x, y ) == FALSE )

pDoc->createWorm( x, y );

else

pDoc->destroyWorm( x, y );

// 윈도우를 다시 그리게 한다.

Invalidate();

CView::OnLButtonUp(nFlags, point);

}

이제 수정된 프로그램을 컴파일한 후 실행한 뒤, 마우스로 클릭하면 아래와 같이 웜이 추가되는 것을 볼 수 있다.

Page 32: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 32 / 35

Timer 이벤트 추가 웜을 추가하기만 해서는 ‘생명 게임’이 동작하지 않는다. 일정 시간마다 세대가 교체되어야 한다. 따라서 타이머 이벤트를 추가하며, 매번 이벤트가 발생할 때 마다 세대가 교체되며 교체된 결과를 화면에 나타내도록 할 것이다.

타이머의 간격은 우선 0.5초로 설정한다. 또한, 윈도우에서는 타이머를 여러 개 선언할 수 있으며 각 타이머 이벤트에 ID를 부여할 수 있게 하였다. 생명게임에서 세대가 교체되는 타이머의 ID는

IDT_CHANGE_GEN 이라고 정의한다. LifeGameView.h 파일에 다음 선언문을 추가한다.

#define IDT_CHANGE_GEN 1

#define TIMER_INTERVAL 500

타이머 기능을 추가하기 위해서 역시 뷰 윈도우에 이벤트 처리 함수를 추가해야 한다. 마우스 이벤트 처리 함수를 추가할 때와 마찬가지로 ctrl-w key를 눌러 클래스 위자드를 호출한다.

‘Class name :’ 선택 상자에서 CLifeGameView 클래스를 선택한 후 ‘Messages :’ 목록에서 WM_TIMER 메시지를 더블 클릭한다. (WM_LBUTTONUP 이벤트는 왼쪽 마우스 버튼을 누르고 떼는 순간 발생한다.) 그러면 다이얼로그 하단의 ‘Member functions ‘ 목록에 OnTimer 메소드가 추가 되었음을 확인

할 수 있다.

Page 33: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 33 / 35

이제 타이머 이벤트가 발생했을 때, 세대를 교체하는 실제 기능을 작성한다.

void CLifeGameView::OnTimer(UINT nIDEvent)

{

// 문서 객체를 얻어낸다.

CLifeGameDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if( nIDEvent == IDT_CHANGE_GEN )

{

// 세대를 교체한다.

pDoc->chgOfGen();

// 윈도우를 다시 그리게 한다.

Invalidate();

}

CView::OnTimer(nIDEvent);

}

그런데 타이머는 언제, 누가 시작하는가? 우선은 마우스의 오른쪽 버튼을 클릭하면 동작하도록 하겠다. 오른쪽 마우스 버튼을 클릭했을 때 – 마우스 우측 마우스 클릭 이벤트가 발생했을 때- 동작하는 메소드를 추가하는 작업은 왼쪽 마우스 버튼 클릭 핸들러을 구현하는 작업과 유사하다. 역시, 클래

스 위자드를 이용해 메소드를 추가하면 되며, 메시지 명칭은 WM_RBUTTONUP이고 메소드 명칭은 OnRButtonUp이다.

void CLifeGameView::OnRButtonUp(UINT nFlags, CPoint point)

{

static BOOL doKill = FALSE;

Page 34: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 34 / 35

// 한번 클릭하면, 타이머를 시작하고..

if( doKill == FALSE )

{

doKill = TRUE;

SetTimer( IDT_CHANGE_GEN, TIMER_INTERVAL, NULL );

}

// 다시 클릭하면 타이머를 해제 한다.

else

{

doKill = FALSE;

KillTimer( IDT_CHANGE_GEN );

}

CView::OnRButtonUp(nFlags, point);

}

4.5 테스트 및 유지보수 4.5.1 테스트

테스트는 다음과 같은 순서로 진행한다.

1. 프로그램을 컴파일 한다. (F7) 2. 컴파일 중 문법 오류가 있다면, 오류를 수정한다. 3. 오류 없이 실행파일이 만들어지면, 생명게임을 실행한다. (Ctrl+F5)

4. 실행한 후 몇번의 마우스 클릭으로 웜을 탄생시킨다 5. 오른쪽 마우스 클릭을 누르는 세대 교체가 시작된다. 0.5초 마다 웜이 태어나고 죽는다면 정상적으로 실행되는 것이다.

6. ‘생명게임’ 예제에 나와 있는 글라이더, 블링커 등을 실행하여 예측된 모습대로 실행되는지 확인한다. 만약, 예측대로 변화하지 않는다면 논리적 오류 이므로 소스를 확인한 후 수정한다. 7. 1번부터 6번까지의 과정을 완료하였다면, 테스트는 종료된다.

4.5.2 유지보수 유지보수는 생명게임의 기능을 보완하고 업그레이드 하는 것이다. 다음의 기능들을 추가할 수 있을 것이다.

메뉴 변경 MFC에 의해 자동으로 생성된 메뉴는 ‘생명 게임’의 기능과 일치하지 않는다. 따라서 메뉴를 변경해

야 할 것이다.

게임 환경 설정 추가 세대가 교체되는 시간을 빠르거나 느리게 조정하는 기능 세대 교체를 일시 정지 시키는 기능

세대 순번을 보여주는 기능 등

Page 35: 프로그램 제작 실전 - pds.egloos.compds.egloos.com/pds/1/200505/25/87/make life game.pdf · 생명 게임(Life game)은 영국의 수학자 존 콘웨이(John Horton Conway)가

Technical document

Endless Creation. SNUT 35 / 35

그래픽 보완 웜 그래픽을 이미지로 교체

5. 정리 이제까지 생명 게임을 구현하는 과정을 통해서 소프트웨어를 제작하는 절차에 대해 학습하였다. 각자 분발하여 더욱 발전된 프로그램을 구현해 보기를 바란다