최근 회사 개발자 전원에게 '객체지향적으로 간단한 머드게임 만들기'라는 과제가 주어졌다. 기본적인 룰은 제공을 해주는 과제였다. 해당 자료는 해당 과제 발표자료이다.
게임 기초 룰
위의 게임 기초 룰을 보면 알 수 있듯이 사실 해당 과제의 기능은 너무 간단해서 구현이 문제가 되진 않는다. 하지만 단순 구현이 과제 핵심 목적이 아니다. 해당 과제의 목적은 객체지향적인 설계에 대한 생각을 기반으로 어떻게 설계했는가 이다. (어떤 구조로 설계했는가)
객체 지향적인 설계
블로그나 커뮤니티에 객체 지향적인 설계에 대한 글은 정말 많다. 그중 빠지지 않고 등장하는 말은 역할과 책임에 따라서 객체를 나눠야 한다는 말이다. 당연히 맞는 소리라고 생각하지만 역할과 책임에 따라서 나눠진 객체들의 관계를 어떻게 관리하는지도 추가되어야 한다고 생각한다. 다시 말하면 역할과 책임에 따라서 나눠진 객체들의 의존성을 어떻게 관리할 것인가라는 말이다.
전체적인 구조
Game 클래스를 통해서 게임 세팅 부분이 시작되고 게임 데이터가 생성된다. 이렇게 생성된 게임 데이터는 유저의 입력을 통한 상호작용을 통해 핸들링된다.
게임 세팅 (게임 시작 시 랜덤으로 맵 생성, 직업리스트 생성)
위에 빨갛게 표시된 부분은 게임 세팅을 통해 결과적으로 만들어지는 게임에 대한 데이터 (랜덤으로 생성된 맵, 직업리스트 등)가 존재하는 계층이다.
맵을 만들어 맵 상태를 저장하는 객체에 집어넣는 기능을 하는 게임 세팅 부분은 싱글톤으로 구현했고, 모두 생성자를 통한 의존성 주입으로 구현했다. 하지만 여러 번 인스턴스화가 되어 맵에 종속되어야 하는 NPC, BOX, ENEMY의 경우 객체 안으로 바로 가져와 사용하게 구현했다.
HUMAN 클래스를 ENEMY가 상속받아 사용하는데 이건 둘의 공통된 추상화된 클래스를 만들었으면 더 좋았을 것 같다.
게임 진행 부분
게임 진행 부분은 명확하게 계층이 분리되게끔 구현했다. 먼저 맨 위에 유저의 입력을 받는 Input 계층, 그리고 중간에 진하게 표시된 머드 게임 프로그램의 핵심로직이 담겨있는 도메인 계층. 게임 세팅을 통해서 생성된 게임 데이터를 핸들링하는 Output 계층으로 나눌 수 있다.
모든 클래스는 의존성 주입을 통해 싱글톤으로 구현했고, 도메인 계층은 핵심로직이기 때문에 다른 계층의 변화에 의한 사이드 이펙트를 줄이기 위해서 output 계층의 추상화된 interface 계층을 만들어 의존성이 역전되게 구현했다.
의존성 관리하는 코드
이렇게 하면서 새삼 프레임워크가 편하다는 걸 느꼈다.
개발 진행하면서 했던 생각들
1. 먼저 프레임워크가 기본적으로 제공하는 기능들에 대한 개념적인 이해 없이 사용해왔다는 생각이었다.
예를 들어 Nest.js 에서 제공하는 DI기능과 IoC 기능들을 사용할 때 정해진 규칙만 지켜서 사용해왔기 때문에 Typescript 만으로 해당 개념을 구현하려고 하니 약간 버벅거리는 느낌이 있었다.
2. 두 번째로는 의존성에 대한 관리이다.
지금은 도메인 별로 구별은 지어놨지만 의존성 주입이 가능한 계층 관계라면 도메인에 상관없이 주입을 하고 있다. 하지만 만약 도메인끼리의 의존성을 아예 끊어야 하는 경우라면 (이 경우가 커지면 MSA라고 생각) 어떻게 의존성을 관리해야 할까에 대한 생각이었다. 정답일지는 모르겠지만 두 도메인 사이에 약속된 이벤트를 통해 관계를 이어가면 될 것 같다. 하지만 해당 이벤트를 발행할 때 논리적인 의존관계도 끊을 수 있는 설계 방식은 아직도 고민이다. (예를 들어 A 도메인에서 "a:1 , a를 가져가서 b에다가 더해라"라는 이벤트를 발행하게 되면 A 도메인과 B 도메인은 여전히 논리적인 의존관계를 갖고 있는 형태가 된다.)
<전체 코드 링크>
https://github.com/JooYoung2274/mud_game-oop-solid-
끝
'세미나' 카테고리의 다른 글
객체지향적으로 머드게임 만들기 - DI 프레임워크 만들어서 리팩토링 (0) | 2023.05.23 |
---|---|
테스트 구축하기 관련 발표 자료 (0) | 2023.02.28 |
백엔드 아키텍처 관련 정리자료 (0) | 2022.12.02 |