UniTask

2025. 4. 21. 21:16ใ†[Unity] Game Programing

 

๐Ÿ“˜ TIL - Unity ๋น„๋™๊ธฐ ์ž‘์—… ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ UniTask ํ•™์Šต

๋‚ ์งœ: 2025๋…„ 4์›” 21์ผ

๐Ÿงญ UniTask๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

UniTask๋Š” Cysharp์—์„œ ๊ฐœ๋ฐœํ•œ Unity ์ „์šฉ ๊ฒฝ๋Ÿ‰ ๋น„๋™๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค. ๊ธฐ๋ณธ .NET์˜ Task ๊ตฌ์กฐ๋ณด๋‹ค ๋” ๊ฐ€๋ณ๊ณ , Garbage-Free์— ๊ฐ€๊นŒ์šด ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•˜์—ฌ Unity์—์„œ์˜ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ค„์ธ๋‹ค.

Unity๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ async/await๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, System.Threading.Tasks.Task ๊ธฐ๋ฐ˜์˜ ๊ตฌ์กฐ๋Š” GC ๋น„์šฉ์ด ํฌ๊ณ , Unity์˜ ํ”„๋ ˆ์ž„ ์‚ฌ์ดํด๊ณผ ์™„๋ฒฝํžˆ ์–ด์šธ๋ฆฌ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— UniTask๊ฐ€ ๋„์ž…๋˜์—ˆ๋‹ค.

๐Ÿ”ง ์ฃผ์š” ํŠน์ง•

  • Unity 2019.4 ์ด์ƒ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • Task๋ณด๋‹ค ํ›จ์”ฌ ์ ์€ GC ๋น„์šฉ
  • Unity์˜ YieldInstruction (WaitForSeconds, WaitUntil ๋“ฑ)์„ ์™„๋ฒฝํžˆ ๋Œ€์ฒด
  • ํƒ€์ž„์Šค์ผ€์ผ ๋ฌด์‹œ ์—ฌ๋ถ€ ๋“ฑ ์œ ์—ฐํ•œ ์„ค์ • ๊ฐ€๋Šฅ
  • Unity ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์™€์˜ ์•ˆ์ „ํ•œ ์ „ํ™˜ ์ œ๊ณต

๐Ÿ“ UniTask vs Task ์ฐจ์ด์ 

ํ•ญ๋ชฉ System.Threading.Tasks.Task UniTask
GC ๋น„์šฉ ๋†’์Œ (๋ฐ•์‹ฑ, ์ƒํƒœ ๊ฐ์ฒด) ๋‚ฎ์Œ (๊ตฌ์กฐ์ฒด ๊ธฐ๋ฐ˜, ๊ฑฐ์˜ 0)
์„ฑ๋Šฅ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๋ฉด ์„ฑ๋Šฅ ์ €ํ•˜ ์ˆ˜์ฒœ ๊ฐœ๋„ ๋ฌธ์ œ์—†์ด ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
Unity ํ†ตํ•ฉ ์ง€์› ์•ˆ ๋จ await WaitForSeconds ๊ฐ€๋Šฅ
ํƒ€์ž„์Šค์ผ€์ผ ๋ฌด์‹œ ๋ถˆ๊ฐ€๋Šฅ ๊ฐ€๋Šฅ (IgnoreTimeScale)
API ํ†ตํ•ฉ ํ‘œ์ค€ Task ๊ธฐ๋ฐ˜ Unity ์นœํ™”์ ์ธ API ์ œ๊ณต

๐Ÿ“‹ ๊ธฐ๋ณธ ์‚ฌ์šฉ ์˜ˆ์ œ


using Cysharp.Threading.Tasks;
using UnityEngine;

public class UniTaskExample : MonoBehaviour
{
    private async void Start()
    {
        Debug.Log("3์ดˆ ๋Œ€๊ธฐ ์‹œ์ž‘...");
        await UniTask.Delay(3000);
        Debug.Log("3์ดˆ ํ›„ ์‹คํ–‰๋จ");

        await DoSomethingAsync();
        Debug.Log("๋ชจ๋“  ์ž‘์—… ์™„๋ฃŒ");
    }

    private async UniTask DoSomethingAsync()
    {
        await UniTask.Yield();
        Debug.Log("ํ”„๋ ˆ์ž„ ์ดํ›„ ์‹คํ–‰");
    }
}
    

โฑ๏ธ ์‹œ๊ฐ„ ๋Œ€๊ธฐ API

UniTask๋Š” ๋‹ค์–‘ํ•œ ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ API๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

  • UniTask.Delay(int milliseconds)
  • UniTask.DelayFrame(int frameCount)
  • UniTask.WaitUntil(() => ์กฐ๊ฑด)
  • UniTask.WaitWhile(() => ์กฐ๊ฑด)

await UniTask.WaitUntil(() => player.IsDead);
    

