개인사

[Unity] Awake()와 Start() 메서드의 차이 본문

게임 개발/유니티(Unity)

[Unity] Awake()와 Start() 메서드의 차이

마달랭 2025. 9. 9. 16:37
728x90

개요

Awake()
  - 씬에 오브젝트가 존재하기만 하면 호출 (비활성화 상태여도 실행)
  - GameObject가 처음 생성될 때 즉시 실행
  - GameObject.SetActive(false) 상태여도 실행됨

Start()
  - GameObject가 활성화된 상태에서만 호출
  - 첫 번째 SetActive(true) 시점에 실행

 

유니티 동작원리 상 GameObject가 비활성화 상태에서는 Start() 메서드가 실행되지 않는다.
Start()는 GameObject가 활성화될 때 최초 1회만 실행된다.
만약 Hierarchy에서 GameObject가 비활성화 상태로 설정되어 있었다면, 활성화 처리하기 전까진 Start()메서드가 실행되지 않으며 GameObject가 활성화 되었을 때 Start() 최초 실행이 이루어진다.

반면 Awake의 경우 씬에 존재하면 비활성화 상태에서도 무조건 실행이 된다.

 

그렇다면 Awake가 Start의 무조건적인 상위 호환인 느낌이지 않을까? 라는 생각이 들 수도 있지만 런타임 환경에서 이벤트나 데이터의 의존 관계에 따라 다르게 사용해 주어야 한다.

하기에서 실제 프로젝트를 진행하며 발생한 문제를 기반으로 예시를 들어보도록 하겠다.

 

 

문제상황

현재 프로젝트를 진행하면서 특정 버튼 최초 1회 클릭 시 로그는 찍히나 오브젝트가 성공적으로 활성화 되지 않는 문제가 발생한 경험이 있다.

[LobbyPanel] 방 생성 버튼 클릭
[LobbyPanel] CreateRoomPanel GameObject 먼저 활성화
[CreateRoomPanel] Show() 호출됨 - 현재 상태: gameObject.activeSelf=True
[CreateRoomPanel] 방 이름 업데이트: 바보님의 방
[CreateRoomPanel] modalBackground 활성화됨
[CreateRoomPanel] gameObject 활성화 완료 - 새 상태: True
[CreateRoomPanel] 방 이름 초기값 설정: 바보님의 방
[LobbyPanel] 방 생성 버튼 클릭
[LobbyPanel] CreateRoomPanel GameObject 먼저 활성화
[CreateRoomPanel] Show() 호출됨 - 현재 상태: gameObject.activeSelf=True
[CreateRoomPanel] 방 이름 업데이트: 바보님의 방
[CreateRoomPanel] modalBackground 활성화됨
[CreateRoomPanel] gameObject 활성화 완료 - 새 상태: True

 

로그를 보면 방 생성 버튼이 클릭 되었고, Show가 호출 되었으며 게임 오브젝트가 활성화 된 것이 보인다.

하지만 실제로 하이어라키상에서 첫번째 이벤트 발생 시에도 게임 오브젝트가 비활성화 되어 있었다. 뭔가 이상하다.

그래서 이벤트 발생 시 처리를 진행하는 코드 블록을 확인해 보았다.

 

 

디버깅

private void OnCreateRoomButtonClicked()
{
    Debug.Log("[LobbyPanel] 방 생성 버튼 클릭");

    // createRoomPanel이 null인 경우 다시 찾아보기
    if (createRoomPanel == null)
    {
        createRoomPanel = FindObjectOfType<CreateRoomPanel>();
        Debug.Log($"[LobbyPanel] CreateRoomPanel 다시 검색: {(createRoomPanel != null ? "찾음" : "못 찾음")}");
    }

    if (createRoomPanel != null)
    {
        // CreateRoomPanel GameObject가 비활성화되어 있으면 Show() 메서드가 실행되지 않으므로 먼저 활성화
        if (!createRoomPanel.gameObject.activeInHierarchy)
        {
            createRoomPanel.gameObject.SetActive(true);
            Debug.Log("[LobbyPanel] CreateRoomPanel GameObject 먼저 활성화");
        }

        createRoomPanel.Show();
    }
    else
    {
        Debug.LogError("[LobbyPanel] CreateRoomPanel을 찾을 수 없습니다. Inspector에서 할당하거나 씬에 CreateRoomPanel이 있는지 확인하세요.");
    }
}

 

혹시 내가 존재하지 않는 게임 오브젝트를 참조하려고 하는 것이지 안을까? 라는 생각에 디버그 코드를 삽입하고 재현스탭을 추가로 수행하며 문제점을 찾기로 하였다.

근데 아무리 유저 플로우를 타고가도 로그상으로나 코드상으로나 잘못된 문제점이 보이지 않았다.

 

이제 이벤트를 발생시키는 주체 말고, 활성화 하고자 하는 게임 오브젝트의 스크립트로 이동을 해 디버깅을 해보았다.

