개발을 하다보면 특별한 규칙없이 그냥 되는대로 구현할 때가 가끔 있다. 하지만 협업을 위해서, 또는 알아보기
쉽고 효율적으로 코드를 짜려면 반드시 디자인패턴을 적용해서 코드를 작성해야 한다.
그 중 하나인 MVC 패턴(Model - Veiw - Controller) 이라는 것을 알아보도록 하자.
MVC 패턴에서 View의 역할은 말그대로 Viewer, 즉 사용자에게 보여지는 UI의 역할이다. 보여주기도 하고 또한 입력을 받아서 Controller에 그 데이터를 전달해준다. Controller는 데이터를 View로부터 전달받아서 Model이라는 데이터 원본의 값을 업데이트한다. Model의 변화를 View가 관찰하여(옵저버 패턴) 업데이트 한다!
이것이 MVC의 가장 간단한 설명이였다. 이 흐름도만 이해하면 코드를 구성할 때 어느정도의 디자인패턴을 구성할 수가 있다.
이 MVC에서 특징적인 부분은 Model과 View가 엄청 밀접하게 관계가 있다는 것이다. 보통 코드를 짤 때는 데이터는 한방향으로 흘러갔다가 다시 한방향으로 나오는 것이 좋다고 알고 있다. 즉, 사용자가 UI에 입력을 하면 UI는 그 데이터를 중간다리에 역할하는 곳에 전달하고 거기에서 데이터를 처리하고 처리된 데이터는 다시 들어왔던 곳인 UI로 역수출해서 데이터가 출력되는 흐름이 좋은 흐름이다.
이는 협업을 위해서나 나중에 버그를 대처하기 위해서 좋은 방법이다. 하지만 이 MVC패턴에서는 View -> Controller -> Model 까지는 데이터의 입력이 잘 되었으나 Model -> Controller -> View의 순으로 데이터가 나오지 않고(이것은 추후에 작성하게 될 MVP패턴이다) 즉, Model이 Controller를 통해서 View로 데이터를 내보내지 않고 Model -> View로 직접 데이터를 내보내고 있다. 이것이 MVC의 특징으로 앞서 말한 Model과 View과 밀접하게 관계되있다고 말한부분이 이것이다.
사실 이 부분을 좀 더 정확하게 말하자면 View는 Model의 변화를 감지하고 있다가(옵저버) Model이 변화하기만 하면 View가 자신의 UI를 업데이트하는 형식이다.
그런데 위 그림을 보면 뭔가 이상하다는 것을 눈치챘을 것이다. 화살표의 방향이 View -> Model이 아니라 Model -> View로 되어있다. 즉, 이는 참조 관계가 아니라 작업의 흐름 혹은 데이터의 흐름 순서도로 보아야 한다.
그렇다면 이제 코드로 들어가서 어떻게 구현하는지 알아보자.
1. 프로퍼티를 사용한 Model 구현
private int hp;
// 메서드 사용
public int GetHP()
{
return this.hp
}
public void SetHP(int hp)
{
this.hp = hp;
}
// Property 사용
public int Hp
{
get { return this.hp; }
set { this.hp = value; }
}
Model 코드는 크게 뭐 없다. 데이터의 원본을 저장하는 것이기 때문에 게임에 사용할 데이터를 변수로 가지고 있기만 하면 된다. 하지만 나는 여기서 Get/Set 함수를 구현하였다. 이는 추후에 프로퍼티 Set함수에 델리게이트를 적용하여 View에 추가하고 View가 Model을 관찰하는 패턴을 만들기 위해서 프로퍼티를 추가하였다.
2. View 구현
private UIView ui;
public int Hp
{
get { return this.hp; }
set
{
this.hp = value;
ui.UpdateHpUI(this.hp);
}
}
View의 기본 구성도 크게 뭐가 없다.. 표시하고자 하는 하이어라키의 ui를 업데이트를 해주는 코드를 작성해 보았다.
3. delegate
아까 View와 Model의 관계는 View가 Model의 변화를 관찰해서 ui요소에 반영한다고 하였다. 이것을 적용하기 위해 코드에 Callback을 적용시켜서 다시 구현해보자.
3-1 Model
using UnityEngine;
using System;
[CreateAssetMenu(fileName = "PlayerData", menuName = "Data/PlayerData")]
public class PlayerData : ScriptableObject
{
private int hp;
public Action<int> onChangeHp;
public int Hp
{
set
{
this.hp = value;
onChangeHp?.Invoke(value);
}
}
}
3-2 View
using UnityEngine;
public class UIView : MonoBehaviour
{
public PlayerData playerData;
private void OnEnable() // 할당
{
playerData.onChangeHp += UpdateHpUI;
}
private void OnDisable()
{
// Scene 변경될 때 오브젝트가 유지되는 상태가 아니라면 이 오브젝트의 메서드를 사용해서는 안되므로 해제.
playerData.onChangeHp -= UpdateHpUI;
}
private void UpdateHpUI(int hp)
{
// DO SOMETHING
}
}
Model 원본이 가지고 있는 콜백 변수에 View의 UpdateHpUI를 등록하여 Model의 Hp가 변화할 때 마다 UpdateHpUI 함수가 발동하여 ui를 업데이트해준다. 이렇게 코드로 보면 Model과 View의 관계가 더욱 확실히 이해될 것이다.
4. Controller
public PlayerData playerData;
public void Foobar()
{
Debug.Log(playerData.hp.Value); // 참조
playerData.hp.Value = 30; // 변경
}
Controller는 View로 부터 데이터의 입력을 전달받아 데이터를 가공하여 Model의 원본 데이터를 업데이트 해준다.
https://luv-n-interest.tistory.com/1377
Unity 에서의 MVC 패턴
0.Model의 역할 게임을 이루는 데이터를 담당한다. Business Logic을 담당한다. ** 즉, Data가 바뀌는 Logic은 Model에서 짜야 한다. 유저 입력을 바탕으로 Controller에서 계산해서 집어넣는다? => X 그러면 안
luv-n-interest.tistory.com
[Unity] 유지보수성과 성능을 모두 챙기는 방법 - 데이터 변경에 따른 콜백과 MVC 분리
0. 개요 데이터가 변경됨에 따라 특정 작업을 수행해야 하는 경우가 있습니다. 예를 들면 HP 값이 변경될 때, 동시에 UI를 갱신해야 하는 경우가 있을 텐데, 이 때 UI를 관리하는 클래스에서 Update()
saens.tistory.com
'게임개발 > 스터디' 카테고리의 다른 글
[스터디] MVMM 패턴에 대한 설명과 사용법 (1) | 2024.04.10 |
---|---|
[스터디] MVP 패턴에 대한 설명과 사용법 (0) | 2024.04.09 |
[스터디] 데이터 로드 & 저장 방법 (Json형식 사용) (0) | 2024.04.09 |
[스터디] 스크립터블 오브젝트란 (0) | 2024.03.31 |
[스터디] 유니티 메모리 변조 방지 - Anti Cheat (0) | 2024.03.31 |