[말랑 퀴즈] 26/09/18 해답

게시:     수정

카테고리:

태그:

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

말랑말랑 퀴즈 — 해답지 ✅

날짜: 2026-09-17 문제 수: 6문제 총점: 35.7/60 (60점) 🟡 복습이 필요해요

📊 채점 결과

문항 유형 난이도 결과 점수
Q1 서술형 🔴 어려움 🔺 부분 정답 3.5 / 10
Q2 객관식 🟡 보통 ⭕ 정답 10 / 10
Q3 서술형 · 📌 오답노트 🔴 어려움 🔺 부분 정답 8.2 / 10
Q4 빈칸 🟢 쉬움 🔺 부분 정답 9.4 / 10
Q5 객관식 🟡 보통 ⭕ 정답 10 / 10
Q6 OX 🟢 쉬움 ❌ 오답 0 / 10
합계       35.7 / 60

총평

객관식 두 문제는 만점이고, 그 안의 근거가 특히 좋습니다. Q2에서 IaaS와 SaaS를 헷갈리지 않고 “D는 PaaS 설명만 맞고 IaaS 설명은 SaaS 이야기”라고 정확히 짚으셨고, Q5에서도 프라우드넷·락넷 오답을 표와 원문 근거로 반박하셨습니다. Q4도 6칸 중 5칸이 정답입니다.

서술형 두 문제(Q1, Q3)에서 크게 흔들렸는데, 공통된 원인이 있습니다. 문제가 두 시나리오를 대비시켜 물었는데(값 전달 vs 참조 전달, 상속 vs 재정의), 답이 한쪽으로 쏠리거나 방향이 뒤바뀌었습니다. Q1(1)에서는 “6번”이라고 해야 할 자리에 “2번”을 쓰셨고, Q1(3)에서는 값/참조 결과가 정확히 반대로 적혔습니다. Q3(1)ㄱㄷ도 비트수준/논리적 상수성의 이름이 서로 자리를 바꿔 앉았습니다. 개념 자체는 알고 계신 게 보이는데, 이름표를 붙이는 순간 헷갈리시는 패턴이라 아래 피드백에서 그 지점을 집중적으로 짚었습니다.

Q6은 이번 회차에서 유일한 완전 오답입니다. “C++은 하나의 언어가 아니다”라는 판단 자체는 정답과 같은 방향인데, 명제가 실제로 틀리게 말한 부분(“ 가지, 템플릿은 STL에 포함”)을 짚지 못하고 다른 근거(C처럼 쓸 수 있다)를 대셔서 판정이 엇갈렸습니다.


Q1. 🔴 어려움 — 🔺 3.5/10

문제: 값에 의한 전달과 상수 참조자에 의한 전달에 대해 답하시오.

(1) validateStudent_Val(plato) 호출 시 복사 생성자·소멸자 호출 횟수와 대상 (2) _Ref에서 비용이 어떻게 달라지는가, const가 추가로 보장하는 것 (3) printNameAndDisplay_Val(wwsb)에서 display()는 어느 버전인가, 이유, 현상의 이름, _Ref라면? (4) 값 전달이 나은 세 종류의 타입, “double 하나짜리 클래스”가 거기 들지 않는 이유 둘

📝 내 풀이:

(1) 복사 2번: Person → Student 생성자 소멸자 2번: Student → Person

(2) 추가적인 호출 필요가 없어진다. 일관성. 내부 값이 변하지 않는다.

(3) 자식의 display를 부른다. 함수 오버라이딩으로 인한 것이다. ref 버전이라면 부모의 display를 호출한다.

(4) 값으로 전달하면 좋은 타입은 모르겠다. 기억나지 않는다.

정답:

(1) 복사 생성자 6번, 소멸자 6번

Student splato복사본이므로 복사 생성자가 불린다. 그런데 StudentPerson을 상속하고, 둘 다 string 멤버를 두 개씩 갖는다. 그래서 하나를 복사하려면 여섯 개를 복사해야 한다.

순서 복사 생성자
1 Student 복사 생성자
2 Person 복사 생성자 (기본 클래스 부분)
3, 4    └ Person 안의 string name, string address
5, 6 Student 안의 string schoolName, string schoolAddress

함수가 끝나면 같은 여섯 개가 역순으로 소멸된다 — Student의 두 stringPerson의 두 stringPersonStudent. 총 6번의 생성자 호출 + 6번의 소멸자 호출. 동작은 맞지만 비효율적이다.

(2) 복사가 사라진다 + const가 수정을 막는다

const Student& s로 받으면 복사 생성자가 아예 호출되지 않는다. 실제로는 포인터를 넘기는 것과 거의 같게 동작하므로 (1)의 12번 호출이 0번이 된다.

