unity-cli connector — 에디터 unfocused 시 요청 처리 지연 이슈
저장소: youngwoocho02/unity-cli
버전: com.youngwoocho02.unity-cli-connector v0.3.15 (unity-connector path)
보고일: 2026-04-30
1. 한 줄 요약
Unity 에디터가 OS 포커스를 잃으면 HTTP 요청 처리 tick 간격이 약 16 ms → 약 3,000 ms 로 떨어져, 짧은 timeout 으로 호출하는 클라이언트가 자주 실패합니다. 포트는 정상 listening, listener 스레드도 살아있지만 dispatch 가 EditorApplication.update 에만 의존하기 때문입니다.
2. 환경
| 항목 |
값 |
| Unity Editor |
6000.4.3f1 (URP, HDRP 동시 설치) |
| Plugin |
com.youngwoocho02.unity-cli-connector v0.3.15 (Git URL 의존성) |
| OS |
Windows 10 Pro 19045 |
| 호출 클라이언트 |
PowerShell 5.1 Invoke-RestMethod (Go HTTP client 도 동일 증상으로 추정) |
| 동시 실행 인스턴스 |
Unity 에디터 3개 (포트 8090 / 8091 / 8092 자동 할당) |
3. 사용자가 관찰한 문제
"시간이 지나면 unity-cli connector 연결을 찾지 못하는건지 실행을 못한다."
- 호출 직후에는 응답이 잘 오는데, 잠시 다른 창으로 작업하다 돌아오면 같은 호출이 timeout.
- 재시도하면 또 한참 뒤에 응답 오거나 연속 timeout.
- 에디터를 클릭해 포커스를 줘 두면 한동안 정상.
- Claude Code 의 PreToolUse 훅(
check-unity-port.ps1, dataPath 검증) 도 위와 같이 connector 가 응답을 안 줄 때 dataPath 매칭을 건너뛰고 fail-open 됨 → 검증 누락.
4. 재현 절차
-
Unity 6000.4.3f1 에디터 1개 실행 → 콘솔에 [UnityCliConnector] HTTP server started on port 8090 확인.
-
에디터 창을 다른 창 뒤로 보내거나 minimize → OS 포커스 상실.
-
외부에서 다음 호출을 timeout 2~3 초로 실행:
$body = '{"command":"exec","params":{"code":"return UnityEngine.Application.dataPath;"}}'
Invoke-RestMethod -Uri "http://127.0.0.1:8090/command" `
-Method Post -ContentType "application/json" `
-Body $body -TimeoutSec 3
→ The operation has timed out.
-
같은 호출을 -TimeoutSec 30 으로 재시도 → 성공.
5. 측정 결과
5번 연속 호출하여 응답에 담긴 Time.realtimeSinceStartup 의 차이를 측정 (에디터 unfocused 상태):
| Call |
응답 timestamp (s) |
Δ (직전 호출 대비) |
| 1 |
478.140 |
— |
| 2 |
481.170 |
+3.030 s |
| 3 |
484.230 |
+3.060 s |
| 4 |
487.270 |
+3.040 s |
| 5 |
490.490 |
+3.220 s |
→ EditorApplication.update 가 평균 3 초마다 1회만 호출됨 (≈ 0.3 Hz).
포커스가 있을 때는 같은 측정이 ms 단위(≈ 60 Hz) 로 나옵니다.
동시에 조회한 에디터 상태 (8090, exec 으로 직접 질의)
isApplicationActive = False ← OS 포커스 상실
EditorApplication.isFocused = False
isPlaying / isPaused = False
isCompiling = False ← 도메인 리로드 무관
isUpdating = False ← Asset Import 무관
interactionMode = 1 (NoThrottling) ← 이미 최적값
runInBackground = False ← Player 설정, 에디터엔 무영향
즉 컴파일·Asset Import·Throttling 설정 모두 정상인데도 unfocused 만으로 tick 이 떨어집니다.
6. 코드 분석 — Editor/HttpServer.cs
// L47
EditorApplication.update += ProcessQueue;
// L120-124
static void ProcessQueue()
{
while (s_Queue.TryDequeue(out var item))
ProcessItem(item);
}
// L210-218 (HandleRequest 내부)
var tcs = new TaskCompletionSource<object>();
s_Queue.Enqueue(new WorkItem { Command = command, Parameters = parameters, Tcs = tcs });
ForceEditorUpdate(); // = InternalEditorUtility.RepaintAllViews()
result = await tcs.Task; // ← main thread 가 tick 할 때까지 대기
핵심 흐름:
- HTTP listener (
ListenLoop) 는 백그라운드 스레드 — 포트는 항상 listening, OS focus 와 무관.
- 요청 도착 시
ConcurrentQueue 에 enqueue → main thread 의 EditorApplication.update 콜백에서 dequeue 후 dispatch.
ForceEditorUpdate() 가 RepaintAllViews() 를 호출해 update 를 깨우려 시도하지만, 에디터 창이 모두 unfocused / occluded 인 상황에선 OS 가 paint message 를 거의 보내지 않아 효과가 없음.
- → 결과적으로 응답은 다음 update tick (≈ 3 s 후) 까지 대기.
7. 근본 원인
Windows 가 background process 의 timer message resolution 을 강하게 throttle 하고, Unity 에디터의 main loop tick 도 이와 함께 늦춰집니다. Edit > Preferences → General → Interaction Mode = No Throttling 으로 두어도 background 상태에서는 ~3 s 간격이 관찰됩니다 (위 측정 결과).
Connector 의 dispatch 가 main-thread tick 에만 결합되어 있기 때문에, 이 OS-레벨 throttling 이 그대로 응답 latency 로 노출됩니다.
훅(Claude Code PreToolUse) 자체는 정상 동작이며 본 이슈와 무관 — connector 가 응답을 안 줄 때 fail-open 으로 통과하므로 timeout 을 만들지 않음.
8. 제안 (수정 후보)
다음 중 하나 또는 조합:
A. delayCall 병행 등록 (가장 작은 변경)
EditorApplication.update += ProcessQueue;
EditorApplication.delayCall += ProcessQueue; // 큐에 들어오자마자 즉시 한 번 더 dispatch 시도
delayCall 은 다음 editor message pump 시 한 번 호출되며, 호출 후 자동 해제되므로 enqueue 시마다 다시 등록해야 합니다.
B. enqueue 직후 main-thread tick 강제
ForceEditorUpdate() 를 다음으로 교체 또는 추가:
static void ForceEditorUpdate()
{
try { EditorApplication.QueuePlayerLoopUpdate(); } catch { }
try { UnityEditorInternal.InternalEditorUtility.RepaintAllViews(); } catch { }
}
QueuePlayerLoopUpdate() 는 다음 frame 의 PlayerLoop 실행을 강제 예약하여 EditorApplication.update 호출 가능성을 높입니다. (단 background 에선 Windows timer 자체가 늦어 100% 보장은 안 됨.)
C. Background-safe 명령은 main-thread 우회
exec 의 C# 컴파일은 어차피 main thread 가 필요하지만, 상태 조회성 명령 (/version, /ping, dataPath 같은 read-only) 은 main thread 마샬링 없이 listener 스레드에서 직접 응답하면 unfocused 시에도 ms 단위로 응답 가능. → dataPath 검증 훅 같은 정적 query 케이스의 timeout 가능성을 0 으로 만듦.
D. 별도 timer 로 main-thread tick 펌프
System.Threading.Timer 를 listener 활성 동안 가동하여 50–100 ms 간격으로 EditorApplication.QueuePlayerLoopUpdate() 를 호출. 단 idle 시 CPU 비용 증가 → 큐가 비어있을 때 일시 정지하는 분기 필요.
9. 클라이언트측 회피책 (참고)
수정 전까지 사용 가능한 우회:
- timeout 을 10 초 이상으로 설정 (
-TimeoutSec 30 권장).
- 작업 중 Unity 에디터 창을 다른 모니터에 보이게 두기 (포커스 없어도 visible 이면 paint 가 발생해 tick 빨라짐).
- 동시 실행 에디터 인스턴스 최소화.
10. 첨부 가능한 추가 자료
요청 시 제공 가능:
- 8090 / 8091 / 8092 인스턴스의
Editor.log 발췌 (connector start/stop 라인 정상)
- focused 상태에서의 동일 측정 (Δ 가 ms 단위로 떨어지는지 비교)
[InitializeOnLoad] 도메인 리로드 시 stop/start 동작 로그
11. 결론
이슈의 본질은 dispatch 가 EditorApplication.update 단일 경로에만 묶여 있다는 점입니다. Listener 와 큐는 정상 동작하므로 dispatch 트리거를 강화하면 (제안 A + C 권장) unfocused 환경에서도 안정적으로 ms 단위 응답이 가능합니다.
이슈 검토 부탁드립니다 🙏
unity-cli connector — 에디터 unfocused 시 요청 처리 지연 이슈
1. 한 줄 요약
Unity 에디터가 OS 포커스를 잃으면 HTTP 요청 처리 tick 간격이 약 16 ms → 약 3,000 ms 로 떨어져, 짧은 timeout 으로 호출하는 클라이언트가 자주 실패합니다. 포트는 정상 listening, listener 스레드도 살아있지만 dispatch 가
EditorApplication.update에만 의존하기 때문입니다.2. 환경
com.youngwoocho02.unity-cli-connectorv0.3.15 (Git URL 의존성)Invoke-RestMethod(Go HTTP client 도 동일 증상으로 추정)3. 사용자가 관찰한 문제
check-unity-port.ps1, dataPath 검증) 도 위와 같이 connector 가 응답을 안 줄 때 dataPath 매칭을 건너뛰고 fail-open 됨 → 검증 누락.4. 재현 절차
Unity 6000.4.3f1 에디터 1개 실행 → 콘솔에
[UnityCliConnector] HTTP server started on port 8090확인.에디터 창을 다른 창 뒤로 보내거나 minimize → OS 포커스 상실.
외부에서 다음 호출을 timeout 2~3 초로 실행:
→
The operation has timed out.같은 호출을
-TimeoutSec 30으로 재시도 → 성공.5. 측정 결과
5번 연속 호출하여 응답에 담긴
Time.realtimeSinceStartup의 차이를 측정 (에디터 unfocused 상태):→
EditorApplication.update가 평균 3 초마다 1회만 호출됨 (≈ 0.3 Hz).포커스가 있을 때는 같은 측정이 ms 단위(≈ 60 Hz) 로 나옵니다.
동시에 조회한 에디터 상태 (8090, exec 으로 직접 질의)
즉 컴파일·Asset Import·Throttling 설정 모두 정상인데도 unfocused 만으로 tick 이 떨어집니다.
6. 코드 분석 —
Editor/HttpServer.cs핵심 흐름:
ListenLoop) 는 백그라운드 스레드 — 포트는 항상 listening, OS focus 와 무관.ConcurrentQueue에 enqueue → main thread 의EditorApplication.update콜백에서 dequeue 후 dispatch.ForceEditorUpdate()가RepaintAllViews()를 호출해 update 를 깨우려 시도하지만, 에디터 창이 모두 unfocused / occluded 인 상황에선 OS 가 paint message 를 거의 보내지 않아 효과가 없음.7. 근본 원인
Windows 가 background process 의 timer message resolution 을 강하게 throttle 하고, Unity 에디터의 main loop tick 도 이와 함께 늦춰집니다.
Edit > Preferences → General → Interaction Mode = No Throttling으로 두어도 background 상태에서는 ~3 s 간격이 관찰됩니다 (위 측정 결과).Connector 의 dispatch 가 main-thread tick 에만 결합되어 있기 때문에, 이 OS-레벨 throttling 이 그대로 응답 latency 로 노출됩니다.
훅(Claude Code PreToolUse) 자체는 정상 동작이며 본 이슈와 무관 — connector 가 응답을 안 줄 때 fail-open 으로 통과하므로 timeout 을 만들지 않음.
8. 제안 (수정 후보)
다음 중 하나 또는 조합:
A.
delayCall병행 등록 (가장 작은 변경)delayCall은 다음 editor message pump 시 한 번 호출되며, 호출 후 자동 해제되므로 enqueue 시마다 다시 등록해야 합니다.B. enqueue 직후 main-thread tick 강제
ForceEditorUpdate()를 다음으로 교체 또는 추가:QueuePlayerLoopUpdate()는 다음 frame 의 PlayerLoop 실행을 강제 예약하여EditorApplication.update호출 가능성을 높입니다. (단 background 에선 Windows timer 자체가 늦어 100% 보장은 안 됨.)C. Background-safe 명령은 main-thread 우회
exec의 C# 컴파일은 어차피 main thread 가 필요하지만, 상태 조회성 명령 (/version,/ping, dataPath 같은 read-only) 은 main thread 마샬링 없이 listener 스레드에서 직접 응답하면 unfocused 시에도 ms 단위로 응답 가능. → dataPath 검증 훅 같은 정적 query 케이스의 timeout 가능성을 0 으로 만듦.D. 별도 timer 로 main-thread tick 펌프
System.Threading.Timer를 listener 활성 동안 가동하여 50–100 ms 간격으로EditorApplication.QueuePlayerLoopUpdate()를 호출. 단 idle 시 CPU 비용 증가 → 큐가 비어있을 때 일시 정지하는 분기 필요.9. 클라이언트측 회피책 (참고)
수정 전까지 사용 가능한 우회:
-TimeoutSec 30권장).10. 첨부 가능한 추가 자료
요청 시 제공 가능:
Editor.log발췌 (connector start/stop 라인 정상)[InitializeOnLoad]도메인 리로드 시 stop/start 동작 로그11. 결론
이슈의 본질은 dispatch 가
EditorApplication.update단일 경로에만 묶여 있다는 점입니다. Listener 와 큐는 정상 동작하므로 dispatch 트리거를 강화하면 (제안 A + C 권장) unfocused 환경에서도 안정적으로 ms 단위 응답이 가능합니다.이슈 검토 부탁드립니다 🙏