2. 보상 지급을 여러 독립 커밋으로 쪼갠 부분 커밋/이중 지급

난이도 상 해설 보기 →

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

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 보상 지급을 여러 독립 커밋으로 쪼갠 부분 커밋/이중 지급
// ----------------------------------------------------------------------------
// 시나리오 (영속화/DB · 서버-클라):
//   - 퀘스트 보상 지급은 DB 에 세 가지를 남긴다: ① 인벤토리에 아이템 추가,
//     ② 지갑에 골드 가산, ③ 수령 로그 기록(중복 수령 방지용).
//   - 아래 IDb.Execute 는 호출 한 번이 곧바로 커밋되는 autocommit 모델이다(각 문장이
//     독립 트랜잭션). 서버는 처리 중 예외/크래시/배포로 중간에 멈출 수 있다.
//
// 요구사항:
//   - 한 번의 보상 지급은 "전부 반영" 또는 "전혀 반영 안 됨" 이어야 한다(원자성).
//   - 같은 (playerId, questId) 보상은 두 번 지급되면 안 된다(멱등성). 재시도/재기동에도.
//
// 과제:
//   이 지급 로직의 잠재적 문제를 모두 찾고, 중간 단계에서 실패하거나 재시도될 때
//   어떤 부분 상태/이중 지급이 생기는지 설명하고, 수정안과 더 나은 설계를 제시하라.
//   (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================

public interface IDb
{
    void Execute(string sql, params object[] args);   // 각 호출이 즉시 커밋(autocommit)
    int  QueryInt(string sql, params object[] args);
}

public class RewardService
{
    private readonly IDb _db;

    public RewardService(IDb db) { _db = db; }

    public void GrantQuestReward(long playerId, long questId, long itemId, int gold)
    {
        int already = _db.QueryInt(
            "SELECT COUNT(*) FROM quest_reward_log WHERE player_id=? AND quest_id=?",
            playerId, questId);                                          // (A)
        if (already > 0) return;

        _db.Execute("INSERT INTO inventory(player_id,item_id,count) VALUES(?,?,1)",
                    playerId, itemId);                                   // (B)

        _db.Execute("UPDATE wallet SET gold = gold + ? WHERE player_id=?",
                    gold, playerId);                                     // (C)

        _db.Execute("INSERT INTO quest_reward_log(player_id,quest_id) VALUES(?,?)",
                    playerId, questId);                                  // (D)
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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