1. STL·자료구조·CS

1. STL 벡터의 장점과 단점

장점은 연속 메모리에서 나옵니다. 인덱스 임의 접근이 O(1)이고, 원소들이 붙어 있어 캐시 지역성이 뛰어나 순회가 실질적으로 가장 빠른 컨테이너이며, 끝 삽입은 amortized O(1)입니다. C 배열·API와의 호환(data())도 좋습니다. 단점은 ① 중간 삽입/삭제가 O(n)(뒤 원소 전부 이동), ② 용량 초과 시 재할당이 일어나 전체 복사/이동 비용이 발생하고 기존 포인터·반복자가 전부 무효화된다는 것, ③ 큰 연속 메모리를 요구한다는 것입니다. 게임에서는 매 프레임 순회하는 데이터(렌더링 패킷, 엔티티 목록)에 벡터가 기본 선택이고, reserve로 재할당을 예방하는 것이 관례입니다.

2. 맵 컨테이너의 장점과 단점

std::map은 레드-블랙 트리 기반 정렬 연관 컨테이너입니다. 장점은 삽입·삭제·탐색이 모두 O(log n)으로 보장되고, 키가 항상 정렬 상태라 순서 순회·범위 검색(lower_bound)이 가능하며, 삽입·삭제에도 다른 원소의 반복자가 유효하게 유지된다는 것입니다. 단점은 노드 기반이라 원소마다 개별 할당 + 포인터 오버헤드가 있고, 노드들이 메모리에 흩어져 캐시 지역성이 나빠 상수 계수가 크다는 것입니다. 정렬이 필요 없다면 평균 O(1)인 unordered_map이 보통 더 빠르므로, “정렬/범위 검색이 필요한가”가 선택 기준입니다.

3. 맵 컨테이너를 어디에서 활용했는가?

[본인 사례로 답변 — 예시 틀] 자체 엔진의 리소스 매니저에서 리소스 키(경로/이름) → 리소스 포인터 매핑에 사용했습니다. 같은 리소스의 중복 로딩을 막기 위해 로드 요청 시 먼저 맵을 탐색하고, 있으면 기존 것을 반환하는 캐시 구조입니다. 또 Shader Reflection 기반 파라미터 시스템에서 셰이더 변수 이름 → 상수버퍼 내 오프셋/슬롯 정보 매핑에 사용해, 문자열 키로 파라미터를 설정하면 올바른 위치에 기록되도록 했습니다. 정렬 순회가 필요 없는 곳은 unordered_map을 썼고, 다만 매 프레임 도는 hot path에서는 문자열 키 조회 자체를 피하도록 핸들/인덱스를 캐싱하는 식으로 조회 비용을 초기화 시점으로 밀었습니다.

4. 맵 컨테이너의 키로 사용할 수 없는 자료형은 무엇이 있는가?

std::map의 키는 비교 가능해야 하므로, operator< (또는 커스텀 비교자)가 정의되지 않은 타입은 키가 될 수 없습니다. 그리고 그 비교는 강한 약순서(strict weak ordering)를 만족해야 하며, 이를 어기는 비교자(예: 로 구현)는 미정의 동작입니다. unordered_map이라면 요구 조건이 달라져 std::hash 특수화와 operator==가 없는 타입은 키가 될 수 없습니다. 둘 다 사용자 정의 구조체는 비교자/해시를 직접 제공하면 키로 쓸 수 있습니다. 추가로 float를 키로 쓰는 것은 문법상 가능하지만 부동소수점 오차로 “같은 키”를 못 찾는 문제가 있어 지양해야 한다고 답하면 좋습니다.

5. 벡터 원소를 포인터/참조로 계속 가리키면 안 되는 이유는?

벡터는 용량이 부족해지면 더 큰 메모리를 새로 할당해 원소를 전부 이동시키고 기존 블록을 해제하기 때문입니다. 이 순간 기존 원소를 가리키던 포인터·참조·반복자는 전부 해제된 메모리를 가리키는 댕글링이 되어, 접근 시 미정의 동작(당장은 멀쩡해 보이다 나중에 터지는 최악의 형태)이 됩니다. 재할당이 아니어도 중간 삽입/삭제는 뒤 원소들을 이동시키므로 같은 문제가 생깁니다. 대응은 ① 원소를 포인터로 장기 보관하지 말고 인덱스나 ID/핸들로 참조, ② 크기가 확정이면 reserve로 재할당 차단, ③ 주소 안정성이 꼭 필요하면 vector<unique_ptr>(노드는 힙에 고정)나 deque·리스트 등 안정성이 보장되는 구조를 선택하는 것입니다.

6. 객체 관리할 때 키에 의한 탐색을 진행할 때 어떤 자료구조를 사용했는가?

[본인 사례로 답변 — 예시 틀] 기본적으로 unordered_map(해시 테이블)을 사용했습니다. 게임 오브젝트 ID → 객체, 리소스 이름 → 리소스처럼 정렬이 필요 없는 키 탐색은 평균 O(1)인 해시가 적합하기 때문입니다. 다만 두 가지를 함께 고려했습니다. 첫째, 매 프레임 반복 조회되는 대상은 탐색 자체를 없애기 위해 첫 조회 후 포인터/인덱스를 캐싱했습니다. 둘째, 순회가 잦은 데이터는 해시맵에 직접 담기보다 벡터에 연속 저장하고 해시맵은 ID → 벡터 인덱스만 관리하는 구조로, 탐색은 O(1)·순회는 캐시 친화적으로 분리했습니다.

7. 해시 충돌이 생겼을 때에는 어떻게 해결하는가?

크게 두 계열입니다. ① 체이닝(separate chaining): 같은 버킷에 들어온 원소들을 연결 리스트 등으로 매달아 두는 방식으로, 구현이 단순하고 부하율에 관대하며 std::unordered_map이 이 방식입니다. ② 개방 주소법(open addressing): 충돌 시 정해진 규칙(선형 탐사, 이차 탐사, 이중 해싱)으로 다른 빈 슬롯을 찾아 넣는 방식으로, 별도 노드 할당이 없어 캐시 친화적이지만 부하율이 높아지면 성능이 급락해 부하율 관리와 삭제 처리(툼스톤)가 까다롭습니다. 공통적으로 부하율이 임계치를 넘으면 리해싱(테이블 확장 후 재배치)이 일어나며, 근본적으로는 좋은 해시 함수로 충돌 자체를 줄이는 것이 우선입니다.

8. STL 리스트는 어떤 자료 구조로 구현된 클래스인가?

std::list는 이중 연결 리스트(doubly linked list)입니다. 각 노드가 데이터와 prev/next 포인터를 가지며, 위치를 아는 상태에서의 삽입·삭제가 O(1)이고, 삽입·삭제 시 다른 원소의 반복자·포인터가 무효화되지 않으며, splice로 노드를 복사 없이 다른 리스트로 옮길 수 있다는 것이 장점입니다. 대신 임의 접근이 불가(O(n))하고, 노드마다 할당 + 포인터 2개의 오버헤드가 있으며, 메모리가 흩어져 캐시 성능이 나빠 실측하면 웬만한 크기까지는 “중간 삽입·삭제조차” 벡터가 더 빠른 경우가 많다는 점까지 언급하면 좋습니다. 참고로 forward_list가 단일 연결 리스트입니다.

9. 배열과 크게 다르지 않음에도 STL 벡터가 가진 장점은 뭔가?

메모리 레이아웃은 배열과 동일하지만(연속 메모리), 그 위에 안전성과 편의가 더해진 것이 가치입니다. ① 동적 크기: 런타임에 자동으로 성장하며 크기를 스스로 압니다(배열은 크기를 따로 들고 다녀야 함). ② RAII: 소멸 시 메모리가 자동 해제되고 예외 상황에서도 누수가 없습니다(new[]/delete[] 수동 관리 불필요). ③ 값 의미론: 복사·이동·비교가 자연스럽고 함수 반환도 안전합니다. ④ STL 생태계: 반복자 기반으로 표준 알고리즘과 결합됩니다. ⑤ at()의 범위 검사 같은 안전장치. 즉 “배열의 성능을 유지하면서 C 배열의 수동 관리 위험을 제거한 것”이 벡터의 본질입니다.

10. push_back과 emplace_back의 차이는 뭔가?

push_back은 완성된 객체를 받아 컨테이너 안으로 복사 또는 이동하고, emplace_back은 생성자 인자들을 완벽 전달(perfect forwarding)받아 컨테이너의 메모리 위에서 객체를 직접 생성합니다. 임시 객체의 생성과 복사/이동을 생략할 수 있으므로, 인자로부터 새로 만드는 경우엔 emplace_back이 유리하거나 최소한 같습니다. 이미 존재하는 객체를 넣을 때는 어차피 복사/이동이라 차이가 없습니다. 주의점으로 emplace는 explicit 생성자도 호출되고 인자가 그대로 전달되므로, vector<unique_ptr>에 raw pointer를 emplace하다 예외가 나면 누수 가능성이 있는 등 암묵 변환 관련 함정이 있어 무조건 emplace가 정답은 아닙니다.

11. 리스트 컨테이너의 sort 함수는 어떤 알고리즘으로 구현되어 있는가?

list::sort는 병합 정렬(merge sort) 계열로 구현됩니다. 이유가 중요합니다. std::sort는 임의 접근 반복자를 요구해 연결 리스트에 쓸 수 없고, 리스트는 노드 포인터만 바꾸면 되므로 원소 이동 없이 분할·병합이 가능한 병합 정렬과 궁합이 좋기 때문입니다. 병합 정렬은 O(n log n)이 보장되고 안정 정렬(stable)이며, 리스트에서는 추가 메모리 없이 구현 가능합니다. 참고로 std::sort는 인트로 정렬(퀵 기반 + 힙 폴백 + 작은 구간 삽입 정렬 하이브리드)이라는 것과 대비해 답하면 좋습니다.

12. 가상 메모리란 무엇인가?

프로세스에게 실제 물리 메모리와 분리된 가상 주소 공간을 제공하는 메모리 관리 기법입니다. 주소 공간을 페이지(보통 4KB) 단위로 나누고, MMU가 페이지 테이블로 가상 주소를 물리 주소로 변환합니다(TLB가 변환 캐시). 효과는 ① 각 프로세스가 독립된 주소 공간을 가져 서로를 침범할 수 없는 보호와 격리, ② 물리 메모리보다 큰 주소 공간 사용 가능(부족분은 디스크 스왑), ③ 물리적으로 흩어진 메모리를 연속처럼 쓸 수 있어 외부 단편화 문제 해소, ④ 실행 파일·DLL의 코드 페이지 공유 같은 효율입니다.

13. 가상 메모리의 단점은 무엇인가?

① 주소 변환 오버헤드: 모든 메모리 접근에 변환이 개입하며, TLB 미스 시 페이지 테이블 워크 비용이 발생합니다. ② 페이지 폴트 비용: 특히 디스크 스왑을 동반하는 폴트는 수 ms 단위로, 나노초 세계의 메모리 접근 대비 수십만 배 느려 게임에서는 프레임 스파이크의 원인이 됩니다. ③ 페이지 테이블 자체의 메모리 오버헤드. ④ 스래싱 위험: 물리 메모리가 부족하면 페이지 교체가 반복되며 시스템이 디스크 I/O에 잠식됩니다. 그래서 게임은 로딩 시점에 필요한 메모리를 미리 확보·터치해두고, 인게임 중 스왑이 일어나지 않는 메모리 예산 관리를 중시합니다.

14. 페이지 폴트란 무엇인가?

접근한 가상 주소의 페이지가 현재 물리 메모리에 매핑되어 있지 않을 때 발생하는 예외(트랩)입니다. OS가 이를 받아 처리하는데, 종류가 나뉩니다. 소프트(minor) 폴트는 페이지가 이미 메모리에 있지만 매핑만 안 된 경우(최초 접근, 공유 페이지)로 비교적 싸고, 하드(major) 폴트는 디스크(스왑, 파일)에서 읽어와야 하는 경우로 매우 비쌉니다. 유효하지 않은 주소 접근이면 보호 위반으로 프로세스가 종료됩니다(access violation). 즉 페이지 폴트는 “요구 페이징이 동작하는 정상 메커니즘”이지만, 빈도와 종류에 따라 성능 문제가 됩니다.

15. 페이지 폴트를 완화하는 법이 무엇인가?

원칙은 “필요한 페이지를 미리, 모아서, 유지하기”입니다. ① 지역성 좋은 메모리 접근 패턴: 자료를 연속 배치하고 순차 접근하면 OS의 프리페칭이 효과를 발휘하고 워킹셋이 작아집니다. ② 사전 로딩/터치: 로딩 화면에서 필요한 리소스를 미리 읽고 접근해 폴트를 로딩 시점으로 몰아둡니다. ③ 메모리 풀·커스텀 할당자: 할당을 큰 블록으로 미리 받아 파편화와 신규 페이지 요구를 줄입니다. ④ 물리 메모리 예산 관리로 스왑 자체를 방지하고, 필요하면 중요 영역을 락(VirtualLock)합니다. ⑤ 워킹셋을 줄이는 데이터 다이어트(압축 텍스처 등)도 근본 대책입니다.

16. 프로세스와 스레드의 차이가 무엇인가?

프로세스는 실행 중인 프로그램의 인스턴스로 독립된 가상 주소 공간, 핸들, 자원을 소유하는 자원 할당의 단위이고, 스레드는 그 프로세스 안에서 코드를 실행하는 CPU 스케줄링의 단위입니다. 같은 프로세스의 스레드들은 주소 공간(코드, 힙, 전역)을 공유하고 스택과 레지스터 컨텍스트만 각자 가집니다. 그래서 스레드는 생성·전환이 가볍고 데이터 공유가 자유로운 대신 동기화 문제(레이스, 데드락)를 짊어지고, 한 스레드의 크래시가 프로세스 전체를 죽입니다. 프로세스 간에는 격리가 보장되는 대신 통신에 IPC가 필요합니다. 게임 클라이언트는 하나의 프로세스 안에서 메인(로직/렌더 제출), 리소스 로딩, 네트워크 수신 등 다중 스레드 구조가 표준입니다.

17. 데드락은 무엇이고 방지 방법은 무엇인가?

둘 이상의 스레드가 서로가 점유한 자원을 기다리며 영원히 진행하지 못하는 상태입니다. 성립 조건 4가지(상호 배제, 점유 대기, 비선점, 순환 대기)가 모두 만족될 때 발생하므로, 방지는 이 중 하나를 깨는 것입니다. 실무에서 가장 효과적인 것은 순환 대기 제거, 즉 모든 락에 전역 순서를 정하고 반드시 그 순서로만 획득하는 락 순서 규약입니다. 그 외 ① 여러 락이 필요하면 std::scoped_lock/std::lock으로 원자적으로 동시 획득, ② try_lock + 타임아웃으로 점유 대기 회피, ③ 락 보유 시간을 최소화하고 락 잡은 채 다른 락을 잡는 코드 자체를 설계에서 배제, ④ 애초에 공유를 줄이는 구조(큐로 데이터 전달, 단일 소유)로 가는 것이 근본 대책입니다.

18. 뮤텍스와 데드락의 상관 관계는 무엇인가?

뮤텍스는 상호 배제를 제공하는 도구인데, 상호 배제는 데드락 4조건의 첫 번째입니다. 즉 뮤텍스를 쓰는 순간 데드락의 필요조건 하나가 성립하며, 여기에 “뮤텍스를 잡은 채 다른 뮤텍스를 기다리는” 코드(점유 대기)와 서로 반대 순서의 획득(순환 대기)이 더해지면 데드락이 완성됩니다. 전형적인 예가 스레드 A는 mutex1→mutex2, 스레드 B는 mutex2→mutex1 순으로 잠그는 경우입니다. 같은 스레드가 같은 뮤텍스를 두 번 잠그는 자기 데드락도 흔한 사례입니다(재귀 뮤텍스가 아닌 한). 요약하면 뮤텍스는 데드락의 원인 그 자체가 아니라 재료이고, 획득 순서 규약과 점유 최소화라는 사용 규율이 함께해야 안전합니다.

