
리액트에서 Composition 은 여러 개의 컴포넌트를 합쳐서 새로운 컴포넌트를 만드는 것을 말합니다.
복잡한 컴포넌트를 쪼개서 여러 개의 컴포넌트를 만들고, 만든 컴포넌트들을 조합해서 새로운 컴포넌트를 만들자 !
여러 개의 컴포넌트들을 어떻게 조합하느냐에 따라 사용기법이 나뉩니다.
하위 컴포넌트를 포함하는 형태의 합성 방법을 말합니다. Sidebar나 Dialog 같은 Box형태의 컴포넌트는 자신의 하위 컴포넌트를 미리 알 수 없습니다. 해당 컴포넌트를 사용하는 개발자가 어떤 것을 넣느냐에 따라 하위 컴포넌트가 달라지기 때문입니다.
Containtment 을 사용하려면 children이라는 prop을 사용해서 조합하면 됩니다.
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
리액트에서는 하위 컴포넌트를 props.children으로 하나로 모아서 제공해 줍니다.
function WelcomeDialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-titile">
어서오세요
</h1>
<p className="Dialog-message">
우리 사이트에 방문하신 것을 환영합니다!
</p>
</FancyBorder>
);
}
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App(props) {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
}
/>
);
}
범용적인 개념을 구별이 되게 구체화 하는 것을 Specialization 이라고 합니다. 리액트에서는 합성(Composition)을 사용하여 Specialization 을 구현합니다.
범용적으로 쓸 수 있는 컴포넌트를 만들어 놓고, 이를 특수화 시켜서 컴포넌트를 사용하는 Composition 방법입니다.
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog(props) {
return (
<Dialog
title="어서오세요"
message="환영합니다!"
/>
);
}
Dialog 컴포넌트를 사용하는 WelcomeDialog 컴포넌트입니다. Dialog 컴포넌트에는 title과 message라는 두 가지 props를 가지고 있는데, 다이얼로그에 나오는 제목과 메시지를 의미합니다.
제목과 메시지를 어떻게 사용하느냐에 따라서 경고 다이얼로그가 될 수도 있고, 인사 다이얼로그가 될 수도 있습니다.
Containtment를 위해 props.children을 사용하고, Specialization을 위해 직접 정의한 props를 사용해봅시다.
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children} // Containtment를 위해 추가
</FancyBorder>
);
}
Containtment를 위해 props.children를 추가했습니다. 이를 통해 하위 컴포넌트가 다이얼로그 하단에 렌더링됩니다.
function SignupDialog(props) {
const [nickname, setNickname] = useState('');
const handleChange = (event) => {
alert(`어서오세요, ${nickname}님!`);
}
return (
<Dialog
title="게시판"
message="닉네임을 입력해주세요.">
<input
value={nickname}
onChange={handleChange}/>
<button onClick={handleSignup}>
가입하기
</button>
</Dialog>
);
}
Dialog 컴포넌트를 사용하는 SignupDialog 컴포넌트입니다. Specialization을 위한 props인 title, message에 값을 넣어주고 있으며, 사용자로부터 닉네임을 입력받고 가입을 하도록 유도하기 위해 input, button 태그가 있습니다. 두 개의 태그는 모두 props.children으로 전달되어 다이얼로그에 표시됩니다.
Composition와 대비되는 개념으로, 상속이라는 뜻을 가집니다. 리액트에서는 다른 컴포넌트로부터 상속받아서 새로운 컴포넌트를 만드는 것을 고려해볼 수 있습니다.
하지만 리액트를 개발한 메타에서 수천 개의 리액트 컴포넌트를 사용했지만, 상속을 사용하여 컴포넌트를 만드는 것을 추천할 사용 사례를 찾지 못했다고 합니다. 리액트에서는 상속보단 컴포지션을 사용해서 개발하는 것이 더 좋은 방법입니다.