27. 파티장 위임 중에 파티장이 탈퇴/접속종료되는 상황
난이도 하 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 파티장 위임 중에 파티장이 탈퇴/접속종료되는 상황
// ----------------------------------------------------------------------------
// 시나리오 (콘텐츠/게임로직 · 서버-클라):
// - 파티에는 파티장(leader) 1명이 있고, 파티장만 초대/추방/해산/위임을 할 수 있다.
// - 파티장은 다른 멤버에게 파티장을 "위임(delegate)" 할 수 있다.
// - 위임 처리 중이거나 직후에 기존 파티장이 "탈퇴(Leave)" 하거나 접속종료(Disconnect)
// 될 수 있다(서로 다른 요청/스레드).
// - 파티장이 떠나면 서버는 남은 멤버 중 한 명으로 파티장을 자동 승계시켜야 한다.
//
// 요구사항:
// - 위임 대상은 "현재 그 파티에 속한, 유효한 멤버" 여야 한다(이미 나간 사람/외부인 금지).
// - 어떤 시점에도 파티장은 정확히 1명이어야 한다(리더 0명=leaderless, 2명 금지).
// - 파티장이 떠나면 남은 멤버가 있는 한 파티는 leaderless 가 되면 안 된다
// (남은 멤버가 없으면 파티 해산).
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 leaderless 파티·유효하지 않은 파티장·
// 리더 2명 같은 상태가 생기는지 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
public class Party
{
public long PartyId;
public long LeaderId;
public List<long> Members = new(); // 파티장 포함 전체 멤버
}
public class PartyService
{
private readonly Dictionary<long, Party> _parties = new(); // partyId -> Party
// 파티장이 다른 멤버에게 파티장 위임
public bool Delegate(long partyId, long requesterId, long targetId)
{
if (!_parties.TryGetValue(partyId, out var party)) return false;
if (party.LeaderId != requesterId) return false; // 파티장만 위임 가능
// (A) 위임 대상 처리를 검토하라
party.LeaderId = targetId;
return true;
}
// 멤버 탈퇴(자발적 Leave)
public void Leave(long partyId, long playerId)
{
if (!_parties.TryGetValue(partyId, out var party)) return;
party.Members.Remove(playerId);
// (B) 떠나는 사람이 파티장일 때의 처리를 검토하라
if (party.LeaderId == playerId)
{
if (party.Members.Count > 0)
party.LeaderId = party.Members[0];
// else: 남은 멤버가 0명인 경우의 처리를 검토하라
}
}
// 접속종료(네트워크 콜백, 다른 스레드)
public void OnDisconnect(long partyId, long playerId)
{
if (!_parties.TryGetValue(partyId, out var party)) return;
// (C) 접속종료 시 처리를 검토하라
party.Members.Remove(playerId);
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 파티장 위임 중에 파티장이 탈퇴/접속종료되는 상황
// ----------------------------------------------------------------------------
// 시나리오 (콘텐츠/게임로직 · 서버-클라):
// - 파티에는 파티장(leader) 1명이 있고, 파티장만 초대/추방/해산/위임을 할 수 있다.
// - 파티장은 다른 멤버에게 파티장을 "위임(delegate)" 할 수 있다.
// - 위임 처리 중/직후에 기존 파티장이 탈퇴(leave) 하거나 접속종료(onDisconnect) 될 수 있다
// (서로 다른 요청/스레드).
// - 파티장이 떠나면 서버는 남은 멤버 중 한 명으로 파티장을 자동 승계시켜야 한다.
//
// 요구사항:
// - 위임 대상은 "현재 그 파티에 속한 유효한 멤버" 여야 한다(이미 나간 사람/외부인 금지).
// - 어떤 시점에도 파티장은 정확히 1명(리더 0명/2명 금지).
// - 파티장이 떠나면 남은 멤버가 있는 한 leaderless 가 되면 안 된다(없으면 해산).
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 leaderless 파티·유효하지 않은 파티장·
// 리더 2명·참조 수명 문제가 생기는지 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <unordered_map>
#include <vector>
#include <algorithm>
struct Party {
int64_t partyId;
int64_t leaderId;
std::vector<int64_t> members; // 파티장 포함 전체 멤버
};
class PartyService {
public:
// 파티장이 다른 멤버에게 위임
bool delegate_(int64_t partyId, int64_t requesterId, int64_t targetId) {
auto it = parties_.find(partyId);
if (it == parties_.end()) return false;
Party& p = it->second;
if (p.leaderId != requesterId) return false; // 파티장만 위임 가능
// (A) 위임 대상 처리를 검토하라
p.leaderId = targetId;
return true;
}
// 멤버 탈퇴(자발적 leave)
void leave(int64_t partyId, int64_t playerId) {
auto it = parties_.find(partyId);
if (it == parties_.end()) return;
Party& p = it->second;
auto& m = p.members;
m.erase(std::remove(m.begin(), m.end(), playerId), m.end());
// (B) 떠나는 사람이 파티장일 때의 처리를 검토하라
if (p.leaderId == playerId) {
if (!m.empty())
p.leaderId = m[0];
// else: 남은 멤버가 0명인 경우의 처리를 검토하라
}
}
// 접속종료(네트워크 콜백, 다른 스레드)
void onDisconnect(int64_t partyId, int64_t playerId) {
auto it = parties_.find(partyId);
if (it == parties_.end()) return;
Party& p = it->second;
// (C) 접속종료 시 처리를 검토하라
auto& m = p.members;
m.erase(std::remove(m.begin(), m.end(), playerId), m.end());
}
private:
std::unordered_map<int64_t, Party> parties_; // partyId -> Party
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.