본문 바로가기

Unity/디자인 패턴

[Unity] MVC(Model-View-Controller) 패턴 이해 및 활용

MVC(Model-View-Controller) 패턴이란 무엇인가?

개발을 하다 보면, 프로젝트가 커질수록 코드가 점점 복잡해지는 문제를 마주하게 됩니다. 처음에는 간단하게 시작한 코드도 새로운 기능이 추가되고, 다양한 상황을 처리해야 할 필요성이 생기면서 점차 관리하기 어려운 상태에 이르게 됩니다. 예를 들어, 캐릭터의 상태를 업데이트하고, 그 정보를 화면에 표시하며, 동시에 사용자의 입력을 처리하는 모든 작업이 하나의 스크립트에 담겨있다면, 코드가 비대해지고 유지보수가 어렵게 됩니다.

이러한 문제를 해결하기 위해 MVC 패턴을 사용하는 것이 매우 유용합니다. MVC 패턴은 코드를 Model(모델), View(뷰), Controller(컨트롤러)로 분리하여 각 부분의 역할을 명확하게 정의합니다. 이렇게 역할을 분리하면 코드를 훨씬 더 체계적이고 이해하기 쉽게 만들 수 있습니다.

  • Model은 게임의 데이터와 로직을 관리합니다. 캐릭터의 체력, 점수, 아이템 목록 등과 같은 상태를 저장하고 처리하는 역할을 합니다.
  • View는 데이터를 시각적으로 표현하는 부분입니다. 사용자가 화면에서 보는 UI 요소, 캐릭터의 상태나 점수를 표시하는 텍스트 등이 여기에 해당됩니다.
  • Controller는 사용자 입력을 처리하고, Model과 View를 연결하는 역할을 합니다. 사용자가 키보드나 마우스를 통해 명령을 입력하면, Controller는 이를 처리하고 Model을 업데이트한 후, 그 결과를 View에 반영합니다.

이렇게 각 기능을 분리함으로써 얻을 수 있는 가장 큰 이점은 유지보수성확장성입니다. 만약 게임에 새로운 기능을 추가하거나, 기존 기능을 수정해야 할 때, 관련된 코드만 수정하면 되기 때문에 다른 부분에 미치는 영향을 최소화할 수 있습니다. 예를 들어, 게임의 UI 디자인을 변경하고 싶다면 View만 수정하면 되고, 게임의 로직을 변경하고 싶다면 Model만 손보면 됩니다. 이렇게 각 부분이 독립적으로 동작하기 때문에, 코드가 복잡해지더라도 비교적 쉽게 관리할 수 있습니다.

Unity에서 MVC 패턴 구현하기

유니티에서 MVC 패턴을 구현하는 방법을 간단한 예제와 함께 알아보겠습니다.

PlayerModel.cs

먼저, Model을 구현해봅니다. Model은 게임 내 데이터를 관리하는 역할을 합니다. 예를 들어, PlayerModel이라는 클래스를 만들어 플레이어의 상태(예: 체력, 점수)를 관리할 수 있습니다.

public class PlayerModel
{
    public int Health { get; set; }
    public int Score { get; set; }

    public PlayerModel(int health, int score)
    {
        Health = health;
        Score = score;
    }
}

PlayerView.cs

이제 View를 구현해보겠습니다. View는 사용자에게 데이터를 보여주는 역할을 합니다. 예를 들어, PlayerView라는 클래스를 통해 화면에 플레이어의 체력과 점수를 표시할 수 있습니다.

public class PlayerView : MonoBehaviour
{
    public Text healthText;
    public Text scoreText;

    public void UpdateHealth(int health)
    {
        healthText.text = "Health: " + health.ToString();
    }

    public void UpdateScore(int score)
    {
        scoreText.text = "Score: " + score.ToString();
    }
}

PlayerController.cs

마지막으로 Controller를 구현해보겠습니다. Controller는 사용자의 입력을 처리하고, 이를 Model과 View에 반영하는 역할을 합니다.

public class PlayerController : MonoBehaviour
{
    private PlayerModel model;
    private PlayerView view;

    void Start()
    {
        model = new PlayerModel(100, 0);
        view = GetComponent<PlayerView>();
        view.UpdateHealth(model.Health);
        view.UpdateScore(model.Score);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            model.Score += 10;
            view.UpdateScore(model.Score);
        }
    }
}

이 코드에서는 스페이스바를 눌렀을 때 점수가 증가하고, 화면에 즉시 반영됩니다. 이렇게 MVC 패턴을 사용하면, 각 요소가 독립적으로 작동하여 코드의 유지보수성과 확장성을 높일 수 있습니다.

유니티에 적용하기

기본 세팅은 다음 포스트를 참고하시기 바랍니다.

[Unity] 유니티 연습 개발 환경 기본 세팅 - 오월의 사중주

이 포스트에서는 다양한 Unity 예제 실험, 간단한 프로젝트, 기능 구현 및 테스트 등에 사용될 기본 Unity 설정을 다룰 것입니다. 또한 제가 사용하는 Unity 레이아웃에 대한 소개도 포함됩니다. 이 설정은 주관적인 것이므로, 이 글을 맹목적으로 신뢰하지 마시고 참고용으로
  • MainCamera의 Clear Flags를 Skybox에서 Solid Color로 바꾸고, 검정색으로 변경해줍니다.

  • Hierarchy 창에 우클릭 후 UI > Text - TextMeshPro 를 선택합니다.

  • Text를 두개 생성하여 각각 이름을 붙여주고 위치를 조정합니다. 저는 PlayerHealth와 Score로 명명하였습니다.

  • 빈 오브젝트를 생성하고 Player라고 명명합니다.

  • Player 오브젝트에 위에서 작성했던 PlayerView Script와 PlayerController Script를 부착합니다.
  • PlayerView Script의 Health Text에는 PlayerHealth를 이어주고, ScoreText에는 Score를 이어줍니다.

이제 Play 버튼을 누른 후 Space 키를 누르면 Score가 증가하는 것을 볼 수 있습니다.

MVC 패턴 실제 적용 예제 - 쿠키 클리커 만들기

위에서 설명한 Model, View, Controller를 하나의 간단한 게임 시나리오로 통합해봅니다. 이 과정에서 MVC 패턴이 어떻게 동작하는지, 각 요소가 어떻게 상호작용하는지 자세히 살펴볼 것입니다.

이 게임에서는 사용자가 버튼을 클릭할 때마다 쿠키가 생성되고, 그 수가 화면에 표시됩니다. 또한 일정한 수의 쿠키를 획득하면 자동으로 쿠키를 생성해주는 기능도 추가해보겠습니다.

프로젝트 설정

새 Unity 프로젝트를 생성하거나, 기존 프로젝트에서 Model, View, Controller 폴더를 프로젝트의 Scripts 폴더 아래에 생성합니다. 각 폴더에 필요한 스크립트를 추가합니다. 기존 폴더에서 진행할 경우, 기존에 만들어둔 Player 스크립트 또한 각 역할 폴더에 넣어주도록 합시다. 이번 포스트에서는 기존 프로젝트에 이어 진행하겠습니다.

Model: 게임 데이터 관리

먼저, CookieModel을 만들어 쿠키의 개수와 자동 생성 기능을 관리합니다.

// CookieModel.cs
public class CookieModel
{
    public int CookieCount { get; private set; }
    public int AutoClickerCount { get; private set; }

    public void AddCookie(int amount)
    {
        CookieCount += amount;
    }

    public void BuyAutoClicker()
    {
        if (CookieCount >= 10) // 자동 클릭기를 사기 위한 쿠키 개수
        {
            AutoClickerCount++;
            CookieCount -= 10;
        }
    }

