2007년 01월 04일
Nebula와 UNIX
유닉스의 가장 큰 특징은 간결하다라는 것이다. 간결하지 않은 것은 유닉스가 아니다. 유닉스는 '예외'를 최소화하는 운영체제이다. 그럼 간결한 것이 왜 좋은 것일까. 간결한 것은 아름다운 것이다. 'Simple is beauty!'라는 말도 있지 않은가. 극단적으로 이야기하자면 간결한 것은 곧 예술이라고도 표현할 수 있다. 이렇듯 컴퓨팅 시스템에서도 하드웨어든 소프트웨어든 역시 간결한 것의 장점은 굳이 설명할 필요가 없을 것이다.
그러면 이러한 간결함의 미덕을 프로그램의 관점에서 살펴보기로 하자. 간결한 프로그램은 우선 이해하기 쉽다. 이해하기 쉬운 프로그램은 작성한 사람이 아닌 다른 사람이 수정하는 경우에도 수정하기 쉽다. 이것은 유지 보수하기가 쉽다는 것으로 다시 설명이 된다. 나날이 복잡해 지는 컴퓨팅 환경에서는 이러한 간결함의 특징은 단순한 업무 상의 효율 정도로 그치는 것이 아니라 기술 우위의 수단으로 작용하여 강력한 경쟁 도구가 되기도 한다. Nebula 역시 유닉스의 간결함을 그대로 따르고 있다. 그러므로 Nebula는 (일부의 선입견과 달리) 이해하기 매우 쉬운! 게임 엔진이다. 그런데 이렇게 이해하기 쉬워야 함에도 불구하고 여전히 Nebula를 이해하기 힘들다고 이야기하는 사람들이 많다. 이런 사람들은 이러한 유닉스의 특징이 익숙하지 않아서는 아닌지 혹은 3D와 관련한 여러 이론에 대한 선행 지식이 없어서는 아닌지 다시 한번 깊이 생각해 볼 일이다. 이러한 점을 염두에 두고 Nebula와 유닉스의 공통점에 대해서 살펴 보자.
지면상의 이유로 유닉스의 특징을 따르는 Nebula의 특징을 모두 살펴 볼 수는 없고, 여기에서는 다음의 몇가지 중요한 특징들에 대해서 살펴 보기로 하자.
- 유닉스의 셸과 Nebula
- 유닉스의 프로세스와 Nebula
유닉스는 운영체제이고 운영체제는 컴퓨터를 사용해서 무엇인가를 하고자 할 때 시스템을 제어하고 관리하는 소프트웨어이다. 사용자는 운영체제가 시스템을 제어하고 관리하도록 명령을 내리게 되는데 이 명령은 셸 명령을 통해서 하게 된다. 셸(shell) 명령이란 운영 체제에 명령을 내리기 위해 어떤 명령어를 입력하는 것을 의미한다. 유닉스의 셸은 간단하지만 강력하다.
Nebula에서 유닉스의 셸에 해당하는 것이 바로 스크립트 인터페이스(script interface)이며 이를 처리하는 클래스는 nScriptServer 클래스이다.
유닉스에는 한가지의 셸만 있는 것이 아니라 본 셸(sh), 본 어게인 셸(bash), C 셸 등 여러 종류의 셸이 존재한다. 이렇게 다양한 셸이 존재하게 된 이유는 시간이 지나면서 셸에 대한 사용자의 요구 사항이 늘어나거나 사용자가 증가하면서 각기 다른 취향과 목적이 다양해짐에 따라 원하는 셸을 선택하거나 혹은 개발해서 사용할 수 있도록 한데 있다.
Nebula에서도 여러 가지의 스크립트 언어를 사용한 스크립트 인터페이스를 지원하며 사용자의 목적에 따라 원하는 스크립트를 사용할 수 있다. Nebula에서는 nScriptServer를 상속받은 클래스에서 실제 사용할 스크립트 서버를 정의하게 된다. 예를 들어 Nebula의 기본적인 스크립트는 Tcl 이지만 사용 목적에 따라 Python이나 Lua를 사용할 수 있다. Python을 사용하는 경우에는 nPythonServer를 Lua의 경우에는 nLuaServer를 사용한다.
그러면 운영 체제와 입력한 셸 명령어는 어떻게 사용자에게 연결되는 것일까. 먼저 사용자가 운영 체제에 어떤 셸 명령을 내리기 위해서는 키 입력과 운영체제를 연결하는 매개체가 필요하다. 다음으로 운영 체제 입력한 내용을 처리한 결과를 사용자에게 보여주어야 하는 매개체가 필요하다. 여기에서의 매개체가 바로 콘솔이다. Nebula에서도 nConServer 클래스를 통해서 이러한 과정을 그대로 흉내내고 있다.
이제 다시 셸 명령어로 돌아와서 프로세스를 통해서 원하는 작업을 하는 경우를 생각해 보자. 프로세스란 '어떤 것을 하는 어떤 것'을 말한다. 유닉스에서 어떤 작업을 하기 위해서는 그 작업을 위한 프로세스를 생성해야 한다. 생성된 프로세스는 해당 작업을 한 다음 더 이상 수행해야 할 작업이 없을 때는 종료하게 된다. 이 때 프로세스의 생성과 종료는 셸 명령어에 의해서 처리된다.
이것을 Nebula의 관점에서 살펴 보면 다음과 같이 설명할 수 있다. 프로세스에 해당하는 것은 nRoot를 상속한 클래스이다. nRoot 클래스를 상속한 클래스는 스크립팅 인터페이스를 제공한다. 바꾸어 말하면 이들 클래스는 클래스의 멤버 함수를 스크립팅 인터페이스로 제공해서 콘솔에서 이들을 호출할 수 있다는 이야기이다. 물론 스크립트가 필요없는 경우에는 제공하지 않아도 된다. 이들 클래스의 생성 및 삭제(종료)는 셸 명령어에 해당하는 네뷸라 스크립트의 'new'와 'delete' 명령어로 처리한다.
다음은 nshapenode라는 객체를 생성하는 Tcl 스크립트이다. nshapenode는 메쉬 데이터를 출력하는 것이므로 다음은 화면에 메쉬를 출력하기 위한 프로세스를 생성한 것으로 설명할 수도 있다.
그리고 프로세스가 실행 되면서 수행하는 다양한 처리는 운영 체제의 시스템 호출이 아니라 프로세스에서 제공하는 인터페이스를 통해서 처리하게 된다. 이 말이 무슨 말인지 워드 프로세스를 예로 들어 설명해 보겠다. 워드 프로세스의 실행 - 프로세스의 생성에 해당한다 - 은 셸 명령어를 통해서 운영체제가 하지만 워드 프로세스에서 문서를 작성하고 편집하는 다양한 기능들은 워드 프로세스라는 프로세스에서 제공하게 된다. 이렇게 설명하면 당연한 것 처럼 이해되는 사실이 실제로 게임 엔진을 제작하다 보면 이러한 처리가 모듈화 되지 않고 구분 없이 작성되는 경우가 허다하다. 이것은 엔진의 재사용이 어렵다는 정도의 단점뿐만 아니라 최악의 경우에는 진행중인 프로젝트에 엄청난 재앙을 초래할 수도 있다.
앞서 프로세스의 생성 및 소멸과 관련된 처리는 운영체제에서 한다고 했는데, Nebula에서도 마찬가지이며 이것은 아래의 C++ 코드를 보면 더욱 명확하게 이해할 수 있다.
모든 nRoot 클래스를 상속한 클래스는 운영체제에 해당하는 nKernelServer::New() 함수를 통해서 생성된다. 일반적으로 커널이라고 하면 운영체제의 핵심에 해당하는 것으로 다른 모든 부분에 여러 가지 기본적인 서비스(프로세스의 생성도 여기에 포함된다)를 제공하는 것이다. 이렇게 보면 Nebula의 nKernelServer라는 클래스 이름만으로 이 클래스가 처리하는 일들이 쉽게 이해가 갈 것이다. (실제로 Nebula의 여러 클래스 이름은 각각의 유래가 있는 경우가 많으며 이를 알면 매우 이해가 쉽다.)
다음 Nebula Tcl 스크립트는 Nebula의 예제 중 하나인 타이거 탱크를 화면에 출력하는 코드이다. 타이거 탱크의 nshapenode 객체의 생성을 위한 'new' 명령어와 생성한 객체를 선택하는 'sel' 명령어만 셸의 명령어이고 .settexture, setmesh, setshader 등은 모두 nshapenode 에서 제공하는 스크립트 명령어들이다. 메쉬 데이터의 렌더링과 관련된 처리는 모두 nshapenode에서 제공하는 인터페이스를 통해서 처리한다는 것을 알 수 있다.

