Blazor 이벤트 처리 완벽 가이드 — @onclick부터 입력 이벤트까지

이벤트 처리
📚 이 글에서 다루는 것

이벤트 처리

버튼 클릭, 입력 변경 등 다양한 DOM 이벤트를 Blazor에서 처리하는 방법을 익힙니다.

📌 학습 목표

  • Blazor에서 DOM 이벤트를 C# 메서드에 연결하는 기본 원리를 이해합니다.
  • @onclick, @oninput, @onchange 등 주요 이벤트 디렉티브를 사용할 수 있습니다.
  • 이벤트 인수(EventArgs)를 받아 이벤트 상세 정보를 활용할 수 있습니다.
  • 람다 표현식으로 인라인 이벤트 핸들러를 작성할 수 있습니다.
  • 이벤트 버블링과 기본 동작 방지 패턴을 적용할 수 있습니다.

📝 개념 설명

1. Blazor 이벤트 처리의 기본 원리

기존 JavaScript 개발에서는 addEventListeneronclick 속성으로 DOM 이벤트를 처리했습니다. Blazor에서는 이 과정을 C# 메서드와 직접 연결할 수 있습니다. HTML 태그의 이벤트 속성 앞에 @ 기호를 붙이면 Blazor가 해당 이벤트를 가로채어 지정한 C# 핸들러 메서드를 호출합니다.

 

이벤트가 발생하면 Blazor는 자동으로 컴포넌트 상태를 갱신하고 렌더링을 재실행하므로, 개별 DOM 조작 코드 없이도 UI가 최신 상태를 반영합니다.

🔄 Blazor 이벤트 처리 흐름
사용자 액션
(클릭·입력 등)
Blazor 런타임
이벤트 캐치
C# 핸들러
메서드 호출
상태 변경 후
UI 재렌더링

2. @onclick — 클릭 이벤트

@onclick은 가장 자주 사용하는 이벤트 디렉티브입니다. 버튼이나 다른 요소를 클릭했을 때 지정한 C# 메서드를 실행합니다.

기본 형식:
<button @onclick="HandleClick">클릭하세요</button>

@code {
    private void HandleClick()
    {
        // 클릭 시 실행할 코드
    }
}

메서드 이름만 전달할 때는 괄호를 붙이지 않는다는 점에 주의하세요. @onclick="HandleClick()"이 아니라 @onclick="HandleClick"입니다.

3. MouseEventArgs — 클릭 상세 정보 받기

클릭한 위치, 어떤 마우스 버튼인지 등 이벤트 상세 정보가 필요하면 MouseEventArgs 매개변수를 핸들러에 추가합니다.

<button @onclick="HandleClickWithArgs">클릭 정보 확인</button>

@code {
    private void HandleClickWithArgs(MouseEventArgs e)
    {
        Console.WriteLine($"클릭 위치: X={e.ClientX}, Y={e.ClientY}");
        Console.WriteLine($"버튼 종류: {e.Button}"); // 0=왼쪽, 1=중간, 2=오른쪽
    }
}

4. @oninput / @onchange — 입력 이벤트

텍스트 필드의 값이 변경될 때는 @oninput 또는 @onchange를 사용합니다.

  • @oninput: 사용자가 키를 누를 때마다 실시간으로 발생합니다.
  • @onchange: 입력 필드에서 포커스가 벗어날 때 발생합니다.
<input type="text" @oninput="OnInputChanged" />
<p>입력값: @inputValue</p>

@code {
    private string inputValue = string.Empty;

    private void OnInputChanged(ChangeEventArgs e)
    {
        inputValue = e.Value?.ToString() ?? string.Empty;
    }
}

ChangeEventArgsValue 속성으로 현재 입력값을 가져옵니다.

5. 람다 표현식으로 인라인 핸들러 작성

간단한 로직이라면 별도 메서드를 만들지 않고 람다식으로 인라인 핸들러를 정의할 수 있습니다.

<button @onclick="() => count++">카운트 증가</button>
<p>현재 카운트: @count</p>

@code {
    private int count = 0;
}

이벤트 인수도 함께 받으려면 다음처럼 작성합니다.

<button @onclick="(e) => HandleWithArgs(e, "추가 데이터")">버튼</button>
⚠️ 람다 사용 시 주의: 루프 안에서 람다를 사용할 때는 변수 캡처 문제가 발생할 수 있습니다. 루프 변수를 직접 캡처하지 말고 로컬 복사본을 만들어 사용하세요.

