언어 관련/C#

C# 기술 면접 질문 정리 04

chinodaiski 2025. 4. 17. 20:54

기술 면접을 준비하며, 내가 알고 있는 지식을 바탕으로 관련된 문제들을 정리해보았다. 이 답변들은 반드시 정답이 아닐 수 있으며, 면접관이 기대하는 방향과 다를 수도 있다. 참고할 경우 이러한 점을 감안하고 읽는 것이 좋다.

 

문제

31. Unity 생명주기(Unity Life Cycle)에 대해서 설명해주세요.

더보기

Unity 생명주기(Unity Life Cycle)는 Unity 엔진이 스크립트를 실행할 때 호출하는 일련의 함수 흐름이다.

 

이벤트 함수의 실행 순서 - https://docs.unity3d.com/kr/2019.4/Manual/ExecutionOrder.html


32. MonoBehaviour 클래스의 주요 메서드와 그 기능에 대해 설명해주세요.
    - MonoBehaviour 클래스에서 `Start`와 `Awake`의 차이점은 무엇이며, 이를 적절히 사용하는 방법에 대해 설명해주세요.

더보기

MonoBehaviour는 UnityEngine 네임스페이스에 포함된 기본 클래스이며, Unity의 모든 컴포넌트 기반 스크립트는 MonoBehaviour를 상속받아야 한다. MonoBehaviour를 상속하면 Unity의 생명주기(Life Cycle)에 따라 자동으로 호출되는 다양한 메서드들을 사용할 수 있으며, 대표적으로 Awake(), Start(), Update(), LateUpdate(), FixedUpdate(), OnEnable(), OnDisable(), OnDestroy() 등이 있다.

 

Awake는 오브젝트가 생성될 때 호출되며, 이는 무조건 실행되는 함수이다. 보통 내부 데이터를 초기화 한다던지, 컴포넌트를 가져온다던지, 싱글턴 인스턴스를 만든다던지 할 떄 사용한다.

 

Start는 Awake 호출 이후, 오브젝트가 활성화(SetActive(true))된 상태에서 첫 프레임이 실행되기 전에 실행된다. 비활성(SetActive(false)) 상태라면 실행되지 않는다. Awake 다음에 실행되며, 외부 오브젝트와 상호작용할 때 주로 사용한다. 예를 들어 Awake에서 초기화 됬을 다른 오브젝트의 컴포넌트에 접근하여 사용한다.

 

동일한 프레임 내에서 모든 Awake() → OnEnable() → Start() 순으로 호출된다. 이중 OnEnable은 활성화가 될 때 마다 호출되긴 한다. 그래서 보통 UI 초기화에 많이 사용된다.


33. Update, FixedUpdate, LateUpdate의 차이점에 대해 설명해주세요.

더보기

Update는 매 프레임 호출되며, LateUpdate는 Update가 끝난 뒤 호출된다. FixedUpdate는 내부적으로 고정된 시간 간격으로 호출되는 것을 보장하며 이로 인해 물리 기반 로직은 FixedUpdate에서 호출해야만 정상적으로 작동한다. 


34. Time.deltaTime이란 무엇이며, 사용하는 이유에 대해 설명해주세요.

더보기

`Time.deltaTime`은 Unity에서 이전 프레임과 현재 프레임 사이에 걸린 시간(초 단위)을 의미한다. 예를 들어, 프레임 속도가 60fps일 경우, `deltaTime` 값은 약 0.0166f (1/60)이다. Unity의 MonoBehaviour 내부에서 매 프레임마다 값이 자동 갱신된다.

 

Unity에서 게임은 프레임 단위로 로직이 실행된다. 하지만 사용자마다 사용하는 PC 성능, 디바이스에 따라 프레임 속도(FPS)는 다르다.
프레임 속도에 따라 오브젝트가 이동하는 거리가 달라지면, 빠른 컴퓨터일수록 오브젝트가 더 멀리 이동하게 되어 게임의 결과가 달라지게 된다.
이 문제를 해결하기 위해 이동거리에 `Time.deltaTime`을 곱해서 프레임 수에 관계없이 일정한 속도로 동작하도록 보정해야 하는데 주로 사용한다. 

 

유니티에서 시간은 내부적으로 c++로 되어 있다곤 하지만, 실제로 어떤 방식을 사용했는지는 어디에도 나와 있지 않다. 정밀도를 볼 때 QueryPerformanceCounter 를 사용하지 않았나 추측만 할 뿐이다.


35. 코루틴의 동작원리와 사용해본 예시를 함께 설명해주세요.

더보기

코루틴(Coroutine)은 Unity에서 일시 중단 가능한 함수를 의미한다. 일반적인 함수는 호출되면 끝날 때까지 계속 실행되지만, 코루틴은 중간에 `yield` 키워드를 사용하여 일시적으로 멈췄다가 이후에 다시 이어서 실행할 수 있는 함수이다.

Unity에서는 `IEnumerator`를 반환형으로 가지는 함수에 `StartCoroutine()`을 호출하면 코루틴이 시작된다.

 

Unity는 매 프레임마다 코루틴을 관리하는 '코루틴 스케줄러'를 돌린다. `IEnumerator`를 반환하는 메서드를 `StartCoroutine()`으로 등록하면, Unity는 이 메서드를 프레임 루프의 일부로 등록한다.

