본문 바로가기

조건부 렌더링

if문·삼항 연산자·논리곱·null 반환을 이용해 상태와 조건에 따라 표시할 컴포넌트와 요소를 선택합니다.

우리는 핵심 개념인 컴포넌트, props, state, 그리고 이벤트 처리를 학습했습니다.

이 장에서는 조건부 렌더링, 리스트 렌더링, 폼 처리처럼 동적인 UI를 만드는 방법을 다룹니다.

이번 장에서는 조건부 렌더링(Conditional Rendering)에 대해 다룰 것입니다.

조건부 렌더링은 특정 조건이 만족될 때만 컴포넌트나 UI 요소를 화면에 보여주거나, 또는 다른 컴포넌트를 보여주는 기법을 의미합니다.

예를 들어, 사용자가 로그인했을 때만 마이 페이지 버튼을 보여주거나, 로딩 중일 때는 로딩 스피너를 보여주는 것 등이 모두 조건부 렌더링의 예시입니다.

리액트에서 조건부 렌더링을 구현하는 방법은 여러 가지가 있으며, 상황에 따라 적절한 방법을 선택하는 것이 중요합니다.


if 문 (조건문) 사용하기

가장 기본적인 조건부 렌더링 방법은 자바스크립트의 if 문을 사용하는 것입니다.

if 문은 컴포넌트의 return 문 바깥에서 조건을 검사하고, 그 결과에 따라 다른 JSX를 반환하도록 할 때 유용합니다.

예제: 로그인 상태에 따른 메시지 표시

앞서 만든 Vite 프로젝트에서 이어갑니다. 아래 예제에서 .js로 표기한 JSX 파일은 .jsx로 저장하고, App.js 수정 안내는 기존 App.jsx에 적용합니다.

LoginControl.js 컴포넌트 생성: src/components 폴더 안에 LoginControl.js 파일을 생성합니다.

src/components/LoginControl.js
import React, { useState } from 'react';

function GuestGreeting() {
  return <h2>로그인 해주세요!</h2>;
}

function UserGreeting(props) {
  return <h2>환영합니다, {props.userName}님!</h2>;
}

function LoginButton(props) {
  return (
    <button onClick={props.onClick}>
      로그인
    </button>
  );
}

function LogoutButton(props) {
  return (
    <button onClick={props.onClick}>
      로그아웃
    </button>
  );
}

function LoginControl() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [userName, setUserName] = useState(''); // 로그인 시 사용자 이름 저장

  const handleLoginClick = () => {
    setIsLoggedIn(true);
    setUserName('김코딩'); // 예시 사용자 이름 설정
  };

  const handleLogoutClick = () => {
    setIsLoggedIn(false);
    setUserName('');
  };

  let button; // (1) 조건에 따라 렌더링할 버튼 변수 선언

  if (isLoggedIn) { // (2) if 문을 사용하여 조건 검사
    button = <LogoutButton onClick={handleLogoutClick} />;
  } else {
    button = <LoginButton onClick={handleLoginClick} />;
  }

  let greeting; // (3) 조건에 따라 렌더링할 인사말 변수 선언

  if (isLoggedIn) {
    greeting = <UserGreeting userName={userName} />;
  } else {
    greeting = <GuestGreeting />;
  }

  return (
    <div style={{ border: '1px solid #0056b3', padding: '20px', margin: '20px', borderRadius: '8px', textAlign: 'center' }}>
      {greeting} {/* (4) JSX 내에서 변수 사용 */}
      {button}
    </div>
  );
}

export default LoginControl;
  • isLoggedIn state에 따라 다른 greeting 컴포넌트(GuestGreeting 또는 UserGreeting)와 다른 button 컴포넌트(LoginButton 또는 LogoutButton)를 할당합니다.
  • return 문 안에서는 이 변수들을 {} 중괄호 안에 넣어 렌더링합니다.
App.js에서 LoginControl 컴포넌트 사용
src/App.js
import React from 'react';
import './App.css';
import LoginControl from './components/LoginControl'; // LoginControl 컴포넌트 불러오기

function App() {
  return (
    <div className="App">
      <h1>React 조건부 렌더링 예시</h1>
      <LoginControl />
    </div>
  );
}

