15. 차단 목록 갱신과 메시지 전달의 경합 (C#)

난이도 하 해설 보기 →

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

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 차단 목록 갱신과 귓속말/메시지 전달의 경합
// ----------------------------------------------------------------------------
// 시나리오:
//   - 플레이어는 다른 플레이어를 "차단(block)" 할 수 있다. 차단하면 그 사람이
//     보낸 귓속말/메시지가 나에게 전달되지 않아야 한다.
//   - 메시지 전달 워커 스레드가 귓속말을 처리하는 동안, 다른 스레드(소셜 API)는
//     수시로 차단 추가/해제를 처리한다(차단 목록이 동시 변경됨).
//   - 차단 판정 규칙: "수신자가 발신자를 차단했는가" 를 검사해야 한다
//     (즉, 수신자의 차단 목록에 발신자가 들어 있으면 전달 금지).
//
// 요구사항:
//   - 차단 목록이 동시 변경돼도 전달 처리가 예외로 죽지 않아야 한다.
//   - 막 차단한 상대의 메시지가 새어 들어오거나, 차단 해제했는데 계속 막히면 안 된다.
//   - 차단 판정 방향(누가 누구를 차단했는가)이 정확해야 한다.
//
// 과제:
//   이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 깨지는지(동시 인터리빙 포함)
//   설명하고, 수정안과 더 나은 설계를 제시하라.
//   (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================

using System;
using System.Collections.Generic;

public class Player
{
    public long Id;
    // 내가 차단한 사람들의 id 집합. 소셜 API 스레드가 Add/Remove 한다.
    public HashSet<long> Blocked = new HashSet<long>();

    public void Deliver(string msg) { /* 세션으로 전송(생략) */ }
}

public class SocialService
{
    // playerId -> Player
    private readonly Dictionary<long, Player> _players;

    public SocialService(Dictionary<long, Player> players) { _players = players; }

    // 소셜 API 스레드: 차단 추가/해제
    public void Block(long me, long target)
    {
        var p = _players[me];
        p.Blocked.Add(target);          // (A)
    }

    public void Unblock(long me, long target)
    {
        var p = _players[me];
        p.Blocked.Remove(target);       // (A)
    }

    // 메시지 워커 스레드: 귓속말 전달
    public void DeliverWhisper(long fromId, long toId, string msg)
    {
        var to = _players[toId];

        // (B) 차단 여부 검사 후 전달
        bool blocked = false;
        foreach (var id in to.Blocked)  // 발신자가 차단 목록에 있는지
        {
            if (id == fromId) { blocked = true; break; }
        }

        // (C) 차단이 아니면 전달
        if (!blocked)
            to.Deliver(msg);
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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