sword_game/interview.md

7 KiB

C++ 게임 서버 기술 면접 예상 질문 리스트

이 프로젝트를 포트폴리오로 제출했을 때, 면접관(특히 C++ 시니어 개발자)이 물어볼 만한 기술적 질문들을 정리했습니다. 본인의 답변 방향을 정하는 데 참고하세요.


1. Modern C++ & Memory Management

Q1. std::thread 대신 C++20의 std::jthread를 사용한 이유는 무엇인가요?

  • 키워드: RAII, 자동 join, 협력적 중단(Stop Token).
  • 답변 포인트: 일반 thread는 소멸 시 join()이나 detach()가 호출되지 않으면 프로그램이 종료되지만, jthread는 소멸 시 자동으로 join을 수행해 자원 누수를 막아줍니다. 또한 stop_token을 통해 외부에서 스레드를 안전하게 중단시키는 표준화된 방법을 제공하기 때문입니다.

Q2. std::unique_ptrstd::shared_ptr를 사용한 기준은 무엇인가요?

  • 키워드: 소유권(Ownership), 참조 카운팅, 성능.
  • 답변 포인트: 객체의 소유권이 명확한 경우(NetworkService 내부의 io_context 등)는 성능 오버헤드가 적은 unique_ptr를 사용했습니다. 반면 Session과 같이 여러 곳(매니저, 핸들러 등)에서 참조되고 생명주기가 유동적인 경우 shared_ptr를 사용하여 안전하게 관리했습니다.

2. Networking & Boost.Asio

Q3. Boost.Asio의 Proactor 패턴에 대해 설명하고, Reactor 패턴과의 차이점을 말씀해 주세요.

  • 키워드: 비동기 I/O, 완료 통지(Completion Notification), 추상화.
  • 답변 포인트: Asio는 OS가 비동기 작업을 완료한 후 핸들러를 호출해주는 Proactor 패턴을 따릅니다. Linux의 epoll은 본래 "준비되었음"을 알리는 Reactor 방식이지만, Asio는 이를 내부적으로 래핑하여 사용자에게 "데이터 수신이 완료되었음"을 알리는 Proactor 방식으로 추상화하여 제공합니다. 이를 통해 개발자는 OS별 로우 레벨 차이(Linux epoll vs Windows IOCP)에 신경 쓰지 않고 일관된 비동기 비즈니스 로직에만 집중할 수 있습니다.

Q4. 클라이언트 종료 시 socket_.shutdown()을 명시적으로 호출한 이유는 무엇인가요?

  • 키워드: 블로킹(Blocking), EOF, 깨우기.
  • 답변 포인트: 본 프로젝트에서 클라이언트는 서버 기능을 검증하기 위한 테스트 도구로 설계되었습니다. 따라서 서버만큼 복잡한 비동기 로직을 적용하기보다, 배경 스레드에서 동기식 read를 호출하는 단순한 구조를 선택했습니다. 이때 발생하는 스레드 블로킹 문제는 shutdown()을 통해 소켓 통로를 강제로 닫아 read 함수가 즉시 리턴되도록 유도하여 해결했습니다. 이는 서버의 고성능 비동기 로직 개발에 더 집중하기 위한 의도적인 설계 선택(Trade-off)입니다.

Q5. 왜 NetworkServiceDatabaseManager가 동일한 io_context를 공유하도록 설계했나요?

  • 키워드: 리소스 효율성, 통합 종료(Unified Shutdown), 스레드 풀 공유.
  • 답변 포인트: 이전에는 시그널 관리용과 네트워크 서비스용 컨텍스트가 분리되어 있었으나, 시스템 리소스를 더 효율적으로 사용하기 위해 하나로 통합했습니다. 이를 통해 단일 스레드 풀이 모든 비동기 작업(네트워크, DB, 시그널)을 유연하게 처리할 수 있으며, main_context.stop() 호출 한 번으로 서버의 모든 서비스가 안정적으로 종료되는 'Graceful Shutdown'을 구현하기 용이해졌습니다. 외부에서 io_context를 주입받는(DI) 방식을 사용하여 결합도도 낮추었습니다.