6. 비동기 이벤트 핸들러

이벤트 핸들러에서 API 호출이나 비동기 작업을 수행할 때는 async Task를 반환 타입으로 지정합니다. void 대신 Task를 반환해야 예외가 올바르게 처리됩니다.

<button @onclick="LoadDataAsync">데이터 불러오기</button>

@code {
    private async Task LoadDataAsync()
    {
        await Task.Delay(1000); // 실제 코드에서는 API 호출
        Console.WriteLine("데이터 로딩 완료");
    }
}

7. 기타 주요 이벤트 디렉티브

Blazor는 표준 DOM 이벤트 대부분을 지원합니다.

📌 주요 이벤트 디렉티브 한눈에 보기
🖱️
마우스 이벤트
@onclick, @ondblclick, @onmouseover, @onmouseout, @onmousedown, @onmouseup
⌨️
키보드 이벤트
@onkeydown, @onkeyup, @onkeypress (KeyboardEventArgs로 키 정보 접근)
📝
입력 이벤트
@oninput (실시간), @onchange (포커스 아웃), @onfocus, @onblur
📋
폼 이벤트
@onsubmit (폼 제출), @onreset (폼 초기화)

8. 이벤트 버블링 방지와 기본 동작 방지

자식 요소의 이벤트가 부모 요소로 전파(버블링)되는 것을 막으려면 @onclick:stopPropagation을 사용합니다. 링크(<a>) 클릭 시 페이지 이동 같은 기본 동작을 막으려면 @onclick:preventDefault를 사용합니다.

<!-- 부모 클릭 이벤트로 전파 방지 -->
<div @onclick="ParentClick">
    <button @onclick="ChildClick" @onclick:stopPropagation="true">
        자식 버튼
    </button>
</div>

<!-- 링크의 기본 이동 동작 방지 -->
<a href="https://example.com" @onclick="HandleLink" @onclick:preventDefault="true">
    링크처럼 보이는 버튼
</a>

💡 예제 & 실습

실전 예제: 간단한 카운터 + 색상 변경 컴포넌트

지금까지 배운 여러 이벤트를 한 컴포넌트에서 사용하는 종합 예제입니다.

@page "/event-demo"

<h3>이벤트 처리 데모</h3>

<!-- 버튼 클릭 이벤트 -->
<button @onclick="Increment">+1</button>
<button @onclick="Decrement">-1</button>
<button @onclick="Reset">초기화</button>
<p>현재 카운트: <strong>@count</strong></p>

<hr />

<!-- 실시간 입력 이벤트 -->
<input type="text" 
       placeholder="이름을 입력하세요"
       @oninput="OnNameChanged" />
<p>안녕하세요, <strong>@userName</strong>님!</p>

<hr />

<!-- 키보드 이벤트 -->
<input type="text"
       placeholder="Enter 키를 눌러보세요"
       @onkeydown="OnKeyDown" />
<p>마지막 키: @lastKey</p>

@code {
    private int count = 0;
    private string userName = "손님";
    private string lastKey = "없음";

    private void Increment() => count++;
    private void Decrement() => count--;
    private void Reset() => count = 0;

    private void OnNameChanged(ChangeEventArgs e)
    {
        var value = e.Value?.ToString();
        userName = string.IsNullOrEmpty(value) ? "손님" : value;
    }

    private void OnKeyDown(KeyboardEventArgs e)
    {
        lastKey = e.Key;
        if (e.Key == "Enter")
        {
            Console.WriteLine("Enter 키 감지!");
        }
    }
}

단계별 해설

  1. Increment / Decrement / Reset: 람다 대신 짧은 메서드로 분리하여 가독성을 높였습니다. => 표현식 바디로 한 줄로 작성합니다.
  2. OnNameChanged: ChangeEventArgs.Valueobject? 타입이므로 ToString()과 null 처리가 필요합니다.
  3. OnKeyDown: KeyboardEventArgs.Key로 누른 키의 이름을 문자열로 받을 수 있습니다. "Enter", "Escape", "ArrowUp" 등 표준 키 이름을 사용합니다.

실전 예제 2: 마우스 위치 추적

<div @onmousemove="TrackMouse" 
     style="width:300px; height:200px; border:1px solid #ccc; background:#f5f5f5;">
    마우스를 이 영역 위에서 움직여보세요
