[말랑 퀴즈] 26/10/13 해답

게시:     수정

카테고리:

태그:

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

말랑말랑 퀴즈 — 해답지 ✅

날짜: 2026-10-13 문제 수: 6문제 총점: 51.3/60 (86점) ✅ 잘했어요

📊 채점 결과

문항 유형 난이도 결과 점수
Q1 OX 🟢 쉬움 🔺 부분 정답 9 / 10
Q2 객관식 🟡 보통 ⭕ 정답 10 / 10
Q3 빈칸 채우기 🟢 쉬움 🔺 부분 정답 8.8 / 10
Q4 서술형 🔴 어려움 🔺 부분 정답 6.5 / 10
Q5 OX 🟡 보통 🔺 부분 정답 7 / 10
Q6 📌 빈칸 채우기 🟢 쉬움 ⭕ 정답 10 / 10
합계       51.3 / 60

총평

객관식(Q2)과 오답노트 복습 문제(Q6)를 만점으로 풀었고, OX 문제 두 개도 판단은 모두 맞혔습니다. 반면 Q4 swap 문제는 (3)에서 “std에 새 템플릿을 추가하는 것은 표준 위반(미정의 동작)”이라는 핵심을 놓쳤고, (4)도 ADL 근거가 빠졌습니다. Q5는 결론(X)은 맞았지만 이유를 remove_reference와 주소 값 문제로 잘못 기억하셨습니다. 실제 이유는 0/NULL이 정수 타입으로 추론되기 때문입니다.


Q1. 🟢 쉬움 — 🔺 9/10

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

클래스 안에 string& 참조자 멤버와 const T 상수 멤버가 있더라도, 컴파일러는 복사 대입 연산자를 암시적으로 생성해 주므로 p = s; 같은 대입은 별도 정의 없이 그대로 컴파일된다.

📝 내 답: X. 복사 대입 연산자를 암시적 생성해주긴 한다. 각각 참조와 상수이기에 재할당해줄수 없으며, 컴파일 에러가 발생한다. 이 경우, 직접 복사 대입 연산자를 구현해주어야 한다.

정답: X

해설:

참조자 멤버나 상수 멤버가 있으면 컴파일러는 복사 대입 연산자를 만들지 못해 컴파일을 거부한다. C++의 참조자는 원래 참조하던 것과 다른 객체를 참조할 수 없고, 이미 초기화된 상수의 값은 바꿀 수 없기 때문이다. 이런 경우에는 복사 대입 연산자를 직접 만들어 주어야 한다. (복사 대입 연산자를 private로 선언한 기본 클래스에서 파생된 경우에도 암시적 생성이 불가능하다.)

🔍 피드백: 판단(X)이 맞아 7점입니다. 참조자·상수는 재할당할 수 없다는 이유와 직접 구현해야 한다는 해결책도 정확합니다. 다만 “암시적 생성해주긴 한다”는 표현은 정확하지 않습니다. 이 경우 컴파일러는 복사 대입 연산자를 생성하지 못하고, 그 결과 대입 시점에 컴파일 에러가 납니다. 근거 3점 중 2점입니다.

출처: game_dev/cpp/chapter2/2025-04-22-cpp_2_5.md


Q2. 🟡 보통 — ⭕ 10/10

문제: 게임 로직을 여러 서버에 나누는 비동기 분산 처리(서버 1이 서버 2에 명령만 보내고 결과를 기다리지 않는 방식)에 대한 설명으로 가장 올바른 것은?

Player_Attack(player, monster) {
	player.bullet--;
	otherServer.DamageCharacter(player.id, monster.id, 10);
}

DamageCharacter(callerServer, attacker, character, damage) {
	character.hitPoint -= damage;
	if(character.hitPoint < 0) {
		callerServer.GiveItem(attacker.id, gold, 30);
		DeleteEntity(character, 10sec);
	}
}
  • A. 서버 1은 서버 2의 처리 결과를 받을 때까지 대기하며, 그동안 플레이어 정보에 잠금을 건다.
  • B. 잠금으로 인한 병목이 없지만, 반환 값을 주고받을 수 없어서 모든 로직을 이 방식으로 구현하기는 어렵거나 불가능하다.
  • C. 서버 1과 서버 2가 각각 플레이어와 몬스터의 원본과 사본을 모두 가지고, 로직은 각 서버가 알아서 처리한다.
  • D. 분산 처리 전의 코드와 완전히 동일한 코드로 구현할 수 있다.