private void Start()
{
    InitializeUI();
    SetupEventHandlers();
    Hide();
}

 

결론적으론 이게 문제였다, Start()메서드 블록에 Hide()가 존재하였기 때문에 최초 활성화가 되자 마자 초기화 처리 및 이벤트 핸들러를 할당한 후에 곧 바로 Hide()메서드가 실행되어 마치 게임 오브젝트가 비활성화 된 것처럼 보인 것이다.

실제로는 활성화가 되자마자 초기화 후 다시 비활성화를 처리하는 상태가 되었다.

 

 

트러블슈팅

그럼 이러한 상황이 왜 생긴 것인지에 대해 곰곰히 생각을 해보았다.

 

유니티 에디터의 하이어라키상으론 오브젝트가 다음과 같이 배치되어 있다.

  1. 여기서 활성화 하려고 하는 것은 CreateRoomPanel이다.
  2. 근데 CreateRoomPanel은 초기 상태가 비활성화 되어 있는 상태이다.
  3. 어찌보면 당연한 것이다, 해당 패널은 이벤트가 호출되었을 때에만 노출되는 모달이기 때문이다.

하지만 스크립트와는 불일치가 발생한다, 해당 모달은 비활성 상태로 씬에 배치된상태인데, Start메서드에서 Hide처리를 해버리면 활성화 되자 마자 비활성화 처리되는 것이 정상 작동이다.

즉, 스크립트는 CreateRoomPanel오브젝트가 활성화 상태로 씬에 배치될 것으로 생각이 되어 씬에 진입 하였을 때 CreateRoomPanel에 대한 초기화를 진행 후 비활성 처리를 하려고 한 것이다.

하지만 의도와는 다르게 CreateRoomPanel오브젝트가 비활성 상태로 씬에 배치되어 있었으므로, 최초 1회 활성화가 되었을 때, 즉, 이벤트가 호출되었을 때 Start()메서드 블록이 수행되어 마치 활성화가 되지 않은 것 처럼 보인 것이다.

 

private void Start()
{
    InitializeUI();
    SetupEventHandlers();
    // Hide() 제거 - 초기 상태는 GameObject 비활성화로 관리
}

 

따라서 위와 같이 코드를 변경해 주니 모든 문제가 해결되었다.

 

 

회고

그럼 차리리 Start()블록에 해당하는 것을 Awake()블록에 할당하는게 좋지 않은가? 라는 생각이 들었다.

사실상 Hide처리는 유니티 에디터의 하이어라키 상에서 내가 활성화 처리할 것인지 비활성화 할 것인지에 따른 사소한 문제이지만, 오브젝트 초기화 시점은 문제가 될 수 있다.

 

Awake() 블록에서 초기화를 진행한다면, 씬이 활성화된 시점에 초기화가 진행되므로 런타임 상에서 해당 오브젝트가 참조하려고 하는 데이터의 값이 불일치할 가능성이 있다. 하지만 Start() 블록에서 초기화를 한다면 해당 시점에 참조하려고 하는 데이터를 받아올 수 있기 때문에 비교적 안전하다.

 

현재 나는 씬을 이동할때 씬과 씬 사이에 데이터 로딩용 씬을 하나씩 추가해 둔 상태이다.

MainScene -> SingleCore -> SingleGameplayScene
MainScene -> MultiCore -> MultiGameplayScene

 

예를 들면 위처럼 MainScene에서 싱글/멀티 플레이를 선택 시 각 Core씬에서 모드에 필요한 데이터를 미리 초기화 한다.

초기화가 완료되기 전까진 다음 씬으로 넘어가지 않으며, 초기화가 완료된 시점에 다음 씬으로 넘어가게 설계를 해두었다.

따라서 현재 내 경우엔 Awake() 블록에서 초기화를 진행해도 이슈가 생기지 않는다.

 

단 이와 같은 설계가 아닌 씬에서 바로 씬으로 넘어가도록 설계가 되어있는 경우엔 서버든 메모리던 데이터를 제대로 받아오지 않은 시점에 Awake() 블록에서 초기화를 진행하려고 하면 쓰레기값이 들어있던지 에러를 유발할 수 있다.

 

사실 이미 Start()와 Awake()의 차이점은 알고 있었지만 이번 기회로 무심하게 넘겼던 이 상황을 다시 되짚어 볼 수 있는 시간이 되어 뜻깊은 경험을 할 수 있었다.

728x90

'게임 개발 > 유니티(Unity)' 카테고리의 다른 글

[Unity] 2D 캐릭터 위치 변경  (1) 2024.12.27
[Unity] 오브젝트 네이밍 자동화  (0) 2024.12.27
[Unity] 2D 타일 충돌 처리  (0) 2024.12.26
[Unity] 2D 캐릭터 이동  (0) 2024.12.26
[Unity] 2D 타일맵 배치  (1) 2024.12.26