</div>
<p>마우스 위치: X=@mouseX, Y=@mouseY</p>

@code {
    private double mouseX = 0;
    private double mouseY = 0;

    private void TrackMouse(MouseEventArgs e)
    {
        mouseX = Math.Round(e.OffsetX, 1);
        mouseY = Math.Round(e.OffsetY, 1);
    }
}

MouseEventArgsClientX/Y(뷰포트 기준), OffsetX/Y(요소 기준), ScreenX/Y(화면 기준) 등 다양한 좌표 속성을 제공합니다.


⚠️ 자주 틀리는 것 / 주의사항

실수 1: 메서드 호출 괄호를 붙이는 경우

@onclick="HandleClick()" — 컴파일 오류 또는 의도치 않은 동작
@onclick="HandleClick" — 메서드 참조(괄호 없음)

 

단, 람다식으로 인자를 전달할 때는 괄호가 필요합니다: @onclick="() => HandleClick(param)"

실수 2: async void 사용

private async void LoadAsync() — 예외가 조용히 삼켜짐
private async Task LoadAsync() — 예외가 올바르게 전파됨

 

Blazor 이벤트 핸들러는 비동기일 때 반드시 async Task를 반환해야 합니다.

실수 3: @oninput과 @onchange 혼동

@oninput은 키를 누를 때마다 발생하고, @onchange는 포커스가 벗어날 때 발생합니다. 실시간 검색 같은 기능은 @oninput, 폼 저장 전 유효성 검사는 @onchange가 적합합니다.

실수 4: 루프 안에서 람다 변수 캡처

@foreach (var item in items)
{
    <!-- ❌ 잘못된 예: item이 루프 마지막 값으로 고정됨 -->
    <button @onclick="() => SelectItem(item)">@item.Name</button>
}

Blazor의 @foreach는 JavaScript와 달리 각 반복마다 새로운 변수 범위를 생성하므로 위 코드는 실제로 올바르게 동작합니다. 단, for (int i = 0; ...) 루프에서는 int captured = i;처럼 로컬 복사본이 필요합니다.

실수 5: StateHasChanged() 불필요하게 호출

표준 이벤트 핸들러(@onclick 등)가 종료되면 Blazor가 자동으로 렌더링을 재실행합니다. 핸들러 안에서 StateHasChanged()를 명시적으로 호출할 필요가 없습니다. 타이머 콜백이나 외부 이벤트처럼 Blazor 이벤트 사이클 밖에서 상태를 변경할 때만 사용하세요.


🎯 마무리

Blazor의 이벤트 처리는 HTML의 이벤트 속성 앞에 @만 붙이면 C# 메서드와 직접 연결됩니다. 이 단순한 원칙 위에 EventArgs 매개변수, 람다 인라인 핸들러, 비동기 Task 핸들러, 이벤트 수정자(stopPropagation·preventDefault)를 조합하면 실무에서 필요한 대부분의 사용자 상호작용을 처리할 수 있습니다.

 

이벤트 처리에 익숙해지면 컴포넌트가 ‘살아있는 UI’처럼 느껴지기 시작합니다. 다양한 이벤트 디렉티브를 직접 코드에 적용하며 손에 익히는 것이 가장 빠른 학습법입니다.

✅ 핵심 정리
  • Blazor 이벤트 디렉티브는 HTML 이벤트 속성 앞에 @를 붙인 형태입니다 (@onclick, @oninput 등).
  • 메서드를 연결할 때는 괄호 없이 메서드 이름만 씁니다: @onclick="HandleClick"
  • 이벤트 상세 정보는 MouseEventArgs, KeyboardEventArgs, ChangeEventArgs 등으로 받습니다.
  • 간단한 로직은 람다식 인라인 핸들러로 처리할 수 있습니다: @onclick="() => count++"
  • 비동기 핸들러는 반드시 async Task를 반환해야 합니다 (async void 금지).
  • 이벤트 버블링 방지: @onclick:stopPropagation, 기본 동작 방지: @onclick:preventDefault
  • 표준 이벤트 핸들러 종료 후 Blazor는 자동으로 UI를 재렌더링합니다.

댓글 남기기

Wordpress Social Share Plugin powered by Ultimatelysocial
Copy link
URL has been copied successfully!
THREADS
RSS
error: 저작권 콘텐츠보호를 부탁드립니다.