📝 내 선택: B

정답: B

해설:

비동기 분산 처리는 결과를 기다리지 않고 다음 할 일을 시작하므로 잠금으로 인한 병목이 없다. 대신 반환 값을 주고받을 수 없어(반환 값 없는 함수만으로 구현하는 것과 유사) 서로 일방적인 통보를 하는 방식으로 구현해야 하고, 모든 로직을 이 방식으로 구현하기는 어렵거나 불가능하다.

  • A: 대기와 잠금이 필요한 것은 동기 분산 처리(동기식 명령 처리법)다.
  • C: 원본과 사본을 가지고 로컬에서 처리하는 것은 데이터 복제 기반 로컬 처리다.
  • D: 분산 처리 전 코드와 동일한 것도 데이터 복제 기반 로컬 처리다.

🔍 피드백: B를 정확히 골랐습니다. A(대기·잠금 없음)와 C(데이터 분산 처리와 혼동)의 근거도 맞습니다. D는 “함수 시그니처가 바뀌어야 한다”고 쓰셨는데, 이 보기는 정확히는 데이터 복제 기반 로컬 처리에 해당하는 설명입니다. 비동기 방식에서는 코드를 서버 간 명령 호출 형태로 바꿔야 한다는 점에서 방향은 맞습니다.

출처: server/game_server/9/2026-03-19-game_server_9_6.md


Q3. 🟢 쉬움 — 🔺 8.8/10

문제: 빈칸을 채우시오.

UML 다이어그램에 대한 설명이다.

  • 시퀀스 다이어그램에서 상호 작용의 주체인 ‘객체’(예: 클라이언트, 서버)는 ① ___ 모양의 도형으로 표현된다. (도형 이름 두 단어)
  • 시퀀스 다이어그램에서 객체들이 주고받는 ‘메시지’(예: Chat(), ShowChat())는 ② ___로 표현된다. (한 단어)
  • 액티비티 다이어그램에서 하나하나의 ‘활동 상태’는 ③ ___ 도형으로 표현된다. (도형 이름 한 단어)
  • 액티비티 다이어그램에서 “해커”인 경우와 “실제 사용자”인 경우처럼 경우를 나누어 흐름을 갈라지게 하는 구조를 ④ ___(이)라고 한다. (두 단어)

📝 내 답: 네모, 선, 원, 분기

정답:

① 네모 상자 ② 화살표 ③ 타원 ④ 조건 분기

해설:

시퀀스 다이어그램은 객체를 네모 상자로, 메시지를 화살표로 표현한다. 액티비티 다이어그램은 활동 상태를 타원 도형으로 표현하고, 조건 분기문으로 “해커 / 실제 사용자” 같은 경우를 나눌 수 있다.

🔍 피드백: 빈칸당 2.5점입니다. ①(네모), ③(원≈타원), ④(분기≈조건 분기)는 같은 개념으로 인정했습니다. ②는 “선”이라고 쓰셨는데, 메시지는 방향이 있는 화살표로 그립니다. 방향이 메시지를 보내는 쪽과 받는 쪽을 나타내므로 절반(1.25점)만 드렸습니다.

출처: server/game_server/5/2026-02-12-game_server_5_1.md


Q4. 🔴 어려움 — 🔺 6.5/10

문제: C++에서 swap을 직접 만든 타입(pImpl 포인터를 가진 Widget)에 맞게 지원하는 방법에 대해 답하시오.

(1) Widget이 클래스 템플릿이 아닐 때, std::swap을 Widget에 대해 완전 특수화하여 그 안에서 swap(a.pImpl, b.pImpl);을 직접 호출하면 컴파일되지 않는다. 그 이유와 해결 방법을 쓰시오.

(2) Widget이 클래스 템플릿(Widget<T>)일 때, std::swap을 Widget<T>에 대해 부분 특수화하는 코드는 왜 동작하지 않는가?

(3) 아래 코드는 컴파일은 되지만 문제가 있다. 어떤 문제인지 쓰시오.

namespace std {
	template<typename T>
	void swap(Widget<T>& a, Widget<T>& b) {
		a.swap(b);
	}
}

