Prosto

Awake, OnEnable, Start, OnDisable 호출 순서 - Unity 6에서 다시 확인해보기 본문

Programing/Unity 3D

Awake, OnEnable, Start, OnDisable 호출 순서 - Unity 6에서 다시 확인해보기

Prosto 2026. 8. 1. 04:07
반응형

Prosto Unity

처음 작성한 날: 2017년 5월 1일
다시 확인하고 고친 날: 2026년 8월 1일
확인한 버전: Unity 6000.4.2f1
예전 글: Awake, Start, OnEnable, OnDisable - 유니티 스크립트
English edition: Unity 6 MonoBehaviour Lifecycle

2017년 5월에 Awake, Start, OnEnable, OnDisable의 호출 순서를 정리한 글을 쓴 적이 있습니다.

오랜만에 그 글을 다시 읽어보니 큰 흐름은 지금도 맞았습니다. 다만 당시에는 “Awake는 최초로 활성화될 때 호출된다” 정도로 설명을 끝냈는데, 지금 기준으로 보면 조금 뭉뚱그린 표현입니다. GameObject가 꺼져 있는 경우와 컴포넌트의 체크만 꺼져 있는 경우가 서로 다르고, FixedUpdate도 렌더링 프레임과 같은 개념으로 생각하면 곤란합니다.

그래서 예전 내용을 그대로 옮기지 않고 Unity 6000.4.2f1 프로젝트를 따로 만들어 다시 실행해봤습니다. 이번 글은 그 결과를 바탕으로 고친 2026년판입니다.

먼저 결과부터 보면

활성 상태인 GameObject에 활성화된 스크립트가 붙어 있을 때, 같은 컴포넌트 안의 시작 순서는 다음과 같았습니다.

Awake
OnEnable
Start
첫 번째 Update

스크립트 컴포넌트의 체크를 해제하면 OnDisable이 호출됩니다. 다시 체크하면 OnEnable이 호출되고요. 이때 AwakeStart는 다시 호출되지 않았습니다.

처음 실행: Awake → OnEnable → Start → Update ...
비활성화: OnDisable
재활성화: OnEnable → Update ...
파괴: OnDisable → OnDestroy

2017년 Unity에서 확인한 Awake OnEnable Start 순서

2017년 당시 Unity에서 확인했던 순서입니다. Unity 6000.4.2f1에서도 같은 순서로 확인됐습니다.

직접 확인할 수 있는 스크립트

빈 GameObject를 하나 만들고 아래 스크립트를 붙인 뒤 Console 창을 열어보면 됩니다. Update는 매 프레임 호출되므로 로그가 계속 쌓이지 않게 첫 번째 호출만 출력하도록 했습니다.

using UnityEngine;

public sealed class LifecycleProbe : MonoBehaviour
{
    private bool loggedFirstUpdate;

    private void Awake()
    {
        Debug.Log($"{name}: Awake");
    }

    private void OnEnable()
    {
        Debug.Log($"{name}: OnEnable");
    }

    private void Start()
    {
        Debug.Log($"{name}: Start");
    }

    private void Update()
    {
        if (loggedFirstUpdate)
            return;

        loggedFirstUpdate = true;
        Debug.Log($"{name}: first Update");
    }

    private void FixedUpdate()
    {
        // Rigidbody에 힘을 주는 것과 같은 물리 처리를 여기에 둡니다.
    }

    private void OnDisable()
    {
        Debug.Log($"{name}: OnDisable");
    }

    private void OnDestroy()
    {
        Debug.Log($"{name}: OnDestroy");
    }
}

Awake는 정확히 언제 호출될까?

Awake는 해당 스크립트 인스턴스의 수명 동안 한 번만 호출됩니다. 같은 GameObject에 있는 컴포넌트를 GetComponent로 찾아 저장하거나, 자기 내부 값을 준비하는 곳으로 사용하기 좋습니다.

여기서 헷갈리기 쉬운 부분이 하나 있습니다.

  • GameObject는 켜져 있고 스크립트 컴포넌트만 꺼져 있다면 Awake는 호출됩니다. OnEnableStart는 컴포넌트를 켤 때까지 기다립니다.
  • GameObject 자체가 꺼져 있다면 Awake도 아직 호출되지 않습니다. GameObject를 처음 켜는 순간 Awake → OnEnable → Start 순서로 진행됩니다.

