19. 친구 추가/차단이 양쪽에서 동시에 일어나는 상황 (소셜 상태 정합)

난이도 하 해설 보기 →

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

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 친구 추가/차단이 양쪽에서 동시에 일어나는 상황 (소셜 상태 정합)
// ----------------------------------------------------------------------------
// 시나리오:
//   - 플레이어는 "친구 목록" 과 "차단 목록" 을 가진다.
//   - 친구 추가(AddFriend): A 가 B 를 친구로 추가하면 양쪽 친구 목록에 서로를 넣는다
//     (양방향). 단, 상대가 나를 차단했거나 내가 상대를 차단했으면 추가하면 안 된다.
//   - 차단(Block): A 가 B 를 차단하면 A 의 차단 목록에 B 를 넣고, 기존 친구 관계가
//     있으면 양쪽에서 친구를 끊는다.
//   - 친구/차단 요청은 서로 다른 두 플레이어가 동시에(예: A 는 B 를 친구 추가, B 는
//     동시에 A 를 차단) 보낼 수 있고, 서버는 여러 스레드에서 동시 처리한다.
//
// 요구사항:
//   - 친구 관계는 항상 양방향으로 일관돼야 한다(한쪽만 친구인 상태 금지).
//   - 차단된 상대와는 친구가 될 수 없다("차단 중인데 친구 목록에 있음" 금지).
//   - 동시 추가/차단에도 컬렉션이 손상되거나 예외로 죽지 않아야 한다.
//
// 과제:
//   이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 깨지는지(동시 인터리빙 포함)
//   설명하고, 수정안과 더 나은 설계를 제시하라.
//   (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================

using System;
using System.Collections.Generic;

public class SocialManager
{
    // 플레이어 -> 친구 ID 집합
    private readonly Dictionary<long, HashSet<long>> _friends = new();
    // 플레이어 -> 차단한 ID 집합
    private readonly Dictionary<long, HashSet<long>> _blocks  = new();

    private HashSet<long> FriendsOf(long id)
    {
        if (!_friends.TryGetValue(id, out var s)) { s = new HashSet<long>(); _friends[id] = s; }
        return s;
    }
    private HashSet<long> BlocksOf(long id)
    {
        if (!_blocks.TryGetValue(id, out var s)) { s = new HashSet<long>(); _blocks[id] = s; }
        return s;
    }

    // 친구 추가(양방향)
    public bool AddFriend(long a, long b)
    {
        if (a == b) return false;

        // (A) 차단 검사 후 양쪽 목록에 추가 — 검사와 추가가 분리되어 있다
        if (BlocksOf(a).Contains(b) || BlocksOf(b).Contains(a)) return false;

        FriendsOf(a).Add(b);
        FriendsOf(b).Add(a);
        return true;
    }

    // 차단
    public bool Block(long a, long b)
    {
        if (a == b) return false;

        // (B) 차단 추가
        BlocksOf(a).Add(b);

        // (C) 기존 친구 관계 해제
        FriendsOf(a).Remove(b);
        FriendsOf(b).Remove(a);
        return true;
    }

    public bool IsFriend(long a, long b) => FriendsOf(a).Contains(b);
    public bool IsBlocked(long a, long b) => BlocksOf(a).Contains(b);
}
내 리뷰 · C#
내 답안 · 자동 저장

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