(4) 클래스 템플릿인 경우의 올바른 해결 방법에서, 비멤버 swap은 어디에 두어야 하는가? 그리고 사용자 코드에서 swap을 호출할 때 std::swap(obj1, obj2); 대신 어떻게 호출해야 하며, 그 이유는 무엇인가?

📝 내 풀이:

(1) pImpl은 private 접근 지정자에 있기 때문이다. 클래스 내에 swap 멤버 함수를 두고, 완전 특수화한 swap 함수가 클래스 내의 swap 멤버 함수를 호출하도록 구현한다.

(2) C++에서는 클래스 템플릿에 대한 부분 특수화는 허용하지만, 함수 템플릿에 대해서는 허용하지 않는다.

(3) 2에서 말했던 함수 템플릿에 대한 부분 특수화이기 떄문이다.

(4) 같은 네임스페이스 안에 두어야 한다. using std::swap; swap(a, b); 와 같이 사용한다. C++이 swap을 탐색하는 순서 때문이다.

정답:

(1) pImpl이 Widget의 private 멤버라 특수화된 std::swap에서 접근할 수 없기 때문이다. 해결: Widget에 public 멤버 함수 swap(내부에서 using std::swap; swap(pImpl, other.pImpl);)을 추가하고, std::swap<Widget> 특수화에서는 a.swap(b)를 호출한다.

(2) std::swap은 함수 템플릿이고, C++은 클래스 템플릿에 대해서만 부분 특수화를 허용하며 함수 템플릿의 부분 특수화는 허용하지 않기 때문이다 (컴파일 에러).

(3) std 네임스페이스에 새로운 템플릿(함수)을 추가하는 것은 표준 위반이라서, 컴파일은 되더라도 실행 결과가 미정의 동작(UB)이다. (std 안의 기존 템플릿에 대한 완전 특수화는 허용되지만 새 템플릿 추가는 안 된다.)

(4) Widget과 WidgetImpl이 들어 있는 사용자 네임스페이스(예: WidgetStuff) 안에 비멤버 swap(내부에서 멤버 swap 호출)을 오버로딩으로 둔다. 사용자 코드에서는 using std::swap;을 선언한 뒤 swap(obj1, obj2);로 호출해야 한다. std::swap(...)처럼 네임스페이스를 명시하면 std::swap만 호출되어 사용자 정의 swap을 쓸 수 없기 때문이며, 위처럼 호출해야 인자 기반 탐색(ADL)으로 사용자 네임스페이스의 swap이 선택되고 없을 때만 std::swap이 쓰인다.

🔍 피드백:

  • (1) 2.5/2.5 — private 접근 문제와 멤버 swap + 특수화 버전의 호출 구조를 정확히 답했습니다.
  • (2) 2.5/2.5 — 함수 템플릿은 부분 특수화가 허용되지 않는다는 점을 정확히 짚었습니다.
  • (3) 0/2.5 — (2)와 같은 이유라고 쓰셨는데, 이 코드는 부분 특수화가 아니라 std에 새 템플릿을 오버로딩으로 추가한 것입니다(부분 특수화였다면 컴파일 에러가 났을 것입니다). 문제에서 “컴파일은 된다”고 했으니 컴파일 에러 이유는 답이 될 수 없습니다. 핵심은 std에 새 템플릿을 추가하는 것이 표준 위반이라 미정의 동작이라는 점입니다.
  • (4) 1.5/2.5 — using std::swap; swap(a, b); 호출 방식은 맞았고, 비멤버 swap을 Widget이 있는 사용자 네임스페이스에 둔다는 방향도 맞습니다(“같은 네임스페이스”가 무엇과 같은지 명시하면 더 정확합니다). 다만 이유를 “탐색 순서”로만 쓰셔서, std::swap(...)처럼 네임스페이스를 명시하면 사용자 정의 swap이 무시된다는 점과 인자 기반 탐색(ADL)이 사용자 네임스페이스의 swap을 찾아 준다는 점이 빠졌습니다.

출처: game_dev/cpp/chapter4/2025-07-02-cpp_4_25.md


Q5. 🟡 보통 — 🔺 7/10

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

널 포인터를 의미하는 0이나 NULL을 보편 참조(T&&)를 쓰는 완벽 전달 함수 템플릿 fwd에 넘기면, 컴파일러는 이를 포인터 타입으로 추론하므로 f를 직접 호출했을 때와 똑같이 널 포인터로 완벽하게 전달된다.

