[말랑 퀴즈] 26/09/15 문제
카테고리: MallangQuiz
태그: Quiz
제 말랑말랑 퀴즈 생성기는 이곳에서 확인하실 수 있습니다.
말랑말랑 퀴즈 📝
날짜: 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.h와 Person을 쓰는 파일이 몽땅 다시 컴파일된다. 그래서 #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.
Date와Address는 전방 선언으로 해결되지만,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++ 한 줄은 기계어 수준에서 세 연산으로 쪼개진다. 그 세 연산을 쓰고, messageCount가 10인 상태에서 두 스레드가 동시에 이 함수를 호출했는데 결과가 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
댓글남기기