Blazor 이벤트 처리기를 사용하여 C# 코드를 DOM 이벤트에 연결

완료됨

대부분의 HTML 요소는 페이지 로딩이 완료되거나, 사용자가 단추를 클릭했거나, HTML 요소의 내용이 변경된 경우와 같이 중요한 일이 발생할 때 트리거되는 이벤트를 노출합니다. 앱은 다음과 같은 여러 가지 방법으로 이벤트를 처리할 수 있습니다.

  • 앱은 이벤트를 무시할 수 있습니다.
  • 앱은 JavaScript로 작성된 이벤트 처리기를 실행하여 이벤트를 처리할 수 있습니다.
  • 앱은 C#으로 작성된 Blazor 이벤트 처리기를 실행하여 이벤트를 처리할 수 있습니다.

이 단원에서는 세 번째 옵션, 즉 C#에서 Blazor 이벤트 처리기를 만들어 이벤트를 처리하는 방법을 자세히 살펴봅니다.

Blazor 및 C#을 사용하여 이벤트 처리

Blazor 앱의 HTML 태그에 있는 각 요소는 여러 이벤트를 지원합니다. 이러한 이벤트는 대부분 일반 웹 애플리케이션에서 사용할 수 있는 DOM 이벤트에 해당하지만 코드를 작성하여 트리거되는 사용자 정의 이벤트를 만들 수도 있습니다. Blazor를 사용하여 이벤트를 캡처하려면 이벤트를 처리하는 C# 메서드를 작성한 다음 Blazor 지시문을 사용하여 이벤트를 메서드에 바인딩합니다. DOM 이벤트의 경우 Blazor 지시문은 @onkeydown 또는 @onfocus 같은 동등한 HTML 이벤트와 동일한 이름을 공유합니다. 예를 들어 Blazor Server 앱을 사용하여 생성된 샘플 앱은 Counter.razor 페이지에 다음 코드를 포함합니다. 이 페이지에는 단추가 표시됩니다. 사용자가 단추를 선택하면 @onclick 이벤트는 단추를 클릭한 횟수를 나타내는 카운터를 증가시키는 IncrementCount 메서드를 트리거합니다. 카운터 변수의 값은 페이지에서 <p> 요소에 의해 표시됩니다.

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

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

