🧩 동적 JSX 태그 (Dynamic JSX Tag)
동적 JSX 태그는 런타임 값에 따라 HTML 태그를 결정하는 패턴입니다.
React에서는 흔히 디자인 시스템이나 공통 컴포넌트에서 사용되며, TypeScript와 함께 사용할 경우 타입 추론 문제를 반드시 고려해야 합니다.
1️⃣ 동적 JSX 태그란?
JSX에서 태그 이름을 문자열이나 변수로 동적으로 결정하는 방식
const Tag = 'h1';
return <Tag>Title</Tag>;
이 패턴을 사용하면 다음과 같은 컴포넌트를 만들 수 있습니다.
<Title level={1}>Main Title</Title>
<Title level={3}>Section Title</Title>
👉 같은 컴포넌트로 다양한 HTML 태그를 표현할 수 있음
2️⃣ 왜 동적 태그가 필요한가?
🔹 문제 상황
제목 컴포넌트를 만들 때:
h1 ~ h5모두 필요- 스타일은 디자인 시스템에서 통제
- 시맨틱 태그는 유지해야 함
❌ 단순한 방식의 한계
<h1 className="...">Title</h1>
<h2 className="...">Title</h2>
- 중복 코드 증가
- 스타일 변경 시 전체 수정 필요
- CMS / 데이터 기반 UI에 부적합
👉 태그는 동적으로, 스타일은 중앙 관리할 필요가 있음
3️⃣ TypeScript에서 동적 태그
다음 코드는 타입 에러가 발생합니다.
const Tag = `h${level}`;
return <Tag>Title</Tag>;
❓ 왜 에러가 날까?
TypeScript는 위 코드를 이렇게 추론합니다.
const Tag: string;
하지만 JSX에서 태그로 사용할 수 있는 것은:
- HTML 태그 (
JSX.IntrinsicElements) - React 컴포넌트 타입
❌ string은 JSX 태그로 인식되지 않음
✅ 해결: as const로 타입 좁히기
const Tag = `h${level}` as const;
타입 추론 결과
"h1" | "h2" | "h3" | "h4" | "h5"
이 값들은 모두 JSX.IntrinsicElements에 포함된 유효한 HTML 태그 이름 입니다.
👉 TypeScript가 JSX 태그로 인식 가능 ✅
내부 동작 관점 정리
level값은 유니언 타입 (1 | 2 | 3 | 4 | 5)- 템플릿 리터럴로 태그 문자열 생성
as const로 문자열을 리터럴 유니언으로 고정- JSX가
IntrinsicElements로 인식 <Tag />사용 가능
4️⃣ 실전 예시
interface TitleProps {
level?: 1 | 2 | 3 | 4 | 5;
children: React.ReactNode;
className: string;
}
export default function Title({ level = 3, children, className }: TitleProps) {
const Tag = `h${level}` as const;
const style = {
1: "text-2xl font-bold",
2: "text-lg font-semibold",
3: "text-base font-semibold",
4: "text-sm font-medium",
5: "text-xs font-medium uppercase tracking-wide",
};
return (
<Tag className={`${style[level]} ${className}`}>
{children}
</Tag>
);
}
- 시맨틱 태그 유지
- 스타일 일관성 확보
- 타입 안정성 보장
✍️ 한 줄 정리
동적 JSX 태그는 런타임 값으로 태그를 선택하는 패턴이며,
TypeScript에서는as const를 통해 JSX가 인식 가능한 태그 타입으로 좁혀야 합니다.