ReactReact 기초 · 5기초

조건부 렌더링과 리스트 — 동적 UI 패턴

React조건부렌더링리스트mapkey

조건부 렌더링

function UserBadge({ isAdmin, isPremium }: { isAdmin: boolean; isPremium: boolean }) {
    // 1. if/else (렌더 밖)
    if (isAdmin) {
        return <span className="badge-admin">관리자</span>;
    }

    // 2. 삼항 연산자
    return (
        <span className={isPremium ? "badge-premium" : "badge-free"}>
            {isPremium ? "프리미엄" : "무료"}
        </span>
    );
}

&& 단축 평가

function Notification({ count, message }: { count: number; message?: string }) {
    return (
        <div>
            {/* count > 0일 때만 렌더링 */}
            {count > 0 && <span className="badge">{count}</span>}

            {/* 주의: 0은 렌더링됨! */}
            {/* ❌ */ count && <span>{count}</span>}
            {/* ✅ */ count > 0 && <span>{count}</span>}

            {/* 메시지가 있을 때만 */}
            {message && <p>{message}</p>}
        </div>
    );
}

nullish 합체와 옵셔널 체이닝

function Profile({ user }: { user?: { name: string; bio?: string } }) {
    return (
        <div>
            <h2>{user?.name ?? "익명"}</h2>
            <p>{user?.bio ?? "소개글이 없습니다."}</p>
        </div>
    );
}

복잡한 조건 처리

type LoadState = "idle" | "loading" | "success" | "error";

function DataView({ state, data, error }: {
    state: LoadState;
    data: string[] | null;
    error: string | null;
}) {
    // switch나 객체 맵으로 처리
    const content = {
        idle: <p>조회 버튼을 눌러주세요.</p>,
        loading: <Spinner />,
        success: <DataList items={data ?? []} />,
        error: <ErrorMessage message={error ?? "알 수 없는 오류"} />,
    }[state];

    return <div className="container">{content}</div>;
}

리스트 렌더링

interface Product {
    id: string;
    name: string;
    price: number;
    inStock: boolean;
}

function ProductList({ products }: { products: Product[] }) {
    if (products.length === 0) {
        return <p>상품이 없습니다.</p>;
    }

    return (
        <ul>
            {products.map(product => (
                <li key={product.id}>           {/* key 필수! */}
                    <span>{product.name}</span>
                    <span>{product.price.toLocaleString()}원</span>
                    {!product.inStock && <span>품절</span>}
                </li>
            ))}
        </ul>
    );
}

key의 중요성

flowchart TB
    subgraph BEFORE["렌더링 전"]
        B1["key=1 철수"]
        B2["key=2 영희"]
        B3["key=3 민준"]
    end
    subgraph AFTER["영희 삭제 후"]
        A1["key=1 철수"]
        A3["key=3 민준"]
    end
    BEFORE -->|"key로 추적"| AFTER
  • key는 React가 리스트 항목을 추적하는 고유 식별자
  • 배열 인덱스를 key로 쓰면 삭제/정렬 시 버그 발생
  • 데이터의 고유 ID 사용 권장
// ❌ 인덱스를 key로 사용
items.map((item, index) => <li key={index}>{item}</li>)

// ✅ 고유 ID를 key로 사용
items.map(item => <li key={item.id}>{item.name}</li>)

리스트 필터/정렬

function FilteredList({ items }: { items: Product[] }) {
    const [filter, setFilter] = useState<"all" | "inStock">("all");
    const [sortBy, setSortBy] = useState<"name" | "price">("name");

    const displayed = items
        .filter(item => filter === "all" || item.inStock)
        .sort((a, b) => {
            if (sortBy === "name") return a.name.localeCompare(b.name);
            return a.price - b.price;
        });

    return (
        <div>
            <select onChange={e => setFilter(e.target.value as "all" | "inStock")}>
                <option value="all">전체</option>
                <option value="inStock">재고 있음</option>
            </select>
            <ul>
                {displayed.map(item => (
                    <li key={item.id}>{item.name} — {item.price}원</li>
                ))}
            </ul>
        </div>
    );
}

중첩 리스트

interface Category {
    id: string;
    name: string;
    items: Product[];
}

function CategoryList({ categories }: { categories: Category[] }) {
    return (
        <div>
            {categories.map(category => (
                <section key={category.id}>
                    <h2>{category.name}</h2>
                    <ul>
                        {category.items.map(item => (
                            <li key={item.id}>{item.name}</li>
                        ))}
                    </ul>
                </section>
            ))}
        </div>
    );
}

정리

패턴사용 시점
if/else완전히 다른 컴포넌트 반환
삼항 연산자두 가지 중 하나
&&조건부 표시/숨김
??null/undefined 대체값
.map()배열 → JSX 변환
key리스트 항목 고유 식별

다음 편에서는 useRef와 useMemo — DOM 참조와 성능 최적화를 배웁니다.

궁금한 점이 있으신가요?

협업·의뢰는 아래로, 가벼운 소통은 인스타그램 @bluefox._.hi도 환영이에요.