핵심 정리
private IEnumerator FlashDamage()
{
indicator.SetActive(true);
yield return new WaitForSeconds(0.15f);
indicator.SetActive(false);
}
// 시작 — Coroutine 핸들을 저장하면 나중에 중단할 수 있음
private Coroutine _flashHandle;
private void OnHit()
{
if (_flashHandle != null) StopCoroutine(_flashHandle);
_flashHandle = StartCoroutine(FlashDamage());
}
private void OnDisable()
{
if (_flashHandle != null)
{
StopCoroutine(_flashHandle);
_flashHandle = null;
}
}구조 이해
코루틴이 잘 맞는 상황
코루틴은 "단계 사이에 기다림이 끼어 있는 순차 흐름"을 표현할 때 특히 강합니다. 상태 변수를 여러 개 만들지 않고도 읽기 쉬운 시간 기반 로직을 작성할 수 있습니다.
- UI 페이드 인/아웃, 쿨다운 표시
- 튜토리얼 단계별 연출, 시간차 소환
- "켜기 → 기다리기 → 끄기" 같은 단순 시퀀스
Update타이머 변수를 여러 개 쌓기보다 흐름으로 읽고 싶을 때
반면 매 프레임 값을 관측하고 갱신해야 하는 로직은 Update가 훨씬 자연스럽습니다. 코루틴과 Update는 경쟁 관계가 아니라 역할 분담입니다.
yield return 종류
| yield 구문 | 동작 |
|---|---|
yield return null | 다음 프레임에 재개 |
new WaitForSeconds(t) | t초 후 재개 (timeScale 영향 받음) |
new WaitForSecondsRealtime(t) | t초 후 재개 (timeScale 영향 없음) |
new WaitUntil(() => condition) | 조건이 true가 될 때 재개 |
new WaitWhile(() => condition) | 조건이 false가 될 때 재개 |
new WaitForEndOfFrame() | 프레임 렌더링 완료 후 재개 |
new WaitForFixedUpdate() | 다음 FixedUpdate 후 재개 |
WaitForSeconds와 WaitForSecondsRealtime의 차이는 일시정지 구현에서 바로 드러납니다. timeScale = 0으로 게임을 멈출 때 메뉴 애니메이션이나 로딩 바는 계속 돌아야 하므로 WaitForSecondsRealtime을 써야 합니다.
중복 실행 방지
같은 코루틴을 여러 번 StartCoroutine하면 인스턴스가 중복 실행됩니다. 한 번만 돌아야 하는 연출은 핸들을 저장하고 재시작 전에 중단 하는 패턴을 씁니다.
// 중복 없이 항상 최신 1개만 실행
if (_handle != null) StopCoroutine(_handle);
_handle = StartCoroutine(MyRoutine());코루틴은 스레드가 아니다
코루틴은 메인 스레드에서 yield 경계마다 잠깐 제어권을 돌려주는 구조입니다. 무거운 연산을 yield 없이 오래 돌리면 프레임이 그대로 끊깁니다. 진짜 백그라운드 처리가 필요하면 Task, UniTask, 또는 Job System을 사용해야 합니다.
오브젝트 비활성화·파괴와의 관계
오브젝트가 SetActive(false)되거나 파괴되면 그 오브젝트에 속한 코루틴은 자동으로 중단됩니다. OnDisable에서 StopCoroutine을 명시적으로 호출하면 핸들을 초기화해 재활성화 시 꼬임을 방지할 수 있습니다.
체크포인트
| 항목 | 코루틴 | Update 타이머 |
|---|---|---|
| 순차 흐름 | 읽기 쉬움 | 상태 변수 필요 |
| 매 프레임 관측 | 부적합 | 자연스러움 |
| 중단/재개 제어 | StopCoroutine | 플래그 변수 |
| timeScale 연동 | WaitForSeconds / WaitForSecondsRealtime 선택 | deltaTime / unscaledDeltaTime 선택 |
| 백그라운드 처리 | 불가 (메인 스레드) | 불가 (메인 스레드) |
| 상황 | 선택 |
|---|---|
| 연출 시퀀스 (켜기→기다리기→끄기) | 코루틴 |
| 쿨다운, 게이지, 매 프레임 수치 갱신 | Update |
| pause 중에도 동작해야 하는 대기 | WaitForSecondsRealtime |
| 백그라운드 비동기 처리 | async/await + UniTask |
| 비교 | 더 잘 맞는 쪽 | 이유 |
|---|---|---|
| 순차 연출 vs 지속 관측 | 코루틴 / Update | 흐름을 써야 하는지, 매 프레임 상태를 봐야 하는지 다름 |
WaitForSeconds vs WaitForSecondsRealtime | timeScale 영향 받음 / 받지 않음 | pause 중에도 돌아야 하는지 여부 |
코루틴 vs async/await | Unity 프레임 흐름 / 실제 비동기 작업 | 파일 I/O, 네트워크는 코루틴만으로 해결되지 않음 |
주의할 점
코루틴은 "나눠서 실행하는 문법"이지 "백그라운드 처리"가 아닙니다. yield 없이 무거운 루프를 돌리면 그 프레임은 그대로 끊깁니다. 또 같은 코루틴을 중단 없이 여러 번 StartCoroutine하면 인스턴스가 쌓이므로, 한 번만 돌아야 하는 연출은 핸들을 저장해 관리하세요.
// ❌ 일시정지 메뉴인데 WaitForSeconds 사용
yield return new WaitForSeconds(1f);
// → timeScale = 0 이면 재개되지 않음
// ✅ pause 중에도 돌아야 하면 realtime 계열 사용
yield return new WaitForSecondsRealtime(1f);참고 링크
3 sources