Blazor 반복 렌더링과 리스트 — @foreach · @for 완전 가이드

반복 렌더링과 리스트
📚
한눈에 보기

반복 렌더링과 리스트

@foreach, @for 지시어로 컬렉션 데이터를 반복해 리스트 UI를 구성합니다.

📌 학습 목표

이 글을 완독하면 다음을 할 수 있습니다:

  1. @foreach 지시어를 사용해 컬렉션 데이터를 반복 렌더링할 수 있습니다.
  2. @for 지시어로 인덱스 기반 반복 UI를 구성할 수 있습니다.
  3. List<T>, 배열 등 다양한 컬렉션 타입을 Blazor UI에 출력할 수 있습니다.
  4. @key 속성으로 리스트 렌더링 성능을 최적화할 수 있습니다.
  5. 빈 컬렉션을 안전하게 처리하고 조건부 메시지를 표시할 수 있습니다.

📝 개념 설명

1. Blazor의 반복 렌더링이란?

Blazor 컴포넌트에서는 C#의 반복문을 Razor 지시어 형태로 HTML 템플릿 안에 직접 작성할 수 있습니다. @foreach@for는 컬렉션을 순회하며 각 항목을 HTML 요소로 렌더링하는 핵심 메커니즘입니다. 일반 C# 코드와 달리, Razor 템플릿 안에서 반복문을 사용할 때는 @ 접두사를 붙여 Blazor 엔진이 C# 구문임을 인식하도록 해야 합니다.

2. @foreach 지시어

@foreach는 컬렉션(List, 배열, IEnumerable 등)의 각 요소를 순서대로 꺼내어 HTML 블록을 반복 생성합니다. 인덱스가 필요 없고 전체 항목을 순회할 때 가장 간결한 방법입니다.

@foreach (var 변수명 in 컬렉션)
{
    <HTML요소>@변수명</HTML요소>
}

3. @for 지시어

@for는 인덱스를 직접 제어해야 할 때 사용합니다. 순번 출력, 홀수 번째 항목 강조 등 인덱스 값이 필요한 경우에 적합합니다.

@for (int i = 0; i < 컬렉션.Count; i++)
{
    <HTML요소>@컬렉션[i]</HTML요소>
}

4. @key 속성 — 렌더링 성능 최적화

Blazor의 가상 DOM 비교(Diffing) 알고리즘은 리스트 항목이 추가·삭제·이동될 때 어떤 항목이 변경되었는지 추적합니다. @key 속성에 고유 식별자(ID 등)를 지정하면 변경 전후 항목을 정확히 매핑하여 불필요한 DOM 재생성을 방지합니다.

@foreach (var item in items)
{
    <div @key='item.Id'>@item.Name</div>
}
💡 @key 사용 권장 상황
  • 항목을 동적으로 추가·삭제·재정렬하는 리스트
  • 각 항목이 고유한 ID를 가진 모델 객체
  • 리스트 항목 내에 <input> 등 입력 필드가 포함된 경우

5. 빈 컬렉션 처리

컬렉션이 비어 있을 때 아무 내용도 렌더링되지 않으면 사용자 경험이 저하됩니다. @if@foreach를 조합하여 빈 상태 메시지를 표시하는 것이 좋은 관행입니다.

@if (items.Count == 0)
{
    <p>표시할 항목이 없습니다.</p>
}
else
{
    @foreach (var item in items)
    {
        <li>@item</li>
    }
}

 

 

⚖️ @foreach vs @for 비교

 

항목@foreach@for
사용 목적전체 컬렉션 순회인덱스 제어 필요 시
코드 가독성높음 (간결)중간 (조건 표현 가능)
인덱스 접근별도 변수 필요기본 제공 (i 변수)
주요 용도일반 리스트 출력순번·홀짝·범위 처리
지원 타입IEnumerable 전체인덱서 지원 컬렉션

💡 예제 & 실습

예제 1. @foreach로 문자열 리스트 출력

가장 기본적인 형태로, List<string><ul> 목록으로 렌더링합니다.

@page "/fruits"

<h3>과일 목록</h3>
<ul>
    @foreach (var fruit in fruits)
    {
        <li>@fruit</li>
    }
</ul>

@code {
    private List<string> fruits = new()
    {
        "사과", "바나나", "오렌지", "포도"
    };
}

렌더링 결과: 사과 / 바나나 / 오렌지 / 포도 항목이 순서대로 <li>로 출력됩니다.

예제 2. @for로 순번과 함께 출력

인덱스 i를 활용해 순번을 함께 표시합니다.

@page "/ranking"

<h3>인기 프로그래밍 언어 순위</h3>
<ol>
    @for (int i = 0; i < languages.Count; i++)
    {
        <li><strong>@(i + 1)위</strong> — @languages[i]</li>
    }
</ol>

@code {
    private List<string> languages = new()
    {
        "C#", "Python", "JavaScript", "Java"
    };
}

@(i + 1)처럼 괄호로 감싸면 C# 표현식을 직접 계산해 출력할 수 있습니다. 0-based 인덱스를 1-based 순번으로 변환하는 일반적인 패턴입니다.

예제 3. 객체 리스트 + @key 성능 최적화

실무에서는 모델 클래스 컬렉션을 다루는 경우가 많습니다. @key와 빈 컬렉션 처리를 함께 적용합니다.

