본문 바로가기

Develop/Frontend 가이드

[React] 조건부 렌더링 패턴 Conditional Rendering Pattern

반응형

Conditional Rendering
조건부 렌더링 패턴

조건부 렌더링 패턴 Conditional Rendering Pattern

리액트의 조건부 렌더링 문서에서 조건에 따라 랜더링할 컴포넌트를 선택하는 방법을 소개하고 있지만 추가적으로 알아두면 유용한 best practices 패턴이 몇 가지 더 있어서, 하나씩 소개드리도록 하겠습니다.

소개드릴 패턴 목록입니다.

  • if else 패턴
  • : ? 패턴
  • && 패턴
  • switch case 패턴
  • enum 패턴
  • HOC 패턴

if else 패턴

리액트 공식 문서에서 소개하는 가장 기본적인 패턴입니다.

function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;
    if (isLoggedIn) {
        return <UserGreeting />;
    }
    else {
        return <GeustGreeting />;
    }
}

if 문 조건에 따라 UserGreeting 컴포넌트를 렌더링할지 GuestGreeting 컴포넌트를 렌더링할지 결정합니다.

if else 패턴은 컴포넌트가 조건에 따라 렌더링할 내용 전체가 결정되는게 특징인 패턴입니다. 이 패턴은 컴포넌트의 일부분만 선택적으로 렌더링하기 적합하지 않습니다.

예를 들어, 아래와 같이 if else 패턴을 작성하면 동작하지 않습니다.

function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;

    return (
        <React.Fragment>
        {
            // 'Unexpected token' error 발생
            if (isLoggedIn) {
                return <UserGreeting />;
            }
            else {
                return <GeustGreeting />;
            }
        }
        </React.Fragment>
    );
}

if else 패턴을 사용하면서 일부분만 렌더링하려면 즉시 실행 함수 (IIFE) 를 사용해야 가능합니다.

function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;

    return (
        <React.Fragment>
        {
            // 즉시 실행 함수 IIFE
            (() => {
                    if (isLoggedIn) {
                        return <UserGreeting />;
                    }
                    else {
                        return <GeustGreeting />;
                    }
                }
            )();
        }
        </React.Fragment>
    );
}

즉시 실행 함수를 사용한 if else 패턴은 가독성이 떨어지는 문제가 심각해, 사용을 권장하지 않습니다. 그리고 if else 패턴으로 렌더링 대상 전체가 변경된다면, 새로운 컴포넌트를 작성하는게 더 적합하기 때문에 if else 패턴은 효과적으로 활용하기 어려운 패턴입니다.

: ? 패턴

if else 패턴과 마찬가지로 리액트 공식 문서에서 인라인 If-Else 라고 소개하는 패턴입니다.

function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;
    return (
        <React.Fragment>
            // 삼항 연산자 ' (조건) ? (참일 경우) : (거짓일 경우) ' 로 부분 조건부 렌더링 구현
            { isLoggedIn ? ( <UserGreeting /> ) : ( <GeustGreeting /> ) }
        </React.Fragment>
    );
}

: ? 패턴은 if else 패턴의 치명적인 단점인 부분 렌더링이 가능하도록 개선한 패턴입니다. : ? 패턴은 JavaScript 문법으로 간결하게 사용이 가능한게 장점인 패턴입니다. 하지만 : ? 패턴 또한 단점이 있는데 컴포넌트를 렌더링할지 말지 결정할 때 불필요하게 null 을 반환하는 코드를 작성해야 합니다.

function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;
    return (
        <React.Fragment>
            // 로그인했을 경우만 '<HelloMessage />' 를 렌더링
            { isLoggedIn ? ( <HelloMessage /> ) : null }
        </React.Fragment>
    );
}

null 을 반환한다고 해서 심각한 성능 저하가 발생하지는 않기 때문에 그대로 사용해도 되지만, && 패턴으로 개선이 가능합니다.

&& 패턴

&& 패턴은 : ? 패턴과 함께 자주 사용되는 조건부 렌더링 패턴입니다. (선행 조건) && (후행 조건) 논리 연산자는 선행 조건이 참이어야만 후행 조건을 평가하고 후행 조건을 평가한 결과를 반환하는 특징이 있습니다.

(true && 'Hello');
// 결과 = 'Hello'

