43. 인증 완료 전 도착 패킷의 미인증 처리와 상태 게이트 부재

난이도 중 해설 보기 →

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

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] 인증 완료 전에 도착한 게임 패킷의 처리 순서 (핸드셰이크 경합)
// ----------------------------------------------------------------------------
// 시나리오:
//   클라이언트가 접속하면 서버는 먼저 AUTH 패킷의 토큰을 비동기로 검증한다(외부 인증 서버 호출).
//   검증이 끝나면 세션에 계정 식별자를 채우고 인증 완료 상태로 전이한다.
//   그런데 클라이언트는 접속 직후 인증 응답을 기다리지 않고 이동/명령 패킷을 곧바로 보낼 수 있다.
//
// 요구사항:
//   - 인증이 끝나기 전 도착한 게임 명령은 인증된 신원으로만 처리되어야 한다
//     (미인증 상태에서 실행되거나, 잘못된 신원으로 실행되면 안 된다).
//   - 인증 완료 처리(신원 기록)와 이후 명령 처리 사이에 상태가 뒤섞이면 안 된다.
//
// 과제:
//   SessionDispatcher.OnReceive 의 (A)(B) 를 검토하라. 클라가 접속 직후
//   AUTH 와 MOVE 를 연달아 보내고, 인증 검증이 비동기로 진행되는 흐름을 따라가며
//   어떤 결과가 나올 수 있는지, 어떻게 고쳐야 하는지 설명하라.
// ============================================================================

using System.Threading.Tasks;

public enum SessionState { Connecting, Authenticating, Authenticated }

public sealed class Session
{
    public long AccountId;          // 인증 완료 후 채워짐 (기본 0)
    public SessionState State = SessionState.Connecting;
}

public sealed class SessionDispatcher
{
    private readonly IAuthService _auth;
    private readonly ICommandHandler _handler;
    public SessionDispatcher(IAuthService auth, ICommandHandler handler) { _auth = auth; _handler = handler; }

    public void OnReceive(Session session, Packet msg)
    {
        switch (msg.Type)
        {
            case MsgType.Auth:
                session.State = SessionState.Authenticating;
                _auth.ValidateAsync(msg.Token).ContinueWith(t =>
                {
                    session.AccountId = t.Result.AccountId;
                    session.State = SessionState.Authenticated;   // (A)
                });
                break;

            default:
                _handler.Handle(session, msg);                    // (B)
                break;
        }
    }
}

public enum MsgType { Auth, Move, UseItem, Chat }
public sealed class Packet { public MsgType Type; public string Token = ""; public byte[] Body = System.Array.Empty<byte>(); }
public sealed class AuthResult { public long AccountId; }
public interface IAuthService { Task<AuthResult> ValidateAsync(string token); }
public interface ICommandHandler { void Handle(Session session, Packet msg); }
내 리뷰 · C#
내 답안 · 자동 저장

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