완벽한 게임 아키텍처에 관하여

과연 완벽한 게임 아키텍처란 존재할까?

과거 소프트웨어 아키텍처 101이라는 책을 읽고 지금 게임 아키텍처에 공부하며 느낀점과 생각들을 정리하고자 한다.

제목을 완벽한 게임 아키텍처라고 어그로를 끌었지만, 프로그래밍에서 완벽한 아키텍처는 없다. 단순히 해당 게임을 설계하기 위해 잘 만들어진 구조만 있을 뿐이다.

비관적인 이야기가 아니라 프로그래밍이 매력적인 이유이기도 하다. 아키텍처 세상은 그만큼의 자율성을 보여주고, 현재 상황에 맞게 잘 설계해야 함을 알려준다. 애초에 아키텍처란 트레이드 오프의 끝판왕이다.

프로그래밍에 갓 입문한 사람일수록 대부분 은총알을 찾고싶어하며 그 과정에서 다양한 신기술을 배워보고 싶어한다. 나도 그러했기 때문에 온갖 신기술에 대한 관심이 넘쳐난다. 하지만 여러 신기술을 공부해보다 결국 다시 본질을 찾게 된다.

이 내용도 조금만 생각해보면 신기술이 계속해서 나오고 더 발전된 내용이 세분화되어 나오는 이유도 그 분야에서 지속해서 발전이 있기 때문이다. 게임 아키텍처에 대한 탐구에서 아키텍처는 본질적으로 완벽할 수 없음을 깨닫게 되기 때문이다. 모든 상황이 트레이드 오프임을 깨닫게 된다면 그 상황에 맞춰 유연하게 대처할 수 있게 된다.

레거시 코드를 욕하지 마라. 그 코드는 그 상황의 최선의 코드일 것이다.

게임에서 아키텍처

특히 게임에는 정해진 아키텍처가 매우 한정적이라는 것을 자주 체험하게 되는데, 백엔드나 프론트엔드는 어느정도 정규화된 패턴이나 아키텍처가 존재하는 반면 게임은 도메인에 따라 그 상황에 맞게 트레이드 오프를 계산해야 한다.

물론 본질적으로는 같지만, 게임이라는 매우매우 넓은 주제에서는 다르게 접근해야 한다.

예를 들어 유니티의 경우 애초에 게임이라는 방대한 주제에 대해서 많이 열려있다. 모든 설계를 사용자에게 맡기며, 사용자는 게임에 따라 그 구조를 새롭게 설계해야 한다. 반대로 언리얼의 경우 GAS라는 시스템은 겨냥 자체가 AAA급 게임에 맞춰져 있고, 멀티나 설계 측면에서 많은 것을 제공해주고 있다.

엔진자체도 선택과 집중을 하기도 한다. C++의 단점이자 장점인 사용자가 메모리를 조작할 수 있다는 점을 언리얼은 게임의 사양에 직접적으로 관여하는 부분이나 유니티의 개발자 편의성의 측면은 분명하게 차이가 난다.

바로 이런 것이 트레이드 오프라고 할 수 있다. 어떤 아키텍처든 디커플링을 위해 디커플링을 포기하는 것이다. 어떤 게임을 설계하느냐에 따라 그 게임의 특성에 맞게 유연하게 설계됨으로 이런 설계 능력이나 흐름을 잘 읽을수록 능력이 좋다고 생각한다.

논외지만 학습에서 오는 러닝코스트도 하나의 트레이드 오프로 봐야한다.

싱글톤 패턴에 대하여

미디엄에 올라온 한 개발자가 스파게티 코드(1개의 게임매니저 설계 구조)에 대해서 작성한 글이다.

글을 요약하자면 쉽고 머릿속에 있는 로직을 그대로 하나의 객체가 부담하여 모든 것을 관리하는 싱글톤 패턴으로 만든 게임에 대한 분석 글이다. 흔히 트리 형태가 아닌 서로서로 참조하며 매우 강한 결합도를 가지며 캡슐화나 객체의 자율성이 없는 구조로 이어진다.

하지만 되게 흥미로운 그래프를 볼 수 있는데, 간단한 게임일수록 이 과정이 더 유리하다는 것이다. 스스로 AAA급 게임을 만들 것이 아니라면 이런 구조가 더 효율적일 수 있다고 한다. 그래프나 글을 보면 알 수 있듯이 새로운 기능이나 구조가 추가될수록 구조가 모든 객체에 영향을 주기에 기하급수적으로 복잡함이 높아진다.

쉽게 라이프서클에 대한 경합이 발생하거나 서로에 대한 참조로 한 가지 수정에 대해 큰 파급력을 가지기도 하며, 테스트코드의 작성또한 어려워지고 이는 수정의 어려움, 디버깅의 어려움으로 이어진다.

본래 설계는 게임이 커질수록 더욱더 어려워진다.

바로 이런 것이 유연한 설계가 필요한 이유이며 이런 유연한 설계는 그만큼의 비용이 든다. 즉, 다시 돌아와서 결국은 트레이드 오프라는 것이다.

싱글톤 패턴의 개발방식과 아키텍처 설계형 개발의 큰 차이는 당장 앞의 나무만 보고 설계하는 것과 전체적인 숲을 보고 설계하는 것의 차이라고 생각한다.

그렇다면 진짜 싱글톤을 사용하지 말아야 할까?

지금의 내 생각은 조금은 다르다. 데이터 자체의 불변을 보장하며 사이드 이펙트를 발생하지 않는다면 싱글톤을 사용해 로직에서 불 필요한 부분들을 제거할 수 있다. 예를 들어 사운드, 데이터, 게임 로직 등을 싱글톤으로 만들어서 사용한다면 이는 꽤나 효율적일 수 있다.

사실 사운드는 동의한다고 해도 데이터나 게임 로직을 넣는 순간 게임의 구조 자체가 자율성이 매우 떨어질 수 있다. 하지만 자신의 게임이 선형적 스토리나 한개의 관리자가 로직을 관리하는 것이 유용하다고 한다면 이는 꽤나 효율적일 수 있다.

정리하자면 정답인 구조, 은총알은 없으니 상황에 맞게 트레이드 오프를 계산하고 적절한 설계를 하는 것이 중요하다. 물론 어떤 코드는 의존성이 강하게 결합되고 자율성이 없지만 기능은 정상동작하고 사용자의 요구사항에 다 만족한다면 그 코드는 잘 짠 코드라고 생각한다. 유지보수나 확장성은 좀 다른 이야기지만 말이다.

하지만 적절한, 유연한, 해당 게임에 맞는 설계를 하고 싶다면, 본인이 하나의 프레임워크에서 항상 똑같은 코드만 짜고 있다면 객체지향을 깊게 공부하는 것을 추천한다. 유연한 설계는 자율성에서 부터 출발하며 대부분의 아키텍처의 핵심은 디커플링이자 자율성이다.

이런 흐름을 이해하기 위해선 실제 다른 프레임워크들이 어떻게 그것을 실현하고 있는지를 보고 이해하는 것이 중요하다. 다른 기본적인 아키텍처를 살펴보면 (동작, 행위, 뷰)와 (로직처리, 계산, 프레젠테이션) 그리고 (데이터, 저장, 모델)로 나누어져 있다. 여기서 얼마나 더 세분화하여 해당 시스템에 맞게 나누고 강제성을 부여하느냐의 차이라는 생각이 든다.

이를 제대로 이해하기 위해선 객체지향적 지식이 필요하며 그 뒤로 리팩터링, 테스트코드 등의 요구사항이 붙는 것 같다.

태그: ,

카테고리:

업데이트:

댓글남기기