nviewer로 본 타이거 탱크
운영체제에서 한번에 하나의 프로세스만을 실행시키는 일은 드물다. 그래서 이번에는 유닉스에서 각 프로세스들이 어떻게 서로 상호 작용하는 하는지에 대해서 살펴보자.
앞서 이야기한 바와 같이 유닉스는 간결하다. 이것은 복잡한 것을 만들 수 없다는 이야기가 아니다. 유닉스에서는 복잡한 프로세스가 필요한 경우 이 복잡한 프로세스를 만드는 것이 아니라 간단한 프로세스 여러 개를 연결해서 복잡한 프로세스가 처리하는 하는 일과 동일한 처리를 할 수 있도록 만든다. 이 때 각 프로세스는 파이프(pipe)를 통해서 연결이 된다. 프로세스 간에 연결된 파이프는 프로세스들이 서로 필요한 정보를 주고 받으며 통신하는 커뮤니케이션 통로가 된다.
이렇게 간단한 기본 구성 요소들을 필요에 맞게 결합함으로써 어떠한 복잡한 것이라도 만들 수 있는 운영체제인 것이다. 이 방법의 장점은 복잡한 작업이 필요할 때 마다 새로운 프로세스를 만드는 것이 아니라 기존의 프로세스들을 필요한 만큼 파이프를 통해서 연결시키고 기존의 프로세스 중에서 없을 경우에만 새로운 프로세스를 생성하면 되므로 작업이 간단해 지며, 또한 기존의 프로세스를 수정이 필요한 경우에도 역시 간단하게 작업할 수 있다. 왜냐 하면 이미 각 프로세스는 작업의 단위만큼 간단하게 작성이 되어 있기 때문이다. Nebula 역시 이러한 유닉스의 장점을 그대로 가지고 있다.(주1) Nebula의 툴들이 작은 여러 개의 툴들로 구성되어 있다는 사실이나 메쉬의 렌더링과 에니메이션에 관련한 모듈들이 분리되어 있다는 사실들이 바로 그것이다. 각각의 모듈들은 작은 단위로 분리되어 있어서 이해하기 쉽고 수정이 용이할 뿐 아니라 기존의 모듈이 더 이상 효율적이지 않은 경우 그냥 새로 작성할 수 있도록 잘 분리되어 있다.
이상에서 살펴본 바와 같이 Nebula에서는 엔진을 구성하는 각 모듈의 생성과 이들의 관계가 유닉스의 시스템을 제어하고 관리하는 방법과 비슷하다. 때문에 유닉스의 철학과 기본에 대해서 이해하고 있으면 Nebula 역시 쉽게 이해할 수가 있다.
누군가는 게임을 만들면서 무슨 운영체제씩이나 거론하냐고 반문할지도 모르겠다. 하지만 유닉스의 탄생을 살펴 보자. 유닉스는 AT&T의 벨 연구소의 데니스 리치와 켄 톰슨의 노력으로 탄생했고 데니스와 켄이 유닉스 개발에 착수한 이유는 PDP-11에서 우주전쟁 게임을 즐기고 싶다는 개인적인 이유 때문이었다. 이 위대한 운영체제도 게임에서 시작 되었다는 이야기이다. :-) 그리고 점점 더 복잡해지고 있는 게임을 보면 운영체제와 같은 게임 엔진의 설계가 필요한 것도 전혀 터무니 없는 일만은 아니다. 게다가 둠이 실행될 때 "DOOM OS Loading..."이라는 메시지가 출력되었던 것을 보면 이러한 생각이 최근에 나온 것도 아니다. 둠은 이미 10년도 더 된 게임이 아니던가-
The Art of UNIX Programming, Eric S. Raymond
Nebula를 이해하기 위해서 유닉스에 대해서 새로 배우기는 힘든 일이다. 그래서 끝으로 유닉스의 특징과 철학에 대해서 잘 설명해 놓은 책 한권을 소개한다. Unix 및 Linux 프로그래머들의 유연하고 재사용가능한 수준 높은 소프트웨어를 만드는 지혜에 대해서 잘 설명한 책이 바로 이 책이다. 유닉스의 정신에 대해서 친숙하지 않아서 Nebula의 이해가 어려운 사람은 이 책이 많은 도움이 되리라 생각한다. 정보 문화사에서 옮긴 번역서도 나와 있으니 참고하기 바란다.
# by | 2007/01/04 22:46 | Nebula Croquis | 트랙백 | 덧글(2)
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
nice to see you!