Runtime: HttpClient๋Š” ์‹œ๊ฐ„ ์ดˆ๊ณผ ์‹œ TaskCanceledException์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2017๋…„ 05์›” 25์ผ  ยท  119์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: dotnet/runtime

HttpClient๋Š” ์ผ๋ถ€ ์ƒํ™ฉ์—์„œ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์‹œ TaskCanceledException์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์„œ๋ฒ„๊ฐ€ ๊ณผ๋ถ€ํ•˜ ์ƒํƒœ์ผ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ๋Š˜๋ ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ MSDN ํฌ๋Ÿผ ์Šค๋ ˆ๋“œ๋Š” ๊ฒฝํ—˜ํ•œ ๋ฌธ์ œ์˜ ๋ณธ์งˆ์„ ํฌ์ฐฉํ•ฉ๋‹ˆ๋‹ค. https://social.msdn.microsoft.com/Forums/en-US/d8d87789-0ac9-4294-84a0-91c9fa27e353/bug-in-httpclientgetasync-should-throw -webexception-not-taskcancelledexception?forum=netfxnetcom&prof=required

๊ฐ์‚ฌ ํ•ด์š”

area-System.Net.Http enhancement

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

@karelz ๋Š” ์ด ์˜คํ•ด์˜ ์†Œ์ง€๊ฐ€ ์žˆ๋Š” ์˜ˆ์™ธ์˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š” ๋‹ค๋ฅธ ๊ณ ๊ฐ์œผ๋กœ ๋ฉ”๋ชจ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. :)

๋ชจ๋“  119 ๋Œ“๊ธ€

@awithy ์–ด๋–ค ๋ฒ„์ „์˜ .NET Core๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

1.1

์˜ฌ๋ฐ”๋ฅธ ๋””์ž์ธ์ด๋“  ์•„๋‹ˆ๋“ , ์„ค๊ณ„์ƒ OperationCanceledExceptions๋Š” ์‹œ๊ฐ„ ์ดˆ๊ณผ์— ๋Œ€ํ•ด throw๋ฉ๋‹ˆ๋‹ค(TaskCanceledException์€ OperationCanceledException์ž„).
์ฐธ์กฐ: @davidsh

์šฐ๋ฆฌ ํŒ€์€ ์ด๊ฒƒ์ด ์ง๊ด€์ ์ด์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์ง€๋งŒ ์„ค๊ณ„๋œ ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ถˆํ–‰ํžˆ๋„, ์šฐ๋ฆฌ๊ฐ€ ์ด๊ฒƒ์„ ์ณค์„ ๋•Œ ์šฐ๋ฆฌ๋Š” ์ทจ์†Œ ์†Œ์Šค๋ฅผ ์ฐพ๋Š” ๋ฐ ์•ฝ๊ฐ„์˜ ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ž‘์—… ์ทจ์†Œ์™€ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ๊ฝค ์ถ”ํ•œ ์ผ์ž…๋‹ˆ๋‹ค(์ด๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž ์ง€์ • ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค). ์ด๊ฒƒ๋„ ์ทจ์†Œ๋ผ๋Š” ๊ฐœ๋…์„ ๋‚จ์šฉํ•œ ๊ฒƒ ๊ฐ™๋‹ค.

๋น ๋ฅธ ์‘๋‹ต์— ๋‹ค์‹œ ํ•œ๋ฒˆ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

์„ค๊ณ„์ƒ + ์ง€๋‚œ 6๊ฐœ์›” ์ด์ƒ ๋™์•ˆ 2๋ช…์˜ ๊ณ ๊ฐ์ด ๋ถˆ๋งŒ์„ ์ œ๊ธฐํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
ํ์‡„.

@karelz ๋Š” ์ด ์˜คํ•ด์˜ ์†Œ์ง€๊ฐ€ ์žˆ๋Š” ์˜ˆ์™ธ์˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š” ๋‹ค๋ฅธ ๊ณ ๊ฐ์œผ๋กœ ๋ฉ”๋ชจ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. :)

์ •๋ณด๋ฅผ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ƒ์œ„ ๊ฒŒ์‹œ๋ฌผ์—๋„ ํˆฌํ‘œํ•ด ์ฃผ์„ธ์š”(๋ฌธ์ œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ์Œ). ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์˜ฌ๋ฐ”๋ฅธ ์„ค๊ณ„ ์—ฌ๋ถ€์— ๊ด€๊ณ„์—†์ด ์„ค๊ณ„์ƒ OperationCanceledExceptions๋Š” ์‹œ๊ฐ„ ์ดˆ๊ณผ์— ๋Œ€ํ•ด throw๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋‚˜์œ ๋””์ž์ธ IMO์ž…๋‹ˆ๋‹ค. ์š”์ฒญ์ด ์‹ค์ œ๋กœ ์ทจ์†Œ๋˜์—ˆ๋Š”์ง€(์ฆ‰, SendAsync ์— ์ „๋‹ฌ๋œ ์ทจ์†Œ ํ† ํฐ์ด ์ทจ์†Œ๋˜์—ˆ๋Š”์ง€) ๋˜๋Š” ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๊ฒฝ๊ณผํ–ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. ํ›„์ž์˜ ๊ฒฝ์šฐ ์žฌ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์ด ํ•ฉ๋ฆฌ์ ์ด์ง€๋งŒ ์ „์ž์˜ ๊ฒฝ์šฐ์—๋Š” ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ์— ๋”ฑ ๋งž๋Š” TimeoutException ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

์˜ˆ, ์ด๊ฒƒ์€ ๋˜ํ•œ ์šฐ๋ฆฌ ํŒ€์„ ๋ฃจํ”„์— ๋น ๋œจ๋ ธ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์ „์ฒด ์Šคํƒ ์˜ค๋ฒ„ํ”Œ ๋กœ ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฉฐ์น  ์ „์— ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๊ฒŒ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. https://www.thomaslevesque.com/2018/02/25/better-timeout-handling-with-httpclient/

๊ต‰์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ๊ด€์‹ฌ์ด ๋” ์ปค์ง์— ๋”ฐ๋ผ ์žฌ๊ฐœ๋ฐฉ - StackOverflow ์Šค๋ ˆ๋“œ๋Š” ์ด์ œ 69๊ฐœ์˜ ์ฐฌ์„ฑ์„ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค.

cc @dotnet/ncl

์–ด๋–ค ์†Œ์‹์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์—ฌ์ „ํžˆ dotnet core 2.0์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

2.1 ์ดํ›„์˜ ์ฃผ์š” ๋ฌธ์ œ ๋ชฉ๋ก์— ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ 2.0/2.1์—์„œ๋Š” ์ˆ˜์ •๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ˆ˜ํ–‰ํ•  ์ž‘์—…์—๋Š” ๋ช‡ ๊ฐ€์ง€ ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. $# TaskCanceledException TimeoutException ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.

    • ์žฅ์ : ๋Ÿฐํƒ€์ž„ ์‹œ ๋ช…์‹œ์  ์ทจ์†Œ ์ž‘์—…๊ณผ ํƒ€์ž„์•„์›ƒ์„ ์‰ฝ๊ฒŒ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • ๋‹จ์ : ๊ธฐ์ˆ ์ ์ธ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ - ์ƒˆ๋กœ์šด ์œ ํ˜•์˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  2. $#$ TimeoutException $#$์™€ ๊ฐ™์€ ๋‚ด๋ถ€ ์˜ˆ์™ธ์™€ ํ•จ๊ป˜ TaskCanceledException ๋˜์ง€๊ธฐ

    • ์žฅ์ : ๋Ÿฐํƒ€์ž„ ์‹œ ๋ช…์‹œ์  ์ทจ์†Œ ์ž‘์—…๊ณผ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜ธํ™˜๋˜๋Š” ์˜ˆ์™ธ ์œ ํ˜•์ž…๋‹ˆ๋‹ค.

    • ์—ด๋ฆฐ ์งˆ๋ฌธ: InnerException ( TimeoutException.InnerException ๋กœ)๋ฅผ ๋ณด์กดํ•˜๋ฉด์„œ ์›๋ž˜์˜ TaskCanceledException ์Šคํƒ์„ ๋ฒ„๋ฆฌ๊ณ  ์ƒˆ ์Šคํƒ์„ ๋˜์ ธ๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ์›๋ž˜ TaskCanceledException ๋ฅผ ๋ณด์กดํ•˜๊ณ  ํฌ์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?



      • ์ƒˆ TaskCanceledException -> ์ƒˆ TimeoutException -> ์›๋ž˜ TaskCanceledException(null์ผ ์ˆ˜ ์žˆ๋Š” ์›๋ž˜ InnerException ํฌํ•จ)


      • ๋˜๋Š”: ์ƒˆ๋กœ์šด TaskCanceledException -> ์ƒˆ๋กœ์šด TimeoutException -> ์›๋ž˜ TaskCanceledException.InnerException(null์ผ ์ˆ˜ ์žˆ์Œ)



  3. ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์ด์œ ๋กœ ์–ธ๊ธ‰ํ•˜๋Š” ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ TaskCanceledException ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.

    • ์žฅ์ : ๋ฉ”์‹œ์ง€/๋กœ๊ทธ์—์„œ ๋ช…์‹œ์  ์ทจ์†Œ ์ž‘์—…๊ณผ ํƒ€์ž„์•„์›ƒ ๊ตฌ๋ถ„ ๊ฐ€๋Šฅ

    • ๋‹จ์ : ๋Ÿฐํƒ€์ž„์— ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ "๋””๋ฒ„๊ทธ ์ „์šฉ"์ž…๋‹ˆ๋‹ค.

    • ์—ด๋ฆฐ ์งˆ๋ฌธ: [2]์™€ ๋™์ผ - ์›๋ž˜ TaskCanceledException(๋ฐ ์Šคํƒ)์„ ๋ž˜ํ•‘ํ•˜๊ฑฐ๋‚˜ ๊ต์ฒดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์›๋ž˜ TaskCanceledException ์˜ ์›๋ž˜ ์Šคํƒ์„ ๋ฒ„๋ฆฌ๊ณ  ์˜ต์…˜ [2]์— ๊ธฐ๋Œ€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
@stephentoub @davidsh ์ƒ๊ฐ์ด ์žˆ์œผ์‹ญ๋‹ˆ๊นŒ?

BTW: ์‹œ๊ฐ„ ์ œํ•œ์„ ์„ค์ •ํ•œ HttpClient.SendAsync ์—์„œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
https://github.com/dotnet/corefx/blob/30fb78875148665b98748ede3013641734d9bf5c/src/System.Net.Http/src/System/Net/Http/HttpClient.cs#L433 -L461

์ตœ์ƒ์œ„ ์˜ˆ์™ธ๋Š” ํ•ญ์ƒ HttpRequestException์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด HttpClient์˜ ํ‘œ์ค€ API ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค.

๊ทธ ์•ˆ์—์„œ ๋‚ด๋ถ€ ์˜ˆ์™ธ๋กœ "์‹œ๊ฐ„ ์ดˆ๊ณผ"๋Š” ์•„๋งˆ๋„ TimeoutException์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์‹ค, ์ด๊ฒƒ์„ ๋‹ค๋ฅธ ๋ฌธ์ œ์™€ ๊ฒฐํ•ฉํ•œ๋‹ค๋ฉด(WebExceptionStatus์™€ ์œ ์‚ฌํ•œ ์ƒˆ๋กœ์šด ์—ด๊ฑฐํ˜• ์†์„ฑ์„ ํฌํ•จํ•˜๋„๋ก HttpRequestion์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ด€๋ จํ•˜์—ฌ) ๊ฐœ๋ฐœ์ž๋Š” ํ•ด๋‹น ์—ด๊ฑฐํ˜•์„ ํ™•์ธํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๋ฉฐ ๋‚ด๋ถ€ ์˜ˆ์™ธ๋ฅผ ์ „ํ˜€ ๊ฒ€์‚ฌํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์˜ต์…˜ 1)์ด์ง€๋งŒ HttpClient API์˜ ์ตœ์ƒ์œ„ ์˜ˆ์™ธ๊ฐ€ ํ•ญ์ƒ HttpRequestException์ด๋ผ๋Š” ํ˜„์žฌ ์•ฑ ํ˜ธํ™˜ API ๋ชจ๋ธ์„ ๊ณ„์† ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ : ๋‹ค์Œ๊ณผ ๊ฐ™์€ Stream API์—์„œ HTTP ์‘๋‹ต ์ŠคํŠธ๋ฆผ์„ ์ง์ ‘ ์ฝ์„ ๋•Œ "์‹œ๊ฐ„ ์ดˆ๊ณผ"๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

```c#
var ํด๋ผ์ด์–ธํŠธ = ์ƒˆ๋กœ์šด HttpClient();
์ŠคํŠธ๋ฆผ responseStream = client.GetStreamAsync(url)๋ฅผ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.

int bytesRead = responseStream.ReadAsync(๋ฒ„ํผ, 0, ๋ฒ„ํผ ๊ธธ์ด)๋ฅผ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.
```

๊ณ„์†ํ•ด์„œ System.IO.IOException ๋“ฑ์„ ์ตœ์ƒ์œ„ ์˜ˆ์™ธ๋กœ ๋˜์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค(์‹ค์ œ๋กœ ์ด๊ฒƒ์— ๋Œ€ํ•ด SocketsHttpHandler์— ๋ช‡ ๊ฐ€์ง€ ๋ฒ„๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค).

@davidsh ๋ช…์‹œ์  ์‚ฌ์šฉ์ž ์ทจ์†Œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ HttpRequestException ๋ฅผ ๋˜์ง€๋ผ๊ณ  ์ œ์•ˆํ•ฉ๋‹ˆ๊นŒ? ๊ทธ๋“ค์€ ์˜ค๋Š˜ TaskCanceledException ๋ฅผ ๋˜์กŒ๊ณ  ๊ดœ์ฐฎ์•„ ๋ณด์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๋ฐ”๊พธ๊ณ  ์‹ถ์€ ๊ฒƒ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ...
ํด๋ผ์ด์–ธํŠธ ์ธก ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ $#$ TaskCanceledException HttpRequestException ๋กœ ๋…ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

@davidsh ๋ช…์‹œ์  ์‚ฌ์šฉ์ž ์ทจ์†Œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ HttpRequestException์„ throwํ•  ๊ฒƒ์„ ์ œ์•ˆํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ๊ทธ๊ฒƒ๋“ค์€ ์˜ค๋Š˜ TaskCanceledException์„ ๋˜์กŒ๊ณ  ๊ดœ์ฐฎ์•„ ๋ณด์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๋ฐ”๊พธ๊ณ  ์‹ถ์€ ๊ฒƒ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ...

์ด ์งˆ๋ฌธ์— ๋‹ตํ•˜๊ธฐ ์ „์— ์ •ํ™•ํ•œ ํ˜„์žฌ ๋™์ž‘์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด .NET Framework๋ฅผ ํฌํ•จํ•œ ์Šคํƒ์˜ ๋‹ค์–‘ํ•œ ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ์ผ๋ถ€ ์Šคํƒ์—์„œ OperationCanceledException์˜ ๋‚ด๋ถ€ ์˜ˆ์™ธ์™€ ํ•จ๊ป˜ HttpRequestException์„ throwํ•ฉ๋‹ˆ๋‹ค. TaskCanceledException์€ OperationCanceledException์˜ ํŒŒ์ƒ ์œ ํ˜•์ž…๋‹ˆ๋‹ค.

๋ช…์‹œ์  ์‚ฌ์šฉ์ž ์ทจ์†Œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ

๋˜ํ•œ "๋ช…์‹œ์ "์ด ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ์ •์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, HttpClient.Timeout ์†์„ฑ์„ "๋ช…์‹œ์ "์œผ๋กœ ์„ค์ •ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ์•„๋‹ˆ์˜ค๋ผ๊ณ  ๋งํ•˜๋Š” ๊ฒƒ ๊ฐ™์•„์š”. CancellationTokenSource ๊ฐ์ฒด์—์„œ .Cancel()์„ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ(CancellationToken ๊ฐ์ฒด์— ์ „๋‹ฌ๋œ ๊ฒƒ์— ์˜ํ–ฅ์„ ๋ฏธ์นจ)? "๋ช…์‹œ์ "์ธ๊ฐ€์š”? ์šฐ๋ฆฌ๊ฐ€ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ์˜ˆ๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

