✅ 끝났다 (2026-09-07). 빌드 실측으로 확인했다. 창을 20초 이상 끌어도 ① 클라에 종료 알림이 뜨지 않고 ② 서버 콘솔에
무응답으로 N개 세션을 끊었다가 찍히지 않는다. 두 조건이 각각 다른 조각을 증명한다 — 서버 로그가 없으면 타이머가 듣는 것이고, 클라 알림이 없으면 오탐 가드가 듣는 것이다.
배경
T-040에서 서버 몫만 들어갔다. 이건 클라 몫이다.
창을 5초 이상 끌면 연결이 끊긴다(증상 B). 원인은 WindowManager.BeginWindowDrag가
창 이동을 OS 모달 루프에 위임해서 그동안 Unity 메인 루프가 통째로 멈추는 것이다
→ 경위 로그.
B-1(세션 루프를 스레드풀로)은 이미 들어갔지만 그것만으로는 증상이 안 사라진다.
| 무엇을 푸는가 | 상태 | |
|---|---|---|
| B-1 서버 몫 | 큐를 비우는 쪽 — 소켓 송수신이 메인 스레드에 안 묶인다 | ✅ add9b9f |
| 이 일감 (클라 몫) | 큐에 넣는 쪽 — PingManager가 Update()에서 핑을 만든다 |
❌ |
드래그로
Update가 멈추면 큐에 들어갈 핑 자체가 없다. 아무리 큐가 잘 비워져도 넣는 쪽이 멈춰 있으면 소용이 없다. 둘은 한 쌍이다.
⚠️ 빌드에서 드래그 증상은 지금도 그대로 재현된다.
드래그를 없애는 선택지는 없다
데스크톱 상주형이라 OS 타이틀바를 지웠고, 그 대가로 UI 패널이 타이틀바 역할을 한다 —
창 이동은 빼도 되는 기능이 아니다. OS 위임도 스냅·더블클릭 최대화·모니터 간 이동·DPI 전환을
다시 만들지 않기 위한 선택이다
(DesktopWindow 규칙.md §5-7).
할 일
되돌린 커밋 45b55c6에 완성된 형태가 다 있다. 그중 세 가지만 되살린다.
- [x] 핑 송신을
Update()→System.Threading.Timer로 이설- 판정·알림은
Update()에 남긴다. 옮기는 것은 보내는 일 하나뿐이다 - ⚠️ 타이머 스레드에서 Unity API·
ClientLogger를 부르지 않는다 —UnityEngine.Object컨텍스트를 만져 터진다. 예외는 삼켜 문구만 남기고 메인 스레드가 대신 찍는다(타이머 콜백의 미처리 예외는 프로세스를 내린다) - ✅
C_PingRequest는QuietPacketIds라 송신 훅이 로그 전에 조기 반환한다 — 확인해 둔 사항 - ✅
MikaClient.Send는Session?.SendPacket이라 연결 전에는 무해하게 무시된다
- 판정·알림은
- [x] 시간 기준을
Time.unscaledTime→Stopwatch(단조 시계) 로- 타이머 스레드와 메인 스레드가 같은 시각을 읽어야 한다. Unity 시간 API는 메인 스레드 전용이고,
DateTime.UtcNow는 시스템 시각이 보정되면 판정이 함께 흔들린다
- 타이머 스레드와 메인 스레드가 같은 시각을 읽어야 한다. Unity 시간 API는 메인 스레드 전용이고,
- [x] 긴 프레임 정지 뒤 오탐 종료 알림 가드
- 드래그·절전·긴 GC 뒤 첫 프레임에서 "무응답 N초"로 오판해 앱을 내리는 경로를 막는다. 멈춰 있던 구간은 "서버가 응답하지 않은 시간"이 아니다
- [x] 타이머 정리 —
OnDisable·OnDestroy- 없으면 에디터에서 플레이를 멈춰도 타이머 스레드가 도메인 리로드 너머까지 살아남는다
- [x] 빌드 실측 — 창을 20초 이상 끌어도 연결이 유지된다 (2026-09-07 확인)
가져오지 않을 것
- ⛔ 핑 주기 5초 → 2초. 같은 커밋(
45b55c6)에 섞여 있지만 그건 A-2다. A는 A-1(서버 판정 15초)로 결정돼 이미 적용됐고, 그러면 5초가 규칙에 맞는 값이다 (핑 주기 ≤ 판정 시간 ÷ 3 = 5초). 트래픽을 2.5배로 늘릴 이유가 없다. - ⛔
45b55c6의 주석 중 "송신 루프는 이미 스레드풀에서 돈다" — 그 커밋 시점에는 틀린 말이었다(경위 로그의 "첫 진단이 틀렸던 지점"). 지금 사실이 된 것은 B-1 덕분이므로, 주석에는 둘이 한 쌍이라는 것을 적는다 — B-1이 되돌아가면 이 타이머도 함께 무력해진다.
완료 조건
빌드에서 창을 10초 이상 끌어도 연결이 끊기지 않는다.
서버 콘솔에 그 세션이 무응답으로 끊었다로 잡히지 않는다.
⚠️ 에디터에서는 재현되지 않는다 —
BeginWindowDrag가#if !UNITY_EDITOR다. 빌드로 확인하기 전에는 닫지 않는다. 2026-08-28에dotnet build만 통과시키고 불완전한 수정을 올린 전례가 있다.
막고 있는 것 / 선행 일감
- 없음 — 지금 착수할 수 있다. 선행이던 B-1 서버 몫은
add9b9f로 들어갔다.
관련 커밋
45b55c6되살릴 원본 (되돌려져 있다) ·2fb9dcb철회