조건부 렌더링(&&, 삼항, early return)과 리스트 렌더링(map, key)을 실전 상황에서 올바르게 사용하는 방법과 흔한 실수를 배웁니다.
🔀 조건부 렌더링 — 상황에 맞게 UI를 보여주기
실제 앱에서는 조건에 따라 다른 UI를 보여줘야 하는 경우가 매우 많습니다. 로그인한 사용자에게는 프로필을, 비로그인에게는 로그인 버튼을 보여주는 것처럼요. 리액트에서는 일반 JS 조건문과 연산자를 활용합니다.
1️⃣ 방법 1: && 단축 평가
특정 조건에서만 컴포넌트를 보여주고, 아니면 아무것도 없을 때 사용합니다.
function Notification({ count }: { count: number }) {
return (
<div>
{count > 0 && (
<span className="badge">{count}개의 알림</span>
)}
</div>
)
}
⚠️ 함정 주의: 왼쪽 값이 0이면 0이 화면에 렌더링됩니다! 숫자를 조건으로 쓸 때는 반드시 불리언으로 변환하세요.
// ❌ count가 0이면 '0'이 화면에 출력됨
{count && <span>{count}개</span>}
// ✅ 불리언으로 변환
{count > 0 && <span>{count}개</span>}
{!!count && <span>{count}개</span>}
2️⃣ 방법 2: 삼항 연산자
조건에 따라 두 가지 중 하나를 보여줄 때 사용합니다.
function AuthButton({ isLoggedIn }: { isLoggedIn: boolean }) {
return (
<header>
{isLoggedIn
? <button onClick={logout}>로그아웃</button>
: <button onClick={login}>로그인</button>
}
</header>
)
}
삼항 연산자는 짧을 때 좋습니다. 조건 안에 또 삼항을 넣는 중첩은 가독성을 크게 해칩니다. 그럴 때는 early return 방식을 사용하세요.
3️⃣ 방법 3: Early Return (조기 반환)
컴포넌트 함수 안에서 조건에 따라 다른 JSX를 return하는 방식입니다. 조건이 복잡할 때 가장 깔끔합니다.
function UserProfile({ user }: { user: User | null }) {
if (!user) {
return <p>로그인이 필요합니다.</p>
}
if (user.isBanned) {
return <p>정지된 계정입니다.</p>
}
// 여기 아래로 오면 user가 있고 정지되지 않은 상태
return (
<div>
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.bio}</p>
</div>
)
}
📃 리스트 렌더링 — map으로 배열을 JSX로
배열 데이터를 JSX 목록으로 변환할 때 Array.map()을 사용합니다. 각 항목을 JSX로 변환해 새 배열을 만들고, 리액트가 이것을 렌더링합니다.
type Product = { id: number; name: string; price: number }
const products: Product[] = [
{ id: 1, name: '노트북', price: 1200000 },
{ id: 2, name: '마우스', price: 35000 },
{ id: 3, name: '키보드', price: 85000 },
]
function ProductList() {
return (
<ul>
{products.map((product) => (
<li key={product.id}>
{product.name} — {product.price.toLocaleString()}원
</li>
))}
</ul>
)
}
🔑 key — 왜 필수인가
리스트 아이템에는 반드시 key prop을 줘야 합니다. key는 리액트가 리스트 변경 시 어떤 항목이 바뀌었는지 추적하는 데 사용합니다.
| 좋은 key | 나쁜 key | 이유 |
|---|---|---|
product.id | index | 순서가 바뀌면 index도 바뀌어 버그 발생 |
user.email | Math.random() | 매 렌더마다 새 key → 전체 재렌더 |
| UUID | 중복 값 | key는 목록 안에서 유일해야 함 |
배열 인덱스(index)를 key로 쓰면 왜 문제가 될까요? 목록에서 중간 항목을 삭제하면 그 이후 항목의 인덱스가 모두 바뀝니다. 리액트는 key가 같으면 같은 컴포넌트로 인식하기 때문에 잘못된 상태가 유지되는 버그가 발생합니다.
🔍 빈 배열과 로딩 처리
실제 앱에서는 데이터가 없거나 로딩 중인 경우를 처리해야 합니다:
function ProductList({ products, isLoading }: { products: Product[]; isLoading: boolean }) {
if (isLoading) {
return <div>불러오는 중...</div>
}
if (products.length === 0) {
return <div>상품이 없습니다.</div>
}
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
)
}
✅ 정리
- &&: 조건이 true일 때만 렌더링 (숫자 0 주의).
- 삼항 연산자: 두 가지 중 하나를 보여줄 때.
- early return: 복잡한 조건 분기 — 컴포넌트 맨 위에서 먼저 처리.
map()으로 배열을 JSX 배열로 변환해 렌더링합니다.key는 반드시 제공. 배열 인덱스 대신 안정적인 고유값(id)을 사용합니다.
관련 주제
- && 단축 평가
- 삼항 연산자
- early return
- null 반환으로 숨기기
- 0 렌더링 함정
- 개발·프로그래밍
- 개발·프로그래밍 강의
- 리액트 입문 — 컴포넌트로 만드는 웹
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