19. CPU와 GPU의 차이

설계 목표가 다릅니다. CPU는 지연(latency) 최소화 지향으로, 소수의 강력한 코어에 큰 캐시, 분기 예측, 비순차 실행을 투자해 복잡하고 순차적이며 분기 많은 작업을 빠르게 처리합니다. GPU는 처리량(throughput) 지향으로, 수천 개의 단순한 ALU를 SIMT로 묶어 동일 연산을 대량 데이터에 적용하는 데 특화되어 있으며, 메모리 지연은 캐시가 아니라 대규모 스레드 교체로 숨깁니다. 그래서 게임 로직·AI·컬링처럼 분기 많고 의존성 있는 일은 CPU가, 정점 변환·픽셀 셰이딩처럼 균일한 대량 병렬 작업은 GPU가 맡는 분업이 성립합니다.

20. CPU의 역할은 무엇인가?

프로그램의 명령어를 인출-해석-실행(fetch-decode-execute)하며 시스템 전체를 제어하는 중앙 처리 장치입니다. 산술·논리 연산(ALU), 제어 흐름(분기, 함수 호출), 메모리 접근을 수행하고, 인터럽트를 받아 OS와 함께 프로세스 스케줄링·I/O를 관장합니다. 게임 프레임 기준으로 보면 입력 처리, 게임 로직, 물리, AI, 애니메이션 갱신, 컬링, 드로우콜 제출까지가 CPU의 일이고, 그 결과로 만든 명령을 GPU에 넘기는 지휘자 역할입니다.

21. 64비트 체제에서부터 SIMD 연산이 대두되게 되는 이유는 무엇인가?

정확히는 SIMD 자체는 32비트 시대(SSE)부터 있었지만, x64에서 사실상 기본기가 된 이유가 있습니다. ① x64 ABI는 SSE2 지원을 보장하므로, 컴파일러가 하드웨어 존재 여부를 확인할 필요 없이 부동소수점 연산 자체를 기본적으로 SSE로 생성합니다(구형 x87 FPU 대체). ② 사용 가능한 XMM 레지스터가 8개에서 16개로 늘어 벡터화 효율이 좋아졌습니다. ③ 시대 배경으로, 단일 코어 클럭 성장이 한계에 부딪히며 성능 향상의 방향이 병렬성(멀티코어 + 데이터 병렬 SIMD)으로 넘어간 시기와 맞물립니다. 게임 수학 라이브러리(DirectXMath)가 XMVECTOR를 SIMD 레지스터에 맞춰 설계하고 16바이트 정렬을 요구하는 것이 그 직접적인 결과물입니다.

22. 메모리 외부 단편화를 방지할 수 있는 방법은 무엇이 있는가?

외부 단편화는 빈 메모리 총량은 충분한데 조각나 있어 큰 연속 블록을 할당하지 못하는 문제입니다. 방지책은 ① 페이징(가상 메모리): 고정 크기 페이지로 관리해 연속 물리 메모리가 필요 없게 만드는 시스템 차원의 해법입니다. ② 메모리 풀 / 고정 크기 할당자: 같은 크기 객체를 전용 풀에서 할당하면 어떤 구멍이든 재사용 가능해 단편화가 원천 차단됩니다. ③ 버디 시스템처럼 병합이 쉬운 할당 전략. ④ 애플리케이션 차원에서는 수명별 할당 전략이 효과적인데, 프레임 단위 임시 데이터는 스택/아레나 할당자로 한꺼번에 밀어 해제하고, 레벨 단위 데이터는 레벨 시작 시 큰 블록으로 잡는 식입니다. ⑤ 압축(컴팩션)은 포인터 재배치가 필요해 C++ 게임에서는 현실적으로 어렵고, 핸들 기반 간접 참조 구조에서만 가능합니다.

23. 물리 메모리 중 메모리 접근 속도가 가장 빠른 순으로 말해보시오

레지스터 → L1 캐시 → L2 캐시 → L3 캐시 → 메인 메모리(DRAM) → (SSD/HDD 저장장치) 순입니다. 대략적인 감각으로 레지스터는 사이클 미만1사이클, L1은 약 4사이클, L2는 약 12사이클, L3는 약 3040사이클, DRAM은 100300사이클(수십수백 ns) 수준으로 단계마다 수 배씩 벌어집니다. 이 계층 차이가 곧 “캐시 친화적 데이터 배치(연속 메모리, 순차 접근)가 알고리즘 복잡도만큼 중요하다”는 최적화 원칙의 근거입니다.

24. VRAM의 접근 속도는 어느 정도로 빠를까?

VRAM(GDDR/HBM)은 대역폭 특화 메모리입니다. GDDR6 기준 수백 GB/s로 시스템 DRAM(수십 GB/s)의 몇 배에 달하는 대역폭을 갖지만, 개별 접근의 지연(latency)은 시스템 RAM보다 오히려 깁니다. GPU가 대량 스레드로 지연을 숨기는 구조라 대역폭에 올인한 설계이기 때문입니다. 함께 알아둘 중요한 사실은 CPU↔GPU 간 전송은 PCIe를 거치므로(수십 GB/s 수준 + 왕복 지연) VRAM 내부 접근보다 훨씬 느린 병목이라는 것입니다. 그래서 리소스는 가급적 VRAM에 상주시키고, 매 프레임 CPU→GPU 업로드(상수 버퍼, 다이나믹 버퍼)는 크기를 최소화하며, GPU 결과를 CPU로 읽어오는 readback은 파이프라인 스톨을 유발해 특히 피해야 합니다.

25. 셰이더에 데이터를 전달할 때 왜 가급적 상수 버퍼를 사용하자고 하는가?

상수 버퍼는 “드로우 동안 모든 스레드가 동일하게 읽는 소량 데이터”라는 용도에 하드웨어가 최적화되어 있기 때문입니다. GPU는 상수 버퍼를 전용 캐시·레지스터 영역에 올려 텍스처/일반 버퍼 경로보다 낮은 지연으로 브로드캐스트 접근을 처리하고, 모든 스레드가 같은 주소를 읽는 uniform 접근 패턴에서 가장 효율적입니다. 또한 바인딩·갱신 비용이 정형화되어 있어 드라이버가 다루기 쉽고, 16바이트 정렬 규칙에 맞춰 패킹하면 낭비 없이 전달됩니다. 반대로 대용량·랜덤 접근 데이터는 상수 버퍼의 크기 제한(64KB)과 접근 패턴에 맞지 않으므로 구조화 버퍼(SRV)가 적합합니다. 즉 “스레드 공통 소량 파라미터는 상수 버퍼, 대량 배열 데이터는 구조화 버퍼”로 용도를 구분하는 것이 원칙이고, 갱신 빈도별(프레임/오브젝트/머티리얼 단위)로 상수 버퍼를 분리해 갱신 비용을 줄이는 것이 실무 관례입니다. 제 Shader Reflection 파라미터 시스템도 이 상수 버퍼 슬롯·오프셋 정보를 리플렉션으로 자동 추출해 관리하는 구조였습니다.


2. 기술소개서 & 3D 그래픽스

이 파트는 본인 경험 질문이 섞여 있습니다. 경험 질문은 [답변 틀]을 참고해 부기님 실제 사실로 채워 말하세요. 그래픽스 이론 질문 중 상세 버전은 “3D그래픽스_면접_모범답변.md”에 있으므로 여기서는 실전 구술용 압축 버전으로 정리합니다.

1. 렌더링 패킷 구조를 왜 설계했는가?

