7. 한정 상점 구매와 재고 정합성
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 한정 상점 구매와 재고 정합성
// ----------------------------------------------------------------------------
// 시나리오:
// - 한정 상점(flash sale)에서 아이템을 판다. 각 상품은 잔여 재고(stock)가 있다.
// - 구매 시: 재고가 있으면 골드를 차감하고 재고를 1 줄이고 아이템을 지급한다.
// - 인당 구매 한도(perPlayerLimit)가 있다. 한 플레이어가 그 이상 살 수 없다.
// - 수많은 IO 스레드가 오픈 직후 같은 상품에 동시에 구매 요청을 쏟아붓는다.
//
// 요구사항:
// - 재고보다 더 팔리면 안 된다(oversell 금지).
// - 골드 차감과 재고 차감과 아이템 지급은 함께 일어나야 한다(원자성).
// - 인당 한도를 초과 구매할 수 없다.
// - 골드 부족/재고 소진/한도 초과 시 아무것도 변하지 않아야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜 문제인지 설명하고,
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
using System.Threading;
public class Player
{
public long Id;
public long Gold;
public readonly object Lock = new object();
public void GiveItem(long itemId, int count) { /* 인벤토리 추가 (생략) */ }
}
public class ShopItem
{
public long ItemId;
public long Price;
public int Stock; // (A) 잔여 재고
public int PerPlayerLimit;
// 플레이어별 누적 구매 수량
public Dictionary<long, int> Bought = new(); // (B)
}
public class ShopService
{
// 구매. 성공하면 true.
public bool Buy(ShopItem item, Player buyer, int qty)
{
if (qty <= 0) return false;
// (C) 재고 확인
if (Volatile.Read(ref item.Stock) < qty)
return false;
// (D) 인당 한도 확인
int already = item.Bought.TryGetValue(buyer.Id, out var c) ? c : 0;
if (already + qty > item.PerPlayerLimit)
return false;
// (E) 골드 확인/차감
long cost = item.Price * qty;
lock (buyer.Lock)
{
if (buyer.Gold < cost)
return false;
buyer.Gold -= cost;
}
// (F) 재고 차감
Interlocked.Add(ref item.Stock, -qty);
// (G) 누적 구매 갱신
item.Bought[buyer.Id] = already + qty;
// (H) 아이템 지급
buyer.GiveItem(item.ItemId, qty);
return true;
}
private readonly object _shopLock = new object(); // (I)
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 한정 상점 구매와 재고 정합성
// ----------------------------------------------------------------------------
// 시나리오:
// - 한정 상점(flash sale)에서 아이템을 판다. 각 상품은 잔여 재고(stock)가 있다.
// - 구매 시: 재고가 있으면 골드를 차감하고 재고를 1 줄이고 아이템을 지급한다.
// - 인당 구매 한도(perPlayerLimit)가 있다. 한 플레이어가 그 이상 살 수 없다.
// - 수많은 IO 스레드가 오픈 직후 같은 상품에 동시에 구매 요청을 쏟아붓는다.
//
// 요구사항:
// - 재고보다 더 팔리면 안 된다(oversell 금지).
// - 골드 차감과 재고 차감과 아이템 지급은 함께 일어나야 한다(원자성).
// - 인당 한도를 초과 구매할 수 없다.
// - 골드 부족/재고 소진/한도 초과 시 아무것도 변하지 않아야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜 문제인지 설명하고,
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <atomic>
#include <cstdint>
#include <unordered_map>
#include <mutex>
struct Player {
int64_t id;
int64_t gold;
std::mutex lock;
void GiveItem(int64_t itemId, int32_t count) { /* 인벤토리 추가 (생략) */ }
};
struct ShopItem {
int64_t itemId;
int64_t price;
std::atomic<int32_t> stock; // (A) 잔여 재고
int32_t perPlayerLimit;
// 플레이어별 누적 구매 수량
std::unordered_map<int64_t, int32_t> bought; // (B)
};
class ShopService {
public:
// 구매. 성공하면 true.
bool Buy(ShopItem& item, Player& buyer, int32_t qty) {
if (qty <= 0) return false;
// (C) 재고 확인
if (item.stock.load() < qty)
return false;
// (D) 인당 한도 확인
int32_t already = item.bought[buyer.id];
if (already + qty > item.perPlayerLimit)
return false;
// (E) 골드 확인/차감
int64_t cost = item.price * qty;
{
std::lock_guard<std::mutex> lk(buyer.lock);
if (buyer.gold < cost)
return false;
buyer.gold -= cost;
}
// (F) 재고 차감
item.stock.fetch_sub(qty);
// (G) 누적 구매 갱신
item.bought[buyer.id] = already + qty;
// (H) 아이템 지급
buyer.GiveItem(item.itemId, qty);
return true;
}
private:
std::mutex shopLock_; // (I)
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.