서론
어느 날 (거의) 3살 된 딸아이가 저에게 게임을 만들어 달라고 부탁했습니다. 잠시 후 MessagePipe 라는 메시징 파이프라인(Pub/Sub) 라이브러리 가 출시되었음을 알게 되었습니다. 그리고 그간 궁금했던 VContainer 라는 Dependency Injection 라이브러리 가 있습니다. 저는 또한 Unity를 위한 충분한 아키텍처를 찾기 위한 여정을 진행 중이지만 이 기회를 통해 모든 것을 함께 할 수 있습니다.
게임 자체는 Tebak Angka(인도네시아어로 “숫자 추측”)라는 간단한 숫자 추측 게임입니다. 숫자가 주어지면 선택한 숫자에서 올바른 숫자를 선택하십시오. 게임 자체로서 만들기가 쉬워야 합니다. 그러나 여기에서 나의 주요 초점은 아키텍처 개념을 시도하는 동시에 MessagePipe 및 VContainer도 배우는 것입니다.
Project Settings – Script Compilation – Scripting Define Symbols
UNITASK_DOTWEEN_SUPPORT
Github 프로젝트 URL : https://github.com/ripandy/TebakAngka
플레이 가능한 URL : https://ripandy.github.io/TebakAngka/

Architecture
나는 잠시 동안 Clean Architecture를 배우고 있습니다. 클린 아키텍처를 프레임워크로 잘못 생각한 적이 있었고 그렇지 않다는 것을 힘들게 배웠습니다. 그 실수는 저로 하여금 클린 아키텍처를 혐오하게 만들었습니다. 다른 아키텍처, 디자인 패턴, 프레임워크 등을 시도하면서 클린 아키텍처는 실제로 개념이지 프레임워크가 아님을 깨달았습니다.
내 이해에 따르면 클린 아키텍처는 주어진 시간에 손상될 수 있는 다른 프레임워크, 도구 또는 라이브러리와의 독립성을 강조합니다. 그러나 요즘에는 일반적으로 개발에 실제로 매우 도움이 되기 때문에 다른 라이브러리에 의존하지 않는 것이 정말 어렵습니다. 따라서 내 이해로는 가장 유용한 프레임워크나 라이브러리 몇 가지를 직접 선택하는 것이 좋습니다. 이번에는 MessagePipe와 VContainer입니다. 다른 모든 것은 교체 가능하도록 설계되어야 합니다.

