[React] 가독성을 높인 조건부렌더링

이은진·2021년 2월 3일
9

React Study

목록 보기
9/10
post-custom-banner

컴포넌트 함수 내부에서 특정 값에 따라 선택적으로 렌더링하는 것을 조건부 렌더링이라고 한다. 가독성을 높이는 조건부 렌더링 방법과 특징을 알아보고 어떻게 가장 최적화된 코드를 작성할 수 있는지 알아보았다.

1. if else / 삼항연산자 패턴

function GreetingA({ isLogin, name }) { 
  if (isLogin) { 
    return <p>{`${name}님 안녕하세요.`}</p>; 
  } else { 
    return <p>권한이 없습니다.</p>; 
  }
}

function GreetingB({ isLogin, name }) {
  return <p>{isLogin ? `${name}님 안녕하세요.` : '권한이 없습니다.'}</p>
}

리액트 공식 문서에서 소개하는 가장 기본적인 패턴이다. GreetingA 함수의 경우 if문 조건인 로그인 여부에 따라, 어떤 요소를 렌더링할지 결정한다. 이 패턴은 컴포넌트의 일부분만 선택적으로 렌더링하기 적합하지 않다. GreetingB 함수는 삼항연산자를 활용해서 조건부렌더링을 한다. if else 패턴의 단점인 부분 렌더링이 가능하도록 개선한 패턴이다. GreetingB의 코드가 더 간결하고 p 태그가 한 번만 등장해서 GreetingA보다 좋은 것 같지만, 매번 그렇지는 않다. 컴포넌트를 다른 조건에 따라 다르게 렌더링하는 게 아니라, 컴포넌트 렌더링 여부 자체를 결정할 때는 불필요하게 null을 반환하는 코드를 작성해야 한다.

function GreetingB({ isLogin, name }) {
  return (
    <React.Fragment>
    	{isLogin ? <p>`${name}님 안녕하세요.`</p> : null }
    </React.Fragment>
  )
}

null을 반환한다고 해서 심각한 성능 저하가 발생하지는 않기 때문에 그대로 사용해도 되지만 이 부분은 && 패턴으로 더 효율적으로 개선할 수 있다.

2. && 패턴

조건부 렌더링을 구현할 때는 삼항 연산자가 유용한 경우도 있지만, 대부분 && 연산자가 가독성이 더 좋다. && 패턴은 (선행 조건) && (후행 조건) 논리 연산자는 선행 조건이 참이어야만 후행 조건을 평가하고, 후행 조건을 평가한 결과를 반환한다.

function Greeting({ isLogin, name, cash }) {
  return (
    <div>
      저희 사이트에 방문해주셔서 감사합니다.
      {
        isLogin && (
          <div>
            <p>{name}님 안녕하세요.</p>
            <p>현재 보유하신 금액은 {cash}원입니다.</p>
          </div>
        )
      }
    </div>
  )
}

컴포넌트의 일부분만 선택적으로 렌더링할 수 있을 뿐만 아니라, 코드의 끝에 null을 생략해도 되기 때문에 가독성이 좋아진다.

실제 프로젝트에서는 여러 개의 조건에 따라 다양한 상태를 렌더링해야 하는 경우가 많은데 이런 경우 && 패턴이 의미있게 사용될 수 있다. 다음 코드는 메인페이지에서 보여줄 문구를 조건에 따라 &&패턴으로 표현한 결과다. 조건은 다음과 같다.

  • 이벤트 기간에는 개인정보를 생략한 채, 이벤트 문구를 보여준다.
  • 이벤트 기간이 아닌 경우, 로그인을 했더라도 캐시가 10만이 넘으면 해킹한 사람이므로 개인정보를 보여주지 않는다.
function Greeting() {
  return (
    <div>
      저희 사이트에 방문해주셔서 감사합니다. 
      {isEvent && (
          <div>
            <p>오늘의 이벤트를 놓치지 마세요.</p>
            <button onClick={onClickEvent}>이벤트 참여하기</button>
          </div>
      )}
      {!isEvent &&
        isLogin &&
        cash <= 100000 && (
          <div>
            <p>{name}님 안녕하세요.</p>
            <p>현재 보유하신 금액은 {cash}원입니다.</p>
          </div>
      )}
    </div>
  )
}