@code {
    private int currentCount = 0;

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

많은 이벤트 처리기 메서드는 추가 컨텍스트 정보를 제공하는 매개 변수를 사용합니다. 이 매개 변수를 EventArgs 매개 변수라고 합니다. 예를 들어, @onclick 이벤트는 사용자가 클릭한 단추에 대한 정보를 전달하거나, 단추를 클릭할 때 Ctrl 또는 Alt와 같은 버튼을 함께 눌렀는지 여부에 대한 정보를 MouseEventArgs 매개 변수로 전달합니다. 메서드를 호출할 때는 이 매개 변수를 제공할 필요가 없습니다. Blazor 런타임은 자동으로 이를 추가합니다. 이 매개 변수는 이벤트 처리기에서 쿼리할 수 있습니다. 다음 코드는 사용자가 버튼을 클릭할 때 Ctrl 키를 함께 누르는 경우 이전 예제에 표시된 카운터를 5씩 증가시킵니다.

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

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


@code {
    private int currentCount = 0;

    private void IncrementCount(MouseEventArgs e)
    {
        if (e.CtrlKey) // Ctrl key pressed as well
        {
            currentCount += 5;
        }
        else
        {
            currentCount++;
        }
    }
}

다른 이벤트는 다른 EventArgs 매개 변수를 제공합니다. 예를 들어, @onkeypress 이벤트는 사용자가 누른 키를 나타내는 KeyboardEventArgs 매개 변수를 전달합니다. DOM 이벤트의 경우 이 정보가 필요하지 않으면 이벤트 처리 메서드에서 EventArgs 매개 변수를 생략하면 됩니다.

JavaScript에서의 이벤트 처리와 Blazor를 사용한 이벤트 처리의 차이 이해

기존 웹 애플리케이션은 JavaScript를 사용하여 이벤트를 캡처하고 처리합니다. HTML <스크립트> 요소의 일부로 함수를 만든 다음 이벤트가 발생할 때 해당 함수를 호출하도록 합니다. 앞의 Blazor 예제와 비교하기 위해 다음 코드는 값을 증분하고 사용자가 내 클릭 단추를 선택할 때마다 결과를 표시하는 HTML 페이지의 조각을 표시합니다. 이 코드는 jQuery 라이브러리를 사용하여 DOM에 액세스합니다.

<p id="currentCount">Current count: 0</p>

<button class="btn btn-primary" onclick="incrementCount()">Click me</button>

<!-- Omitted for brevity -->

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
    var currentCount = 0;

    function incrementCount() {
        currentCount++;
        $('#currentCount').html('Current count:' + currentCount);
    }
</script>

두 버전의 이벤트 처리기에서 구문상 차이점 외에도 다음과 같은 기능 차이를 식별해야 합니다.

  • JavaScript는 이벤트 이름 앞에 @ 기호를 붙이지 않으며 이는 Blazor 지시문이 아닙니다.
  • Blazor 코드에서 이벤트에 연결할 때 이벤트 처리 메서드의 이름을 지정합니다. JavaScript에서는 이벤트 처리 메서드를 호출하는 문을 작성하고 둥근 대괄호와 필요한 매개 변수를 지정합니다.
  • 가장 중요한 점은 JavaScript 이벤트 처리기는 클라이언트의 브라우저에서 실행된다는 것입니다. Blazor Server 앱을 빌드하는 경우 Blazor 이벤트 처리기는 서버에서 실행되며 이벤트 처리기가 완료된 경우 UI에 대한 변경 내용으로만 브라우저를 업데이트합니다. 또한 Blazor 메커니즘을 사용하면 이벤트 처리기가 세션 간에 공유되는 고정적인 데이터에 액세스할 수 있지만 JavaScript 모델은 그렇지 않습니다. 그러나 @onmousemove와 같이 자주 발생하는 일부 이벤트를 처리하면 서버로의 네트워크 왕복이 필요하기 때문에 사용자 인터페이스가 느려질 수 있습니다. JavaScript를 사용하여 브라우저에서 이와 같은 이벤트를 처리하는 것이 좋습니다.

Important

이벤트 처리기에서 JavaScript 코드를 사용하여 DOM을 조작할 수 있을 뿐 아니라 C# Blazor 코드를 사용하여 DOM을 조작할 수도 있습니다. 하지만 Blazor는 필요한 경우 사용자 인터페이스를 새로 고치는 데 사용되는 DOM의 자체 복사본을 유지 관리합니다. JavaScript 및 Blazor 코드를 사용하여 DOM에서 동일한 요소를 변경하는 경우 DOM이 손상될 위험이 있으며 웹앱에서 데이터의 프라이버시 및 보안이 손상될 수도 있습니다.

비동기식으로 이벤트 처리

기본적으로 Blazor 이벤트 처리기는 동기식입니다. 이벤트 처리기가 잠재적으로 장기 실행 작업(예: 웹 서비스 호출)을 수행하는 경우 작업이 완료될 때까지 이벤트 처리기를 실행하는 스레드가 차단됩니다. 이로 인해 사용자 인터페이스에서 응답이 잘못될 수 있습니다. 이를 위해 이벤트 처리기 메서드를 비동기식으로 지정할 수 있습니다. C# async 키워드를 사용합니다. 메서드는 Task 개체를 반환해야 합니다. 그런 다음 이벤트 처리기 메서드 내에서 await 연산자를 사용하여 별도의 스레드에서 장기 실행 작업을 시작하고 다른 작업을 위해 현재 스레드를 해제할 수 있습니다. 장기 실행 작업이 완료되면 이벤트 처리기가 다시 시작됩니다. 아래 예제 이벤트 처리기는 시간이 많이 걸리는 메서드를 비동기식으로 실행합니다.

<button @onclick="DoWork">Run time-consuming operation</button>

@code {
    private async Task DoWork()
    {
        // Call a method that takes a long time to run and free the current thread
        var data = await timeConsumingOperation();

        // Omitted for brevity
    }
}

참고 항목

C#에서 비동기 메서드를 만드는 방법에 대한 자세한 내용은 비동기 프로그래밍 시나리오를 참조하세요.

이벤트를 사용하여 DOM 요소에 포커스 설정

HTML 페이지에서 사용자는 요소 사이를 탭할 수 있으며, HTML 요소가 페이지에 나타나는 순서대로 포커스가 자연스럽게 이동합니다. 경우에 따라 이 시퀀스를 재정의하고 사용자가 특정 요소를 방문하도록 강제해야 할 수 있습니다.

이 작업을 수행하는 가장 간단한 방법은 FocusAsync 메서드를 사용하는 것입니다. 이 메서드는 ElementReference 개체의 인스턴스 메서드입니다. ElementReference는 포커스를 설정할 항목을 참조해야 합니다. @ref 특성을 사용하여 요소 참조를 지정하고 코드에서 이름이 같은 C# 개체를 만듭니다.

다음 예제에서는 <단추> 요소에 대한 @onclick 이벤트 처리기가 <입력> 요소에 포커스를 설정합니다. <입력> 요소의 @onfocus 이벤트 처리기는 해당 요소가 포커스를 받을 때 “포커스를 받았습니다” 라는 메시지를 표시합니다. <입력> 요소는 코드의 InputField 변수를 통해 참조됩니다.

<button class="btn btn-primary" @onclick="ChangeFocus">Click me to change focus</button>
<input @ref=InputField @onfocus="HandleFocus" value="@data"/>

@code {
    private ElementReference InputField;
    private string data;

    private async Task ChangeFocus()
    {
        await InputField.FocusAsync();
    }

    private async Task HandleFocus()
    {
        data = "Received focus";
    }

다음 이미지는 사용자가 단추를 선택할 때의 결과를 보여줍니다.

사용자가 입력 요소에 포커스를 설정하기 위해 단추를 클릭한 후의 웹 페이지의 스크린샷.

참고 항목

앱은 오류 발생 후 사용자에게 입력을 수정하도록 요청하는 등 특정 이유로 특정 컨트롤에만 포커스를 보내야 합니다. 포커스를 사용하여 사용자가 고정된 순서로 페이지의 요소를 탐색하도록 하지마세요. 이렇게 하면 요소를 다시 검토하여 입력을 변경하려는 사용자에게 매우 번거로운 일이 될 수 있습니다.

인라인 이벤트 처리기 작성

C#은 람다 식을 지원합니다. 람다 식을 사용하면 익명 함수를 만들 수 있습니다. 람다 식은 페이지 또는 구성 요소의 다른 곳에서 다시 사용할 필요가 없는 간단한 이벤트 처리기가 있는 경우에 유용합니다. 이 단원의 시작 부분에 표시된 초기 클릭 횟수 예제에서 IncrementCount 메서드를 제거하고 메서드 호출을 동일한 작업을 수행하는 람다 식으로 바꿀 수 있습니다.

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="() => currentCount++">Click me</button>

@code {
    private int currentCount = 0;
}

참고 항목

람다 식의 작동 방식에 대한 자세한 내용은 람다 식 및 익명 함수에서 확인하세요.

이 방법은 이벤트 처리 메서드에 대한 다른 인수를 제공하려는 경우에도 유용합니다. 다음 예제에서 메서드 HandleClick은 일반 클릭 이벤트 처리기와 동일한 방식으로 MouseEventArgs 매개 변수를 사용하지만 문자열 매개 변수도 허용합니다. 이 메서드는 이전처럼 클릭 이벤트를 처리하지만 사용자가 Ctrl 키를 눌렀는지 여부에 대한 메시지도 표시합니다. 람다 식은 HandleCLick 메서드를 호출하여 MouseEventArgs 매개 변수(mouseEvent) 및 문자열을 전달합니다.

@page "/counter"
@inject IJSRuntime JS

<h1>Counter</h1>

<p id="currentCount">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick='mouseEvent => HandleClick(mouseEvent, "Hello")'>Click me</button>

@code {
    private int currentCount = 0;

    private async Task HandleClick(MouseEventArgs e, string msg)
    {
        if (e.CtrlKey) // Ctrl key pressed as well
        {
            await JS.InvokeVoidAsync("alert", msg);
            currentCount += 5;
        }
        else
        {
            currentCount++;
        }
    }
}

참고 항목

이 예제에서는 Blazor에서 해당하는 함수가 없으므로 JavaScript alert 함수를 사용하여 메시지를 표시합니다. JavaScript interop를 사용하여 Blazor 코드에서 JavaScript를 호출합니다. 이 기술의 세부 정보는 별도 모듈의 주제입니다.

이벤트에 대한 기본 DOM 작업 재정의

여러 DOM 이벤트에는 해당 이벤트에 사용할 수 있는 이벤트 처리기가 있는지 여부에 관계없이 이벤트가 발생할 때 실행되는 기본 작업이 있습니다. 예를 들어 <입력> 요소의 @onkeypress 이벤트는 항상 사용자가 누른 키에 해당하는 문자를 표시하고 키 누름을 처리합니다. 다음 예제에서 @onkeypress 이벤트는 사용자의 입력을 대문자로 변환하는 데 사용됩니다. 또한 사용자가 @ 문자를 입력하면 이벤트 처리기가 경고를 표시합니다.

<input value=@data @onkeypress="ProcessKeyPress"/>

@code {
    private string data;

    private async Task ProcessKeyPress(KeyboardEventArgs e)
    {
        if (e.Key == "@")
        {
            await JS.InvokeVoidAsync("alert", "You pressed @");
        }
        else
        {
            data += e.Key.ToUpper();
        }
    }
}

이 코드를 실행하고 @ 키를 누르면 경고가 표시되지만 @ 문자도 입력에 추가됩니다. @ 문자 추가는 이 이벤트의 기본 동작입니다.

@ 문자를 보여 주는 사용자 입력의 스크린샷.

이 문자를 입력 상자에 표시하지 않으려는 경우 다음과 같이 이벤트의 preventDefault 특성으로 기본 작업을 재정의할 수 있습니다.

<input value=@data @onkeypress="ProcessKeyPress" @onkeypress:preventDefault />

이벤트는 계속 발생하지만 이벤트 처리기에서 정의한 작업만 수행됩니다.

DOM에서 자식 요소의 일부 이벤트는 부모 요소의 이벤트를 트리거할 수 있습니다. 다음 예제의 <div> 요소에는 @onclick 이벤트 처리기가 포함됩니다. <div> 내의 <단추>에는 자체 @onclick 이벤트 처리기가 있습니다. 또한 div<div>에는 <입력> 요소가 포함됩니다.

<div @onclick="HandleDivClick">
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
    <input value=@data @onkeypress="ProcessKeyPress" @onkeypress:preventDefault />
</div>

@code {
    private async Task HandleDivClick()
    {
        await JS.InvokeVoidAsync("alert", "Div click");
    }

    private async Task ProcessKeyPress(KeyboardEventArgs e)
    {
        // Omitted for brevity
    }

    private int currentCount = 0;

    private void IncrementCount(MouseEventArgs e)
    {
        // Omitted for brevity
    }
}

앱이 실행될 때 사용자가 <div> 요소가 차지하는 영역에서 한 요소(또는 빈 공간)를 클릭하면 HandleDivClick 메서드가 실행되고 메시지가 표시됩니다. 사용자가 Click me 단추를 선택하면 IncrementCount 메서드가 실행된 다음 HandleDivClick이 실행됩니다. @onclick 이벤트는 DOM 트리를 따라 전파됩니다. <div>가 @onclick 이벤트를 처리한 다른 요소의 일부인 경우 해당 이벤트 처리기도 실행되며 이는 DOM 트리의 루트까지 반복됩니다. 다음과 같이 이벤트의 stopPropagation 특성으로 이벤트의 이러한 상향 확산을 줄일 수 있습니다.

<div @onclick="HandleDivClick">
    <button class="btn btn-primary" @onclick="IncrementCount" @onclick:stopPropagation>Click me</button>
    <!-- Omitted for brevity -->
</div>

EventCallback을 사용하여 구성 요소 간에 이벤트 처리

Blazor 페이지에는 하나 이상의 Blazor 구성 요소가 포함될 수 있으며 구성 요소는 부모-자식 관계에 중첩될 수 있습니다. 자식 구성 요소의 이벤트는 EventCallback을 사용하여 부모 구성 요소에서 이벤트 처리기 메서드를 트리거할 수 있습니다. 콜백은 부모 구성 요소의 메서드를 참조합니다. 자식 구성 요소는 콜백을 호출하여 메서드를 실행할 수 있습니다. 이 메커니즘은 C# 애플리케이션에서 delegate를 사용하여 메서드를 참조하는 것과 비슷합니다.

콜백은 단일 매개 변수를 사용할 수 있습니다. EventCallback가 제네릭 형식인 경우 type 매개 변수는 콜백에 전달되는 인수의 형식을 지정합니다.

한 예로 다음 시나리오를 생각해 보겠습니다. 사용자가 입력 문자열을 입력하고 몇 가지 방식으로 해당 문자열을 변환할 수 있는 TextDisplay라는 구성 요소를 만들려고 합니다. 문자열을 대문자, 소문자, 대/소문자 혼합으로 변한하거나 문자열에서 문자를 필터링하거나 다른 유형의 변환을 수행해야 합니다. 그러나 TextDisplay 구성 요소에 대한 코드를 작성할 때 변환 프로세스가 무엇인지 알 수 없으며 대신 이 작업을 다른 구성 요소로 연기하려고 합니다. 다음 코드는 TextDisplay 구성 요소를 표시합니다. 사용자가 텍스트 값을 입력할 수 있는 <입력> 요소의 양식에 입력 문자열을 제공합니다.

@* TextDisplay component *@
@using WebApplication.Data;

<p>Enter text:</p>
<input @onkeypress="HandleKeyPress" value="@data" />

@code {
    [Parameter]
    public EventCallback<KeyTransformation> OnKeyPressCallback { get; set; }

    private string data;

    private async Task HandleKeyPress(KeyboardEventArgs e)
    {
        KeyTransformation t = new KeyTransformation() { Key = e.Key };
        await OnKeyPressCallback.InvokeAsync(t);
        data += t.TransformedKey;
    }
}

TextDisplay 구성 요소는 OnKeyPressCallback이라는 EventCallback 개체를 사용합니다. 메서드의 코드는 HandleKeypress 콜백을 호출합니다. 키를 누를 때마다 @onkeypress 이벤트 처리기가 실행되고 HandleKeypress 메서드를 호출합니다. HandleKeypress 메서드는 사용자가 누른 키를 사용하여 KeyTransformation 개체를 만들고 이 개체를 콜백에 매개 변수로 전달합니다. KeyTransformation 형식은 두 개의 필드가 있는 간단한 클래스입니다.

namespace WebApplication.Data
{
    public class KeyTransformation
    {
        public string Key { get; set; }
        public string TransformedKey { get; set; }
    }
}

key 필드에는 사용자가 입력한 값이 포함되며, TransformedKey 필드에는 키가 처리될 때 변환된 키의 값이 포함됩니다.

이 예제에서 EventCallback 개체는 구성 요소 매개 변수이며 구성 요소를 만들 때 해당 값이 제공됩니다. 이 작업은 TextTransformer라는 다른 구성 요소에 의해 수행됩니다.

@page "/texttransformer"
@using WebApplication.Data;

<h1>Text Transformer - Parent</h1>

<TextDisplay OnKeypressCallback="@TransformText" />

@code {
    private void TransformText(KeyTransformation k)
    {
        k.TransformedKey = k.Key.ToUpper();
    }
}

TextTransformer 구성 요소는 TextDisplay 구성 요소의 인스턴스를 만드는 Blazor 페이지입니다. OnKeypressCallback 매개 변수를 페이지의 코드 섹션에 있는 TransformText 메서드에 대한 참조로 채울 수 있습니다. TransformText 메서드는 제공된 KeyTransformation 개체를 인수로 사용하고, TransformedKey 속성을 대문자로 변환된 Key 속성의 값으로 채웁니다. 다음 다이어그램에서는 사용자가 TextTransformer 페이지에 표시되는 TextDisplay 구성 요소의 <입력> 필드에 값을 입력할 때의 컨트롤 흐름을 보여줍니다.

자식 구성 요소에 EventCallback이 있는 제어 흐름의 다이어그램.

이 방법의 장점은 OnKeypressCallback 매개 변수에 대한 콜백을 제공하는 모든 페이지에서 TextDisplay 구성 요소를 사용할 수 있다는 것입니다. 디스플레이와 처리 작업은 완전히 분리되어 있습니다. TextDisplay 구성 요소에서 EventCallback 매개 변수의 서명과 일치하는 다른 콜백에 대해 TransformText 메서드를 전환할 수 있습니다.

콜백이 적절한 EventArgs 매개 변수를 사용하여 형식이 지정된 경우 중간 메서드를 사용하지 않고 콜백을 이벤트 처리기에 직접 연결할 수 있습니다. 예를 들어 자식 구성 요소는 다음과 같은 @onclick 마우스 이벤트를 처리할 수 있는 콜백을 참조할 수 있습니다.

<button @onclick="OnClickCallback">
    Click me!
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}

이 경우 EventCallbackMouseEventArgs 형식 매개 변수를 사용합니다. 그러므로 @onclick 이벤트에 대한 처리기로 지정할 수 있습니다.

지식 확인

1.

Blazor 구성 요소 간에 이벤트를 전달하는 데 사용해야 하는 기능은 무엇인가요?

2.

HTML DOM 요소에서 기본 동작을 재정의하는 데 사용되는 메서드