실행 결과 보장
실행 결과 보장이란 무엇인가? 우리가 만드는 모든 기능의 동작에 있어서, 그 결과가 항상 일관되도록 하는 것이다.
"여러분이 제공한 서비스가 10번 실행했을 때 1번꼴로 오류가 발생한다."
여러분이 위 예시의 서비스의 소비자라 하였을 때 해당 서비스의 만족도가 어떨 것 같은가? 대다수의 사람은 서비스에 대한 문제와 불만 사항을 제기할 것이다. 개발자의 입장에서 위의 예시를 살펴보자, 여기서 의문이 생길 수 있다.
"왜, 같은 코드와 환경에서 서로 다른 결과가 나올 수 있는 것인가?"
위의 의문과 같은 상황을 실행 결과가 보장되지 못한 상황이다. 그렇다면 어떻게 실행 결과를 보장하고, 보장되지 못하는 문제가 발생하는지 알아보자.
실행 결과 보장되지 않는 경우
- 무조건 특정 GameObject와 Component가 존재한다는 전제를 하고 작성한 스크립트를 사용하는 것으로 문제가 발생한다.
- 실행 순서의 차이로 인하여서 문제가 발생한다.
using UnityEngine; public class A : MonoBehaviour { private void Awake() { Rigidbody rigid = GetComponent<Rigidbody>(); } }
위의 Awake들 중에서 어떤 것이 먼저 실행될 것 같은가? 실행 해보았을 때의 결과는 어떤 것이 먼저 실행된다고 특정 할 수 없다.using UnityEngine; public class B : MonoBehaviour { private void Awake() { gameObject.AddComponent<Rigidbody>(); } }
- "GameObject 하나에 A와 B Component가 추가되어 있다."
- 초기화나 여러 번의 재사용으로 인한 값의 변화에 따라서 문제가 발생할 수 있다.
- "기존의 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가 자동으로 추가된다.
비동기 사용의 주의
처음 비동기를 사용하는 이들이라면, 꼭 사용에 앞서 아래와 같은 점을 명심하였으면 한다.
- 비동기로 정보를 불러온다면, 정보를 전부 불러오기 이전에 해당 정보에 접근하지 않도록 한다.
- 비동기 작업이 끝나는 시간은 일정하지 않다.
실행 결과의 보장을 신경 써서 개발한다면, 예기치 못한 오류나 목표한 수행 결과와 다른 결과가 나오지 않는, 더욱 안정적인 서비스를 제공할 수 있을 것이다.