Menu

리액트 조건부 렌더링: if, 삼항 연산자, &&, null

React의 조건부 렌더링은 평범한 자바스크립트입니다. if 문, 삼항 연산자, &&가 컴포넌트가 반환할 JSX를 결정합니다. 각 패턴, &&가 출력할 수 있는 엉뚱한 0, null 반환, 그리고 컴포넌트를 숨기는 것과 렌더링하지 않는 것이 다른 이유를 배웁니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

React의 조건부 렌더링은 컴포넌트가 무엇을 반환할지 평범한 자바스크립트로 고르는 것입니다. return 전의 if 문, JSX 안의 삼항 연산자 cond ? a : b, 또는 무언가를 보여 주거나 아무것도 보여 주지 않는 cond && <Thing />을 사용합니다. 따로 배워야 할 템플릿 문법은 없습니다.

버튼을 클릭하면 메시지와 라벨이 모두 바뀝니다. 다음 렌더링에서 각 삼항 연산자가 다른 문자열을 고르기 때문입니다. useState(false)를 useState(true)로 바꾸면 미리보기가 로그인한 상태로 시작합니다.

if 문과 조기 반환

JSX 중괄호는 문이 아니라 표현식을 받으므로 마크업 안에 {if (x) ...}라고 쓸 수 없습니다. 하지만 return 전에는 어떤 if든 쓸 수 있습니다. 컴포넌트의 상태 전체가 다르게 보일 때(로딩, 오류, 빈 상태)는 각 경우마다 일찍 반환하고, 주요 마크업은 일반적인 경우를 위해 남겨 두세요.

각 버튼은 받은편지함을 다른 상태로 만들고, 각 상태는 짧고 읽기 쉬운 자기만의 분기를 가집니다. 조기 반환을 쓰면 중첩 없이 정상 경로를 맨 아래에 둘 수 있습니다.

JSX 안의 삼항 연산자

마크업의 작은 부분만 바뀐다면 인라인 삼항 연산자가 코드를 한곳에 모아 줍니다: {isOpen ? <CloseIcon /> : <MenuIcon />}. 삼항 연산자는 선택지가 두 개일 때 잘 맞습니다. 세 개 이상을 위한 중첩 삼항 연산자는 금세 읽기 어려워집니다.

// Hard to follow
{status === 'loading' ? <Spinner /> : status === 'error' ? <ErrorBox /> : <List />}

이런 코드를 보면 로직을 return 전의 if 체인(위처럼)이나 객체 맵(아래에서 설명)으로 옮기세요.

&&와 엉뚱한 0

cond && <Thing />은 cond가 truthy이면 <Thing />을 렌더링하고 false이면 아무것도 렌더링하지 않습니다. &&는 왼쪽이 falsy이면 왼쪽을 반환하고, React는 false, null, undefined에 대해 아무것도 렌더링하지 않기 때문에 동작합니다.

함정은 0도 falsy인데 React는 숫자를 렌더링한다는 점입니다. 그래서 카운트가 0일 때 count && <Badge />는 페이지에 0을 그대로 출력합니다.

읽지 않은 메시지가 없을 때 첫 줄은 "Broken: 0"으로 표시되고, 두 번째 줄은 "Fixed:" 뒤에 아무것도 보여 주지 않습니다. New message를 클릭하면 두 줄이 일치하고, Mark all read를 클릭하면 0이 돌아옵니다. 해결 방법은 &&에 진짜 불리언을 주는 것입니다: unread > 0, items.length > 0, 또는 Boolean(unread). 빈 문자열 ''도 원리상 같은 문제가 있지만 눈에 보이지 않게 렌더링되므로 거의 알아차리지 못합니다.

null 반환하기

컴포넌트는 null을 반환해서 아무것도 렌더링하지 않을 수 있습니다. 경고할 것이 있을 때만 나타나는 경고 배너가 좋은 예입니다. 부모는 항상 <Warning />을 렌더링하고, 결정은 컴포넌트가 합니다.

콘솔을 보세요. Warning 함수는 null을 반환하는 경우를 포함해 렌더링할 때마다 로그를 남깁니다. null을 반환하면 출력은 건너뛰지만 작업은 건너뛰지 않습니다. 훅과 이펙트는 여전히 실행되므로, 컴포넌트가 비용이 크거나 무언가(타이머, 구독)를 시작한다면 부모에서 결정하고 아예 렌더링하지 마세요.

JSX를 변수에 저장하기

JSX는 값이므로 변수에 할당하고, if 문으로 조립한 뒤, 나중에 마크업에 넣을 수 있습니다. 큰 레이아웃의 한 조각이 여러 조건에 따라 달라질 때 유용합니다.

function Checkout({ cart, user }) {
    let action;
    if (!user) {
        action = <a href="/login">Log in to check out</a>;
    } else if (cart.length === 0) {
        action = <p>Your cart is empty.</p>;
    } else {
        action = <button>Pay now</button>;
    }

    return (
        <section>
            <h2>Your cart</h2>
            {action}
        </section>
    );
}

