TIL

[UE5] ProjectFT#17 프로젝트 마무리(데이터 기반 거점 시스템 구현 — 퀘스트부터 제작, 창고, 레벨 이동까지)

think95592 2026. 7. 28. 14:22

1. 프로젝트 및 담당 기능 소개

프로젝트 소개

이 프로젝트에서 거점은 플레이어가 탐색을 준비하고, 획득한 자원을 정리하는 중심 공간입니다.

플레이어는 거점에서 퀘스트를 확인하고 아이템을 거래·보관·제작한 뒤 다음 탐색 지역을 선택할 수 있습니다.

담당한 기능

  • 거점 컴퓨터와 전용 UI
  • 퀘스트 시스템
  • 상점 및 중고거래
  • 제작 시스템
  • 창고 시스템
  • 레벨 선택 및 이동
  • 거점 UI의 MVVM 구조
  • 거점 진행 상태 저장 및 복원

사용 기술

  • Unreal Engine 5
  • C++
  • Blueprint
  • DataTable 및 Data Asset
  • MVVM
  • GameInstance Subsystem
  • Gameplay Message
  • SaveGame

2. 거점의 전체 플레이 흐름

거점의 개별 기능을 따로 제공하는 것보다, 탐색 전후의 흐름이 자연스럽게 연결되는 것을 목표로 했습니다.

퀘스트 수락
→ 상점과 중고거래 이용
→ 제작 및 아이템 정리
→ 창고에 탐색 자원 보관
→ 탐색 지역 선택
→ 마트 탐색
→ 거점 복귀
→ 상품 및 거래 목록 갱신


3. 시스템을 어떻게 설계했는가

3.1 데이터 중심 설계

퀘스트, 제작법, 상품과 레벨 정보를 코드에 직접 작성하지 않고 DataTable 및 Data Asset으로 분리했습니다.

  • 퀘스트 데이터: 목표, 조건, 보상, 후속 퀘스트
  • 제작 데이터: 필요 재료, 수량, 결과 아이템
  • 상점 데이터: 상품, 가격, 해금 여부, 거래 생성 규칙
  • 레벨 데이터: 이동할 레벨, 프리로드 에셋, 배경음악
  • 아이템 데이터: 이름, 아이콘, 가격과 기본 정보

이를 통해 콘텐츠를 추가하거나 수정할 때 게임 로직을 직접 변경하지 않고 데이터 설정만으로 관리할 수 있도록 구성했습니다.

3.2 기능별 Subsystem

기능의 핵심 로직은 액터나 Widget이 아니라 기능별 Subsystem에서 처리했습니다.

  • Quest Subsystem
  • Shop Subsystem
  • Crafting Subsystem
  • Storage Subsystem
  • Game Flow Subsystem
  • Save Subsystem

거점 액터는 상호작용과 UI 실행을 담당하고, 실제 거래·제작·아이템 이동과 같은 게임 규칙은 Subsystem이 담당합니다.

3.3 MVVM 적용

View는 화면 표시와 입력 전달, ViewModel은 UI 상태와 명령 전달, Subsystem은 실제 게임 규칙을 담당하도록 분리했습니다.

플레이어 입력
→ Widget Blueprint
→ ViewModel
→ Subsystem
→ 데이터 및 인벤토리 변경
→ ViewModel 갱신
→ Widget 화면 반영

ViewModel과 Subsystem에서 실행 가능 여부를 다시 검사하기 때문에 UI 상태와 실제 게임 상태가 달라져도 잘못된 거래나 제작이 실행되는 것을 방지할 수 있습니다.

플레이어가 거점 액터와 상호작용하면 UI Manager가 Widget과 ViewModel을 생성하고 연결합니다. View는 플레이어의 입력을 ViewModel에 전달하며, ViewModel은 입력값을 검증한 뒤 기능별 Subsystem에 작업을 요청합니다. Subsystem은 실제 인벤토리와 게임 상태를 기준으로 실행 가능 여부를 다시 확인하고 데이터를 변경합니다. 변경 결과는 인벤토리 이벤트와 ViewModel의 변경 알림을 통해 Widget에 반영됩니다.

 


4. 기능별 구현

각 기능은 아래 형식을 동일하게 사용합니다.

기능의 목적
→ 사용한 데이터
→ 처리 흐름
→ 다른 시스템과의 연결
→ 결과 화면

4.1 컴퓨터

퀘스트, 상점과 중고거래에 접근하는 거점의 메인 인터페이스입니다.

컴퓨터와 상호작용하면 전용 카메라로 이동하고, 데스크톱 형태의 UI가 표시됩니다. 카메라 이동에는 보간을 적용해 실제 컴퓨터를 사용하는 느낌을 표현했습니다.

 



