Skip to Content
WebReactReact 기초07. 조건부 렌더링으로 읽기 상태 표시

이번 편의 결과물: 각 책 카드에 읽음/읽는 중/읽고 싶음 배지(StatusBadge)가 상태값에 따라 다르게 표시됩니다. · 다루는 개념: if/삼항/&&로 조건부 렌더링, 상태값에 따라 다른 UI 렌더링, 빈 목록 상태 처리

이 편에서 만드는 파일

bookshelf/src/ ├── components/ │ ├── StatusBadge.jsx (+) │ └── BookCard.jsx (~) └── App.jsx (변경 없음)

개념 정리

books.seed.json의 각 책은 status 필드로 'wish', 'reading', 'done' 중 하나를 가집니다. 지금까지 BookCard는 이 값을 화면에 전혀 쓰지 않았습니다. 이 편에서는 status 값에 따라 다른 배지를 그리는 조건부 렌더링을 추가합니다.

조건부 렌더링 세 가지 방법

방법형태언제 쓰나
if/else함수 본문에서 다른 JSX를 return반환할 마크업 구조 자체가 크게 다를 때
삼항 연산자조건 ? A : BJSX 안에서 두 가지 중 하나를 표현식으로 골라야 할 때
&& 연산자조건 && A조건이 참일 때만 보여주고, 거짓이면 아무것도 안 보여줄 때

세 방법 모두 최종적으로는 “이 렌더링에서 어떤 JSX를 반환할지”를 고르는 것입니다. React는 조건에 따라 컴포넌트를 껐다 켰다 하는 것이 아니라, 매 렌더링마다 계산된 JSX(또는 null)를 그릴 뿐입니다.

&& 연산자를 쓸 때 흔한 함정

조건 && <span>내용</span>에서 조건0이면 화면에 0이 그대로 찍힙니다. 0은 falsy 값이지만 &&는 첫 피연산자가 falsy이면 그 값 자체를 반환하기 때문입니다. books.length && <BookList ... />처럼 숫자를 조건으로 쓸 때는 books.length > 0 && ...처럼 명시적으로 boolean으로 만듭니다.

{count && <span>{count}개</span>} // count가 0이면 화면에 "0"이 찍힘 {count > 0 && <span>{count}개</span>} // count가 0이면 아무것도 안 그려짐

status 값과 배지 매핑

status 값배지 문구배지 색
'wish'읽고 싶음회색
'reading'읽는 중파랑
'done'읽음초록

세 가지 이상의 값을 분기할 때는 if/else if 사슬이나 객체 조회(lookup)가 삼항 연산자를 여러 번 중첩하는 것보다 읽기 쉽습니다. 이번 편은 객체 조회 방식을 씁니다.

실습

1. StatusBadge.jsx 만들기

src/components/StatusBadge.jsx를 새로 만듭니다.

2. 코드 작성

// src/components/StatusBadge.jsx const STATUS_LABEL = { wish: '읽고 싶음', reading: '읽는 중', done: '읽음', }; const STATUS_COLOR = { wish: '#6b7280', reading: '#2563eb', done: '#16a34a', }; function StatusBadge({ status }) { const label = STATUS_LABEL[status] ?? '알 수 없음'; const color = STATUS_COLOR[status] ?? '#9ca3af'; return ( <span className="status-badge" style={{ backgroundColor: color }}> {label} </span> ); } export default StatusBadge;
// src/components/BookCard.jsx import StatusBadge from './StatusBadge.jsx'; function BookCard({ title, author, coverColor, pages, status }) { return ( <article className="book-card" style={{ borderTop: `6px solid ${coverColor}` }}> <StatusBadge status={status} /> <h2>{title}</h2> <p>{author}</p> <p className="book-pages">{pages}쪽</p> </article> ); } export default BookCard;

