11. 계정 정지/강제 종료(킥) vs 진행 중인 정상 처리
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 계정 정지/강제 종료(킥) 명령 vs 진행 중인 정상 처리
// ----------------------------------------------------------------------------
// 시나리오:
// - 운영툴/어드민이 어떤 플레이어를 "정지(ban) + 즉시 강제 종료(kick)" 할 수 있다.
// 이 명령은 별도의 어드민 스레드에서 SessionManager 로 들어온다.
// - 동시에 그 플레이어의 게임 패킷은 IO/로직 워커 스레드에서 정상 처리 중일 수 있다
// (예: 거래 체결, 보상 지급, 진행도 저장 같은 다단계 작업).
// - 강제 종료는 소켓을 닫고 세션을 레지스트리에서 제거한다.
//
// 요구사항:
// - 정지된 플레이어는 정지 시점 이후의 행동(아이템 획득/거래 체결 등)을
// 완료할 수 없어야 한다(부정 이득 차단).
// - 강제 종료 중에도 서버는 죽으면 안 되고, 세션 자원(소켓 등)은 정확히
// 한 번만 정리되어야 한다(이중 정리/누수 금지).
// - 진행 중이던 정상 작업이 "절반만 적용된 상태"로 남으면 안 된다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 발생하는지 설명하고,
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
using System.Net.Sockets;
public class Player
{
public long Id;
public bool Banned; // (A) 정지 플래그
public void ApplyReward(int amount) { /* 골드/아이템 지급 (생략) */ }
public void SaveProgress() { /* DB 저장 (생략) */ }
}
public class GameSession
{
public long Id;
public Player Player;
public Socket Socket;
public bool Closed; // (B) 종료 여부
public GameSession(long id, Player p, Socket s) { Id = id; Player = p; Socket = s; }
// 워커 스레드: 정상 게임 패킷 처리(다단계 작업의 예)
public void HandleRewardPacket(int amount)
{
if (Player.Banned) return; // (C) 진입 시 1회 검사
// ... 검증/계산 등 시간이 걸리는 작업 ...
Player.ApplyReward(amount); // (D) 보상 지급
Player.SaveProgress(); // (E) 진행도 저장
}
public void Close()
{
Closed = true;
Socket.Close(); // (F) 소켓 닫기/Dispose
}
}
public class SessionManager
{
private readonly Dictionary<long, GameSession> _sessions = new();
public void Add(GameSession s) { _sessions[s.Id] = s; }
public GameSession Find(long sessionId)
=> _sessions.TryGetValue(sessionId, out var s) ? s : null;
// 어드민 스레드: 정지 + 강제 종료
public void BanAndKick(long sessionId)
{
GameSession s = Find(sessionId);
if (s == null) return;
s.Player.Banned = true; // (G) 정지 표시
s.Close(); // (H) 강제 종료
_sessions.Remove(sessionId); // (I) 레지스트리에서 제거
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 계정 정지/강제 종료(킥) 명령 vs 진행 중인 정상 처리
// ----------------------------------------------------------------------------
// 시나리오:
// - 운영툴/어드민이 어떤 플레이어를 "정지(ban) + 즉시 강제 종료(kick)" 할 수 있다.
// 이 명령은 별도의 어드민 스레드에서 SessionManager 로 들어온다.
// - 동시에 그 플레이어의 게임 패킷은 IO/로직 워커 스레드에서 정상 처리 중일 수 있다
// (예: 거래 체결, 보상 지급, 진행도 저장 같은 다단계 작업).
// - 강제 종료는 소켓 fd 를 닫고 세션을 레지스트리에서 제거한다.
//
// 요구사항:
// - 정지된 플레이어는 정지 시점 이후의 행동(아이템 획득/거래 체결 등)을
// 완료할 수 없어야 한다(부정 이득 차단).
// - 강제 종료 중에도 서버는 죽으면 안 되고, 소켓 fd 는 정확히 한 번만
// close 되어야 한다(이중 close/fd 재사용 사고 금지).
// - 진행 중이던 정상 작업이 "절반만 적용된 상태"로 남으면 안 된다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 발생하는지 설명하고,
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <unordered_map>
#include <unistd.h> // close()
struct Player
{
int64_t id;
bool banned; // (A) 정지 플래그
void ApplyReward(int amount) { /* 골드/아이템 지급 (생략) */ }
void SaveProgress() { /* DB 저장 (생략) */ }
};
struct GameSession
{
int64_t id;
Player* player;
int fd; // 소켓 file descriptor
bool closed; // (B) 종료 여부
// 워커 스레드: 정상 게임 패킷 처리(다단계 작업의 예)
void HandleRewardPacket(int amount)
{
if (player->banned) return; // (C) 진입 시 1회 검사
// ... 검증/계산 등 시간이 걸리는 작업 ...
player->ApplyReward(amount); // (D) 보상 지급
player->SaveProgress(); // (E) 진행도 저장
}
void Close()
{
closed = true;
::close(fd); // (F) fd 닫기
}
};
class SessionManager
{
public:
void Add(GameSession* s) { sessions_[s->id] = s; }
GameSession* Find(int64_t sessionId)
{
auto it = sessions_.find(sessionId);
return it == sessions_.end() ? nullptr : it->second;
}
// 어드민 스레드: 정지 + 강제 종료
void BanAndKick(int64_t sessionId)
{
GameSession* s = Find(sessionId);
if (!s) return;
s->player->banned = true; // (G) 정지 표시
s->Close(); // (H) 강제 종료
sessions_.erase(sessionId); // (I) 레지스트리에서 제거
delete s; // (J) 세션 객체 해제
}
private:
std::unordered_map<int64_t, GameSession*> sessions_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.