const는 그 위에 함수 안에서 객체가 수정되는 것을 막아 준다. 참조자로 넘기면 원본을 건드릴 수 있게 되는데, const가 그 위험을 봉인한다 — 값 전달의 “원본은 안전하다”는 성질을 비용 없이 얻는 것이다.

(3) 복사손실(slicing) — 기본 클래스 버전이 불린다

printNameAndDisplay_Val(Window w)WindowWithScrollBars를 넘기면 내부적으로 이렇게 된다.

Window w = wwsb;     // Window의 복사 생성자로 Window 객체를 만든다

wWindow 객체다. wwsbWindow 부분만 복사되고, WindowWithScrollBars가 덧붙인 부분은 잘려 나간다. 그래서 w.display()는 가상 함수임에도 Window::display() 가 불린다 — w는 애초에 WindowWithScrollBars가 아니니까.

이 현상을 복사손실(slicing) 이라 한다.

_Ref 버전이라면 const Window& wwwsb 자체를 가리키므로 원래 타입이 유지되고, w.display()WindowWithScrollBars::display() 를 부른다. 참조자 전달은 복사 비용만 아끼는 게 아니라 다형성도 지켜 준다.

(4) 값 전달이 나은 세 종류 — 기본제공 타입, STL 반복자, 함수 객체 타입

이 셋은 작고 복사가 싸며, 값으로 다루도록 설계되어 있다. 나머지 사용자 정의 타입은 참조자로 넘긴다.

double 하나만 가진 작은 클래스”가 예외에 들지 않는 이유는 두 가지다.

  1. 컴파일러가 레지스터에 안 얹어 줄 수 있다. double 자체는 레지스터에 올라가지만, 그것을 감싼 사용자 정의 클래스는 컴파일러가 다르게 취급할 수 있어 “작으니까 싸다”는 보장이 없다.
  2. 사용자 정의 타입의 구조는 언제든 변할 수 있다. 지금은 double 하나지만 나중에 멤버가 늘어나면 값 전달을 쓰던 모든 호출이 갑자기 비싸진다. 참조자로 넘겨 두면 구조가 바뀌어도 비용이 그대로다.

🔍 피드백: 논점별로 (1) 0.5 / (2) 2.0 / (3) 1.0 / (4) 0을 받아 3.5점입니다. 이 단원이 처음 나온 회차치고는 아쉬운 점수인데, 짚어 보면 방향이 어긋난 곳이 많아서입니다.

(1)이 가장 크게 어긋났습니다. “복사 2번”이라 쓰셨는데 정답은 6번입니다. Student → Person이라는 상속 관계는 정확히 보셨지만, 그 사이에 있는 string 멤버들을 세지 않으셨습니다.

Student 복사 생성자
  └ Person 복사 생성자           ← 여기까지가 2번
      └ string name 복사        ← 이것도 복사 생성자 호출이다
      └ string address 복사
  └ string schoolName 복사
  └ string schoolAddress 복사

PersonStudent는 각각 string 멤버를 두 개씩 갖고 있고, 멤버 객체를 복사할 때도 그 멤버의 복사 생성자가 호출됩니다. 그래서 “클래스 2개(Student, Person)”가 아니라 “복사되는 객체 6개(Student 자신 + Person 자신 + string 4개)”를 세어야 합니다. 소멸자도 마찬가지로 6번입니다.

(2)의 “일관성”은 이 항목의 용어가 아닙니다. 원문이 말하는 것은 const가 객체를 수정하지 못하게 막아 준다는 것 하나입니다. “추가적인 호출 필요가 없어진다”는 정확한 표현입니다 — 그게 곧 복사 생성자 호출이 사라진다는 뜻이니까요.

(3)이 정확히 반대로 적혔습니다. “자식의 display를 부른다 / ref라면 부모의 display”라고 쓰셨는데, 실제로는 이렇습니다.

  부르는 함수
_Val(값 전달) 부모(Window) 의 display — ❌ 오답에서는 “자식”
_Ref(참조 전달) 자식(WindowWithScrollBars) 의 display — ❌ 오답에서는 “부모”

이유가 헷갈리신 지점도 정확히 반대입니다. “함수 오버라이딩으로 인한 것”이라 쓰셨는데, 값 전달에서 부모 버전이 불리는 이유는 오버라이딩이 작동해서가 아니라 작동할 수 없어서입니다. Window w = wwsb;로 복사하는 순간 w는 이미 순수한 Window 객체가 되어 버려서, WindowWithScrollBars의 존재 자체가 사라집니다. 이 현상의 이름이 복사손실(slicing) 입니다 — 객체의 “덧붙은 부분이 잘려 나간다”는 뜻입니다.

(4)는 미응답 처리했습니다. 정답은 기본제공 타입(int, double 등), STL 반복자, 함수 객체 타입 세 가지입니다. “double 하나짜리 클래스”가 예외가 아닌 이유는 ① 컴파일러가 그 클래스를 레지스터에 안 얹어 줄 수 있고 ② 사용자 정의 타입은 나중에 멤버가 늘어날 수 있어서(값 전달 비용이 갑자기 커짐)입니다.

한 가지 조언을 드리면, 이 항목은 “값 전달 = 무겁고 위험, 참조 전달 = 가볍고 안전” 이라는 한 방향의 이야기입니다. (2)와 (3)에서 그 방향을 반대로 적으신 걸 보면, 두 시나리오를 비교할 때 어느 쪽이 “원본”이고 어느 쪽이 “복사본”인지를 한 번 더 확인하는 습관을 들이시면 좋겠습니다.

출처: game_dev/cpp/chapter4/2025-06-18-cpp_4_20.md


Q2. 🟡 보통 — ⭕ 10/10

문제: 온프레미스 서버와 클라우드 서버, 그리고 클라우드 계층(IaaS/PaaS/SaaS)에 대한 설명으로 가장 올바른 것은?

  • A. 온프레미스 서버는 데이터센터에 직접 하드웨어를 설치하므로 자동 스케일 아웃이 가능하고, 클라우드 서버는 가상 머신이라 사용자 수가 갑자기 늘 때 대응하지 못한다.
  • B. 클라우드 서버는 리얼 머신 안에 가상 머신을 여러 개 띄우는 구조라 리얼 머신보다 빠르고, 서버 한 대당 유지 비용도 온프레미스보다 낮다. 다만 처리 속도의 균일성이 높아 실시간 게임에 유리하다.
  • C. 온프레미스는 서버 한 대당 유지 비용이 낮고 처리 속도가 균일하지만 증설·철거가 느려 유동적인 사용자 수에 빠르게 대응하지 못한다. 클라우드는 가상 머신이라 리얼 머신보다 느리고 유지 비용이 높지만 필요할 때 쉽게 늘리거나 줄일 수 있고 자동 스케일 아웃이 가능하다. 클라우드 계층은 IaaS → PaaS → SaaS로 갈수록 제어 수준과 관리 복잡도가 함께 낮아진다.
  • D. PaaS는 가상 머신 자체를 제공하는 가장 낮은 계층으로, 운영체제부터 직접 관리해야 하므로 제어권이 최대다. IaaS는 운영체제와 프레임워크가 이미 돌아가고 있어 건드릴 수 없으며 웹 호스팅이 그 예다.

📝 내 선택: C — A: 설명이 반대이다. / B: 서버 한 대당 유지 비용이 온프레미스보다 낮다는 것 빼곤 다 반대이다. / D: PaaS에 대한 설명은 맞을 것 같다. 하지만 IaaS에 대한 설명은 틀렸다. 이는 SaaS에 대한 내용이다.

정답: C

해설:

온프레미스(on-premise) 서버 — 자체 서버·온프림 서버라고도 하며, 데이터센터에 직접 서버 하드웨어와 운영체제를 설치한다. 단점은 유동적인 사용자 수에 빠르게 대응하지 못한다는 것이다.

클라우드 서버 — 리얼 머신 안에 가상 머신을 여러 개 띄워 서버를 구축한다. 장점은 필요할 때마다 쉽게 늘리거나 줄일 수 있다는 것이고, 단점은 가상 머신이 리얼 머신보다 느리다는 것이다.

구분 물리적(온프레미스) 서버 클라우드 서버
구조 서버 한 대에 운영체제 하나 서버 한 대에 여러 운영체제(가상 머신)
서버 한 대당 유지 비용 낮음 높음
증설 / 철거 속도 느림 빠름
자동 스케일 아웃 불가능 가능
처리 속도 균일성 높음 낮음
장애 처리 느림 빠름

클라우드 서비스의 세 계층

계층 무엇을 제공하는가 예시 제어 수준
IaaS 가상 머신 자체. 운영체제부터 직접 관리 Oracle Cloud 컴퓨팅 서비스 높음
PaaS 운영체제·프레임워크가 이미 돌아가고 있고 건드릴 수 없음. 애플리케이션 개발에 집중 웹 호스팅 중간
SaaS 완성된 소프트웨어. 코딩 자체가 필요 없음 Google Analytics 낮음

