유니티/개념 정리

실행 결과 보장

fore4022 2024. 10. 21. 03:36

실행 결과 보장이란 무엇인가? 우리가 만드는 모든 기능의 동작에 있어서, 그 결과가 항상 일관되도록 하는 것이다.

"여러분이 제공한 서비스가 10번 실행했을 때 1번꼴로 오류가 발생한다."

여러분이 위 예시의 서비스의 소비자라 하였을 때 해당 서비스의 만족도가 어떨 것 같은가? 대다수의 사람은 서비스에 대한 문제와 불만 사항을 제기할 것이다. 개발자의 입장에서 위의 예시를 살펴보자, 여기서 의문이 생길 수 있다.

"왜, 같은 코드와 환경에서 서로 다른 결과가 나올 수 있는 것인가?"

위의 의문과 같은 상황을 실행 결과가 보장되지 못한 상황이다. 그렇다면 어떻게 실행 결과를 보장하고, 보장되지 못하는 문제가 발생하는지 알아보자.


실행 결과 보장되지 않는 경우

  1. 무조건 특정 GameObject와 Component가 존재한다는 전제를 하고 작성한 스크립트를 사용하는 것으로 문제가 발생한다.
  2. 실행 순서의 차이로 인하여서 문제가 발생한다.
    using UnityEngine;
    public class A : MonoBehaviour
    {
    		private void Awake()
    		{
    				Rigidbody rigid = GetComponent<Rigidbody>();
    		}
    }
    
    using UnityEngine;
    public class B : MonoBehaviour
    {
    		private void Awake()
    		{
    				gameObject.AddComponent<Rigidbody>();
    		}
    }
    
    위의 Awake들 중에서 어떤 것이 먼저 실행될 것 같은가? 실행 해보았을 때의 결과는 어떤 것이 먼저 실행된다고 특정 할 수 없다.
  3. "GameObject 하나에 A와 B Component가 추가되어 있다."
  4. 초기화나 여러 번의 재사용으로 인한 값의 변화에 따라서 문제가 발생할 수 있다.
  5. "기존의 Rigidbody를 가져와야 할 GameObject가 아닌 다른 GameObject인 경우"

실행 결과 보장하기

사전 점검

특정 값이나 Component를 사용할 때 null값인 경우에 문제가 발생하지 않도록 해준다.

Rigidbody rigid;

private void Start()
{
		if(TryGetComponent<Rigidbody>(out Rigidbody rigid))
		{
				this.rigid = rigid;
		}
		else
		{
				rigid = gameObject.AddComponent<Rigidbody>();
		}
}//Rigidbody가 없더라도 추가해줘서, 동작에 문제가 생기지 않도록 한다.
Rigidbody rigid = GetComponent<Rigidbody>();

if(rigid == null)
{
		rigid = gameObject.AddComponent<Rigidbody>();
}

Debug.Log(rigid);
//Rigidbody가 없더라도 추가해줘서, 동작에 문제가 생기지 않도록 한다.

코루틴 대기

코루틴의 yield return new WaitUntil()을 통해서 값 초기화, Instance 생성 등 특정 동작의 완료를 기다리고, 이어지는 다음 동작을 안정적으로 수행할 수 있다.

private int value = 0;

private void Start()
{
		StartCoroutine(Example());
private void Update()
{
		value++;
}
private IEnumerator Example()
{
		yield return new WaitUntil(() => value > 100);
		
		Debug.Log("value > 100");
}//value가 100이 넘을 때 log를 출력해준다.

Call back

Call back 구조를 이용해서 값 초기화, Instance 생성 등 특정 동작의 수행 후 다음 동작을 수행한다.

private Action actionExample = null;

private GameObject example;

private void Start()
{
		actionExample += MethodB();
}
private void MethodA()
{
		for(int i = 0; i < 1000000; i++)
		{
				Instantiate(example);
		}
		
		actionExample?.Invoke();
}
private void MethodB()
{
		Debug.Log("Complete");
}//MethodA의 동작이 끝나고 MethodB가 호출되어 동작한다.

RequireComponent

RequireComponent를 통해서 객체에 사용하고자 하는 Component가 자동으로 추가되는 것으로, Component가 없어서 발생하는 문제를 없앨 수 있다. 이 스크립트가 어떤 스크립트를 필요로 하는지 쉽게 알 수 있다.

using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class Example : MonoBehaviour
{
		private void Awake()
		{
				Rigidbody rigid = GetComponent<Rigidbody>();
				
				Debug.Log(rigid);
		}
}//GameObject에 Example Component를 추가하면, Rigidbody가 자동으로 추가된다.

비동기 사용의 주의

처음 비동기를 사용하는 이들이라면, 꼭 사용에 앞서 아래와 같은 점을 명심하였으면 한다.

  • 비동기로 정보를 불러온다면, 정보를 전부 불러오기 이전에 해당 정보에 접근하지 않도록 한다.
  • 비동기 작업이 끝나는 시간은 일정하지 않다.

실행 결과의 보장을 신경 써서 개발한다면, 예기치 못한 오류나 목표한 수행 결과와 다른 결과가 나오지 않는, 더욱 안정적인 서비스를 제공할 수 있을 것이다.