export default App;

결과 확인: 브라우저에서 로그인/로그아웃 버튼을 클릭해 보세요.

처음에는 로그인 해주세요! / 로그인, 로그인 버튼을 누른 뒤에는 환영합니다, 김코딩님! / 로그아웃, 로그아웃 뒤에는 처음 표시가 나와야 합니다. 이벤트 핸들러가 다음 상태를 요청하면 다음 렌더에서 두 if 문이 같은 isLoggedIn을 읽어 인사말과 버튼을 함께 고릅니다.

여기서 로그인은 화면 분기를 연습하기 위한 로컬 상태입니다. 서버에 비밀번호를 보내거나 사용자를 인증하는 코드는 없습니다. 버튼의 표시를 바꾸는 일과 실제 인증을 구별합니다.

if 문은 여러 컴포넌트를 반환해야 하거나, 조건이 복잡하여 가독성을 높여야 할 때 유용합니다.


인라인 조건부 렌더링 (삼항 연산자 ? :)

JSX 내부에서 간단하게 조건을 처리하고 싶을 때는 삼항 연산자(condition ? true : false)를 사용할 수 있습니다.

이는 if 문보다 간결하게 조건을 표현할 수 있어 자주 사용됩니다.

예제: MessageChanger 컴포넌트를 삼항 연산자로 리팩토링

이전 장에서 만들었던 MessageChanger 컴포넌트를 삼항 연산자로 조건부 렌더링을 적용하여 수정해 보겠습니다.

src/components/MessageChanger.js
import React, { useState } from 'react';

function MessageChanger() {
  const [showMessage, setShowMessage] = useState(false); // 메시지 표시 여부 상태

  const handleToggleMessage = () => {
    setShowMessage(!showMessage); // 상태 반전
  };

  return (
    <div style={{ border: '1px dashed #999', padding: '15px', margin: '20px', borderRadius: '8px' }}>
      <h2>메시지 토글</h2>
      {/* true이면 표시 메시지, false이면 숨김 안내 문장을 보여줌 */}
      {showMessage ? ( // (1) 삼항 연산자 사용
        <p style={{ fontSize: '20px', fontWeight: 'bold', color: '#28a745' }}>
          지금 메시지가 보입니다!
        </p>
      ) : (
        <p style={{ fontSize: '16px', color: '#6c757d' }}>
          메시지가 숨겨져 있습니다.
        </p>
      )}
      <button onClick={handleToggleMessage} style={{ padding: '10px 20px', fontSize: '16px', cursor: 'pointer' }}>
        {showMessage ? '메시지 숨기기' : '메시지 보기'} {/* 버튼 텍스트도 조건부 렌더링 */}
      </button>
    </div>
  );
}

export default MessageChanger;
  • showMessage state의 값에 따라 다른 <p> 태그가 렌더링됩니다.
  • 버튼의 텍스트도 showMessage 값에 따라 메시지 숨기기 또는 메시지 보기로 변경됩니다.

삼항 연산자는 조건이 간단하고, 참과 거짓 두 가지 경우 중 하나를 렌더링할 때 매우 효과적입니다.


논리 연산자 && (AND) 사용하기

특정 조건이 참(True)일 때만 어떤 요소를 렌더링하고, 거짓일 때는 아무것도 렌더링하지 않고 싶을 때 논리 AND(&&) 연산자를 사용할 수 있습니다.

리액트에서 JSX가 반환될 수 있는 값 중 null, false, undefined는 화면에 렌더링되지 않는다는 특성을 이용한 것입니다.

true && expression은 expression으로 평가되고, false && expression은 false로 평가됩니다. 왼쪽에 숫자 0을 두면 0이 렌더되므로 개수 조건은 count > 0처럼 불리언으로 만듭니다.

예제: 경고 메시지 표시/숨기기
src/components/WarningBanner.js
import React, { useState } from 'react';

function WarningBanner({ warn }) { // warn props를 받음
  if (!warn) { // warn이 false면 null을 반환하여 아무것도 렌더링하지 않음
    return null;
  }

  return (
    <div style={{ backgroundColor: '#ffc107', color: 'white', padding: '10px', borderRadius: '5px', marginBottom: '15px' }}>
      경고! 데이터가 안전하지 않습니다.
    </div>
  );
}