.NET Framework๋Š” HttpClient.Timeout ๋ฅผ ์„ค์ •ํ•  ๋•Œ๋„ TaskCanceledException์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ ์ทจ์†Œ์™€ ๊ตฌํ˜„ ์ทจ์†Œ๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์—†์œผ๋ฉด ๋‘˜ ๋‹ค ์„ ํƒํ•˜์‹ญ์‹œ์˜ค. ํด๋ผ์ด์–ธํŠธ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ํŒŒ์ƒ๋œ ํ† ํฐ์„ ๋งŒ๋“œ์„ธ์š”.
```c#
var ํด๋ผ์ด์–ธํŠธ ํ† ํฐ = ....
var innerTokenSource = CancellationTokenSource.CreateLinkedTokenSource(clientToken);

If at some point you catch **OperationCancelledException**:
```c#
try
{
     //invocation with innerToken
}
catch(OperationCancelledException oce)
{
      if(clientToken.IsCancellationRequested)
          throw; //as is, it is client initiated and in higher priority
      if(innerToken.IsCancellationRequested)
           //wrap it in HttpException, TimeoutException, whatever, wrap it in another linked token until you process all cases.
}

๊ธฐ๋ณธ์ ์œผ๋กœ ์ทจ์†Œ๋ฅผ ์œ ๋ฐœํ•œ ํ† ํฐ์„ ์ฐพ์„ ๋•Œ๊นŒ์ง€ ํด๋ผ์ด์–ธํŠธ ํ† ํฐ์—์„œ ๊ฐ€์žฅ ๊นŠ์€ ๋ ˆ์ด์–ด๊นŒ์ง€ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๋ ˆ์ด์–ด๋ฅผ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.

2.2 ๋˜๋Š” 3.0์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? @karelz @davidsh , cc @NickCraver . ๋‚˜๋Š” TimeoutException์„ ๋˜์ง€๋Š” ์˜ต์…˜ 1) ๋˜๋Š” HttpRequestException์—์„œ ํŒŒ์ƒ๋œ ์ƒˆ๋กœ์šด HttpTimeoutException์„ ๋˜์ง€๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”!

2.2 ๋˜๋Š” 3.0์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

2.2 ์น˜๊ณ ๋Š” ๋„ˆ๋ฌด ๋Šฆ์–ด์„œ ์ง€๋‚œ์ฃผ์— ์ถœ์‹œ ๋˜์—ˆ์–ด์š” ;)

2.2 ์น˜๊ณ ๋Š” ๋„ˆ๋ฌด ๋Šฆ์–ด์„œ ์ง€๋‚œ์ฃผ์— ์ถœ์‹œ ๋˜์—ˆ์–ด์š” ;)

์•„, ์‰ฌ๋Š” ์‹œ๊ฐ„์— ์ด๋ ‡๊ฒŒ ๋˜๋Š”๊ตฐ์š” ;)

์•ฝ๊ฐ„ ๋ฐ”๋ณด ๊ฐ™์ง€๋งŒ ๋‚ด๊ฐ€ ์ฐพ์€ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ OperationCanceledException์— ๋Œ€ํ•œ catch ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ค‘์ฒฉํ•œ ๋‹ค์Œ IsCancellationRequested ๊ฐ’์„ ํ‰๊ฐ€ํ•˜์—ฌ ์ทจ์†Œ ํ† ํฐ์ด ์‹œ๊ฐ„ ์ดˆ๊ณผ ๋˜๋Š” ์‹ค์ œ ์„ธ์…˜ ์ค‘๋‹จ์˜ ๊ฒฐ๊ณผ์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ API ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋…ผ๋ฆฌ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋…ผ๋ฆฌ ๋˜๋Š” ์„œ๋น„์Šค ๊ณ„์ธต์— ์žˆ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹จ์ˆœ์„ฑ์„ ์œ„ํ•ด ์˜ˆ์ œ๋ฅผ ํ†ตํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค.

````์ƒคํ”„
[HttpGet]
๊ณต๊ฐœ ๋น„๋™๊ธฐ ์ž‘์—…์ธ๋ฑ์Šค(CancellationToken ์ทจ์†Œ ํ† ํฐ)
{
๋…ธ๋ ฅํ•˜๋‹ค
{
// ์ด๊ฒƒ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ ์ž์ฒด๊ฐ€ ์•„๋‹Œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋˜๋Š” ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ ์ˆ˜ํ–‰๋˜์ง€๋งŒ ๋‹จ์ˆœ์„ฑ์„ ์œ„ํ•ด ์—ฌ๊ธฐ์—์„œ ์š”์ ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
๋…ธ๋ ฅํ•˜๋‹ค
{
//HttpClient ์ธ์Šคํ„ด์Šค์— HttpClientFactory ์‚ฌ์šฉ
var httpClient = _httpClientFactory.CreateClient();

        //Set an absurd timeout to illustrate the point
        httpClient.Timeout = new TimeSpan(0, 0, 0, 0, 1);

        //Perform call that requires special timeout logic                
        var httpResponse = await httpClient.GetAsync("https://someurl.com/api/long/running");

        //... (if GetAsync doesn't fail, handle the response as desired)
    }
    catch (OperationCanceledException innerOperationCanceled)
    {
        //If a canceled token exception occurs due to a timeout, "IsCancellationRequested" should be false
        if (cancellationToken.IsCancellationRequested)
        {
            //Bubble exception to global handler
            throw innerOperationCanceled;
        }
        else
        {
            //... (perform timeout logic here)
        }                    
    }                

}
catch (OperationCanceledException operationCanceledEx)
{
    _logger.LogWarning(operationCanceledEx, "Request was aborted by the end user.");
    return new StatusCodeResult(499);
}
catch (Exception ex)
{
    _logger.LogError(ex, "An unexepcted error occured.");
    return new StatusCodeResult(500);
}

}
````

์ด ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋ณด๋‹ค ์šฐ์•„ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ๊ธฐ์‚ฌ๋ฅผ ๋ณด์•˜์ง€๋งŒ ๋ชจ๋‘ ํŒŒ์ดํ”„๋ผ์ธ ๋“ฑ์„ ํŒŒํ—ค์ณ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์ด ์™„๋ฒฝํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฏธ๋ž˜์— ์‹ค์ œ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์˜ˆ์™ธ์— ๋Œ€ํ•œ ๋” ๋‚˜์€ ์ง€์›์ด ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ํ’€์–ด ์ฃผ๋‹ค.

http ํด๋ผ์ด์–ธํŠธ๋กœ ์ž‘์—…ํ•˜๋Š” ๊ฒƒ์€ ์™„์ „ํžˆ ์ตœ์•…์ž…๋‹ˆ๋‹ค. HttpClient๋ฅผ ์‚ญ์ œํ•˜๊ณ  ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์•ฝ๊ฐ„ ๋ฐ”๋ณด ๊ฐ™์ง€๋งŒ ๋‚ด๊ฐ€ ์ฐพ์€ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ OperationCanceledException์— ๋Œ€ํ•œ catch ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ค‘์ฒฉํ•œ ๋‹ค์Œ IsCancellationRequested ๊ฐ’์„ ํ‰๊ฐ€ํ•˜์—ฌ ์ทจ์†Œ ํ† ํฐ์ด ์‹œ๊ฐ„ ์ดˆ๊ณผ ๋˜๋Š” ์‹ค์ œ ์„ธ์…˜ ์ค‘๋‹จ์˜ ๊ฒฐ๊ณผ์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ API ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋…ผ๋ฆฌ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋…ผ๋ฆฌ ๋˜๋Š” ์„œ๋น„์Šค ๊ณ„์ธต์— ์žˆ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹จ์ˆœ์„ฑ์„ ์œ„ํ•ด ์˜ˆ์ œ๋ฅผ ํ†ตํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค.

[HttpGet]
public async Task<IActionResult> Index(CancellationToken cancellationToken)
{
  try
  {
      //This would normally be done in a business logic or service layer rather than in the controller itself but I'm illustrating the point here for simplicity sake
      try
      {
          //Using HttpClientFactory for HttpClient instances      
          var httpClient = _httpClientFactory.CreateClient();

          //Set an absurd timeout to illustrate the point
          httpClient.Timeout = new TimeSpan(0, 0, 0, 0, 1);

          //Perform call that requires special timeout logic                
          var httpResponse = await httpClient.GetAsync("https://someurl.com/api/long/running");

          //... (if GetAsync doesn't fail, handle the response as desired)
      }
      catch (OperationCanceledException innerOperationCanceled)
      {
          //If a canceled token exception occurs due to a timeout, "IsCancellationRequested" should be false
          if (cancellationToken.IsCancellationRequested)
          {
              //Bubble exception to global handler
              throw innerOperationCanceled;
          }
          else
          {
              //... (perform timeout logic here)
          }                    
      }                

  }
  catch (OperationCanceledException operationCanceledEx)
  {
      _logger.LogWarning(operationCanceledEx, "Request was aborted by the end user.");
      return new StatusCodeResult(499);
  }
  catch (Exception ex)
  {
      _logger.LogError(ex, "An unexepcted error occured.");
      return new StatusCodeResult(500);
  }
}

์ด ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋ณด๋‹ค ์šฐ์•„ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ๊ธฐ์‚ฌ๋ฅผ ๋ณด์•˜์ง€๋งŒ ๋ชจ๋‘ ํŒŒ์ดํ”„๋ผ์ธ ๋“ฑ์„ ํŒŒํ—ค์ณ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์ด ์™„๋ฒฝํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฏธ๋ž˜์— ์‹ค์ œ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์˜ˆ์™ธ์— ๋Œ€ํ•œ ๋” ๋‚˜์€ ์ง€์›์ด ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ํ’€์–ด ์ฃผ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ์ด ๋”์ฐํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์ ˆ๋Œ€์ ์œผ๋กœ ๊ณจ์น˜ ์•„ํ”ˆ ์ผ์ž…๋‹ˆ๋‹ค. HttpClient๋Š” Microsoft์—์„œ ๋งŒ๋“  ๊ฐ€์žฅ ๋ˆ„์ถœ๋˜๋Š” ์ถ”์ƒํ™”์ž…๋‹ˆ๋‹ค.

์‹œ๊ฐ„ ์ดˆ๊ณผ์— ๋Œ€ํ•œ ๋ณด๋‹ค ๊ตฌ์ฒด์ ์ธ ์˜ˆ์™ธ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๊ทผ์‹œ์•ˆ์ ์œผ๋กœ ๋ณด์ธ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ๋žŒ๋“ค์ด ์‹ค์ œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์ทจ์†Œ๋œ ํ† ํฐ ์˜ˆ์™ธ์™€ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ๋ฅผ ์›ํ•˜๋Š” ์•„์ฃผ ๊ธฐ๋ณธ์ ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

http ํด๋ผ์ด์–ธํŠธ๋กœ ์ž‘์—…ํ•˜๋Š” ๊ฒƒ์€ ์™„์ „ํžˆ ์ตœ์•…์ž…๋‹ˆ๋‹ค. HttpClient๋ฅผ ์‚ญ์ œํ•˜๊ณ  ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\

๊ทธ๊ฒƒ์€ ๋ณ„๋กœ ๋„์›€์ด ๋˜์ง€ ์•Š๊ฑฐ๋‚˜ ๊ฑด์„ค์ ์ธ ํ”ผ๋“œ๋ฐฑ @dotnetchris ์ž…๋‹ˆ๋‹ค. ๊ณ ๊ฐ๊ณผ MSFT ์ง์›์€ ๋ชจ๋‘ ๋” ๋‚˜์€ ๊ฒƒ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์—ฌ๊ธฐ ์žˆ์œผ๋ฉฐ @karelz ์™€ ๊ฐ™์€ ์‚ฌ๋žŒ๋“ค์€ ์—ฌ์ „ํžˆ โ€‹โ€‹๊ณ ๊ฐ ํ”ผ๋“œ๋ฐฑ์— ๊ท€๋ฅผ ๊ธฐ์šธ์ด๊ณ  ๊ฐœ์„ ํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋“ค์ด ๊ณต๊ฐœ์ ์œผ๋กœ ๊ฐœ๋ฐœ์„ ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์š”๊ตฌ ์‚ฌํ•ญ์€ ์—†์œผ๋ฉฐ, ๊ทธ๋“ค์˜ ์„ ์˜๋ฅผ ๋ถ€์ •์ ์œผ๋กœ ๋ถˆํƒœ์šฐ์ง€ ์•Š๋„๋ก ํ•ฉ์‹œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๊ทธ๋“ค์ด ๊ณต์„ ๊ฐ€์ง€๊ณ  ์ง‘์— ๊ฐ€๊ธฐ๋กœ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๋” ๋‚˜์  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”! :)

์˜ˆ์ƒ ๋™์ž‘์„ ๊ฐ€์ง„ HttpClient2(์ž„์‹œ ์ด๋ฆ„)๋ฅผ ๋งŒ๋“ค๊ณ  ์„ค๋ช…์„œ์—์„œ "HttpClient๋Š” ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. HttpClient2๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค"๋ผ๊ณ  ์•Œ๋ฆฌ๊ณ  ๋‘˜ ์‚ฌ์ด์˜ ์ฐจ์ด์ ์„ ์„ค๋ช…ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด .NetFramework์— ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์‚ฝ์ž…๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

HttpClient๋Š” ์˜ˆ์™ธ ๋ฐœ์ƒ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์ธํ„ฐ๋„ท ํ˜ธ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์€ ์˜ˆ์™ธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ฃผ์†Œ์˜ ๋ฌดํ•œํ•œ ์ˆœ์—ด์„ ๊ฐ์•ˆํ•  ๋•Œ ์‹ค์ œ๋กœ ์˜ˆ์ƒ๋˜๋Š” ๊ฒƒ์€ ์˜ˆ์™ธ์ ์ธ ๊ฒฝ์šฐ๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ์‹ฌ์ง€์–ด 3๋…„ ์ „์— HttpClient๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ๋‚˜์œ์ง€์— ๋Œ€ํ•ด ๋ธ”๋กœ๊ทธ์— ๊ธ€์„ ๋‚จ๊ฒผ์Šต๋‹ˆ๋‹ค. https://dotnetchris.wordpress.com/2015/12/11/working-with-httpclient-is-absurd/

์ด๊ฒƒ์€ ๋ˆˆ์— ๋„๊ฒŒ ๋‚˜์œ ๋””์ž์ธ์ž…๋‹ˆ๋‹ค.

Microsoft์˜ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ HttpClient๋ฅผ ์‚ฌ์šฉํ•œ ์ ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ํ™•์‹คํžˆ ์•„๋‹Œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ด ๋”์ฐํ•œ API ํ‘œ๋ฉด ๋„ˆ๋จธ์—๋Š” ๊ทธ๊ฒƒ์˜ ๊ตฌ์„ฑ๊ณผ ์Šค๋ ˆ๋”ฉ์— ๊ด€ํ•œ ์˜จ๊ฐ– ์ข…๋ฅ˜์˜ ์ถ”์ƒ์ ์ธ ๋ˆ„์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๊ตฌ์ œ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์ด ๋ฏฟ์„ ์ˆ˜ ์—†๊ณ  ์˜ค์ง ์•„์ฃผ ์ƒˆ๋กœ์šด ๊ฒƒ์ด ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์†”๋ฃจ์…˜์ด๋ผ๋Š” ๊ฒƒ์„ ๋ฏฟ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. HttpClient2, HttpClientSlim์€ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

Http ์•ก์„ธ์Šค๋Š” ์˜ˆ์™ธ๋ฅผ throwํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•ญ์ƒ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋ฉฐ ๊ฒฐ๊ณผ๋Š” ์™„๋ฃŒ, ์‹œ๊ฐ„ ์ดˆ๊ณผ, http ์‘๋‹ต์„ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. throw๊ฐ€ ํ—ˆ์šฉ๋˜์–ด์•ผ ํ•˜๋Š” ์œ ์ผํ•œ ์ด์œ ๋Š” EnsureSuccess() ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์„ธ๊ณ„์—์„œ ์˜ˆ์™ธ๋ฅผ throwํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ธํ„ฐ๋„ท์˜ ์™„์ „ํžˆ ์ •์ƒ์ ์ธ ๋™์ž‘์— ๋Œ€ํ•œ ์ œ์–ด ํ๋ฆ„ ์ฒ˜๋ฆฌ๊ฐ€ ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜ธ์ŠคํŠธ ๊ฐ€๋™, ํ˜ธ์ŠคํŠธ ๊ฐ€๋™ ์ค‘์ง€, ํ˜ธ์ŠคํŠธ ์‹œ๊ฐ„ ์ดˆ๊ณผ. ๊ทธ ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ์˜ˆ์™ธ์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

