[React] 리스트 렌더링
개요
React에서 리스트를 렌더링할 때는 JavaScript의 Array.map() 메서드를 사용합니다. 각 항목에는 반드시 고유한 key 속성을 지정해야 합니다.
기본 리스트
function App() {
const fruits = ["사과", "바나나", "딸기", "포도"];
return (
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
);
}
key 속성
key는 React가 각 항목을 구별하고 효율적으로 업데이트하기 위해 필요한 필수 속성입니다.
// ✅ 고유 ID가 있는 경우 (권장)
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
];
users.map((user) => (
<li key={user.id}>{user.name}</li> // id를 key로 사용
));
// ⚠️ 인덱스를 key로 사용 (순서가 변하지 않는 경우만)
fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
));
// ❌ key 미설정 - 경고 발생, 렌더링 오류 가능
fruits.map((fruit) => <li>{fruit}</li>);
key는 같은 레벨 안에서 고유하면 됩니다. 다른 배열끼리는 중복 가능.
순서가 변하거나 항목을 추가/삭제한다면 반드시 고유 ID를 key로 사용하세요.
객체 배열 렌더링
interface User {
id: number;
name: string;
role: string;
active: boolean;
}
const users: User[] = [
{ id: 1, name: "Alice", role: "백엔드", active: true },
{ id: 2, name: "Bob", role: "프론트엔드", active: false },
{ id: 3, name: "Charlie", role: "DevOps", active: true },
];
// 방법 1: 인라인 렌더링
function App() {
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} - {user.role}
{user.active && <span> (활성)</span>}
</li>
))}
</ul>
);
}
// 방법 2: 별도 컴포넌트로 분리 (권장)
function UserItem({ user }: { user: User }) {
return (
<li style=>
<strong>{user.name}</strong>
<span> - {user.role}</span>
{!user.active && <span> (비활성)</span>}
</li>
);
}
function App() {
return (
<ul>
{users.map((user) => (
<UserItem key={user.id} user={user} />
))}
</ul>
);
}
필터링
import { useState } from "react";
const products = [
{ id: 1, name: "노트북", category: "전자기기", price: 1500000 },
{ id: 2, name: "마우스", category: "전자기기", price: 30000 },
{ id: 3, name: "책상", category: "가구", price: 200000 },
{ id: 4, name: "의자", category: "가구", price: 150000 },
];
function App() {
const [category, setCategory] = useState("전체");
// 필터링
const filtered = category === "전체"
? products
: products.filter((p) => p.category === category);
return (
<div>
<div>
{["전체", "전자기기", "가구"].map((cat) => (
<button
key={cat}
onClick={() => setCategory(cat)}
style=
>
{cat}
</button>
))}
</div>
<ul>
{filtered.map((product) => (
<li key={product.id}>
{product.name} - {product.price.toLocaleString()}원
</li>
))}
</ul>
<p>총 {filtered.length}개</p>
</div>
);
}
정렬
import { useState } from "react";
type SortKey = "name" | "price";
function App() {
const [sortBy, setSortBy] = useState<SortKey>("name");
const sorted = [...products].sort((a, b) => {
if (sortBy === "name") return a.name.localeCompare(b.name);
return a.price - b.price;
});
return (
<div>
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value as SortKey)}
>
<option value="name">이름순</option>
<option value="price">가격순</option>
</select>
<ul>
{sorted.map((product) => (
<li key={product.id}>{product.name} - {product.price.toLocaleString()}원</li>
))}
</ul>
</div>
);
}
원본 배열이 변경되지 않도록
[...products].sort()처럼 복사 후 정렬해야 합니다.
빈 배열 처리
function App() {
const [items, setItems] = useState<string[]>([]);
return (
<div>
{items.length === 0 ? (
<p>항목이 없습니다.</p>
) : (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
)}
</div>
);
}
중첩 리스트
interface Category {
id: number;
name: string;
items: string[];
}
const categories: Category[] = [
{ id: 1, name: "과일", items: ["사과", "바나나", "딸기"] },
{ id: 2, name: "채소", items: ["당근", "오이", "토마토"] },
];
function App() {
return (
<div>
{categories.map((category) => (
<div key={category.id}>
<h3>{category.name}</h3>
<ul>
{category.items.map((item, index) => (
<li key={index}>{item}</li> // 중첩 리스트의 key는 해당 배열 내에서 고유하면 됨
))}
</ul>
</div>
))}
</div>
);
}