function AlertControl() {
  const [showWarning, setShowWarning] = useState(true);

  const handleToggleClick = () => {
    setShowWarning(prevWarn => !prevWarn);
  };

  return (
    <div style={{ border: '1px solid #dc3545', padding: '20px', margin: '20px', borderRadius: '8px', textAlign: 'center' }}>
      <h2>경고 배너 제어</h2>
      {/* showWarning이 true일 때만 WarningBanner 컴포넌트가 렌더링됨 */}
      {showWarning && <WarningBanner warn={true} />} {/* (1) 논리 AND 연산자 사용 */}
      <button onClick={handleToggleClick} style={{ padding: '10px 20px', fontSize: '16px', cursor: 'pointer' }}>
        {showWarning ? '경고 숨기기' : '경고 보이기'}
      </button>
    </div>
  );
}

export default AlertControl;
  • showWarning && <WarningBanner warn={true} />: showWarning이 true일 때만 <WarningBanner> 컴포넌트가 렌더링되고, false일 때는 아무것도 렌더링되지 않습니다.

&& 연산자는 특정 조건이 충족될 때만 UI 요소를 보여주고, 그렇지 않을 때는 단순히 숨기고 싶을 때 매우 간결하게 사용할 수 있습니다.


컴포넌트 렌더링 막기: null 반환

컴포넌트가 조건을 검사한 뒤 화면에 표시할 결과를 비워야 할 때가 있습니다.

이럴 때는 컴포넌트의 return 문에서 null을 반환하면 됩니다.

컴포넌트 함수는 실행되지만 null을 반환하면 화면에 표시할 자식 결과가 없습니다. 부모 트리에서 컴포넌트를 제거하는 것과는 달라 컴포넌트의 상태는 유지될 수 있습니다.

위 WarningBanner 컴포넌트에서 이미 이 방법을 사용했습니다.

function WarningBanner({ warn }) {
  if (!warn) { // warn이 false면
    return null; // 아무것도 렌더링하지 않음
  }
  // ... (JSX 렌더링 코드)
}

null을 반환하는 것은 컴포넌트의 렌더링 출력을 완전히 숨기는 효과가 있습니다.


조건부 렌더링 방법 선택 가이드

어떤 조건부 렌더링 방법을 선택할지는 분기 범위, 대체 화면의 필요 여부, JSX 안에서 읽히는 정도에 따라 달라집니다.

  • if 문
    • 조건이 복잡하거나, 조건에 따라 완전히 다른 여러 개의 JSX 블록을 렌더링해야 할 때.
    • 함수형 컴포넌트의 return 문 바깥에서 로직을 분리하여 가독성을 높이고 싶을 때.
  • 삼항 연산자 (? :)
    • 참일 때와 거짓일 때 모두 렌더링해야 하는 내용이 있고, 비교적 간결한 JSX를 반환할 때.
    • JSX 내부에서 인라인으로 조건을 표현하기 가장 적합합니다.
  • 논리 AND 연산자 (&&)
    • 조건이 참일 때만 특정 요소를 렌더링하고, 거짓일 때는 아무것도 렌더링하지 않을 때.
    • 매우 간결하게 한 줄로 표현할 수 있습니다.
  • null 반환
    • 특정 조건에서 컴포넌트의 화면 출력을 비우고 싶을 때.

아래 다이어그램은 반드시 따라야 하는 순서가 아니라, 조건의 복잡도와 렌더링 의도에 따라 if, 삼항 연산자, &&, null 반환을 고르는 기준을 정리한 것입니다.

조건식은 원하는 표시 결과에 맞춰 읽기 쉽게 고릅니다

CONDITIONAL OUTPUT

조건식은 원하는 표시 결과에 맞춰 읽기 쉽게 고릅니다

if·삼항식·논리곱은 JSX를 선택하는 문법이고 null은 비어 있는 반환 결과입니다. 순서대로 실행하는 네 단계가 아니며, 본문의 로그인과 경고 예에서 표시할 결과를 먼저 정합니다.

조건부 렌더링 선택렌더 조건에서 여러단계 여부를 판단해 예는 if로, 아니오는 결과형태 판단으로 연결한다. 결과형태의 세 가지는 둘중하나→삼항식, 참일때만→논리곱, 표시없음→null이다. 문법선택의 읽기 가이드이며 반드시 실행하는 순서나 유일한 문법 분류가 아니다. null은 if나삼항식의 결과로도 반환할 수 있다.렌더 조건여러 단계검사?if조기 반환원하는표시 결과?예아니요둘 중 하나참일 때만표시 없음삼항식참 · 거짓 결과 선택&&참일 때 결과 포함null표시할 자식 없음
조건부 렌더링 선택 · 모바일렌더 조건에서 여러단계 여부를 판단해 예는 if로, 아니오는 결과형태 판단으로 연결한다. 결과형태의 세 가지는 둘중하나→삼항식, 참일때만→논리곱, 표시없음→null이다. 문법선택의 읽기 가이드이며 반드시 실행하는 순서나 유일한 문법 분류가 아니다. null은 if나삼항식의 결과로도 반환할 수 있다. 모바일은 같은 노드와 방향을 다른 배치로 보여 줍니다.렌더 조건여러 단계검사?if조기 반환원하는표시 결과?예아니요삼항식둘 중 하나&&참일 때만null표시할 자식 없음

if / 삼항식 · 둘 중 하나

isLoggedIn: false → GuestGreeting
isLoggedIn: true → UserGreeting

LoginControl은 인사말과 버튼을 각각 if로 선택합니다. MessageChanger는 참·거짓 모두 p와 버튼 문구가 있어 삼항식이 간결합니다.

&& / null · 비어 있는 결과

showWarning && <WarningBanner />
if (!warn) return null;

첫 식은 부모가 배너를 포함할지 결정합니다. null 반환은 함수가 실행돼도 자식 화면을 비웁니다. 부모 트리에서 제거하는 것과 상태 보존이 다를 수 있습니다.

숫자 0 && JSX는 0을 표시합니다. 개수 조건은 count > 0처럼 불리언으로 만듭니다. 복잡한 중첩은 변수나 컴포넌트로 분리합니다.

이 방법들을 사용하면 props와 state에 따라 컴포넌트의 표시 내용을 조정할 수 있습니다.

다른 입력으로 직접 판단하기

코드를 실행하기 전에 다음 결과를 적어 보세요.

  1. handleLoginClick에서 저장하는 이름만 지수로 바꾸고 로그인하면 인사말과 버튼은 어떻게 표시되나요?
  2. 새 메시지 개수가 0일 때 count && <p>새 메시지</p>와 count > 0 && <p>새 메시지</p>는 각각 무엇을 표시하나요?
  3. 원래 AlertControl에서 경고 숨기기를 누르면 부모가 WarningBanner를 호출하나요? 대신 부모가 항상 <WarningBanner warn={showWarning} />를 반환한다면 거짓 조건을 어디에서 처리하나요?
판단 과정 확인
  1. 로그인 상태가 참이므로 환영합니다, 지수님! / 로그아웃입니다. 이름을 바꿔도 버튼을 고르는 조건은 같습니다.
  2. 첫 식은 자바스크립트에서 0으로 평가되므로 숫자 0이 표시됩니다. 두 번째 식은 거짓으로 평가되므로 표시할 자식이 없습니다. &&가 언제나 불리언을 반환한다고 가정하지 않습니다.
  3. 원래 부모의 && 식은 거짓이 되어 해당 자식이 트리에서 빠집니다. 바꾼 식에서는 WarningBanner 함수가 warn을 검사하고 return null을 실행합니다. 이 함수가 빈 결과를 반환하는 것과 부모가 컴포넌트 자체를 제거하는 것은 다릅니다.

조건문 문법과 0의 주의점은 React의 조건부 렌더링 설명에서도 확인할 수 있습니다.

다음 절에서는 리액트 컴포넌트의 또 다른 중요한 개념인 리스트 렌더링에 대해 학습하겠습니다.