    public void AutoGenerateCookies()
    {
        CookieCount += AutoClickerCount;
    }
}

이 클래스는 쿠키의 수를 관리하며, 자동 클릭기를 구매하고 자동으로 쿠키를 생성하는 기능을 포함합니다.

View: UI 요소 관리

다음으로, CookieView를 만들어 쿠키의 수와 자동 클릭기 수를 화면에 표시합니다.

using TMPro;
using UnityEngine;

public class CookieView : MonoBehaviour
{
    public TextMeshProUGUI cookieCountText;
    public TextMeshProUGUI autoClickerCountText;

    public void UpdateCookieCount(int count)
    {
        cookieCountText.text = "Cookies: " + count.ToString();
    }

    public void UpdateAutoClickerCount(int count)
    {
        autoClickerCountText.text = "Auto Clickers: " + count.ToString();
    }
}

이 클래스는 TextMeshProUGUI를 사용하여 화면에 쿠키 개수와 자동 클릭기 개수를 표시합니다.

Controller: 사용자 입력 처리

이제 CookieController를 만들어 사용자의 입력을 처리하고, Model과 View를 연결합니다.

using UnityEngine;

public class CookieController : MonoBehaviour
{
    private CookieModel model;
    private CookieView view;

    void Start()
    {
        model = new CookieModel();
        view = GetComponent<CookieView>();

        // 초기 상태 업데이트
        view.UpdateCookieCount(model.CookieCount);
        view.UpdateAutoClickerCount(model.AutoClickerCount);

        // 1초마다 자동 쿠키 생성
        InvokeRepeating("AutoGenerateCookies", 1f, 1f);
    }

    public void OnCookieClick()
    {
        model.AddCookie(1);
        view.UpdateCookieCount(model.CookieCount);
    }

    public void OnBuyAutoClickerClick()
    {
        model.BuyAutoClicker();
        view.UpdateCookieCount(model.CookieCount);
        view.UpdateAutoClickerCount(model.AutoClickerCount);
    }

    private void AutoGenerateCookies()
    {
        model.AutoGenerateCookies();
        view.UpdateCookieCount(model.CookieCount);
    }
}

이 클래스는 쿠키 클릭과 자동 클릭기 구매 버튼을 처리하며, 일정 시간마다 자동으로 쿠키를 생성하는 기능을 제공합니다.

Unity에서의 설정

  1. UI 구성:
    • Canvas를 추가하고, 그 안에 Button 두 개(Cookie Click, Buy Auto Cookie Clicker)와 TextMeshPro - Text 두 개(Cookie Score, Auto Clicker Count)를 배치합니다.
    • 첫 번째 버튼은 쿠키를 클릭하는 기능을 담당하며, 두 번째 버튼은 자동 클릭기를 구매하는 기능을 담당합니다.
    • TextMeshPro - Text 요소는 쿠키 개수와 자동 클릭기 개수를 표시합니다.

  1. 스크립트 연결:
    • CookieController와 CookieView 스크립트를 동일한 GameObject (Cookie Clicker Manager)에 추가하고, TextMeshProUGUI 필드를 Unity 에디터에서 설정합니다.

  1. Button 이벤트 설정:
    • 첫 번째 버튼은 CookieController의 OnCookieClick 메서드를 호출하도록 설정합니다.
    • 두 번째 버튼은 CookieController의 OnBuyAutoClickerClick 메서드를 호출하도록 설정합니다.

게임 테스트

이제 게임을 실행하고 쿠키를 클릭하여 쿠키 개수를 늘려보세요. 일정 수의 쿠키를 모으면 자동 클릭기를 구매할 수 있으며, 자동 클릭기가 활성화되면 일정 시간마다 자동으로 쿠키가 생성됩니다.

완성 프로젝트

완성된 프로젝트를 올려둔 Github 주소입니다.

UnityGithub/DesignPatternProject at main · ralskwo/UnityGithub