위로 갈수록 제어 수준과 관리 복잡도가 함께 낮아진다.

  • A — 정반대다. 자동 스케일 아웃은 클라우드의 강점이고, 온프레미스는 유동적 사용자 수에 대응하지 못하는 것이 약점이다.
  • B — 세 군데가 틀렸다. 가상 머신은 리얼 머신보다 느리고, 유지 비용은 높으며, 처리 속도 균일성은 낮다. 클라우드가 이기는 건 속도가 아니라 유연성이다.
  • D — IaaS와 PaaS의 설명이 뒤바뀌었다. 가상 머신 자체를 제공하며 제어권이 최대인 것이 IaaS, 운영체제·프레임워크가 이미 깔려 있고 웹 호스팅이 예인 것이 PaaS다.

🔍 피드백: 만점입니다. 세 오답을 쳐낸 근거가 특히 좋습니다. B에서 “유지 비용이 낮다는 것 빼곤 다 반대”라고 정확히 짚으신 게 인상적입니다 — 하나의 참인 부분과 여러 개의 거짓인 부분이 섞인 함정 보기를 통째로 맞다/틀리다로 보지 않고 항목별로 쪼개서 판정하셨습니다. D에 대해서도 “IaaS 설명은 사실 SaaS 이야기”라고 정확히 짚으셨는데, 정확히는 PaaS 이야기입니다(운영체제·프레임워크가 이미 돌아가고 있어 건드릴 수 없는 게 PaaS, 코딩 자체가 필요 없는 완성품이 SaaS). 다만 D 전체를 오답으로 배제하는 데는 지장이 없었습니다.

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


Q3. 🔴 어려움 · 📌 오답노트 — 🔺 8.2/10

문제: const의 상수성에 대해 답하시오.

(1) ㉠ char& operator[]() const를 컴파일러가 const로 인정하는 근거 개념 ㉡ 그런데 왜 상수 객체의 내용이 바뀌는가 ㉢ 이를 보완하는 다른 개념과 그 정의 (2) 캐싱 코드에 붙일 키워드와 대상, 붙이지 않아야 할 멤버와 이유 (3) tb[0] = 'x'ctb[0] = 'x' — 되는지, 안 되면 정확히 무엇 때문에 (4) ㉠ (b)가 (a)를 호출하는 세 단계 ㉡ 반대 방향이 안 되는 이유

📝 내 풀이:

(1) ㄱ: 논리적 상수성 ㄴ: 반환값이 참조이므로, 외부에서 수정이 가능해진다. ㄷ: 비트단위 상수성. 완전히 상수성을 보장하는 것이 아니라, 필요한 부분만 상수성을 보장하면 된다.

(2) mutable. textLength, lengthIsValid이며 pText는 읽기만 한다.

(3) 1은 되고 2는 안된다. 2번은 상수 객체이며 상수 반환값이기 때문

(4) ㄱ: const_cast, static_cast 1단계: const 붙이기 2단계: const 버전 함수 호출 3단계: const 제거

ㄴ: 정확히 기억 안난다.

정답:

(1) 비트수준 상수성의 허점과 논리적 상수성

㉠ 비트수준(물리적) 상수성

어떤 멤버 함수가 그 객체의 어떤 데이터 멤버도 건드리지 않아야 그 멤버 함수가 const임을 인정한다.

operator[]pText를 읽기만 하고 수정하지 않으므로, 비트수준 기준으로는 const가 맞다.

㉡ 반환값이 “주소”이기 때문이다

char& operator[](size_t position) const { return pText[position]; }

이 함수는 멤버를 수정하지 않지만, pText가 가리키는 문자에 대한 참조(주소)를 밖으로 내보낸다. 그 주소를 받은 바깥 코드는 *pc = 'J'처럼 그 주소가 가리키는 값을 마음대로 고칠 수 있다. 함수 자신은 결백하지만, 문을 열어 준 셈이다. 비트수준 상수성은 함수 본문만 검사하므로 이 허점을 잡지 못한다.

㉢ 논리적 상수성

일부 몇 비트 정도는 수정할 수 있되, 그것을 사용자 측에서 알아채지 못하면 된다.

비트수준 상수성은 “한 비트도 안 건드린다”는 기준이라 위처럼 실질적으로는 바뀌는데 통과하기도 하고, 반대로 (2)처럼 실질적으로는 안 바뀌는데 막히기도 한다. 논리적 상수성은 그 기준을 “사용자가 보기에 상수인가” 로 옮긴 것이다.

(2) mutable — 캐시 멤버 두 개에

class CTextBlock {
public:
    size_t length() const;
private:
    char*          pText;            // ← 붙이지 않는다
    mutable size_t textLength;       // ✅
    mutable bool   lengthIsValid;    // ✅
};

