12. 귓속말: 대상 로그아웃/채널이동 직후 전송 · C#

난이도 하 해설 보기 →

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

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 귓속말: 대상이 막 로그아웃/채널이동한 직후의 전송
// ----------------------------------------------------------------------------
// 시나리오:
//   - 플레이어가 귓속말을 보내면 클라가 C_Whisper(targetName, text) 를 보낸다.
//   - 서버는 대상 이름으로 온라인 세션을 찾아 그 세션으로 메시지를 전달한다.
//   - 대상은 "막 로그아웃"했거나 "다른 채널/인스턴스로 이동"한 직후일 수 있다.
//     로그아웃/이동은 별도 스레드(OnLogout)에서 처리되어 레지스트리에서 제거된다.
//   - 귓속말 전송(Whisper)과 로그아웃(OnLogout)은 서로 다른 스레드에서 거의 동시에
//     일어날 수 있다.
//
// 요구사항:
//   - 대상이 오프라인/이동 중이면 보낸 사람에게 "상대가 접속 중이 아닙니다"를 알린다.
//   - 이미 끊긴 세션으로 전송하면 안 된다(오송신 금지).
//   - 이름→세션 조회는 동시 변경(로그아웃/로그인)에 안전해야 한다.
//
// 과제:
//   이 코드의 잠재적 문제를 모두 찾고, 어떤 경합에서 예외/오송신/유실이 나는지
//   설명하고, 수정안과 더 나은 설계를 제시하라. (먼저 직접 리뷰 후 answer.md 와 대조)
// ============================================================================

using System;
using System.Collections.Generic;

public class Session
{
    public long   SessionId;
    public string Name;
    public bool   Alive = true;
    public void Send(string msg) { /* 소켓 송신(생략) */ }
}

public class ChatService
{
    private readonly Dictionary<string, Session> _byName = new Dictionary<string, Session>();

    // 귓속말 처리. 로직 워커 스레드에서 호출된다.
    public bool Whisper(Session from, string targetName, string text)
    {
        // (A) 이름으로 대상 세션 조회
        Session target = _byName[targetName];

        // (B) 생사/유효성 확인 없이 바로 송신
        target.Send($"{from.Name} (귓속말): {text}");
        return true;
    }

    // 로그인 시 등록
    public void OnLogin(Session s)
    {
        _byName[s.Name] = s;
    }

    // 로그아웃/채널이동 시 다른 스레드에서 호출
    public void OnLogout(Session s)
    {
        // (C) 레지스트리에서 제거
        _byName.Remove(s.Name);
        s.Alive = false;
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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