학습 깃허브 주소 : https://github.com/jong212/DataManager (DataManagingExtension 브랜치)
데이터 드리븐 수업 자료 : https://drive.google.com/file/d/1gWbcaoAGcVyvnJUtf9JlJMD6upLsU-kh/view?usp=sharing
엑셀 => Xml 변환 프로그램 : https://drive.google.com/file/d/1lev45TU8MoOt4ciPD8ZY7yPsCL_N5BAu/view?usp=sharing
학습 자료 및 파일은 공개하지 않습니다.
📌 데이터 드리븐(Data-Driven) 이란?
게임의 동작이나 로직을 직접 코드로 작성하기보다는, 외부 데이터(XML, JSON 등)를 통해 제어하거나 구성하는 방식.
장점
- 로직과 데이터를 분리 (로직과 데이터 분리하는 것이 가장 큰 장점으로 생각 된다)
- 코드 수정 없이 데이터만 바꿔서 게임 동작이나 콘텐츠 추가 가능.
- 디자이너나 기획자가 프로그래머 도움 없이 데이터 수정하거나 관리할 수 있어 개발 속도가 빨라진다.
구조 요약
1. 데이터 가져오기 : 로컬의 XML 파일을 불러온다.
2. 파싱 : 불러온 XML 데이터를 파싱해서 딕셔너리에 저장한다.
3. 사용 : 딕셔너리에 접근해서 데이터를 가져와 사용한다.
📌 구조 설계 방법
1. Exel -> Xml 파일 변환
Character, Skill, Buff 이름으로 된 엑셀 파일 3개를 준비
Xml 변환기를 통해 Buff, Character, Skill 각각 변환.
변환 완료 된 파일을 DataParser폴더 안에 모두 이동 시키고
DataParser 폴더는 프로젝트 파일 Root에 위치 시킵니다.
데이터 드리븐의 핵심은 데이터와 로직을 분리하는 것입니다 위 과정을 통해 데이터를 분리하였다고 볼 수 있습니다
2. 스크립트 파일 준비
DataMapper.cs (데이터 모델 준비)
데이터 맵퍼에서는 무엇을 할까요?
바로 `데이터 모델`을 준비하는 것입니다.
청사진으로 활용 될 모델들을 작성할 것이기 떄문에 모노비는 상속받지 않습니다.
DataManager라는 곳에서 파싱 된 데이터를 딕셔너리에 넣기 전 담는 그릇이 필요 한데 바로 그 그릇이 `데이터 모델` 이며 DataMapper.cs에서 정의합니다. 저는 Character 모델, Buff 모델, Skill 모델을 작성하였습니다.
요약하면 DataMappers에서는 청사진으로 사용 될 모델만 잘 선언해 두기만 하면 됩니다.
주의사항이 있다면 Xml파일의 이름과 클래스명을 일치해야 합니다.
DataManager.cs (Xml 로드, 파싱, 캐싱 등의 역할 담당)
DataManager의 역할 중 하나인 캐싱은 데이터를 보관하는 역할을 뜻하며 전역에서 접근이 가능하도록 Inst 정적 변수를 선언했습니다.
파싱 된 데이터를 캐싱하기 위한 딕셔너리를 선언합니다.
아래의 딕셔너리의 값으로 맵퍼에서 선언했던 그릇들을 사용합니다. (데이터를 담는 그릇!)
Awake 시점에 데이터 세팅이 완료되어야 Start에서 DataManager를 통해 데이터에 접근할 수 있습니다.
로드 할 데이터를 Swith문으로 분기 (하드 코딩 된 부분은 우선 무시)
XDocument.Load 메서드를 통해 데이터를 로드합니다.
파싱을 진행하면서 결과적으로 캐싱합니다.
나머지 Buff,Skill에 대한 Xml 데이터도 위 Character와 동일하게 파싱 => 캐싱 과정을 거칩니다.
지금 까지의 과정을 통해 모든 `Xml 데이터를 딕셔너리에 캐싱했으며 이제 DataManager 에서는 딱히 처리 할 부분이 없습니다.
DataManigingExtension.cs (데이터 반환 로직 확장 메서드 사용)
저는 데이터를 반환하는 로직을 작성하려고 합니다. 하지만 이 로직을 DataManager에 직접 작성하지 않을 계획입니다. DataManager는 데이터의 `로드, 파싱, 캐싱`이라는 세 가지 책임만 가지도록 설계할 것입니다. 데이터 반환과 관련된 비즈니스 로직은 DataManigingExtension에서 확장 메서드(Extension Methods)를 통해 분리하여 작성할 것입니다.
View가 데이터에 접근하려면 당연히 DataManager의 반환 메서드를 통해 데이터를 가져와야 합니다. 예를 들어, 캐릭터 정보를 반환받기 위해 View는 아래와 같이 호출합니다:
DataManager.Inst.캐릭터정보반환();
하지만 실제 반환 로직은 DataManager에 작성되지 않고, DataManigingExtension에 확장 메서드로 작성될 것입니다. 이렇게 하면 `DataManager 내부 로직을 분리하면서도, 외부에서는 직관적인 접근 방식을 유지`할 수 있습니다.
확장 메서드를 사용한 이유
확장 메서드를 사용하는 가장 큰 이유는 `단일 책임 원칙`을 준수하기 위함입니다. 객체지향 설계에서 한 클래스는 하나의 책임만 가져야 합니다. 저는 데이터의 `로드, 파싱, 캐싱`까지를 하나의 책임으로 보고 이를 DataManager의 역할로 한정했습니다.
확장 메서드 사용 방법
확장 메서드를 사용하려면
1. 아래 그림처럼 static 선언 합니다
2. 첫 번째 매게 변수로 "this", "클래스명", "매게변수명"을 적습니다
그러면 비즈니스 로직을 DataManigingExtention이라는 곳에서 적었지만 외부에서 DataManager에 접근해도 GetCharacterData 확장메서드를 찾을 수 있고 접근도 가능하게 됩니다.
실제 GetCharacterData 확장메서드를 호출하는 곳을 보면
DataManagingExtention.GetCharacterData()가 아니라
DataManager.Inst.GetCharacterData() 인 것을 볼 수 있습니다.
나머지 확장메서드도 작성해 봅니다.
눈여겨 볼 부분은 GetBuffDescription 메서드에서 GetBuffData 메서드를 호출하는 부분인데 마찬가지로 manager 매게변수를 통해 접근이 가능한 것으로 보입니다.
최종적으로 View에서는 데이터 매니저의 확장메서드를 호출하면 데이터를 가져올 수 있게 됩니다.
게임시작 전에는 데이터가 없지만 게임 시작 후 이미지는 Exel 데이터를 잘 반영한 것으로 보입니다.