코루틴 함수 내부에서 `yield return` 키워드를 통해 다음 조건까지 일시 중단하고, 조건이 만족되는 시점에 다시 실행을 재개한다.

 

일정 주기로 실행해야하는 기능이나, 이미지를 Fade In/Out 시키는데 주로 사용했다.


36. Invoke와 코루틴의 차이에 대해 설명해주세요.

더보기

둘다 메인스레드에 의해 실행되는 것은 마찬가지로, 시간 기반으로 호출을 예약할 수 있다는 점에서 비슷한 기능을 가지고 있다고 볼 수 있다.

하지만 내부 구조는 완전히 다른데, 코루틴의 경우 Unity는 이를 내부적으로 전역적으로 관리되는 CoroutineManager 또는 코루틴 실행 큐에서 관리한다. MonoBehaviour가 비활성화되거나 파괴되면, 해당 코루틴은 CoroutineManager에서 자동 제거된다. 그에 반에 Invoke는 각 MonoBehaviour 객체 내부에서 Invoke 스케줄 테이블을 가지며 각 객체가 자신의 예약 상태를 관리하고 있고, 실행 시점은 전역 엔진 루프 중  Update 시점 직전에 일괄적으로 체크하여 실행여부를 결정한다. 


37. 코루틴과 멀티쓰레딩은 어떤 차이가 있는지 설명해주세요.

더보기

코루틴의 모든 실행은 메인 스레드에서 일어나며, Unity API를 자유롭게 호출할 수 있다. 이에 반해 멀티스레딩은 C# 또는 OS 수준에서 진짜로 병렬로 실행되는 작업 단위로 새로운 스레드를 만들어 동시에 여러 작업을 수행할 수 있다. 단, Unity의 대부분의 API는 메인 스레드에서만 호출 가능하기에 API를 사용하고 싶다면 잡큐 등 메인 스레드에 일감을 thread-safe하게 넘겨주어 실행하게 해야한다.


38. 유니티 최적화 기법은 어떤 것들이 있나요?
    - 최적화를 해본 적이 있나요? 없다면 어떤 최적화가 있는지 설명해주세요.
    - 최적화에서 가장 중요한 부분은 무엇인가요?
    - 최적화를 위해서 적용해본 텍스쳐 포맷이 있나요?

더보기

유니티에서의 최적화는 성능 저하의 원인을 파악하고, 필요한 부분에 한정해서 효율적으로 개선하는 게 핵심이다.
대표적인 기법으로는 오브젝트 풀링배칭 및 드로우콜 최소화애니메이션/메시 최적화텍스처 압축오클루전 컬링LOD(Level of Detail) 등이 있다.
Update 반복문에서 불필요한 로직을 제거하고, 필요한 컴포넌트는 캐싱하는 식으로 프레임 드랍을 개선한 적도 있다.

 

최적화에서 가장 중요한 건 문제의 원인을 명확히 파악하는 것이라고 생각한다.
프로파일러 없이 막연하게 작업하면 오히려 과잉 최적화가 될 수 있기 때문에, 유니티의 Profiler나 Memory Profiler, Stats 창 등을 활용해서 정확한 진단 후에, 꼭 필요한 부분만 최적화하는 접근이 중요하다고 생각한다.

 

모바일 빌드에서는 텍스처 용량이 특히 중요하기 때문에 ETC2, ASTC 같은 압축 포맷을 적용해야한다고 한다.
용량을 줄이면서도 퀄리티를 유지해야 했기 때문에, UI는 RGBA Compressed로, 3D 모델 텍스처는 ETC2 RGB 형식으로 설정해 용량 대비 품질을 조절할 수 있다고 한다. -> 사용해보진 않아서 잘 모르겠다.


39. 드로우콜에 대해서 설명하고, 최적화하는 방식에 대해 알고 있는 것이 있으면 설명하세요.

더보기

드로우콜은 GPU에 렌더링 명령을 보내는 호출 한 번을 의미한다.
오브젝트마다 머티리얼이나 쉐이더가 다르면 드로우콜이 늘어나고, 렌더링 비용이 증가하게 된다.

이를 줄이기 위한 대표적인 방법으로는 Static BatchingDynamic BatchingGPU InstancingSprite Atlas 이 있다.
과거 UI 시스템에서 버튼 이미지가 모두 개별 텍스처였을 때 드로우콜이 급격히 늘어난 적이 있었고,
Sprite Atlas를 사용해 하나의 텍스처로 묶어 드로우콜을 절반 이하로 줄인 경험이 있다.


40. Find 함수 사용을 자제해야 하는 이유에 대해 설명해주세요.

더보기

GameObject.Find()나 FindObjectOfType() 같은 함수는 전체 씬을 탐색하는 방식이기 때문에, 실행 시 성능 비용이 크다. 특히 Update()나 Start() 같은 곳에서 반복적으로 사용하면 프레임 드랍이나 렉의 원인이 될 수 있다.

필요한 경우에는 미리 SerializeField로 참조를 연결하거나, Awake()에서 한 번만 찾고 변수에 캐싱해서 사용하는 방식이 훨씬 효율적이다.