← 일감 목록
T-020

로그인 생명주기(Create → Login → Destroy) 테스트

서버 대기 우선순위 보통 마감 미정

배경

seam은 열렸는데 그 위에 아직 아무것도 안 올렸다.

2026-08-03에 IClientChannel·IDBQueueUser를 열었지만 로그인 흐름은 못 봤다 — Entity.Create()LogicExecutor.Instance.Post로 던지는 비동기라 OnCreate가 그 자리에서 돌지 않았기 때문이다. 2026-08-07에 ILogicExecutor 주입으로 그 장애물을 없앴고, FakeLogicExecutor(즉시 실행 모드)까지 만들어 뒀다. 이제 짜기만 하면 된다.

지금 비어 있는 것:

  • Login()의 패킷 순서 규칙이 주석으로만 지켜진다. "캐릭터를 슬롯보다 먼저 보낸다 — 슬롯이 CharacterId를 참조하므로 클라이언트가 슬롯을 그릴 때 캐릭터를 이미 알고 있어야 한다" (User.cs:135-137). 순서를 바꾸는 커밋이 와도 컴파일은 통과하고 클라만 깨진다.
  • OnCreateAccountRepository 예약이 검증되지 않는다.
  • SessionWatchdog.Sweeppublic으로 열어 뒀는데 테스트 0건이다.

할 일

  • [ ] Create()OnCreateFakeDBQueueAccountRepository가 예약되는지
  • [ ] Login()이 보내는 패킷 5종의 순서 — 특히 S_CharacterListResponseS_WorkStationSlotsResponse보다 앞인지 (FakeClientChannel.Sent가 객체 그대로 담는다)
  • [ ] Login()이 끊긴 통로(IsConnected == false)에서는 Destroy()로 빠지는지 (User.cs:122-126)
  • [ ] Destroy()Disconnect(now)종료 정산: 접속 중 완성된 판정은 지급하고, 푸시는 하지 않고(notify: false), 미달 조각은 버린다
  • [ ] SessionWatchdog.Sweep(now) — 좀비 세션만 끊고 살아 있는 세션은 두는지

🔴 먼저 정해야 하는 것 — 전역 UserManager.Instance

Login()Destroy()도 프로세스 전역 싱글턴을 만진다.

경로 위치
Login()UserManager.Instance.JoinUser(this) User.cs:128
OnDestroyDisconnectUserManager.Instance.LeaveUser(this) User.cs:173

xUnit은 테스트 클래스를 병렬로 돌리므로 그대로 두면 테스트끼리 샌다. (그래서 2026-08-07의 멱등 가드 테스트는 즉시 실행 모드를 못 쓰고 기록 모드로 갔다.) 셋 중 하나를 골라야 한다:

  1. UserManager를 주입 가능하게 만든다 — 근본 해결이지만 SessionUserExtensions· ClientPacketHandler·User 자신이 정적으로 참조해 범위가 패킷 핸들러 전체로 번진다.
  2. [Collection]으로 직렬화한다 — 싸지만 전역 오염을 인정하고 사는 것이다.
  3. 그 경로를 피해 검증한다Login() 대신 그 아래 Send* 단위로 본다. 규칙 검증이 약해진다.

2026-08-07 리팩터링이 UserManager만 싱글턴으로 남긴 이유가 1번의 비용이다. 이 일감의 실제 크기는 여기서 갈린다.

완료 조건

  • 위 체크리스트가 테스트로 있고 dotnet test가 초록이다.
  • 각 테스트마다 red를 확인했다 — 특히 패킷 순서는 순서를 뒤집어 그 테스트만 빨개지는지 본다.
  • 전역 UserManager 처리 방침이 코드에 반영되고, 고른 이유가 주석이나 Agent 로그에 남았다.

막고 있는 것 / 선행 일감

  • 없음. seam(ILogicExecutor 주입 + FakeLogicExecutor)은 2026-08-07에 이미 열렸다.

관련 커밋