프로젝트 아키텍처 개요: NetworkData, UKLingoNetworkSystem, UPopupManager
이 문서는 Onepiece 프로젝트 내 UKLingoNetworkSystem, UPopupManager, 그리고 NetworkData 구조에 적용된 주요 프로그래밍 디자인 패턴을 설명합니다. 프로젝트는 언리얼 엔진의 특성을 잘 활용하여 효율적이고 현대적인 아키텍처를 구축하고 있습니다.
핵심 컴포넌트 요약
| 클래스/파일 | 핵심 패턴 | 보조 패턴 | 주요 역할 |
|---|---|---|---|
| UKLingoNetworkSystem | 싱글톤 (Singleton), 퍼사드 (Facade) | 비동기 콜백 (Observer) | 게임 전체에서 단 하나만 존재하며, 복잡한 네트워크 통신을 단순한 함수 호출로 제공 |
| UPopupManager | 싱글톤 (Singleton), 매니저 (Manager) | 오브젝트 풀 (Object Pool), 팩토리 (Factory) | 플레이어별로 존재하며, 모든 UI 팝업의 생성, 소멸, 스택 관리를 중앙에서 처리 |
| NetworkData.h | 데이터 전송 객체 (DTO) | - | 서버와 클라이언트 간에 데이터를 주고받기 위한 순수 데이터 구조체들의 집합 |
1. UKLingoNetworkSystem: 네트워크 관리를 위한 통합 창구
UKLingoNetworkSystem은 서버와의 모든 통신을 책임지는 중앙 시스템으로, 다음과 같은 디자인 패턴이 적용되었습니다.
A. 싱글톤 (Singleton) 패턴
- 구현:
UGameInstanceSubsystem을 상속하여 구현되었습니다. 언리얼 엔진은 이 클래스의 인스턴스를 게임 시작 시 단 하나만 생성하고 게임 종료 시까지 유지하도록 보장합니다. - 접근:
UKLingoNetworkSystem::Get()과 같은 정적 접근자(static getter)를 통해 게임 내 어디서든 이 유일한 인스턴스에 접근할 수 있습니다. - 목적: 네트워크 기능이 필요한 모든 오브젝트가 각자 연결을 관리할 필요 없이, 검증된 단일 통로를 통해 일관된 방식으로 서버와 통신할 수 있도록 합니다.
B. 퍼사드 (Facade) 패턴
- 구현:
RequestUserRegister(),RequestListenAudio(),RequestChatAudio()등 다수의 고수준 공개(public) 메서드들을 통해 나타납니다. - 목적: HTTP 요청 생성, 헤더 설정, 데이터 직렬화(JSON 변환), 비동기 전송, 응답 수신 및 파싱 등 복잡한 네트워크 처리 과정을 모두 내부적으로 캡슐화(숨김)합니다. 외부 호출자는 단순히 목적에 맞는 함수를 호출하고 결과만 받으면 됩니다.
- 효과: 다른 개발자들은 네트워크의 내부 동작 복잡성을 알 필요 없이 단순한 API를 통해 기능을 사용할 수 있어 코드의 가독성을 높이고 유지보수를 용이하게 합니다.
C. 비동기 콜백 (Asynchronous Callback) / 옵저버 (Observer) 패턴
- 구현: 모든 요청 함수는
FResponseUserRegisterDelegate와 같은 델리게이트(Delegate)를 파라미터로 받습니다. - 목적: 네트워크 요청은 즉시 완료되지 않으므로, 이 패턴은 요청을 보낸 후 프로그램이 메인 스레드를 블로킹하지 않고 다른 작업을 계속하도록 합니다. 서버로부터 응답이 오면, 미리 등록해 둔 콜백 함수(델리게이트)가 실행되어 결과 처리를 시작합니다.
- 효과: 요청을 보낸 객체와 응답을 처리하는 로직을 분리(Decoupling)하여 유연하고 반응성이 좋은 애플리케이션을 구축할 수 있습니다.
2. UPopupManager: UI 팝업의 무대 감독
UPopupManager는 게임에 표시되는 모든 종류의 팝업(메시지 박스, 퀘스트 창 등)을 총괄하는 시스템입니다.
A. 싱글톤 (Singleton) 패턴
- 구현:
ULocalPlayerSubsystem을 상속하여 구현되었습니다. - 목적:
UGameInstanceSubsystem과 유사하게 단일 인스턴스를 보장하지만, 이 싱글톤은 로컬 플레이어(Local Player) 단위로 생성됩니다. 이는 멀티플레이어 환경에서 각 플레이어에게 독립적인 UI 상태 관리가 필요할 때 적합합니다. - 효과: UI는 플레이어에게 종속적인 경우가 많으므로, 플레이어별로 독립적인 UI 상태(예: 팝업 스택)를 효율적으로 관리할 수 있습니다.
B. 매니저 (Manager) 패턴
- 구현:
ShowPopup(),HideCurrentPopup(),PopupStack관리 로직 등 클래스의 핵심 기능 자체가 이 패턴을 따릅니다. - 목적: 팝업 위젯의 생성, 소멸, 표시 순서(Z-order), 스택 관리(팝업 위에 다른 팝업이 뜨는 경우)를 중앙에서 책임집니다.
- 효과: UI 관련 로직이 여러 곳에 흩어지는 것을 방지하고, 팝업 관리 정책을 한곳에서 변경할 수 있어 유지보수가 매우 편리합니다.
C. 팩토리 (Factory) + 오브젝트 풀 (Object Pool) 패턴
- 구현:
ShowPopupAs,() EnsurePopupWidget()함수와PopupClassMap,PopupWidgetMap변수에서 찾아볼 수 있습니다.- 팩토리:
ShowPopup(EPopupType::MsgBox)처럼 특정 타입을 요청하면, 매니저가PopupClassMap을 보고 적절한 위젯 클래스를 찾아 인스턴스를 생성합니다. 호출자는 구체적인 위젯 클래스를 알 필요가 없습니다. - 오브젝트 풀: 생성된 위젯은 숨겨질 때 즉시 파괴되지 않고
PopupWidgetMap에 저장되었다가, 나중에 같은 타입의 팝업이 필요할 때 재사용될 수 있습니다.
- 팩토리:
- 효과: 반복적인 위젯 생성/소멸 비용을 줄여 성능을 향상시키고, 팝업 생성 로직을 표준화하여 일관된 팝업 관리를 가능하게 합니다.
3. NetworkData.h: 소통을 위한 데이터 규격
이 파일은 서버와 클라이언트가 "같은 언어"로 대화하기 위한 약속(프로토콜)을 정의한 것에 가깝습니다.
A. 데이터 전송 객체 (Data Transfer Object, DTO)
- 구현:
FResponseUserRegister,FRequestInterviewAnswer등 다수의USTRUCT선언들이 이 패턴에 해당합니다. - 목적: 이 구조체들은 오직 데이터를 담는 용도로만 사용됩니다. 내부에는 복잡한 비즈니스 로직 없이, 서버의 JSON 데이터와 1:1로 매핑되는 속성들만 정의되어 있습니다.
- 효과: 네트워크를 통해 주고받는 데이터의 형식을 명확하게 정의하여 클라이언트와 서버 간의 데이터 일관성을 유지할 수 있습니다. 언리얼의 리플렉션 시스템(
USTRUCT,UPROPERTY)과 결합되어 JSON과 구조체 간의 자동 변환을 용이하게 하여 개발 효율성을 높입니다.
B. 네임스페이스를 이용한 설정 관리
- 구현:
NetworkConfig및RequestAPI네임스페이스를 사용하여 관련된 상수 및 유틸리티 함수들을 그룹화합니다. - 목적: 서버 URL, API 엔드포인트와 같은 네트워크 관련 설정과 경로들을 한곳에 모아 관리함으로써 전역 네임스페이스 오염을 방지하고 코드의 응집도를 높입니다.
- 효과: 설정 변경 시 해당 네임스페이스 내에서만 수정하면 되므로 유지보수가 편리하고, 관련 정보들을 한눈에 파악하기 쉽게 합니다.
결론
이 프로젝트는 다음과 같이 효율적이고 현대적인 소프트웨어 아키텍처 원칙을 충실히 따르고 있습니다.
- 관심사의 분리 (Separation of Concerns):
UKLingoNetworkSystem은 "통신",UPopupManager는 "UI",NetworkData는 "데이터 형식"이라는 각자의 역할에만 집중합니다. - 중앙 집중 관리: 싱글톤 및 매니저 패턴을 통해 복잡한 기능(네트워크, UI)을 한곳에서 통제하여 코드의 일관성을 유지하고 버그 발생 가능성을 줄입니다.
- 낮은 결합도 (Low Coupling): 퍼사드와 비동기 콜백 패턴을 통해 각 시스템을 사용하는 쪽에서는 내부의 복잡한 구현을 알 필요가 없어, 독립적인 개발, 테스트 및 변경이 용이한 구조를 만듭니다.
이러한 디자인 패턴의 적용은 코드의 재사용성을 높이고, 시스템의 확장성을 확보하며, 전반적인 개발 및 유지보수 효율성을 크게 향상시킵니다.