웹페이지에서 특정 상황에 따라 UI를 보여주거나 숨겨야 할 때가 있습니다. 기존에는 다음과 같은 방법으로 조건부 렌더링을 처리하고 있었습니다.
const { is_editable } = article;
// 1. if문
if(!is_editable) return (
<div>
<UpperUI />
<NonEditableUI />
<LowerUI />
</div>
) else return (
<div>
<UpperUI />
<EditableUI />
<LowerUI />
</div>
);
// 2. display: 'none' (tailwind에서 'hidden')
return (
<div>
<UpperUI />
<NonEditableUI className={`${is_editable && 'hidden'}`} />
<EditableUI className={`${!is_editable && 'hidden'}`} />
<LowerUI />
</div>
);
// 3. 삼항 연산자
return (
<div>
<UpperUI />
{ !is_editable ?
<NonEditableUI className={`${is_editable && 'hidden'}`} />
: <EditableUI className={`${!is_editable && 'hidden'}`} />
}
<LowerUI />
</div>
);
// 4. 논리 연산자
return (
<div>
<UpperUI />
{ !is_editable && <NonEditableUI /> }
{ is_editalbe && <EditableUI /> }
<LowerUI />
</div>
);
위 방법을 상황에 따라 선택해서 조건부 렌더링을 구현했지만, 코드 가독성이 떨어지고 내부 로직 흐름이 그대로 드러난다는 문제가 있었습니다. 간단한 코드에서는 괜찮더라도 복잡한 로직을 가진 코드를 유지보수하거나 타인이 볼 필요가 있을 경우 어려움이 생길 수 있습니다. 선언형 프로그래밍을 지향하는 리액트를 사용한다면 그에 맞게 선언적으로 렌더링을 처리할 방법이 필요했습니다.
조건부 렌더링을 선언적으로 처리하기 위해 ConditionalRender 컴포넌트를 추가했습니다.
type ConditionalRenderProps = {
condition: boolean | null | undefined;
children: ReactNode;
};
export default function ConditionalRender({ condition, children }: ConditionalRenderProps) {
if (condition) return children;
return null;
}
렌더링 조건 condition을 prop으로 전달받아 해당 조건을 만족할 경우 자식 요소를 렌더링하고, 그렇지 않다면 렌더링하지 않는 매우 단순한 구조를 가진 컴포넌트입니다. 이 컴포넌트는 아래와 같이 사용할 수 있습니다.
const { is_editable } = article;
return (
<div>
<UpperUI />
<ConditionalRender condition={!is_editalbe}>
<NonEditableUI />
</ConditionalRender>
<ConditionalRender condition={is_editalbe}>
<EditableUI />
</ConditionalRender>
<LowerUI />
</div>
);
이처럼 코드를 선언적으로 작성할 경우 코드 가독성이 좋아지고 추후 유지보수에도 용이한 장점이 있습니다. 하지만 선언형 프로그래밍이 모든 로직을 처리하는 것에는 한계가 있기 때문에 상황에 맞춰 명령형과 선언형 중 적절한 방식을 선택할 필요가 있습니다. 예를 들어, 반응형 카드 모양 제어에는 여전히 기존 방법 중 display: none을 활용하고 있습니다.
// PlanCard.tsx
return (
<div className="size-full">
{/* 카드형; 모바일에서 카드형 고정 */}
<div className={`h-full ${shape === 'bar' && 'md:hidden'}`}>
<PlanCardShapeCard />
</div>
{/* 바형 */}
<div className={`h-full max-md:hidden ${shape !== 'bar' && 'md:hidden'}`}>
<PlanCardShapeBar />
</div>
</div>
);