children — 태그 사이에 넣은 것
Article
5강에서 props로 값을 넘겼다. 그런데 이런 경우는 어떻게 할까.
카드 박스를 만드는데, 안에 들어갈 내용이 매번 다르다. 어떤 카드에는 글자만, 어떤 카드에는 이미지와 버튼이 들어간다.
내용을 props로 넘기려면 이렇게 된다.
<Card title="제목" text="본문" imageUrl="/a.png" buttonLabel="더보기" />
내용이 조금만 달라져도 props가 하나씩 늘어난다. **4강에서 말한 "props 다섯 개가 넘으면 잘못 자른 신호"**가 바로 이 상황이다.
태그 사이에 넣는다
HTML을 쓰듯 열고 닫는 태그 사이에 내용을 넣으면 된다.
<Card>
<h3>제목</h3>
<p>본문</p>
</Card>
그 내용은 children이라는 이름의 props로 전달된다.
function Card({ children }) {
return <div className="card">{children}</div>;
}
children은 React가 자동으로 채워주는 특별한 props다. 직접 넘긴 적이 없는데 들어 있다.
- <Card>…</Card>태그 사이에 쓴 것
- props.children자동으로 담긴다
- {children}원하는 자리에 그린다
껍데기와 알맹이를 나눈다
이 방식의 진짜 가치는 역할 분리다.
function Modal({ children, onClose }) {
return (
<div className="overlay" onClick={onClose}>
<div className="modal" onClick={e => e.stopPropagation()}>
<button className="close" onClick={onClose}>
✕
</button>
{children}
</div>
</div>
);
}
Modal은 어떻게 보일지만 안다. 배경을 어둡게 깔고, 가운데 띄우고, 닫기 버튼을 붙인다. 안에 무엇이 들어오는지는 모르고 관심도 없다.
쓰는 쪽은 무엇을 보여줄지만 정한다.
<Modal onClose={handleClose}>
<h2>정말 삭제하시겠습니까?</h2>
<button onClick={handleDelete}>삭제</button>
</Modal>
Modal을 고치지 않고도 온갖 내용을 넣을 수 있다.
props 로 전부
Card 가 내용을 안다
- 새 요소가 필요할 때마다 props 추가
hasImage,showButton같은 플래그가 늘어난다- Card 안이
if투성이가 된다 - 쓰는 쪽이 Card 를 고쳐야 한다
children
Card 는 껍데기만 안다
- 무엇을 넣든 Card 는 그대로
- 플래그가 필요 없다
- 쓰는 쪽이 자유롭게 조합
- Card 를 고칠 일이 없다
자리를 여러 개 만든다
children은 하나뿐이다. 헤더와 본문을 따로 받고 싶다면 어떻게 할까.
JSX 자체를 props로 넘긴다. 3강에서 "JSX는 값"이라고 했으니 가능한 일이다.
function Layout({ header, sidebar, children }) {
return (
<div className="layout">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}
function App() {
return (
<Layout header={<Logo />} sidebar={<Nav />}>
<ProductList />
</Layout>
);
}
header와 sidebar는 그냥 props인데 값이 JSX다. 다른 언어에서 슬롯(slot)이라 부르는 것과 같다.
- Layout
- header 자리
- sidebar 자리
- children 자리
props 를 계속 아래로 내려보내는 문제
컴포넌트가 깊어지면 이런 일이 생긴다.
function App() {
const user = { name: "지원" };
return <Page user={user} />;
}
function Page({ user }) {
return <Sidebar user={user} />;
}
function Sidebar({ user }) {
return <Profile user={user} />;
}
function Profile({ user }) {
return <p>{user.name}</p>;
}
Page와 Sidebar는 user를 쓰지도 않으면서 받아서 넘기기만 한다. 이걸 prop drilling(props 뚫고 내려가기)이라 부른다.
중간 컴포넌트들이 알 필요 없는 데이터를 알게 되고, user에 필드 하나만 추가해도 네 파일을 고쳐야 한다.
- Appuser 를 가진다
- Page안 쓰는데 받는다
- Sidebar안 쓰는데 받는다
- Profile여기서 쓴다
합성으로 한 겹 줄인다
Context를 배우기 전에도 방법이 있다. 깊이 내려보내는 대신, 완성된 조각을 넘긴다.
function App() {
const user = { name: "지원" };
return (
<Page sidebar={<Profile user={user} />} />
);
}
function Page({ sidebar }) {
return <Sidebar>{sidebar}</Sidebar>;
}
Profile을 App에서 만들어 통째로 넘겼다. 이제 Page와 Sidebar는 user가 무엇인지 모른다. 알 필요가 없어졌다.
children 을 다룰 때 조심할 것
children은 하나일 수도, 배열일 수도, 없을 수도 있다.
<Card>글자 하나</Card>
<Card><p>하나</p><p>둘</p></Card>
<Card></Card>
첫 번째는 문자열, 두 번째는 배열, 세 번째는 undefined다. 그래서 children.length 같은 걸 믿으면 안 된다.
없을 때 대비는 간단하다.
function Card({ children }) {
if (!children) return null;
return <div className="card">{children}</div>;
}
null을 반환하면 아무것도 그리지 않는다. 다음 강의의 주제다.
정리하면
- 태그 사이에 넣은 내용은
children이라는 props로 자동 전달된다. - 내용을 props로 다 넘기려 하면 플래그가 늘어난다. 껍데기는
children으로 자리만 내어준다. - 자리가 여러 개면 JSX를 props로 넘긴다. JSX는 값이다.
- 쓰지도 않는 props를 거쳐 내려보내는 것을 prop drilling이라 한다.
- 깊이가 얕다면 합성으로 해결된다. Context는 마지막 수단이다.
children의 타입을 검사하지 않는다. 자리만 내어주고 판단은 쓰는 쪽에 맡긴다.
다음 강의에서는 조건에 따라 다른 화면을 그리는 법을 정리한다. Part 2의 마지막이다.