mutable을 붙인 멤버는 const의 제한에서 예외 대우를 받아 상수 멤버 함수 안에서도 수정할 수 있다. 붙일 대상은 length() 안에서 실제로 수정되는 두 멤버textLengthlengthIsValid다.

pText에는 붙이지 않는다. length()pTextstrlen(pText)읽기만 할 뿐 수정하지 않으므로 예외가 필요 없다.

(3) const 오버로딩과 반환 타입

코드 호출되는 함수 반환 타입 결과
tb[0] = 'x' (b) 비상수 버전 — tb가 비상수 객체이므로 char& ⭕ 대입 가능
ctb[0] = 'x' (a) 상수 버전 — ctb가 상수 객체이므로 const char& ❌ 컴파일 에러

②가 안 되는 이유는 ctb가 상수 객체라서가 아니라, 더 정확히는 호출된 (a)의 반환 타입이 const char&이기 때문이다. const char&에는 대입할 수 없다. 즉 operator[]반환 타입에 붙은 const 가 상수 객체 쓰기를 막는 실제 장치다.

(4) 코드 중복 제거 — const를 붙이고, 부르고, 떼어낸다

char& operator[](size_t position) {
    return const_cast<char&>(                          // ③ const를 떼어낸다
        static_cast<const TextBlock&>(*this)           // ① const를 붙인다
            [position]                                 // ② 상수 버전 (a)를 호출한다
    );
}

㉠ 세 단계

  1. static_cast<const TextBlock&>(*this)*thisconst붙여 상수 객체로 만든다. 그래야 다음 단계에서 상수 버전이 선택된다.
  2. [position] — 상수 객체에 대해 operator[]를 부르므로 (a) 상수 버전이 호출된다. 반환은 const char&.
  3. const_cast<char&>(...) — 그 const char&에서 const떼어내 char&로 돌려준다. 비상수 버전의 반환 타입에 맞추는 것이다.

㉡ 반대 방향이 안 되는 이유

(a)가 (b)를 호출하려면 상수 객체에서 const를 떼어내 비상수 버전을 불러야 한다. 그런데 (b)는 객체를 수정할 수 있는 함수다. 상수 객체를 비상수로 위장해 수정 가능한 함수에 넘기는 것은 상수 객체를 상수가 아니게 만드는 것이라 위험하다. 반면 (b)→(a) 방향은 비상수 객체에 const붙이는 것이라 안전하다 — 권한을 줄이는 쪽은 언제나 안전하다.

🔍 피드백: 논점별로 (1) 1.0 / (2) 2.5 / (3) 2.5 / (4) 2.2를 받아 8.2점입니다. (3)과 (4)㉠ 모두 원래 채점에서 제가 실수로 감점했던 부분입니다 — (3)은 판정·근거 모두 정확했고, (4)㉠도 빈칸 순서를 정확히 따른 정답이었습니다. 둘 다 만점(또는 만점에 가깝게)으로 정정합니다. 남은 감점은 (1)의 복사 횟수 오답, (4)㉡의 미응답입니다.

(1)에서 ㄱ과 ㄷ의 이름이 서로 바뀌었습니다.

  쓰신 답 정답
㉠ (컴파일러가 const로 인정하는 근거) 논리적 상수성 비트수준(물리적) 상수성
㉢ (그 허점을 보완하는 개념) 비트단위 상수성 논리적 상수성

㉢에서 쓰신 설명(“완전히 상수성을 보장하는 게 아니라 필요한 부분만 보장하면 된다”)은 정확히 논리적 상수성의 정의입니다. 그런데 그 설명에 “비트단위 상수성”이라는 이름을 붙이셨습니다. 반대로 ㉠에는 “논리적 상수성”이라는 이름을 붙이셨는데, 컴파일러가 실제로 검사하는 건 “한 비트도 안 건드렸는가”이므로 그건 비트수준 상수성입니다. 두 이름의 정의는 정확히 아시는데 라벨이 서로 자리를 바꿔 앉은 상태입니다. 구분하는 요령: 컴파일러가 기계적으로 검사하는 쪽(본문에 대입문이 있는가)= 비트수준, 사람이 “겉으로 티가 안 나면 된다”고 판단하는 쪽 = 논리적.

㉡(“반환값이 참조이므로 외부에서 수정 가능”)은 정확합니다.

(2)는 거의 만점입니다. mutable을 붙일 두 멤버와 pText를 제외하는 이유(“읽기만 한다”)까지 정확합니다.

