Microsoft MVP성태의 닷넷 이야기
닷넷: 2321. Blazor에서 발생할 수 있는 async void 메서드의 부작용 [링크 복사], [링크+제목 복사],
조회: 2486
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

(시리즈 글이 12개 있습니다.)
.NET Framework: 698. C# 컴파일러 대신 직접 구현하는 비동기(async/await) 코드
; https://www.sysnet.pe.kr/2/0/11351

.NET Framework: 716. async 메서드의 void 반환 타입 사용에 대하여
; https://www.sysnet.pe.kr/2/0/11414

.NET Framework: 717. Task를 포함하지 않는 async 메서드의 동작 방식
; https://www.sysnet.pe.kr/2/0/11415

.NET Framework: 719. Task를 포함하는 async 메서드의 동작 방식
; https://www.sysnet.pe.kr/2/0/11417

.NET Framework: 731. C# - await을 Task 타입이 아닌 사용자 정의 타입에 적용하는 방법
; https://www.sysnet.pe.kr/2/0/11456

.NET Framework: 737. C# - async를 Task 타입이 아닌 사용자 정의 타입에 적용하는 방법
; https://www.sysnet.pe.kr/2/0/11484

.NET Framework: 813. C# async 메서드에서 out/ref/in 유형의 인자를 사용하지 못하는 이유
; https://www.sysnet.pe.kr/2/0/11850

닷넷: 2138. C# - async 메서드 호출 원칙
; https://www.sysnet.pe.kr/2/0/13405

닷넷: 2147. C# - 비동기 메서드의 async 예약어 유무에 따른 차이
; https://www.sysnet.pe.kr/2/0/13421

닷넷: 2318. C# - (async Task가 아닌) async void 사용 시의 부작용
; https://www.sysnet.pe.kr/2/0/13884

닷넷: 2319. ASP.NET Core Web API / Razor 페이지에서 발생할 수 있는 async void 메서드의 부작용
; https://www.sysnet.pe.kr/2/0/13885

닷넷: 2321. Blazor에서 발생할 수 있는 async void 메서드의 부작용
; https://www.sysnet.pe.kr/2/0/13888




Blazor에서 발생할 수 있는 async void 메서드의 부작용

지난 글을 통해 ASP.NET Core Web 환경에서 async void 메서드를 사용한 것에 대한 부작용을 설명했는데요,

ASP.NET Core Web API / Razor 페이지에서 발생할 수 있는 async void 메서드의 부작용
; https://www.sysnet.pe.kr/2/0/13885

이와 유사한 문제를 Blazor에서도 겪게 됩니다. 예를 들어 Blazor 프로젝트 생성 시 기본 작성되는 Counter.razor 파일은,

@page "/counter"
@rendermode InteractiveServer

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

기본적으로 동기 방식의 메서드로 정의돼 있는데요, 만약 해당 메서드 내부의 코드에서 await을 사용하고 싶어 무심코 async void로 정의한다면,

@code {
    private int currentCount = 0;

    private async void IncrementCount()
    {
        await Task.Delay(1000);
        currentCount++;
    }
}

Blazor 내부 프레임워크에서는 저 메서드에 대한 호출을 InvokeAsync로 경유합니다.

// src/Components/Components/src/EventCallbackWorkItem.cs
// https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/EventCallbackWorkItem.cs

internal static Task InvokeAsync<T>(MulticastDelegate? @delegate, T arg)
{
    switch (@delegate)
    {
        case null:
            return Task.CompletedTask;

        case Action action:
            action.Invoke(); // async void로 정의한 메서드 호출
            return Task.CompletedTask;

        case Action<T> actionEventArgs:
            actionEventArgs.Invoke(arg);
            return Task.CompletedTask;

        case Func<Task> func: // async Task로 정의한 메서드 호출
            return func.Invoke();

        case Func<T, Task> funcEventArgs:
            return funcEventArgs.Invoke(arg);

        default:
            {
                try
                {
                    return @delegate.DynamicInvoke(arg) as Task ?? Task.CompletedTask;
                }
                catch (TargetInvocationException e)
                {
                    // Since we fell into the DynamicInvoke case, any exception will be wrapped
                    // in a TIE. We can expect this to be thrown synchronously, so it's low overhead
                    // to unwrap it.
                    return Task.FromException(e.InnerException!);
                }
            }
    }
}

지난 글을 이해했다면, IncrementCount가 어떻게 동작하게 될지 예상할 수 있을 텐데요, 즉, "Click me" 버튼을 누르면 첫 번째 클릭에는 화면에 아무런 업데이트가 되지 않습니다. 그리고 두 번째로 누르면 이전 첫 번째 버튼 클릭으로 증가했던 "1"이 화면에 보이지만 실제 currentCount의 실제 값은 2가 된 상태로 서버에 저장돼 있는 것입니다.




