Blazor 조건부 렌더링 완전 정복 — @if·@switch 동적 UI

조건부 렌더링
📚
학습 포인트

조건부 렌더링

@if, @switch 지시어를 활용해 조건에 따라 UI를 동적으로 표시합니다.

Blazor 조건부 렌더링 — @if · @switch 동적 UI
이 강의에서는 Blazor의 핵심 렌더링 제어 기법인 조건부 렌더링을 학습합니다. @if, @switch 지시어를 통해 사용자 상태·역할·데이터에 따라 화면을 유연하게 구성하는 방법을 익힙니다.

📌 학습 목표

  • Blazor Razor 구문에서 @if / @else if / @else 지시어로 조건별 UI를 분기할 수 있습니다.
  • @switch / @case 지시어를 활용해 다중 조건을 간결하게 처리할 수 있습니다.
  • 로그인 상태, 역할(Role), 데이터 존재 여부 등 실전 시나리오에 조건부 렌더링을 적용할 수 있습니다.
  • 조건부 렌더링과 CSS 클래스 동적 바인딩을 조합하여 UI를 정교하게 제어할 수 있습니다.
  • 불필요한 조건 중첩을 피하는 클린 코드 패턴을 작성할 수 있습니다.

📝 개념 설명

1. 조건부 렌더링이란?

조건부 렌더링(Conditional Rendering)은 특정 조건이 충족될 때만 HTML 요소를 화면에 표시하는 기법입니다. Blazor는 C# 코드와 HTML을 Razor 구문으로 혼합하기 때문에, C#의 if, switch 제어 흐름을 그대로 템플릿 안에 사용할 수 있습니다.

 

React의 {condition && <Component />}, Vue의 v-if와 유사한 개념이지만, Blazor에서는 순수 C# 문법을 @ 접두사만 붙여 사용합니다. 별도의 템플릿 문법을 새로 배울 필요가 없는 것이 Blazor의 큰 장점입니다.

2. @if 지시어 기본 문법

Blazor Razor 파일(.razor)에서 @if는 아래와 같이 사용합니다.

@* 단순 조건 *@
@if (조건식)
{
    <p>조건이 참일 때 표시됩니다.</p>
}

@* else 포함 *@
@if (isLoggedIn)
{
    <p>환영합니다, 사용자님!</p>
}
else
{
    <p>로그인이 필요합니다.</p>
}

@* else if 다중 분기 *@
@if (score >= 90)
{
    <span class='grade-a'>A 등급</span>
}
else if (score >= 80)
{
    <span class='grade-b'>B 등급</span>
}
else if (score >= 70)
{
    <span class='grade-c'>C 등급</span>
}
else
{
    <span class='grade-f'>재수강 필요</span>
}

@code {
    bool isLoggedIn = true;
    int score = 85;
}
💡 핵심: @if 블록 안에는 HTML 요소, 다른 컴포넌트, 심지어 또 다른 @if를 자유롭게 중첩할 수 있습니다. 중괄호 { } 안이 Razor 렌더링 영역이 됩니다.

3. @switch 지시어

같은 변수에 대한 여러 값 비교는 @if / @else if를 반복하는 것보다 @switch가 훨씬 간결합니다. C#의 switch 문과 동일한 구조입니다.

@switch (userRole)
{
    case "Admin":
        <div class='admin-panel'>
            <h4>관리자 대시보드</h4>
            <button>사용자 관리</button>
        </div>
        break;
    case "Editor":
        <div class='editor-panel'>
            <h4>편집자 메뉴</h4>
            <button>글 작성</button>
        </div>
        break;
    case "Viewer":
        <p>읽기 전용 모드입니다.</p>
        break;
    default:
        <p>알 수 없는 역할입니다.</p>
        break;
}

@code {
    string userRole = "Editor";
}

4. 조건부 렌더링과 CSS 클래스 동적 바인딩 조합

요소를 완전히 제거하는 것 외에도, CSS 클래스를 동적으로 변경하여 표시/숨김을 제어하는 패턴도 자주 사용됩니다.

@* 방법 1: @if로 요소 자체를 제거 (DOM에서 완전히 삭제) *@
@if (showAlert)
{
    <div class='alert alert-danger'>오류가 발생했습니다!</div>
}

@* 방법 2: CSS class 동적 바인딩 (DOM에 존재하되 숨김) *@
<div class='@(showAlert ? "visible" : "hidden")'>
    오류가 발생했습니다!
</div>

@* 방법 3: style 직접 제어 *@
<div style='display:@(showAlert ? "block" : "none")'>
    오류가 발생했습니다!
</div>

