티스토리 뷰

Unity를 사용하다 보면 상당히 자주 사용하게 되는 것이 Awake(), Start(), Update(), FixedUpdate(), LateUpdate()이다.

전부 '특정 타이밍'에 적용되다 보니 타이밍을 맞춰서 사용하는 경우에 상당히 자주 사용하게 된다.

다만, 이러한 함수들에도 난점은 분명하게 존재한다. 바로 '같은 타이밍이라면 순서를 알 수 있는 수단이 전무하다'는 것.

이러한 점 때문에 클래스 간의 선후관계는 Awake() → Start()나 Update() → LateUpdate() 정도 말고는 불가능하다.

즉, 어떤 경우라고 해도 2단계의 선후관계만 만들 수 있다는 이야기.

 

Update() → LateUpdate()는 쓸 일이 딱히 없기 때문에 상관이 없지만, Awake() → Start()에서 문제가 자주 발생한다.

GameManager에 있는 Player 변수를 이용하여 초기화하는 Info라는 클래스가 있고, 이 내부의 Gauge 객체가 Info의 초기화 정보를 이용해서 초기화 된다고 가정 해 보자.

 

1. Player 변수를 GameManager에 할당한다

2. Info 클래스가 GameManager에 있는 Player 변수를 이용해서 초기화된다.

3. Info 내부의 Gauge 객체가 Info의 초기화 정보를 이용해서 초기화된다.

이렇게 3개의 단계로 구성되게 되는데, 전부 초기화 관련이라서 반드시 Awake()나 Start 단계에서 호출이 되어야 하는 기능들이다.

이런 경우에는 Awake() → Start() 만으로는 구현할 수가 없게 된다.

 

그렇다면 선후관계를 만들지 않으면 되는 거 아닌가? 싶을 수도 있지만, UI는 '초기화 된 객체의 정보를 기반으로 초기화를 해야하기 때문에' 어떻게 하던 선후관계가 발생할 수 밖에 없다.

 

이제, 이에 대한 해결법을 알아보자.


작업 처리 우선도 설정

우선, 코드를 수정하지 않고 시행할 수 있는 방법이다.

Edit - Project Setting으로 설정 창을 연 뒤, Script Execution Order로 들어간다.

그렇게 하면 위의 창이 표시될 건데, 여기서 우하단의 +를 누른 뒤에 실행 순서를 조정할 클래스를 선택하면 된다.

이후에 Default Time의 위나 아래로 옮기면 그 위치에 맞게 전체적인 스크립트 실행 순서가 조정된다.

[위쪽이 먼저 실행되고, 아래쪽이 나중에 실행된다.]

 

이렇게 하면 스크립트의 실행 순서가 고정이 되어 상술한 3단계 이상의 선후관계도 Awake() → Start()로 구현할 수 있다.

다만, 코드 이외의 영역에 손을 대야하는 데다가, 스크립트 전체의 실행 속도가 고정되는 것이라서 다소 껄끄러운 방법인 것이 사실이다.

그렇다면 다른 방법은 뭐가 있을까.


초기화의 주체를 바꾸기 

간단하게 말하면, 초기화를 할 때 참조하는 변수의 소유 클래스에서 초기화 함수를 호출하도록 하는 방법이다.

즉, Awake() → Start()를 사용하는 게 아니라, 독자적인 순서 구조를 확립한다는 것.

그렇게 되면 Start()라는 타이밍 하나에 모든 선후관계가 작동되게 작성이 가능하고, 상술한 Script Execution Order를 조정할 필요도 없게 된다.

물론, Awake() 문에서 변수의 소유 클래스에게 해당 클래스를 할당하는 절차가 필요하긴 하지만, 얼마나 많은 단계의 선후관계가 필요하더라도 설정을 건들일 필요 없이 모두 구현할 수 있다는 점이 강점.

 

사실, Awake()와 Start()의 처리 타이밍이 달라도 한참 다른데다가, 엄밀히 말해서 선후관계를 형성하기 위해서 존재하는 함수가 아니기 때문에 Awake()는 내부 구성 변수 할당용으로, Start()는 초기화용으로 사용하도록 고정할 수 있다는 점에서 이쪽이 더 적합한 방법이라고 할 수도 있다.

 

그리고 이것이 Manager 클래스들, 정확히는 Singletone을 형성하는 이유 중 하나이기도 하다.

Manager 클래스들은 기본적으로 프로그램 전체에서 범용적으로 활용하는 요소들을 담아두기 위해 사용되는 클래스들인데, 상술한 Awake()문에서 변수의 소유 클래스에게 해당 클래스를 건네주는 용도로도 사용할 수 있기 때문.

물론, 이건 선후관계가 성립하는 두 클래스의 인스턴스가 서로 상하위 관계가 아닌 경우에 사용하는 방법이고, 상하위 관계라면 그냥 상위나 동위 인스턴스의 클래스에서 GetComponant<>()로 직접 가져오면 된다.

 

특히 정보의 소유주랑 완전히 다른 인스턴스로 구성되는 UI에서 이런 방식을 사용하는 경우가 많은데, UI쪽은 표시량이 꽤 많은 편이라서 로딩 속도를 줄이기 위해서 동적으로 생성하는 경우도 잦아서 아예 UIManager에서 생성해서 가지고 있도록 구성하는 경우도 많다.

 

아니면 ScriptableObject를 정보 교환의 중개역으로 두는 경우도 있다.

이러면 Manager를 거치지 않고 두 객체 사이의 정보 교환을 할 수가 있다 = Manager 클래스의 구성이 간결해진다는 점이 강점. 다만, ScriptableObject라는 별개 중개 클래스를 두고 있다는 점에서 코드가 다소 분산되는 경향이 있다는 점은 주의해야 할 것이다.


Unity를 이용해서 개발하다 보면 들기 쉬운 습관이 Awake()와 Start()를 선후관계 형성용으로 사용하는 것이다.

개인적으로는 이런 방식으로 작업하다 보면 복잡한 시스템을 구성하거나 UI 연동 등에서 난감해 지는 경우가 잦다 보니, 가능하면 빠르게 고치는 편이 좋다고 생각한다.

물론, 본인도 이러한 습관을 깨달은 지 얼마 안된 시점인지라 잘 안되는 내용이긴 한데, 그래도 차후 작업을 할 때 주의해서 작업하려고 노력하고 있다.

 

개인적으로 자주 고민하고 실천하려고 노력하는 사항이 있다.

한번 쯤 본인의 코딩 습관을 돌아보자. 그러면 보일 것이다.

내 코드에 자리잡고 있는 안좋은 습관의 흔적이 말이다.

이걸 고칠 때에는, 나는 한층 더 성장할 것이다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함