동일한 depth에 있는 자식요소가 2개이상일때 부모요소가 이를 감싸주지 않으면 에러가 뜬다.
처음엔 div로 감싸서 해결했는데 나중에 빈 요소 <> Fragment를 사용해도 된다 라는 피드백을 받았다.
뭐가 다른거지 싶어서 찾아보게 되었다.
React에서 JSX는 항상 하나의 부모 요소만 반환해야 한다는 제약이 있다.
실무에서는 이 제약 때문에 의도치 않게 div,span 같은 불필요한 태그가 DOM에 생기는 경우가 많기에
이런 상황에서 깔끔한 구조와 정확한 렌더링을 위한 도구가 Fragment이다.
기본 사용법
<Fragment></Fragment>
축약형
<></>
둘다 같은 요소를 의미하지만 상황에 따라서 축약형은 사용이 불가능한 경우가 있다.
data.map(item => (
<key={item.value}> // ❌ 에러: 축약형은 key를 가질 수 없음
<div>{item.label}</div>
<div>{item.value}</div>
</>
));
import { Fragment } from "react"
data.map(item => (
<Fragment key={item.value}> // ❌ 에러: 축약형은 key를 가질 수 없음
<div>{item.label}</div>
<div>{item.value}</div>
</>
));
React 17부터 도입된 "Automatic JSX Runtime" 덕분에
JSX 내부에서 사용하는 React.Fragment, React.createElement 등을 컴파일러에서 자동으로 import해줘서
우리가 직접 import React from 'react' 하지 않아도 된다.
대신 <Fragment></Fragment> 를 사용한 경우엔 해당 요소가 어디서 온건지를 확실히 해야하기 때문에
import { Fragment } from "react" 가 필요하다.
React 17에 도입된 Automatic JSX Runtime 관련 내용도 한번 정리해봐야겠다.
| Fragment | div | |
|---|---|---|
| DOM에 렌더되는가? | ❌ 렌더되지 않음 (완전 사라짐) | ✅ 렌더됨 (실제 DOM에 생김) |
| 역할 | JSX 내부에서 그룹만 하기 | JSX를 그룹하고 실제 DOM 요소 역할도 함 |
| 필요성 | 불필요한 DOM 트리 방지 | 레이아웃이나 스타일 조정 시 유용 |
| key 사용 | <Fragment key="x"> 가능 (단, 명시적으로 import 필요) | <div key="x"> 가능 |
실제 자주 사용하는 경우가 조건부 렌더링인 경우이다.
const Header = ({ id }: { id:string }) => {
return (
<>
{id ? (
<>
<UserMenu />
<LogoutButton />
</>
) : (
<>
<LoginButton />
<SignupButton />
</>
)}
</>
);
}
id에 따라서 컴포넌트 내용을 다르게 넘기고 싶은데 jsx는 하나의 요소만 리턴해야하기 해서 최상단을 항상 불필요한 요소가 필요하다.
하지만 Fragment를 사용하여 실제 DOM에 그려진 코드를 까보면 내가 원하는 요소만 들어간것을 확인할 수 있다
<UserMenu />
<LogoutButton />
만약 Fragment대신 div로 요소를 감쌌다면 아래처럼 불필요한 요소가 추가된다.
<div>
<div>
<UserMenu />
<LogoutButton />
</div>
</div>
단순히 여러 요소를 그룹화해야 하는 경우엔 DOM에 영향을 주지 않는 Fragment를 사용하고
레이아웃을 잡거나 스타일이 필요한 경우엔 div같은 실제 DOM에 영향을 주는 요소를 사용해야 한다.