HttpClient๋Š” ์˜ˆ์™ธ ๋ฐœ์ƒ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์ธํ„ฐ๋„ท ํ˜ธ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์€ ์˜ˆ์™ธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ฃผ์†Œ์˜ ๋ฌดํ•œํ•œ ์ˆœ์—ด์„ ๊ฐ์•ˆํ•  ๋•Œ ์‹ค์ œ๋กœ ์˜ˆ์ƒ๋˜๋Š” ๊ฒƒ์€ ์˜ˆ์™ธ์ ์ธ ๊ฒฝ์šฐ๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@dotnetchris ์ด ์ฃผ์ œ์— ๋Œ€ํ•ด ๋งŽ์€ ์—ด์ •์„ ๊ฐ–๊ณ  ๊ณ„์‹  ๊ฒƒ ๊ฐ™์œผ๋‹ˆ ์‹œ๊ฐ„์„ ๋‚ด์–ด HttpClient2์— ๋Œ€ํ•ด ์ œ์•ˆ๋œ ์ „์ฒด API๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์ƒˆ ๋ฌธ์ œ๋กœ ํ† ๋ก ์„ ์œ„ํ•ด ํŒ€์— ์ œ์ถœํ•˜๋Š” ๊ฒƒ์ด ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

๋‚˜๋Š” ๋” ๊นŠ์€ ๊ธฐ์ˆ  ์ˆ˜์ค€์—์„œ HttpClient๋ฅผ ์ข‹๊ฑฐ๋‚˜ ๋‚˜์˜๊ฒŒ ๋งŒ๋“œ๋Š” ํ•ต์‹ฌ ์‚ฌํ•ญ์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ @dotnetchris์˜ ์˜๊ฒฌ์— ์ถ”๊ฐ€ํ•  ๊ฒƒ์ด ์—†์Šต๋‹ˆ๋‹ค. MS ๊ฐœ๋ฐœํŒ€์„ ๋น„ํŒํ•˜๋Š” ๊ฒƒ์€ ์ ์ ˆํ•˜์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋งค์šฐ ๊ฐ„๋‹จํ•ด ๋ณด์ด๋Š” ์ƒํ™ฉ์— ๋Œ€ํ•œ ์†”๋ฃจ์…˜์„ ์—ฐ๊ตฌํ•˜๋Š” ๋ฐ ๋ช‡ ์‹œ๊ฐ„๊ณผ ๋ช‡ ์‹œ๊ฐ„์„ ์†Œ๋น„ํ•˜๋Š” ๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ๋‹ต๋‹ตํ•œ์ง€ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • ๊ฒฐ์ •/๋ณ€๊ฒฝ์ด ์ด๋ฃจ์–ด์ง„ ์ด์œ ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์„ ๊ฒช๋Š”๋‹ค๋Š” ๊ฒƒ์€ MS์˜ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋™์‹œ์— ์—ฌ์ „ํžˆ ์ž‘์—…์„ ์™„๋ฃŒํ•˜๊ณ  ์ „๋‹ฌํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ œ ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉ์ž ์—ญํ• ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด OAuth ํ”„๋กœ์„ธ์Šค ์ค‘์— ํ˜ธ์ถœํ•˜๋Š” ์™ธ๋ถ€ ์„œ๋น„์Šค๊ฐ€ ์žˆ์œผ๋ฉฐ ํ•ด๋‹น ํ˜ธ์ถœ์ด ์ง€์ •๋œ ์‹œ๊ฐ„ ๋ฒ”์œ„ ๋‚ด์—์„œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋˜๋ฉด(๋ถˆํ–‰ํ•˜๊ฒŒ๋„ ์›ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ๋งŽ์ด ๋ฐœ์ƒํ•จ) ๋ณด์กฐ ๋ฐฉ๋ฒ•์œผ๋กœ ๋˜๋Œ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. .

์‹ค์ œ๋กœ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ์™€ ์ทจ์†Œ๋ฅผ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ์›ํ•˜๋Š” ์ด์œ ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋Š” ๋ฌด์ˆ˜ํžˆ ๋งŽ์€ ๋‹ค๋ฅธ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์™€ ๊ฐ™์€ ์ผ๋ฐ˜์ ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€ํ•œ ๋” ๋‚˜์€ ์ง€์›์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋Š” ์ด์œ ๋Š” ์ œ๊ฐ€ ์ดํ•ดํ•˜๊ธฐ ํž˜๋“  ๋ถ€๋ถ„์ด๋ฉฐ MS ํŒ€์—์„œ ์ถ”๊ฐ€๋กœ ์ƒ๊ฐํ•ด๋ณผ ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ์ง„์‹ฌ์œผ๋กœ ๋ฐ”๋ž๋‹ˆ๋‹ค.

@karelz ๋ˆ„๊ตฐ๊ฐ€์˜ API ์ œ์•ˆ์ธ 3.0์˜ ์ผ์ •์— ๋งž์ถฐ ์ด๊ฒƒ์„ ์–ป์œผ๋ ค๋ฉด ๋ฌด์—‡์ด ํ•„์š”ํ• ๊นŒ์š”?

์ด๊ฒƒ์€ API ์ œ์•ˆ์— ๊ด€ํ•œ ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์˜ฌ๋ฐ”๋ฅธ ๊ตฌํ˜„์„ ์ฐพ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ 3.0 ๋ฐฑ๋กœ๊ทธ์— ์žˆ์œผ๋ฉฐ ๋ชฉ๋ก์—์„œ ์ƒ๋‹นํžˆ ๋†’์Šต๋‹ˆ๋‹ค(HTTP/2 ๋ฐ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์‹œ๋‚˜๋ฆฌ์˜ค ๋’ค์— ์žˆ์Œ). ์šฐ๋ฆฌ ํŒ€์ด 3.0์—์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์•„์ฃผ ์ข‹์€ ๊ธฐํšŒ์ž…๋‹ˆ๋‹ค.
๋ฌผ๋ก  ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๋™๊ธฐ๋ฅผ ๋ถ€์—ฌํ•˜๋ฉด ์šฐ๋ฆฌ๋„ ๊ธฐ์—ฌ๋ฅผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@karelz ์ด ๊ฒฝ์šฐ ์ปค๋ฎค๋‹ˆํ‹ฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋„์›€์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? API ์ œ์•ˆ์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ์–ธ๊ธ‰ํ•œ ์ด์œ ๋Š” "์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ˆ˜ํ–‰ํ•  ๋ช‡ ๊ฐ€์ง€ ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค."๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฉ”์‹œ์ง€์—์„œ ํŒ€์ด ๋ชฉ๋ก์—์„œ ์„ ํ˜ธํ•˜๋Š” ๋Œ€์•ˆ์„ ๊ฒฐ์ •ํ–ˆ๋Š”์ง€ ํ™•์‹ ํ•  ์ˆ˜ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ๋ฐฉํ–ฅ์„ ๊ฒฐ์ •ํ–ˆ๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ๊ฑฐ๊ธฐ์—์„œ ๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”!

๊ธฐ์ˆ ์ ์œผ๋กœ ๋งํ•˜๋ฉด API ์ œ์•ˆ ์งˆ๋ฌธ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๊ฒฐ์ •์—๋Š” API ์Šน์ธ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ฐจ์ด์ ์„ ๋…ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์กฐ์ •ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์•ฝ๊ฐ„์˜ ํ† ๋ก ์ด ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๊ตฌํ˜„์—์„œ ์˜ต์…˜์ด ๋„ˆ๋ฌด ๋งŽ์ด ๋‹ค๋ฅผ ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ๋„์›€์„ ์›ํ•  ๊ฒฝ์šฐ ์˜ต์…˜์„ ๊ตฌํ˜„ํ•˜๋ฉด 98%๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ์™ธ๊ฐ€ ํ•œ ๊ณณ์—์„œ ๋ฐœ์ƒํ•˜๊ณ  ์œ„์— ๋‚˜์—ด๋œ [1]-[3] ์˜ต์…˜์— ๋งž๊ฒŒ ๋‚˜์ค‘์— ์‰ฝ๊ฒŒ ๋ณ€๊ฒฝ๋จ).

์•ˆ๋…•! ์šฐ๋ฆฌ๋Š” ์ตœ๊ทผ์—๋„ ์ด ๋ฌธ์ œ๋ฅผ ๊ฒช์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ 3.0์— ๋Œ€ํ•œ ๊ด€์‹ฌ์ด ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•˜์‹ญ๋‹ˆ๊นŒ?

์˜ˆ, ์—ฌ์ „ํžˆ 3.0 ๋ชฉ๋ก์— ์žˆ์Šต๋‹ˆ๋‹ค. 3.0์—์„œ ํ•ด๊ฒฐ๋  ๊ฐ€๋Šฅ์„ฑ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹90%์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ์˜ค๋Š˜ ์ด๊ฒƒ์„ ๋งŒ๋‚ฌ๋‹ค. ๋‚˜๋Š” HttpClient์— ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์„ค์ •ํ–ˆ๊ณ  ๊ทธ๊ฒƒ์€ ๋‚˜์—๊ฒŒ ์ด๊ฒƒ์„ ์ค€๋‹ค.

{System.Threading.Tasks.TaskCanceledException: A task was canceled.}
CancellationRequested = true

๋‚˜๋Š” ์ด๊ฒƒ์ด ๋ฌธ์ œ๊ฐ€๋˜๋Š” ์‹œ๊ฐ„ ์ดˆ๊ณผ์— ๋Œ€ํ•ด ์–ธ๊ธ‰ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ํ˜ผ๋ž€ ์Šค๋Ÿฝ๋‹ค๋Š” ๊ฒƒ์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.

์Œ, ์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. ์•ฝ๊ฐ„ ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์ด ์Šค๋ ˆ๋“œ์˜ ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์€ HTTP ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ TaskCanceledException ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ ๊ฐ™์ง€๋งŒ ๊ทธ๊ฒŒ ์ œ๊ฐ€ ์–ป๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค... .NET Core 2.1์—์„œ OperationCanceledException ๋ฅผ ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค...

์‹œ๊ฐ„ ์ดˆ๊ณผ์— ๋Œ€ํ•ด OperationCanceledException์„ ๋ฐœ์ƒ์‹œํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ œ๋Œ€๋กœ ๋ฌธ์„œํ™”๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๋„๋Œ€์ฒด ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ์ถ”์ ํ•˜๋Š” ๋ฐ ๋ช‡ ์‹œ๊ฐ„์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค.

https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.sendasync

HttpRequestException ์‹œ๊ฐ„ ์ดˆ๊ณผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ๋ช…ํ™•ํ•˜๊ฒŒ ๋ช…์‹œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค...

TaskCanceledException์€ OperationCanceledException์—์„œ ํŒŒ์ƒ๋ฉ๋‹ˆ๋‹ค.

@stephentoub ์˜ˆ, ํ•˜์ง€๋งŒ TaskCanceledException ๋ฅผ ๋ฐ›์ง€ ๋ชปํ•˜๊ณ  OperationCanceledException ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

์—์„œ์™€ ๊ฐ™์ด TaskCanceledException ๋ฅผ ์žก๋Š” catch ๋ธ”๋ก์€ ์˜ˆ์™ธ๋ฅผ ํฌ์ฐฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŠนํžˆ OperationCanceledException ์— ๋Œ€ํ•œ catch ๋ธ”๋ก์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” ์ด์ œ ์˜ˆ์™ธ ์œ ํ˜•์„ ํ•„ํ„ฐ๋งํ•˜์—ฌ HTTP ์‹œ๊ฐ„ ์ดˆ๊ณผ์— ๋Œ€ํ•ด ๊ตฌ์ฒด์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์‹œ๊ฐ„ ์ดˆ๊ณผ์ž„์„ ์•„๋Š” ๋ฐฉ๋ฒ•์€ TaskCanceledException ์ด ์•„๋‹Œ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.

```c#
๋…ธ๋ ฅํ•˜๋‹ค {
(var ์‘๋‹ต = await s_httpClient.SendAsync(request, _updateCancellationSource.Token).ConfigureAwait(false)) ์‚ฌ์šฉ
return (int)response.StatusCode < 400;
}
catch (OperationCanceledException ex) when (ex.GetType() != typeof(TaskCanceledException)) {
// HTTP ํƒ€์ž„์•„์›ƒ
๊ฑฐ์ง“์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
}
catch(HttpRequestException) {
๊ฑฐ์ง“์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
}

or alternatively this works as well, which might be better:

```c#
            try {
                using (var response = await s_httpClient.SendAsync(request, _updateCancellationSource.Token).ConfigureAwait(false))
                    return (int)response.StatusCode < 400;
            }
            catch (OperationCanceledException ex) when (ex.GetType() == typeof(OperationCanceledException)) {
                // HTTP timeout
                return false;
            }
            catch (HttpRequestException) {
                return false;
            }

์Œ, ์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. ์•ฝ๊ฐ„ ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์ด ์Šค๋ ˆ๋“œ์˜ ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์€ HTTP ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ TaskCanceledException ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ ๊ฐ™์ง€๋งŒ ๊ทธ๊ฒŒ ์ œ๊ฐ€ ์–ป๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค... .NET Core 2.1์—์„œ OperationCanceledException ๋ฅผ ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค...

์‹œ๊ฐ„ ์ดˆ๊ณผ์— ๋Œ€ํ•ด OperationCanceledException์„ ๋ฐœ์ƒ์‹œํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ œ๋Œ€๋กœ ๋ฌธ์„œํ™”๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๋„๋Œ€์ฒด ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ์ถ”์ ํ•˜๋Š” ๋ฐ ๋ช‡ ์‹œ๊ฐ„์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค.

https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.sendasync

HttpRequestException ์‹œ๊ฐ„ ์ดˆ๊ณผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ๋ช…ํ™•ํ•˜๊ฒŒ ๋ช…์‹œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค...

์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•(๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๋งŒํผ ๊นจ๋—ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ๋ฐฉ๋ฒ•)์„ ๋ณด์—ฌ์ฃผ๋Š” ์ฝ”๋“œ ์˜ˆ์ œ๋ฅผ ํฌํ•จํ•˜์—ฌ OperationCanceledExecption์ด ์ฐธ์กฐ๋˜๋Š” ์‹ค์ œ๋กœ ์—ฌ๋Ÿฌ ์‘๋‹ต์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด ์Šค๋ ˆ๋“œ๋ฅผ ์ฝ์—ˆ์Šต๋‹ˆ๊นŒ?

@Hobray ์˜ˆ, ๋‚ด๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ์—ฌ์ „ํžˆ ์ด ์Šค๋ ˆ๋“œ์˜ ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์‹œ๊ฐ„ ์ดˆ๊ณผ ์‹œ TaskCanceledException ๋ฅผ ๋ฐ›๊ณ  ์žˆ๋Š”์ง€ ์„ค๋ช…ํ•˜์ง€ ๋ชปํ•˜์ง€๋งŒ ์ €๋Š” ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ท€ํ•˜์˜ ์ฝ”๋“œ ์˜ˆ์ œ๋„ ๋ณด์•˜์ง€๋งŒ ๋ถˆํ–‰ํžˆ๋„ ๊ท€ํ•˜๊ฐ€ ๊ณ ๋ คํ•˜์ง€ ์•Š์€ ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ์ฝ”๋“œ๋Š” ์ด ๋ฌธ์ œ๋ฅผ ํ”ผํ•˜๊ณ  ์ทจ์†Œ ํ† ํฐ์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๊ณ  ๊ฒŒ์‹œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

@mikernet ์•„๋งˆ๋„ ๊ทธ ๋ฌธ๊ตฌ๊ฐ€ ๋‚˜๋ฅผ ๋ฒ„๋ ธ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์Ÿ ์กฐ๊ฑด์„ ๊ณ ๋ คํ•˜์ง€ ์•Š์€ ๊ฒƒ์ด ๋งž์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋ฉ‹์ง„ ํ•ด๊ฒฐ์ฑ…์ด ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ๋‚ด๊ฐ€ ๊ฐ„๊ณผ ํ•œ ๊ฒƒ์„ ์„ค๋ช…ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋“ฃ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์•Œ์•„์ฐจ๋ฆฐ ํ•œ ๊ฐ€์ง€๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๋Œ€ ๋ฏธ๋“ค์›จ์–ด ํ˜ธ์ถœ์—์„œ ์‹œ๊ฐ„ ์ดˆ๊ณผ ๋˜๋Š” ์ทจ์†Œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์ •ํ™•ํ•œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฏธ๋“ค์›จ์–ด ํ˜ธ์ถœ ๋‚ด๋ถ€์—๋Š” TaskCanceledException ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ถˆํ–‰ํžˆ๋„ ๋‚˜๋Š” ๊ทธ ์ด์œ ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด .NET Core ์ฝ”๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํŒŒํ—ค์น  ์‹œ๊ฐ„์ด ์—†์ง€๋งŒ ๋‚ด๊ฐ€ ์•Œ์•„์ฐจ๋ฆฐ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@Hobray ํ•˜ํ•˜ ๋ฌผ๋ก ์ž…๋‹ˆ๋‹ค. ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๊ฒฝ์Ÿ ์ƒํ™ฉ์— ๋Œ€ํ•ด ๋น„๋ฐ€์Šค๋Ÿฝ๊ฒŒ ๋งํ•  ์˜๋„๋Š” ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋™ ์ค‘์— ๋งˆ์ง€๋ง‰ ๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๋™์•ˆ ์ „ํ™”๋ฅผ ๋ฐ›๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์„ค๋ช…ํ•˜๊ธฐ ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค.