[답변 틀 — 본인 구현 사실에 맞게 조정] 게임 로직과 렌더링 사이의 결합을 끊기 위해서입니다. 게임 오브젝트가 자기 Render 함수에서 직접 드로우콜을 날리는 구조는 세 가지 문제가 있었습니다. ① 그리는 순서를 제어할 수 없어 상태 변경 최소화 정렬(셰이더/머티리얼별)과 불투명 앞→뒤, 반투명 뒤→앞 정렬이 불가능하고, ② 렌더링 API가 게임 로직 전반에 스며들어 DLL 구조에서 Engine과 Client의 경계가 무너지며, ③ 컬링·정렬 같은 렌더러 차원의 일괄 처리를 끼워 넣을 지점이 없습니다. 그래서 오브젝트는 “무엇을 그려달라”는 데이터(메시, 머티리얼, 월드 행렬, 렌더 그룹)만 패킷으로 제출하고, 렌더러가 수집 → 컬링 → 정렬 → 일괄 드로우하는 구조로 설계했습니다. 결과적으로 Hi-Z 컬링과 정렬 최적화를 렌더러 한 곳에 자연스럽게 통합할 수 있었고, 이것이 드로우콜 최적화의 기반이 됐습니다. 상용 엔진들이 렌더 커맨드/프록시 개념으로 로직과 렌더링을 분리하는 것과 같은 설계 의도입니다.

2. 왜 팀장이 되었는가? (왜 그 파트를 맡았는가?)

[본인 사실로 답변 — 답변 구조 제안] 이 질문의 의도는 리더십 자랑이 아니라 협업 태도와 역할 인식 확인입니다. 추천 구조: ① 사실 관계(자원했는지, 추대였는지) 한 문장 → ② 맡은 이유(엔진/렌더링 파트를 담당해 전체 구조를 가장 넓게 보는 위치였고, 팀원 간 인터페이스 조율이 필요했다는 식의 기능적 이유) → ③ 팀장으로서 실제로 한 일 한두 가지(작업 분배와 일정 관리보다는, 기술적 의사결정 조율·머지 충돌 방지 규칙·코드 컨벤션 정리처럼 구체적인 것) → ④ 배운 점 한 문장(예: “설득은 말이 아니라 동작하는 예제 코드로 하는 게 빠르다는 걸 배웠습니다”). 겸손하되 수동적으로 들리지 않게, “필요한 역할이 비어 있어 맡았다”는 톤이 안전합니다.

3. 디퍼드 렌더링의 장점이 무엇이길래 사용했는가?

프로젝트 목표가 다수의 동적 광원이 있는 씬이었기 때문입니다. 포워드는 조명 비용이 오브젝트 픽셀 × 라이트 수로 늘고 오버드로우된 픽셀의 조명 계산이 낭비되는 반면, 디퍼드는 지오메트리와 조명을 분리해 화면에 실제 보이는 픽셀에 대해서만, 그 픽셀에 영향을 주는 라이트만 계산합니다. 라이트를 볼륨으로 그려 영향 범위를 한정할 수 있어 광원 수십 개 규모에서도 비용이 통제됩니다. 물론 G-Buffer 대역폭, 반투명 불가, MSAA 제약이라는 대가를 알고 선택했고, 반투명은 포워드 패스로 분리해 하이브리드로 처리했습니다. “장점을 나열해서”가 아니라 “우리 씬의 요구사항이 이것이어서”라는 의사결정 서사로 답하는 것이 중요합니다.

4-1. 렌더링 과정을 추가할 때 디퍼드와 포워드를 어떤 기준으로 선택하겠는가?

기준은 세 가지입니다. ① 반투명 여부: 블렌딩이 필요한 것(파티클, 유리, 이펙트)은 G-Buffer에 담을 수 없으므로 무조건 포워드입니다. ② 조명 모델의 통일성: G-Buffer 파라미터로 표현 가능한 표준 재질은 디퍼드가 효율적이고, 특수한 셰이딩(카툰 외곽선, 헤어, 피부 SSS, 커스텀 NPR)은 픽셀별 파라미터가 달라 포워드가 유연합니다. ③ 라이트 수 대비 지오메트리 비용: 라이트가 많고 오버드로우가 큰 씬이면 디퍼드의 분리 이득이 커집니다. 요약하면 “불투명 표준 재질은 디퍼드, 반투명·특수 재질은 포워드”이고, 실무 렌더러가 대부분 이 하이브리드인 이유입니다.

4-2. G-Buffer가 무엇인가?

디퍼드 렌더링 1패스에서 조명 계산에 필요한 픽셀별 표면 정보를 기록해두는 렌더 타겟 묶음(MRT)입니다. 알베도, 법선, 재질 파라미터(스페큘러/러프니스 등)를 담고, 위치는 따로 저장하지 않고 깊이 버퍼로부터 역산하는 것이 대역폭 절약의 정석입니다. “기하 정보의 스크린 스페이스 캐시”라고 한 줄 요약할 수 있으며, G-Buffer 설계는 곧 대역폭과 표현력 사이의 트레이드오프 설계입니다.

5. 팀 프로젝트 진행 중에 어떤 어려움이 있었는가?

[본인 사실로 답변 — 답변 구조 제안] STAR 구조(상황→과제→행동→결과)로, 기술적 갈등이나 협업 문제 중 해결 과정이 구체적인 것 하나를 고르세요. 좋은 소재 예시: ① 엔진(DLL) 인터페이스를 사이에 둔 팀원과의 의존성 문제 — 클라이언트 팀원이 엔진 내부에 직접 접근하려 해 경계가 무너질 뻔한 것을, 인터페이스 확장과 문서화로 해결. ② 통합 시점의 성능 급락 — 각자 파트는 문제없었지만 합치니 프레임이 떨어졌고, 측정으로 병목을 특정해 해결(89% 최적화 서사와 연결 가능). ③ 8인 프로젝트에서의 머지/컨벤션 충돌 — 규칙 수립으로 해결. 피해야 할 답: “특별한 어려움은 없었다”(관찰력 부족으로 보임), 특정 팀원 험담(협업 리스크로 보임). 결과와 배운 점으로 반드시 마무리하세요.

6. 헝가리안 표기법에 대해서 어떻게 생각하는가?

(실제로 멤버변수는 헝가리안, 지역변수는 카멜을 혼용해 지적받은 질문 — 방어적이지 않게, 근거 있는 소신 + 유연성으로 답하는 것이 좋습니다.)

[추천 답변] 타입을 접두어로 새기는 시스템 헝가리안(dwSize, lpszName)은 현대 C++에서는 단점이 더 크다고 생각합니다. IDE가 타입을 즉시 보여주고, auto·템플릿과 어울리지 않으며, 타입 변경 시 이름이 거짓말을 하게 되기 때문입니다. 다만 제가 사용한 것은 타입이 아니라 스코프를 구분하는 관례로, 멤버 변수에 m_ 계열 접두어를 붙여 함수 안에서 멤버와 지역 변수를 즉시 구분하고 셰도잉 실수를 줄이려는 목적이었습니다. 이건 언리얼이나 여러 사내 컨벤션에서도 유지되는 관례라고 이해하고 있습니다. 지역 변수는 접두어 없이 카멜을 쓴 것도 같은 원칙(스코프 표시가 필요한 곳에만 표시)이었는데, 지적해주신 대로 문서화된 규칙 없이 혼용으로 보였다면 그것 자체가 문제라고 생각합니다. 표기법의 본질은 특정 스타일의 우열이 아니라 팀 전체의 일관성이므로, 입사하면 팀 컨벤션을 따르는 것이 당연하다고 생각합니다.

7. 프러스텀 컬링이 중요하게 된 이유는 무엇인가?

씬 규모가 화면에 보이는 양을 압도하게 되면서입니다. 오픈월드처럼 씬 전체 오브젝트 수가 수만 개여도 카메라에 실제로 담기는 것은 일부인데, 보이지 않는 것까지 드로우콜을 제출하면 CPU 제출 비용과 GPU 정점 처리가 전부 낭비입니다. 프러스텀 컬링은 그 낭비를 가장 저렴한 비용(바운딩 볼륨 vs 6평면 검사)으로, 파이프라인의 가장 이른 지점(제출 전)에서 잘라내는 1차 필터입니다. 제 프로젝트에서도 컬링 이전에는 맵 전체가 매 프레임 제출되어 드로우콜 13,000의 원인이 됐고, 프러스텀 컬링 + 오클루전 컬링(Hi-Z) + 컬링 단위 세분화(메시 아일랜드 분할)의 3단 구성으로 해결했습니다. “일찍 버릴수록 싸다”는 렌더링 최적화의 대원칙을 보여주는 대표 기법입니다.