@code {
    bool showAlert = false;
}
⚖️ 방법 1 vs 2 선택 기준: 방법 1(@if)은 DOM에서 요소가 완전히 제거되므로 보안상 민감한 콘텐츠(관리자 버튼 등)에 적합합니다. 방법 2·3은 DOM에 요소가 남아있으므로 자주 토글되어 성능이 중요한 경우(애니메이션 등)에 적합합니다.
📌 조건부 렌더링 핵심 포인트
🔀
@if / @else
2~3개 조건 분기에 최적. C# if 문과 완전 동일 문법
🎛️
@switch / @case
동일 변수의 다중 값 비교. 역할(Role)·상태(Status) 분기에 적합
🗑️
DOM 제거
@if로 감싸면 조건 불충족 시 HTML이 DOM에서 완전 삭제됨
👁️
CSS 토글
class/style 동적 바인딩으로 DOM 유지하며 표시/숨김 전환

💡 예제 & 실습

실습 1: 로그인 상태에 따른 네비게이션 분기

실무에서 가장 흔한 패턴입니다. 로그인 여부에 따라 완전히 다른 메뉴를 표시합니다.

@* NavMenu.razor (발췌) *@
<nav class='navbar'>
    <a href='/'>홈</a>

    @if (isAuthenticated)
    {
        <a href='/dashboard'>대시보드</a>
        <a href='/profile'>내 프로필</a>
        <button @onclick='Logout'>로그아웃</button>
    }
    else
    {
        <a href='/login'>로그인</a>
        <a href='/register'>회원가입</a>
    }
</nav>

@code {
    private bool isAuthenticated = false;

    private void Logout()
    {
        isAuthenticated = false;
    }
}

해설: isAuthenticated 값이 false이면 로그인/회원가입 링크만, true이면 대시보드/프로필/로그아웃만 렌더링됩니다. 버튼 클릭 시 isAuthenticated가 변경되면 Blazor가 자동으로 UI를 다시 렌더링합니다.

실습 2: 데이터 로딩 상태 3단계 분기

API 호출 중 로딩 스피너 → 에러 메시지 → 실제 데이터 순서로 표시하는 전형적인 패턴입니다.

@* DataPage.razor *@
@page "/data"

@if (isLoading)
{
    <div class='loading-spinner'>
        <p>⏳ 데이터를 불러오는 중...</p>
    </div>
}
else if (hasError)
{
    <div class='error-box'>
        <h4>❌ 오류 발생</h4>
        <p>@errorMessage</p>
        <button @onclick='RetryLoad'>다시 시도</button>
    </div>
}
else if (items == null || items.Count == 0)
{
    <p>📭 표시할 데이터가 없습니다.</p>
}
else
{
    <ul>
        @foreach (var item in items)
        {
            <li>@item.Name</li>
        }
    </ul>
}

@code {
    private bool isLoading = true;
    private bool hasError = false;
    private string errorMessage = string.Empty;
    private List<Item> items = new();

    protected override async Task OnInitializedAsync()
    {
        try
        {
            items = await DataService.GetItemsAsync();
        }
        catch (Exception ex)
        {
            hasError = true;
            errorMessage = ex.Message;
        }
        finally
        {
            isLoading = false;
        }
    }

    private async Task RetryLoad()
    {
        isLoading = true;
        hasError = false;
        await OnInitializedAsync();
    }
}

해설: 로딩·에러·빈 목록·정상 데이터를 각각 분기하면 사용자 경험이 크게 향상됩니다. finally 블록에서 isLoading = false를 설정하면 에러 발생 시에도 스피너가 반드시 해제됩니다.

실습 3: @switch로 주문 상태 배지 표시

@* OrderStatus.razor *@
<div class='order-card'>
    <h4>주문 #@OrderId</h4>

    @switch (Status)
    {
        case OrderStatus.Pending:
            <span class='badge badge-yellow'>⏳ 결제 대기</span>
            break;
        case OrderStatus.Processing:
            <span class='badge badge-blue'>🔄 처리 중</span>
            break;
        case OrderStatus.Shipped:
            <span class='badge badge-purple'>🚚 배송 중</span>
            break;
        case OrderStatus.Delivered:
            <span class='badge badge-green'>✅ 배송 완료</span>
            break;
        case OrderStatus.Cancelled:
            <span class='badge badge-red'>❌ 취소됨</span>
            break;
        default:
            <span class='badge badge-gray'>알 수 없음</span>
            break;
    }
</div>

@code {
    [Parameter] public int OrderId { get; set; }
    [Parameter] public OrderStatus Status { get; set; }
}

public enum OrderStatus
{
    Pending, Processing, Shipped, Delivered, Cancelled
}