HttpClient ์‹œ๊ฐ„ ์ดˆ๊ณผ์™€ ์ทจ์†Œ ํ† ํฐ ์ทจ์†Œ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋Š” ์ง€์  ์‚ฌ์ด์— ์ž‘์—… ์ทจ์†Œ๊ฐ€ ์‹œ์ž‘๋œ ๊ฒฝ์šฐ ์†”๋ฃจ์…˜์˜ ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‚ด ์ƒํ™ฉ์—์„œ๋Š” ์—…์ŠคํŠธ๋ฆผ ์ฝ”๋“œ๊ฐ€ ์‚ฌ์šฉ์ž ์ทจ์†Œ๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ณ„์† ๋ฒ„๋ธ”๋งํ•˜๋ ค๋ฉด ์ž‘์—… ์ทจ์†Œ ์˜ˆ์™ธ๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ ์‹ค์ œ๋กœ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋œ ๊ฒฝ์šฐ์—๋งŒ ์˜ˆ์™ธ๋ฅผ catchํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ when ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์บ์น˜๋ฅผ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค. ํ† ํฐ ๊ฒ€์‚ฌ ์†”๋ฃจ์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์€ OperationCanceledException ๊ฐ€ ํ•„ํ„ฐ๋ฅผ ํ†ต๊ณผํ•˜๊ณ  ํ”„๋กœ๊ทธ๋žจ์ด ๋ฒ„๋ธ”๋ง๋˜๊ณ  ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋ˆ„๊ตฐ๊ฐ€ ์—…์ŠคํŠธ๋ฆผ์—์„œ ์ž‘์—…์„ ์ทจ์†Œํ•˜๋ฉด TaskCanceledException ๊ฐ€ ๋˜์ ธ์ง€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋˜์งˆ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. OperationCanceledException .

HttpClient ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ์™€ ์ž‘์—… ์ทจ์†Œ๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ๋ฐœ์ƒํ•  ๋•Œ ์‹œ๊ฐ„ ์ดˆ๊ณผ์™€ ์‚ฌ์šฉ์ž ์ทจ์†Œ ๊ฐ„์˜ ๊ตฌ๋ณ„์€ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ค‘์š” ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค... ๊ทธ๋Ÿฌ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ˆ˜์ค€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ ์ค‘์ด๊ณ  ์ž‘์—… ์ทจ์†Œ ๋ฒ„๋ธ”์ด ๋” ๋†’์€ ์ˆ˜์ค€์—์„œ ์ฒ˜๋ฆฌ๋˜๋„๋ก ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ํ™•์‹คํžˆ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹น์‹ ์ด ๋งํ•˜๋Š” ๊ฒƒ์ด ์‚ฌ์‹ค์ด๊ณ  ํ˜ธ์ถœ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋ฌด์—‡์ธ์ง€์— ๋”ฐ๋ผ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋‹ค๋ฅธ ์˜ˆ์™ธ ์œ ํ˜•์„ ๋˜์ง„๋‹ค๋ฉด ๊ทธ๊ฒƒ์€ ๋ถ„๋ช…ํžˆ ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๋ฌธ์ œ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ์˜๋„๋œ ๋™์ž‘์ด๊ฑฐ๋‚˜ ๊ทธ๋ž˜์•ผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—†์Šต๋‹ˆ๋‹ค.

๊ฐ€๋Šฅํ•œ ๊ฒฝ์Ÿ ์กฐ๊ฑด์— ํฅ๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ๊ณ ๋ คํ•˜์ง€ ์•Š์•˜๋‹ค. ์šด ์ข‹๊ฒŒ๋„ ์ œ ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ๋Š” ํฐ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์˜ˆ์™ธ ๋ณ€ํ˜•์— ๊ด€ํ•ด์„œ๋Š” ๋””๋ฒ„๊น…ํ•˜๋Š” ๋™์•ˆ ๋ช‡ ๋ฒˆ ๋ณธ ๊ฒƒ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์—…ํ•˜๊ณ  ์žˆ๋Š” ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋ฒ™์–ด๋ฆฌ๋กœ ๋งŒ๋“ค ์‹œ๊ฐ„์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์–ด๋ฆฌ์„์€ Chrome ๊ฒ€์ƒ‰์ฃผ์†Œ์ฐฝ ์‚ฌ์ „ ๋กœ๋“œ ํ˜ธ์ถœ์€ ํƒ€์ด๋ฐ์ด ์ ์ ˆํ–ˆ์„ ๋•Œ ๋ฏธ๋“ค์›จ์–ด์—์„œ ๋ณธ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ์‹์œผ๋กœ๋“  ๊ทธ๊ฒƒ์„ ํ™•์‹คํžˆ ์ฆ๋ช…ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์˜ฌ๋ฐ”๋ฅธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ๋ฌด์—‡์ธ์ง€ ์•„์ง ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‚ด ์†”๋ฃจ์…˜์€ http ์‹œ๊ฐ„ ์ดˆ๊ณผ๋กœ ์ธํ•ด OperationCanceledException์ด ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์—์„œ ๊ฐ€์žฅ ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค. TaskCanceledException์ด ๋ฐœ์ƒํ•˜๊ณ  ์•„๋ฌด๋„ ํ•ด๋‹น ๋™์ž‘์„ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•˜์ง€ ์•Š์€ ์ƒํ™ฉ์„ ์žฌํ˜„ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํ™˜๊ฒฝ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๋‚˜์™€ ์ผ์น˜ํ•˜๋Š” ๊ฒฝ์šฐ ์˜ฌ๋ฐ”๋ฅธ ์†”๋ฃจ์…˜์ด๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ถ€ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์—์„œ TaskCanceledException์ด ๋ฐœ์ƒํ•˜๋ฉด ๋‚ด ์†”๋ฃจ์…˜์ด ํ•ด๋‹น ์ปจํ…์ŠคํŠธ์— ๋Œ€ํ•œ ๋ฒ”์šฉ ์†”๋ฃจ์…˜์œผ๋กœ ์‹คํŒจํ•˜๊ณ  ์ œ์•ˆ๋œ ๋‹ค๋ฅธ ์†”๋ฃจ์…˜์ด "์ตœ์ƒ์˜" ์†”๋ฃจ์…˜์ด์ง€๋งŒ ์ƒํ™ฉ์— ์ค‘์š”ํ•˜๊ฑฐ๋‚˜ ์ค‘์š”ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ํฌํ•จ๋œ ๊ฒฝ์šฐ ๋‹น์‹ ์€ ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋ถ„๋ช…ํžˆ ํฐ ๋ฒ„๊ทธ๊ฐ€ ๋  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— TaskCanceledExceptions๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ์ฃผ์žฅํ•˜๋Š” ์‚ฌ๋žŒ์˜ ์ž์„ธํ•œ ๋‚ด์šฉ์„ ๋“ฃ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด ์‹ค์ œ๋กœ ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ๋‹ค์†Œ ํšŒ์˜์ ์ด๋‹ค.