(3)은 만점입니다. “①은 되고 ②는 안 된다”는 정답과 정확히 일치하는 판정이고, “②번은 상수 객체이며 상수 반환값이기 때문”이라는 근거도 정확합니다 — ctb가 상수 객체라서 상수 버전 operator[]가 호출되고, 그 반환 타입이 const char&라서 대입이 안 된다는 인과관계를 정확히 짚으셨습니다. (처음 채점에서 이 부분을 0점 처리했었는데, 판정과 근거를 착각한 제 채점 실수였습니다. 바로잡습니다.)

(4)㉠은 만점입니다. 문제의 빈칸이 코드에 나온 순서대로 ③단계(바깥) → ①단계(안쪽) 였는데, “const_cast, static_cast” 라고 쓰신 것이 바로 그 순서(③=const_cast, ①=static_cast)와 정확히 일치합니다. 제가 이를 “진행 순서(1→2→3)대로 캐스트를 나열한 것”으로 잘못 해석해서 반대로 적혔다고 부당하게 감점했었는데, 실제로는 빈칸 순서를 그대로 따른 정답이었습니다. “1단계: const 붙이기 / 2단계: const 버전 함수 호출 / 3단계: const 제거”라는 절차 설명도 정확했습니다.

㉡은 미응답 처리했습니다. 반대 방향((a)가 (b) 호출)이 안 되는 이유는 상수 객체를 비상수로 위장해 “수정 가능한” 함수에 넘기게 되어, 상수 객체의 안전성이 깨지기 때문입니다. 권한을 늘리는 방향은 위험하고, 줄이는 방향(지금의 (b)→(a))은 안전합니다.

출처: game_dev/cpp/chapter1/2025-04-06-cpp_1_3.md 오답노트: wn-628f38c5 · 2026-09-10 최초 오답 · 이번이 2번째 복습


Q4. 🟢 쉬움 — 🔺 9.4/10

문제: 빈칸을 채우시오.

  • 첫 번째 원칙 — 분산 처리를 피할 수 있다면 ① ___.
  • 어디를 분산할지는 ② ___ 을(를) 통해 지점을 엄선한다.
  • 나누려는 데이터의 ③ ___ 을(를) 확인하고 분산한다.
  • 분산 처리 방식 세 가지 — ④ ___ 분산 처리 / ⑤ ___ 분산 처리 / 데이터 ⑥ ___ 기반 로컬 처리

📝 내 답: 피해라, 성능 분석, 응집도, 동기, 비동기, 동기화

정답:

① 피하자 ② 성능 분석 ③ 응집력 ④ 동기 ⑤ 비동기 ⑥ 동기화

해설:

분산 처리 전략은 네 줄로 요약된다.

① 분산 처리를 피할 수 있다면 피하자. 분산은 공짜가 아니다 — 네트워크 장비 과부하, 디버깅 난이도, 운영체제 부하, 클라우드 회선 신뢰성 문제가 따라온다. 한 대 안에서 해결할 수 있으면 그게 최선이다.

② 성능 분석을 통해 분산 처리 지점을 엄선하자. 감으로 나누지 않는다. 과부하 지점을 찾고, 그 안에서 어느 함수가 무거운지까지 파고들어, 코드 최적화를 먼저 한 뒤에도 부족할 때 분산한다.

③ 데이터 응집력을 확인하고 분산하자. 응집력이 높은 데이터를 억지로 나누면 서버 간 통신이 폭증한다. 서로 다른 게임방, 다른 리전처럼 응집력이 낮은 곳이 나누기 좋은 지점이다.

④⑤⑥ 세 가지 방식

방식 동작
동기 분산 처리 다른 서버에 요청하고 응답을 기다린다
비동기 분산 처리 다른 서버에 요청만 던지고 기다리지 않는다
데이터 동기화 기반 로컬 처리 필요한 데이터를 서버끼리 맞춰 두고 자기 메모리에서 판단한다

🔍 피드백: ①②④⑤⑥을 정답 처리하고 ③에 부분 점수를 주어 9.4점입니다.

“피해라”는 어미만 다를 뿐 뜻이 같아 정답 처리했습니다. 나머지 다섯 칸도 전부 정확합니다.

③ “응집도”에는 부분 점수를 드렸습니다. 원문 표현은 응집력입니다. 같은 개념을 가리키는 표현이라 크게 감점하지는 않았지만, 이 단원(9.10)과 이어지는 매치메이킹 단원(10.3)에서 모두 “응집력” 으로 쓰이고 있으니 그대로 맞춰 두시면 좋습니다.

출처: server/game_server/9/2026-03-22-game_server_9_10.md


Q5. 🟡 보통 — ⭕ 10/10

