화면은 항상 똑같지 않아요. 로그인했으면 이걸, 아니면 저걸 보여주고, 배열 데이터를 목록으로 쭉 그려야 하죠. 이번 장에서 조건부 렌더링과 리스트 렌더링(map · key)을 확실히 잡아봐요.
조건부 렌더링(Conditional Rendering)은 어떤 조건이냐에 따라 화면에 다른 걸 보여주는 것이에요. 예를 들어 로그인 상태면 "로그아웃 버튼"을, 아니면 "로그인 버튼"을 보여주는 식이죠. JSX 안에서는 if 문을 직접 쓸 수 없어서, 주로 삼항 연산자나 논리 &&를 써요.
? :&&&&·map…)은 전부 순수 자바스크립트예요. 리액트에서만 통하는 특별한 문법이 아니라 어디서든 쓰는 JS 기술이죠. 리액트가 더하는 건 딱 몇 가지 "규칙"뿐이에요.
조건 ? A : B&& / ||.map() · .filter(){ } 안에 넣어야 화면에 반영false·null·undefined는 안 그림, 그런데 0은 그림key를 붙여야 함조건 ? A : B 🟦 순수 JS
둘 중 하나를 보여줄 때 딱이에요. "조건이 참이면 A, 아니면 B."
function Greeting({ isLogin }) {
return (
<div>
{isLogin ? <p>환영합니다!</p> : <p>로그인이 필요해요.</p>}
</div>
);
}
조건 && A 🟦 순수 JS
있을 때만 보여주고, 아니면 아무것도 안 보여줄 때 써요. 조건이 참이면 A가 렌더링되고, 거짓이면 아무것도 안 나와요("있으면 보여주고 없으면 말고").
function Inbox({ count }) {
return (
<div>
{count > 0 && <p>새 메시지 {count}개</p>}
</div>
);
}
{content}처럼 꽂거나, 컴포넌트 맨 위에서 early return(먼저 반환)으로 처리하면 깔끔해요.
function Profile({ user }) {
// early return: user가 없으면 여기서 끝
if (!user) return <p>로그인이 필요해요.</p>;
// 변수에 담기
const badge = user.isAdmin ? <span>👑 관리자</span> : null;
return (
<div>
<h2>{user.name}</h2>
{badge}
</div>
);
}
조건 && A에서 조건 자리에 숫자 0이 오면 문제가 생겨요. 자바스크립트에서 0은 falsy지만, 리액트는 0이라는 값을 그대로 화면에 그려버려요. 그래서 아무것도 안 나오길 기대했는데 화면에 "0"이 덩그러니 찍힙니다.
false처럼 "거짓"으로 취급되는 값이에요. 0, ""(빈 문자열), null, undefined, NaN이 여기에 속해요.function Cart({ count }) {
// ❌ count가 0이면 화면에 "0"이 그대로 찍힘!
return (
<div>
{count && <p>장바구니에 {count}개 담김</p>}
</div>
);
}
0 && <p>…</p>은 자바스크립트에서 왼쪽이 falsy면 그 값(0)을 그대로 반환해요. 즉 결과가 false가 아니라 숫자 0이에요. 리액트는 false·null·undefined는 화면에 안 그리지만, 0은 유효한 값이라 그대로 그려요.
🧩 이 함정은 절반은 JS(&&가 0을 반환하는 동작), 절반은 리액트(0을 화면에 그리는 규칙)의 합작이에요.
해결책은 조건을 진짜 불리언(true/false)으로 만들어 주는 거예요. count > 0처럼 비교식을 쓰면 결과가 true/false라 안전합니다.
function Cart({ count }) {
// ✅ count > 0 은 true/false → 0일 땐 아무것도 안 나옴
return (
<div>
{count > 0 && <p>장바구니에 {count}개 담김</p>}
</div>
);
}
{배열.length && …}도 같은 함정이에요. 배열이 비면 length가 0이라 화면에 "0"이 찍혀요. 항상 length > 0 && …처럼 비교식으로 감싸세요.화면에 여러 개를 반복해서 그릴 때는 배열의 map() 메서드를 써요. map은 배열의 각 항목을 JSX 요소로 변환해서 JSX 배열을 만들어요. 리액트는 그 배열을 받아 하나하나 화면에 그립니다.
.map()은 순수 자바스크립트 배열 메서드예요 — 리액트 전용이 아니라 어디서나 쓰는 JS 기능이죠. 리액트가 여기에 더하는 건 뒤에 나올 key 하나뿐이에요(그건 🟪 리액트 규칙).function FruitList() {
const fruits = ['사과', '바나나', '포도'];
return (
<ul>
{fruits.map((fruit) => (
<li>{fruit}</li>
))}
</ul>
);
}
// 결과: 사과 · 바나나 · 포도 세 개의 <li>
객체 배열이면 원하는 속성을 꺼내 쓰면 돼요.
const items = [
{ id: 1, name: '키보드' },
{ id: 2, name: '마우스' },
];
function ProductList() {
return (
<ul>
{items.map((item) => (
<li>{item.name}</li>
))}
</ul>
);
}
map." for 문 대신 map으로 데이터 배열 → JSX 배열을 만들어 꽂는다고 기억하세요.map으로 만든 각 요소에는 고유한 key를 붙여야 해요. key는 리액트가 "어떤 항목이 추가·삭제·변경됐는지"를 효율적으로 추적하기 위한 이름표예요(재조정 과정에서 사용). key가 없으면 콘솔에 경고가 뜨고, 목록이 바뀔 때 비효율적으로 다시 그려질 수 있어요.
const items = [
{ id: 1, name: '키보드' },
{ id: 2, name: '마우스' },
];
function ProductList() {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
고유 id가 없다고 map((item, index) => <li key={index}>)처럼 배열 index를 key로 쓰는 경우가 많아요. 목록이 절대 안 바뀌면(고정 순서, 추가·삭제 없음) 괜찮지만, 목록 순서가 바뀌거나 항목을 추가·삭제하면 버그가 생겨요.
// ❌ 순서가 바뀌거나 추가/삭제되면 버그 위험
{items.map((item, index) => (
<li key={index}>{item.name}</li>
))}
// ✅ 데이터의 고유 id를 key로
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
crypto.randomUUID()). 정말 어쩔 수 없고 목록이 정적(추가·삭제·정렬 없음)이라면 그때만 index를 key로 써도 괜찮아요.
? :, 있을 때만 → 논리 &&, 복잡하면 → 변수/early return.{count && …}에서 count가 0이면 화면에 "0"이 찍혀요 → {count > 0 && …}로 감싸기.map()으로 JSX 배열로 만들어 그려요.