8. 렌더링 파이프라인이란 무엇인가?

3D 정점 데이터가 2D 화면 픽셀이 되기까지의 표준화된 처리 단계입니다. D3D11 기준 IA → VS → (테셀레이션/GS) → 래스터라이저 → PS → OM 순이고, 크게 “정점 단위 기하 처리 → 래스터화 → 픽셀 단위 처리”로 요약됩니다. 프로그래머블 단계(셰이더)와 고정 기능 단계가 교차하며, 각 단계가 스트림처럼 병렬로 겹쳐 실행되는 구조입니다. (상세 단계별 역할은 그래픽스 문서 1~9번 참고)

9. 짐벌락은 무엇이고 어떻게 해결할 수 있는가?

오일러 각에서 축별 회전을 순차 적용하는 구조 때문에, 중간 축이 ±90도가 되면 첫 축과 셋째 축의 회전이 겹쳐 자유도 하나를 잃는 현상입니다. 카메라가 정확히 위를 볼 때 yaw와 roll이 같은 회전이 되어버리는 것이 대표 예입니다. 해결은 회전을 순차 축 회전이 아닌 단일 표현으로 다루는 것으로, 쿼터니언이 표준 해법입니다(축+각을 4개의 수로 표현, 짐벌락 없음, SLERP 보간 가능). 회전 행렬로 누적하는 방법도 짐벌락은 없지만 보간과 오차 복구에서 쿼터니언이 우위입니다. 실무 절충으로, FPS 카메라처럼 pitch를 ±90도 근처에서 클램프해 문제 지점을 회피하는 방법도 함께 언급하면 좋습니다.

10. 벡터의 내적은 무엇인가?

두 벡터의 대응 성분 곱의 합이며, 기하학적으로 |a||b|cosθ입니다. “두 벡터가 얼마나 같은 방향인가”를 스칼라 하나로 알려주는 연산으로, 부호로 앞뒤 판정, cos 임계값 비교로 시야각 판정, N·L로 디퓨즈 조명, 투영·반사 벡터 계산의 기초가 됩니다. 곱셈·덧셈만으로 각도 정보를 얻는 가장 값싼 도구라 게임 수학에서 사용 빈도가 가장 높습니다.

11. 깊이 테스트란 무엇이고 왜 필요한가?

픽셀을 기록하기 전에 깊이 버퍼의 기존 깊이와 비교해, 더 가까우면 기록하고 멀면 버리는 가시성 판정입니다. 필요한 이유는 은면 제거, 즉 “가까운 것이 먼 것을 가린다”를 그리는 순서와 무관하게 픽셀 단위로 정확히 보장하기 위해서입니다. 깊이 테스트가 없으면 나중에 그린 것이 무조건 위에 덮여, 오브젝트 정렬만으로는 교차하는 지오메트리를 올바르게 처리할 수 없습니다. 하드웨어는 이를 셰이딩 전에 앞당겨(Early-Z) 가려질 픽셀의 셰이딩 비용까지 절약합니다.

12. 깊이값을 정규화하는 과정은 왜 필요한가?

깊이 버퍼가 고정 포맷·유한 정밀도의 저장소이기 때문입니다. 뷰 공간 깊이는 씬마다 nearfar 범위가 제각각이므로, 이를 01 표준 범위로 정규화해야 어떤 씬이든 동일한 버퍼 포맷과 하드웨어 비교 로직으로 처리할 수 있습니다. 원근 투영에서는 이 정규화가 1/z 형태의 비선형 매핑이 되어 정밀도가 near 쪽에 편중된다는 것, 그래서 near 설정과 Reversed-Z가 중요해진다는 것까지 이어 말하면 이해 깊이를 보여줄 수 있습니다.

13. 투영 행렬의 역할은 무엇인가?

카메라가 보는 3D 영역(절두체)을 클리핑과 화면 매핑이 가능한 표준 상자(NDC)로 변환하는 행렬입니다. FOV·종횡비·near/far가 행렬 성분을 결정하고, 원근 투영의 핵심 트릭은 뷰 공간 깊이를 w에 실어두어 이후 원근 나눗셈에서 “멀수록 작게”가 만들어지게 하는 것입니다. 즉 원근감이라는 비선형 효과를 행렬(선형)과 나눗셈의 2단계로 분리 구현하는 장치입니다.

14. 원근 투영과 직교 투영의 원리와 차이가 무엇인가?

원근 투영은 시야가 절두체이고, 깊이를 w에 복사한 뒤 w로 나누어 거리에 반비례해 축소하므로 원근감이 있습니다(사람의 눈, 3D 월드 카메라). 직교 투영은 시야가 직육면체이고 w가 항상 1이라 나눗셈 효과가 없어 거리와 무관하게 크기가 유지됩니다(UI, 2D, 디렉셔널 라이트 섀도우 맵). 한 줄로 요약하면 “차이의 본질은 w 나눗셈의 유무”입니다.


3. C++ 관련

1. 보편 참조란 무엇인가?

템플릿 인자 추론이나 auto 문맥에서의 T&&로, 전달된 인자가 lvalue면 lvalue 참조로, rvalue면 rvalue 참조로 동작하는 참조입니다(공식 용어는 forwarding reference). 원리는 참조 축약(reference collapsing) 규칙으로, T가 lvalue 참조로 추론되면 & + &&가 &로 접힙니다. 존재 이유는 완벽 전달(perfect forwarding)입니다. 인자의 값 범주(lvalue/rvalue)를 보존한 채 다른 함수로 넘기려면 T&& 로 받고 std::forward로 전달해야 하며, emplace_back, make_unique 같은 표준 시설이 전부 이 메커니즘 위에 있습니다. 주의점은 형태가 rvalue 참조와 똑같아 헷갈린다는 것으로, 추론이 일어나는 T&&만 보편 참조이고 구체 타입의 Widget&&는 그냥 rvalue 참조입니다.

2. 생성자에서 가상함수를 쓰면 안 되는 이유는 무엇인가?

생성자 실행 중에는 객체가 아직 그 파생 타입이 아니기 때문입니다. C++는 기반 클래스부터 순서대로 생성하는데, 기반 클래스 생성자 실행 중에는 vtable 포인터가 기반 클래스 것으로 설정되어 있어, 이 시점의 가상 호출은 파생 클래스 재정의가 아니라 기반 클래스 버전으로 결정됩니다. 의도(파생 버전 호출)와 다르게 동작하고, 순수 가상 함수라면 미정의 동작/크래시입니다. 파생 클래스 멤버가 아직 초기화되지 않은 상태에서 파생 함수가 실행되면 위험하므로, 언어가 의도적으로 이렇게 설계한 것입니다. 소멸자에서도 같은 이유로(파생 부분이 먼저 파괴됨) 동일한 문제가 있습니다.

3. 생성자에서 가상함수를 쓰지 않고 가상함수로 안전하게 초기화하는 법은 없는가?

2단계 초기화(two-phase initialization) 패턴이 대표적입니다. 생성자는 멤버를 안전한 기본 상태로만 만들고, 객체가 완성된 후 별도의 Init()/Initialize() 가상 함수를 호출해 파생 클래스별 초기화를 수행하는 방식입니다. 문제는 호출을 잊을 수 있다는 것이므로, 팩토리 함수와 결합해 강제하는 것이 안전합니다. 즉 생성자를 외부에서 직접 못 쓰게 하고(protected), static Create() 팩토리가 “생성 → Init 호출 → 반환(실패 시 null)“을 책임지는 구조입니다. 게임 엔진에서 GameObject/Component 초기화가 대부분 이 패턴인 이유이며, 제 엔진에서도 객체 생성은 팩토리를 통해 Initialize까지 보장하는 구조를 사용했습니다. 그 외 파생 클래스에 필요한 정보를 생성자 인자로 내려받는 설계 변경, CRTP로 정적 다형성을 쓰는 방법도 상황에 따라 대안이 됩니다.

4. 템플릿 비대화란 무엇이고 어떻게 완화하겠는가?