@mikernet TaskCanceledException ๋˜๋Š” OperationCanceledException ๊ฐ€ ๋˜์ ธ์กŒ๋Š”์ง€ ์—ฌ๋ถ€๋Š” ํฌ๊ฒŒ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋˜๋Š” ๊ตฌํ˜„ ์„ธ๋ถ€ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์€ OperationException ( TaskCanceledException ๋„ ์žก์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค)๋ฅผ ์žก์•„๋‚ด๊ณ  Hobray์˜ ์ฝ”๋ฉ˜ํŠธ(https://github.com/)์— ์„ค๋ช…๋œ ๋Œ€๋กœ ์ „๋‹ฌํ•œ ์ทจ์†Œ ํ† ํฐ์ด ์ทจ์†Œ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. dotnet/corefx/issues/20296#issuecomment-449510983).

@thomaslevesque ๋‚˜๋Š” ์ด๋ฏธ ๊ทธ ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋ฌธ์ œ์ธ ์ด์œ ๋ฅผ ์–ธ๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์„ ๋‹ค๋ฃจ์ง€ ์•Š์œผ๋ฉด ๊ท€ํ•˜์˜ ์˜๊ฒฌ์€๋ณ„๋กœ ์œ ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์„ค๋ช…ํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์ง€ ์•Š๊ณ  ๋ณ€ํ™˜์— ์ฐธ์—ฌํ•˜๊ธฐ ์ „์— ์ง„์ˆ ์„ ๋‹ค์‹œ ํ•ด์‹œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

"๊ตฌํ˜„ ์„ธ๋ถ€ ์‚ฌํ•ญ"์— ๊ด€ํ•ด์„œ๋Š” - ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ถˆํ–‰ํžˆ๋„ ๋‚˜๋Š” ๊ทธ๊ฒƒ์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ž‘์—…์ด ์‹ค์ œ๋กœ ์ทจ์†Œ๋˜์—ˆ๊ฑฐ๋‚˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•  ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์„ค๋ช…๋œ ๊ฒฝ์Ÿ ์กฐ๊ฑด์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ๋‘ ์‹œ๋‚˜๋ฆฌ์˜ค ์ค‘ ์–ด๋–ค ๊ฒƒ์ด ๋ฐœ์ƒํ–ˆ๋Š”์ง€๋ฅผ ์˜ˆ์™ธ๋กœ๋ถ€ํ„ฐ 100% ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. OperationCanceledException ๋Š” ์ด ๋•Œ๋ฌธ์— ์ข‹์ง€ ์•Š์€ ์„ ํƒ์ž…๋‹ˆ๋‹ค...ํ”„๋ ˆ์ž„์›Œํฌ์—๋Š” ์ด๋ฏธ TimeoutException ๊ฐ€ ์žˆ์–ด ๋” ๋‚˜์€ ํ›„๋ณด์ž๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ์ž‘์—…์— ๋Œ€ํ•œ ์˜ˆ์™ธ๋Š” ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์˜ˆ์™ธ์˜ ์›์ธ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ถ„๊ธฐ ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ๊ฒƒ์ด๋ผ๋ฉด ์˜ˆ์™ธ์˜ ์›์ธ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ๋Š” ๋‹ค๋ฅธ ๊ฐœ์ฒด์˜ ๋ฐ์ดํ„ฐ ํ™•์ธ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๊ฒฝ์Ÿ ์กฐ๊ฑด ๋ฌธ์ œ๋ฅผ ๊ตฌ๊ฑธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ์˜ˆ์™ธ ์œ ํ˜•์ด ๊ตฌํ˜„ ์„ธ๋ถ€ ์‚ฌํ•ญ์ด๋ผ๋Š” ์ธก๋ฉด์—์„œ...์ €๋Š” ์˜ˆ์™ธ์— ๋Œ€ํ•ด ๊ทธ๊ฒƒ์ด ์‚ฌ์‹ค์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ ์–ด๋„ ์ž˜ ํ™•๋ฆฝ๋œ .NET ๋ฌธ์„œ ๊ด€ํ–‰์˜ ๊ด€์ ์—์„œ๋Š” ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. .NET Framework์—์„œ ๋ฌธ์„œ์— "Y๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์˜ˆ์™ธ X๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค"๋ผ๊ณ  ๋ช…์‹œ๋˜์–ด ์žˆ๊ณ  ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์‹ค์ œ๋กœ ํ•ด๋‹น ์˜ˆ์™ธ์˜ ๊ณต๊ฐœ์ ์œผ๋กœ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋Š” ํ•˜์œ„ ํด๋ž˜์Šค๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” .NET Framework์˜ ๋‹ค๋ฅธ ์˜ˆ ๋ฅผ ๋“ค์–ด ๋ณด์‹ญ์‹œ์˜ค.

๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  TaskCanceledException ๋Š” ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ์ž๊ฐ€ ์‹œ์ž‘ํ•œ ์ž‘์—… ์ทจ์†Œ๋ฅผ ์œ„ํ•ด ์˜ˆ์•ฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•ž์„œ ๋งํ–ˆ๋“ฏ์ด ์ด๋ฏธ ์˜ˆ์™ธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋‚ด๋ถ€ ์ž‘์—… ์ทจ์†Œ๋ฅผ ์บก์ฒ˜ํ•˜๊ณ  ๋” ์ ์ ˆํ•œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž‘์—… ์ทจ์†Œ ํ† ํฐ์œผ๋กœ ์‹œ์ž‘๋œ ์ž‘์—… ์ทจ์†Œ์— ๋Œ€ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ธฐ๋Š” ๋น„๋™๊ธฐ ์ž‘์—… ์ค‘์— ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ๊ด€๋ จ ์—†๋Š” ๋ฌธ์ œ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ฑ…์ž„์ด ์—†์Šต๋‹ˆ๋‹ค.

@mikernet ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ํ† ๋ก ์—์„œ ๊ทธ ๋ถ€๋ถ„์„ ๋†“์ณค์Šต๋‹ˆ๋‹ค. ์˜ˆ, ์ƒ๊ฐํ•˜์ง€ ๋ชปํ•œ ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ƒ๊ฐ๋ณด๋‹ค ํฐ ๋ฌธ์ œ๋Š” ์•„๋‹์ง€๋„... ํƒ€์ž„์•„์›ƒ์ด ๋ฐœ์ƒํ•œ ํ›„ ํ† ํฐ์˜ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์ „์— ์ทจ์†Œ ํ† ํฐ์ด ์‹ ํ˜ธ๋ฅผ ๋ฐ›์œผ๋ฉด ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ฃ ? ์ด ๊ฒฝ์šฐ ์ž‘์—…์ด ์–ด์จŒ๋“  ์ทจ์†Œ๋˜๋Š” ๊ฒฝ์šฐ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€๋Š” ์‹ค์ œ๋กœ ์ค‘์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ OperationCanceledException ๋ฒ„๋ธ”์ด ๋ฐœ์ƒํ•˜๋„๋ก ํ•˜์—ฌ ํ˜ธ์ถœ์ž๊ฐ€ ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ทจ์†Œ๋˜์—ˆ๋‹ค๊ณ  ๋ฏฟ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์‹ค์ œ๋กœ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฑฐ์ง“๋ง์ด์ง€๋งŒ ํ˜ธ์ถœ์ž๋Š” ๋‹ค์Œ์„ ์›ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ค‘์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์–ด์จŒ๋“  ์ž‘์—…์„ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค.

์„ค๋ช…๋œ ๊ฒฝ์Ÿ ์กฐ๊ฑด์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ๋‘ ์‹œ๋‚˜๋ฆฌ์˜ค ์ค‘ ์–ด๋–ค ๊ฒƒ์ด ๋ฐœ์ƒํ–ˆ๋Š”์ง€๋ฅผ ์˜ˆ์™ธ๋กœ๋ถ€ํ„ฐ 100% ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. OperationCanceledException ๋Š” ์ด ๋•Œ๋ฌธ์— ์ข‹์ง€ ์•Š์€ ์„ ํƒ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ํ”„๋ ˆ์ž„์›Œํฌ์—๋Š” ์ด๋ฏธ TimeoutException ๊ฐ€ ์žˆ์–ด ๋” ๋‚˜์€ ํ›„๋ณด์ž๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ „์ ์œผ๋กœ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ์˜ ํ–‰๋™์€ ๋ถ„๋ช…ํžˆ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ์˜๊ฒฌ์œผ๋กœ๋Š” ๊ทธ๊ฒƒ์„ ๊ณ ์น  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ @karelz ์˜ ์˜๊ฒฌ ์—์„œ ์˜ต์…˜ 1์ž…๋‹ˆ๋‹ค. ์˜ˆ, ๊ทธ๊ฒƒ์€ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์˜ ์ฝ”๋“œ ๊ธฐ๋ฐ˜์ด ์–ด์จŒ๋“  ์ด ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฏ€๋กœ ์ƒํ™ฉ์„ ์•…ํ™”์‹œํ‚ค์ง€๋Š” ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@thomaslevesque ๊ธ€์Ž„์š”, ํ•˜์ง€๋งŒ ์ €๋Š” ๊ทธ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ถ€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ๋Š” ์ค‘์š”ํ•˜๊ฑฐ๋‚˜ ์ค‘์š”ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ ๊ฒฝ์šฐ์—๋Š” ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ๋‚ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐฉ๋ฒ•์˜ ์‚ฌ์šฉ์ž๋Š” ์ž‘์—…์„ ์ทจ์†Œํ•  ๋•Œ OperationCanceledException ๋ฅผ ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. TaskCanceledException ๋ฅผ ๊ธฐ๋Œ€ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์ด๋ฅผ ์žก์•„์„œ "์•ˆ์ „"ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ทจ์†Œ ํ† ํฐ์„ ํ™•์ธํ•˜๊ณ  ์ทจ์†Œ๋œ ๊ฒƒ์„ ํ™•์ธํ•˜๊ณ  ์˜ˆ์™ธ๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ์ฒ˜๋ฆฌ๊ธฐ์— ์˜ํ•ด ํฌ์ฐฉ๋˜์ง€ ์•Š๊ณ  ํ”„๋กœ๊ทธ๋žจ์ด ์ถฉ๋Œํ•˜๋Š” ์˜ˆ์™ธ๋ฅผ ๋ฐฉ๊ธˆ ์ „๋‹ฌํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด ๋ฐฉ๋ฒ•์€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค.

๋ฌผ๋ก , ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค...์ƒˆ๋กœ ๋ž˜ํ•‘๋œ TaskCanceledException ๋ฅผ ๋˜์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ๋Ÿฌ๋ฉด ์ตœ์ƒ์œ„ ์Šคํƒ ์ถ”์ ์ด ์†์‹ค๋˜๊ณ  HTTP ์‹œ๊ฐ„ ์ดˆ๊ณผ๋Š” ๋‚ด ์„œ๋น„์Šค์˜ ์ž‘์—… ์ทจ์†Œ์™€ ํ›จ์”ฌ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. - HTTP ์‹œ๊ฐ„ ์ดˆ๊ณผ๋Š” ์ด๋™ ์˜ค๋žœ ์‹œ๊ฐ„ ๋™์•ˆ ์บ์‹œ์— ์ €์žฅ๋˜์ง€๋งŒ ์ž‘์—… ์ทจ์†Œ๋Š” ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฒฝ์Ÿ ์กฐ๊ฑด์œผ๋กœ ์ธํ•ด ์‹ค์ œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์บ์‹ฑํ•˜๋Š” ๊ฒƒ์„ ๋†“์น˜๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

OperationCanceledException ๋ฅผ ์žก๊ณ  ์ทจ์†Œ ํ† ํฐ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ทจ์†Œ๋œ ๊ฒฝ์šฐ ์ „์ฒด ํ”„๋กœ๊ทธ๋žจ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์‹œ ๋˜์ง€๊ธฐ ์ „์— ์˜ˆ์™ธ ์œ ํ˜•์„ ํ™•์ธํ•˜์—ฌ TaskCanceledException ์ธ์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. ์ด๋Ÿฌํ•œ ์กฐ๊ฑด ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๊ฑฐ์ง“์ด๋ฉด ์‹œ๊ฐ„ ์ดˆ๊ณผ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๋‚จ์•„ ์žˆ๋Š” ๋ฌธ์ œ๋Š” ๊ฒฝํ•ฉ ์ƒํƒœ๊ฐ€ ๋ฏธ๋ฌ˜ํ•˜์ง€๋งŒ ๋นก๋นกํ•œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ์„ค์ •๋œ ๊ณผ๋ถ€ํ•˜ ์„œ๋น„์Šค์—์„œ ๋ถ„๋‹น ์ˆ˜์ฒœ ๊ฐœ์˜ HTTP ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์–ด๋Š ์ชฝ์ด๋“  ๊ทธ๊ฒƒ์€ ํ•ดํ‚ค์ด๋ฉฐ ๋‹น์‹ ์ด ์ง€์ ํ•œ๋Œ€๋กœ ์–ด๋–ป๊ฒŒ ๋“  ํ•ด๊ฒฐ๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

(๊ฒฝํ•ฉ ์กฐ๊ฑด์€ OperationCanceledException ๋Œ€์‹  TaskCanceledException ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ํ™˜๊ฒฝ์—์„œ๋งŒ ๋ฐœ์ƒํ•˜์ง€๋งŒ, ์ ์–ด๋„ ์†”๋ฃจ์…˜์€ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ์˜ˆ์™ธ ์œ ํ˜•์„ ๋ฒ„๋ธ”๋งํ•˜๋Š” ๊ฒƒ์œผ๋กœ๋ถ€ํ„ฐ ์•ˆ์ „ํ•˜๊ณ  ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ตฌํ˜„ ์„ธ๋ถ€ ์ •๋ณด๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒฝ์šฐ์—๋„ ์—ฌ์ „ํžˆ ํ•ฉ๋ฆฌ์ ์œผ๋กœ ์ž˜ ์ž‘๋™ํ•˜๋ ค๋ฉด ๊ตฌํ˜„ ์„ธ๋ถ€ ์ •๋ณด์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

http ์‹œ๊ฐ„ ์ดˆ๊ณผ์˜ ๊ฒฝ์šฐ HttpClient ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์‹œ๊ฐ„ ์ดˆ๊ณผ ์˜ˆ์™ธ๋Š” ์ผ๋ฐ˜ System.TimeoutException ๋Œ€์‹  System.Net.HttpRequestException (๋˜๋Š” ํ•˜์œ„ ํ•ญ๋ชฉ?) ์œ ํ˜•์ด ์•„๋‹ˆ์–ด์•ผ ํ•ฉ๋‹ˆ๊นŒ? ์˜ˆ๋ฅผ ๋“ค์–ด, ์ข‹์€ ์˜ค๋ž˜๋œ WebClient ๋Š” $# WebExceptionStatus.Timeout ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” Status ์†์„ฑ๊ณผ ํ•จ๊ป˜ WebException #$๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

์•ˆํƒ€๊น๊ฒŒ๋„ 3.0์—์„œ๋Š” ์ด ์ž‘์—…์„ ์™„๋ฃŒํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ฏธ๋ž˜๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
์—ฌ์ „ํžˆ ๋ฐฑ๋กœ๊ทธ ์ƒ๋‹จ์— ์žˆ์ง€๋งŒ ํ˜„์‹ค์ ์œผ๋กœ 3.0์—์„œ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค :(.

@karelz oh no :(๋‘ ๋ฒˆ์งธ ํšŒ์‚ฌ์—์„œ ์—ฐ์†์œผ๋กœ ์ด ๋ฌธ์ œ๋ฅผ ๊ฒช์—ˆ๋˜ ๋ฐฉ๋ฒ•์„ ๋‹ค์‹œ ๊ฒŒ์‹œํ•˜๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ง์ ‘ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด 3.0์— ๋Œ€ํ•œ PR ์ œ์ถœ ๊ธฐ๊ฐ„์ด ์–ผ๋งˆ๋‚˜ ๋ ๊นŒ์š”? ๊ฑด๋ฐฐ

์ปจํ…์ŠคํŠธ์— ๋”ฐ๋ผ TaskCancelledException๊ณผ OperationCancelledException์„ ๋ฐ›๋Š” ๊ฒƒ ์‚ฌ์ด์˜ ์ฐจ์ด์ ์„ ๋ณด๊ณ ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค๊ณผ ํ•จ๊ป˜ ์œ„์˜ ๋งŽ์€ ์ฃผ์„์—์„œ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ์ •ํ™•ํžˆ ์ถ”์ ํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์„ ๊ฒช๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. MSFT์˜ ๋ˆ„๊ตฌ๋“ ์ง€ ์ด๋ฅผ ๋ฐํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@mikernet ์˜ ์ด ๋Œ“๊ธ€ ์ข‹์•„์š”:

์—์„œ์™€ ๊ฐ™์ด TaskCanceledException์„ ์žก๋Š” catch ๋ธ”๋ก์€ ์˜ˆ์™ธ๋ฅผ catchํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŠนํžˆ OperationCanceledException์— ๋Œ€ํ•œ catch ๋ธ”๋ก์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

cc @davidsh @stephentoub ์•„๋งˆ๋„?

TaskCanceledException์€ OperationCanceledException์—์„œ ํŒŒ์ƒ๋˜๋ฉฐ ์•ฝ๊ฐ„์˜ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. OperationCanceledException์„ ์žก์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

@karelz ๋Š” ์›๋ž˜ ๊ฒŒ์‹œํ•œ ์˜ต์…˜ 1์„ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด ์™„์ „ํžˆ ๋ถˆ์พŒํ•ฉ๋‹ˆ๊นŒ?

StackExchange.Redis์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์€ Timeout ์˜ˆ์™ธ์—์„œ ํŒŒ์ƒ๋œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— HttpClient์— ๋Œ€ํ•œ Timeout ์˜ˆ์™ธ๋ฅผ ์žก๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค...

์ƒ์†์ด ์ฃผ์–ด์ง„ @stephentoub ๋ฅผ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. @mikernet์˜ ์•„๋ž˜ ๋Œ“๊ธ€์ด ์ œ ๋งˆ์Œ์„ ๋“ค๋œจ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค... ๊ทธ๊ฐ€ ์„ค๋ช…ํ•˜๋Š” ์ด ํ–‰๋™์€ ์ผ์ข…์˜ ๋ฒ„๊ทธ์—ฌ์•ผ ํ•˜๋Š” ๊ฑด๊ฐ€์š”?? ๊ทธ๋Ÿฌ๋‚˜ ์‚ฌ์‹ค์ด๋ผ๋ฉด ์ˆ˜์ • ๋˜๋Š” ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์— ์˜ํ–ฅ์„ ๋ฏธ์น  ๊ฒƒ์ž…๋‹ˆ๋‹คโ€ฆ

"TaskCanceledException์„ catchํ•˜๋Š” catch ๋ธ”๋ก์€ ์˜ˆ์™ธ๋ฅผ catchํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŠนํžˆ OperationCanceledException์— ๋Œ€ํ•œ catch ๋ธ”๋ก์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."

์•„๋‹ˆ๋ฉด OperationEx๋ฅผ ๋˜์ง€๋Š” ๊ณณ๋„ ์žˆ๊ณ  TaskEx๋ฅผ ๋˜์ง€๋Š” ๊ณณ๋„ ์žˆ๋‹ค๋Š” ๋ง์”€์ด์‹ ๊ฐ€์š”? ์•„๋งˆ๋„ ์‚ฌ๋žŒ๋“ค์ด ASP ๋ฏธ๋“ค์›จ์–ด์—์„œ ์˜ค๋Š” ๊ฒƒ์„ ๋ณด๊ณ  HttpClient์—์„œ ์˜จ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ?

@mikernet ์—์„œ ์•„๋ž˜์˜ ์ด ๋Œ“๊ธ€์ด ์ œ ๋งˆ์Œ์„ ๋ถˆ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค... ๊ทธ๊ฐ€ ์„ค๋ช…ํ•˜๋Š” ์ด ๋™์ž‘์€ ์ผ์ข…์˜ ๋ฒ„๊ทธ์—ฌ์•ผ ํ•ฉ๋‹ˆ๊นŒ??

๊ทธ๊ฒŒ ์™œ "๋งˆ์Œ์ด ํ”๋“ค๋ฆฌ๋Š”" ๊ฒƒ์ž…๋‹ˆ๊นŒ? ์˜ˆ์™ธ B๊ฐ€ ์˜ˆ์™ธ A์—์„œ ํŒŒ์ƒ๋˜๋Š” ๊ฒฝ์šฐ B์— ๋Œ€ํ•œ catch ๋ธ”๋ก์€ ๊ตฌ์ฒด์ ์œผ๋กœ A์ธ ์˜ˆ์™ธ๋ฅผ catchํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ArgumentNullException์— ๋Œ€ํ•œ catch ๋ธ”๋ก์ด ์žˆ๋Š” ๊ฒฝ์šฐ " throw new ArgumentException(...)"์„ catchํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์•„๋‹ˆ๋ฉด OperationEx๋ฅผ ๋˜์ง€๋Š” ๊ณณ๋„ ์žˆ๊ณ  TaskEx๋ฅผ ๋˜์ง€๋Š” ๊ณณ๋„ ์žˆ๋‹ค๋Š” ๋ง์”€์ด์‹ ๊ฐ€์š”?

์˜ค๋ฅธ์ชฝ. ์ผ๋ฐ˜์ ์œผ๋กœ ์ฝ”๋“œ๋Š” OCE๋งŒ ๊ฐ€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž‘์„ฑ ๋ฐฉ์‹์— ๋”ฐ๋ผ ์ผ๋ถ€ ์ฝ”๋“œ๋Š” ํŒŒ์ƒ๋œ TCE๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด TaskCompletionSource.TrySetCanceled๋ฅผ ํ†ตํ•ด ์™„๋ฃŒ๋œ ์ž‘์—…์€ TaskCanceledException์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

Stephen, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ์˜ ์†Œ๋ฆฌ์—์„œ ์—ฌ๋Ÿฌ ์‚ฌ๋žŒ์ด TaskCE๊ฐ€ ๋ชจ๋“  ๊ณณ์—์„œ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ธ์ƒ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด mikernet์—์„œ ์–ธ๊ธ‰ํ•œ ๋™์ž‘์ด ์˜๋ฏธ๊ฐ€ ์—†์—ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค... ์ด์ œ ๋ช…ํ™•ํ•˜๊ฒŒ ํ–ˆ์œผ๋‹ˆ, ๋…ผ๋ฆฌ์  ๊ด€์ฐฐ :)

@stephentoub , ์ด ์ง€์นจ์ด ์–ด๋””์—๋‚˜ ๋ฌธ์„œํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๊นŒ? ํ•‘ @guardrex

์ผ๋ฐ˜์ ์œผ๋กœ ์ฝ”๋“œ๋Š” OCE๋งŒ ๊ฐ€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค...

์ €๋Š” ์š”์ฆ˜ ASP.NET ๋ฌธ์„œ ์„ธํŠธ๋ฅผ ์ž‘์—…ํ•ฉ๋‹ˆ๋‹ค. dotnet/docs ๊ณ ์–‘์ด์— ๋Œ€ํ•œ ๋ฌธ์ œ ์—ด๊ธฐ ...

https://github.com/dotnet/docs/issues

@karelz ๋ฐ @davidsh ์™€์˜ ๊ตฌ์ฒด์ ์ธ ์ œ์•ˆ์ด ์žˆ์—ˆ๋˜ ๊ณณ์—์„œ ์ด ๋Œ€ํ™”๋ฅผ ๋‹ค์‹œ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? HttpClient API ๋ชจ๋ธ์˜ ์†Œ์œ ๊ถŒ์ด ์žˆ๋Š” ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๋‚˜์•„๊ฐˆ ๋ฐฉํ–ฅ์— ๋Œ€ํ•œ ๊ฒฐ์ •์„ ๋‚ด๋ ค์•ผ ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋Š๊ปด์ง‘๋‹ˆ๋‹ค. WebExceptionStatus์™€ ์œ ์‚ฌํ•œ ์ƒˆ๋กœ์šด ์—ด๊ฑฐํ˜• ์†์„ฑ์„ ํฌํ•จํ•˜๋„๋ก HttpRequestion์„ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด ๋” ํฐ ๋ฆฌํŒฉํ„ฐ๋ง์— ๋Œ€ํ•œ ์žฅ๊ธฐ์ ์œผ๋กœ ๋ณด์ด๋Š” ์ฃผ์ œ์™€ ํ•จ๊ป˜ ํ•ญ์ƒ HttpRequestException(๋ช…์‹œ์  ์ทจ์†Œ ์ œ์™ธ??)์ธ ์ตœ์ƒ์œ„ ์˜ˆ์™ธ์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ๋…ผ์˜๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ด ์ „์ฒด ๋ฌธ์ œ๊ฐ€ ํ•„์š”ํ•œ ์‹ค์ œ ์ฝ”๋“œ ๋ณ€๊ฒฝ๊ณผ ๊ด€๋ จํ•˜์—ฌ ๋ณต์žกํ•ด์ง€๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋Š๊ปด์ง‘๋‹ˆ๋‹ค. ์ผ๋ถ€ ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๊ธฐ ์œ„ํ•ด ๋ฐ˜๋ณต๋˜์–ด์•ผ ํ•˜๋Š” HttpClient API ๋ชจ๋ธ์˜ ์ง€๋ถ„/์†Œ์œ ๊ถŒ์„ ๊ฐ€์ง„ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์—ฌ๊ธฐ์— ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ cancellationToken.IsCancellationRequested ๊ฐ€ true ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    public class TimeoutHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            try
            {
                var response = await base.SendAsync(request, cancellationToken);
                response.EnsureSuccessStatusCode();

                return response;
            }
            catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
            {
                throw new TimeoutException("Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.");
            }
        }

๊ฐ•์ œ๋กœ ํƒ€์ž„์•„์›ƒ์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋Š” TaskCancelledException IOException ์ž…๋‹ˆ๋‹ค.

System.Threading.Tasks.TaskCanceledException: The operation was canceled. ---> System.IO.IOException: Unable to read data from the transport connection: A operaรงรฃo de E/S foi anulada devido a uma saรญda de thread ou a uma requisiรงรฃo de aplicativo. ---> System.Net.Sockets.SocketException: A operaรงรฃo de E/S foi anulada devido a uma saรญda de thread ou a uma requisiรงรฃo de aplicativo
   --- End of inner exception stack trace ---
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token)
   at System.Net.Http.HttpConnection.FillAsync()
   at System.Net.Http.HttpConnection.ReadNextResponseHeaderLineAsync(Boolean foldedHeadersAllowed)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

...

(๋ฒˆ์—ญ๋œ ์˜ˆ์™ธ ํ…์ŠคํŠธ์— ๋Œ€ํ•ด ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด๊ฒƒ์„ .NET Core์—๋„ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.)

๋ณด์‹œ๋‹ค์‹œํ”ผ IsCancellationRequest ์„ ํ™•์ธํ•˜์—ฌ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์ œ์•ˆํ•œ ๋Œ€๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ํ–ˆ์ง€๋งŒ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์‹œ์—๋„ true ๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ๊ตฌ์„ฑ๋œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๊ฒฝ๊ณผํ–ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ํƒ€์ด๋จธ๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ถ„๋ช…ํžˆ ๋‚˜๋Š” ์ฃฝ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค . ์ด๊ฒƒ์€ ๋”์ฐํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š” @FelipeTaveira ,

์ทจ์†Œ๊ฐ€ TimeoutHandler ์˜ ์—…์ŠคํŠธ๋ฆผ์—์„œ HttpClient ์ž์ฒด์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ ‘๊ทผ ๋ฐฉ์‹์ด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•ธ๋“ค๋Ÿฌ๋Š” Timeout ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ HttpClient ์— ์˜ํ•ด ์ทจ์†Œ๋˜๋Š” ์ทจ์†Œ ํ† ํฐ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ทจ์†Œ ํ† ํฐ์ด ์ทจ์†Œ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์€ HttpClient ์— ์˜ํ•ด ํ˜ธ์ถœ๋œ ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ HttpClient ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ์—์„œ๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ •์˜ ํ•ธ๋“ค๋Ÿฌ์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ $#$8 Timeout.InfiniteTimeSpan #$ ๋กœ ์„ค์ •ํ•˜๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž์ฒด ํ•ธ๋“ค๋Ÿฌ์—์„œ ์‹œ๊ฐ„ ์ดˆ๊ณผ ๋™์ž‘์„ ์ œ์–ดํ•˜์—ฌ HttpClient ์˜ Timeout ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ธฐ์‚ฌ ์— ํ‘œ์‹œ๋จ(์ „์ฒด ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ )

@thomaslevesque ์˜ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๋‚˜๋Š” ๊ท€ํ•˜์˜ ์ฝ”๋“œ๋ฅผ ์ฝ๊ณ  ๊ทธ ๋ถ€๋ถ„์ด ์š”์ฒญ๋‹น ์ œํ•œ ์‹œ๊ฐ„ ๊ตฌ์„ฑ์„ ์ง€์›ํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์ง€๋งŒ TimeoutException ๋„ ์ง€์›ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

์•ž์œผ๋กœ ์ด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๊ทธ๋งŒํ•œ ๊ฐ€์น˜๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— TaskCanceledException ๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์‹œ ๋ฐœ์ƒํ•˜์—ฌ ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.

์ €๋Š” TaskCanceledExceptions ๊ฐ€ ์ด์ „ ์ง์žฅ์—์„œ ์ƒ์‚ฐ ๋ฌธ์ œ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ํ˜ผ๋ž€์˜ ์ฃผ์š” ์›์ธ์ด์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ์ฆ๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ HttpClient์— ๋Œ€ํ•œ ํ˜ธ์ถœ์„ ๋ž˜ํ•‘ํ•˜๊ณ  ์ ์ ˆํ•œ ๊ฒฝ์šฐ TimeoutException์„ ๋˜์ง‘๋‹ˆ๋‹ค.

@eiriktsarpalis ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” 5.0์—์„œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ๊ณ„ํš์ด๋ฏ€๋กœ ์›ํ•˜๋Š” ๊ฒฝ์šฐ ํ•ด๋‹น ๊ธฐ๊ฐ„ ๋‚ด์— ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์šฐ๋ฆฌ ํŒ€์— ํ•ฉ๋ฅ˜ํ–ˆ์Šต๋‹ˆ๋‹ค ๐Ÿ˜‡.

์ผ๋…„? ๐Ÿ˜ต .NET Core์˜ ์•ฝ์†๋œ ๋ฏผ์ฒฉ์„ฑ์€ ์–ด๋””์— ์žˆ์Šต๋‹ˆ๊นŒ?

ํ•˜๋‚˜์˜ ๋ฌธ์ œ๋กœ ์ „์ฒด ํ”„๋กœ์ ํŠธ์˜ ๋ฏผ์ฒฉ์„ฑ์„ ํŒ๋‹จํ•˜๋Š” ๊ฒƒ์€ ๋‹ค์†Œ ... ๋ถˆ๊ณตํ‰ํ•ฉ๋‹ˆ๋‹ค.
์˜ˆ, ์•ฝ๊ฐ„ ๋‹นํ™ฉ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ ์ด ๋ฌธ์ œ๊ฐ€ ์žˆ์ง€๋งŒ ์ˆ˜์ •ํ•˜๊ธฐ ์–ด๋ ต๊ณ (ํ˜ธํ™˜์„ฑ ์˜ํ–ฅ์œผ๋กœ) ๋จผ์ € ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๋” ๋†’์€ ์šฐ์„ ์ˆœ์œ„์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋ชฉํ‘œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค(๋„คํŠธ์›Œํ‚น์—์„œ). ์—ญ์‚ฌ ์ „๋ฐ˜์— ๊ฑธ์ณ ์ด์ •ํ‘œ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. ์ด ๋ฌธ์ œ์˜.

BTW: ๋‹ค๋ฅธ ๋งŽ์€ OSS ํ”„๋กœ์ ํŠธ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ SLA๋‚˜ ETA ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์–ธ์ œ ์ˆ˜์ •๋ ์ง€์— ๋Œ€ํ•œ ์•ฝ์†์ด ์—†์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์€ ์šฐ์„  ์ˆœ์œ„, ๋‹ค๋ฅธ ์ž‘์—…, ์–ด๋ ค์›€ ๋ฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์ „๋ฌธ๊ฐ€์˜ ์ˆ˜์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

ํ•˜๋‚˜์˜ ๋ฌธ์ œ๋กœ ์ „์ฒด ํ”„๋กœ์ ํŠธ์˜ ๋ฏผ์ฒฉ์„ฑ์„ ํŒ๋‹จํ•˜๋Š” ๊ฒƒ์€ ๋‹ค์†Œ ... ๋ถˆ๊ณตํ‰ํ•ฉ๋‹ˆ๋‹ค.

๋ญ, ์†”์งํ•œ ์งˆ๋ฌธ์ด์—ˆ๋‹ค. ๋ฒŒ์จ 2๋…„์ด ํ˜๋ €๊ณ  ๋˜ 1๋…„์ด ๊ฑธ๋ฆฐ๋‹ค๋Š” ๊ฒƒ์„ ๋ณด๊ณ  ์กฐ๊ธˆ ๋†€๋ž์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ๊ณต๊ฒฉ์€ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค ๐Ÿ™‚. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋จผ์ € ๋‹ต๋ณ€ํ•ด์•ผ ํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ์งˆ๋ฌธ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํƒ€ํ˜‘์˜ ์„ธ๊ณ„์—์„œ ... ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์ข‹์•„ํ•˜์ง€ ์•Š์ง€๋งŒ ์”น์„ ์ˆ˜์žˆ๋Š” ๋˜ ๋‹ค๋ฅธ ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

TaskCanceledException ๋ฅผ ๊ณ„์† ๋˜์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ CancellationToken ๋ฅผ ์„ผํŠธ๋ฆฌ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

c# catch(TaskCanceledException ex) when(ex.CancellationToken == HttpClient.TimeoutToken) { }

๊ทธ๋ฆฌ๊ณ  ๋˜ ํ•˜๋‚˜: TaskCanceledException ์—์„œ ํŒŒ์ƒ๋œ ์ƒˆ๋กœ์šด HttpTimeoutException ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋˜ ํ•˜๋‚˜: TaskCanceledException ์—์„œ ํŒŒ์ƒ๋œ ์ƒˆ๋กœ์šด HttpTimeoutException ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋ชจ๋“  ์šฐ๋ ค๋ฅผ ํ•ด๊ฒฐํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์‰ฝ๊ฒŒ ํฌ์ฐฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์—ฌ์ „ํžˆ TaskCanceledException ์ด๋ฏ€๋กœ ํ˜„์žฌ ํ•ด๋‹น ์˜ˆ์™ธ๋ฅผ ํฌ์ฐฉํ•˜๋Š” ์ฝ”๋“œ๋Š” ์ƒˆ ์˜ˆ์™ธ๋„ ํฌ์ฐฉํ•ฉ๋‹ˆ๋‹ค(์ค‘๋‹จ ๋ณ€๊ฒฝ ์—†์Œ).

๊ทธ๋ฆฌ๊ณ  ๋˜ ํ•˜๋‚˜: TaskCanceledException์—์„œ ํŒŒ์ƒ๋œ ์ƒˆ๋กœ์šด HttpTimeoutException์„ ๋˜์ง‘๋‹ˆ๋‹ค.

๋‹ค์†Œ ๋ณด๊ธฐ ํ‰ํ•˜์ง€๋งŒ ์•„๋งˆ๋„ ์ข‹์€ ์ ˆ์ถฉ์•ˆ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋˜ ํ•˜๋‚˜: TaskCanceledException ์—์„œ ํŒŒ์ƒ๋œ ์ƒˆ๋กœ์šด HttpTimeoutException ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋ชจ๋“  ์šฐ๋ ค๋ฅผ ํ•ด๊ฒฐํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์‰ฝ๊ฒŒ ํฌ์ฐฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์—ฌ์ „ํžˆ TaskCanceledException ์ด๋ฏ€๋กœ ํ˜„์žฌ ํ•ด๋‹น ์˜ˆ์™ธ๋ฅผ ํฌ์ฐฉํ•˜๋Š” ์ฝ”๋“œ๋Š” ์ƒˆ ์˜ˆ์™ธ๋„ ํฌ์ฐฉํ•ฉ๋‹ˆ๋‹ค(์ค‘๋‹จ ๋ณ€๊ฒฝ ์—†์Œ).

์Œ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์ด _๊ฐ€๋Šฅ_ํ•ฉ๋‹ˆ๋‹ค.

try
{
  // ...
}
catch (Exception ex) when (ex.GetType() == typeof(TaskCanceledException))
{
}

๋”ฐ๋ผ์„œ ์ด๊ฒƒ์€ ๊ธฐ์ˆ ์ ์œผ๋กœ ํš๊ธฐ์ ์ธ ๋ณ€๊ฒฝ์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋‘ ์„ธ๊ณ„ ๋ชจ๋‘์—์„œ ์•ฝ๊ฐ„์˜ ์ตœ์•…์˜ ์ƒํ™ฉ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ ๋ธŒ๋ ˆ์ดํ‚น ์ฒด์ธ์ง€๊ฐ€ ์žˆ๋Š” ๋™์‹œ์— ์ผ๋ฐ˜ TimeoutException ๋ฅผ ์–ป์ง€ ๋ชปํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ธฐ์ˆ ์ ์œผ๋กœ ํš๊ธฐ์ ์ธ ๋ณ€๊ฒฝ์ด ๋  ์ˆ˜ ์žˆ๋„๋ก ์ด์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

FWIW, ๊ฑฐ์˜ ๋ชจ๋“  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๊นจ์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์•ž์œผ๋กœ ์ง„ํ–‰ํ•˜๋ ค๋ฉด ์–ด๋”˜๊ฐ€์— ์„ ์„ ๊ทธ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. corefx์—์„œ ์šฐ๋ฆฌ๋Š” ๋” ํŒŒ์ƒ๋œ ์œ ํ˜•์„ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ๋˜์ง€๋Š” ๊ฒƒ์„ ์ค‘๋‹จํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/breaking-change-rules.md#exceptions

ํƒ€ํ˜‘์˜ ์„ธ๊ณ„์—์„œ ... ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์ข‹์•„ํ•˜์ง€ ์•Š์ง€๋งŒ ์”น์„ ์ˆ˜์žˆ๋Š” ๋˜ ๋‹ค๋ฅธ ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

TaskCanceledException ๋ฅผ ๊ณ„์† ๋˜์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ CancellationToken ๋ฅผ ์„ผํŠธ๋ฆฌ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

catch(TaskCanceledException ex) when(ex.CancellationToken == HttpClient.TimeoutToken)
{
}

๋‚˜๋Š” ์ด๊ฒƒ์„ ์ •๋ง๋กœ ์ข‹์•„ํ•œ๋‹ค. "๋ชป์ƒ๊ฒผ๋‹ค""๋Š” ๊ฒƒ ์™ธ์— ๋‹ค๋ฅธ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์˜ค๋Š˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ex.CancellationToken ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

"๋ชป์ƒ๊ฒผ๋‹ค"๋Š” ๊ฒƒ ์™ธ์— ๋‹ค๋ฅธ ๋‹จ์ ์€ ์—†๋‚˜์š”?

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ TimeoutToken์ด ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ๊ณ  ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ทจ์†Œ ์‹ ํ˜ธ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์€ ์ •๋ง ์‰ฌ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ด๋Ÿฌํ•œ ์šฉ๋„๋กœ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋””์ž์ธ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์„ ์ •๋ง๋กœ ์ข‹์•„ํ•œ๋‹ค. "๋ชป์ƒ๊ฒผ๋‹ค""๋Š” ๊ฒƒ ์™ธ์— ๋‹ค๋ฅธ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์˜ค๋Š˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ex.CancellationToken ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋ฌธ์„œ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ์–ธ๊ธ‰ํ•  ์ˆ˜ ์žˆ๋Š” ํŠน์ • ์˜ˆ์™ธ ์œ ํ˜•์„ ์žก๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๋œ ์ง๊ด€์ ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” OperationCanceledException ๋ฐ ํŒŒ์ƒ ์œ ํ˜•์„ ๊ณ ๋ คํ•˜์—ฌ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์˜ค๋ฅ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์ค‘๋‹จ๋œ ์‹คํ–‰์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐ ์ต์ˆ™ํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ์ดˆ๊ณผ ์‹œ HttpClient ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ผ๋ฐ˜์ ์œผ๋กœ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค. HTTP API์— ๋Œ€ํ•œ ํ˜ธ์ถœ์ด ์žˆ์—ˆ๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋™์ž‘์€ ์˜ˆ๋ฅผ ๋“ค์–ด ASP.NET Core ํŒŒ์ดํ”„๋ผ์ธ ๋ฏธ๋“ค์›จ์–ด์—์„œ ๋” ๋†’์€ ์ˆ˜์ค€์—์„œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋™์•ˆ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” OperationCanceledException , error ๋ฐ not-error, ๋˜๋Š” ๋ชจ๋“  HttpClient ํ˜ธ์ถœ์„ throw-catch ๋ธ”๋ก์œผ๋กœ ๋ž˜ํ•‘ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋˜ ํ•˜๋‚˜: TaskCanceledException์—์„œ ํŒŒ์ƒ๋œ ์ƒˆ๋กœ์šด HttpTimeoutException์„ ๋˜์ง‘๋‹ˆ๋‹ค.

๋‹ค์†Œ ๋ณด๊ธฐ ํ‰ํ•˜์ง€๋งŒ ์•„๋งˆ๋„ ์ข‹์€ ์ ˆ์ถฉ์•ˆ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ด€๋ จ๋œ ๋””์ž์ธ ์ œ์•ฝ๊ณผ ์ด์ „ ๋ฒ„์ „๊ณผ์˜ ํ˜ธํ™˜์„ฑ ๊ณ ๋ ค ์‚ฌํ•ญ์„ ๊ณ ๋ คํ•  ๋•Œ ์ •๋ง ์ถ”์•…ํ•œ๊ฐ€์š”? ์ด ์Šค๋ ˆ๋“œ์˜ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๋‹จ์ ์ด ์—†๋Š” ๋ถ„๋ช…ํžˆ ์šฐ์ˆ˜ํ•œ ์†”๋ฃจ์…˜์„ ์ œ์•ˆํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค(์‚ฌ์šฉ ๋˜๋Š” ๋””์ž์ธ ์ˆœ๋„ ๋“ฑ).

์šฐ๋ฆฌ๋Š” ์˜ˆ์™ธ ์œ ํ˜•์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์ด๊ฒƒ์„ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. "์–ด๋””์—์„œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๊นŒ?"๋ผ๋Š” ์˜ค๋žœ ๋ฌธ์ œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. -- ์˜ˆ๋ฅผ ๋“ค์–ด, ์™€์ด์–ด์— ๋„๋‹ฌํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ์š”์ฒญ์ด ์‹œ๊ฐ„ ์ดˆ๊ณผ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค -- ์šฐ๋ฆฌ๊ฐ€ ์‚ดํŽด๋ด์•ผ ํ•˜๊ณ  ์†”๋ฃจ์…˜์€ ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€๋Š” ๋ฐฉํ–ฅ๊ณผ ํ˜ธํ™˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์˜ˆ์™ธ ์œ ํ˜•์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์ด๊ฒƒ์„ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. "์–ด๋””์—์„œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๊นŒ?"๋ผ๋Š” ์˜ค๋žœ ๋ฌธ์ œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. -- ์˜ˆ๋ฅผ ๋“ค์–ด, ์™€์ด์–ด์— ๋„๋‹ฌํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ์š”์ฒญ์ด ์‹œ๊ฐ„ ์ดˆ๊ณผ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค -- ์šฐ๋ฆฌ๊ฐ€ ์‚ดํŽด๋ด์•ผ ํ•˜๊ณ  ์†”๋ฃจ์…˜์€ ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€๋Š” ๋ฐฉํ–ฅ๊ณผ ํ˜ธํ™˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์ •๋ง๋กœ ๋‹น์‹ ์ด ์•Œ์•„์•ผ ํ•  ์‚ฌํ•ญ์ž…๋‹ˆ๊นŒ? ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์œ„์น˜์— ๋”ฐ๋ผ ์˜ค๋ฅ˜๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์ €์—๊ฒŒ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Œ์„ ์•„๋Š” ๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.
๋” ๋งŽ์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์ถ”๊ฐ€ ์š”๊ตฌ ์‚ฌํ•ญ์œผ๋กœ ์ธํ•ด .NET 5 ๊ธฐ์ฐจ๋ฅผ ๋†“์น˜๋Š” ๊ฒƒ์„ ์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.wink:

@scalablecory ๊ฐ€ ์ œ๊ธฐํ•œ ๋ฌธ์ œ๋Š” ์ฃผ๋ชฉํ•  ๋งŒํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ์ด๊ฒƒ์ด ๋” ๋นจ๋ฆฌ ์ˆ˜์ •๋˜๋Š” ๊ฒƒ์„ ๋ฐฉํ•ดํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ์ดˆ๊ณผ ์ด์œ ๋ฅผ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ด์— ๋Œ€ํ•œ ์˜ˆ์™ธ ๋ฌธ์ œ ์ˆ˜์ •์„ ์ง€์—ฐํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ๋‚˜์ค‘์— ์–ธ์ œ๋“ ์ง€ Reason ์†์„ฑ์„ ์˜ˆ์™ธ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@thomaslevesque์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ „์— ๊ทธ๊ฒƒ์„ ํฐ ๋ฌธ์ œ๋กœ ๋“ค์–ด ๋ณธ ์ ์ด ์—†์Šต๋‹ˆ๋‹ค. ์–ด๋””์—์„œ ์˜ค๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ?

์•ฝ๊ฐ„์˜ ๋ช…์‹œ์ ์ธ ์กฐ์น˜๊ฐ€ ํ•„์š”ํ•œ ์ด์œ ๋ฅผ ํŒŒํ—ค์ณ๋„ ๊ดœ์ฐฎ์€ ๋ฐ˜๋ฉด ์ผ๋ฐ˜ ๊ฐœ๋ฐœ์ž์˜ ๊ฒฝ์šฐ ํ•œ ๊ฐ€์ง€ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด ์•Œ๊ณ /๊ด€๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฝค ํ‹ˆ์ƒˆ ๋””๋ฒ„๊น… ์‚ฌ๋ก€์ž…๋‹ˆ๊นŒ?

์šฐ๋ฆฌ๋Š” ์˜ˆ์™ธ ์œ ํ˜•์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์ด๊ฒƒ์„ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. "์–ด๋””์—์„œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๊นŒ?"๋ผ๋Š” ์˜ค๋žœ ๋ฌธ์ œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. -- ์˜ˆ๋ฅผ ๋“ค์–ด, ์™€์ด์–ด์— ๋„๋‹ฌํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ์š”์ฒญ์ด ์‹œ๊ฐ„ ์ดˆ๊ณผ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค -- ์šฐ๋ฆฌ๊ฐ€ ์‚ดํŽด๋ด์•ผ ํ•˜๊ณ  ์†”๋ฃจ์…˜์€ ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€๋Š” ๋ฐฉํ–ฅ๊ณผ ํ˜ธํ™˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์˜๋ฏธํ•˜๋Š” ๋ฐ”๋Š” HttpClient๊ฐ€ ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์˜ฌ๋ฐ”๋ฅธ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์˜ˆ์™ธ ์œ ํ˜•์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์ด ๊ฐ HttpMessageHandler์˜ ์ฑ…์ž„์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์˜๋ฏธํ•˜๋Š” ๋ฐ”๋Š” HttpClient๊ฐ€ ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์˜ฌ๋ฐ”๋ฅธ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์˜ˆ์™ธ ์œ ํ˜•์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์ด ๊ฐ HttpMessageHandler์˜ ์ฑ…์ž„์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์˜ค๋Š˜๋‚  ์ •์˜๋œ ์ถ”์ƒํ™”๋กœ ๊ทธ๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ๊ฐ€๋Šฅํ•œ์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. HttpMessageHandler๋Š” HttpClient์˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ์— ๋Œ€ํ•ด ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. HttpMessageHandler์— ๊ด€ํ•œ ํ•œ, ํ˜ธ์ถœ์ž์˜ ์ทจ์†Œ ํ† ํฐ ์‹ ํ˜ธ, HttpClient์˜ CancelPendingRequests ํ˜ธ์ถœ, HttpClient์˜ ๊ฐ•์ œ ์ œํ•œ ์‹œ๊ฐ„ ๋งŒ๋ฃŒ ๋“ฑ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ด์œ ๋กœ ์ทจ์†Œ ์š”์ฒญ์ด ์žˆ์„ ์ˆ˜ ์žˆ๋Š” ์ทจ์†Œ ํ† ํฐ์„ ์ „๋‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์ •๋ง๋กœ ๋‹น์‹ ์ด ์•Œ์•„์•ผ ํ•  ์‚ฌํ•ญ์ž…๋‹ˆ๊นŒ? ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์œ„์น˜์— ๋”ฐ๋ผ ์˜ค๋ฅ˜๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์ €์—๊ฒŒ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Œ์„ ์•„๋Š” ๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„๊ฐ€ ๊ท€ํ•˜์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ–ˆ๋Š”์ง€ ์•„๋Š” ๊ฒƒ์ด ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ง„๋‹จ์—๋„ ์ข‹์Šต๋‹ˆ๋‹ค. ์›๊ฒฉ ์„œ๋ฒ„๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋˜์ง€๋Š” ์•Š์ง€๋งŒ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ๋ณด๊ณ ํ•˜๋Š” ๊ฒƒ์€ ์‹ค๋ง์Šค๋Ÿฌ์šด ๊ฒฝํ—˜์ž…๋‹ˆ๋‹ค.

@davidsh ๋Š” ๋” ๋งŽ์€ ํ†ต์ฐฐ๋ ฅ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@thomaslevesque์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ „์— ๊ทธ๊ฒƒ์„ ํฐ ๋ฌธ์ œ๋กœ ๋“ค์–ด ๋ณธ ์ ์ด ์—†์Šต๋‹ˆ๋‹ค. ์–ด๋””์—์„œ ์˜ค๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ?

์ตœ๊ทผ NCL ํšŒ์˜์—์„œ ์ œ๊ธฐ๋œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์ผ์„ ํญ๋กœํ•˜๊ฑฐ๋‚˜ ์ง€์—ฐ์‹œํ‚ฌ ๋งŒํผ ์ค‘์š”ํ•˜์ง€ ์•Š๋‹ค๊ณ  ๊ฒฐ์ •ํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋จผ์ € ๋น ๋ฅธ ํ† ๋ก ์˜ ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

@dotnet/ncl @stephentoub ์ด๊ฑธ ์นจ๋Œ€์— ๋†”๋‘์ž. ์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ๊ฐ€ ํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ์ด ๋ฏธ๋ฌ˜ํ•˜๊ฒŒ ๋ฌด๋„ˆ์งˆ ๊ฒƒ์ด๋ผ๋Š” ๊ฒƒ์„ ์••๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ตœ์•…์˜ ์„ ํƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์•ž์œผ๋กœ ๋‚˜์•„๊ฐˆ ๊ฒƒ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

TaskCanceledException ์—์„œ ํŒŒ์ƒ๋œ ์ƒˆ๋กœ์šด HttpTimeoutException ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.

๋ฐ˜๋Œ€?

TaskCanceledException์—์„œ ํŒŒ์ƒ๋œ ์ƒˆ๋กœ์šด HttpTimeoutException์„ ๋˜์ง‘๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ํ•ฉ๋‹ˆ๊นŒ? ์•„๋งˆ๋„ ์ฝ”๋“œ๋ฅผ ์Šคํฌ๋Ÿฌ๋น™ํ•˜๊ณ  CancellationToken.ThrowIfCancellationRequested ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ชจ๋“  ์œ„์น˜๋ฅผ ์ฐพ์•„ ๋‹ค์Œ์œผ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

c# if (token.IsCancellationRequested) throw new HttpTimeoutException(EXTRASTUFF, token);

๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์šฐ๋ฆฌ๊ฐ€ ์‹ค์ œ๋กœ OperationCancelledException ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜๋„ ์žˆ๋Š” ๋ช…์‹œ์  ์žฅ์†Œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด์จŒ๋“  ๋ชจ๋“  ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ๊ฒ€ํ† ํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์–ด์จŒ๋“  ๋ชจ๋“  ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ๊ฒ€ํ† ํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์•„๋งˆ๋„ ์•„๋‹ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ์ œ์–ดํ•˜์ง€ ์•Š๋Š” ๊ณณ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์•„์ง ํ™•์‹คํ•˜์ง€ ์•Š์Œ). ๋”ฐ๋ผ์„œ ์ž ์žฌ์ ์œผ๋กœ ์ž„์˜์˜ TaskCancelledException/OperationCancelledException์„ ์žก๊ณ  ์ƒˆ๋กœ์šด HttpTimeoutException์„ ๋‹ค์‹œ ๋ฐœ์ƒ์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์•„๋งˆ๋„ ์ฝ”๋“œ๋ฅผ ์Šคํฌ๋Ÿฌ๋น™ํ•˜๊ณ  CancellationToken.ThrowIfCancellationRequested๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ชจ๋“  ์œ„์น˜๋ฅผ ์ฐพ์•„ ๋‹ค์Œ์œผ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์–ด์จŒ๋“  ๋ชจ๋“  ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ๊ฒ€ํ† ํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์ •ํ™•ํžˆ.