@page "/products"

<h3>상품 목록</h3>
@if (products.Count == 0)
{
    <p>등록된 상품이 없습니다.</p>
}
else
{
    <ul>
        @foreach (var p in products)
        {
            <li @key='p.Id'>
                <strong>@p.Name</strong> — ₩@p.Price.ToString("N0")
            </li>
        }
    </ul>
}

@code {
    private List<Product> products = new()
    {
        new Product { Id = 1, Name = "노트북", Price = 1200000 },
        new Product { Id = 2, Name = "마우스", Price = 35000 },
        new Product { Id = 3, Name = "키보드", Price = 89000 }
    };

    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public int Price { get; set; }
    }
}
  • <li @key='p.Id'> — 고유 ID로 Blazor Diffing 최적화
  • p.Price.ToString("N0") — 숫자를 천 단위 콤마 형식으로 출력
  • Name { get; set; } = "" — null 방지 기본값 지정

예제 4. 중첩 @foreach — 카테고리별 리스트

@foreach 안에 또 다른 @foreach를 중첩하여 계층형 데이터를 표현할 수 있습니다.

@page "/menu"

@foreach (var category in menu)
{
    <h4>@category.CategoryName</h4>
    <ul>
        @foreach (var item in category.Items)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    record MenuCategory(string CategoryName, List<string> Items);

    private List<MenuCategory> menu = new()
    {
        new("한식", new() { "비빔밥", "김치찌개", "불고기" }),
        new("양식", new() { "파스타", "스테이크", "피자" })
    };
}

 

 

📌 반복 렌더링 핵심 포인트

 

🔄
@foreach
컬렉션 전체를 순회. 인덱스 불필요 시 가장 간결한 선택
🔢
@for
인덱스(i)가 필요할 때. 순번 출력·홀짝 조건에 활용
🔑
@key
고유 ID로 Diffing 최적화. 동적 변경 리스트에 필수
🛡️
빈 컬렉션 방어
@if + @foreach 조합으로 빈 상태를 별도 메시지로 처리

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

⚠️ 주의 1. @for 인덱스 범위 초과

i <= 컬렉션.Count처럼 <=을 사용하면 마지막 인덱스를 초과하여 IndexOutOfRangeException이 발생합니다. 반드시 <(엄격한 미만)을 사용하세요.

@* ❌ 잘못된 예 — 인덱스 초과 오류 *@
@for (int i = 0; i <= items.Count; i++) { ... }

@* ✅ 올바른 예 *@
@for (int i = 0; i < items.Count; i++) { ... }

⚠️ 주의 2. @key 없는 동적 리스트

@key를 지정하지 않으면 Blazor는 위치(순서)로만 항목을 구분합니다. 리스트 중간에서 항목을 삭제하면 이후 항목들의 DOM이 불필요하게 전부 다시 렌더링되어 성능이 저하되고, 입력 필드의 값이 잘못된 항목에 남을 수 있습니다.

@* ❌ 동적 리스트에 @key 없음 *@
@foreach (var item in items)
{
    <div>@item.Name</div>
}

@* ✅ 고유 키 지정 *@
@foreach (var item in items)
{
    <div @key='item.Id'>@item.Name</div>
}

⚠️ 주의 3. null 컬렉션 순회

컬렉션 자체가 null이면 @foreach에서 NullReferenceException이 발생합니다. 선언 시 빈 컬렉션으로 초기화하는 습관을 들이세요.

@* ❌ null이면 런타임 오류 *@
private List<string> items;

@* ✅ 빈 리스트로 초기화 *@
private List<string> items = new();

⚠️ 주의 4. 람다 이벤트에서 루프 변수 캡처

버튼 클릭 등 이벤트 핸들러 람다에서 루프 변수를 직접 참조할 때, 복잡한 비동기 시나리오에서 마지막 값만 캡처될 수 있습니다. 로컬 변수에 복사하는 패턴을 권장합니다.

@foreach (var item in items)
{
    var current = item;  // 안전한 로컬 복사
    <button @onclick='() => HandleClick(current)'>@current</button>
}

🎯 마무리

Blazor의 반복 렌더링은 @foreach@for 두 지시어를 중심으로 동작합니다. 전체 컬렉션 순회에는 @foreach가 가장 간결하고, 인덱스가 필요한 경우에는 @for를 선택합니다. 동적으로 변경되는 리스트에는 반드시 @key를 지정하여 렌더링 성능을 보호하고, 컬렉션은 항상 new()로 초기화하여 null 오류를 원천 차단하세요.

✅ 핵심 정리
  • @foreach: 컬렉션 전체 순회. var item in collection 패턴으로 각 요소에 접근
  • @for: 인덱스 기반 반복. i 변수로 순번 표시·범위 제어 가능
  • @key: 고유 식별자 지정 → Blazor Diffing 최적화, DOM 재생성 최소화
  • 빈 컬렉션: @if (items.Count == 0)으로 빈 상태 메시지 별도 처리
  • null 방지: 컬렉션은 선언 시 new()로 초기화 필수
  • 중첩 반복: @foreach 안에 @foreach 중첩 가능 — 카테고리별 계층형 데이터 표현에 활용

댓글 남기기

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