템플릿이 인스턴스화되는 타입 조합마다 별도의 코드가 생성되어, 바이너리 크기와 컴파일 시간이 부풀고 명령어 캐시 효율이 떨어지는 문제(code bloat)입니다. vector, vector, vector<MyType*>가 각각 완전한 코드 사본을 만드는 식입니다. 완화 방법은 ① 타입 무관 로직 분리: 템플릿에서 타입에 의존하지 않는 부분을 비템플릿 기반 클래스나 일반 함수로 빼고, 템플릿은 얇은 타입 안전 래퍼로 만듭니다(Effective C++의 처방으로, 포인터 컨테이너를 void* 기반 공통 구현 + 타입 캐스팅 래퍼로 만드는 것이 고전 예). ② 명시적 인스턴스화로 생성 지점을 한 번역 단위로 모아 중복 생성을 줄입니다. ③ 불필요한 타입 파라미터 제거(예: 비타입 파라미터로 크기를 받던 것을 런타임 인자로). ④ 근본적으로는 그 코드가 정말 템플릿이어야 하는지, 다형성이나 std::span 같은 타입 소거 수단으로 충분한지 설계 단계에서 검토하는 것입니다.

5. shared_ptr로 관리하는 객체 내부에서 this 포인터 사용을 지양해야 하는 이유는?

내부에서 this로 새 shared_ptr를 만들면(shared_ptr(this)) 기존 제어 블록과 무관한 두 번째 제어 블록이 생겨, 참조 카운트가 독립적으로 돌다가 같은 객체를 이중 해제(double delete)하기 때문입니다. this를 콜백·다른 객체에 raw pointer로 넘기는 경우도 위험한데, shared_ptr 쪽 수명이 끝나면 그쪽은 댕글링 포인터가 됩니다. 올바른 해법은 std::enable_shared_from_this를 상속하고 shared_from_this()로 기존 제어 블록을 공유하는 shared_ptr를 얻는 것입니다. 단 생성자 안에서는 아직 shared_ptr 소유가 성립하기 전이라 shared_from_this() 호출이 예외를 던진다는 것, 그리고 콜백에 수명을 연장하고 싶지 않다면 weak_ptr을 잡고 사용 시점에 lock으로 유효성을 확인하는 패턴(순환 참조 방지)까지 언급하면 완성도 높은 답변이 됩니다.

6. 부동소수점의 오차는 왜 생기는가?

float가 실수를 유한 비트(부호 1 + 지수 8 + 가수 23)로 근사하기 때문입니다. 0.1처럼 이진법으로 무한소수인 값은 정확히 저장할 수 없어 표현 오차가 생기고, 연산 결과가 표현 가능한 값이 아니면 매번 반올림되어 연산 오차가 누적됩니다. 또 표현 간격이 값의 크기에 비례해 벌어지므로 큰 수 + 작은 수에서 작은 수의 비트가 소실됩니다(원점에서 먼 오픈월드 좌표의 지터가 대표 증상). 그래서 실수 비교는 ==가 아닌 epsilon 비교로 하고, 큰 좌표는 원점 이동으로, 네트워크 결정성이 필요하면 고정소수점으로 대응합니다.

7. call by value와 call by reference의 차이는 무엇인가?

값 전달은 인자의 복사본을 만들어 넘기므로 함수 안의 변경이 원본에 영향을 주지 않고, 참조 전달은 원본의 별칭을 넘기므로 함수가 원본을 직접 읽고 쓸 수 있습니다. 선택 기준은 두 가지입니다. ① 원본 수정 의도가 있으면 참조(T&), ② 수정하지 않는 큰 객체는 복사 비용을 피하려 const 참조(const T&)로 받는 것이 기본 관례입니다. 반대로 int, float, 포인터 같은 작은 타입은 값 전달이 오히려 저렴하고 안전합니다(레지스터 전달, 별칭 걱정 없음). 현대 C++에서는 여기에 이동 의미론이 더해져, 소유권을 넘기는 인자는 값으로 받고 std::move하는 패턴도 있다는 것까지 말하면 좋습니다.

8. 클래스 멤버 데이터를 은닉화/캡슐화하는 이유는 무엇인가?

데이터와 그 데이터를 유효하게 유지하는 규칙(불변식)을 한 곳에서 강제하기 위해서입니다. 멤버가 public이면 아무 코드나 아무 값으로 바꿀 수 있어, “체력은 0~최대치 사이” 같은 불변식을 클래스가 보장할 수 없고, 값이 잘못됐을 때 원인 지점을 특정할 수도 없습니다. 접근을 메서드로 한정하면 ① 유효성 검사와 부수 처리(체력 변경 시 사망 판정, UI 갱신 이벤트)를 단일 경로에 두고, ② 내부 표현을 바꿔도(자료구조 교체, 지연 계산) 외부 코드가 깨지지 않는 인터페이스-구현 분리가 성립하며, ③ 디버깅 시 변경 지점이 한정되어 추적이 쉽습니다. 요약하면 캡슐화는 숨기기 자체가 목적이 아니라 변경 가능 지점의 통제입니다.

9. 게터/세터로 접근 지정자가 무의미해지는 것을 지양해야 하는 이유는?

모든 멤버에 기계적으로 GetX/SetX를 뚫어주면 사실상 public 멤버와 다름없어, 캡슐화가 형식만 남고 실질을 잃기 때문입니다. 문제는 두 가지입니다. ① 불변식 보호 실패: 검증 없는 세터는 아무 값이나 허용하므로 은닉의 목적이 사라집니다. ② 설계 신호: 외부가 게터로 값을 꺼내 판단·조작 후 세터로 되돌려 넣는 코드는, 그 로직이 데이터를 소유한 클래스 안에 있어야 한다는 신호입니다(“묻지 말고 시켜라”). 예를 들어 hp = GetHP() - dmg; SetHP(hp)보다 TakeDamage(dmg)가 옳은 설계로, 클램프·사망 판정·이벤트를 클래스가 책임질 수 있습니다. 그래서 원칙은 “세터는 기본이 아니라 예외”이며, 꼭 필요한 조작만 의미 있는 행위 메서드로 노출하고, 읽기도 필요한 것만 열어주는 것입니다.


면접 활용 팁

  • 경험 질문 우선 준비: 기술소개서 파트의 1(렌더링 패킷), 2(팀장), 5(팀 어려움), 6(표기법)은 정답이 없는 본인 서사 질문입니다. 틀만 외우지 말고 실제 사실을 채워 소리 내어 말하는 연습이 필요합니다. 특히 6번처럼 지적을 받았던 질문은 방어가 아니라 “근거 있는 소신 + 팀 컨벤션 우선” 태도로 답하는 것이 핵심입니다.
  • STL 단골 콤보: 1→5→9번(벡터 장단점 → 재할당과 포인터 무효화 → 배열 대비 가치)은 한 흐름이고, 2→4→7번(맵 → 키 조건 → 해시 충돌)도 세트입니다.
  • C++ 콤보: 2→3번(생성자 가상함수 → 2단계 초기화/팩토리)은 반드시 이어서 나오고, 부기님 엔진의 팩토리 패턴 경험과 연결하면 강합니다. 8→9번(캡슐화 → 게터세터 남용)도 세트로, “묻지 말고 시켜라” 한 마디를 준비해두세요.
  • 25번(상수 버퍼): Shader Reflection 시스템 이야기로 자연스럽게 연결되는 질문이므로, 갱신 빈도별 상수 버퍼 분리까지 언급해 기술소개서와 CS 지식을 한 답변에서 잇는 연습을 해두면 좋습니다.

11. CS 기본 질문

운영체제

  1. 프로세스 상태 전이를 설명해주세요.
  2. 스케줄링이란 무엇인가요?
  3. 선점형과 비선점형 스케줄링의 차이는 무엇인가요?
  4. 가상 메모리를 사용하는 이유는 무엇인가요?
  5. 페이징과 세그멘테이션의 차이는 무엇인가요?
  6. 페이지 교체 알고리즘에는 무엇이 있나요?
  7. 캐시 메모리를 사용하는 이유는 무엇인가요?
  8. 지역성의 원리를 설명해주세요.
  9. 시간 지역성과 공간 지역성의 차이는 무엇인가요?
  10. 캐시 미스에는 어떤 종류가 있나요?