ํ•ธ๋“ค๋Ÿฌ์— ๊ด€ํ•œ ํ•œ ๊ทธ๋“ค์ด ๋ฐ›๋Š” ๋ชจ๋“  ๊ฒƒ์€ CancellationToken์ž…๋‹ˆ๋‹ค. ๊ทธ๋“ค์€ ๊ทธ ํ† ํฐ์ด ์–ด๋””์„œ ์™”๋Š”์ง€, ๋ˆ„๊ฐ€ ์ƒ์„ฑํ–ˆ๋Š”์ง€ ๋˜๋Š” ์ทจ์†Œ๋ฅผ ์š”์ฒญํ–ˆ์„ ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋ฅผ ์•Œ์ง€ ๋ชปํ•˜๊ณ  ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋“ค์ด ์•„๋Š” ๊ฒƒ์€ ์–ด๋–ค ์‹œ์ ์—์„œ ์ทจ์†Œ๊ฐ€ ์š”์ฒญ๋  ์ˆ˜ ์žˆ๊ณ  ์‘๋‹ต์œผ๋กœ ์ทจ์†Œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•ธ๋“ค๋Ÿฌ์—์„œ ํ•  ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค.

๊ด€๋ จ CancellationTokenSource๋ฅผ ๋งŒ๋“ค๊ณ  ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์€ HttpClient ์ž์ฒด์ž…๋‹ˆ๋‹ค.
https://github.com/dotnet/corefx/blob/bdd33976fe3713cc3e78b83cf2a1176c70b793be/src/System.Net.Http/src/System/Net/Http/HttpClient.cs#L488
ํƒ€์ž„์•„์›ƒ์€ ์ „์ ์œผ๋กœ HttpClient ์ž์ฒด์˜ ๋ฐœ๋ช…์ด๋ฉฐ ํŠน๋ณ„ํ•œ ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚œ ์ทจ์†Œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ธ์ง€ ์—ฌ๋ถ€๋งŒ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, ์œ„์—์„œ ์ฐธ์กฐํ•œ ์ฝ”๋“œ๋Š” ๋ณ„๋„์˜ CancellationTokenSource๋กœ ๋˜๋Š” ์ž์ฒด์ ์œผ๋กœ ์‹œ๊ฐ„ ์ดˆ๊ณผ ๋…ผ๋ฆฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  CTS๋ฅผ ์ทจ์†Œํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ๋ณ„๋„๋กœ ์ถ”์ ํ•˜๋„๋ก ๋ฆฌํŒฉํ† ๋งํ•ด์•ผ ํ•˜๋ฉฐ ๋˜ํ•œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ๋„ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. ์—ฌ๊ธฐ์— ์ถ”๊ฐ€ ํ• ๋‹น์„ ์ถ”๊ฐ€ํ•  ๊ฒƒ์ด๋ผ๋Š” ์ ์—์„œ ์ด๊ฒƒ์€ ์œ ๋ฃŒ๊ฐ€ ์•„๋‹ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์ง€๋งŒ ์ฝ”๋“œ ๊ด€์ ์—์„œ๋Š” ๋น„๊ต์  ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ HttpClient์˜ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ„์น˜์— ์žˆ์Šต๋‹ˆ๋‹ค.
https://github.com/dotnet/corefx/blob/bdd33976fe3713cc3e78b83cf2a1176c70b793be/src/System.Net.Http/src/System/Net/Http/HttpClient.cs#L536 -L541
๊ทธ๋ฆฌ๊ณ 
https://github.com/dotnet/corefx/blob/bdd33976fe3713cc3e78b83cf2a1176c70b793be/src/System.Net.Http/src/System/Net/Http/HttpClient.cs#L562 -L566
์˜ˆ์™ธ๋ฅผ ํฌ์ฐฉํ•˜๋ฉด ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ ์ทจ์†Œ๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋งŒ๋ฃŒ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ๋งŒ๋ฃŒ๋œ ๊ฒฝ์šฐ ์ทจ์†Œ์˜ ์›์ธ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์›ํ•˜๋Š” ์œ ํ˜•์˜ ์ƒˆ ์˜ˆ์™ธ๋ฅผ throwํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ œํ•œ ์‹œ๊ฐ„์ด ๋งŒ๋ฃŒ๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ  ๋งŒ๋ฃŒ๋œ ๊ฒฝ์šฐ ์ทจ์†Œ์˜ ์›์ธ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์›ํ•˜๋Š” ์œ ํ˜•์˜ ์ƒˆ ์˜ˆ์™ธ๋ฅผ throwํ•˜๋ ค๋ฉด ํŠน์ˆ˜ํ•œ ๊ฒฝ์šฐ ์ทจ์†Œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

