24. 귀속/거래불가 아이템이 교환·판매·우편 경로로 새는 상황
난이도 하 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 귀속/거래불가 아이템이 교환·판매·우편 경로로 새는 상황
// ----------------------------------------------------------------------------
// 시나리오 (콘텐츠/게임로직 · 서버-클라):
// - 아이템에는 "귀속(BindOnPickup/BindOnEquip)" 과 "거래 가능 여부(Tradable)"
// 속성이 있다. 귀속된 아이템·거래불가 아이템은 다른 플레이어에게 넘어가면 안 된다
// (직거래·우편 첨부·경매 출품 금지). 단, 상점 판매(NPC) 는 정책상 일부 허용될 수 있다.
// - 클라이언트는 거래/우편/경매 요청 패킷에 itemId 와 함께 부가 정보를 담아 보낸다.
// - 서버는 이 요청을 받아 아이템 이동을 수행한다.
//
// 요구사항:
// - 귀속/거래불가 아이템은 직거래·우편 첨부·경매 출품 경로로 절대 이전되면 안 된다.
// - "이 아이템이 거래 가능한지" 는 서버 권위 데이터로 판정해야 한다(클라 값 신뢰 금지).
// - 이동 시 소유권 검증(요청자가 실제 소유자인지)도 필요하다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 귀속·거래불가 아이템이 다른 플레이어에게
// 새어나가는지 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
public class Item
{
public long ItemId;
public int TemplateId;
public long OwnerId;
public bool Bound; // 귀속 여부(서버 권위)
public bool Tradable; // 거래 가능 여부(서버 권위, 템플릿에서 옴)
}
// 클라가 보낸 거래 요청
public class TradeRequest
{
public long FromPlayer;
public long ToPlayer;
public long ItemId;
public bool ClientSaysTradable; // 클라가 "이건 거래 가능"이라고 보낸 값
}
public class ItemTransferService
{
private readonly Dictionary<long, Item> _items; // itemId -> Item
private readonly Func<long, long, bool> _giveToPlayer; // (toPlayer, itemId) 인벤토리 추가
public ItemTransferService(Dictionary<long, Item> items, Func<long, long, bool> giveToPlayer)
{
_items = items;
_giveToPlayer = giveToPlayer;
}
// 직거래: From -> To 로 아이템 이전
public bool Trade(TradeRequest req)
{
if (!_items.TryGetValue(req.ItemId, out var item)) return false;
// (A) 거래 가능 여부를 클라가 보낸 값으로 판정
if (!req.ClientSaysTradable) return false;
item.OwnerId = req.ToPlayer;
_giveToPlayer(req.ToPlayer, req.ItemId);
return true;
}
// 우편 첨부: 아이템을 우편으로 다른 플레이어에게 보냄
public bool AttachToMail(long fromPlayer, long toPlayer, long itemId)
{
if (!_items.TryGetValue(itemId, out var item)) return false;
// (B) 귀속/거래불가 검증 없이 그대로 첨부
item.OwnerId = toPlayer;
_giveToPlayer(toPlayer, itemId);
return true;
}
// 경매 출품: 아이템을 경매장에 올림(이후 낙찰자에게 이전)
public bool ListOnAuction(long sellerId, long itemId)
{
if (!_items.TryGetValue(itemId, out var item)) return false;
// (C) 소유자 확인도, 귀속/거래불가 검증도 없음
item.OwnerId = 0; // 0 = 경매장 보관
return true;
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 귀속/거래불가 아이템이 교환·판매·우편 경로로 새는 상황
// ----------------------------------------------------------------------------
// 시나리오 (콘텐츠/게임로직 · 서버-클라):
// - 아이템에는 "귀속(bound)" 과 "거래 가능 여부(tradable)" 속성이 있다(서버 권위).
// 귀속·거래불가 아이템은 직거래·우편 첨부·경매 출품으로 다른 플레이어에게 넘어가면 안 된다.
// - 클라이언트는 거래/우편/경매 요청에 itemId 와 부가 정보를 담아 보낸다.
// - 서버는 itemId 로 아이템을 찾아 소유권을 옮긴다.
//
// 요구사항:
// - 귀속/거래불가 아이템은 플레이어 간 이전 경로로 절대 넘어가면 안 된다.
// - 거래 가능 여부는 서버 권위 데이터로 판정한다(클라 값 신뢰 금지).
// - 이전 시 요청자가 실제 소유자인지(소유권) 확인해야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 귀속·거래불가 아이템이 다른 플레이어에게
// 새어나가는지 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <unordered_map>
#include <functional>
struct Item {
int64_t itemId;
int templateId;
int64_t ownerId;
bool bound; // 귀속 여부(서버 권위)
bool tradable; // 거래 가능 여부(서버 권위)
};
// 클라가 보낸 거래 요청
struct TradeRequest {
int64_t fromPlayer;
int64_t toPlayer;
int64_t itemId;
bool clientSaysTradable; // 클라가 "거래 가능"이라고 보낸 값
};
class ItemTransferService {
public:
ItemTransferService(std::unordered_map<int64_t, Item>& items,
std::function<bool(int64_t, int64_t)> giveToPlayer)
: items_(items), giveToPlayer_(std::move(giveToPlayer)) {}
// 직거래: from -> to
bool trade(const TradeRequest& req) {
auto it = items_.find(req.itemId);
if (it == items_.end()) return false;
Item& item = it->second;
// (A) 거래 가능 여부를 클라가 보낸 값으로 판정
if (!req.clientSaysTradable) return false;
item.ownerId = req.toPlayer;
return giveToPlayer_(req.toPlayer, req.itemId);
}
// 우편 첨부
bool attachToMail(int64_t fromPlayer, int64_t toPlayer, int64_t itemId) {
auto it = items_.find(itemId);
if (it == items_.end()) return false;
Item& item = it->second;
// (B) 귀속/거래불가 검증 없이 그대로 첨부
item.ownerId = toPlayer;
return giveToPlayer_(toPlayer, itemId);
}
// 경매 출품
bool listOnAuction(int64_t sellerId, int64_t itemId) {
auto it = items_.find(itemId);
if (it == items_.end()) return false;
Item& item = it->second;
// (C) 소유자 확인도, 귀속/거래불가 검증도 없음
item.ownerId = 0; // 0 = 경매장 보관
return true;
}
private:
std::unordered_map<int64_t, Item>& items_;
std::function<bool(int64_t, int64_t)> giveToPlayer_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.