예전 글의 “최초로 활성화될 때”라는 설명은 두 경우를 구분하지 못한다는 점에서 이번에 고쳤습니다.

OnEnable과 OnDisable은 한 쌍으로 생각하면 편하다

OnEnable은 GameObject와 컴포넌트가 실제로 동작 가능한 상태가 될 때마다 호출됩니다. 반대로 컴포넌트를 끄거나 GameObject를 비활성화하면 OnDisable이 호출됩니다. 파괴하거나 씬을 내릴 때도 활성 상태였던 컴포넌트에서는 OnDisable이 호출될 수 있습니다.

실제로는 이벤트를 연결하고 해제할 때 자주 사용합니다. OnEnable에서 이벤트를 구독했다면 OnDisable에서 해제하는 식입니다. 이렇게 짝을 맞춰두면 오브젝트를 여러 번 켰을 때 이벤트가 중복 등록되는 문제를 줄일 수 있습니다.

Start는 Awake보다 늦지만, 모든 순서를 보장하지는 않는다

Start는 한 번만 호출되며 자신의 Awake보다는 항상 늦습니다. 처음 씬을 불러올 때는 씬 오브젝트들의 Awake가 끝난 뒤 Start가 실행됩니다. 그래서 A가 자신의 값을 Awake에서 준비하고, B가 그 값을 Start에서 읽는 기본적인 방식은 지금도 사용할 수 있습니다.

하지만 서로 다른 GameObject의 Awake끼리 어느 쪽이 먼저일지는 보장되지 않습니다. Update도 마찬가지입니다. 실행할 때마다 우연히 같은 순서로 보이더라도 그 순서에 기대어 코드를 만들면 나중에 문제가 생길 수 있습니다.

정말 순서를 정해야 한다면 Unity의 Script Execution Order[DefaultExecutionOrder]를 사용할 수 있습니다. 다만 작은 프로젝트에서는 초기화 순서를 담당하는 코드를 따로 두는 쪽이 나중에 확인하기 더 편한 경우도 많았습니다.

Update와 FixedUpdate는 서로 다른 시간으로 움직인다

Update는 활성화된 MonoBehaviour에서 렌더링 프레임마다 한 번 호출됩니다. 프레임 속도와 관계없이 일정하게 움직이게 하려면 보통 Time.deltaTime을 함께 사용합니다.

FixedUpdate는 물리 시뮬레이션의 고정 시간 단계에서 호출됩니다. 기본값은 0.02초, 즉 초당 50단계지만 Time.fixedDeltaTime이나 Project Settings의 Time 설정에서 바꿀 수 있습니다.

중요한 것은 FixedUpdate가 렌더링 프레임마다 정확히 한 번 호출되는 함수가 아니라는 점입니다. 상황에 따라 한 렌더링 프레임 사이에 0번, 1번 또는 여러 번 호출될 수 있습니다. 일반적인 입력 확인은 Update에서 하고, Rigidbody.AddForce 같은 물리 처리는 FixedUpdate에서 하는 이유입니다.

다시 정리하면

2017년 글에서 확인했던 Awake → OnEnable → Start 순서는 Unity 6에서도 그대로였습니다. 달라진 것은 핵심 순서라기보다 설명해야 할 범위였습니다.

  • Awake는 인스턴스당 한 번이며, GameObject가 꺼져 있으면 활성화될 때까지 기다립니다.
  • OnEnableOnDisable은 오브젝트를 켜고 끌 때마다 반복될 수 있습니다.
  • Start는 한 번만 실행되며 자신의 Awake보다 늦습니다.
  • 서로 다른 GameObject 사이의 같은 이벤트 함수 호출 순서는 기본적으로 보장되지 않습니다.
  • FixedUpdate의 0.02초는 기본값이며 렌더링 프레임당 호출 횟수는 달라질 수 있습니다.

오래된 강좌라고 해서 전부 버릴 필요는 없었습니다. 큰 원리는 남아 있었고, 지금 다시 직접 돌려보니 어디까지가 맞는 설명이고 어디부터 보완해야 하는지가 더 잘 보였습니다. 앞으로 예전에 작성했던 Unity 글도 이런 방식으로 현재 버전에서 다시 확인해보려고 합니다.

참고한 Unity 공식 문서

반응형
Comments