참고로, Blazor에서 저 부작용이 더 위험한 이유가 바로 상태 저장이 된다는 점 때문입니다. 그래서 자칫 개발자가 테스트를 하면서 첫 번째 클릭에 대한 반응이 없는 것을 뭔가 웹 브라우저의 클릭이 단순히 무시된 것으로 착각할 수 있기 때문에 별다른 자각 없이 저 문제를 안고 릴리스하는 사태가 발생할 수 있습니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 2/17/2025]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 16  17  18  [19]  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13464정성태11/28/202310371닷넷: 2174. C# - .NET 7부터 UnmanagedCallersOnly 함수 export 기능을 AOT 빌드에 통합파일 다운로드1
13463정성태11/27/20239724오류 유형: 881. Visual Studio - NU1605: Warning As Error: Detected package downgrade
13462정성태11/27/202310324오류 유형: 880. Visual Studio - error CS0246: The type or namespace name '...' could not be found
13461정성태11/26/202310131닷넷: 2173. .NET Core 3/5+ 기반의 COM Server를 registry 등록 없이 사용하는 방법파일 다운로드1
13460정성태11/26/202310166닷넷: 2172. .NET 6+ 기반의 COM Server 내에 Type Library를 내장하는 방법파일 다운로드1
13459정성태11/26/202310615닷넷: 2171. .NET Core 3/5+ 기반의 COM Server를 기존의 regasm처럼 등록하는 방법파일 다운로드1
13458정성태11/26/202310694닷넷: 2170. .NET Core/5+ 기반의 COM Server를 tlb 파일을 생성하는 방법(tlbexp)
13457정성태11/25/202310340VS.NET IDE: 187. Visual Studio - 16.9 버전부터 추가된 "Display inline type hints" 옵션
13456정성태11/25/202311072닷넷: 2169. C# - OpenAI를 사용해 PDF 데이터를 대상으로 OpenAI 챗봇 작성 [1]파일 다운로드1
13455정성태11/25/202310705닷넷: 2168. C# - Azure.AI.OpenAI 패키지로 OpenAI 사용파일 다운로드1
13454정성태11/23/202311062닷넷: 2167. C# - Qdrant Vector DB를 이용한 Embedding 벡터 값 보관/조회 (Azure OpenAI) [1]파일 다운로드1
13453정성태11/23/20239631오류 유형: 879. docker desktop 설치 시 "Invalid JSON string. (Exception from HRESULT: 0x83750007)"
13452정성태11/22/202310169닷넷: 2166. C# - Azure OpenAI API를 이용해 사용자가 제공하는 정보를 대상으로 검색하는 방법파일 다운로드1
13451정성태11/21/202310265닷넷: 2165. C# - Azure OpenAI API를 이용해 ChatGPT처럼 동작하는 콘솔 응용 프로그램 제작파일 다운로드1
13450정성태11/21/202310072닷넷: 2164. C# - Octokit을 이용한 GitHub Issue 검색파일 다운로드1
13449정성태11/21/202310537개발 환경 구성: 688. Azure OpenAI 서비스 신청 방법
13448정성태11/20/202310289닷넷: 2163. .NET 8 - Dynamic PGO를 결합한 성능 향상파일 다운로드1
13447정성태11/16/202310643닷넷: 2162. ASP.NET Core 웹 사이트의 SSL 설정을 코드로 하는 방법
13446정성태11/16/202310563닷넷: 2161. .NET Conf 2023 - Day 1 Blazor 개요 정리
13445정성태11/15/202311587Linux: 62. 리눅스/WSL에서 CA 인증서를 저장하는 방법
13444정성태11/15/202310778닷넷: 2160. C# 12 - Experimental 특성 지원
13443정성태11/14/202310490개발 환경 구성: 687. OpenSSL로 생성한 사용자 인증서를 ASP.NET Core 웹 사이트에 적용하는 방법
13442정성태11/13/202310169개발 환경 구성: 686. 비주얼 스튜디오로 실행한 ASP.NET Core 사이트를 WSL 2 인스턴스에서 https로 접속하는 방법
13441정성태11/12/202310781닷넷: 2159. C# - ASP.NET Core 프로젝트에서 서버 Socket을 직접 생성하는 방법파일 다운로드1
13440정성태11/11/20239641Windows: 253. 소켓 Listen 시 방화벽의 Public/Private 제어 기능이 비활성화된 경우
13439정성태11/10/202311515닷넷: 2158. C# - 소켓 포트를 미리 시스템에 등록/예약해 사용하는 방법(Port Exclusion Ranges)파일 다운로드1
... 16  17  18  [19]  20  21  22  23  24  25  26  27  28  29  30  ...