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

게시:     수정

카테고리:

태그:

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

말랑말랑 퀴즈 — 해답지 ✅

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

📊 채점 결과

문항 유형 난이도 결과 점수
Q1 객관식 🟡 보통 ⭕ 정답 10 / 10
Q2 서술형 🔴 어려움 🔺 부분 정답 2.1 / 10
Q3 OX 🟡 보통 ⭕ 정답 10 / 10
Q4 빈칸 🟡 보통 ⬜ 미응답 0 / 10
Q5 객관식 🟡 보통 ⭕ 정답 10 / 10
Q6 서술형 · 📌 오답노트 🟡 보통 🔺 부분 정답 5.7 / 10
합계       37.8 / 60

총평

객관식·OX 세 문제(Q1, Q3, Q5)는 전부 만점입니다. 특히 Q3에서 Overlapped I/O가 “지속적으로 상태를 확인하지 않는다”는 점과 윈도 전용이라는 점을 정확히 짚으셨고, Q1·Q5도 오답 보기 하나하나를 근거와 함께 반박하셨습니다.

서술형 두 문제(Q2, Q6)에서 크게 점수가 깎였습니다. Q2(특수 멤버 함수 자동 작성 규칙)는 이번에 처음 나온 단원이라 그런지 세부 조건이 많이 헷갈리셨고, 특히 (2)번은 정답과 반대로 적으셨습니다. Q4는 “잘 기억나지 않는다”로 미응답 처리했습니다 — enum class 단원도 아직 익숙하지 않으신 것 같습니다.

Q6(오답노트)은 절반 정도 맞히셨습니다. isStillLoading의 역할은 정확히 이해하고 계셨지만, “멀티스레딩이 필요한 세 상황” 중 이 예시가 어디 해당하는지는 헷갈리셨습니다.


Q1. 🟡 보통 — ⭕ 10/10

문제: TCP·UDP의 수신 버퍼가 가득 찼을 때 송신 측이 어떻게 되는지에 대한 설명으로 가장 올바른 것은?

  • A. TCP와 UDP 모두 수신 버퍼가 가득 차면 송신 측의 send()가 즉시 블로킹되어, 데이터가 유실되는 일은 절대 없다.
  • B. TCP는 수신 버퍼가 가득 차면 송신 측 send()가 블로킹되어 흐름 제어가 이루어지지만, UDP는 수신 버퍼가 가득 차도 송신 측이 멈추지 않고 계속 보내기 때문에 넘치는 데이터가 그대로 버려진다. 이런 UDP의 무분별한 전송은 네트워크 혼잡을 일으켜 같은 네트워크를 쓰는 TCP 통신에도 불리하게 작용할 수 있다.
  • C. recv()는 수신 버퍼가 완전히 가득 찰 때까지 기다렸다가 한 번에 리턴하며, 1바이트만 도착한 상태에서는 리턴하지 않는다.
  • D. 라우터는 처리 능력을 초과하는 패킷이 들어와도 절대 패킷을 버리지 않고, 큐에 무한정 쌓아 순서대로 처리한다.

📝 내 답: B — A: UDP는 상대방의 상태를 고려하지 않고 보내기에 유실된다. 이와 반대로 TCP는 블로킹된다. / C: TCP는 데이터그램 기반이며, 전송끝이라는 문자가 있었던 것으로 기억한다. / D: 라우터는 큐에 무한정 쌓지 않으며, 일정 수치를 초과하면 버려진다.

정답: B

해설:

항목 TCP UDP
수신 조건 1바이트라도 있으면 리턴 데이터그램 1개라도 있으면 리턴
버퍼 가득 시 송신 측 send() 블로킹 send() 계속 실행 (데이터 버려짐)
흐름 제어 수신자 성능에 맞춰 조정 수신자 성능 고려 안 함

