[말랑 퀴즈] 26/09/15 문제

게시:     수정

카테고리:

태그:

제 말랑말랑 퀴즈 생성기는 이곳에서 확인하실 수 있습니다.

말랑말랑 퀴즈 📝

날짜: 2026-09-15 문제 수: 6문제


Q1. 🔴 어려움

아래 헤더는 Person 클래스를 정의하면서 멤버 타입의 헤더들을 전부 #include 하고 있다.

// Person.h
#include <string>
#include "date.h"
#include "address.h"

class Person {
public:
    Person(const std::string& name, const Date& birthday, const Address& addr);
    std::string name() const;
    std::string birthDate() const;
    std::string address() const;
private:
    std::string theName;
    Date        theBirthDate;
    Address     theAddress;
};

이러면 Date의 구현부를 살짝만 고쳐도 Person.hPerson을 쓰는 파일이 몽땅 다시 컴파일된다. 그래서 #include를 빼고 전방 선언으로 바꿔 보았다.

namespace std { class string; }     // string 전방 선언
class Date;                          // 전방 선언
class Address;                       // 전방 선언

class Person {
    ...
private:
    std::string theName;
    Date        theBirthDate;
    Address     theAddress;
};

그런데 이 시도는 실패한다. 이 상황에 대한 설명으로 가장 올바른 것은?

  • A. 전방 선언만으로도 충분하다. 컴파일러는 Person 객체의 크기를 링크 타임에 알아내므로, 멤버 타입의 정의는 컴파일 시점에 필요하지 않다. 실패한 이유는 단지 date.h를 링크 대상에서 빠뜨렸기 때문이다.
  • B. DateAddress는 전방 선언으로 해결되지만, std::string이 문제다. namespace std { class string; }은 올바른 선언이므로, 실패 원인은 <string>을 빼면 name()의 반환형을 알 수 없게 되는 것뿐이다. <string>만 다시 포함하면 된다.
  • C. 실패 이유는 두 가지다. ① string은 클래스가 아니라 typedef 로 정의되어 있어 class string;처럼 선언할 수 없고, ② 컴파일러가 Person p; 를 위한 공간을 잡으려면 Person의 크기를 알아야 하는데, 그러려면 멤버인 Date·Address정의가 필요하다. 해법은 pimpl 관용구 — 구현부를 별도 클래스로 빼고 그것을 가리키는 포인터만 갖는 것이다. 포인터는 크기가 고정이라 가리키는 타입의 선언만 있으면 된다.
  • D. pimpl의 두 방식 중 인터페이스 클래스는 가상 함수를 쓰지 않으므로 호출 비용이 없고, 핸들 클래스는 vtable 포인터를 가져야 해서 객체마다 메모리가 커진다. 따라서 성능이 중요하면 인터페이스 클래스를 골라야 한다.

출처: game_dev/cpp/chapter5/2025-10-07-cpp_5_31.md

📝 내 선택:


Q2. 🟡 보통

온라인 게임에서 플레이어 정보를 어디에 저장하느냐에 대한 문제다.

(1) 온라인 게임은 플레이어 정보를 클라이언트가 아니라 서버에 저장한다. 클라이언트에 저장하면 생기는 문제 두 가지를 쓰시오. 그리고 그럼에도 클라이언트에 저장해도 되는 데이터의 예를 두 가지 드시오.

(2) 서버는 보통 그 정보를 데이터베이스에 넣는다. DB를 쓰는 이유 중 하나가 트랜잭션이다.

  • ㉠ 사용자 1이 사용자 2에게 100만 원을 송금하는 과정은 “1의 계좌에서 100만 원 차감 → 2의 계좌에 100만 원 추가” 두 단계다. 1단계만 끝난 뒤 시스템이 중단되면 무슨 일이 벌어지는가?
  • ㉡ 트랜잭션의 정의를 쓰고, 그것이 보장하는 성질의 이름을 쓰시오.

(3) DB를 쓰는 또 다른 이유 둘에 대해 답하시오.

  • ㉠ DB의 락(Lock) 은 멀티스레드 프로그래밍의 무엇과 비슷한 개념이며, 어떤 문제를 막는가?
  • ㉡ DB가 장애 내성을 갖는 것은 어떤 장치 덕분인가? 그 장치가 시스템 중단 후 무엇을 가능하게 하는지 쓰시오.

출처: server/game_server/4/2026-02-11-game_server_4_7.md

📝 내 풀이:


Q3. 🟢 쉬움

빈칸을 채우시오.

MongoDB를 수평 확장(샤딩)하면 서버가 세 종류로 나뉜다.