데이터베이스

  1. 관계형 데이터베이스란 무엇인가요?
  2. 기본 키와 외래 키의 차이는 무엇인가요?
  3. 인덱스를 사용하는 이유는 무엇인가요?
  4. 인덱스를 많이 만들면 왜 문제가 되나요?
  5. 트랜잭션의 ACID를 설명해주세요.
  6. 게임 아이템 지급에 트랜잭션이 필요한 이유는 무엇인가요?
  7. 중복 보상 지급을 방지하려면 어떻게 해야 하나요?
  8. 낙관적 락과 비관적 락의 차이는 무엇인가요?
  9. 클라이언트가 DB에 직접 접근하면 안 되는 이유는 무엇인가요?
  10. 캐시와 데이터베이스 간 불일치를 어떻게 처리하나요?

보안·안정성

  1. 버퍼 오버런이란 무엇인가요?
  2. 정수 오버플로우가 보안 문제가 될 수 있는 이유는 무엇인가요?
  3. 사용자 입력을 신뢰하면 안 되는 이유는 무엇인가요?
  4. 클라이언트 변조를 완전히 방지할 수 있나요?
  5. 암호화와 해싱의 차이는 무엇인가요?
  6. 대칭키와 비대칭키 암호화의 차이는 무엇인가요?
  7. HTTPS와 TLS의 역할은 무엇인가요?
  8. 비밀번호를 평문으로 저장하면 안 되는 이유는 무엇인가요?
  9. 체크섬은 무엇인가요?
  10. 파일 무결성 검사는 어떻게 하나요?

12. 아웃게임·라이브 서비스 직무 질문

이 공고에서는 이 영역이 기술 기본기 다음으로 중요합니다.

아웃게임 시스템

  1. 아웃게임 콘텐츠란 무엇이라고 생각하나요?
  2. 인게임 콘텐츠와 아웃게임 콘텐츠의 차이는 무엇인가요?
  3. 인벤토리 시스템을 설계해보세요.
  4. 아이템의 획득, 장착, 해제, 삭제 흐름을 설명해주세요.
  5. 기간제 아이템은 어떻게 관리하나요?
  6. 클라이언트 시간으로 기간제를 판정하면 안 되는 이유는 무엇인가요?
  7. 아이템 데이터와 아이템 인스턴스 데이터를 어떻게 구분하나요?
  8. 아이템 고유 ID가 필요한 이유는 무엇인가요?
  9. 동일 종류 아이템 여러 개를 어떻게 구분하나요?
  10. 스택형 아이템과 비스택형 아이템을 어떻게 표현하나요?
  11. 인벤토리 정렬과 필터링은 어디에서 처리하나요?
  12. UI와 데이터 모델을 어떻게 분리하나요?
  13. 서버 응답이 늦을 때 UI는 어떻게 처리하나요?
  14. 아이템 구매 버튼을 연속 클릭하면 어떻게 방지하나요?
  15. 구매 요청은 성공했지만 응답이 유실되면 어떻게 처리하나요?
  16. 중복 구매나 중복 지급을 어떻게 방지하나요?
  17. 우편함 시스템을 설계해보세요.
  18. 친구 시스템을 설계해보세요.
  19. 차단 목록 시스템을 설계해보세요.
  20. 클랜 시스템을 설계해보세요.
  21. 랭킹 시스템의 클라이언트 구조를 설계해보세요.
  22. 채팅 시스템에서 채널을 어떻게 구분하나요?
  23. 금칙어 필터링은 클라이언트와 서버 중 어디에서 처리해야 하나요?
  24. 프로필, 칭호, 휘장 시스템을 어떻게 구성하나요?
  25. 매칭룸에서 플레이어 정보를 어떤 순서로 로딩하나요?
  26. 데이터 일부만 로딩됐을 때 화면은 어떻게 보여주나요?
  27. 화면 전환 중 네트워크 응답이 도착하면 어떻게 처리하나요?
  28. UI가 닫힌 뒤 콜백이 도착하면 어떻게 해야 하나요?
  29. 비동기 요청의 생명주기를 어떻게 관리하나요?
  30. 아웃게임 시스템에서 상태 머신을 사용할 수 있는 사례를 말해주세요.

라이브 서비스

  1. 라이브 서비스에서 가장 중요하다고 생각하는 것은 무엇인가요?
  2. 신규 개발과 라이브 유지보수의 차이는 무엇인가요?
  3. 라이브 환경에서 재현되지 않는 버그를 어떻게 조사하나요?
  4. 로그에는 어떤 정보를 남겨야 하나요?
  5. 로그 레벨을 어떻게 구분하나요?
  6. 크래시가 발생하면 어떤 순서로 분석하나요?
  7. 콜스택만으로 원인을 찾지 못하면 어떻게 하나요?
  8. 덤프 파일에서 무엇을 확인하나요?
  9. 라이브 패치에서 하위 호환성이 중요한 이유는 무엇인가요?
  10. 클라이언트와 서버 버전이 다르면 어떻게 처리하나요?
  11. 패킷 프로토콜을 변경할 때 무엇을 고려해야 하나요?
  12. 설정 파일이나 테이블 데이터 변경으로 기능을 제어하는 이유는 무엇인가요?
  13. Feature Flag란 무엇인가요?
  14. 특정 국가에서만 기능을 활성화하려면 어떻게 설계하나요?
  15. 국가별 UI, 텍스트, 시간대 차이는 어떻게 처리하나요?
  16. 현지화 문자열 길이가 달라질 때 UI는 어떻게 대응하나요?
  17. 한국어, 영어, 중국어 문자열 처리에서 주의할 점은 무엇인가요?
  18. 국가마다 다른 법률이나 결제 정책은 어떻게 분리하나요?
  19. 핫픽스와 정규 패치의 차이는 무엇인가요?
  20. 롤백 가능한 기능을 어떻게 설계하나요?
  21. 장애 발생 시 기능을 즉시 비활성화하려면 어떤 구조가 필요한가요?
  22. 데이터가 잘못 배포됐을 때 클라이언트는 어떻게 방어해야 하나요?
  23. 서버 응답 필드가 누락되거나 값이 잘못된 경우 어떻게 처리하나요?
  24. 예외 상황에서도 게임이 종료되지 않도록 하려면 어떻게 하나요?
  25. 실패를 사용자에게 어떻게 안내하나요?
  26. 네트워크 재시도는 무조건 많이 하면 좋은가요?
  27. 지수 백오프란 무엇인가요?
  28. 동일 장애가 반복될 때 로그 폭증을 어떻게 막나요?
  29. 운영툴과 클라이언트 사이의 데이터 일관성을 어떻게 유지하나요?
  30. 이전 버전 사용자와 신규 버전 사용자가 동시에 접속할 때 무엇을 고려해야 하나요?

13. 프로젝트 기반 질문

실제 면접에서는 이론 질문보다 프로젝트를 파고드는 경우가 많습니다.

자체 엔진

  1. 자체 엔진에서 본인이 구현한 핵심 구조는 무엇인가요?
  2. 엔진과 게임 로직을 어떻게 분리했나요?
  3. 객체 생명주기를 어떻게 관리했나요?
  4. Scene과 Level 구조를 어떻게 구성했나요?
  5. Component 패턴을 선택한 이유는 무엇인가요?
  6. 상속 중심 구조와 Component 구조의 장단점은 무엇인가요?
  7. 렌더링 객체를 어떤 기준으로 분류했나요?
  8. 렌더 큐를 어떻게 구성했나요?
  9. 투명 객체 정렬은 어떻게 처리했나요?
  10. 리소스 중복 로딩은 어떻게 방지했나요?
  11. 리소스 매니저의 키는 무엇을 사용했나요?
  12. 텍스처나 메시를 언제 해제했나요?
  13. 장면 전환 시 리소스를 어떻게 처리했나요?
  14. 렌더링 최적화 전후 수치는 어떻게 측정했나요?
  15. 드로우콜을 13,000에서 2,000으로 줄인 구체적인 방법은 무엇인가요?
  16. 드로우콜 감소로 CPU와 GPU 중 어느 쪽이 개선됐나요?
  17. 컬링 정밀도를 4배 개선했다는 의미는 무엇인가요?
  18. 컬링 오브젝트 수와 프레임 시간은 어떻게 변했나요?
  19. SRV 일괄 바인딩을 구현한 이유는 무엇인가요?
  20. 상태 변경 횟수를 줄이기 위해 어떤 정렬을 적용했나요?

