Files
XRLib/Assets/Scripts/UVC/Network/DebounceRequester.cs
2025-09-26 18:08:07 +09:00

167 lines
6.3 KiB
C#

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