[말랑 퀴즈] 26/08/21 해답

게시:     수정

카테고리:

태그:

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

말랑말랑 퀴즈 — 해답지 ✅

날짜: 2026-08-21 문제 수: 5문제 총점: 24.3/50 (49점) 🔴 다시 공부해요

📊 채점 결과

문항 유형 난이도 결과 점수
Q1 서술형 🔴 어려움 🔺 부분 정답 2.5 / 10
Q2 객관식 🟡 보통 ❌ 오답 0 / 10
Q3 빈칸 채우기 🟢 쉬움 🔺 부분 정답 5 / 10
Q4 OX 🔴 어려움 ⭕ 정답 10 / 10
Q5 서술형 🟡 보통 🔺 부분 정답 6.8 / 10
합계       24.3 / 50

총평

Q4와 Q5(1)이 이번 회차에서 가장 깔끔했습니다. Q4는 new AddressLines[]가 안 보이는 함정을 그대로 지나쳐 “배열이니까 delete[]“라고 바로 짚으셨고, Q5(1)의 세 단계 요약은 군더더기 없이 정확합니다. 코드를 읽고 무슨 일이 벌어지는지 파악하는 능력은 안정적입니다.

문제는 “왜 그렇게 되어 있는가”를 묻는 순간 흔들린다는 점입니다. Q1에서 두 훅이 각각 어느 상태에 대응하는지는 맞혔지만, 이 훅들이 서버에만 존재한다는 대전제를 놓치고 “클라에서 호출된다”고 쓰셨습니다. 이건 같은 출처 글이 맨 앞에서 강조한 내용입니다. 그리고 문제 코드 안에 if (!ReadyToStartMatch())가 그대로 보이는데도 그 조건을 답에 쓰지 않으셨습니다. 답의 근거가 화면에 있는데 지나친 경우입니다.

Q2는 제가 32비트와 64비트의 증상을 서로 뒤바꿔 놓은 보기였는데 그대로 고르셨습니다. “둘 다 메모리 문제로 죽는다”까지만 기억하고 죽는 방식의 차이를 안 보신 것으로 보입니다. Q3 ①도 마찬가지로 용어 자체가 안 떠오른 경우입니다.

어제 67점에서 떨어졌지만 틀린 지점이 흩어져 있지 않고 한 갈래로 모입니다. 개념의 이름과 조건을 짚어 말하는 연습에 집중하면 됩니다. 아래 복습 포인트가 그 목록입니다.


Q1. 🔴 어려움 — 🔺 2.5/10

문제: AGameMode에서 NotifyBeginPlay()가 호출되는 지점이 두 곳인 이유와 조건별 실행 경로를 설명하고, Super:: 호출 이전에 SpawnLoot()를 도는 AEPGameMode::HandleMatchHasStarted()의 잠복 위험을 설명하시오.

// 엔진 GameMode.cpp:148-161
void AGameMode::HandleMatchIsWaitingToStart()
{
    ...
    if (!ReadyToStartMatch())
    {
        GetWorldSettings()->NotifyBeginPlay();       // ①
    }
}

// 엔진 GameMode.cpp:203-221
void AGameMode::HandleMatchHasStarted()
{
    ...
    GetWorldSettings()->NotifyBeginPlay();           // ②
}
void AEPGameMode::HandleMatchHasStarted()
{
    for (TActorIterator<AEPItemSpawner> It(GetWorld()); It; ++It)
        It->SpawnLoot();                 // ← Super 호출 이전

    Super::HandleMatchHasStarted();      // ← 여기서 NotifyBeginPlay()
    ...
}

📝 내 답:

(1) HandleMatchIsWaitingToStart()는 서버 측에서 WaitingToStart에 도착한 순간 클라에서 호출되고, HandleMatchHasStarted()는 서버 측에서 InProgress에 도착한 순간 클라에서 호출된다.

두 곳이 존재하는 이유는 클라가 접속한 시점이 각자 달라 BeginPlay가 호출되지 않을 수 있기 때문이다.

