47. 펫 소환/소환해제와 스탯 버프 적용 경합 (C#)
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// [시나리오] MMORPG 펫/소환수 시스템 (서버 권위, 서버-클라)
//
// 플레이어는 펫을 "소환"하면 펫이 필드에 스폰되고, 펫이 제공하는
// 스탯 버프(예: 공격력 +5%)가 플레이어에게 적용된다.
// "소환 해제"하면 펫이 사라지고 그 버프가 제거된다.
//
// [요구사항]
// - 한 플레이어당 활성 펫은 최대 1마리.
// - 플레이어에게 적용된 펫 버프의 수는 항상 활성 펫 수와 정확히 일치해야 한다.
// - 클라는 소환/해제 버튼을 빠르게 연타할 수 있고, 요청은 서로 다른
// 수신 처리 흐름에서 동시에 도착할 수 있다.
//
// [과제]
// 아래 SummonPet / DismissPet 을 리뷰하라. (A)(B) 지점의 동작을 근거로
// 어떤 순서의 요청이 겹칠 때 어떤 잘못된 결과(스탯/엔티티/버프)가 나오는지
// 재현 시나리오와 함께 설명하고, 수정안을 제시하라.
using System.Threading.Tasks;
using System.Collections.Generic;
public class PetService
{
private readonly Dictionary<long, PetEntity> _activePets = new();
private readonly PlayerStats _stats;
private readonly IPetRepository _repo;
private readonly IWorld _world;
public async Task SummonPet(long playerId, int petId)
{
// (A) 이미 활성 펫이 있으면 무시
if (_activePets.ContainsKey(playerId))
return;
PetData data = await _repo.LoadPet(playerId, petId); // DB/캐시 조회
var pet = _world.Spawn(data);
// (B)
_activePets[playerId] = pet;
_stats.AddModifier(playerId, data.BuffModifier);
}
public async Task DismissPet(long playerId)
{
if (!_activePets.TryGetValue(playerId, out var pet))
return;
await _world.Despawn(pet);
_activePets.Remove(playerId);
_stats.RemoveModifier(playerId, pet.BuffModifier);
}
} 결함 코드 · C++
// [시나리오] MMORPG 펫/소환수 시스템 (서버 권위, 서버-클라)
//
// 플레이어가 펫을 "소환"하면 펫 엔티티가 스폰되고 스탯 버프가 적용된다.
// "소환 해제"하면 펫이 파괴되고 버프가 제거된다.
//
// [요구사항]
// - 한 플레이어당 활성 펫 최대 1마리, 버프 수 = 활성 펫 수.
// - 소환/해제 요청은 여러 워커 스레드에서 동시에 도착할 수 있다.
//
// [과제]
// 아래 코드를 리뷰하라. (A)(B) 지점의 동작을 근거로, 어떤 요청 순서가
// 겹칠 때 어떤 잘못된 결과(스탯/엔티티/메모리)가 나오는지 재현 시나리오와
// 함께 설명하고 수정안을 제시하라.
#include <unordered_map>
#include <memory>
#include <cstdint>
struct PetData { double buffMod; int petId; };
struct PetEntity { double buffMod; /* ...월드 상태... */ };
struct PlayerStats { void AddMod(int64_t, double); void RemoveMod(int64_t, double); };
struct World {
PetEntity* Spawn(const PetData&);
void Despawn(PetEntity*);
};
struct PetRepo { PetData LoadPet(int64_t, int); };
class PetService {
std::unordered_map<int64_t, PetEntity*> activePets_;
PlayerStats stats_;
World world_;
PetRepo repo_;
public:
void SummonPet(int64_t playerId, int petId) {
// (A) 이미 활성 펫이 있으면 무시
if (activePets_.find(playerId) != activePets_.end())
return;
PetData data = repo_.LoadPet(playerId, petId);
PetEntity* pet = world_.Spawn(data);
// (B)
activePets_[playerId] = pet;
stats_.AddMod(playerId, data.buffMod);
}
void DismissPet(int64_t playerId) {
auto it = activePets_.find(playerId);
if (it == activePets_.end()) return;
PetEntity* pet = it->second;
world_.Despawn(pet);
stats_.RemoveMod(playerId, pet->buffMod);
activePets_.erase(it);
delete pet;
}
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.