TCP는 흐름 제어가 있어서, 수신 버퍼가 가득 차면 송신 측 send()블로킹된다. UDP는 흐름 제어가 없어서, 수신 버퍼가 가득 차도 송신 측은 계속 보내고 넘치는 데이터는 그냥 버려진다. UDP가 이렇게 마구잡이로 보내면 네트워크 경쟁에서 TCP가 밀리는 혼잡 현상이 생긴다.

  • A — 반대다. UDP는 블로킹되지 않고 데이터를 버린다.
  • Crecv()1바이트라도 있으면 즉시 리턴한다.
  • D — 원문은 라우터가 처리 능력을 넘으면 “패킷을 늦게 처리하거나 버린다“고 말한다.

🔍 피드백: 만점입니다. A·D에 대한 반박은 정확합니다. 다만 C에 대한 메모(“TCP는 데이터그램 기반”, “전송끝 문자”)는 이 포스트 내용과 무관한 착각입니다 — TCP는 데이터그램 기반이 아니라 스트림 기반이고, C가 틀린 진짜 이유는 “1바이트라도 있으면 즉시 리턴한다”는 원문과 반대로 적었기 때문입니다. 결과적으로 C를 오답 처리한 최종 판단은 맞았지만, 근거는 다시 짚어 두시면 좋겠습니다.

출처: server/game_server/3/2026-02-03-game_server_3_5.md


Q2. 🔴 어려움 — 🔺 2.1/10

문제: C++11의 특수 멤버 함수 자동 작성 규칙에 대해 답하시오.

(1) 이동 생성자·이동 대입 연산자가 자동으로 작성되려면 만족해야 하는 세 조건은? (2) 두 이동 연산은 서로 독립적인가? 하나만 선언했을 때 다른 하나도 자동으로 작성되는가? (3) 두 복사 연산은 서로 독립적이다. 그런데 어떤 경우에 이 둘이 자동으로 삭제(비활성화)되는가? (4) 소멸자를 직접 선언한 StringTable 클래스 예시에서, 그것이 이동 연산에 미치는 영향과 성능 문제, = default로 해결되는 이유

📝 내 풀이:

(1) 복사 생성자, 생성자, 소멸자를 선언하지 않아야한다.

(2) 독립적이지 않고, 하나를 선언했을때 다른 하나도 자동 작성된다. (잘모름)

(3) 1번이랑 비슷했던 것 같은데 잘모르겠다.

(4) 이동 생성자와 이동 대입 연산자가 생성되지 않는다.

정답:

(1) 세 조건 — 복사 연산 없음 + 이동 연산 없음 + 소멸자 없음

클래스에 다음이 모두 해당해야 이동 생성자·이동 대입 연산자가 자동 작성된다.

  • 어떤 복사 연산(복사 생성자, 복사 대입 연산자)도 선언되어 있지 않다.
  • 어떤 이동 연산도 선언되어 있지 않다.
  • 소멸자가 선언되어 있지 않다.

(2) 독립적이지 않다

이동 생성자가 선언되어 있으면 컴파일러가 이동 대입 연산자를 작성하지 않고, 이동 대입 연산자가 선언되어 있으면 이동 생성자를 작성하지 않는다. 하나를 선언하면 다른 하나는 자동으로 작성되지 않는다.

(3) 이동 연산이 하나라도 선언되면 복사 연산이 삭제된다

복사 생성자와 복사 대입 연산자는 서로 독립적이라, 하나를 선언해도 다른 하나의 자동 작성을 막지 않는다. 그러나 이동 연산이 하나라도 선언되어 있으면 두 복사 연산 모두 삭제(비활성화)된다.

(4) 소멸자 선언 → 이동 연산 미작성 → “이동”이 실제로는 “복사”가 됨 → 성능 저하

StringTable은 소멸자를 직접 선언했으므로 (1)의 조건이 깨져 이동 생성자·이동 대입 연산자가 자동으로 작성되지 않는다. 이동 연산이 없으므로, StringTable 객체를 이동하려는 코드는 대신 복사 연산으로 처리되어 내부 std::map도 “이동”이 아니라 복사된다. 소멸자 하나를 추가했을 뿐인데 모든 “이동” 코드가 무거운 복사로 바뀌면서 엄청난 성능 문제가 생긴다. = default로 복사·이동 연산을 명시적으로 선언해 두면 이 문제가 애초에 생기지 않는다.

