-
[C#] TaskCanceledException과 OperationCanceledException, HttpClient 타임아웃 구분Microsoft .NET/C# 2026. 11. 10. 00:33

HttpClient요청이 타임아웃됐는데 로그에는 "작업이 취소되었습니다"로만 남아 원인을 못 찾는 경우가 많습니다. 두 예외의 차이와 구분 방법입니다.절차
- 증상 확인
- 원인: .NET 5 이후 타임아웃도 취소로 처리
- 구분: InnerException이 TimeoutException인지 확인
- 타임아웃과 사용자 취소 분리 처리
1. 증상
System.Threading.Tasks.TaskCanceledException: A task was canceled. at System.Net.Http.HttpClient.HandleFailure(Exception e, ...)내가 취소한 적이 없는데 취소 예외가 올라와서 서버 문제인지 코드 문제인지 헷갈립니다.
2. 원인
HttpClient의 기본 타임아웃(100초)이 지나면 내부적으로CancellationToken을 취소시키고, 그 결과TaskCanceledException이 올라옵니다. .NET 5부터는InnerException에TimeoutException이 함께 들어 있습니다.상속 관계는 다음과 같습니다.
OperationCanceledExceptionTaskCanceledException—OperationCanceledException을 상속
따라서
catch (OperationCanceledException)만 쓰면 내 취소와 타임아웃이 함께 잡힙니다.3. 구분 코드
try { var res = await client.GetAsync(url, ct); res.EnsureSuccessStatusCode(); } catch (OperationCanceledException ex) when (ex.InnerException is TimeoutException) { // HttpClient 타임아웃 logger.LogWarning("요청 타임아웃: {Url}", url); } catch (OperationCanceledException) { // 사용자가 취소한 경우(ct) } catch (HttpRequestException ex) { // 연결 실패, DNS, 인증서 오류 등 }4. 타임아웃 시간 지정
기본 100초는 대부분 너무 깁니다. 요청 단위로 줄이는 편이 낫습니다.
client.Timeout = TimeSpan.FromSeconds(10); // 요청마다 적용 using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); cts.CancelAfter(TimeSpan.FromSeconds(10)); var res = await client.GetAsync(url, cts.Token);전역 설정은
IHttpClientFactory등록에서 합니다.builder.Services.AddHttpClient("api", c => { c.BaseAddress = new Uri("https://api.example.com"); c.Timeout = TimeSpan.FromSeconds(10); });정리
- 타임아웃도
TaskCanceledException으로 온다 InnerException is TimeoutException으로 구분한다- 기본 100초를 그대로 두지 않는다
'Microsoft .NET > C#' 카테고리의 다른 글
[C#] HttpClient 재시도, IHttpClientFactory와 표준 리질리언스 정책 (0) 2026.11.10 [C#] record 타입 정리, class와 차이와 with 식 (0) 2026.11.10 [C#] string 타입 형식지정 (0) 2021.10.15 [C#] Thread, Thread 인자 전달, BeginInvoke, Invoke 복붙용 (0) 2021.03.17 [C#] ColorDialog (색상 선택 컨트롤, 다이얼로그) (0) 2020.07.14