본문 바로가기
Study

Unity, Coroutine과 Invoke 차이

by Client. DJ 2023. 11. 10.
반응형

Coroutine과 Invoke는 무슨 차이점이 있는걸까?

유니티를 사용하다보면 인보크와 코루틴의 차이점이 무엇인지에 대해 의문을 품은 적이 있으실겁니다. 이 둘은 마치 싱글 쓰레드를 사용한다는 전설을 가지고 있는 유니티에서 마치 멀티 쓰레드를 사용하는 것처럼 편하게 사용할 수 있습니다.

 

여담으로 유니티는 싱글 쓰레드만 사용하지 않습니다. 이러한 처리들을 위해서 여러 백그라운드 워커쓰레드가 함께 동작하고 있으며, 이는 프로파일러를 통해서 확인이 가능합니다. (최근 회사에 유니티 코리아 세미나가 있었는데, 절대 싱글 쓰레드만 사용하지 않는다고 강조를 하더군요.)

 

실제로 프로젝트 작업을 하다보면 이 둘에 대해 구분 없이 쓰는 분도 있으며 대게는 어떻게 관리가 이루어지는지에 대해서 모르는 시는 분들이 생각보다 꽤 있습니다. 이번 글을 통해 사용방법과 특징을 알려드리겠습니다.

Coroutine(코루틴)

특징

  • C# 기반의 반복기(enumerator)를 사용하는 개념이다. (IEnumerator로 만들어진 함수를 사용한다.)
  • yield return을 호출하여 프레임 단위, 시간 단위, 특정 조건이 만족이 될 때 등으로 대기가 가능하다.
  • StartCoroutine()으로 실행 가능하며, Coroutine이라는 변수를 선언해 실행한 코루틴을 담을 수 있다. ex) Coroutine co = StartCoroutine("함수 이름");
  • 코루틴이 호출된 게임오브젝트(또는 호출한 컴포넌트)가 활성화 상태일 때만 실행 가능하다.
  • 반대로 코루틴이 정상적으로 실행된 상태에서 게임오브젝트(또는 호출한 컴포넌트)가 비활성화되면 중단되며, StopCoroutine()을 통하여 중지 가능하다.

대체적으로 코루틴은 OnEnable()에서 호출하는 방식으로 주로 사용됩니다. 반복이되는 함수여도(예를 들어 현재 시간을 찍는 UI) 해당 오브젝트가 비활성화되면 자동으로 중지되기 때문에 관리 측면에서는 매우 편합니다.

예제

현재 시간을 출력하는 예제입니다.

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class TimerUI : MonoBehaviour
{
    public Text timerText;

    Coroutine timerCoroutine = null;

    private void OnEnable()
    {
        // 타이머 코루틴을 호출한다.
        PlayTimer();
    }
    
    private void PlayTimer()
    {
        if (timerCoroutine != null) StopCoroutine(timerCoroutine);
        timerCoroutine = StartCoroutine(Timer());
    }
    
    private IEnumerator Timer()
    {
        // 1초마다 현재 시간을 UI에 출력한다.
        while (true)
        {
            timerText.text = DateTime.Now.ToString();
            yield return new WaitForSeconds(1f);
        }
    }
}

Invoke(인보크)

특징

  • 일반적으로 만들어진 void 함수를 Invoke("함수 이름", "시작 딜레이")로 호출이 가능하다.
  • InvokeRepeating("함수 이름", "시작 딜레이", "반복 간격")를 통해 같은 함수를 반복 호출 가능하다.
  • Invoke는 게임오브젝트(또는 호출한 컴포넌트)의 상태와 무관하게 작동된다.
  • 자동으로 중지되는 타이밍은 게임오브젝트의 파괴되는 시점이며, CancelInvoke("함수 이름")을 통해서 직접 중지할 수도 있다.

인보크는 게임오브젝트 상태와 상관 없이 언제든지 호출할 수 있으며, 반대로 게임오브젝트 파괴가 아닌 이상 자동으로 중지가 되지 않습니다. 자칫 잘못하면 게임이 종료될 때까지 미쳐 중지 못 한 오브젝트들의 함수가 작동하여 문제를 만들 수 있습니다. 잘못된 사용으로 인해 중첩으로 여러번 호출하는 경우도 대게 있습니다. 프로젝트를 같이 진행하는 중에 다른 분들이 작성한 코드로 인해 문제가 됐던 적이 꽤있었습니다.

예제 - Invoke

현재 시간을 한 번 출력하는 예제입니다.

using System;
using UnityEngine;
using UnityEngine.UI;

public class TimerUI : MonoBehaviour
{
    public Text timerText;

    private void OnEnable()
    {
        // 5초 뒤에 Timer 함수를 실행하여 현재 시간을 한 번 표시한다.
        Invoke("Timer", 5f);
    }

    private void OnDisable()
    {
        // 실행 중인 Invoke를 취소한다. (딜레이 시간 5초보다 먼저 종료가 필요한 경우에도 해당)
        CancelInvoke("Timer");
    }

    private void Timer()
    {
        timerText.text = DateTime.Now.ToString();
    }
}

예제 - InvokeRepeating

현재 시간을 출력하는 예제입니다.

using System;
using UnityEngine;
using UnityEngine.UI;

public class TimerUI : MonoBehaviour
{
    public Text timerText;

    private void OnEnable()
    {
        // 5초 뒤에 Timer 함수를 실행하고, 1초마다 반복 실행하여 실시간을 표시해준다.
        InvokeRepeating("Timer", 5f, 1f);
    }

    private void OnDisable()
    {
        // 실행 중인 InvokeRepeating을 취소한다.
        CancelInvoke("Timer");
    }

    private void Timer()
    {
        timerText.text = DateTime.Now.ToString();
    }
}

마무리

둘의 차이는 어느 시점에 중지가 되는지, 어떻게 활용해야 좀 더 용이한지 차이입니다. 사실 근본적으로는 멀티 쓰레딩처럼 사용하고자에 의미가 있습니다. 개인적으로는 코루틴 사용을 좀 더 지향합니다. 앞서 설명했듯이 Invoke(인보크)의 경우 자칫 잘못하면 중지 타이밍을 놓칠 수 있어, 메모리에 누적이 되어서 문제가 될 수도 있고, 실제로도 잘모르고 사용하여 코루틴처럼 사용하거나 실수로 중지 하지 않아서 문제가 되는 케이스를 많이 보았기도 했습니다. 반면 Coroutine(코루틴)은 타이밍 조절도 명확하고 쉬우며(프레임, 시간, 조건) 오브젝트가 잠시 사용되지 않는 비활성화 상태에서 자동 중지가 되니 관리 측면에서나 가독성 측면에서도 좋아서 보다 더 유리하다고 생각합니다.


관련글

반응형

댓글