&& 패턴은 이러한 특징을 적극적으로 활용한 패턴입니다.

function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;
    return (
        <React.Fragment>
            { isLoggedIn && ( <HelloMessage /> ) }
        </React.Fragment>
    );
}

: ? 패턴이 null 을 반환하게 되는 경우를 보완하기 때문에, : ? 패턴과 && 패턴을 같이 사용하면 조건부 렌더링을 효과적으로 구현할 수 있습니다.

switch case 패턴

switch case 패턴은 리액트 공식 문서에서 알려주지 않은 패턴으로, if else 패턴과 유사한 패턴입니다.

function Notification(props) {
    const status = props.status;

    switch (status) {
        case 'info':
            return <Info />;
        case 'warning':
            return <Warning />;
        case 'error':
            return <Error />;
        default:
            return null;
  }
}

switch case 패턴은 아래와 같이 TypeScript 와 함께 사용할 때 훌륭한 시너지를 발휘하는 패턴입니다.

enum Status {
    Info = "Info", 
    Warning = "Warning",
    Error = "Error"
}

type NotificationProps = {
    text: string;
    status: Status;
};

function Notification(props : NotificationProps) {
      const status = props.status;

    switch (status) {
      case Status.Info:
        return <Info />;
      case Status.Warning:
        return <Warning />;
      case Status.Error:
        return <Error />;
      default:
        return null;
    }
}

switch case 패턴도 if else 패턴과 마찬가지로 부분 렌더링을 구현하기 까다롭기도 하고, TypeScript 없이 JavaScript 만으로는 활용도가 떨어지기 때문에 자주 사용되는 패턴은 아닙니다.

enum 패턴

enum 패턴은 switch case 패턴을 개선한 형태로 더 간결하게 조건부 렌더링을 구현할 수 있는 패턴입니다.

const Status = {
  info: <Info />,
  warning: <Warning />,
  error: <Error />,
};

function Notification(props) {
    const status = props.status;

    return (
        <div>
        { Status[status] }
        </div>
    );
}

또한 enum 패턴은 switch case 패턴과 달리 부분 렌더링도 훌륭하게 소화할 수 있습니다. 만약 enum 패턴에 변수를 적용하고 싶다면 아래와 같이 작성하면 변수도 적용할 수 있습니다.

const getStatus = (text) => ({
  info: <Info text={ text } />,
  warning: <Warning text={ text } />,
  error: <Error text={ text } />,
});

function Notification(props) {
    const status = props.status;
    const text = props.text;

    return (
        <div>
        { getStatus(text)[status] }
        </div>
    );
}

하지만 enum 패턴에서 변수를 전달할 정도라면 컴포넌트로 분리해야 하는 코드일 가능성이 높으니, enum 패턴을 사용하기 전 컴포넌트로 분리할지 여부를 고민해봐야 하는 패턴입니다.

HOC 패턴

HOC 패턴은 고차 컴포넌트 (Higher-Order Components) 를 활용한 패턴입니다. HOC 패턴은 언어의 문법을 활용하는 다른 패턴과 달리 리액트의 특징을 활용하는 패턴입니다. 고차 컴포넌트는 JavaScript 언어의 고차 함수 (Higher-Order Functions) 와 비슷하게 컴포넌트를 변수로 전달받아 컴포넌트를 반환하는 함수입니다.

HOC 패턴은 아래와 같이 정의합니다.

// `withConditionComponent` 를 반환하는 고차 컴포넌트
function withCondition(Component) {

    return function withConditionComponent({ condition, ...props }) {
        return (
            <React.Fragment>
            // `condition` 에 따라 `Component` 렌더링 여부 결정
            { condition && <Component { ...props } /> }
            </React.Fragment>
        );
    }
}

HOC 패턴은 아래와 같이 사용합니다.

const NotificationWithCondition = withCondition(Notification);

function App(props) {

    const isNotificationVisible = props.isNotificationVisible;

    retrun (
        <div>
            <NotificationWithCondition condition={isNotificationVisible} />
        </div>
    );
}

HOC 패턴은 고차 컴포넌트를 사용했기 때문에 재활용성이 높은 패턴입니다. 하지만 고차 컴포넌트를 구현하려면 React 를 어느 정도 이해하고 있어야 해서 제대로 활용하려면 React 개발 경험을 필요로 하는 패턴입니다.

반응형