문제: 네트워크 엔진에 대한 설명으로 가장 올바른 것은?

  • A. 네트워크 엔진이 필요한 이유는 운영체제마다 소켓 API가 완전히 동일해서 굳이 직접 다룰 필요가 없기 때문이다. 엔진은 그 동일한 API를 한 번 더 감싼 편의 계층일 뿐이다.
  • B. 게임 스파크플레이팹은 로그인·매치메이킹·플레이어 정보 관리 같은 기능을 서버 개발 없이도 쓸 수 있게 하며, 특정 운영체제에 구애받지 않는 SaaS 형태다. 락넷은 유니티·언리얼 엔진에 기본 내장된 엔진이고, 프라우드넷은 클라이언트-서버 네트워킹과 클라이언트 간 직접 네트워킹을 단순하게 쓸 수 있게 하면서 암호화·압축·흐름 제어를 제공한다.
  • C. 프라우드넷은 C# 하나만 지원하며, 그 대신 윈도·iOS·안드로이드·플레이스테이션·Xbox·WebGL 등 가장 다양한 클라이언트 플랫폼을 지원하는 것이 차별성이다.
  • D. 락넷은 게임 스파크·플레이팹처럼 SaaS 형태로 제공되어 운영체제에 구애받지 않으므로, 유니티나 언리얼을 쓰지 않는 프로젝트에서 가장 먼저 검토할 엔진이다.

📝 내 선택: B — A: 운영체제마다 소켓 API가 다르기 때문에 네트워크 엔진이 필요한 것이다. / C: 표에 보이는 것처럼 C++도 지원한다. / D: 락넷은 SaaS가 아니다. 정확히 무슨 형태인지는 기억나지 않지만 언리얼에서 사용하는 것으로 알고 있다.

정답: B

해설:

네트워크 엔진이 필요한 이유는 두 가지다.

  1. 운영체제마다 소켓 API를 사용하는 방식이 조금씩 다르다.
  2. 소켓 API에서 제공되지 않는 기능을 직접 만들어야 할 때도 있다.

즉 엔진은 “같은 API를 한 번 더 감싼 것”이 아니라, OS별 차이를 흡수하고 소켓이 주지 않는 기능(암호화·압축·흐름 제어·P2P·매치메이킹 등)을 얹어 주는 층이다.

종류 차별성 지원 운영체제 지원 언어
프라우드넷 C/S 네트워킹과 클라이언트 간 직접 네트워킹을 단순하게. 암호화·압축·흐름 제어 제공 윈도, 리눅스, iOS, 안드로이드, PS4, WebGL C++, C#
포톤 서버 다양한 플랫폼의 게임 클라이언트 지원 윈도, iOS, 안드로이드, PS, Xbox, WebGL C#
게임 스파크 로그인·매치메이킹·플레이어 정보 관리를 서버 개발 없이 구애받지 않음, SaaS C++, C#, Objective-C, 자바
플레이팹 게임 스파크와 유사 구애받지 않음, SaaS C++, C#, Lua, Objective-C, 자바, 자바스크립트
락넷 유니티/언리얼 엔진에 기본 내장 윈도, 리눅스, iOS, 안드로이드 C++, C#
  • A — 반대다. 소켓 API는 OS마다 조금씩 달라서 엔진이 필요한 것이고, 소켓이 제공하지 않는 기능을 얹어 주는 것도 엔진의 역할이다.
  • C — C#만 지원하고 가장 다양한 클라이언트 플랫폼을 지원하는 것은 포톤 서버의 설명이다. 프라우드넷은 C++과 C#을 지원한다.
  • D — 락넷은 SaaS가 아니라 유니티·언리얼에 기본 내장된 엔진이다. SaaS 형태인 것은 게임 스파크와 플레이팹이다.

🔍 피드백: 만점입니다. D에 대해 “SaaS는 아니지만 정확한 형태는 기억나지 않는다”며 솔직하게 쓰신 부분을 채워 드리면 — 락넷은 SaaS 서비스가 아니라 유니티·언리얼 엔진에 기본 내장된 엔진입니다. “구애받지 않는 SaaS”는 게임 스파크·플레이팹 두 곳만 해당하고, 락넷·프라우드넷·포톤 서버는 모두 특정 OS 목록을 지원하는 일반 SDK/미들웨어 형태입니다. 판단은 정확했으니 이 구분만 채워 두시면 완결됩니다.

출처: server/game_server/6/2026-02-18-game_server_6_1.md


Q6. 🟢 쉬움 — ❌ 0/10

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