4.2 퀘스트

퀘스트의 내용, 조건, 보상과 진행 관계를 데이터 테이블로 관리했습니다.

퀘스트 상태는 잠김, 수락 가능, 진행 중, 완료로 구분했습니다. 퀘스트 완료 시 NextQuestIDs에 등록된 후속 퀘스트를 해금하여 데이터 설정만으로 분기 구조를 구성할 수 있도록 했습니다.

아이템 제출형 퀘스트는 플레이어와 창고의 아이템을 함께 확인합니다. 행동형 퀘스트는 Gameplay Message를 통해 구매, 판매, 제작, 시설 파괴와 탐색 행동을 기록합니다.

퀘스트 데이터 조회
→ 수락 및 조건 등록
→ 행동 이벤트 수신
→ 진행도 증가
→ 조건 충족 확인
→ 보상 지급
→ 후속 퀘스트 해금

 

 

 

4.3 상점 및 중고거래

일반 상점은 고정 상품과 무작위 상품으로 구성했습니다. 상품의 가격, 수량과 해금 여부는 데이터 에셋에서 설정합니다.

중고거래는 아이템 데이터와 문장 템플릿을 조합해 구매 요청 글과 판매 글을 자동 생성합니다. 탐색을 마치고 거점으로 복귀하면 무작위 상품과 중고거래 목록을 갱신합니다.

구매와 판매 과정에서는 플레이어와 창고의 재화 및 아이템을 함께 확인합니다. 거래에 실패하면 차감한 재화나 아이템을 복구하도록 처리했습니다.

 

 

4.4 제작

제작 레시피는 데이터 테이블을 통해 관리합니다. 각 레시피에는 필요 재료와 수량, 결과 아이템과 제작 수량을 설정할 수 있습니다.

제작할 때 플레이어와 창고의 재료를 합산하여 확인합니다. 플레이어 인벤토리의 재료를 먼저 사용하고, 부족한 수량은 창고에서 차감합니다.

레시피 검색
→ 해금 상태 확인
→ 전체 재료 수량 확인
→ 재료 차감
→ 결과 아이템 지급
→ 제작 완료 이벤트 전송

결과 아이템을 지급하지 못하면 차감한 재료를 원래 인벤토리로 돌려 제작 전 상태를 복구합니다.

4.5 창고

플레이어 인벤토리와 별도의 창고 인벤토리를 구성했습니다.

개별 수량 이동, 절반 및 최대 수량 선택, 다중 선택, 전체 보관과 회수를 지원합니다. 인벤토리 변경 이벤트가 발생하면 ViewModel이 양쪽 목록과 버튼 상태를 다시 구성합니다.

창고의 아이템은 단순히 보관하는 것에서 끝나지 않고 제작, 퀘스트 제출, 상점 판매와 코인 결제에도 함께 사용됩니다.

 

 

4.6 레벨 선택

거점 출구와 상호작용하면 탐색 지역 선택 UI가 표시됩니다.

지역마다 입장 조건과 필요 아이템을 설정할 수 있습니다. 조건을 충족하면 입장 아이템을 차감하고, 현재 게임 상태를 저장한 뒤 로딩 레벨을 거쳐 선택한 탐색 지역으로 이동합니다.

이동 요청에 실패하면 차감한 입장 아이템을 다시 지급합니다.

 


5. 구현하면서 해결한 문제

5.1 UI와 게임 로직의 결합

문제

초기에는 액터와 Widget이 데이터를 직접 처리했습니다. 기능이 늘어나면서 Widget마다 데이터 조회와 갱신 코드가 중복됐고, UI 수정이 게임 로직에 영향을 주기 시작했습니다.

해결

기능 로직을 Subsystem으로 이동하고, ViewModel을 통해 Widget과 연결했습니다.

결과

  • UI와 게임 규칙의 책임 분리
  • Widget Blueprint의 수정 범위 감소
  • 동일한 로직을 여러 화면에서 재사용 가능
  • 데이터 변경에 따른 UI 갱신 방식 통일

5.2 플레이어와 창고 자원의 통합

문제

제작이나 퀘스트 제출을 위해 창고 아이템을 매번 플레이어 인벤토리로 꺼내야 하면 거점 이용 과정이 번거로워집니다.

해결

플레이어와 창고의 수량을 합산해서 조회하고, 플레이어 인벤토리부터 사용한 뒤 부족한 수량을 창고에서 차감하는 공통 기능을 구성했습니다.

결과

창고에 보관된 재료와 퀘스트 아이템을 꺼내지 않고 바로 사용할 수 있게 되었습니다.

5.3 처리 도중 발생하는 데이터 불일치

문제