HttpClient.Timeout ์†์„ฑ์ด ๊ด€๋ จ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ด๊ฒƒ์€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๊นŒ? ์•„๋งˆ๋„ "๋ฌดํ•œ"์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. HttpClient API(GetAsync, SendAync ๋“ฑ)์— ์ง์ ‘ ์ „๋‹ฌ๋œ ์ทจ์†Œ ํ† ํฐ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋˜‘๊ฐ™์ด ์ž‘๋™ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๊นŒ?

HttpClient.Timeout ์†์„ฑ์ด ๊ด€๋ จ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ด๊ฒƒ์€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๊นŒ?

์งˆ๋ฌธ์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค... ์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” ์œ ์ผํ•œ ์‹œ๊ฐ„ ์ œํ•œ์ž…๋‹ˆ๋‹ค. ๋ฌดํ•œ์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ์ด ์‹œ๊ฐ„ ์ดˆ๊ณผ๋กœ ์ธํ•ด ์ทจ์†Œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉฐ ํ•  ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ ํŠน์ˆ˜ํ•œ ๊ฒฝ์šฐ Timeout == Infinite์ด๋ฉฐ ๊ณ„์†ํ•ด์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
https://github.com/dotnet/corefx/blob/bdd33976fe3713cc3e78b83cf2a1176c70b793be/src/System.Net.Http/src/System/Net/Http/HttpClient.cs#L481

