Drag(UI), MVC, MVP

감사콩·2025년 11월 10일

유니티

목록 보기
15/29

서론


어느 게임에서나 흔히 볼 수 있는 Drag & Drop.
이 기능의 원리를 알아보고 Unity 상에서의 구현 과정을 알아보겠다.

이후엔 디자인패턴 중 하나인 MVC에 대해 예시와 함께 알아보고
MVC 패턴과 흡사한 MVP 패턴도 같이 알아보겠다.


TIL 요약


  1. Drag 는 시작 // ing // 종료 3step으로 구성되며, 스텝에 따라 필요한 기능을 부여
  2. Drop 시 위치가 올바른 지, 해당 위치에 놓이면 어떤 행동을 수행하는 지 정의해둬야 함.
  3. MVC 패턴은 설계를 3가지 핵심 구성요소로 나누는 디자인 패턴.
  4. MVP는 MVC와 흡사하지만 Presenter라는 중개자가 컨트롤러를 대신하여
    View의 의존성 해결, 테스트 용이 등의 장점이 있음.

Drag


시작 // ing // 종료

드래그는 이 3Step으로 구성된다.
각 Step마다 필요한 기능이 무엇이 있는 지를 스크립트를 작성하며 알아보겠다.


DragableImage


드래그가 가능하게 할 이미지에 부착할 스크립트.
해당 스크립트를 통해 Drag & Drop 중 Drag 기능을 구현

순서대로 진행해보자.


1. using

UI, EventSystem 추가.


2. 인터페이스, 필드 지정

인터페이스 3가지를 지정하여 3Step을 구현하도록 제작
IBeginDragHandler, IDragHandler, IEndDragHandler

원하는 위치로의 드래그 실패 시, 돌아갈 정보를 저장하기 위해 부모패널의 트랜스폼값 저장
이미지의 투명화, blocksRaycasts 설정을 위해 CanvasGroup을 AddComponent


3. OnBeginDrag

드래깅 시작 시, 필요한 기능을 모아둔 메서드

인자값은 PointerEventData
이 내부에 드래깅에 필요한 모든 정보가 들어 있음.

  1. 원복을 위해 기존 부모 객체 transform 기억
  2. 캔버스 내의 원활한 이동을 위해 최상위 계층으로 이동
  3. 드래깅 중인 이미지를 반투명화(canvasGroup의 alpha 값 조절)
  4. 드롭 영역 감지가 원활하도록 레이캐스트 블로킹 false

4. OnDrag

드래깅 도중, 필요한 기능을 모아둔 메서드

마찬가지로 인자값 PointerEventData
(마우스)포인터의 위치로 포지션값을 변경

5.OnEndDrag

드래깅 마무리 시, 필요한 기능을 모아둔 메서드

  1. 투명도, 레이케스트 블라킹 원복
  2. 드랍이 감지되지 않았을 경우, 즉 올바른 드랍 위치가 아닐 경우? 원래 부모객체로 원복

드래그 할 이미지에 부착할 스크립트 작성은 이걸로 마무리.



DropZone


드랍이 가능한 패널에 부착할 스크립트.
해당 스크립트를 통해 Drag & Drop 중 Drop 기능을 구현

여기도 IDropHandler 라는 인터페이스를 부착.

드래그 중이던 대상의 부모를 해당 패널로 변경하는 메서드 제작.



결과


패널A의 자식객체인 이미지를
패널B로 옮겨보겠다

우측 상단의 하이어라키를 보면 패널 A에서 상위 객체인 Canvas로 이동되는 모습
투명도 등의 설정도 전부 적용되었다.

이후 image 필드를 통해
드래그 도중엔 크기를 작게 만드는 등의 기능을 넣어보는 것도 좋을 듯.

기본적인 Drag & Drop은 구현했으니
이제 서론 아래의 움짤인 드래그하여 객체 생성 구현으로 넘어가보겠다.


드래그하여 객체 생성


마우스의 위치값을 받아와
해당 위치의 충돌 정보를 받아와 Instantiate

이 함수는 OnEndDrag 메서드에 추가하고
메서드 실행 이후엔 기존 이미지 오브젝트는 파괴하도록 구현하여 완료



MVC (Moder View Controller)


설계를 3가지 핵심 구성요소로 나누는 디자인 패턴.

  • Model: 데이터와 로직 담당 (플레이어 체력, 경험치, 점수 등의 정보)
  • View: 표시 담당 (정보를 출력)
  • Controller: 입력 및 로직 연결 담당 (입력을 받아 모델을 변경)

언제 고려해야 할 지?


  • UI, 입력, 데이터가 뒤섞여 코드가 복잡해질 경우
  • 협업 간 UI담당, 로직 담당 등을 나눌 경우
  • 게임 내 정보(HP, Gold, Inven) UI가 여러 곳에서 표시되어야 할 때

MVC 패턴을 활용하여 Player의 Hp가 줄어들고 늘어나는 기능을 제작해보겠다.


M: PlayerModel