거래와 제작은 재화 차감, 재료 소모, 결과물 지급처럼 여러 단계로 처리됩니다. 중간 단계에서 실패하면 아이템만 사라지거나 결과물만 지급되는 문제가 발생할 수 있습니다.

해결

작업 전 실행 가능 여부를 검사하고, 작업 중 실패하면 이미 변경한 아이템과 재화를 이전 상태로 되돌리는 롤백 처리를 적용했습니다.

결과

일부 단계만 반영되어 아이템이나 재화가 비정상적으로 변경되는 상황을 방지했습니다.

5.4 시스템 간 직접 참조

문제

상점과 제작 시스템이 퀘스트 시스템을 직접 호출하면 새로운 퀘스트 조건을 추가할수록 시스템 간 의존성이 증가합니다.

해결

구매, 판매와 제작 성공 시 Gameplay Message를 발행하고, 퀘스트 시스템이 필요한 메시지를 구독하도록 구성했습니다.

결과

각 기능을 독립적으로 유지하면서 플레이어의 다양한 행동을 퀘스트 조건으로 사용할 수 있게 되었습니다.

5.5 데이터 입력 오류

문제

데이터 기반 구조에서는 잘못된 ID나 형식이 입력되면 여러 퀘스트의 진행 흐름이 동시에 손상될 수 있습니다.

해결

퀘스트 데이터를 가져올 때 중복 ID, 잘못된 조건 형식, 잘못된 보상 값과 존재하지 않는 후속 퀘스트 ID를 검증했습니다. 오류가 발견되면 기존 데이터를 유지하도록 구성했습니다.

결과

잘못된 데이터가 전체 퀘스트 테이블에 반영되는 것을 방지했습니다.


6. 작업을 통해 배운 점

데이터 중심 설계에도 검증이 필요하다

데이터를 코드에서 분리하면 콘텐츠를 쉽게 확장할 수 있지만, 잘못된 데이터도 쉽게 추가될 수 있습니다. 데이터 구조뿐 아니라 입력 검증과 단일 원본 관리까지 함께 설계해야 한다는 것을 배웠습니다.

저장 범위는 기능 구현 전에 정해야 한다

인벤토리와 퀘스트뿐 아니라 해금 상태, 무작위 거래 목록과 현재 게임 흐름처럼 어떤 상태를 유지할지 초기에 결정해야 합니다. 저장 범위가 늦게 정해지면 각 시스템을 다시 수정해야 한다는 점을 경험했습니다.

UI 검증만으로는 안전하지 않다

버튼을 비활성화해도 실제 게임 상태는 언제든 변경될 수 있습니다. ViewModel과 Subsystem에서 실행 직전 데이터를 다시 검사해야 안전하게 기능을 처리할 수 있다는 것을 배웠습니다.

여러 데이터를 변경하는 작업은 하나의 거래처럼 처리해야 한다

아이템 이동, 구매와 제작처럼 여러 데이터를 변경하는 기능은 중간 실패 가능성을 고려해야 합니다. 사전 검증과 롤백을 통해 전체 작업이 성공하거나 이전 상태로 복구되도록 설계하는 것이 중요했습니다.

시스템 연결 방식도 확장성에 영향을 준다

직접 참조는 처음에는 간단하지만 기능이 많아질수록 의존성이 증가합니다. 여러 시스템에서 발생하는 행동을 이벤트로 전달하면 각 기능의 책임을 유지하면서도 새로운 조건을 추가할 수 있다는 점을 배웠습니다.


7. 추가로 개선하고 싶은 부분

  • 상점 상품 해금 상태와 중고거래 목록의 저장 범위 확장
  • 레시피와 퀘스트에서 사용하는 아이템 ID 자동 검증
  • 콘텐츠 데이터의 단일 원본 관리
  • 공통 거래 및 롤백 기능의 모듈화
  • 퀘스트 진행 경로 자동 테스트
  • 개발용 설정과 실제 플레이 설정 분리

이 부분은 실패 목록처럼 작성하기보다, 현재 구조를 이해한 뒤 발견한 다음 개선 방향으로 정리합니다.


8. 마무리

이번 작업에서는 거점에 필요한 기능을 각각 구현하는 것에서 그치지 않고, 퀘스트부터 거래, 제작, 창고와 레벨 이동까지 하나의 플레이 흐름으로 연결했습니다.

또한 DataTable과 Data Asset을 활용한 데이터 중심 설계, MVVM 기반 UI 구조, Subsystem을 통한 기능 분리와 Gameplay Message를 이용한 이벤트 통신을 실제 게임 기능에 적용했습니다.

이 과정에서 기능을 정상적으로 동작하게 만드는 것뿐 아니라 데이터 일관성, 실패 복구, 저장 범위와 시스템 간 책임을 함께 고려해야 한다는 것을 배웠습니다.