우선, 위는 이 프로젝트에 대한 (간단하게 디자인된) 클래스 다이어그램입니다. Clean Architecture 개념을 따르려고 노력했습니다. Domain, Presentation, View 레이어를 사용해 보았습니다. 또한 MessagePipe 레이어를 도메인과 프레젠테이션 레이어 사이의 브리지로 추가합니다. 이제 각 레이어에 대해 논의하겠습니다.
Domain Layer
이 계층에는 애플리케이션의 비즈니스 로직이 포함됩니다. 이 계층은 응용 프로그램을 매우 구체적이고 다른 응용 프로그램과 다르게 만듭니다. 이 프로젝트에서 이 계층은 이것이 숫자 추측 게임임을 분명히 했습니다.
이 레이어에는 실제로 게임 모델과 게임 상태라는 두 개의 하위 레이어가 있습니다. 게임 모델에는 데이터만 포함되고 게임 상태에는 데이터가 없지만 게임 모델의 데이터를 처리합니다. Clean Architecture의 정의에서 이는 각각 엔티티 및 유스 케이스 레이어와 동일합니다.
게임 상태는 게임의 흐름을 나타냅니다. 게임은 레벨 진행 및 난이도와 함께 질문 및 답변 세트가 생성되는 레벨 생성 상태에서 시작됩니다. 다음으로 게임은 앱이 사용자의 답변을 기다리는 사용자 입력 상태로 진행됩니다. 마지막으로 게임은 사용자의 답변을 확인합니다. 게임 상태 흐름은 게임 상태 엔진을 사용하여 관리됩니다.
게임의 논리는 충분히 명확합니다. 질문 및 답변 생성, 답변 대기, 답변 확인. 이 시점에서 게임이 실제로 완료되었다고 할 수 있습니다. 그러나 그것은 여전히 논리일 뿐이며, 아무 것도 표시되지 않고 상호작용이 전혀 없는 매우 지루한 게임입니다. 숫자 표시 방법, 애니메이션 방법 또는 사용자가 답변을 제출하는 방법에 대한 구현이나 언급이 없습니다. 이들은 비즈니스 로직 범위 밖에 있으며 프레젠테이션 및/또는 보기 계층에서 처리됩니다.
Presentation Layer
이 계층은 도메인 계층의 정보를 처리한 다음 보기 계층으로 전달합니다. 정보 변환이 필요한 경우 이 레이어에서도 처리됩니다.
Clean Architecture에 따르면 이상적으로는 Interface를 사용하여 View Layer와 통신해야 합니다. 그러나 이것이 실제 Unity 프로젝트이므로 Unity의 클래스를 직접 참조해도 괜찮다고 결정했습니다. 그 이유는 Unity 외부에서 이 프로젝트를 변환할 의도가 없었기 때문에 불필요한 인터페이스를 추가하여 코드를 복잡하게 만든 이유입니다. 나는 여기서 KISS principle을 따르려고 노력했습니다 .
프리젠테이션 계층의 아이디어는 도메인 계층을 사용자에게 제시하는 것입니다. 이 경우 사용자에게 질문과 사용 가능한 번호에 대해 알려주고 싶습니다. 그런 다음 사용자의 답변을 기다립니다. 답변을 제출한 후 답변이 맞는지 틀린지 사용자에게 제시합니다.
클린 아키텍처 용어에서 프레젠테이션 계층은 인터페이스 어댑터 계층과 동일합니다. Clean Architecture를 참조하여 Presentation Layer에 Presenter 및 Controller라는 하위 계층을 추가합니다. Presenter는 애니메이션, 가시성, 효과 등의 객체를 볼 수 있도록 유출 데이터를 관리하고, Controller는 버튼 누름 이벤트와 같은 유입 또는 입력 데이터를 관리합니다.
View Layer
클래스 다이어그램은 View/Unity 부분을 더 작게 보이게 했습니다. 그러나 실제로는 애니메이션, 오디오 소스, 파티클, 장면 관리 등과 같은 많은 부분이 그곳에서 발생합니다. 이 레이어는 모두 Unity 기본 사항에 관한 것입니다. MonoBehavior, UI 등에서 파생된 구성 요소, 클래스는 이 계층으로 분류됩니다. 논리와 디자인/UX 사이의 이러한 분리는 프로그래밍 부분을 건드리고 싶지 않은 디자이너와 팀에서 작업할 때 도움이 될 수 있습니다.
이 계층의 주요 아이디어는 응용 프로그램의 가시적이거나 상호 작용 가능한 부분입니다. 개인적으로 View 레이어의 특성은 그대로 두면 전체 앱과의 상관 관계가 거의 또는 전혀 없을 수 있다는 것입니다. 예를 들어, 이 앱의 숫자는 단독으로는 의미가 없지만 이 특정 앱에서는 선택의 의미가 있습니다. 다른 앱에서 점수 표시로 사용하고 싶은지 누가 알겠습니까?
Design Pattern
아키텍처는 프레임워크도 아니고 디자인 패턴도 아닙니다. 아키텍처는 프로젝트를 구조화하는 방법입니다. 프로젝트 아키텍처 내에서 하나 이상의 디자인 패턴을 사용할 수 있습니다. Clean Architecture를 배우면서 가장 큰 실수를 저질렀던 곳입니다. 아키텍처를 프레임워크로 생각했기 때문에 프로젝트의 모든 부분은 특정 규칙이나 디자인 패턴을 따라야 합니다. 그리고 그것은 확실히 좋지 않습니다. 필요할 때마다 적절한 디자인 패턴을 사용하십시오.
이 프로젝트에서는 도메인 레이어의 The State Pattern을 사용했습니다. 기본적으로 상태는 상태 범위 내에서 모든 프로세스를 제한합니다. 다른 프로세스는 상태가 변경될 때까지 기다려야 합니다. 게임 플레이를 상태로 분리했습니다. 그런 다음 상태는 생성 수준, 사용자 입력 대기, 결과 확인에서 흐른 다음 생성 수준에서 다시 시작합니다.
암묵적으로 표현 계층에서 MVP 패턴도 사용했습니다. 정확히 말하면 Presenter는 컨트롤러 클래스와 Presenter 클래스 자체이고, View는 Unity의 객체와 MonoBehavior에서 파생된 클래스이며, Model은 IAsyncSubscriber 구독을 통해 MessagePipe에서 받은 값입니다.
MessagePipe
MessagePipe는 도메인 레이어와 프레젠테이션 레이어 사이의 브리지로 사용됩니다. 이 프로젝트에서는 Game State가 (a)wait 상태가 완료될 때 표시로 Presentation 및 View 레이어를 기다릴 수 있도록 사용 IAsyncPublisher와 IAsyncSubscriber을 페어링합니다.
실제 클래스의 참조는 인터페이스일 뿐입니다. 인터페이스의 실제 구현은 MessagePipe 라이브러리 내에 있으며 종속성 주입 기술을 사용하여 주입됩니다. 이 프로젝트에서 종속성 주입 라이브러리는 VContainer입니다.
인터페이스에 대한 참조를 만들고 주입을 처리하는 것 외에는 MessagePipe 부분에서 특정 작업을 수행하지 않지만 실제 프로젝트에 상당한 영향을 미칩니다.
VContainer
가볍고 매우 강력한 의존성 주입 라이브러리. 이전에 Unity용으로 다른 DI 라이브러리를 사용해 본 적이 있지만 개인적으로 단순성보다 VContainer를 선호합니다.
VContainer가 하는 일은 기본적으로 프로젝트에서 사용되는 클래스에 대한 참조를 유지한 다음 이를 필요로 하는 클래스에 주입하는 것입니다. 이것은 우리 자신의 참조를 관리하는 것이 상당히 번거롭기 때문에 매우 유용합니다.
첨언
나는 내가 완벽하지 않다는 것을 이해합니다. 이해가 안가는 부분이 많아서 계속 공부하겠습니다. 내가 여전히 의심하는 몇 가지 사항은 다음과 같습니다.
- 이 프로젝트의 아키텍처는 주로 클린 아키텍처를 참조했지만 DDD 등을 참조했을 수 있으므로 클린 아키텍처라고 부르는 것이 확실하지 않습니다. 그러나 한 가지 확실한 것은 그것이 계층화된 아키텍처라는 것입니다.
- CardView와 ResultView가 View Layer인지 Presentation Layer인지 결정하기 어렵습니다. 개인적으로 비슷한 역할이라고 생각합니다.
- 개인적으로 DI 부분은 GameplayLifetimeScope의 하나의 큰 DI가 아니라 Game State(Use Case) 또는 Presenter로 구분할 수 있다고 생각합니다.
- 이 글의 프로젝트는 간단합니다. 대규모 프로젝트에서는 레이어, 역할 등의 분리에 어려움이 있을 수 있습니다. 이는 다른 프로젝트에 대한 완벽한 레이어링 예제를 보장하지 않습니다. 하지만 저는 개인적으로 이 정도면 “충분하다”고 생각합니다.
결론
- 클린 아키텍처는 프레임워크가 아니라 개념입니다. 같은 실수를 하지 마십시오.
- 소수의 손으로 선택한 프레임워크나 라이브러리에 의존하는 것은 괜찮습니다. 이 경우 MessagePipe 및 VContainer .
- 레이어의 어느 부분에든 적합한 디자인 패턴을 사용해도 좋습니다. 이 프로젝트에서는 도메인 레이어 의 상태 패턴 을 사용했습니다.
- 이 프로젝트는 계층화된 아키텍처를 사용하며 Domain Layer, Presentation Layer, View Layer가 있습니다.
- Domain Layer는 비즈니스 로직을 처리하고 프로젝트를 특정 애플리케이션으로 정의합니다.
- 프리젠테이션 계층은 도메인 계층에서 뷰 계층으로 정보를 전달하고 변환합니다.
- View Layer는 애플리케이션의 가시적, 청각적, 상호 작용 가능한 부분을 처리합니다.
- MessagePipe는 이 프로젝트에서 Domain Layer와 Presentation Layer 사이의 가교 역할을 하는 Pub/Sub Messaging Pipeline 라이브러리입니다.
- VContainer는 가볍고 강력한 종속성 주입 라이브러리입니다.
읽어 주셔서 감사합니다! 모든 건설적인 피드백을 높이 평가합니다!
참조
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
https://www.freecodecamp.org/news/a-quick-introduction-to-clean-architecture-990c014448d2/
https://github.com/Cysharp/MessagePipe
https://github.com/hadashiA/VContainer