핵심 3요소 중, 데이터와 로직을 담당할 Model 스크립트를 만들어보겠다.


  • 오브젝트에 부착할 스크립트가 아니므로 Monobehavior X.
  • health 변수에 접근할 수 있게 하는 읽기 전용 프로퍼티 제작.
    외부에서 현재 체력 값을 확인할 수는 있지만, 직접 변경할 수는 없게 하여 데이터 무결성 보호.
private int health = 100;
public int Health => health;
  • 체력 변동을 알리는 이벤트 액션 델리게이트 선언.
  • 체력의 증감을 수행하는 TakeDamage, Healing 메서드 제작.

다음은 MVC 중 V, View 스크립트를 작성하겠다.


V: PlayerView


정보의 출력을 담당할 View 스크립트.

  • 출력될 Text 오브젝트를 연결하기 위해 직렬화
  • 해당 오브젝트에 출력을 명령할 UpdateHealthUI를 제작(인자값 현재 체력)

마지막으로 C, Controller 스크립트를 작성하겠다.


C: PlayerController


입력을 감지하여 데이터(Model)를 변경하고
화면 표시(View)와 데이터(Model)를 서로 연결하는 역할.

이건 코드 내용이 좀 길고, 내가 추후에 까먹더라도 다시 기억하기 위해
하나하나 풀어서 적어두겠다.

1. 필드

    [SerializeField] private PlayerView _playerView;
    private PlayerModel _playerModel;
    private InputAction _damageAction;
    private InputAction _healingAction;
  1. _playerView: View 역할을 하는 컴포넌트의 참조, View 오브젝트를 연결하도록 직렬화.
  2. _playerModel: Model 역할을 하는 PlayerModel 클래스의 참조
  3. _damageAction, _healingAction: InputSystem에 정의된 액션들을 저장할 필드.

2. Awake

  1. PlayerModel 인스턴스 생성 및 초기화
  2. InputSystem 액션 맵에 정의된 어택과 힐을 가져와 선언.

3. Start ★★

여기가 가장 중요

  1. Model과 View를 연결하는 코드

_playerModel.OnHealthChanged += _playerView.UpdateHealthUI;
_playerModel 에서 체력이 변경될 때마다 발생하는 이벤트에
_playerView 의 체력 업데이트 메서드를 구독시켜줌

이로써 Controller는 데이터(Model)와 UI(View)의 연결을 관리하는 역할만 하고
서로를 직접 호출하지 않는 느슨한 결합 상태가 됨.

  1. 시작 시 초기 체력 View에 전달.

4. OnEnable & OnDisable


스크립트가 활성화될 때 입력 액션을 활성화하고
입력이 발생했을 때 호출될 메서드를 등록.
입력이 수행(performed) 되었을 때, 각 메서드를 호출하도록 연결


구독 해제~

5. 메서드

해당 메서드를 수행하여 모델의 메서드를 실행하여 데이터를 변화시킴.

스크립트 작성이 완료되었으니
체력 변화를 테스트해보겠다.

테스트


체력 회복, 감소 정상 작동

결론


PlayerController 는 중개자 역할을 수행하여 사용자의 입력을 받아
PlayerModel 의 데이터를 수정, PlayerView와 연결된 이벤트를 통해
PlayerView 를 업데이트



MVP


MVC랑 비슷하지만 다른 디자인 패턴

  • Model: 데이터와 로직 담당 (플레이어 체력, 경험치, 점수 등의 정보)
  • View: 표시 담당 (정보를 출력)
  • Presenter: 중개자 역할

1. 방식


  1. View는 사용자 입력을 감지하면 Presenter에게 알리고
  2. Presenter가 로직을 처리하여 Model을 변경
  3. Presenter는 View에게 "이 텍스트를 이걸로 바꿔라"와 같은 명령을 보내
  4. View가 수동적으로 UI를 업데이트 하도록 만듦

2. 장점


View와 Model은 서로를 알 필요가 없으며, Presenter를 통해서만 간접적으로 소통하게 됨.
코드의 결합도를 낮추고 유지보수성 및 테스트 용이성을 극대화

View는 데이터(Model)를 알 필요 없이 Presenter의 명령만 따르면 되고
Presenter는 실제 View 대신 Mock View를 사용하여 테스트 가능

3. MVC와의 차이점


View의 의존성 해결

MVC: View는 Model을 직접 구독하여 데이터를 가져옴.
MVP: View는 Presenter의 인터페이스를 참조.

Presenter의 중계

Presenter가 Model의 변경 이벤트를 구독
이 이벤트가 발생하면 View의 메서드를 직접 호출하여 View를 업데이트



마무리


간단하지만 방식을 모르면 만들 수 없거나 어려운 게 내 생각보다 많다.
여러 예시 코드를 보는 습관을 들이자.

또한 MVC를 사용할 때의 간단한 구현 등의 메리트보단

개인적으론 Model 의존성을 해결하고 테스트에 용이한 MVP 쪽을 더 사용하게 될 것 같다.

profile
안녕하시와요

1개의 댓글

comment-user-thumbnail
2025년 11월 10일

TIL의 요약 파트가 그냥 목차 같습니다. 큰 주제로 묶어서 간단한 설명을 추가해 주세요.

답글 달기