(2) 매칭 시작 대기방이 따로 없는 경우? 잘 모르겠다.

정답:

(1) BeginPlay 지점이 두 곳인 이유

핵심은 엔진 주석 그대로다. “대기가 발생하면 대기 시작 시점에, 대기 없이 바로 시작하면 매치 시작 시점에” BeginPlay를 쏜다. 어느 경로든 정확히 한 번만 불리게 하려는 구조다.

갈림길은 HandleMatchIsWaitingToStart() 안의 if (!ReadyToStartMatch())다.

ReadyToStartMatch() 최초 결과 의미 BeginPlay 시점
거짓 인원이 모자라 대기해야 함 HandleMatchIsWaitingToStart
조건 충족, 곧바로 시작 HandleMatchHasStarted

WaitingToStart에서 실제로 머무를 예정이면 그 자리에서 먼저 BeginPlay를 돌려주고(액터들이 대기 중에도 정상 동작해야 하므로), 곧바로 InProgress로 넘어갈 거라면 굳이 여기서 쏘지 않고 HandleMatchHasStarted에 맡긴다.

MinPlayersToStart를 걸어 최소 인원을 요구하는 프로젝트라면 보통 ①번 경로를 탄다. 그래서 매치가 시작될 무렵에는 BeginPlay가 이미 끝나 있다.

(2) 잠복 위험

문제는 SpawnLoot()Super::HandleMatchHasStarted()보다 먼저 호출된다는 점이다. NotifyBeginPlay()는 그 Super:: 안에 있으므로, ②번 경로에서는 SpawnLoot()가 액터의 BeginPlay보다 먼저 돈다.

① 경로 (인원 대기 O): BeginPlay(대기 시점) → ... → SpawnLoot() → Super::  ✅ 안전
② 경로 (즉시 시작)  : SpawnLoot() → Super:: → BeginPlay                  ⚠️ 역전

지금 안전한 이유는 AEPItemSpawnerBeginPlay 오버라이드가 없기 때문이다. 초기화할 게 없으니 순서가 뒤집혀도 티가 나지 않는다.

따라서 실제 버그가 되는 조건은 두 가지가 겹칠 때다.

  1. AEPItemSpawnerBeginPlay에서 무언가를 초기화하도록 바뀌고 (예: 스폰 테이블 캐싱, 풀 준비)
  2. ReadyToStartMatch()가 처음부터 참이 되는 상황 (즉시 시작 — 최소 인원 조건을 없애거나, 이미 인원이 다 찬 상태로 맵에 진입)

이때 초기화되지 않은 상태에서 SpawnLoot()가 도는 버그가 된다. 게다가 ①번 경로에서는 재현되지 않으므로 특정 조건에서만 터지는 형태라 더 까다롭다.

해결은 간단하다. Super::HandleMatchHasStarted()먼저 호출한 뒤 SpawnLoot()를 돌리면 두 경로 모두에서 BeginPlay가 앞선다.

🔍 피드백: 두 소문항 5점씩으로 채점했습니다.

  • (1) 1.0 / 5 — 각 훅이 어느 상태에 대응하는지는 맞습니다. 그런데 두 가지가 어긋났습니다.

    첫째, “클라에서 호출된다”가 틀렸습니다. AGameMode서버에만 존재하고 클라이언트에는 인스턴스 자체가 없습니다. 그래서 클라가 알아야 할 값을 GameState로 올려 DOREPLIFETIME으로 복제하는 것입니다. 이 두 훅은 전부 서버에서만 돕니다.

    둘째, 문제가 물은 “어떤 조건일 때 어느 쪽이 실행되는지“에 답하지 못하셨습니다. 그런데 그 조건이 문제 코드 안에 그대로 있었습니다 — if (!ReadyToStartMatch())입니다. 대기가 실제로 발생하면 ①, 조건이 이미 충족돼 바로 시작하면 ② 입니다. BeginPlay가 두 경로 어디서든 딱 한 번만 불리게 하려는 장치입니다.

    “클라 접속 시점이 각자 달라서”라고 쓰신 것은 BeginPlay를 플레이어별 이벤트로 오해하신 것으로 보입니다. NotifyBeginPlay()월드의 액터 전체에 한 번 쏘는 것이라 접속 순서와 무관합니다.

  • (2) 1.5 / 5 — “잘 모르겠다”로 맺으셨지만 그 앞의 추측이 아깝습니다. “매칭 시작 대기방이 따로 없는 경우”가 사실상 정답의 절반입니다. 대기가 없다는 건 ReadyToStartMatch()가 처음부터 참이라는 뜻이고, 그게 바로 ②번 경로 — 위험한 쪽입니다. 감이 맞았습니다.

    여기서 한 발만 더 갔으면 됐습니다. “그러면 무슨 순서가 되지?” 를 따라가 보세요. SpawnLoot()Super::보다 먼저 있고 NotifyBeginPlay()Super:: 안에 있으니, ②번 경로에서는 아이템 스폰이 액터의 BeginPlay보다 먼저 돕니다. 스포너가 BeginPlay에서 초기화를 하게 되는 날 곧바로 버그가 됩니다.

    추측에 자신이 없더라도 “~라면 순서가 이렇게 되어 문제” 까지 적어보세요. 그 한 줄이 부분 점수를 크게 바꿉니다.

출처: game_dev/devlog/2026-02-08-EP_Gameplay_Framework-3.md


Q2. 🟡 보통 — ❌ 0/10

문제: 분산 처리가 없는 단일 게임 서버에 동시접속자 수가 계속 늘어날 때 각 구성 요소에서 벌어지는 일에 대한 설명으로 가장 올바른 것은?

  • A. 32비트 서버는 물리 메모리보다 많은 메모리를 할당해 대량의 메모리 스와핑이 발생하고, 64비트 서버는 malloc()이 null을 반환하며 비정상 종료한다.
  • B. 서버의 CPU/RAM 사용량이 늘어나는 것은 메시지 수신 속도가 처리 속도를 앞지르고 요청 발생 속도가 송신 속도를 앞지르기 때문이며, 네트워크 기기 쪽에서는 라우터 과부하로 패킷이 유실되고 TCP 재전송 타임아웃으로 연결이 해제된다.
  • C. 클라이언트가 겪는 증상은 메시지 응답 지연과 접속 지연뿐이며, 이미 맺어진 TCP 연결 자체는 끊기지 않는다.
  • D. 데이터베이스는 질의 수가 디스크 최대 처리 속도를 넘지 않는 한 메모리 사용량이 늘어나지 않으므로 병목이 되지 않는다.

📝 내 답: A

정답: B

해설:

과부하는 서버 · 클라이언트 · DB · 네트워크 기기 네 군데에서 동시에 드러난다.

  • B (정답): 서버 자원 사용량이 늘어나는 메커니즘이 정확하다. 수신 속도 > 처리 속도, 요청 발생 속도 > 송신 속도가 되면 처리하지 못한 메시지가 쌓이면서 CPU와 RAM을 함께 갉아먹는다. 네트워크 기기 쪽 설명도 맞다. 라우터 과부하 → 패킷 유실 → TCP 재전송 타임아웃 → 연결 해제로 이어지고, TCP 소켓에서는 ECONNABORTED 오류가 뜬다.
  • A (오답): 32비트와 64비트가 뒤바뀌었다. 32비트 서버는 주소 공간 한계 때문에 malloc()null을 리턴하고 비정상 종료한다. 64비트 서버는 주소 공간이 넉넉해서 곧장 죽지 않는 대신, ① 물리 메모리보다 많이 할당하고 → ② 대량 메모리 스와핑이 발생하고 → ③ 실행 속도가 떨어지면서 할당량이 더 늘고 → ④ 결국 메모리 할당 함수에서 문제가 터진다.
  • C (오답): 응답 지연과 접속 지연에서 끝나지 않는다. 연결이 돌발적으로 해제된다. 원인은 TCP 재전송 타임아웃사용자 정의 킵얼라이브 메시징 타임아웃이며, 접속 실패로 인한 타임아웃도 발생한다.
  • D (오답): 인과가 반대로 서술됐다. DB에서도 질의 수가 디스크 최대 처리 속도를 넘지 못하면서 처리 대기가 쌓이고, 그 결과 메모리 사용량이 증가해 메모리 할당 함수에서 문제가 발생한다.

🔍 피드백: A를 고르셔서 0점입니다. A는 32비트와 64비트의 증상을 서로 맞바꿔 놓은 보기입니다.

기억을 바로잡는 기준은 주소 공간입니다.

  주소 공간 과부하 시
32비트 좁다 (~4GB 한계) 더 못 준다 → malloc()null 리턴바로 비정상 종료
64비트 넓다 계속 준다 → 물리 메모리 초과 → 스와핑 → 느려짐 → 할당량 더 증가 → 결국 문제

32비트는 “못 줘서” 즉사하고, 64비트는 “계속 줘서” 서서히 말라죽습니다. 스와핑은 디스크를 메모리처럼 쓰는 것이라 물리 메모리가 넉넉해야 벌어지는 현상이고, 그건 64비트 쪽 이야기입니다. 32비트는 스와핑까지 갈 여유 없이 주소 공간에서 먼저 막힙니다.

한편 나머지 보기들도 각각 다른 축을 뒤집어 놓은 것이니 함께 정리해두시면 좋습니다. C는 연결이 돌발 해제된다는 사실을, D는 DB도 메모리 문제로 무너진다는 사실을 부정하고 있습니다.

출처: server/game_server/9/2026-03-17-game_server_9_2.md


Q3. 🟢 쉬움 — 🔺 5/10

문제: 빈칸을 채우시오.

  • 동기 분산 처리: ① ___ 법칙이 심하게 작용한다.
  • 데이터 복제 기반 분산 처리: ② ___이(가) 깨진다.

기능 단위로 서버를 따로 두는 방식을 기능적 분산 처리 또는 ③ ___ 분산 처리라고 부른다.

📝 내 답: ① 모르겠다 (한 서버 약간의 변경점으로도 다수의 서버가 영향을 받는 법칙이 있었나?) / ② 무결성 / ③ 수직

정답:

① 암달(Amdahl) ② 데이터 일관성 ③ 수직

해설:

수평 분산 처리로 해결되지 않는 경우가 세 가지 있다.

방식 한계
동기 분산 처리 암달의 법칙이 심하게 작용
비동기 분산 처리 요청과 응답을 주고받아야 함
데이터 복제 기반 분산 처리 데이터 일관성이 깨짐

이럴 때 쓰는 것이 기능적 분산 처리 = 수직 분산 처리다. 같은 일을 하는 서버를 여러 대 늘리는(수평) 대신, 경매장 서버 · 채팅 서버처럼 기능별로 서버를 나눈다.

클라이언트 ─┬─ 게임 서버 ─┬─ 경매장 서버
            └─ 채팅 서버   │
                 게임 서버 ─┘

경매장은 모든 플레이어가 같은 매물 목록을 봐야 해서 데이터를 쪼개거나 복제하기 어렵다. 그래서 경매장 기능만 담당하는 서버 한 대를 두고 게임 서버들이 거기에 붙는 식으로 푼다.

다만 수평 분산 처리보다 분산 효율성이 떨어지므로 최후의 수단으로 본다.

🔍 피드백: 빈칸 3개를 각 3.33점으로 배분했습니다.

  • ① 0 / 3.33암달(Amdahl)의 법칙입니다. 적어두신 추측(“한 서버의 변경점으로 다수 서버가 영향받는 법칙”)은 다른 이야기입니다. 암달의 법칙은 병렬화할 수 없는 부분이 전체 성능 향상의 상한을 정한다는 법칙입니다. 예를 들어 작업의 10%가 반드시 순차적이면, 서버를 무한히 늘려도 속도는 최대 10배를 넘지 못합니다. 동기 분산 처리는 응답을 기다리는 동안 그 구간이 통째로 순차 구간이 되어버려서, 이 법칙에 정면으로 걸립니다.
  • ② 1.7 / 3.33 — 절반만 드립니다. “데이터가 어긋난다”는 방향은 맞게 잡으셨습니다. 다만 답은 일관성(consistency) 이고 무결성(integrity) 은 다른 개념입니다. 무결성은 데이터가 규칙과 제약을 지키고 있는가(외래 키가 유효한가, 타입이 맞는가)이고, 일관성은 복제본들이 서로 같은 값을 보고 있는가입니다. 복제 기반 분산에서 깨지는 건 후자입니다. 복제 = 사본이 여러 개 = 일관성 문제로 묶어두세요.
  • ③ 3.3 / 3.33 — 정답입니다. 수평(horizontal)의 반대가 수직(vertical)이라는 대응을 잘 잡고 계십니다.

출처: server/game_server/9/2026-03-20-game_server_9_8.md


Q4. 🔴 어려움 — ⭕ 10/10

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

typedef std::string AddressLines[4];
std::string* pal = new AddressLines;
delete pal;

new를 호출할 때 []를 쓰지 않았으므로, 해제도 [] 없이 delete pal;로 하는 것이 new와 delete의 형태를 맞춘 올바른 코드다.

📝 내 답: X — “배열은 delete [] 형태로 사용해야 한다.”

정답: X

해설:

[]가 눈에 보이느냐가 아니라, 실제로 배열을 할당했느냐가 기준이다.

AddressLinesstring[4]의 typedef이므로 new AddressLines는 사실상 이것과 같다.

std::string* pal = new std::string[4];   // ← new AddressLines의 실체

배열을 할당한 것이므로 반드시 delete[]로 해제해야 한다.

delete pal;      // ❌ 정의되지 않은 동작
delete [] pal;   // ✅ 올바름

왜 교차 사용이 안 되는가

배열을 할당하면 런타임이 배열 크기 정보까지 함께 저장한다. delete[]가 각 원소의 소멸자를 정확한 횟수만큼 호출하려면 이 정보가 필요하다.

인덱스 0 1 2
배열 배열 크기 n 객체 1 객체 2
단일 객체 객체 1      

메모리 배치 자체가 다르기 때문에 deletedelete[]내부 동작이 완전히 다르다. 배열에 delete를 쓰면 첫 번째 객체만 소멸되고 나머지 원소의 소멸자는 호출되지 않아 정의되지 않은 동작이 된다.

이 문제의 진짜 함정typedef배열이라는 사실을 감췄다는 것이다. 그래서 결론은 이렇다.

배열 타입을 typedef로 감추는 것은 피하는 것이 좋다.

애초에 이런 실수가 불가능한 대안을 쓰는 편이 낫다.

std::vector<std::string> pal(4);

🔍 피드백: 판단 7점, 근거 3점 모두 만점입니다. 이번 회차에서 가장 깔끔한 답이었습니다.

이 문제의 함정은 new AddressLines라는 표기에 []가 안 보인다는 것이었습니다. 명제도 “[]를 안 썼으니 delete가 맞다”며 그럴듯하게 유도했는데, “배열은” 이라고 단정하고 들어가신 걸 보면 typedef 뒤에 배열이 숨어 있다는 걸 정확히 보셨습니다.

기준을 한 문장으로 굳혀두시면 좋습니다. 판단 기준은 표기가 아니라 실제 할당물입니다. new 뒤에 오는 이름이 배열 타입이면, []가 안 보여도 delete[]입니다.

한 가지만 덧붙이면 — 그래서 원 저자의 결론은 “잘 구분해서 쓰자”가 아니라 “배열 타입을 typedef로 감추지 말자” 입니다. 애초에 헷갈릴 코드를 만들지 않는 쪽이고, 더 나아가면 std::vectorstd::array를 쓰면 이 실수 자체가 불가능해집니다.

출처: game_dev/cpp/chapter3/2025-06-12-cpp_3_16.md


Q5. 🟡 보통 — 🔺 6.8/10

문제: 프라우드넷 채팅 서버의 Chat() 의사 코드에서 1·2·3번 단계가 하는 일, CriticalSectionLock이 필요한 이유, 클라이언트 FrameMove()의 역할을 설명하시오.

MyGameC2S::Stub::Chat(senderHostID, rmiContext, text) {
    CriticalSectionLock lock(m_critSec, true);

    // 1
    shared_ptr<RemoteClient> sender =
        m_remoteClients.find(senderHostID).second;

    // 2
    vector<HostID> sendTo;
    for(auto r : m_remoteClients) {
        if(r.first != senderHostID)
            sendTo.push_back(r.first);
    }

    // 3
    m_s2cProxy.ShowChat(&r[0], r.size(), sender->m_name, text);
}

📝 내 답:

(1) 1. 전송자의 RemoteClient 객체를 얻는 과정 2. 수신자들의 HostID를 수집하는 과정 3. 수신자들에게 송신자의 이름과 메세지를 전송한다.

(2) 도중 접속/퇴장이 발생해 m_remoteClients 변수에 영향이 가, 전송자를 찾는 과정에 오류가 발생할 수 있기 때문

(3) 클라이언트의 한 프레임을 진행시킨다.

정답:

(1) 1·2·3번 단계

  • 1번: senderHostIDm_remoteClients를 조회해 송신자의 정보를 알아낸다. 3번에서 sender->m_name을 쓰기 위해 필요하다. 클라이언트가 자기 이름을 직접 실어 보내는 게 아니라 서버가 갖고 있는 목록에서 꺼낸다는 점이 중요하다.
  • 2번: 접속자 전체를 순회하며 송신자를 제외한 수신자 목록을 만든다. if(r.first != senderHostID)가 그 역할이다.
  • 3번: 만든 목록을 대상으로 멀티캐스트한다. 수신자마다 따로 호출하는 것이 아니라 HostID 배열과 개수를 넘겨 한 번에 보낸다.

(2) CriticalSectionLock이 필요한 이유

CNetServer기본적으로 멀티스레드로 동작하기 때문이다. (설정에 따라 싱글스레드로 만들 수도 있다.)

그래서 RMI나 이벤트 함수 호출이 여러 스레드에서 동시에 실행될 수 있다. m_remoteClients는 그 함수들이 공유하는 자료구조다.

OnClientJoin(clientInfo)  { ... m_remoteClients.Add(...); }     // 스레드 A
OnClientLeave(clientInfo) { ... m_remoteClients.Remove(...); }  // 스레드 B
Chat(...)                 { ... m_remoteClients 순회 ... }       // 스레드 C

보호하지 않으면 한 스레드가 목록을 순회하는 도중 다른 스레드가 항목을 추가·삭제할 수 있고, 컨테이너가 깨지거나 무효화된 반복자를 참조하게 된다.

(3) FrameMove()의 역할

클라이언트의 이벤트 및 수신 처리를 진행하는 함수다.

MainLoop() {
    while(true) {
        m_netClient->FrameMove();   // 네트워크 이벤트·수신 처리
        update_scene();
        render_scene();
    }
}

네트워크 처리를 별도 스레드가 알아서 하는 게 아니라, 메인 루프가 매 프레임 직접 호출해 쌓인 것을 꺼내 처리하는 폴링 방식이다. 이 호출 안에서 OnJoinServerComplete(), OnLeaveServer(), 그리고 서버가 보낸 ShowChat() 같은 RMI 수신 처리가 일어난다.

참고로 OnJoinServerComplete()(서버 연결 성공/실패)와 OnLeaveServer()(연결 중도 해제)는 직접 구현해야 하는 이벤트 함수다.

