React Fragment: 불필요한 래퍼 없이 요소를 묶는 방법
한눈에 보기
React Fragment는 여러 자식 요소를 추가 DOM 노드 없이 하나로 묶어 반환하게 해 주는 문법 요소다. 하나의 루트 요소 제약을 래퍼 없이 해결해 DOM 깊이와 CSS 레이아웃 문제를 예방하는 원리와 key 사용법을 정리한다.
최종 업데이트 2026.09.17
React Fragment(리액트 프래그먼트)는 여러 자식 요소를 추가 DOM 노드 없이 하나로 묶어 반환하게 해 주는 React의 문법 요소다. JSX에서 컴포넌트는 반드시 하나의 루트 요소만 반환해야 하는데, Fragment를 사용하면 의미 없는 래퍼(wrapper) 요소를 만들지 않고도 이 제약을 지킬 수 있다. 그 결과 DOM 트리가 불필요하게 깊어지는 것을 막고 CSS 레이아웃이 깨지는 문제도 예방할 수 있다.
하나의 루트 요소 제약과 Fragment의 등장
JSX는 브라우저가 그대로 이해하는 문법이 아니라 컴파일 단계에서 함수 호출로 변환되는 표현식이다. 자바스크립트 함수가 값 하나를 반환하듯이 컴포넌트도 표현식 하나, 곧 루트 요소 하나를 반환해야 한다. 형제 요소 여러 개를 그대로 나란히 반환하려고 하면 문법 오류가 발생한다.
Fragment가 없던 시절에는 이 제약을 피하려고 습관적으로 div로 감싸는 코드가 많았다. 그러나 이런 래퍼는 화면 구조상 아무 의미가 없는데도 실제 DOM에 그대로 렌더링되어 트리를 깊게 만들고 스타일과 접근성에 부작용을 일으켰다. React 16에서 Fragment가 도입되면서 렌더링 결과에는 나타나지 않는 논리적인 묶음이 가능해졌다.
래퍼 div가 일으키는 대표적인 문제
- 플렉스박스(Flexbox)나 그리드(Grid) 레이아웃에서 부모와 자식 사이에 끼어들어 정렬 규칙을 깨뜨린다.
- table 요소 안에는 tr, td처럼 정해진 자식만 올 수 있으므로 div가 들어가면 유효하지 않은 HTML이 된다.
- DOM 노드 수가 늘어나 메모리를 더 쓰게 되고 개발자 도구에서 구조를 파악하기도 어려워진다.
기본 사용법과 축약 문법
Fragment는 두 가지 방식으로 쓸 수 있다. 하나는 react 패키지에서 가져온 Fragment 컴포넌트를 명시적으로 사용하는 방식이고, 다른 하나는 빈 태그처럼 보이는 축약 문법이다. 두 방식 모두 렌더링 결과는 완전히 같다.
function Profile() {
return (
<>
<h3>소개</h3>
<p>프론트엔드 개발자입니다.</p>
</>
);
}
축약 문법은 짧고 읽기 쉽지만 어떤 속성도 붙일 수 없다는 제약이 있다. 속성이 필요한 순간, 특히 key를 지정해야 하는 순간에는 명시적인 Fragment 컴포넌트를 사용해야 한다.
key가 필요한 반복 렌더링
배열을 순회하며 형제 요소 쌍을 반복 렌더링할 때가 Fragment에 key를 지정하는 대표적인 상황이다. 예를 들어 용어와 설명을 dt, dd 쌍으로 출력하는 경우, 각 쌍을 div로 감싸면 dl의 콘텐츠 모델이 깨지므로 Fragment로 묶고 key를 부여한다.
import { Fragment } from 'react';
function Glossary({ items }) {
return (
<dl>
{items.map((item) => (
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
))}
</dl>
);
}
key는 React가 재조정(reconciliation) 과정에서 각 항목을 식별하는 근거가 되므로 Fragment라고 해서 예외가 아니다. 현재 Fragment에 지정할 수 있는 속성은 key가 유일하다.
래퍼 요소와 Fragment 비교
| 구분 | 래퍼 div | Fragment |
|---|---|---|
| 실제 DOM 노드 | 생성된다 | 생성되지 않는다 |
| CSS 레이아웃 영향 | 부모와 자식 사이에 끼어든다 | 영향이 없다 |
| 스타일과 이벤트 지정 | 가능하다 | 불가능하다 |
| HTML 유효성 | table, dl 내부에서 깨질 수 있다 | 문제가 없다 |
| 적합한 경우 | 실제 묶음 컨테이너가 필요할 때 | 문법 제약만 해결하면 될 때 |
주의할 점
- Fragment는 DOM에 존재하지 않으므로 스타일, 클래스, 이벤트 핸들러를 붙일 수 없다. 시각적인 컨테이너가 필요하다면 의미에 맞는 실제 요소를 쓰는 것이 맞다.
- 축약 문법에는 key를 포함해 어떤 속성도 줄 수 없으므로 반복 렌더링에서는 반드시 명시적 Fragment를 사용한다.
- 모든 래퍼를 기계적으로 없애는 것이 목적이 아니다. 접근성 그룹화나 스타일 적용처럼 실제 역할이 있는 요소는 남겨야 한다.
- 불필요한 래퍼를 줄이면 접근성 트리도 단순해져서 스크린 리더 사용자에게 전달되는 구조가 명확해진다는 부수 효과도 있다.
면접에서 이렇게 물어본다
- JSX에서 컴포넌트가 루트 요소를 하나만 반환해야 하는 이유를 컴파일 관점에서 설명해 보라.
- 래퍼 div 대신 Fragment를 써야 하는 구체적인 상황을 CSS 레이아웃과 HTML 유효성 측면에서 들어 보라.
- 축약 문법과 명시적 Fragment의 차이는 무엇이고, key가 필요할 때는 어느 쪽을 써야 하는가.
- Fragment가 실제 DOM과 재조정 과정에서 각각 어떻게 다루어지는지 설명해 보라.
React / Vue에서 같이 읽기
- React Hook: 함수 컴포넌트의 상태와 로직 재사용React Hook은 함수 컴포넌트에서 상태 관리와 사이드 이펙트 처리를 가능하게 하는 함수들의 모음이다. 훅의 등장 배경, 호출 순서 기반 동작 원리와 규칙, 커스텀 훅을 통한 로직 재사용까지 면접 관점에서 정리한다.
- React와 Spring Boot 연동 구조: CORS, 프록시, 배포 전략React SPA와 Spring Boot REST API 서버를 분리해 구성하는 아키텍처를 다룬다. 개발 환경의 CORS와 프록시 설정, 정적 빌드 산출물의 통합 서빙과 분리 서빙 전략, 새로고침 404 해결까지 원리 중심으로 설명한다.
- Vue 3와 Firebase 이메일 인증: 토큰 기반 인증 흐름Firebase Authentication은 이메일 인증을 서버 구현 없이 제공하는 관리형 인증 서비스다. ID 토큰과 리프레시 토큰의 흐름, Vue 3 컴포저블 통합, 라우터 가드와 세션 유지, 보안 주의점을 원리 중심으로 정리한다.
- Vue 3와 Firebase 소셜 로그인: OAuth 위임과 연동 원리소셜 로그인의 바탕인 OAuth 2.0 권한 위임 원리와 Firebase가 소셜 제공자를 중개하는 인증 흐름을 다룬다. 팝업과 리다이렉트 방식 비교, Vue 3 구현, 계정 충돌 처리까지 정리한다.
이 개념, 내 코드에서는 어떻게 물어볼까요
데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.
공고 넣고 시작하기