연출 시퀀스 시스템

  1. 연출 시퀀스 관리자를 만든 이유는 무엇인가요?
  2. 기존 구조에서 어떤 문제가 있었나요?
  3. 각 단계를 어떻게 표현했나요?
  4. 단계 완료 조건은 어떻게 판단했나요?
  5. 인터럽트 허용 여부는 어떻게 정의했나요?
  6. 중단된 연출의 상태를 어떻게 복구했나요?
  7. 카메라, UI, 후처리, 타임스케일을 어떻게 통합했나요?
  8. 동시에 연출 요청이 여러 개 들어오면 어떻게 처리했나요?
  9. 우선순위가 필요한 연출은 어떻게 처리했나요?
  10. 연출 도중 객체가 파괴되면 어떻게 했나요?
  11. 화면 전환이나 레벨 종료 시 시퀀스를 어떻게 정리했나요?
  12. Sequence와 State Machine의 차이는 무엇인가요?
  13. Command 패턴을 적용할 수 있었나요?
  14. Undo 또는 Rollback 구조와 유사한 점은 무엇인가요?
  15. 이 구조를 아웃게임 UI 연출에 어떻게 적용할 수 있나요?

워커 스레드

  1. 워커 스레드에서 어떤 작업을 처리했나요?
  2. 메인 스레드에서 처리하지 않은 이유는 무엇인가요?
  3. 작업 큐는 어떻게 구성했나요?
  4. 결과를 메인 스레드에 어떻게 전달했나요?
  5. 객체가 작업 도중 파괴될 가능성은 어떻게 처리했나요?
  6. 락은 어디에 사용했나요?
  7. 데드락 가능성은 어떻게 줄였나요?
  8. 스레드를 몇 개 사용했으며 기준은 무엇이었나요?
  9. 성능 개선을 어떻게 측정했나요?
  10. 작업보다 스레드 오버헤드가 더 커지는 경우는 언제인가요?

14. AI 개발 도구 관련 질문

  1. AI 개발 도구를 어떤 업무에 사용해봤나요?
  2. AI가 생성한 코드를 그대로 사용했나요?
  3. 생성된 코드의 정확성을 어떻게 검증했나요?
  4. AI 코드에서 자주 발견한 문제는 무엇인가요?
  5. AI가 만든 코드의 보안 위험을 어떻게 검토하나요?
  6. 라이선스나 저작권 문제는 어떻게 고려하나요?
  7. AI를 사용하면 개발자가 구현 원리를 몰라도 된다고 생각하나요?
  8. AI 도구 사용 전후 개발 과정은 어떻게 달라졌나요?
  9. AI에게 어떤 단위로 작업을 요청하는 것이 효과적이었나요?
  10. 큰 기능을 한 번에 생성시키는 방식의 문제는 무엇인가요?
  11. AI가 프로젝트 규칙을 따르게 하려면 어떻게 하나요?
  12. AI가 생성한 코드를 테스트하는 절차를 설명해주세요.
  13. AI 도구를 라이브 서비스 운영에 어떻게 활용할 수 있나요?
  14. 로그 분석이나 장애 분류에 AI를 사용할 수 있을까요?
  15. 사내 코드나 사용자 데이터를 외부 AI에 입력할 때 어떤 문제가 있나요?
  16. AI를 이용한 시스템 구축이라는 업무를 어떻게 해석했나요?
  17. AI 에이전트와 단순 챗봇의 차이는 무엇인가요?
  18. AI가 잘못된 결과를 반환했을 때 시스템적으로 어떻게 방어하나요?
  19. Human-in-the-loop가 필요한 이유는 무엇인가요?
  20. AI 활용 경험에서 가장 크게 실패했던 사례는 무엇인가요?

15. 설계형 질문

  1. 친구 목록 시스템을 설계해보세요.
  2. 채팅 시스템을 설계해보세요.
  3. 인벤토리 시스템을 설계해보세요.
  4. 기간제 아이템 시스템을 설계해보세요.
  5. 출석 보상 시스템을 설계해보세요.
  6. 우편함과 첨부 보상 시스템을 설계해보세요.
  7. 랭킹 UI 시스템을 설계해보세요.
  8. 플레이어 프로필 로딩 시스템을 설계해보세요.
  9. 로비에서 수십 명의 유저 정보를 로딩하는 구조를 설계해보세요.
  10. 게임 시작 전 필요한 리소스를 비동기로 로딩하는 구조를 설계해보세요.
  11. 네트워크 요청 취소 기능을 설계해보세요.
  12. 화면 전환과 네트워크 응답이 엇갈리는 문제를 해결해보세요.
  13. 다국가 이벤트 시스템을 설계해보세요.
  14. 국가별로 다른 이벤트 기간을 어떻게 처리하나요?
  15. 서버 점검 공지 시스템을 설계해보세요.
  16. 서버 상태에 따라 특정 메뉴를 막는 시스템을 설계해보세요.
  17. 데이터 테이블 기반 콘텐츠 시스템을 설계해보세요.
  18. 설정 데이터의 버전 관리는 어떻게 하나요?
  19. 잘못된 설정 데이터가 들어와도 크래시가 나지 않도록 설계해보세요.
  20. 아웃게임 UI 스택을 설계해보세요.
  21. ESC 키로 가장 위의 UI부터 닫히게 구현해보세요.
  22. 팝업이 중복으로 열리지 않도록 설계해보세요.
  23. 동일 요청이 연속으로 들어오는 것을 방지해보세요.
  24. UI 애니메이션 도중 다른 화면으로 이동하면 어떻게 처리하나요?
  25. 시스템을 테스트하기 쉽게 만들려면 어떻게 의존성을 분리하나요?

16. 인성·상황 질문

  1. 게임 클라이언트 개발자로 전환한 이유는 무엇인가요?
  2. 이전 마케팅·기획 경험이 개발 업무에 어떤 도움이 되나요?
  3. 왜 스마일게이트인가요?
  4. 왜 크로스파이어인가요?
  5. 왜 라이브 개발 직무인가요?
  6. 왜 아웃게임 시스템 개발에 관심이 있나요?
  7. 크로스파이어를 직접 플레이해봤나요?
  8. 크로스파이어의 아웃게임에서 개선하고 싶은 부분은 무엇인가요?
  9. 본인의 가장 강한 기술은 무엇인가요?
  10. 본인의 가장 부족한 기술은 무엇인가요?
  11. 모르는 질문이 나오면 어떻게 대응하나요?
  12. 버그를 해결하지 못했을 때 어떻게 했나요?
  13. 팀원과 기술적 의견이 충돌하면 어떻게 하나요?
  14. 본인이 작성한 코드 때문에 일정이 지연되면 어떻게 하나요?
  15. 다른 사람이 작성한 레거시 코드를 수정해야 한다면 어떻게 접근하나요?
  16. 문서가 없는 시스템을 분석해야 한다면 어떻게 하나요?
  17. 재현되지 않는 버그를 맡으면 어떻게 하나요?
  18. 긴급 패치가 필요한 상황에서 무엇을 우선시하나요?
  19. 품질과 일정이 충돌하면 어떻게 판단하나요?
  20. 새로운 엔진이나 도구를 빠르게 학습한 경험을 말해주세요.
  21. AI에 많이 의존해 개발한 경험을 어떻게 평가하나요?
  22. AI 사용 과정에서 원리를 몰랐던 문제를 어떻게 극복했나요?
  23. 입사 후 가장 먼저 보완해야 할 역량은 무엇인가요?
  24. 입사 후 3개월 동안 어떤 방식으로 적응하겠나요?
  25. 5년 후 어떤 개발자가 되고 싶나요?