34. 공유 사전 압축 프로토콜의 DictAdd 유실·순서 역전 (C#)

난이도 상 해설 보기 →

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

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 공유 사전(dictionary) 기반 문자열 압축 프로토콜
// ----------------------------------------------------------------------------
// 시나리오 (프로토콜 / 서버-클라):
//   - 채팅 필터 문구, 아이템 접두 수식어, 반복 시스템 메시지처럼 자주 등장하는
//     문자열을 매번 그대로 보내지 않고, 서버와 클라가 "공유 사전"을 유지하며
//     문자열을 인덱스(ushort) 하나로 치환해 주고받는다.
//   - 새 문자열이 처음 등장하면 서버가 사전에 추가하고, 클라에게
//     [DictAdd: index, string] 메시지로 통지한다. 이후 그 문자열이 다시
//     등장하면 [DictRef: index] 로 인덱스만 보낸다.
//   - DictAdd 와 DictRef 는 서로 다른 우선순위 큐(예: 안정성 큐/일반 큐)를 타고
//     나갈 수 있어 네트워크 상에서 도착 순서가 뒤바뀌거나, DictAdd 패킷이
//     간헐적으로 유실될 수 있다(비신뢰 구간을 경유하는 배치 전송 가정).
//   - 서버는 사전에 추가한 즉시 그 인덱스를 참조하는 DictRef 를 만들어 낼 수 있다.
//
// 요구사항:
//   - 클라이언트가 특정 인덱스를 담은 DictRef 를 받았을 때, 그 인덱스가
//     가리키는 문자열은 서버가 그 인덱스에 등록한 문자열과 항상 같아야 한다
//     (같은 인덱스가 서로 다른 문자열로 오해석되면 안 된다).
//   - 사전 상태가 어긋난 것을 감지했다면, 엉뚱한 문자열을 화면에 노출하는 대신
//     안전하게 처리해야 한다.
//
// 과제: (A)(B) 지점을 검토하라. DictAdd 유실이나 순서 역전이 있을 때 클라이언트가
//       어떻게 잘못된 문자열을 표시하게 되는지 원인을 밝히고 수정하라.

using System.Collections.Generic;

public sealed class ClientDictionary
{
    // index -> string
    private readonly Dictionary<int, string> _entries = new Dictionary<int, string>();

    // 서버 -> 클라: 새 사전 항목 통지
    public void OnDictAdd(int index, string value)
    {
        // (A) 유실/순서 역전 여부를 고려하지 않고 항상 최신 통지를 그대로 반영
        _entries[index] = value;
    }

    // 서버 -> 클라: 인덱스로 문자열 참조
    public string Resolve(int index)
    {
        // (B) 등록되지 않은 인덱스에 대한 처리만 있고, "다른 값으로 덮였을 가능성"은
        //     고려하지 않는다.
        if (_entries.TryGetValue(index, out var value))
            return value;

        return "[UNKNOWN]";
    }
}

public sealed class ServerDictionary
{
    private readonly Dictionary<string, int> _stringToIndex = new Dictionary<string, int>();
    private readonly List<string> _indexToString = new List<string>();

    // 문자열을 사전에 등록(없으면 추가)하고, 클라에 보낼 (index, isNew) 를 반환한다.
    public (int index, bool isNew) Intern(string value)
    {
        if (_stringToIndex.TryGetValue(value, out var existing))
            return (existing, false);

        int newIndex = _indexToString.Count;
        _indexToString.Add(value);
        _stringToIndex[value] = newIndex;
        return (newIndex, true);
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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