애플리케이션 서버
      │
   ① ___              ← 애플리케이션 서버에서 명령을 받아 적절한 샤드로 송수신한다
      │
      ├── mongod (② ___)     ← 샤드 목록, 컬렉션 샤딩 키 정보, 청크 분포 등
      │                          샤드 클러스터 전체의 메타데이터를 저장한다
      │
      ├── mongod (③ ___ #1)  ← 도큐먼트를 나눠서 보관한다
      ├── mongod (③ ___ #2)
      └── mongod (③ ___ #3)
  • 도큐먼트를 어느 ③ 에 저장할지는 ④ ___ 를 해시 함수에 넣어 나온 정수 값으로 정한다. (예: John → 해시 함수 → 1~4)
  • ④ 가 없는 질의 명령을 받으면, ① 은 어느 ③ 로 보낼지 알 수 없으므로 모든 ③ 에 ⑤ ___ 한다.
  • ② 가 죽으면 모든 샤드가 죽는다. 그래서 별도 서버 기기에 두고, 예비 데이터베이스를 갖추는 ⑥ ___ (replica set)를 하도록 되어 있다.

출처: server/game_server/8/2026-03-13-game_server_8_8.md

📝 내 답:


Q4. 🟡 보통 · 📌 오답노트 (2번째 복습)

채팅 서버의 메시지 처리 코드다. 여러 워커 스레드가 이 함수를 동시에 호출한다.

int            messageCount = 0;
Array<Message> history;          // 내부에 [배열 포인터]와 [크기 정보] 두 멤버를 가진다

void OnChatReceived(Message m) {   // 여러 스레드에서 동시에 호출된다
    history.Add(m);
    messageCount++;
}

(1) messageCount++ 한 줄은 기계어 수준에서 세 연산으로 쪼개진다. 그 세 연산을 쓰고, messageCount10인 상태에서 두 스레드가 동시에 이 함수를 호출했는데 결과가 12가 아니라 11이 되는 실행 순서를 단계별로 적으시오. 이때 무엇이 사라진 것인지 한 문장으로 쓰시오.

(2) history.Add(m) 쪽에서는 (1)보다 더 위험한 일이 벌어질 수 있다.

  • Array의 내부 구조를 근거로, 어떤 상황에서 무슨 일이 벌어지는지 설명하시오. (힌트: 원소를 넣다가 공간이 부족해지면)
  • ㉡ (1)의 문제와 (2)의 문제는 각각 어떤 성질의 위반인가? 두 성질의 이름을 쓰고, 어느 쪽이 어느 쪽인지 짝지으시오.

(3) 이 두 성질을 보장하는 기술을 통틀어 무엇이라 하며, 대표적인 기법 세 가지는 무엇인가? 그리고 위 코드에서 어느 구간을 어떻게 보호해야 하는지 한 줄로 쓰시오.

출처: server/game_server/1/2025-05-30-game_server_1_5.md 오답노트: wn-2de515c0 · 2026-09-09 최초 오답 · 이번이 2번째 복습

📝 내 풀이:


Q5. 🟡 보통

게임 서버에서 MongoDB에 도큐먼트를 넣는 의사코드다.

client = new client("mongodb://localhost:27017");     // ①
db     = client["mydb"];                              // ②
coll   = db["mycollection"];                          // ③

doc = new BsonDocument                                // ④
{
    { "name", "John" },
    { "address", new BsonDocument
        {
            { "City",   "Seoul" },
            { "Street", "Nambu-street" }
        }
    }
};

coll.insert(doc);                                     // ⑤

이 코드에 대한 설명으로 가장 올바른 것은?

  • A. ②와 ③의 순서는 바뀌어도 된다. 컬렉션 이름은 클러스터 안에서 고유하므로 client["mycollection"]으로 먼저 컬렉션을 얻은 뒤, 그 컬렉션이 속한 DB를 나중에 고를 수 있다.
  • B. ④의 BSON은 JSON과 역할이 같지만 평면 구조라서 값 자리에 다른 도큐먼트를 넣을 수 없다. 따라서 위 코드의 "address" 부분처럼 계층적인 데이터는 별도 컬렉션으로 분리하고 참조 키로 연결해야 한다.
  • C. ①②③은 엔드포인트 → DB 인스턴스 이름 → 컬렉션 이름 순으로 범위를 좁혀 들어가는 구조다. ④의 BSON(Binary JSON)은 { "Key", "Value" } 구조가 JSON과 같지만 트리 구조를 사용하므로, "address"의 값 자리에 또 다른 BsonDocument중첩할 수 있다. 이 도큐먼트를 ⑤ insert로 컬렉션에 넣는다.
  • D. ⑤에서 insert에 넘기는 것은 BSON 객체가 아니라 JSON 문자열이다. MongoDB API는 문자열을 받아 서버 쪽에서 파싱하므로, ④처럼 객체를 만드는 과정은 불필요하며 coll.insert("{ name: 'John' }")으로 충분하다.

출처: server/game_server/8/2026-03-14-game_server_8_9.md

📝 내 선택:


Q6. 🟡 보통

다음 명제가 참(O)인지 거짓(X)인지 판단하라.

단일 서버 구조는 클라이언트 — 방화벽 — 스위치/라우터 — 서버 — 데이터베이스로 이어진다. 이 중 라우터/스위치, 방화벽, CPU, 스토리지 가운데 하나라도 한계를 넘어서면 분산 처리가 필요하며, 처음에는 네 가지 중 몇 가지에만 분산을 적용해도 괜찮다.

어디를 분산할지는 성능 분석으로 정한다. 예컨대 게임 서버의 CPU 사용량이 높다면 거기서 멈추지 않고 CPU 안에서 어느 함수가 많은 처리량을 차지하는지까지 더 자세히 분석한다. 그리고 분산에 앞서 코드 최적화를 먼저 진행하고, 그래도 부족할 때 비로소 분산한다.

분산 단위는 데이터 단위 분산기능 단위 분산으로 나뉘고, 게임 로직의 분산 처리 방식은 동기 분산 처리 / 비동기 분산 처리 / 데이터 복제 및 로컬 처리 세 가지다.

출처: server/game_server/9/2026-03-18-game_server_9_4.md

📝 내 답: O / X


MallangQuiz 카테고리 내 다른 글 보러가기

댓글남기기