앞선 글에서 우리는 Compound Pattern이 합성을 “역할 단위로 쪼개기”로,
Headless 컴포넌트가 합성을 “로직과 UI의 분리”로 확장한 모습을 살펴봤다.
그렇다면 이번엔 이런 질문을 던져보자.
“컴포넌트가 표현하는 태그 자체도 바뀔 수 있어야 하지 않을까?”
이 질문은 단순한 편의성 문제가 아니라, 소프트웨어 철학에서 오래전부터 내려온 Polymorphism(다형성)을 UI 설계에 적용하는 시도다.
즉, “하나의 인터페이스, 다양한 구현”이라는 객체지향/함수형의 원리를 React 컴포넌트에 이식하는 것이다.
그 결과물이 바로 Polymorphic 컴포넌트이며, 보통 as prop이라는 작은 문법적 장치로 구현된다.
초창기 React 디자인 시스템에서 흔히 있었던 문제는 태그가 고정돼 있다는 것이었다.
중복된 컴포넌트
같은 “버튼 역할”인데 태그가 달라진다는 이유로 별도 컴포넌트를 늘려야 했다.
→ 코드 중복 + 유지보수 지옥.
접근성과 시맨틱 문제
<Title>공지사항</Title>이 내부적으로 <h2>라면, <h1>이 필요할 때 변종을 찍어내야 했다.
라우팅/SEO 요구
<Button>을 <Link>처럼 써야 할 때, 단순 스타일 공유가 막혀 또 다른 컴포넌트를 만들어야 했다.
➡️ 결과: “하나의 역할 = 여러 컴포넌트”라는 비효율.
📖 참고 | Kent C. Dodds – Compound vs Polymorphic Components
여기서 프로그래밍 철학의 오래된 원리가 등장한다.
Polymorphism(다형성)
“같은 인터페이스로 다양한 구현을 다룰 수 있다.”
UI에도 똑같이 적용하면 된다.
Button이라는 역할(인터페이스)은 유지하되,
실제 DOM 태그나 컴포넌트 구현은 상황에 따라 교체한다.
이 철학을 React에 녹여낸 것이 바로 as prop이다.
<button>, 때로는 <a>, 때로는 <Link>.즉, Polymorphic 컴포넌트는 UI 계층에서 역할(Role)과 표현(Presentation)을 분리하는 방식이다.
React의 합성 철학을 계승하면서도, HTML의 시맨틱 유연성과 디자인 시스템의 일관성을 동시에 잡는다.
📖 참고 | Chakra UI Docs – The as prop
React 초창기 (2013~2015)
대부분 컴포넌트는 특정 태그에 하드코딩.
React Router의 등장
버튼처럼 보이지만 링크로 동작해야 하는 요구 증가 → <Button as={Link}>.
디자인 시스템 확산 (2018~)
Chakra UI, Stitches, Radix 같은 라이브러리가 as prop을 도입.
➡️ as prop은 단순 편의 기능이 아니라, UI 다형성에 대한 업계의 공식 답변이 됐다.
📖 참고 | Radix UI – Primitives, Stitches Polymorphic API
구현 핵심
const Button = ({
as: Comp = "button", // 기본은 <button>
variant = "primary", // 스타일 API
...rest
}) => {
const base = "px-3 py-2 rounded";
const styles = variant === "primary" ? "bg-blue-600 text-white" : "bg-gray-200";
return <Comp className={`${base} ${styles}`} {...rest} />;
};
실제 사용 예시
<Button variant="primary">저장</Button>
<Button as="a" href="https://react.dev" target="_blank">React 공식 문서</Button>
<Button as={Link} to="/dashboard">대시보드로 이동</Button>
구현 핵심
const Typography = ({
as: Comp = "p",
variant = "body",
className = "",
...rest
}) => {
const base = "font-sans";
const styles = {
h1: "text-3xl font-bold",
h2: "text-2xl font-semibold",
body: "text-base",
caption: "text-sm text-gray-500"
};
return (
<Comp className={`${base} ${styles[variant]} ${className}`} {...rest} />
);
};
실제 사용 예시
<Typography as="h1" variant="h1">React 블로그</Typography>
<Typography as="p" variant="body">본문 단락</Typography>
<Typography as="span" variant="caption">업데이트: 2025-09-29</Typography>
📖 참고 | Headless UI Docs
중복 제거와 유지보수 단순화
<Button>, <LinkButton>, <IconButton>… 역할마다 새 컴포넌트를 만들었다.as prop 하나로 커버. 디자인 시스템 업데이트 시 단일 Button만 고치면 전역 반영됨.시맨틱 마크업 + 접근성 보장
<Typography as="h1"> vs <Typography as="h2">.디자인 시스템의 확장성
Button, Typography 같은 공통 컴포넌트만 배우면 전체 시스템 활용 가능.비즈니스 요구 대응 속도
as="a"만 바꾸면 해결.📖 참고 | Stitches Polymorphic API
Chakra UI
as prop 지원.Button as="a", Box as="section", Text as="span"처럼 사용.Radix UI
<DialogTrigger asChild> → <Button>을 바로 자식으로 넣어도 동작.Stitches
styled('button')으로 만든 컴포넌트에 as="a" 전달 가능.실무 시나리오
<Button> 하나만으로 로그인 버튼, 외부 링크, 앱 내 네비게이션을 모두 커버.<Button> 문서만 보고도 다양한 상황에서 활용 가능.📖 참고 | Chakra UI Docs, Radix UI Docs, Stitches Docs
Polymorphic 컴포넌트는 단순히 as prop이라는 문법적 장치가 아니다.
이는 소프트웨어 공학의 고전적 원리인 다형성(Polymorphism)을 UI 설계에 끌어온 시도다.
철학적 관점
<button>, <a>, <Link>라는 표현(Expression)은 자유롭게 교체 가능.실무적 관점
역사적 관점
as prop을 확산시키면서, Polymorphism은 UI 개발의 사실상 표준으로 자리 잡았다.즉, Polymorphic 컴포넌트는 “하나의 인터페이스, 다양한 표현”이라는 고전적 원리를 현대 UI 설계에 적용한 것이다.
Compound Pattern이 구조적 합성, Headless가 로직과 UI의 분리를 보여줬다면, Polymorphic은 표현 계층의 유연성을 실현한다.
이는 React 개발자가 단순히 더 적은 코드를 쓰는 수준을 넘어, 더 깊은 철학을 코드에 구현하는 방법이다.
📖 참고 |
as prop