📝 내 답: X. forward 함수의 인자에는 remove_reference가 있는데, 주소 값이 애초에 없는 값이라 에러가 발생했던 것으로 기억한다.

정답: X

해설:

0이나 NULL을 템플릿에 넘기면 컴파일러는 포인터 타입이 아니라 정수 타입(보통 int)으로 추론한다. 그래서 널 포인터로서 완벽하게 전달되지 못한다. 우회책은 0/NULL 대신 nullptr을 사용하는 것이다.

🔍 피드백: 판단(X)이 맞아 7점입니다. 근거는 틀렸습니다. std::forward의 remove_reference나 “주소 값이 없다”는 문제가 아니라, 템플릿 인자 추론 단계에서 0/NULL이 포인터가 아니라 정수(int)로 추론되는 것이 원인입니다. 그래서 f가 포인터를 기대하면 정수가 전달되어 타입이 맞지 않습니다. 해결책은 nullptr을 쓰는 것입니다. 근거 3점은 드리지 못했습니다.

출처: game_dev/moderncpp/5/2026-09-29-moderncpp_5_30.md


Q6. 🟢 쉬움 · 📌 오답노트 — ⭕ 10/10

문제: 빈칸을 채우시오.

게임 서버가 MongoDB 샤드 클러스터(수평 확장 구조)를 사용한다. 각 구성과 동작에 대한 설명이다.

  • 애플리케이션 서버에서 받은 명령을 적절한 샤드로 보내고 결과를 받아 오는 라우터 역할의 프로그램은 ① ___이다. (영문 소문자 프로그램 이름)
  • 샤드 목록, 컬렉션의 샤딩 키 정보, 청크 분포 같은 샤드 클러스터 전체의 메타데이터를 저장하는 서버는 ② ___이다. (영문 이름)
  • 어떤 도큐먼트를 어느 샤드에 저장할지는, 샤드 키를 ③ ___ 함수에 넣어 나온 정수 값으로 정한다. (한 단어)
  • ②가 죽으면 모든 샤드가 죽으므로, 예비 데이터베이스를 두는 ④ ___을(를) 하도록 되어 있다. (복제본 집합(replica set)으로 구성하는 방식을 가리키는 한 단어)

📝 내 답: mongos, configdb, 해시, 복제

정답:

① mongos ② ConfigDB ③ 해시 ④ 이중화

해설:

mongos는 애플리케이션 서버의 명령을 받아 적절한 샤드로 송수신한다. ConfigDB(mongod)는 샤드 목록, 샤딩 키 정보, 청크 분포 등 클러스터 전체 메타데이터를 저장하며, 죽으면 모든 샤드가 죽기 때문에 이중화(replica set)를 한다. 각 샤드에 저장할 도큐먼트는 샤드 키를 해시 함수에 넣어 나온 정수 값으로 정한다.

🔍 피드백: 네 칸 모두 맞았습니다. ④는 “복제”라고 쓰셨는데, 복제본 집합(replica set)으로 예비 데이터베이스를 두는 방식이라는 같은 개념이라 정답으로 인정했습니다. 용어로는 “이중화”가 정식 표현입니다.

출처: server/game_server/8/2026-03-13-game_server_8_8.md 오답노트: wn-c830945a · 2026-09-15 최초 오답 · 이번이 2번째 복습


📌 복습 포인트

  • Q1 암시적 복사 대입 연산자 — 참조자·상수 멤버가 있으면 컴파일러가 복사 대입 연산자를 생성하지 못하고 컴파일 에러가 남 → game_dev/cpp/chapter2/2025-04-22-cpp_2_5.md
  • Q3 UML 도형 — 시퀀스 다이어그램의 메시지는 화살표(방향 있음) → server/game_server/5/2026-02-12-game_server_5_1.md
  • Q4 swap 지원 — std에 새 템플릿 추가는 미정의 동작, 사용자 네임스페이스 비멤버 swap + using std::swap + ADL → game_dev/cpp/chapter4/2025-07-02-cpp_4_25.md
  • Q5 완벽 전달과 널 포인터 — 0/NULL은 정수로 추론되므로 nullptr을 써야 함 → game_dev/moderncpp/5/2026-09-29-moderncpp_5_30.md

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

댓글남기기