🔍 피드백: 세 소문항 각 3.33점으로 배분했습니다.

  • (1) 3.3 / 3.33 — 만점입니다. 세 단계를 정확하고 간결하게 요약하셨습니다. 특히 2번을 “수신자들의 HostID를 수집”이라고 쓰신 게 좋습니다. 송신자가 빠진다는 걸 짚으신 것이니까요.
  • (2) 2.5 / 3.33“도중 접속/퇴장이 발생해 목록에 영향이 간다” 는 정확한 관찰입니다. 실제로 OnClientJoin/OnClientLeavem_remoteClients를 건드리는 함수들입니다. 다만 왜 “도중에” 그런 일이 벌어질 수 있는지를 한 단계 더 적으셨으면 완성이었습니다. 답은 CNetServer가 기본적으로 멀티스레드로 동작해서, RMI와 이벤트 함수가 서로 다른 스레드에서 동시에 실행되기 때문입니다. 그리고 위험은 “전송자를 찾는 과정”만이 아니라 2번의 순회 도중 항목이 추가·삭제되어 반복자가 무효화되는 쪽이 더 큽니다.
  • (3) 1.0 / 3.33 — “한 프레임을 진행시킨다”는 너무 넓게 잡으셨습니다. 게임의 프레임을 진행시키는 건 그 아래 update_scene()render_scene()이고, FrameMove()가 하는 일은 네트워크 이벤트와 수신 처리뿐입니다. OnJoinServerComplete(), OnLeaveServer(), 서버가 보낸 ShowChat() 같은 RMI가 이 호출 안에서 불립니다.

    중요한 건 이게 폴링 구조라는 점입니다. 별도 스레드가 알아서 콜백을 쏘는 게 아니라 메인 루프가 매 프레임 직접 꺼내 처리합니다. 그래서 FrameMove()를 안 부르면 서버가 뭘 보내든 클라는 아무 반응이 없습니다. 서버 쪽 Q5(2)에서 락이 필요했던 것과 대비되는 지점이라 같이 기억하면 좋습니다 — 서버는 멀티스레드라 보호가 필요하고, 클라는 메인 루프 폴링이라 그럴 일이 없습니다.

출처: server/game_server/6/2026-02-27-game_server_6_8.md


📌 복습 포인트

  • Q1 GameMode의 존재 범위AGameMode서버에만 존재한다. 클라가 알아야 할 값은 GameState로 올려 복제 → game_dev/devlog/2026-02-08-EP_Gameplay_Framework-3.md
  • Q1 BeginPlay 분기 조건ReadyToStartMatch()거짓이면 대기 시점(①), 참이면 매치 시작 시점(②). 어느 쪽이든 딱 한 번 → game_dev/devlog/2026-02-08-EP_Gameplay_Framework-3.md
  • Q1 Super 호출 순서Super:: 안에 NotifyBeginPlay()가 있으므로, 그 앞에 둔 코드는 즉시 시작 경로에서 BeginPlay보다 먼저 돈다game_dev/devlog/2026-02-08-EP_Gameplay_Framework-3.md
  • Q2 32비트 vs 64비트 과부하32비트는 malloc() null로 즉사, 64비트는 스와핑으로 서서히 말라죽는다server/game_server/9/2026-03-17-game_server_9_2.md
  • Q2 클라이언트 증상 — 지연에서 끝나지 않고 TCP 재전송·킵얼라이브 타임아웃으로 연결이 돌발 해제된다 → server/game_server/9/2026-03-17-game_server_9_2.md
  • Q3 암달의 법칙 — 병렬화 못 하는 구간이 성능 향상의 상한을 정한다. 동기 분산이 여기 걸린다 → server/game_server/9/2026-03-20-game_server_9_8.md
  • Q3 일관성 vs 무결성 — 복제본끼리 값이 맞는가 = 일관성, 제약을 지키는가 = 무결성 → server/game_server/9/2026-03-20-game_server_9_8.md
  • Q5 락이 필요한 근본 이유CNetServer멀티스레드라 RMI·이벤트가 여러 스레드에서 동시 실행 → server/game_server/6/2026-02-27-game_server_6_8.md
  • Q5 FrameMove의 정체 — 게임 프레임 진행이 아니라 네트워크 이벤트·수신 처리를 하는 폴링 함수server/game_server/6/2026-02-27-game_server_6_8.md

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

댓글남기기