ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Unity | 코루틴(Coroutine)과 Async/Await(Task)
    다시한번 개발자도전! 2026. 1. 21. 16:07

    < 유니티 코루틴(Coroutine) >

     

    1. 코루틴이란?

     실행을 중간에 멈췄다가, 나중에 다시 시작할 수 있는 아주 특별한 함수.

    • 비유: 영화를 보다가 일시정지하고 화장실에 갔다가, 다시 돌아와서 멈춘 부분부터 이어서 보는 것과 같음.

    2. 비동기적 처리

    특정 작업을 "3초 동안" 실행하고 싶을 때 Update() 안에 짜면 매 프레임마다 복잡한 타이머 로직이 필요.

    코루틴을 쓰면 이걸 아주 직관적으로 짤 수 있음.

    • 예시: 적을 1초 간격으로 스폰하기
    IEnumerator SpawnEnemyRoutine()
    {
        while (true) 
        {
            //적 생성
            Instantiate(enemyPrefab, spawnPoint.position, Quaternion.identity);
            
            // 딱 1초 기다리고 다음 코드 실행함
            yield return new WaitForSeconds(1.0f); 
        }
    }
    
    // 실행은 Start에서 한 번만
    void Start() { StartCoroutine(SpawnEnemyRoutine()); }

    3. 내부 동작 원리

    코루틴은 유니티의 메인 스레드에서 돌아감.(멀티스레드x)

    • 유니티 엔진은 매 프레임마다 돌아가는 코루틴들을 체크함.
    • yield return을 만나면 "다음 프레임(혹은 지정된 시간)에 여기서부터 다시 해!"라고 주소만 저장해두고 바로 제어권을 엔진에 돌려줌.
    • 예시 : 메인 일꾼(단일 스레드)이 일을 하다가 yield return을 만나면, 일을 잠시 내려놓고 "다음 프레임에 다시 올게!" 하고 다른 일(화면 그리기 등)을 하러 가는 방식임.

    ⬇️ 스레드(Thread)란?⬇️

    더보기

    1.스레드(Thread)란?

    • 스레드는 프로세스(프로그램) 내에서 실행되는 하나의 작업 흐름. 쉽게 말해 일꾼 한 명이라고 생각하면 됨.

    2. 단일 스레드 (Single Thread)

    일꾼이 딱 한 명인 시스템임. 유니티의 메인 로직(Update, 코루틴 등)은 기본적으로 이 단일 스레드에서 돌아감.

    • 특징: 한 번에 하나의 일만 할 수 있음.
    • 장점: 일꾼이 한 명이라 데이터가 꼬일 일이 없음(동기화 문제 없음). 설계가 단순함.
    • 단점: 한 작업이 너무 오래 걸리면 전체 프로그램이 멈춰버림(렉, 응답 없음).
    • 비유: 편의점에 계산대와 직원이 딱 하나인 상황임. 앞 손님이 대량 결제를 하면 뒷사람은 마냥 기다려야 함.

    3. 멀티 스레드 (Multi Thread)

    일꾼이 여러 명인 시스템임. 요새 나오는 CPU들은 코어가 많아서 여러 일을 동시에 처리할 수 있음.

    • 특징: 여러 작업을 동시에(병렬로) 처리함.
    • 장점: 무거운 작업을 백그라운드 일꾼에게 맡기면 메인 일꾼은 계속 화면을 그려줄 수 있어 게임이 부드러움.
    • 단점: 일꾼들끼리 같은 물건(데이터)을 동시에 건드리면 싸움이 남(Race Condition). 관리가 매우 까다로움.
    • 비유: 대형 마트의 여러 계산대임. 한쪽이 막혀도 다른 줄은 계속 빠짐.

    4. 차이점 비교

    구분 단일 스레드 (Single Thread) 멀티 스레드 (Multi Thread)
    작업 방식 순차적 (하나씩 처리) 병렬적 (동환에 여러 개 처리)
    안정성 높음 (데이터 충돌 없음) 낮음 (데이터 동기화 필요)
    성능 무거운 작업 시 멈춤 현상 발생 자원을 효율적으로 사용하여 빠름
    비동기 구현 코루틴 등으로 흉내 냄 Task, Thread 등으로 실제 구현

    4. 핵심 문법 정리

    ① yield return 종류(정지 지점)

    문법 의미
    yield return null; 다음 프레임까지 대기
    yield return new WaitForSeconds(f); 입력한 초(f)만큼 대기
    yield return new WaitForFixedUpdate(); 다음 FixedUpdate 타임까지 대기
    yield break; 코루틴을 강제로 완전히 종료

     

    ② 실행과 중지

    // 실행
    Coroutine myCo = StartCoroutine(MyRoutine());
    
    // 중지
    StopCoroutine(myCo); 
    StopAllCoroutines(); // 이 스크립트의 모든 코루틴 중지

     

     

    💡코루틴 꿀팁💡

    더보기

    캐싱 사용: yield return new WaitForSeconds(1f);를 반복문 안에서 계속 쓰면 가비지(Garbage)가 쌓임. 멤버 변수에 미리 담아서 재사용.

     

    비활성화 주의: gameObject.SetActive(false)가 되면 그 오브젝트에서 돌아가던 코루틴은 자동으로 중지됨. 다시 켠다고 해서 멈춘 지점부터 시작되지 않으니 주의해야 함. (단, 스크립트 컴포넌트인 .enabled = false는 영향을 주지 않음)

     

    인자 전달: Invoke와 달리 매개변수를 자유롭게 넘길 수 있으니 적극 활용하길 봄.


    < Async / Await >

    C#에서 비동기 프로그래밍을 동기 코드(위에서 아래로 흐르는 코드)처럼 짤 수 있게 해주는 문법.

    • Async (비동기): 이 메서드는 비동기로 동작할 것이라고 선언하는 키워드임.
    • Await (기다림): 비동기 작업이 끝날 때까지 기다리라는 뜻임. 중요한 건 기다리는 동안 메인 스레드(게임 화면)를 멈추지 않고 제어권을 시스템에 양보함.

    1. 동작 원리

    코루틴이 유니티 엔진의 Update 루프에 의존한다면, Async/Await는 .NET의 Task(작업) 시스템을 기반으로 작동.

    1. 메서드를 실행하다가 await를 만남.
    2. 작업이 끝날 때까지 기다려야 한다는 걸 인식하고, 일단 호출했던 곳으로 제어권을 돌려줌 (게임은 계속 돌아감).
    3. 백그라운드에서 작업이 완료되면, await 바로 다음 줄부터 코드가 다시 실행됨.

    2. 장점

    • 결과값 반환: 코루틴은 값을 직접 못 돌려주지만, Task<T>를 쓰면 int, string 등 원하는 결과를 바로 받을 수 있음.
    • 예외 처리: try-catch 문을 그대로 쓸 수 있어서 서버 통신 에러 등을 잡기가 훨씬 쉬움.
    • 병렬 처리: 여러 작업을 한꺼번에 던져놓고 다 끝날 때까지 기다리는(Task.WhenAll) 로직이 매우 강력함.
    • 범용성: 유니티뿐만 아니라 일반적인 C# 프로그램 어디서든 쓸 수 있음.

     

    3. 코드 예시

    using System.Threading.Tasks; // Task를 쓰기 위해 꼭 필요함!
    using UnityEngine;
    
    public class SimpleAsync : MonoBehaviour
    {
        private async void Start()
        {
            Debug.Log("로딩 시작");
    
            // await를 붙이면 작업이 끝날 때까지 여기서 '비동기'로 기다림
            string result = await GetDataAsync();
    
            // 작업이 끝나면 아래 코드가 이어서 실행됨
            Debug.Log("로딩 완료! 결과: " + result);
        }
    
        // Task<반환값>을 리턴하는 비동기 함수임
        private async Task<string> GetDataAsync()
        {
            // 코루틴의 WaitForSeconds와 비슷한 역할 (2초 대기)
            await Task.Delay(2000); 
    
            Debug.Log("데이터를 성공적으로 가져옴");
            return "서버 데이터 도착!";
        }
    }

     

    💡 키워드 설명💡

    1. async 키워드: "이 함수 안에서 await를 쓸 거야!"라고 선언하는 거임.
    2. await 키워드: 비동기 작업(Task)이 끝날 때까지 기다리되, 메인 스레드(게임 화면)를 잡아먹지 않게 해줌.
    3. Task<string>: 비동기 작업이 끝난 뒤에 string 타입의 결과물을 돌려주겠다는 약속임. (코루틴은 이게 안 됨!)
    4. Task.Delay(2000): 밀리초 단위임. 2000은 2초를 의미함.

    4. 주의할 점 

    • 메인 스레드 제약: await 이후의 코드가 다른 스레드에서 돌아갈 경우, transform.position 같은 유니티 전용 기능을 건드리면 에러가 날 수 있음.
    • 오브젝트 파괴: 코루틴은 오브젝트가 꺼지면 같이 멈추지만, Task는 오브젝트가 파괴되어도 백그라운드에서 계속 돌아감. 그래서 CancellationToken을 써서 직접 멈춰줘야 함.
    • UniTask 추천: 유니티 환경에 최적화된 UniTask라는 라이브러리가 있는데, 실무에선 거의 이걸 씀. (메모리 사용량이 훨씬 적음)

    <  코루틴 (Coroutine) 과 Async / Await  차이점  >

    특징 코루틴 (Coroutine) Async / Await (Task)
    반환값 불가능 (결과를 못 돌려줌) 가능 (Task<T>로 결과 반환)
    에러 처리 try-catch 사용 불가 try-catch로 예외 처리 가능
    메모리 약간의 가비지(GC) 발생 최적화가 더 잘 되어 있음
    중지(Stop) StopCoroutine (직관적임) CancellationToken (약간 복잡함)
    유니티 친화도 유니티 전용 기능 (친화적) C# 표준 기능 (범용적)
    • 단순 연출, 초 단위 타이머, 유니티 컴포넌트 제어  코루틴
    • 네트워크 통신, 복잡한 데이터 로딩, 반환값이 필요한 로직  Async/Await
Designed by Tistory.