🔍 피드백: 논점별로 (1) 1.0 / (2) 0.5 / (3) 0 / (4) 0.6을 받아 2.1점입니다. 이번 단원(특수 멤버 함수 자동 작성 규칙)이 조건이 많고 헷갈리기 쉬운데, 전체적으로 세 조건의 세부 사항이 정리가 안 되어 있는 것 같습니다.

(1)에서 “복사 생성자”만 언급하고 복사 대입 연산자를 빠뜨리셨고, “생성자”라는 표현은 무엇을 가리키는지 불분명합니다. 정답은 복사 연산 둘 다 없음 + 이동 연산 둘 다 없음 + 소멸자 없음입니다 — “생성자”가 아니라 이동 연산이 세 번째 조건 중 하나입니다. “소멸자를 선언하지 않아야 한다”는 정확했습니다.

(2)는 정답과 반대로 적으셨습니다. “하나를 선언했을 때 다른 하나도 자동 작성된다”고 쓰셨는데, 정답은 정반대입니다 — 하나를 선언하면 다른 하나는 자동으로 작성되지 않습니다. “독립적이지 않다”는 판단 자체는 맞았지만, 그 의미를 반대로 해석하셨습니다. 스스로 “(잘모름)”이라 적으신 만큼, 이 규칙은 다시 한 번 짚고 넘어가시면 좋겠습니다.

(3)은 미응답으로 처리했습니다. 정답은 이동 연산이 하나라도 선언되면 복사 연산 둘 다 삭제된다입니다. (1)의 “복사 연산 없음”과는 다른 이야기 — (1)은 이동 연산의 자동 작성 조건이고, (3)은 반대로 이동 연산이 있을 때 복사 연산에 일어나는 일입니다.

(4)는 절반만 답하셨습니다. “이동 생성자·이동 대입 연산자가 생성되지 않는다”까지는 정확한데, 그 뒤에 이어지는 “그래서 이동 코드가 실제로는 복사로 처리되어 성능이 나빠진다”는 부분과 = default로 해결되는 이유”가 빠졌습니다. 이 항목은 (1)~(4)가 사실 하나의 이야기로 이어져 있으니, 다음에 복습할 때는 네 개를 따로 외우기보다 “소멸자 선언 → 조건 깨짐 → 이동 없음 → 복사로 대체됨 → 느려짐 → default로 막기”라는 하나의 흐름으로 기억해 보시면 좋겠습니다.

출처: game_dev/moderncpp/3/2026-08-25-moderncpp_3_17.md


Q3. 🟡 보통 — ⭕ 10/10

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

논블록(non-blocking) 소켓과 Overlapped I/O는 둘 다 소켓 I/O 함수를 호출할 때 사용자 데이터 블록을 커널 버퍼로 복사하는 연산이 발생하며, 이 복사 비용 때문에 두 방식의 성능 차이는 거의 없다. 또한 Overlapped I/O는 크로스 플랫폼 방식이라 리눅스에서도 그대로 사용할 수 있다.

📝 내 답: X. 논블록은 지속적으로 상태를 확인하지만, Overlapped I/O는 그렇지 않았던 것으로 기억한다. 그러므로 성능 차이는 존재하며, Overlapped I/O는 윈도우에서 사용할 수 있어서, 리눅스에서는 epoll을 썼던 것 같다.

정답: X

해설:

구분 논블록 소켓 Overlapped I/O
메모리 복사 발생 발생 안 함
플랫폼 크로스 플랫폼 Windows 전용

논블록 소켓은 소켓 I/O 함수 호출 시 사용자 데이터 블록에 대한 복사 연산이 발생한다. 반면 Overlapped I/O는 데이터 블록 자체를 버퍼로 사용하기 때문에 이 복사 연산이 발생하지 않는다. 또한 Overlapped I/O는 윈도 플랫폼 전용이라 리눅스 등에서는 쓸 수 없다.

