using Cysharp.Threading.Tasks; using System; using System.Collections.Generic; using System.Threading; using UVC.Network; namespace AssetsUVC.Network { /// /// HTTP 요청을 디바운스(debounce) 처리하기 위한 유틸리티 클래스 /// /// /// 이 클래스는 짧은 시간 내에 반복적으로 발생하는 HTTP 요청을 제어하여 /// 마지막 요청만 실행되도록 합니다. 사용자 입력에 따른 API 호출이나 /// 실시간 검색과 같이 연속된 요청을 최적화할 때 유용합니다. /// /// 기본 사용법: /// /// // DebounceRequester 인스턴스 생성 /// var requester = new DebounceRequester(); /// /// // 연속된 검색 요청 예제 /// void OnSearchTextChanged(string searchText) /// { /// requester.Request( /// 300, // 300ms 디바운스 /// "/api/search", /// "get", /// $"{{\"query\": \"{searchText}\"}}", /// null, /// false, /// result => { /// // 검색 결과 처리 /// DisplaySearchResults(result); /// } /// ); /// } /// /// public class DebounceRequester { /// /// 취소 토큰 소스. 이전 요청을 취소하는 데 사용됩니다. /// private CancellationTokenSource cts = new CancellationTokenSource(); /// /// 취소 토큰을 재설정하여 이전 요청을 취소합니다. /// /// /// 이 메소드는 새로운 요청이 시작될 때마다 호출되어 이전에 예약된 요청을 취소합니다. /// private void resetCancellationToken() { cts.Cancel(); cts = new CancellationTokenSource(); } /// /// DebounceRequester가 사용한 리소스를 해제합니다. /// /// /// 이 클래스의 사용이 끝나면 항상 Dispose를 호출하여 리소스 누수를 방지해야 합니다. /// /// /// /// // 리소스 해제 예제 /// public class SearchManager : IDisposable /// { /// private DebounceRequester requester = new DebounceRequester(); /// /// public void Dispose() /// { /// requester.Dispose(); /// } /// } /// /// public void Dispose() { cts.Cancel(); cts.Dispose(); } /// /// 지정된 시간(밀리초) 동안 디바운스된 HTTP 요청을 수행합니다. /// /// 응답 데이터를 변환할 타입 /// 디바운스 시간(밀리초) /// 요청할 URL (http가 포함되지 않은 경우 Domain과 결합) /// HTTP 메소드 (get, post 등) /// 요청 본문으로 전송할 JSON 문자열 (null 가능) /// 추가할 헤더 정보 (null 가능) /// 인증 토큰 사용 여부 /// 요청 완료 후 실행할 콜백 함수 (null 가능) /// /// 이 메소드는 호출된 후 지정된 밀리초 동안 대기하고, 그 시간 내에 다시 호출되면 /// 이전 요청을 취소하고 새로운 타이머를 시작합니다. 디바운스 시간이 경과한 후에만 /// 실제 HTTP 요청이 수행됩니다. /// /// /// /// // 실시간 타이핑 검색 예제 /// private DebounceRequester searchRequester = new DebounceRequester(); /// /// public void OnSearchInputChanged(string searchText) /// { /// // 사용자가 타이핑을 멈춘 후 500ms 후에 검색 요청 /// searchRequester.Request>( /// 500, /// "/api/products/search", /// "get", /// $"{{\"query\": \"{searchText}\"}}", /// null, /// true, /// results => { /// // 검색 결과 UI 업데이트 /// UpdateSearchResultsUI(results); /// } /// ); /// } /// /// // 자동 저장 기능 예제 /// private DebounceRequester autoSaveRequester = new DebounceRequester(); /// /// public void OnDocumentChanged(string documentId, string content) /// { /// // 문서 변경 후 2초 동안 추가 변경이 없으면 저장 /// var body = $"{{\"Id\": \"{documentId}\", \"content\": \"{content}\"}}"; /// /// autoSaveRequester.Request( /// 2000, /// "/api/documents/save", /// "post", /// body, /// null, /// true, /// result => { /// if (result.success) { /// ShowSavedNotification(); /// } /// } /// ); /// } /// /// public void Request(int milliseconds, string url, string method, string body = null, Dictionary header = null, bool useAuth = false, Action action = null) { resetCancellationToken(); UniTask.Delay(TimeSpan.FromMilliseconds(milliseconds), cancellationToken: cts.Token, ignoreTimeScale: false).ContinueWith(async () => { if (action != null) { T result = await HttpRequester.Request(url, method, body, header, useAuth); action.Invoke(result); } else { HttpRequester.Request(url, method, body, header, useAuth).Forget(); } }); } } }