C++은 하나의 언어라기보다 여러 하위 언어의 연합체로 보는 것이 정확하다. 그 하위 언어는 세 가지C, 객체 지향 개념의 C++, 그리고 STL — 이며, 템플릿은 STL을 구현하는 수단이므로 별도의 하위 언어로 치지 않고 STL에 포함시켜 본다.

📝 내 답: O — C++은 하나의 언어가 아니다. 그 예시로 C++를 C처럼 쓸 수도 있다. vector<T> 같이 템플릿도 STL을 구현하는 수단이므로 STL에 포함시켜 볼 수 있다.

정답: X

해설:

“C++을 여러 하위 언어의 연합체로 본다”는 앞부분은 참이다. 그러나 개수와 구성이 틀렸다.

C++은 네 가지 하위 언어 — C, 객체 지향 개념의 C++, 템플릿 C++, STL — 의 연합체다.

템플릿 C++은 STL에 포함되는 것이 아니라 독립된 하위 언어다. 템플릿은 STL을 만드는 데 쓰이긴 하지만, 그 자체로 컴파일 타임 계산(템플릿 메타프로그래밍), 일반화 프로그래밍 등 STL과 무관한 영역을 가진다. 실제로 지금까지 나왔던 항목들 — 템플릿 특수화, 의존 이름과 typename, 템플릿 기본 클래스의 이름 탐색, TMP — 은 STL 이야기가 아니라 템플릿 C++이라는 하위 언어의 규칙이었다.

🔍 피드백: 판단(O)과 근거 모두 0점입니다. 다만 “C++은 하나의 언어가 아니다”라는 방향 자체는 원문과 같습니다. 어긋난 지점은 명제가 실제로 틀리게 말한 부분을 짚지 못하고, 다른 근거(C++을 C처럼 쓸 수 있다는 사실, 템플릿이 STL을 구현하는 수단이라는 사실)로 O를 뒷받침하신 데 있습니다.

이 두 근거는 사실 자체는 틀리지 않습니다. C++을 C처럼 쓰는 것도 맞고, 템플릿이 STL 구현에 쓰이는 것도 맞습니다. 문제는 명제가 “몇 가지인가” 를 구체적으로 못 박아 두었다는 점입니다.

하위 언어는 세 가지다 — C, 객체 지향 C++, STL. 템플릿은 별도로 치지 않고 STL에 포함시킨다.

원문은 네 가지를 말합니다. 템플릿 C++이 STL과 나란히 놓인 독립된 하위 언어이기 때문입니다. “템플릿이 STL을 만드는 데 쓰인다”는 사실과 “그래서 템플릿은 STL의 일부다”라는 결론은 다른 이야기입니다 — 도구가 다른 것을 만드는 데 쓰인다고 해서 그 도구가 만들어진 것의 부분이 되는 것은 아니니까요.

이런 “개수를 명시하는 OX”는 명제 안의 숫자와 나열된 항목의 개수를 직접 세어 보는 것이 가장 확실합니다. “세 가지”라고 적어 놓고 항목을 나열했다면, 그 나열된 개수가 실제로 몇 개인지 원문 기억과 대조해 보는 습관이 도움이 될 것입니다.

출처: game_dev/cpp/chapter1/2025-01-02-cpp_1_1.md


📌 복습 포인트

  • Q1·Q3 공통 — 대비되는 두 시나리오의 방향 확인 — 이번 회차에서 값 전달/참조 전달, 상속/재정의, 비트수준/논리적 상수성처럼 “둘을 대비시키는” 문제에서 방향이 자꾸 뒤바뀌었습니다. 답을 쓰기 전에 “이건 A 얘기인가 B 얘기인가”를 한 번 더 확인하는 습관을 들이면 좋겠습니다.
  • Q1 값 전달의 비용 — 상속 관계 + 멤버 객체까지 모두 세면 6번. 복사손실(slicing)은 값 전달 시 기본 클래스 버전이, 참조 전달 시 파생 클래스 버전이 불린다 → game_dev/cpp/chapter4/2025-06-18-cpp_4_20.md
  • Q3 두 상수성의 이름 (오답노트, 세 번째 복습 예정)비트수준(물리적) = 컴파일러가 기계적으로 검사 / 논리적 = 사람이 “티 안 나면 된다”고 판단. 캐스트는 붙일 때 static_cast, 뗄 때 const_castgame_dev/cpp/chapter1/2025-04-06-cpp_1_3.md
  • Q6 “몇 가지인가”를 묻는 OX — 하위 언어는 네 가지(C / 객체 지향 C++ / 템플릿 C++ / STL). 숫자가 나오는 명제는 항목을 직접 세어 대조할 것 → game_dev/cpp/chapter1/2025-01-02-cpp_1_1.md

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

댓글남기기