🔍 피드백: 만점입니다. “성능 차이가 없다”는 명제를 반박하는 근거로 원문이 강조한 메모리 복사 연산 유무 대신, 재시도(폴링) 여부를 드셨는데 — 이것도 원문에 나오는 논블록 소켓의 실제 단점(would block 시 재시도 호출 낭비)과 같은 맥락이라 타당한 근거입니다. 윈도 전용이라는 판단과, 리눅스에서는 다른 방식(epoll 등)을 쓴다는 배경지식도 정확합니다.

출처: server/game_server/3/2026-02-05-game_server_3_7.md


Q4. 🟡 보통 — ⬜ 0/10

문제: 빈칸을 채우시오.

범위 없는 enum(C++98 스타일)의 열거자는 그 ① ___ 밖으로 새어나가 이름공간을 오염시키지만, 범위 있는 enum(enum class)의 열거자는 ② ___ 안에서만 유효하다. 범위 없는 enum의 열거자는 정수·부동소수점 타입으로 ③ ___ 변환되지만, 범위 있는 enum은 이런 변환이 되지 않아 다른 타입이 필요하면 ④ ___을 직접 써야 한다. 범위 없는 enum은 기본적으로 전방 선언이 안 되지만 ⑤ ___을 지정해 주면 가능해지고, 범위 있는 enum은 이것이 기본적으로 ⑥ ___(으)로 정해져 있어서 언제나 전방 선언이 가능하다.

📝 내 답: (미응답 — “잘 기억나지 않는다”로 명시적으로 응답을 포기함)

정답:

① (열거형의) 범위/스코프 ② (현재) 중괄호 범위 ③ 암묵적으로 ④ 캐스팅(static_cast) ⑤ 바탕 형식(underlying type) ⑥ int

해설:

범위 없는 enum(enum Color { black, white, red };)의 열거자는 그 범위 밖으로 새어나가 이름공간을 오염시킨다. 범위 있는 enum(enum class Color { ... };)의 열거자는 Color::white처럼 범위를 한정해서만 접근할 수 있다.

범위 없는 enum의 열거자는 암묵적으로 정수·부동소수점으로 변환되어 의도치 않은 비교(if (c < 14.5))가 컴파일되지만, 범위 있는 enum은 이런 변환이 없어 캐스팅을 직접 써야 한다.

전방 선언은 컴파일러가 바탕 형식을 알아야 가능하다. 범위 없는 enum은 기본적으로 바탕 형식이 정해져 있지 않아 전방 선언이 안 되지만, 바탕 형식을 직접 지정하면 가능해진다. 범위 있는 enum은 기본 바탕 형식이 int로 정해져 있어 언제나 전방 선언이 가능하다.

🔍 피드백: 이번 단원(enum class)은 Q2와 함께 이번 회차에서 가장 낯선 내용이었던 것 같습니다. 핵심만 정리하면 — ① 이름공간 오염 여부(범위 없는 enum은 새어나감, enum class는 안 새어나감), ② 암묵적 형변환 여부(범위 없는 enum만 됨), ③ 전방 선언 가능 여부(바탕 형식이 정해져 있는지가 관건 — enum class는 기본이 int라 항상 가능) 세 가지 차이만 기억해 두시면 이 단원의 대부분을 커버할 수 있습니다.

출처: game_dev/moderncpp/3/2026-04-19-moderncpp_3_10.md


Q5. 🟡 보통 — ⭕ 10/10

문제: 게임 서버의 데이터 저장 방식에 대한 설명으로 가장 올바른 것은?

  • A. 게임 서버는 보통 RDBMS 하나만 사용하며, NoSQL은 로그·통계 분석에는 적합하지 않아 거의 쓰이지 않는다.
  • B. 게임 서버 개발에서는 흔히 RDBMS와 NoSQL을 함께 사용한다 — 플레이어 데이터는 RDBMS로, 로그·통계 분석은 NoSQL(예: MongoDB)로 관리하는 식이다. MongoDB는 오브젝트 ID를 수동으로 지정할 수도 있고, 한번 정한 샤드 키는 샤드를 재구성하지 않는 한 바꿀 수 없으며, upsert는 “없으면 삽입, 있으면 수정”을 의미한다.
  • C. MongoDB의 인덱스는 RDBMS보다 비용이 낮아서, 인덱스를 최대한 많이 걸어도 성능에 문제가 없다.
  • D. NoSQL은 RDBMS보다 질의 기능이 많고 표준화되어 있어서, 복잡한 조인 질의가 필요한 경우 NoSQL이 항상 더 유리하다.