Contribute to ralskwo/UnityGithub development by creating an account on GitHub.

MVC 패턴의 변형 및 확장: MVP와 MVVM

MVC 패턴은 소프트웨어 개발에서 오랫동안 사용되어 온 디자인 패턴이지만, 다양한 개발 요구 사항에 맞추어 몇 가지 변형이 생겨났습니다. 그중 대표적인 변형으로는 MVP(Model-View-Presenter) 패턴과 MVVM(Model-View-ViewModel) 패턴이 있습니다. 이 패턴들은 MVC의 기본 개념을 유지하면서도, 더 구체적인 요구 사항을 처리할 수 있도록 설계되었습니다.

MVP 패턴 (Model-View-Presenter)

MVP 패턴은 MVC 패턴에서 Controller 대신 Presenter가 도입된 형태입니다. 이 패턴은 주로 UI와 비즈니스 로직 간의 명확한 분리를 더욱 강화하기 위해 만들어졌습니다. MVP 패턴에서 Presenter는 다음과 같은 역할을 합니다:

  • PresenterViewModel 사이의 중재자 역할을 하며, 모든 비즈니스 로직을 처리합니다. 이는 View와 Presenter 간의 강한 결합을 피하고, View는 단순히 데이터를 표시하는 역할만 하도록 만듭니다.
  • View는 Presenter로부터 데이터를 받아 UI를 업데이트하고, 사용자 입력을 Presenter에 전달합니다.
  • 이 패턴은 주로 Android 개발과 같이 View의 테스트가 중요한 환경에서 널리 사용됩니다. View는 가능한 한 단순화되어 테스트를 용이하게 하고, 대부분의 로직은 Presenter에서 처리되기 때문에 View의 역할이 매우 제한적입니다​ (Richard Fu)​ (Patryk Galach).

MVP 패턴은 특히 화면이 많거나 복잡한 UI를 다루는 애플리케이션에서 유리합니다. Presenter가 View의 특정 UI 요소에 의존하지 않기 때문에, 다양한 View를 쉽게 교체하거나 재사용할 수 있습니다.

[Unity] MVP(Model-View-Presenter) 패턴 이해 및 활용 - 오월의 사중주

View는 Unity의 MonoBehaviour를 상속받아 구현합니다. Unity의 UI 요소를 사용해 사용자 입력을 받고, 이를 Presenter에 전달합니다.

MVVM 패턴 (Model-View-ViewModel)

MVVM 패턴은 주로 데이터 바인딩이 중요한 애플리케이션에서 사용되는 패턴으로, ViewModel이라는 새로운 요소가 도입되었습니다. MVVM 패턴의 주요 특징은 다음과 같습니다:

  • ViewModel은 View의 상태와 동작을 캡슐화하여, View와 Model 사이의 데이터를 처리합니다. ViewModel은 View와 직접 연결되지 않으며, 데이터 바인딩을 통해 상호작용합니다. 이로 인해 ViewModel은 테스트 가능하고, 재사용성이 높습니다.
  • View는 ViewModel에 바인딩되어, 자동으로 UI를 업데이트하거나 이벤트를 처리합니다. 이는 WPF(Windows Presentation Foundation)나 Xamarin 같은 프레임워크에서 많이 사용됩니다.
  • MVVM 패턴은 특히 UI가 복잡하고, 상태 관리가 중요한 애플리케이션에서 효율적입니다. ViewModel을 통해 UI 로직을 분리함으로써, 개발자들은 ViewModel을 테스트하거나 재사용할 수 있게 됩니다​ (WorldClassTalent)​ (Pragmate).

MVVM 패턴의 큰 장점은 양방향 데이터 바인딩을 통해 View와 ViewModel 간의 데이터 싱크가 자동으로 이루어진다는 점입니다. 이로 인해 View의 코드가 크게 줄어들고, UI 로직과 비즈니스 로직이 분리되어 테스트와 유지보수가 용이해집니다.