해설: enum@switch의 조합은 상태 기반 UI의 정석 패턴입니다. string 비교보다 컴파일 타임에 오류를 잡을 수 있어 안전합니다.

⚖️ @if vs @switch — 언제 무엇을 쓸까?
항목@if / @else if@switch / @case
적합한 경우범위 비교 (score >= 90), 복잡한 불린 조건단일 변수의 특정 값 비교 (enum, string 상수)
가독성조건 2~3개까지는 간결함조건 4개 이상이면 훨씬 깔끔
타입 지원모든 조건식int, string, enum, char 등 상수 패턴
fall-through없음break 필수 (C#은 암묵적 fall-through 불가)
default 처리else 블록default: 블록

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

1. @if 블록에서 세미콜론 넣기

@* ❌ 잘못된 예 — Razor 지시어에 세미콜론 없음 *@
@if (isLoggedIn);
{
    <p>환영합니다.</p>
}

@* ✅ 올바른 예 *@
@if (isLoggedIn)
{
    <p>환영합니다.</p>
}

@if 뒤에 세미콜론을 붙이면 조건 블록이 빈 문으로 처리되어 항상 렌더링되거나 컴파일 오류가 납니다.

2. @switch의 break 누락

@* ❌ break 누락 — 컴파일 오류 *@
@switch (role)
{
    case "Admin":
        <p>관리자</p>
        @* break 없음 → CS0163 오류 *@
    case "User":
        <p>일반 사용자</p>
        break;
}

C#의 switch는 암묵적 fall-through를 허용하지 않으므로, 각 case 마지막에 반드시 break;를 작성해야 합니다.

3. null 체크 없이 객체 멤버 접근

@* ❌ user가 null이면 NullReferenceException *@
@if (user.IsAdmin)
{
    <button>관리</button>
}

@* ✅ null 조건 연산자 활용 *@
@if (user?.IsAdmin == true)
{
    <button>관리</button>
}

비동기 데이터 로딩 중에는 객체가 null일 수 있습니다. ?.(null 조건 연산자)를 사용하거나, 먼저 @if (user != null)으로 보호 조건을 설정하세요.

4. 조건부 렌더링과 컴포넌트 생명주기

@* 중요: @if로 컴포넌트를 제거하면 Dispose()가 호출됨 *@
@if (showComponent)
{
    <MyHeavyComponent />
}
@* showComponent가 false가 되면 MyHeavyComponent는 완전히 소멸 *@
@* 다시 true가 되면 새 인스턴스가 생성되어 OnInitializedAsync()가 재실행됨 *@
⚠️ 주의: @if로 컴포넌트를 숨기면 해당 컴포넌트의 상태(State)가 초기화됩니다. 상태를 유지하면서 숨기고 싶다면 CSS display:none 방식을 사용하세요.

5. 중첩 @if의 가독성 저하

@* ❌ 과도한 중첩 — 읽기 어려움 *@
@if (isLoggedIn)
{
    @if (isAdmin)
    {
        @if (hasPermission)
        {
            <button>삭제</button>
        }
    }
}

@* ✅ 조건 합치기 *@
@if (isLoggedIn && isAdmin && hasPermission)
{
    <button>삭제</button>
}

🎯 마무리

Blazor의 조건부 렌더링은 C# 개발자에게 매우 친숙한 방식으로 동작합니다. @if@switch는 문법적으로 C#과 완전히 동일하며, HTML 블록을 C# 제어 흐름 안에 자연스럽게 포함시킬 수 있습니다.

 

실무에서는 로딩 상태 3단계 분기(로딩 중 → 에러 → 데이터), 인증·역할 기반 UI 분기, enum 기반 상태 배지 패턴이 가장 많이 사용됩니다. 이 세 가지 패턴을 직접 작성해보면 조건부 렌더링의 대부분 시나리오를 커버할 수 있습니다.

✅ 핵심 정리
  • @if / @else if / @else: 범위 비교나 복합 조건에 사용. HTML 블록을 C# 조건식으로 감싸는 방식
  • @switch / @case: 동일 변수의 다중 값 비교에 사용. enum·상수 비교에 특히 적합하며, 각 case에 break; 필수
  • @if로 컴포넌트를 제거하면 DOM에서 완전히 삭제되고 상태가 초기화됨. 상태 유지가 필요하면 CSS 토글 사용
  • null 조건 연산자(?.)나 null 보호 @if로 비동기 데이터 로딩 중 NullReferenceException 방지
  • 조건이 3개 이상 중첩되면 &&로 합치거나 @switch로 전환하여 가독성 확보
  • 보안 민감 콘텐츠는 @if로 DOM에서 완전 제거. 자주 토글되는 요소는 CSS 방식으로 성능 최적화

댓글 남기기

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