코드 검증이 필요한 상황
- 코드의 의도 명확화
- 버그를 조기에 발견
- 코드의 안정성이 향상
- 문서화 효과
코드 검증 메시지 작성 팁
- 구체적이고 명확한 메시지 사용
- 문제의 원인과 위치 포함
실행 시점에 따른 검증 위치
- Awake/Start 메소드에서 컴포넌트 검증
- 메소드 시작 부분에서 매개변수 검증
- 상태 변경 직후 검증
검증 사용을 피해야 할 경우
- 실제 게임 로직의 일부로 사용
- 외부 입력 검증 (대신 일반적인 유효성 검사 사용)
- 성능에 민감한 부분
싱글톤 참조 검증
public void ProcessGameLogic()
{
Debug.Assert(GameManager.Instance != null, "GameManager instance is missing!");
GameManager.Instance.UpdateGame();
}
직렬화 객체 검증
public class EnemySpawner : MonoBehaviour
{
[SerializeField] private GameObject _enemyPrefab;
private void Start()
{
Debug.Assert(_enemyPrefab != null, "Enemy prefab not assigned in inspector!");
}
}
컴포넌트 참조 검증
public class PlayerController : MonoBehaviour
{
private Rigidbody _rigidbody;
private void Awake()
{
_rigidbody = GetComponent<Rigidbody>();
Debug.Assert(_rigidbody != null, "PlayerController requires Rigidbody component!");
}
}
메소드 매개변수 검증
public void Initialize(Player owner)
{
Debug.Assert(owner != null, "WeaponSystem initialized with null owner!");
_owner = owner;
}
동적 생성 객체 검증
public UIPanel CreatePanel(string panelName)
{
GameObject panelObject = Instantiate(Resources.Load<GameObject>($"UI/{panelName}"));
UIPanel panel = panelObject.GetComponent<UIPanel>();
Debug.Assert(panel != null, $"Created UI panel '{panelName}' missing UIPanel component!");
return panel;
}
리소스 로딩 검증
public GameObject LoadPrefab(string path)
{
GameObject prefab = Resources.Load<GameObject>(path);
Debug.Assert(prefab != null, $"프리팹을 찾을 수 없습니다: {path}");
return prefab;
}
열거형 범위 검증
public void HandleWeaponType(WeaponType weaponType)
{
Debug.Assert(Enum.IsDefined(typeof(WeaponType), weaponType),
$"Invalid weapon type: {weaponType}");
}
컬렉션 인덱스 검증
public Item GetItem(int index)
{
Debug.Assert(index >= 0 && index < _items.Length,
$"Inventory index out of range: {index}");
return _items[index];
}
상태 전이 검증
public void TransitionTo(GameState newState)
{
Debug.Assert(_currentState != newState,
$"Attempting to transition to the same state: {newState}");
}
예시 템플릿: 검증을 활용한 코드 작성 예시
public class BestPracticeExample : MonoBehaviour
{
[SerializeField] private GameObject _prefab;
private Rigidbody _rigidbody;
private void Awake()
{
// 1. 직렬화 객체 검증
Debug.Assert(_prefab != null, $"{nameof(_prefab)} not assigned in {nameof(BestPracticeExample)}!");
// 2. Required 컴포넌트 검증
_rigidbody = GetComponent<Rigidbody>();
Debug.Assert(_rigidbody != null, $"{nameof(BestPracticeExample)} requires Rigidbody component!");
}
public void Initialize(GameManager gameManager, int initialValue)
{
// 3. 싱글톤 및 매개변수 검증
Debug.Assert(gameManager != null, $"{nameof(Initialize)} called with null {nameof(gameManager)}!");
Debug.Assert(initialValue >= 0, $"{nameof(initialValue)} must be non-negative, got {initialValue}!");
}
private GameObject CreateObject(string objectName, ObjectType type)
{
// 4. 열거형 및 동적 생성 검증
Debug.Assert(!string.IsNullOrEmpty(objectName), "CreateObject called with null or empty name!");
Debug.Assert(Enum.IsDefined(typeof(ObjectType), type), $"Invalid ObjectType: {type}");
GameObject newObject = Instantiate(_prefab);
Debug.Assert(newObject != null, $"Failed to instantiate object of type {type}!");
return newObject;
}
}
리팩토링 예시
리팩토링 전
public class BeforeRefactor
{
public void ProcessPlayer(Player player)
{
if (player == null) return;
if (player.Weapon == null) return;
if (player.Health <= 0) return;
// 로직...
}
}
리팩토링 후
public class AfterRefactor
{
public void ProcessPlayer(Player player)
{
Debug.Assert(player != null, "ProcessPlayer called with null player!");
Debug.Assert(player.Weapon != null, "Player weapon is null in ProcessPlayer!");
Debug.Assert(player.Health > 0, "ProcessPlayer called with dead player!");
// 로직...
}
}