[Unity] MVVM(Model-View-ViewModel) 패턴 이해 및 활용 - 오월의 사중주

디자인 패턴은 소프트웨어 개발에서 코드의 유지보수성과 확장성을 높이기 위한 핵심 도구로 사용됩니다. 특히 UI가 복잡한 애플리케이션에서는 코드의 구조가 금방 복잡해지고 비효율적으로 변할 수 있습니다. 이러한 문제를 해결하고자 다양한 디자인 패턴이 제안되었으며, 그 중 하나가 바로

MVC, MVP, MVVM 패턴의 비교

  • MVC는 가장 전통적이고 일반적으로 사용되는 패턴으로, 단순한 애플리케이션부터 복잡한 애플리케이션까지 폭넓게 사용됩니다.
  • MVP는 View의 테스트를 강화하기 위해 설계되었으며, UI와 비즈니스 로직을 명확히 분리하는 데 중점을 둡니다.
  • MVVM은 데이터 바인딩을 최적화하여 UI와 데이터 간의 상호작용을 더욱 효율적으로 만들고, 특히 복잡한 상태 관리를 쉽게 처리할 수 있습니다.

각 패턴은 특정 상황에서 더 나은 선택이 될 수 있으며, 애플리케이션의 요구 사항에 따라 적절한 패턴을 선택하는 것이 중요합니다. 예를 들어, UI 테스트가 중요한 경우 MVP가 적합할 수 있으며, 데이터 바인딩이 중요한 경우 MVVM이 더 나은 선택이 될 수 있습니다.

결론

MVC 패턴은 유니티에서 게임을 개발할 때 코드의 구조를 명확하게 하고, 유지보수를 용이하게 하는 중요한 도구입니다. 이 패턴은 단순히 코드의 모듈화를 넘어서, 팀원 간의 협업을 더욱 효율적으로 만들어 줍니다. 각 역할이 명확하게 구분되어 있어, 여러 명의 개발자가 동시에 다른 부분을 작업할 수 있으며, 코드 변경이 필요한 경우에도 특정 부분만 수정하면 되기 때문에 다른 부분에 미치는 영향을 최소화할 수 있습니다.

물론, 작은 프로젝트에서는 MVC 패턴이 다소 과하게 느껴질 수 있습니다. 초기 설정과 구조화 과정이 다소 복잡할 수 있으며, 간단한 프로젝트에서는 오히려 불필요한 복잡성을 추가할 수 있습니다. 그러나 프로젝트의 규모가 커지고, 코드베이스가 복잡해질수록 MVC 패턴의 장점은 더욱 분명해집니다. 대규모 프로젝트에서의 유지보수와 확장성 측면에서, MVC 패턴은 복잡한 기능을 손쉽게 추가하고, 버그를 수정하며, 새로운 요구 사항에 맞추어 코드를 리팩토링하는 데 큰 도움을 줍니다.

지금까지 설명한 내용을 바탕으로 자신의 프로젝트에 MVC 패턴을 시도해 보시길 권장합니다. 처음에는 이 패턴이 다소 어렵게 느껴질 수 있지만, 패턴을 잘 이해하고 적용하기 시작하면 그 이점은 분명히 느낄 수 있을 것입니다. 더 나아가, 다른 디자인 패턴이나 아키텍처를 함께 학습하여, 프로젝트에 맞는 최적의 설계를 찾아가는 것도 좋은 방법입니다. 이는 궁극적으로 더 나은 소프트웨어를 개발하고, 코드의 품질을 높이는 데 기여할 것입니다.

따라서 MVC 패턴은 단순한 코드 구조화 방법을 넘어, 개발자의 능력을 한 단계 끌어올릴 수 있는 중요한 기법임을 명심하시기 바랍니다. 프로젝트의 성공과 지속 가능한 유지보수를 위해 MVC 패턴을 적극적으로 활용해 보세요.