📝 내 답: B — A: 게임 서버는 용도에 따라 RDBMS와 NoSQL을 결정한다. 그리고 NoSQL이 로그 통계 분석에 적합했다. / C: 비용이 낮은건 맞지만, 인덱스를 많이 건다고 성능에 문제가 없지 않다. / D: RDBMS가 질의 기능이 많고 표준화되어있다.

정답: B

해설:

게임 서버 개발에서는 RDBMS와 NoSQL을 혼용한다 — 플레이어 데이터는 RDBMS, 로그·통계 분석은 NoSQL.

MongoDB 추가 특징

  • 오브젝트 ID는 수동 할당도 가능하다.
  • 샤드 키는 한번 설정하면 변경할 수 없고, 바꾸려면 샤드를 재구성해야 한다.
  • 인덱스 비용은 RDBMS보다 경향이 있다.
  • upsert: “없으면 넣고, 있으면 수정하라.”

  • A — RDBMS와 NoSQL을 혼용하는 것이 일반적이다.
  • C — MongoDB의 인덱스 비용은 RDBMS보다 크다.
  • D — 질의 기능이 많고 표준화된 쪽은 RDBMS다.

🔍 피드백: 만점입니다. A·D에 대한 반박은 정확합니다. 다만 C에 대한 메모 중 “비용이 낮은건 맞지만”이라는 부분은 사실과 반대입니다 — 원문은 MongoDB의 인덱스 비용이 RDBMS보다 크다(낮은 게 아니라)고 말합니다. C를 오답으로 최종 판단하신 것은 맞지만(“인덱스를 많이 걸어도 문제없다”는 결론이 틀렸다는 지적), 그 앞의 “비용이 낮다”는 전제 자체를 사실로 받아들이신 부분은 정정해 두시면 좋겠습니다.

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


Q6. 🟡 보통 · 📌 오답노트 — 🔺 5.7/10

문제: 게임 로딩 화면을 멀티스레드로 구현한 아래 코드에 대해 답하시오.

bool isStillLoading;

Thread1 {
	isStillLoading = true;
	while(isStillLoading) {
		FrameMove();
		Render();
	}
}

Thread2 {
	LoadScene();
	LoadModel();
	LoadTexture();
	LoadAnimation();
	LoadSound();

	isStillLoading = false;
}

(1) isStillLoading 변수의 역할과 두 스레드를 조율하는 방식 (2) 같은 작업을 싱글스레드로 번갈아 호출했을 때 생기는 두 가지 문제 (3) 이 예시가 해당하는 멀티스레딩 필요 상황과, “모든 코어 활용”과의 목적 차이

📝 내 풀이:

(1) 로딩중인지 확인. 로딩이 끝나면 로딩 변수를 false로 하여 thread1이 빠져나오게 하는 식이다.

(2) I/O와 화면 처리를 번갈아가며 하기에 뚝뚝 끊기며, 다른 작업을 하지 못하게된다.

(3) I/O 처리 동안 해야할 다른 작업이 있을 때. 해당 예시는 스레드가 코어보다 적어 활용하지 못하는 경우?

정답:

(1) isStillLoading은 Thread2(로딩)가 끝났음을 Thread1(렌더링)에 알리는 종료 플래그다

Thread1은 isStillLoadingtrue인 동안 반복해서 FrameMove()Render()를 실행한다. Thread2는 로딩을 마치면 isStillLoadingfalse로 바꾸고, Thread1의 while 루프는 그 순간 종료된다.

(2) 프레임이 끊기고, 로직이 복잡해진다

