화면 일부를 상황에 따라 보였다 숨겼다 하는 건 어느 앱이든 매일 하는 일이다. 로그인했으면 이름을, 아니면 로그인 버튼을. 순수 자바스크립트라면 if로 나눠 innerHTML을 갈아 끼우거나 style.display를 만졌겠지만, React는 이걸 JSX 안에서 표현식으로 처리한다. 화면 자체가 조건에 따라 갈라지는 셈이다.
방법이 몇 가지 있는데, 상황마다 어울리는 게 다르다. 하나씩 언제 쓰는지까지 보자.
둘 중 하나면 삼항 연산자다. 조건에 따라 A 아니면 B를 보여줘야 할 때 제일 깔끔한 도구가 삼항 연산자(조건 ? A : B)다. JSX 중괄호 안에 그대로 넣는다.
function Greeting({ isLoggedIn }) {
return (
<div>
{isLoggedIn
? <span>환영합니다</span>
: <button>로그인</button>}
</div>
);
}
isLoggedIn이 true면 화면에 "환영합니다"가, false면 로그인 버튼이 나온다. 값이 바뀌면 화면도 즉시 그쪽으로 갈아탄다. if 문은 JSX 중괄호 안에 직접 못 쓰지만, 삼항은 값을 돌려주는 표현식이라 이렇게 끼워 넣을 수 있다. "이것 아니면 저것"이 명확한 경우에 가장 읽기 좋다.
있을 때만 보여줄 땐 && 를 쓴다. 조건이 참일 때만 뭔가를 보여주고, 거짓이면 아예 아무것도 안 그리고 싶을 때가 있다. 이럴 때 삼항으로 : null을 다는 대신 &&를 쓰면 짧아진다.
function Inbox({ count }) {
return (
<div>
메일함
{count > 0 && <span>새 메일 {count}개</span>}
</div>
);
}
count > 0이 참이면 뒤의 span이 화면에 나오고, 거짓이면 아무것도 안 나온다. 자바스크립트의 &&는 앞이 참일 때만 뒤를 내놓는 성질이 있어서, 이걸 "조건이 참이면 이 요소를 그려라"로 쓰는 것이다. 알림 배지처럼 있을 때만 뜨는 요소에 딱 맞는다.
여기서 숫자 0을 조심해라. 이게 React 초보를 제일 많이 무는 함정이다. 방금 코드에서 조건을 count > 0이 아니라 그냥 count로 쓰면 문제가 터진다.
{count && <span>새 메일 {count}개</span>}
// count가 0일 때 화면에 그냥 0 이 찍힌다
count가 5면 잘 나오다가, 0이 되는 순간 아무것도 안 나오는 게 아니라 화면에 덩그러니 0이 찍힌다. 0 && 무엇은 자바스크립트에서 0을 그대로 돌려주는데, React는 숫자 0은 화면에 그려버리기 때문이다(false나 null은 안 그린다). 나도 목록이 비었을 때 화면 구석에 웬 0이 떠서 한참을 째려봤다. 해법은 조건을 반드시 진짜 불리언으로 만드는 것이다. count > 0 && ...이나 Boolean(count) && ...처럼 쓰면 0일 때 조건이 false가 되어 깔끔하게 사라진다.
통째로 안 그릴 땐 null을 반환한다. 컴포넌트 전체를 어떤 조건에서 아예 화면에 안 내보내고 싶으면, JSX 안에서 고민할 것 없이 함수 맨 위에서 null을 돌려주면 된다.
function Warning({ show }) {
if (!show) return null;
return <p>경고: 저장되지 않았습니다</p>;
}
show가 거짓이면 null을 돌려주고, React는 null을 받으면 아무것도 안 그린다. 조건이 복잡하거나 컴포넌트 전체가 통으로 나타났다 사라지는 경우엔, JSX 중괄호 안에 삼항을 욱여넣는 것보다 이렇게 위에서 일찍 빠져나가는 편이 훨씬 읽기 쉽다. 이걸 조기 반환(early return)이라고 부른다.
조건이 많아지면 변수로 빼라. 상태가 서너 갈래로 갈리면 삼항을 겹쳐 쓰게 되는데, 그러면 금세 눈이 아파진다. 이럴 땐 화면 조각을 미리 변수에 담아두고 마지막에 꽂는다.
function Status({ state }) {
let content;
if (state === "loading") content = <p>불러오는 중</p>;
else if (state === "error") content = <p>에러 발생</p>;
else content = <p>완료</p>;
return <div>{content}</div>;
}
JSX도 결국 값이라 이렇게 변수에 담을 수 있다. if / else if / else로 어떤 조각을 담을지 위에서 정하고, 아래에서는 {content} 한 줄로 꽂기만 한다. 삼항 세 개를 괄호로 겹쳐 쌓은 것보다 상태 흐름이 눈에 잘 들어온다. 로딩, 에러, 성공처럼 갈래가 여럿인 화면에서 자주 쓰는 정리법이다.
목록이 비었을 땐 안내 문구를 대신 낸다. 실무에서 조건부 렌더링이 가장 자주 쓰이는 자리가 빈 목록 처리다. 데이터가 없을 때 텅 빈 화면을 그냥 두면 사용자는 로딩이 멈춘 건지 원래 없는 건지 알 수가 없다.
function TodoList({ items }) {
return items.length === 0
? <p>할 일이 없습니다</p>
: <ul>...</ul>;
}
items.length === 0으로 배열이 비었는지 보고, 비었으면 안내 문구를, 아니면 실제 목록을 그린다. 여기서도 함정을 하나 짚자면, items.length && ...처럼 쓰면 배열이 비었을 때 length가 0이라 앞에서 본 그 0이 화면에 찍힌다. 길이를 조건으로 쓸 땐 === 0이나 > 0으로 명확히 비교하는 습관이 안전하다.
상태 값이 곧 문구면 객체로 매핑한다. 상태에 따라 짧은 문구나 값만 바뀌는 경우라면, if를 여러 개 늘어놓는 대신 객체 하나로 대응표를 만들어 두는 게 깔끔하다.
const label = {
loading: "불러오는 중",
error: "에러 발생",
done: "완료",
};
function Status({ state }) {
return <p>{label[state]}</p>;
}
state가 "error"면 label["error"]가 뽑혀 "에러 발생"이 화면에 나온다. 조건이 늘어나도 if를 새로 치는 게 아니라 객체에 한 줄 추가하면 끝이라, 상태 가짓수가 많을수록 이득이 크다. 다만 이건 결과가 단순한 값이나 문구일 때 어울리고, 상태마다 화면 구조 자체가 크게 다르면 앞의 변수 방식이 낫다.
도구는 상황에 맞춰 고른다. 정리하면 셋 중 하나다. 둘 중 하나를 고를 땐 삼항, 있을 때만 보여줄 땐 &&, 컴포넌트를 통으로 숨길 땐 null 반환. 갈래가 많아지면 변수에 담아 풀어낸다. 그리고 &&를 쓸 땐 조건이 숫자가 되지 않게, 늘 > 0이나 비교식으로 진짜 참거짓을 만들어 준다. 이 감각만 잡히면 화면을 조건에 따라 갈라내는 일이 손에 붙는다.
한 가지 더 덧붙이면, 삼항을 두세 겹으로 겹쳐 쓰고 싶은 순간이 조건부 렌더링을 다시 쪼갤 신호다. 괄호가 깊어질수록 나중에 읽는 사람은 어느 조건이 어느 화면인지 놓친다. 그럴 땐 조기 반환으로 갈래를 위에서 끊거나, 화면 조각을 변수로 빼거나, 아예 작은 컴포넌트로 나누는 걸 먼저 떠올리는 게 좋다. 조건부 렌더링은 결국 화면을 얼마나 읽기 쉽게 갈라놓느냐의 문제지, 한 줄에 조건을 몇 개 욱여넣느냐의 경쟁이 아니다.