HttpClient API(GetAsync, SendAync ๋“ฑ)์— ์ง์ ‘ ์ „๋‹ฌ๋œ ์ทจ์†Œ ํ† ํฐ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋˜‘๊ฐ™์ด ์ž‘๋™ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๊นŒ?

์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ ์ž์‹ ์ด ๋งŒ๋“  ํ† ํฐ ์ค‘ ํ•˜๋‚˜๋กœ ์ „๋‹ฌ๋œ ๋ชจ๋“  ํ† ํฐ์„ ๊ฒฐํ•ฉํ•ฉ๋‹ˆ๋‹ค.
https://github.com/dotnet/corefx/blob/bdd33976fe3713cc3e78b83cf2a1176c70b793be/src/System.Net.Http/src/System/Net/Http/HttpClient.cs#L485
์ด์ „ ์˜๊ฒฌ์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด HttpClient.Timeout์— ๋Œ€ํ•œ ์ž์ฒด ์ฒ˜๋ฆฌ๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. ํ˜ธ์ถœ์ž๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋กœ ๊ตฌ์„ฑํ•œ ํ† ํฐ์„ ์ „๋‹ฌํ•˜๊ธฐ๋กœ ์„ ํƒํ•œ ๊ฒฝ์šฐ ์ด๋ฅผ ํŠน๋ณ„ํžˆ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ํ˜ธ์ถœ์ž์—๊ฒŒ ๋‹ฌ๋ ค ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ํ•œ ๊ทธ๊ฒƒ์€ ์ทจ์†Œ ์š”์ฒญ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

๋ฐ˜๋Œ€?

๋‚ด๋ถ€ ์˜ˆ์™ธ๋กœ TimeoutException์„ ๋ž˜ํ•‘ํ•˜๋Š” ์ทจ์†Œ ์˜ˆ์™ธ๋ฅผ throwํ•˜๋Š” ๋Œ€์‹  ๊ธฐ์กด TimeoutException๊ณผ "๊ฒฝ์Ÿํ•˜๋Š”" ์ƒˆ๋กœ์šด ํŒŒ์ƒ ์˜ˆ์™ธ ์œ ํ˜•์„ ๋„์ž…ํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๋ชฉํ‘œ๊ฐ€ ์†Œ๋น„์ž๊ฐ€ ์ทจ์†Œ๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋กœ ์ธํ•œ ๊ฒƒ์ธ์ง€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด ์ถฉ๋ถ„ํ• ๊นŒ์š”? ์ƒˆ๋กœ์šด ํ‘œ๋ฉด์ ์„ ํ•„์š”๋กœ ํ•˜์ง€ ์•Š๊ณ  ๋™์ผํ•œ ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚ด๋ถ€ ์˜ˆ์™ธ๋กœ TimeoutException์„ ๋ž˜ํ•‘ํ•˜๋Š” ์ทจ์†Œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š” ๋Œ€์‹  ๊ธฐ์กด TimeoutException๊ณผ "๊ฒฝ์Ÿํ•˜๋Š”" ์ƒˆ๋กœ์šด ํŒŒ์ƒ ์˜ˆ์™ธ ์œ ํ˜•์„ ๋„์ž…ํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

ํŽธ์˜์ƒ ๋Œ€๋ถ€๋ถ„. ์“ฐ๊ธฐ๊ฐ€ ๋” ์‰ฝ๊ณ  ์ง๊ด€์ ์ž…๋‹ˆ๋‹ค.

catch(HttpTimeoutException)

~๋ณด๋‹ค

catch(OperationCanceledException ex) when (ex.InnerException is TimeoutException)

์ฆ‰, ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์‹๋ณ„ํ•˜๊ธฐ๊ฐ€ ํ•ฉ๋ฆฌ์ ์œผ๋กœ ์‰ฌ์šด ํ•œ ์–ด๋Š ์ชฝ์ด๋“  ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

TimeoutException์„ ๋‚ด๋ถ€ ์˜ˆ์™ธ๋กœ ๋ž˜ํ•‘ํ•˜๋Š” ์ทจ์†Œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

์ข‹์€ ํƒ€ํ˜‘์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ๊ฐ€์žฅ ํฐ ๊ณจ์นซ๊ฑฐ๋ฆฌ์˜€๋˜ ์˜ˆ์™ธ ๋กœ๊ทธ๋ฅผ โ€‹โ€‹๋ช…ํ™•ํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ธฐ๋ณธ ์ฒ˜๋ฆฌ๊ธฐ์— ์˜ํ•ด ๋ฐœ์ƒํ•˜๋Š” ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ์•„๋‹ˆ๋ผ ์š”์ฒญ์„ ์ทจ์†Œํ•˜๋Š” HttpClient๋ผ๋Š” ์‚ฌ์‹ค์„ ๋” ์ž˜ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์—†์Šต๋‹ˆ๋‹ค.

๋ถ„๋ฅ˜:

  • ์ด ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ InnerException ์˜ TimeoutException ๊ฐ€ ๋˜๋„๋ก ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์‹œ๊ฐ„ ์ดˆ๊ณผ ํŠธ๋ฆฌ๊ฑฐ๋กœ ์ธํ•œ ์ทจ์†Œ๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌ๋ณ„ํ•˜๋Š” ๋กœ๊น…/์ง„๋‹จ์šฉ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
  • ์ทจ์†Œ๋ฅผ ๊ตฌ๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ง€์นจ์œผ๋กœ ๋ฌธ์„œ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. TimeoutException ํ™•์ธ, ๋ฌดํ•œ Timeout $ ์‚ฌ์šฉ, ์‹œ๊ฐ„ ์ดˆ๊ณผ ๊ธฐ๊ฐ„์ด ์žˆ๋Š” CancellationToken ์ „๋‹ฌ ๋“ฑ

์—ฌ๊ธฐ์—์„œ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์•ฝ๊ฐ„์˜ ํƒ€ํ˜‘์ด ํ•„์š”ํ•˜๋ฉฐ ์ด๊ฒƒ์ด ๊ธฐ์กด ์ฝ”๋“œ์™€ ์šฐ์ˆ˜ํ•œ ์ˆ˜์ค€์˜ ํ˜ธํ™˜์„ฑ์„ ์ œ๊ณตํ•  ๊ฒƒ์ด๋ผ๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค.

๋‚ด๋ถ€ ์˜ˆ์™ธ๋กœ TimeoutException์„ ๋ž˜ํ•‘ํ•˜๋Š” ์ทจ์†Œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š” ๋Œ€์‹  ๊ธฐ์กด TimeoutException๊ณผ "๊ฒฝ์Ÿํ•˜๋Š”" ์ƒˆ๋กœ์šด ํŒŒ์ƒ ์˜ˆ์™ธ ์œ ํ˜•์„ ๋„์ž…ํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

ํŽธ์˜์ƒ ๋Œ€๋ถ€๋ถ„. ์“ฐ๊ธฐ๊ฐ€ ๋” ์‰ฝ๊ณ  ์ง๊ด€์ ์ž…๋‹ˆ๋‹ค.

catch(HttpTimeoutException)

~๋ณด๋‹ค

catch(OperationCanceledException ex) when (ex.InnerException is TimeoutException)

์ฆ‰, ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์‹๋ณ„ํ•˜๊ธฐ๊ฐ€ ํ•ฉ๋ฆฌ์ ์œผ๋กœ ์‰ฌ์šด ํ•œ ์–ด๋Š ์ชฝ์ด๋“  ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

@scalablecory ๋ช‡ ์‹œ๊ฐ„ ๋™์•ˆ ํ† ๋ก ์„ ๋†“์นœ ๊ฒƒ ๊ฐ™์•„ ๋ถ€๋„๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜ LoB .NET ๊ฐœ๋ฐœ์ž์™€ ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ์˜์‚ฌ ์†Œํ†ต์„ ํ•œ ๊ฒฝํ—˜์— ๋”ฐ๋ฅด๋ฉด ํŒŒ์ƒ๋œ HttpTimeoutException์„ ์žก๋Š” ๊ฒƒ์ด ์‚ฌ๋žŒ๋“ค์ด ์ดํ•ด/์ฑ„ํƒํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ์‰ฝ์Šต๋‹ˆ๋‹ค. ๋‚ด๋ถ€ ์˜ˆ์™ธ์— ์˜ˆ์™ธ ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์‹ญ์‹œ์˜ค. ๋ชจ๋“  ์‚ฌ๋žŒ์ด ์˜ˆ์™ธ ํ•„ํ„ฐ๊ฐ€ ๋ฌด์—‡์ธ์ง€์กฐ์ฐจ ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

๋™์‹œ์—, ๋‚˜๋Š” ์ด๊ฒƒ์ด ๋‹น์‹ ์ด ๋งํ–ˆ๋“ฏ์ด ํ™•์‹คํ•œ ์Šน/ํƒ€๊ฒฐ ์†”๋ฃจ์…˜์ด ์—†๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค ์ค‘ ํ•˜๋‚˜๋ผ๋Š” ๊ฒƒ์„ ์™„์ „ํžˆ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

@ericsampson ์ด๋ผ๋Š” ์ ์— ๋™์˜ํ–ˆ์ง€๋งŒ ์ด๋Š” ๋งŽ์€ ๊ธฐ์กด ์‚ฌ์šฉ์ž์—๊ฒŒ ํฐ ๋ณ€ํ™”๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. @scalablecory ๊ฐ€ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ํ˜ธํ™˜์„ฑ ์†์ƒ์„ ์ œํ•œํ•˜๋ฉด์„œ ๊ฐœ์„ ์„ ์œ„ํ•ด ํƒ€ํ˜‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์™ธ์— ๋Œ€ํ•ด ์ƒ๊ฐํ•  ๋•Œ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ž˜ํ•‘ ๋ฐฉ์‹์ด ๋” ๋‚ซ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. OperationCanceledException์—์„œ ์˜ˆ์™ธ๋ฅผ ํŒŒ์ƒ์‹œํ‚ค๋ฉด ์ด๋Ÿฌํ•œ ์˜ˆ์™ธ๋ฅผ ๋น„๋™๊ธฐ์‹ ์‚ฌ์šฉ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‹ฌ์ง€์–ด ๊ทธ๊ฒƒ์€ ์ž‘์—…์— ๋นก๋นกํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๊ณผ๊ฑฐ์— ๋ณด์•˜๋“ฏ์ด ๋น„๋™๊ธฐ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ๋ช‡ ๊ฐ€์ง€ ์ง„ํ™”๊ฐ€ ์žˆ์—ˆ๊ณ  ์ž‘์—…์„ ๊ตฌํ˜„ ์„ธ๋ถ€ ์‚ฌํ•ญ์œผ๋กœ ๊ฐ„์ฃผํ•˜์ง€๋งŒ ์˜ˆ๋ฅผ ๋“ค์–ด TimeoutException์€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ์†Œ์ผ“ ๊ฐœ๋…์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.

@ericsampson ์ด๋ผ๋Š” ์ ์— ๋™์˜ํ–ˆ์ง€๋งŒ ์ด๋Š” ๋งŽ์€ ๊ธฐ์กด ์‚ฌ์šฉ์ž์—๊ฒŒ ํฐ ๋ณ€ํ™”๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. @scalablecory ๊ฐ€ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ํ˜ธํ™˜์„ฑ ์†์ƒ์„ ์ œํ•œํ•˜๋ฉด์„œ ๊ฐœ์„ ์„ ์œ„ํ•ด ํƒ€ํ˜‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋ฐ˜๋“œ์‹œ ๋ž˜ํ•‘ ์˜ต์…˜์— ๋™์˜ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ - ๋‚ด๊ฐ€ ์ดํ•ดํ•œ ํ•œ - ๋ฌธ์ œ๋Š” ํŒŒ์ƒ ์˜ต์…˜์ด ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์•„๋‹ˆ๋ผ ๋ž˜ํ•‘ ์˜ต์…˜์ด ๋” ๊ฐ„๋‹จํ•˜๋‹ค๋Š” ์ ์„ ์ง€์ ํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ† ๋ก ์˜ ์•ž๋ถ€๋ถ„์—์„œ @stephentoub๊ฐ€ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ํŒŒ์ƒ๋œ ์˜ˆ์™ธ๋ฅผ throwํ•˜๋Š” ๊ฒƒ์€ corefx ์ง€์นจ์— ๋”ฐ๋ผ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์œผ๋กœ ๊ฐ„์ฃผ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

TaskCanceledException์—์„œ HttpTimeoutException์„ ํŒŒ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์€ ์ด ๋‘ ์˜ˆ์™ธ ์‚ฌ์ด์˜ "is a" ๊ด€๊ณ„๋ฅผ ์œ„๋ฐ˜ํ•ฉ๋‹ˆ๋‹ค. HttpTimeoutException์„ ํŠน๋ณ„ํžˆ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋Š” ํ•œ ์—ฌ์ „ํžˆ ์ทจ์†Œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋„๋ก ํ˜ธ์ถœ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹œ๊ฐ„ ์ดˆ๊ณผ์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ทจ์†Œ ํ† ํฐ์ด ์ทจ์†Œ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์ˆœ๊ฐ„์— ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ์ฒ˜๋ฆฌ์ž…๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ์ฝ”์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋‘ ๊ฐœ์˜ ํƒ€์ž„์•„์›ƒ ์˜ˆ์™ธ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์€ ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.
TaskCanceledException์— ๋‚ด๋ถ€ ์˜ˆ์™ธ๋กœ TimeoutException์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ ์ทจ์†Œ ํ† ํฐ์„ ํ™•์ธํ•˜๊ณ  ์‹œ๊ฐ„ ์ดˆ๊ณผ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์œ ์ผํ•œ ํ•ฉ๋ฆฌ์ ์ธ ํ•ด๊ฒฐ์ฑ…์€ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์‹œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ TimeoutException์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ, ๊ทธ๊ฒƒ์€ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋  ๊ฒƒ์ด์ง€๋งŒ ๋ฌธ์ œ๋Š” ์ด๋ฏธ ์—ฌ๋Ÿฌ ์ฃผ์š” ๋ฆด๋ฆฌ์Šค์— ๊ฑธ์ณ ์žˆ์œผ๋ฉฐ ์ฃผ์š” ๋ฆด๋ฆฌ์Šค์˜ ๋ชฉ์ ์€ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.

๊ธฐ์กด ๋ฒ„์ „์— ๋Œ€ํ•œ ๋ฌธ์„œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ๋ฉ”์„œ๋“œ๊ฐ€ TaskCanceledException ๋ฐ/๋˜๋Š” OperationCanceledException (๋‘ ๊ตฌ์ฒด์ ์ธ ์œ ํ˜• ๋ชจ๋‘ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚จ)์„ throwํ•  ์ˆ˜ ์žˆ์Œ์„ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ํ˜„์žฌ ๋ฌธ์„œ์—๋Š” HttpRequestException ๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค๊ณ  ๋‚˜์™€ ์žˆ๋Š”๋ฐ ์ด๋Š” ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š๊ณ  ์˜คํ•ด์˜ ์†Œ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

@qidydl ์ด ๊ณ„ํš์ž…๋‹ˆ๋‹ค... ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๋ฐฉ๋ฒ•๊ณผ ์ด๋ฅผ ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์™„์ „ํžˆ ๋ฌธ์„œํ™”ํ•ฉ๋‹ˆ๋‹ค.

@alnikola , @scalablecory , @davidsh , @karelz , ๊ทธ๋ฆฌ๊ณ  ์ด ํ† ๋ก /๊ตฌํ˜„์— ์ฐธ์—ฌํ•œ ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ์ •๋ง ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰

๊ด€๋ จ ๋ฌธ์ œ

chunseoklee picture chunseoklee  ยท  3์ฝ”๋ฉ˜ํŠธ

sahithreddyk picture sahithreddyk  ยท  3์ฝ”๋ฉ˜ํŠธ

GitAntoinee picture GitAntoinee  ยท  3์ฝ”๋ฉ˜ํŠธ

Timovzl picture Timovzl  ยท  3์ฝ”๋ฉ˜ํŠธ

aggieben picture aggieben  ยท  3์ฝ”๋ฉ˜ํŠธ