싱글스레드로 렌더링과 로딩 함수를 번갈아 호출하면 각 로딩 함수가 끝날 때까지 다음 Render() 호출이 막혀 프레임이 끊기고, 로딩 단계마다 렌더링 호출을 끼워 넣어야 하므로 코드 로직도 복잡해진다.

(3) “오래 걸리는 일 + 빨리 끝나는 일을 동시에” 상황 — “모든 코어 활용”과는 목적이 다르다

이 예시는 “오래 걸리는 일(로딩) 하나 + 빨리 끝나는 일(매 프레임 렌더링) 여러 개를 동시에 처리”하는 상황이다. 반면 “모든 코어를 최대한 활용하고 싶을 때”(예: 소수 구하기를 스레드로 나눠 처리)는 하나의 작업을 여러 코어에 쪼개 더 빨리 끝내는 것이 목적이다. 이 예시는 속도를 더 내려는 게 아니라, 서로 다른 두 작업을 동시에 진행시켜 체감 품질을 높이는 것이 목적이라는 점에서 다르다.

🔍 피드백: 논점별로 (1) 3.3 / (2) 1.7 / (3) 0.7을 받아 5.7점입니다.

(1)은 만점입니다. “로딩이 끝나면 변수를 false로 하여 thread1이 빠져나오게 한다”는 정확히 정답의 핵심(종료 신호 역할)입니다.

(2)는 절반만 맞았습니다. “뚝뚝 끊긴다”는 정답의 “프레임 끊김”과 정확히 일치합니다. 하지만 두 번째 문제는 “로직이 복잡해진다”인데, “다른 작업을 하지 못하게 된다”라고 쓰신 것은 다른 이야기입니다(이건 오히려 (3)에서 다룰 “동시 처리가 안 됨”에 가깝습니다). 원문은 렌더링 호출을 로딩 단계마다 끼워 넣어야 하는 코드 구조 자체의 복잡함을 두 번째 문제로 꼽습니다.

(3)에서 헷갈리신 부분이 있습니다. “I/O 처리 동안 해야 할 다른 작업이 있을 때”라는 서술은 원문의 두 번째 상황(긴 처리 중 다른 짧은 작업 처리, 디스크 I/O 예시)에 더 가깝습니다. 이 게임 로딩 예시는 첫 번째 상황(오래 걸리는 일 + 빨리 끝나는 일 여러 개를 동시에)의 대표 사례입니다. “모든 코어 활용”과의 차이에 대해서는 “스레드가 코어보다 적어 활용 못하는 경우?”라고 물음표로 답하셨는데, 실제로는 스레드/코어 개수의 문제가 아니라 “목적”의 차이입니다 — 모든 코어 활용은 하나의 작업을 쪼개 더 빨리 끝내는 것이 목적이고, 이 예시는 서로 다른 두 작업을 동시에 진행시키는 것이 목적입니다.

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


📌 복습 포인트

  • Q2 특수 멤버 함수 자동 작성 규칙 (한 흐름으로 기억) — 소멸자 선언 → 조건 깨짐 → 이동 연산 자동 작성 안 됨 → “이동” 코드가 실제로는 복사로 처리됨 → 성능 저하 → = default로 예방 → game_dev/moderncpp/3/2026-08-25-moderncpp_3_17.md
  • Q4 범위 있는 enum(enum class)의 세 차이 — 이름공간 오염 여부 / 암묵적 형변환 여부 / 전방 선언 가능 여부(바탕 형식이 기본으로 int인지) → game_dev/moderncpp/3/2026-04-19-moderncpp_3_10.md
  • Q6 멀티스레딩이 필요한 세 상황 구분 (오답노트, 3번째 복습 예정) — ① 오래 걸리는 일 + 빨리 끝나는 일 동시 처리(이 예시) / ② 긴 처리 중 다른 짧은 작업 / ③ 모든 코어 활용(하나의 작업을 쪼개 속도를 냄) → server/game_server/1/2025-05-28-game_server_1_3.md

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

댓글남기기