STATUS_LABELSTATUS_COLORstatus 문자열을 화면에 보여줄 값으로 바꾸는 조회 객체입니다. STATUS_LABEL[status]undefined가 되는 경우(예상 못 한 값이 들어온 경우)에 대비해 ?? 연산자로 기본값을 둡니다. BookCardstatus를 그대로 StatusBadge에 전달하기만 하고, 문구·색을 어떻게 정할지는 전혀 몰라도 됩니다. 이렇게 판단 로직을 자식 컴포넌트에 가두면 나중에 상태 종류가 늘어나도 StatusBadge 하나만 고치면 됩니다.

3. 실행

npm run dev

4. 확인

  • 카드마다 제목 위에 배지가 보입니다. 클린 코드(status: done)는 초록 “읽음”, 리팩터링 2판(status: reading)은 파랑 “읽는 중”, 함께 자라기(status: wish)는 회색 “읽고 싶음”으로 보입니다.
  • books.seed.json에서 아무 책의 status를 오타("raeding")로 바꿔 저장하면 배지가 “알 수 없음”·회색으로 나오는지 확인한 뒤 원래 값으로 되돌립니다.

직접 해보기

  1. BookList.jsx에서 books.length === 0일 때 보여주는 안내 문구를, && 연산자를 써서 books.length === 0 && <p>...</p>형태로 바꿔봅니다(현재는 if로 조기 반환하는 방식입니다). 두 방식의 결과가 같은지 확인합니다.
  2. StatusBadgepages가 300쪽을 넘으면 배지 옆에 “장편” 문구를 추가로 보여주는 조건부 렌더링을 삼항 연산자로 추가해봅니다.

정답 보기

// BookList.jsx 안, return 이전의 조기 반환 대신 return ( <div className="book-grid"> {books.length === 0 && <p className="empty-state">아직 등록된 책이 없습니다.</p>} {books.map((book) => ( <BookCard key={book.id} {...book} /> ))} </div> );
// StatusBadge.jsx, pages를 props로 받는다고 가정 function StatusBadge({ status, pages }) { const label = STATUS_LABEL[status] ?? '알 수 없음'; const color = STATUS_COLOR[status] ?? '#9ca3af'; return ( <span className="status-badge" style={{ backgroundColor: color }}> {label} {pages > 300 ? ' · 장편' : ''} </span> ); }

첫 번째 방법은 조건이 거짓일 때 빈 배열 자리에 아무것도 안 그려지는 원리를 그대로 이용합니다. 다만 이 경우처럼 “조건에 따라 아예 다른 마크업 구조”를 반환할 때는 if로 조기 반환하는 편이 더 읽기 쉬워, 이 과목은 BookList에서 if 방식을 유지합니다.

자주 하는 실수

증상원인고치는 법
목록이 비었을 때 화면에 0이 찍힘books.length && <p>...</p> 처럼 숫자를 조건으로 그대로 씀books.length === 0 && ...처럼 boolean 비교로 바꾼다
배지에 “알 수 없음”만 계속 보임status 값과 조회 객체의 키 철자가 다름('Reading' vs 'reading')데이터의 실제 값과 조회 객체 키의 대소문자·철자를 맞춘다
삼항 연산자가 여러 줄로 중첩되어 읽기 어려움분기가 3개 이상인데 삼항을 계속 중첩함분기가 3개 이상이면 조회 객체나 if/else if 사슬을 쓴다

확인 문제

문제 14지선다
count && (count개 문구를 보여주는 엘리먼트) 형태로 조건부 렌더링할 때, count가 0이면 화면에 나타나는 것은?
문제 24지선다
count가 0일 때 아무것도 렌더링하지 않으려면 어떻게 고쳐야 하는가?
문제 34지선다
세 가지 이상의 값을 분기할 때 삼항 연산자를 여러 번 중첩하는 대신 권장되는 방법은?
문제 44지선다
StatusBadge가 STATUS_LABEL[status] ?? '알 수 없음' 을 쓰는 이유는?

참고 자료

Last updated on