3. Concurrency & Synchronization

Q6. DatabaseManager에서 사용한 strand는 어떤 역할을 하며, 왜 필요한가요?

  • 키워드: 비동기 순차 실행, Race Condition 방지, Mutex 대안.
  • 답변 포인트: 여러 스레드가 동시에 io_context.run()을 수행할 때, 공유 자원(DB 연결 등)에 접근하는 비동기 작업들이 섞이지 않도록 보장합니다. mutex를 사용해 스레드를 멈춰 세우는 대신, 작업 자체를 순차적으로 큐잉하여 컨텍스트 스위칭 비용을 줄이면서 스레드 안전성을 확보했습니다.

Q7. 왜 코루틴(co_await)을 사용하여 프로젝트를 구성했나요?

  • 키워드: 가독성, 비동기 상태 머신, Callback Hell 방지.
  • 답변 포인트: 비동기 프로그래밍은 콜백이 깊어질수록 코드 흐름을 파악하기 힘들어집니다. C++20 코루틴을 사용하면 논리적인 흐름은 동기 코드처럼 직관적으로 작성하면서도, 실제로는 Non-blocking으로 동작하여 성능과 유지보수성이라는 두 토끼를 다 잡을 수 있기 때문입니다.

Q8. Session::SendPacket을 템플릿으로 구현한 이유는 무엇인가요?

  • 키워드: 코드 중복 제거(DRY), 유지보수성, 타입 안전성.
  • 답변 포인트: 패킷을 보낼 때마다 헤더와 페이로드를 수동으로 조립하는 로직이 여러 핸들러에서 중복되어 나타났습니다. 이를 템플릿 메서드로 추상화하여 중복 코드를 제거했을 뿐만 아니라, 개발자가 바이너리를 직접 다루며 발생할 수 있는 실수(오타, 크기 계산 오류 등)를 원천 차단했습니다. 이는 코드 가독성을 높이고 새로운 패킷을 추가할 때의 생산성을 크게 향상시킵니다.

4. Architecture & Scalability

Q9. 현재 구조에서 수천 명의 동시 접속자를 처리해야 한다면 어떤 부분을 개선하시겠습니까?

  • 키워드: DB Connection Pool, 분산 서버, 로그 버퍼링.
  • 답변 포인트: 현재는 단일 DB 연결을 공유하므로 Connection Pool을 도입해 DB 병목을 줄이겠습니다. 또한 로직 처리를 담당하는 GameServer와 유저 데이터 주권을 가진 DB Proxy 서버를 분리하고, Redis 같은 캐시 계층을 두어 DB 부하를 분산시키는 구조로 제안하겠습니다.

5. Deployment & DevOps

Q10. Docker Multi-stage 빌드를 사용한 이유는 무엇인가요?

  • 키워드: 빌드 환경 분리, 이미지 경량화, 보안.
  • 답변 포인트: 빌드 시에만 필요한 컴파일러(g++, cmake)와 실행에 필요한 런타임 라이브러리를 분리하여 최종 이미지 크기를 수백 MB 단위로 줄였습니다. 이는 보안상 공격 표면을 줄이고 네트워크 전송 및 배포 속도를 높이는 데 유리합니다.

팁: 경력자로서 강조할 태도

  • "PHP와 C++의 가교 역할": PHP의 빠른 생산성과 C++의 높은 성능/자원 제어 능력을 모두 이해하고 있음을 어필하세요.
  • "문제 해결 과정": 특히 클라이언트 종료 데드락을 shutdown()으로 해결했던 경험처럼, 단순히 결과물이 아니라 **"왜 이런 문제가 생겼고 이를 C++ 시스템 콜 수준에서 어떻게 해결했는지"**를 설명할 때 시니어로서의 역량이 돋보입니다.