리액트(React) 이해 기초 2 - Conditional Rendering

리액트로 컴포넌트를 만들다 보면, 빈번하게 사용 되는 패턴 중에 하나는 조건에 따라 다른 컴포넌트를 렌더링 하는 것이다. 예를 들어, 데이터가 로딩 중 일때, 데이터가 없을 때 등등 다양한 상황에서 조건식에 따라 다른 컴포넌트를 렌더링 해야 할 때가 있다. 기본적인 자바스크립트에서 제공하는 조건문(if/else, switch/case 등등)을 사용 하여 렌더링 하는 것 뿐만 아니라 IIFE, HOC, 객체(object) 등을 사용 하여 조건에 맞는 컴포넌트를 렌더링을 할 수도 있다. 이같은 다양한 방법에 대해 알아보자.

자바스크립트 조건문과 연산자를 사용한 방법

if / else

가장 기본적인 방법이다. 컴포넌트 prop 값에 따라 리턴하는 리액트 컴포넌트를 분기 처리한다.
const ConditionalComponent = ({ loading, data }) => { if (loading) { return <LoadingComponent /> } return <h1>{data}</h1> };

switch / case

조건이 다양하다면 switch / case를 활용 할 수도 있다.
const Dialog({ type, ...rest }) => { switch(type) { case 'share': return <ShareDialog {...rest} />; case 'edit': return <EditDialog {...rest} />; case 'delete': return <DeleteDialog {...rest} />; default: return null; } };

&&, || (Logical AND, Logical OR)

논리 연산자(Logical Operator)를 사용하여 조건부 처리 방법.
const LogicalAnd = ({ username }) => { return ( <h1> {username && <span>username</span>} </h1> ); }; const LogicalOr = ({ username }) => { return ( <h1> {username || 'no username'} </h1> ); };

condition ? expr1 : expr2 (Ternary Operator)

삼항 연산자를 사용하여 분기처리 하는 방법.
const TernaryComponent = ({ loading, data }) => { return {loading ? <LoadingComponent /> : <h1>data</h1>}; };

HOC(Higher Order Component)를 이용한 조건부 렌더링

조건부 렌더링을 담당하는 HOC를 만들어 실제 컴포넌트에서는 조건을 신경쓰지 않고(관심사 분리) 렌더링을 하도록 하면, 컴포넌트 자체의 목적도 더 분명하고 재사용성도 증가 시킬 수 있다.
const Success = () => { return <h1>good</h1>; }; const False = () => <h1>condition fail</h1>; const withCondition = (condition, FalsyComponent) => { if (condition()) { return WrappedComponent => WrappedComponent; } return () => FalsyComponent; }; const SuccessWithCondition = withCondition(() => false, False)(Success);

즉시 실행 함수 IIFE(Immediately Invoked Function Expression)를 이용한 방법

IIFE를 활용해서 분기 처리를 할 수도 있다.
const sampleComponent = () => { return ( <div> { (() => { if (flag && flag2 && !flag3) { if (flag4) { return <p>Blah</p> } else if (flag5) { return <p>Meh</p> } else { return <p>Herp</p> } } else { return <p>Derp</p> } })() } </div> ) };

자바스크립트 객체를 활용한 방법

prop의 값이 여러개라면 if / else나 switch / case statement를 사용하기 보다는 객체(object)를 하나 만들어 key(prop value) / value(component)로 만들어 사용하면 코드도 훨씬 깔끔하고 가독성을 높일 수 있다.
import { SHARE_DIALOG, CONFIRM_EDIT_DIALOG, CONFIRM_DELETE_DIALOG, } from './constants/ui'; import ShareDialog from './ShareDialog'; import ConfirmEditDialog from './ConfirmEditDialog'; import ConfirmDeleteDialog from './ConfirmDeleteDialog'; const DIALOG_COMPONENTS_MAP = { [SHARE_DIALOG]: ShareDialog, [CONFIRM_EDIT_DIALOG]: ConfirmEditDialog, [CONFIRM_DELETE_DIALOG]: ConfirmDeleteDialog, }; const GlobalDialog = ({ open, onClose, dialogType, dialogProps }) => { if (!dialogType || !DIALOG_COMPONENTS_MAP[dialogType]) { return null; } const SpecificDialog = DIALOG_COMPONENTS_MAP[dialogType]; return <SpecificDialog {...dialogProps} open={open} onClose={onClose} />; };

외부 라이브러리를 활용하는 방법

recompose의 branch HOC를 활용한 방법

리액트 유틸리티 라이브러리인 recompose의 branch를 사용하여 분기 처리를 해주는 HOC 함수를 사용 할 수도 있다.
const spinnerWhileLoading = isLoading => branch( isLoading, // boolean(true or false) 값을 리턴하는 함수 renderComponent(Spinner) // 조건식이 true일 때 렌더링 ) const enhance = spinnerWhileLoading( props => !(props.title && props.author && props.content) ) // 조건식이 false (isLoading === false)일 때 렌더링 const Post = enhance(({ title, author, content }) => <article> <h1>{title}</h1> <h2>By {author.name}</h2> <div>{content}</div> </article> )
개인적으로 선호하는 방식은 아니라서 여기서 샘플소스를 다루진 않겠지만, https://github.com/AlexGilleran/jsx-control-statements나 https://github.com/romac/react-if 등의 외부 라이브러리를 사용하는 방법도 있으니 참고하자.

리액트에서 조건에 따라 다른 컴포넌트를 렌더링 하는 방법에 대해서 알아봤다. 사실, 리액트에서 제공하는 기능이 아닌 자바스크립트에 내장되어 있는 기능/특징을 활용한 방법이라서 특별해 보이진 않을 것이다. 보통 자바스크립트 템플릿 엔진의 경우 자체적으로 조건부 렌더링을 할 수 있는 특수한 문법을 제공하는게 일반적이지만 리액트의 경우 자바스크립트의 순수 기능을 적극 활용 할 수 있도록 설계되어 있다. 이것이 처음 리액트를 접하는 분들에겐 헷갈릴 수 있는 부분이기도 하니 이점을 염두하고 리액트 컴포넌트 개발을 하도록 하자.

더 읽을 거리: