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++
// ============================================================================
// [코드리뷰 문제] C++ - 친구 추가/차단이 양쪽에서 동시에 일어나는 상황 (소셜 상태 정합)
// ----------------------------------------------------------------------------
// 시나리오:
// - 플레이어는 "친구 목록" 과 "차단 목록" 을 가진다.
// - 친구 추가(AddFriend): A 가 B 를 친구로 추가하면 양쪽 친구 목록에 서로를 넣는다
// (양방향). 단, 상대가 나를 차단했거나 내가 상대를 차단했으면 추가하면 안 된다.
// - 차단(Block): A 가 B 를 차단하면 A 의 차단 목록에 B 를 넣고, 기존 친구 관계가
// 있으면 양쪽에서 친구를 끊는다.
// - 친구/차단 요청은 서로 다른 두 플레이어가 동시에(예: A 는 B 를 친구 추가, B 는
// 동시에 A 를 차단) 보낼 수 있고, 서버는 여러 스레드에서 동시 처리한다.
//
// 요구사항:
// - 친구 관계는 항상 양방향으로 일관돼야 한다(한쪽만 친구인 상태 금지).
// - 차단된 상대와는 친구가 될 수 없다("차단 중인데 친구 목록에 있음" 금지).
// - 동시 추가/차단에도 컨테이너가 손상되거나 정의되지 않은 동작이 나면 안 된다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 깨지는지(동시 인터리빙 포함)
// 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <unordered_map>
#include <unordered_set>
class SocialManager {
public:
// 친구 추가(양방향)
bool AddFriend(std::int64_t a, std::int64_t b) {
if (a == b) return false;
// (A) 차단 검사 후 양쪽 목록에 추가 — 검사와 추가가 분리되어 있다
if (BlocksOf(a).count(b) || BlocksOf(b).count(a)) return false;
FriendsOf(a).insert(b);
FriendsOf(b).insert(a);
return true;
}
// 차단
bool Block(std::int64_t a, std::int64_t b) {
if (a == b) return false;
// (B) 차단 추가
BlocksOf(a).insert(b);
// (C) 기존 친구 관계 해제
FriendsOf(a).erase(b);
FriendsOf(b).erase(a);
return true;
}
bool IsFriend(std::int64_t a, std::int64_t b) { return FriendsOf(a).count(b) != 0; }
bool IsBlocked(std::int64_t a, std::int64_t b) { return BlocksOf(a).count(b) != 0; }
private:
std::unordered_set<std::int64_t>& FriendsOf(std::int64_t id) { return friends_[id]; }
std::unordered_set<std::int64_t>& BlocksOf(std::int64_t id) { return blocks_[id]; }
std::unordered_map<std::int64_t, std::unordered_set<std::int64_t>> friends_;
std::unordered_map<std::int64_t, std::unordered_set<std::int64_t>> blocks_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.