32. 길드 해산과 자금고/창고 접근의 수명·자금 경합

난이도 중 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// 시나리오: 길드 시스템. 길드는 공용 자금고(Funds)와 창고를 가진다.
//   - 길드장이 길드를 해산(Disband)하면: 남은 자금을 길드장에게 환급하고,
//     창고를 정리하고, 길드를 레지스트리에서 제거한다.
//   - 길드원/임원은 자금고에서 출금(Withdraw)할 수 있다.
//   해산과 출금은 서로 다른 요청 워커 스레드에서 같은 길드에 동시에 들어올 수 있다.
//
// 요구사항:
//   - 길드 자금은 총합이 보존돼야 한다. 같은 자금이 해산 환급과 출금으로 "두 번"
//     빠져나가면 안 된다(자금 복제/음수 잔액 금지).
//   - 해산이 끝난(제거된) 길드에 대한 출금은 거부돼야 한다(좀비 접근 금지).
//
// 과제: 해산과 출금이 동시에 일어날 때 이 코드가 어떻게 잘못 동작하는지
//       (A)~(C) 지점을 중심으로 진단하고, 자금 보존과 수명 정합성을 보장하도록
//       수정하라.

using System.Collections.Generic;

public sealed class Guild
{
    public long Id;
    public long LeaderId;
    public long Funds;
    public List<int> Storage = new();
}

public sealed class GuildService
{
    private readonly Dictionary<long, Guild> _guilds = new();

    public void Register(Guild g) => _guilds[g.Id] = g;

    public bool Withdraw(long guildId, long memberId, long amount)
    {
        if (!_guilds.TryGetValue(guildId, out var g))   // (A) 길드 존재 확인
            return false;

        if (g.Funds < amount)                            // (B) 잔액 확인
            return false;

        g.Funds -= amount;                               // (B) 차감
        Bank.Give(memberId, amount);                     // 지급
        return true;
    }

    public void Disband(long guildId, long leaderId)
    {
        if (!_guilds.TryGetValue(guildId, out var g)) return;
        if (g.LeaderId != leaderId) return;

        Bank.Give(g.LeaderId, g.Funds);                  // (C) 남은 자금 환급
        g.Funds = 0;
        g.Storage.Clear();                               // 창고 정리
        _guilds.Remove(guildId);                         // (C) 레지스트리에서 제거
    }
}

public static class Bank { public static void Give(long playerId, long amount) {} }
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.