[리액트 공식문서 읽기] DESCRIBING THE UI - Conditional Rendering

JaeHong Jeong·2023년 8월 19일
post-thumbnail

Overview

컴포넌트에 조건에 따라 다른 것을 표시하고 싶은 경우가 종종있다. 리액트에서는 if 문, && , ?: 와 같은 JavaScript 구문을 사용하여 JSX를 조건부 렌더링을 할 수 있다.

Conditionally returning JSX

패킹되었거나 안되었거나 표시할 수 있는 여러개의 Item 을 렌더링하는 PackingList 컴포넌트가 있다고 가정해보자

function Item({ name, isPacked }) {
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

일부 Item 컴포넌트에는 false 대신 true 로 설정된 isPacked 속성이 있다. isPacked={true} 인 경우 장된 항목에 확인 표시(✔)를 추가하려고 한다.

다음과 같이 [if/else statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else)로 작성할 수 있다.

if (isPacked) {
  return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

만약 isPackedtrue 이면 이 코드는 다른 JSX트리를 반환한다. 변경으로 일부 항목의 끝에 확인 표시가 나타난다.

function Item({ name, isPacked }) {
  if (isPacked) {
    return <li className="item">{name}</li>;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

두 경우 모두 반환되는 항목을 편집하고 결과가 어떻게 변경되는지 확인해라.

JavaScript ifreturn 을 사용하여 분기 로직을 만드는 방법에 주목해라. 리액트에서 제어 흐름(조건과 같은)은 JavaScript에 의해 처리된다.

Conditionally returning nothing with null

어떤 상황에서는 아무것도 렌더링하지 않고 싶을 수 있다. 예를 들어 packed item을 보여주고 싶지 않을 수 있다. 컴포넌트는 어떤 것을 반환해줘야한다. 이 때 null을 반환할 수 있다.

if (isPacked) {
  return null;
}
return <li className="item">{name}</li>;

isPacked 이 참이면 컴포넌트는 null, 아무것도 반환하지 않는다. 그렇지 않으면 JSX를 반환하여 렌더링한다.

function Item({ name, isPacked }) {
  if (isPacked) {
    return null;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

실제로 구성 요소에서 null을 반환하는 것은 렌더링하려는 개발자를 놀라게 할 수 있기 때문에 일반적이지 않다. 자주, 부모 컴포넌트의 JSX에서 컴포넌트를 조건부로 포함거나 제외한다. 방법은 다음과 같다.

Conditionally including JSX

그 전 예제에서는 컴포넌트에서 반환할 JSX트리(있는 경우)를 제어했다. 렌더 출력에서 일부 중복을 이미 발견했을 수 있다.

<li className="item">{name}</li>
<li className="item">{name}</li>

두 코드는 매우 비슷하다. 둘 다 <li className="item">...</li> 를 반환한다.

if (isPacked) {
  return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

이 중복이 유해하지는 않지만 코드를 유지 관리하기 어렵게 만들 수 있다. className 을 변경하려면 어떻게 해야할까? 코드 두 군데에서 해야한다. 이러한 상황에서는 조건부로 약간의 JSX를 포함하여 코드를 더 DRY하게 만들 수 있다.

Conditional (ternary) operator (? :)

JavaScript에는 조건식을 작성하기 위한 간단한 구문(조건 연산자 또는 "삼항 연산자")이 있다.

if (isPacked) {
  return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

이렇게 대신

return (
  <li className="item">
    {isPacked ? name + ' ✔' : name}
  </li>
);

"isPacked가 참이면 (?) name + ' ✔', 그렇지 않으면 (:) name"으로 읽을 수 있다.

💡 DEEP DIVE

이 두 예제는 완전 동일할까?

객체 지향 프로그래밍을 배웠다면 위의 두 예제 중 하나가 <li>의 두 가지 "인스턴스"를 생성할 수 있기 때문에 미묘하게 다르다고 생각할 수 있다. 하지만 ****JSX 요소는 내부 상태를 보유하지 않고 실제 DOM 노드가 아니기 때문에 "인스턴스"가 아니다. 그냥 청사진과 같이 가벼운 description이다. 이 두가지 예제는 완전히 동일하다. Preserving and Resetting State에서 어떻게 작동하는지 자세히 설명되어있다.

이제 완성된 항목의 텍스트를 <del>과 같은 다른 HTML 태그로 감싸서 지우고 싶다고 가정해보자. 각각의 경우에 더 많은 JSX를 더 쉽게 중첩할 수 있도록 더 많은 새 줄과 괄호를 추가할 수 있다.

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {isPacked ? (
        <del>
          {name + ' ✔'}
        </del>
      ) : (
        name
      )}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

이 스타일은 간단한 조건에서 잘 작동하지만 적당히 사용해야한다. 중첩된 조건부 마크업이 너무 많아 컴포넌트가 지저분해지면 정리를 위해 하위 컴포넌트를 추출하는 것이 좋다. 리액트에서 마크업은 코드의 일부이므로 변수 및 함수와 같은 도구를 사용하여 복잡한 표현식을 정리할 수 있다.

Logical AND operator (&&)

또 다른 일반적인 단축키는 JavaScript logical AND (&&) operator이다. 리액트 컴포넌트에서 조건이 참일 때 일부 JSX를 렌더링하거나 그렇지 않으면 아무 것도 렌더링하지 않을 때 종종 쓴다. && 를 사용하면 isPacked 가 참인 경우에만 조건부로 확인 표시를 렌더링할 수 있다.

return (
  <li className="item">
    {name} {isPacked && '✔'}
  </li>
);

isPacked 이면 (&&)는 체크 표시를 렌더링하고, 그렇지 않으면 아무것도 렌더링 하지 않음” 으로 읽을 수 있다.

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✔'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

JavaScript && expression 은 왼쪽(조건)이 참이면 오른쪽(체크 표시)의 값을 반환한다. 하지만 조건이 거짓이면 저체 표현식이 거짓이 된다. 리액트는 falsenull 이나 undefined 와 마찬가지로 JSX트리의 ‘구멍’으로 간주하고 그 자리에 아무 것도 렌더링하지 않는다.

💡 Pitfall

&&의 왼쪽에 숫자를 넣지 마세요.

조건을 테스트하기 위해 JavaScript는 왼쪽을 자동으로 불리언으로 변환한다. 그러나 왼쪽이 0 이면 전체 표현식이 해당 값 (0)을 가져오고 리액트는 아무것도 렌더링하지 않는 것 대신 0 을 렌더링 한다..
예를 들어, 일반적인 실수는 messageCount && <p>New messages</p> 와 같이 코드를 작성하는 것이다. messageCount 가 0일 때 아무것도 렌더링 하지 않는다고 가정하기 쉽지만 실제로는 0 자체를 렌더링 한다.
왼쪽을 messageCount > 0 && <p>New messages</p> 처럼 불리언 값으로 바꿔 고친다

Conditionally assigning JSX to a variable

단축키가 일반 코드를 작성하는 데 방해가 되면 if 문과 변수를 사용해 봐라. [let](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let) 으로 선언된 변수를 재할당할 수 있으므로 표시하려는 기본 콘텐츠인 이름을 제공하여 시작한다.

let itemContent = name;

if 문을 사용하여 isPackedtrue 인 경우 JSX 표현식을 itemContent 에 재할당한다.

if (isPacked) {
  itemContent = name + " ✔";
}

중괄호는 'JavaScript로 들어가는 창'을 연다. 반환된 JSX 트리에 중괄호가 있는 변수를 포함하고 이전에 계산된 표현식을 JSX 내부에 중첩한다.

<li className="item">
  {itemContent}
</li>

이 스타일은 가장 장황하지만 가장 유연하다.

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = (
      <del>
        {name + " ✔"}
      </del>
    );
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

JavaScript에 익숙하지 않은 경우 이러한 다양한 스타일이 처음에는 압도적으로 보일 수 있다. 하지만 이거를 배우면 리액트 컴포넌트 뿐만 아니라 모든 JavaScript 코드를 읽고 작성하는 데 도움이 된다. 처음에 선호하는 것을 선택한 다음 다른 것이 어떻게 작동하는지 잊어버린 경우 이 참조를 다시 참조해라.

Recap

  • 리액트에서는 JavaScript로 분기 로직을 제어한다.
  • if 문을 사용하여 JSX 표현식을 조건부로 반환할 수 있다.
  • 일부 JSX를 변수에 조건부로 저장한 다음 중괄호를 사용하여 다른 JSX에 포함할 수 있다.
  • JSX에서 {cond ? <A /> : <B />} 는 ‘조건이 충족 되면 <A /> 를, 그렇지 않으면 <B /> 를 렌더링’을 의미한다.
  • In JSX, {cond && <A />} means “if cond, render <A />, otherwise nothing”.
  • JSX에서 {cond && <A />} 는 ‘조건이 있으면 <A /> 를 렌더링하고 그렇지 않으면 아무것도 렌더링하지 않음’을 의미한다.
  • 단축키는 일반적이지만 평범한 것을 선호하면 사용할 필요가 없다.
profile
반갑습니다.

2개의 댓글

comment-user-thumbnail
2023년 8월 19일

많은 것을 배웠습니다, 감사합니다.

1개의 답글