객체로 여러 컴포넌트 중 하나 고르기

"여러 개 중 하나"를 고르는 경우라면, 각 값을 컴포넌트에 연결한 객체가 긴 if 체인이나 switch보다 짧고, 경우를 추가하는 것도 한 줄이면 됩니다.

TABS[tab]이 컴포넌트를 고르고, <Panel />이 그것을 렌더링합니다. 변수는 대문자로 시작해야 합니다. JSX는 <panel />을 HTML 태그로 취급합니다. 자기 컴포넌트를 가진 notifications 항목을 TABS에 추가하면 네 번째 버튼이 나타납니다.

숨기는 것과 렌더링하지 않는 것은 다릅니다

무언가를 사라지게 하는 방법은 두 가지이고, 동작이 다릅니다. show && <Counter />는 show가 false일 때 컴포넌트를 트리에서 제거합니다. React는 컴포넌트를 언마운트하고 상태를 버리므로, 다시 나타날 때는 처음부터 시작합니다. CSS로 숨기면(style={{ display: show ? 'block' : 'none' }}) 마운트된 상태가 유지되어 상태가 살아남습니다.

두 카운터를 몇 번씩 클릭하고, 체크박스를 해제했다가 다시 체크해 보세요. 첫 번째 카운터는 0으로 돌아가고 두 번째 카운터는 숫자를 그대로 유지합니다. 둘 다 틀린 것은 아닙니다. 매번 빈 상태로 시작해야 하는 폼에는 제거가 맞고, 스크롤 위치나 입력을 유지해야 하는 탭에는 숨기기가 맞습니다. 렌더링 페이지에서 React가 렌더링 사이에 컴포넌트가 같은 것인지 어떻게 판단하는지 설명합니다.

React 19.2는 세 번째 방법인 <Activity mode="hidden">을 추가합니다. CSS처럼 자식을 숨기고 상태를 유지하면서, 다시 보여 줄 때까지 자식의 이펙트도 정리합니다.

조건부 props와 속성

조건은 어떤 요소가 나타나는지에만 관한 것이 아닙니다. 어떤 prop이든 계산된 값을 받을 수 있으므로, 속성 안의 삼항 연산자나 비교만으로 충분한 경우가 많습니다.

<button disabled={!canSave} className={hasError ? 'field error' : 'field'}>
    Save
</button>
<input aria-invalid={hasError} placeholder={user ? user.email : 'you@example.com'} />

prop의 값이 undefined이면 React는 속성을 아예 빼므로, 가끔만 속성을 추가하는 깔끔한 방법이 됩니다: title={isTruncated ? fullText : undefined}. disabled, checked 같은 불리언 속성에는 true나 false를 넘기면 React가 속성을 추가하거나 제거합니다.

목록 안의 조건

목록에서 항목을 빼려면 map 안에서 null을 반환하지 말고 map하기 전에 배열을 filter하세요. items.filter((i) => i.done).map(...)은 하는 일이 그대로 읽히며, 목록과 key 페이지에서 실행되는 예제로 보여 줍니다.

자주 묻는 질문

JSX 안에서 if 문은 어떻게 작성하나요?

중괄호는 표현식을 받기 때문에 JSX 중괄호 안에 if 문을 넣을 수 없습니다. JSX 안에서는 삼항 연산자(cond ? a : b)나 &&를 쓰거나, return 전에 if를 실행하고 JSX를 변수에 저장하세요.

컴포넌트에 0이 표시되는 이유는 무엇인가요?

count && <Badge />는 count가 0일 때 count 자체를 반환하고, React는 숫자 0을 렌더링합니다. 왼쪽을 진짜 불리언으로 만드세요: count > 0 && <Badge />.

React에서 아무것도 렌더링하지 않으려면 어떻게 하나요?

컴포넌트에서 null을 반환하거나, JSX에 null, undefined, false, true를 넣으세요. React는 이 중 어느 것도 렌더링하지 않습니다.

null을 반환하는 컴포넌트도 실행되나요?

네. 함수가 실행되고, 훅이 실행되고, 이펙트도 동작합니다. null을 반환한다는 것은 페이지에 아무것도 추가하지 않는다는 뜻일 뿐입니다. 작업 자체를 건너뛰려면 부모에서 그 컴포넌트를 렌더링하지 마세요.

컴포넌트를 CSS로 숨겨야 하나요, 제거해야 하나요?

제거하면(show && <Panel />) 언마운트되므로 상태가 버려지고 다음에는 처음부터 시작합니다. display: none으로 숨기면 마운트된 상태가 유지되고 상태도 남습니다. 상태가 살아남아야 하는지에 따라 선택하세요.

Coddy 프로그래밍 언어 일러스트

Coddy로 코딩 배우기

시작하기