f8b21af4be
10_Wiki/Topics 대규모 정리: - 오류 캡처/미완성 stub 문서 227개 제거 - 교차폴더 중복 43클러스터 병합 (63파일 → redirect) - 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건 - 카테고리 MOC 6개 신규 생성 - Graph 섹션 미해결 related-keyword 링크 10,058건 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
196 lines
5.3 KiB
Markdown
196 lines
5.3 KiB
Markdown
---
|
|
id: wiki-2026-0508-jsx
|
|
title: JSX
|
|
category: 10_Wiki/Topics
|
|
status: verified
|
|
canonical_id: self
|
|
aliases: [JavaScript XML, React JSX, TSX]
|
|
duplicate_of: none
|
|
source_trust_level: A
|
|
confidence_score: 0.95
|
|
verification_status: applied
|
|
tags: [jsx, react, typescript, transpilation]
|
|
raw_sources: []
|
|
last_reinforced: 2026-05-10
|
|
github_commit: pending
|
|
tech_stack:
|
|
language: typescript
|
|
framework: react
|
|
---
|
|
|
|
# JSX
|
|
|
|
## 매 한 줄
|
|
> **"매 syntactic sugar over `createElement` — XML-like JS expression"**. React 의 발명, 매 declarative UI 의 핵심. 매 `<div>x</div>` → `jsx('div', null, 'x')` 로 transpile. 2026 default 는 **automatic runtime** (`react/jsx-runtime`) — 매 `import React` 의 불필요.
|
|
|
|
## 매 핵심
|
|
|
|
### 매 transpile model
|
|
- **Classic runtime** (legacy): `<div/>` → `React.createElement('div', null)` — 매 file 마다 `import React` 필요.
|
|
- **Automatic runtime** (2020+, default): `<div/>` → `_jsx('div', null)`, runtime 의 자동 import.
|
|
- **Preserve**: TS `--jsx preserve` — Babel 등 다른 tool 에 위임.
|
|
|
|
### 매 element types
|
|
- **Lowercase** (`div`, `span`): 매 string tag, host component.
|
|
- **Uppercase** (`Foo`): 매 reference, component identifier.
|
|
- **Member access** (`Lib.Btn`): 매 namespaced component.
|
|
- **Fragment** (`<>...</>`): 매 wrapper-less group.
|
|
|
|
### 매 응용
|
|
1. React / Preact / Solid / Qwik 의 매 component definition.
|
|
2. MDX — Markdown + JSX.
|
|
3. JSX as data (Astro, hyperscript variant).
|
|
|
|
## 💻 패턴
|
|
|
|
### Automatic runtime (2026 default)
|
|
```tsx
|
|
// tsconfig.json
|
|
{
|
|
"compilerOptions": {
|
|
"jsx": "react-jsx", // 매 automatic runtime
|
|
"jsxImportSource": "react" // or "preact", "@emotion/react"
|
|
}
|
|
}
|
|
|
|
// Component.tsx — 매 import React 의 X
|
|
export function Hello({ name }: { name: string }) {
|
|
return <h1>Hello, {name}</h1>;
|
|
}
|
|
```
|
|
|
|
### Conditional rendering
|
|
```tsx
|
|
function Status({ user }: { user?: User }) {
|
|
if (!user) return null;
|
|
return (
|
|
<>
|
|
{user.isAdmin && <AdminBadge />}
|
|
{user.posts.length > 0
|
|
? <PostList posts={user.posts} />
|
|
: <EmptyState />}
|
|
</>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Children pattern
|
|
```tsx
|
|
type Props = { title: string; children: React.ReactNode };
|
|
function Card({ title, children }: Props) {
|
|
return (
|
|
<section className="card">
|
|
<h2>{title}</h2>
|
|
<div className="body">{children}</div>
|
|
</section>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Render prop / function-as-children
|
|
```tsx
|
|
type DataLoaderProps<T> = {
|
|
url: string;
|
|
children: (data: T | undefined, loading: boolean) => React.ReactNode;
|
|
};
|
|
function DataLoader<T>({ url, children }: DataLoaderProps<T>) {
|
|
const [data, setData] = useState<T>();
|
|
const [loading, setLoading] = useState(true);
|
|
useEffect(() => {
|
|
fetch(url).then(r => r.json()).then(d => { setData(d); setLoading(false); });
|
|
}, [url]);
|
|
return <>{children(data, loading)}</>;
|
|
}
|
|
```
|
|
|
|
### Polymorphic component (`as` prop)
|
|
```tsx
|
|
type AsProp<C extends React.ElementType> = { as?: C };
|
|
type PolymorphicProps<C extends React.ElementType> =
|
|
AsProp<C> & React.ComponentPropsWithoutRef<C>;
|
|
|
|
function Box<C extends React.ElementType = 'div'>({
|
|
as, ...rest
|
|
}: PolymorphicProps<C>) {
|
|
const Comp = as ?? 'div';
|
|
return <Comp {...rest} />;
|
|
}
|
|
|
|
<Box as="a" href="/x" /> // 매 anchor props 의 typed
|
|
<Box as={MyButton} onClick={...} />
|
|
```
|
|
|
|
### JSX spread + override
|
|
```tsx
|
|
function PrimaryButton(props: React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
|
return <button {...props} className={`btn-primary ${props.className ?? ''}`} />;
|
|
}
|
|
```
|
|
|
|
### Fragment + key
|
|
```tsx
|
|
function List({ items }: { items: Item[] }) {
|
|
return (
|
|
<dl>
|
|
{items.map(it => (
|
|
<React.Fragment key={it.id}>
|
|
<dt>{it.term}</dt>
|
|
<dd>{it.def}</dd>
|
|
</React.Fragment>
|
|
))}
|
|
</dl>
|
|
);
|
|
}
|
|
```
|
|
|
|
### TypeScript — JSX.IntrinsicElements override
|
|
```tsx
|
|
declare global {
|
|
namespace JSX {
|
|
interface IntrinsicElements {
|
|
'my-web-component': React.DetailedHTMLProps<
|
|
React.HTMLAttributes<HTMLElement> & { value?: string },
|
|
HTMLElement
|
|
>;
|
|
}
|
|
}
|
|
}
|
|
<my-web-component value="x" />
|
|
```
|
|
|
|
## 매 결정 기준
|
|
| 상황 | Approach |
|
|
|---|---|
|
|
| 새 React project | `"jsx": "react-jsx"` (automatic) |
|
|
| Preact / Solid | `jsxImportSource` 적절 설정 |
|
|
| Babel 사용 | `"jsx": "preserve"` + babel preset |
|
|
| Web component 통합 | `JSX.IntrinsicElements` 확장 |
|
|
| 매 server component | `"jsx": "react-jsx"` (RSC 호환) |
|
|
|
|
**기본값**: automatic runtime + TypeScript strict.
|
|
|
|
## 🔗 Graph
|
|
- 부모: [[React]] · [[TypeScript]]
|
|
- 변형: [[TSX]] · [[MDX]]
|
|
- Adjacent: [[SWC]] · [[esbuild]]
|
|
|
|
## 🤖 LLM 활용
|
|
**언제**: React/Preact/Solid component 작성, TS JSX 설정 디버깅, polymorphic API 설계.
|
|
**언제 X**: 매 plain HTML template (no transpile needed), 매 Vue SFC (`<template>` 별 syntax).
|
|
|
|
## ❌ 안티패턴
|
|
- **`React` import 누락 (classic runtime)**: 매 `react-jsx` 로 migrate.
|
|
- **lowercase component name**: `<myButton/>` 매 host element 로 처리됨 — 매 PascalCase 강제.
|
|
- **`key` 누락 in list**: reconciliation 비용 폭증.
|
|
- **inline function child 의 남발**: render 마다 새 reference — memoized child re-render 유발.
|
|
|
|
## 🧪 검증 / 중복
|
|
- Verified (react.dev JSX docs, TS Handbook JSX, React 19 RC notes).
|
|
- 신뢰도 A.
|
|
|
|
## 🕓 Changelog
|
|
| 날짜 | 변경 |
|
|
|---|---|
|
|
| 2026-05-08 | Phase 1 |
|
|
| 2026-05-10 | Manual cleanup — JSX automatic runtime + polymorphic patterns 정리 |
|