이벤트 여부 또는 사용자의 상태에 따라 조건이 복잡한데도 코드가 크게 두 그룹이라는 점, 그리고 각 그룹의 조건들이 한눈에 잘 드러난다.

주의1) 변수가 null 또는 undefined일 때

&& 패턴을 사용할 때는 주의할 점이 있다. 변수가 null 또는 undefined일 때를 꼭 고려해야 하는 경우도 있다는 점을 생각해야 한다. 변수가 숫자 타입인 경우, 0은 false고, 문자열 타입인 경우 빈 문자열도 false다. 다음 코드는 && 연산자를 잘못 사용한 예시다.

// 잘못된 예시
<div>
  {cash && <p>{cash}원 보유 중</p>}
  {memo && <p>{200 - memo.length}자 입력 가능</p>}
</div>

&& 연산자를 사용할 때 내가 자주 실수하는 내용이다. cash가 숫자 형태인데 0이면 '0원 보유 중'이 출력되지 않을 것이다. 또 memo에 아무 내용이 없으면 '200자 입력 가능'이라고 출력되어야 하는데 출력되지 않는다. 위 경우에는 명확하게 undefined, null이 아닌 경우라고 표현해야 한다. 다음은 의도한 대로 동작하도록 고친 코드다.

// 올바른 예시
<div>
  {cash !== null && <p>{cash}원 보유 중</p>}
  {memo !== null && <p>{200 - memo.length}자 입력 가능</p>}
</div>

cash !== null 은 cash가 undefined가 아니고 null도 아니면 참이 된다.

주의2) 변수가 배열인 경우

변수가 배열인 경우에는 기본값으로 빈 배열을 넣어 주는 게 좋다. 배열의 기본값이 빈 배열이면 조건부 렌더링을 할 때마다 &&을 입력하지 않아도 되기 때문에 편하게 map 함수를 사용할 수 있다.

<div>
  {
    students && students.map((student, idx) => {
      <div key={idx}>{student}</div>
    })
  }
</div>

students 배열의 기본값으로 빈 배열을 설정해주지 않는다면 students && 또는 students.length > 0 && 를 입력해주어야 map 돌리는 대상이 undefined라는 오류창을 피할 수 있다.

<div>
  {
    products.map((product, idx) => {
      <div key={idx}>{product}</div>
    })
  }
</div>

product 배열의 기본값을 빈 배열로 설정하면 코드가 간결해진다.

3. Switch case

const RealTimeUsagePage = () => {
  const paramsId = useParams().id
  const [ params, setParams ] = useState(paramsId)
  useEffect(() => setParams(paramsId), [paramsId])

  const choosePage = () => {
    switch (params) {
      case 'bytime':
        return <RealTimeUsageByTime />
      case 'bydate':
        return <RealTimeUsageByDate />
      case 'bymonth':
        return <RealTimeUsageByMonth />
      case 'byyear':
        return <RealTimeUsageByYear />
      default:
        return <RealTimeUsageByTime />
    }
  }

  return (
    <React.Fragment>{choosePage()}</React.Fragment>
  );
}

현재 진행중인 프로젝트에서 조금 변경한 코드를 가져왔다. 선택한 메뉴에 따라 전체 페이지 컴포넌트를 다르게 보여주기 위해 useParams()에 switch case를 사용했다. Switch case는 if else문과 비슷한 방법으로 코드를 작성할 수 있고, 마찬가지로 부분 렌더링이 어렵다. 이 방법은 TypeScript와 작성되었을 때 시너지가 난다고 한다.

profile
빵굽는 프론트엔드 개발자
post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 4월 29일

&& 은 약간 hacky 하고, truthy/falsey 한 두 경우를 모두 커버해야 하는 상황이 되면 다시 a ? b : c 형태로 돌아오기 때문에, 저는 처음부터 삼항 연산자를 쓰는것을 선호합니다.

undefined, null 모두 falsey하기 때문에, && 다음의 내용은 실행되지 않습니다.
cash ?? 0 > 0 && ... 그리고 memo?.length ?? 0 > 0 && ... 가 원래 의도하신 바가 아닌가 싶어요.

배열의 경우도 array && array.map(...) 을 array?.map(...) 으로 간소화할 수 있겠습니다.

답글 달기