오늘은 out 키워드와 이벤트 시스템에 대하여 정리해보겠습니다.
out 키워드는 메소드에서 두 개 이상의 값을 반환하고 싶을 때 사용하는 매개변수 수식어입니다. 일반적인 return은 하나의 값만 돌려줄 수 있지만, out을 사용하면 호출한 곳으로 여러 데이터를 전달할 수 있습니다.
out 파라미터는 초기화를 필수적으로 진행해야 합니다.
함수를 호출하는 쪽에서는 변수를 미리 초기화할 필요가 없지만, 함수 내부에서는 함수가 종료되기 전(return 전)에 반드시 해당 변수에 값을 할당해야 합니다.
이는 값이 비어 있는 상태로 반환되어 발생할 수 있는 오류를 컴파일 단계에서 방지해 줍니다.
<style>
using UnityEngine;
using System.Collections.Generic;
public class ItemCatalog { public string name; }
public class CatalogManager : MonoBehaviour
{
private Dictionary<string, ItemCatalog> catalogById = new Dictionary<string, ItemCatalog>();
// out을 사용하여 성공 여부(bool)와 찾은 데이터(entry)를 동시에 반환
public bool TryGetCatalogEntry(string itemId, out ItemCatalog entry)
{
// 1. 함수 종료 전 반드시 초기값 할당 (필수)
entry = default;
if (string.IsNullOrWhiteSpace(itemId))
{
return false;
}
// 2. Dictionary의 TryGetValue도 내부적으로 out을 사용함
return catalogById.TryGetValue(itemId.Trim(), out entry);
}
void Start()
{
// 사용 예시
if (TryGetCatalogEntry("sword_01", out ItemCatalog myItem))
{
Debug.Log("아이템 발견: " + myItem.name);
}
}
}
</style>
발행-구독(Publish-Subscribe) 패턴은 객체들이 서로의 존재를 자세히 몰라도 메시지를 주고받을 수 있게 하는 디자인 패턴입니다. 객체 간의 결합도를 낮추어 유지보수를 쉽게 만듭니다.
주요 구성 요소는 다음과 같습니다.
Publisher (발행자): 이벤트가 발생했음을 알리는 주체입니다. 이때 누가 자신을 구독하는지와 관계없이 메세지를 발행합니다.
Subscriber (구독자): 특정 이벤트가 발생했을 때 행동하는 주체입니다. 발행자에게 "신호가 오면 알려달라"고 등록합니다.
Handler (처리기): 이벤트가 발생했을 때 실제로 실행되는 구체적인 함수 로직입니다.
구독할 때는 += 연산자를 사용하고, 더 이상 신호를 받을 필요가 없거나 객체가 파괴될 때는 반드시 -= 연산자로 구독을 취소해야 합니다.
구독 취소를 잊으면 메모리 누수나 이미 파괴된 객체를 참조하는 에러가 발생할 수 있습니다.
<style>
using UnityEngine;
using System;
public class PlayerHealth : MonoBehaviour
{
// 1. Publisher: 이벤트 정의
public event Action OnPlayerDead;
public void Die()
{
Debug.Log("플레이어 사망");
// 이벤트 알림 (발행)
OnPlayerDead?.Invoke();
}
}
public class UIManager : MonoBehaviour
{
public PlayerHealth player;
private void OnEnable()
{
// 2. Subscriber: 구독하기 (Handler 연결)
player.OnPlayerDead += ShowGameOverUI;
}
private void OnDisable()
{
// 3. 구독 취소하기 (중요!)
player.OnPlayerDead -= ShowGameOverUI;
}
// 4. Handler: 실행될 로직
private void ShowGameOverUI()
{
Debug.Log("게임 오버 화면을 띄웁니다.");
}
}
</style>
IReadOnlyList는 리스트의 데이터를 읽기 전용으로만 제공하는 인터페이스입니다.
인덱스 접근([index])과 데이터 개수 확인(Count)은 가능하지만, 데이터를 추가(Add)하거나 삭제(Remove)하는 수정 기능은 포함되어 있지 않습니다.
IReadOnlyList를 사용하는 이유는 캡슐화 때문입니다.
클래스 내부에서 관리하는 리스트를 외부에 그대로 공개하면, 외부 객체가 마음대로 데이터를 조작하여 시스템이 망가질 수 있습니다.
IReadOnlyList를 통해 반환하면 외부에서는 "보기"만 가능하고 "수정"은 내부 클래스만 할 수 있게 되어 데이터의 무결성을 지킬 수 있습니다.
<style>
using UnityEngine;
using System.Collections.Generic;
public class Inventory : MonoBehaviour
{
// 내부에서는 자유롭게 수정 가능한 List 사용
private List<string> items = new List<string>();
// 외부에는 읽기 전용인 IReadOnlyList로 공개
public IReadOnlyList<string> Items => items;
void Start()
{
items.Add("강철 검");
items.Add("나무 방패");
}
}
public class InventoryViewer : MonoBehaviour
{
public Inventory inventory;
void Display()
{
// 읽기는 가능
Debug.Log("아이템 개수: " + inventory.Items.Count);
Debug.Log("첫 번째 아이템: " + inventory.Items[0]);
// 수정은 불가능 (컴파일 에러 발생)
// inventory.Items.Add("사기 아이템");
}
}
</style>