๐Ÿ—๏ธ UniTask์˜ ๋‚ด๋ถ€ ๊ตฌ์กฐ

UniTask๋Š” ๊ตฌ์กฐ์ฒด(struct) ๊ธฐ๋ฐ˜์œผ๋กœ GC๋ฅผ ์œ ๋ฐœํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ์ƒํƒœ ๋จธ์‹ ์„ ํฌํ•จํ•˜์ง€๋งŒ, ๋ฐ•์‹ฑ(boxing)์ด ์—†๋Š” ๋ฐฉ์‹์œผ๋กœ ์ตœ์ ํ™”๋œ๋‹ค. ๋˜ํ•œ awaiter๋ฅผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•  ์ˆ˜ ์žˆ์–ด ๋‹ค์–‘ํ•œ Unity ์ „์šฉ ๋น„๋™๊ธฐ ์ƒํ™ฉ์— ๋Œ€์‘ ๊ฐ€๋Šฅํ•˜๋‹ค.

๋‹จ์ˆœํžˆ Task์˜ ๊ตฌ์กฐ๋ฅผ ์—๋ฎฌ๋ ˆ์ดํŠธํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, C# async/await ํ‚ค์›Œ๋“œ๋ฅผ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์—ˆ๋‹ค.

๐Ÿ”„ CancellationToken ์ง€์›

Unity๋Š” ์”ฌ ์ „ํ™˜์ด๋‚˜ ์˜ค๋ธŒ์ ํŠธ ํŒŒ๊ดด ์‹œ ์•ˆ์ „ํ•œ ๋น„๋™๊ธฐ ์ž‘์—… ์ทจ์†Œ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค. UniTask๋Š” CancellationToken์„ ์™„๋ฒฝํžˆ ์ง€์›ํ•˜๋ฉฐ, this.GetCancellationTokenOnDestroy()์ฒ˜๋Ÿผ MonoBehaviour์™€ ๊ฒฐํ•ฉ ๊ฐ€๋Šฅํ•˜๋‹ค.


private async UniTaskVoid LoadDataAsync(CancellationToken token)
{
    try
    {
        await UniTask.Delay(5000, cancellationToken: token);
        Debug.Log("์™„๋ฃŒ๋จ");
    }
    catch (OperationCanceledException)
    {
        Debug.Log("์ž‘์—…์ด ์ทจ์†Œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
    }
}
    

๐Ÿšฆ UniTaskVoid์™€ UniTask

  • UniTask: ๋ฐ˜ํ™˜๊ฐ’์ด ์žˆ๋Š” ์ผ๋ฐ˜ ๋น„๋™๊ธฐ ํ•จ์ˆ˜
  • UniTaskVoid: fire-and-forget ์šฉ๋„. ์˜ˆ์™ธ ํ•ธ๋“ค๋ง์ด ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜๊ฐ€ ํ•„์š”

๊ฒŒ์ž„ ๋กœ์ง์—๋Š” UniTask๋ฅผ ์ตœ๋Œ€ํ•œ ์‚ฌ์šฉํ•˜๊ณ , UniTaskVoid๋Š” ์ด๋ฒคํŠธ ์ฝœ๋ฐฑ ์ฒ˜๋ฆฌ๋‚˜ MonoBehaviour์˜ Start ๋“ฑ์—์„œ๋งŒ ์ œํ•œ์ ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

๐Ÿงช ํ…Œ์ŠคํŠธ ๋ฐ ๋””๋ฒ„๊น… ํŒ

  • ๋น„๋™๊ธฐ ๋กœ์ง ์ค‘๋‹จ ์‹œ Debug.Log๋กœ ์œ„์น˜๋ฅผ ์ถ”์ 
  • Try-Catch ๋ธ”๋ก์„ ์ ๊ทน ํ™œ์šฉํ•˜์—ฌ ์˜ˆ์™ธ ํ™•์ธ
  • UniTask.SwitchToMainThread() ์‚ฌ์šฉ ์‹œ์  ํ™•์ธ

๐Ÿ“‰ GC ๋ฐ ์„ฑ๋Šฅ ์ตœ์ ํ™”

  • Update ์•ˆ์—์„œ async void ์‚ฌ์šฉ ๊ธˆ์ง€
  • ๊ฐ€๋Šฅํ•œ ๋ฐ˜๋ณต ๋น„๋™๊ธฐ ๋กœ์ง์€ UniTask.Yield ๋˜๋Š” DelayFrame ํ™œ์šฉ
  • ๋ฐ˜๋ณต์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ๋Š” UniTask.Lazy ๋˜๋Š” UniTask.Defer ๋“ฑ์„ ํ†ตํ•ด ์ƒ์„ฑ ์ตœ์†Œํ™”

๐Ÿ“š ์ฐธ๊ณ  ๋งํฌ