본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Frontend

합성: 상속 대신 Composition

작성 2026. 10. 13.추가
목차5개 항목

합성(Composition) 은 여러 컴포넌트를 조합해 새로운 컴포넌트를 만드는 것이다. React는 컴포넌트 사이의 코드 재사용을 상속 대신 합성으로 해결한다. React 엘리먼트는 그냥 객체라서 원시 값·함수처럼 props로 넘길 수 있고, 이것이 합성의 바탕이다.

합성 패턴

패턴언제한 줄 코드
children (containment)안에 무엇이 올지 모르는 박스(레이아웃, Sidebar, Dialog)<Border>{children}</Border>
이름 있는 슬롯 props구멍이 여러 개 필요할 때<SplitPane left={<Contacts />} right={<Chat />} />
특수화 (specialization)범용 컴포넌트의 특수한 경우를 만들 때const WelcomeDialog = () => <Dialog title="Welcome" />
as prop (polymorphic)스타일·동작은 그대로 두고 그릴 요소만 바꿀 때<Button as="a" href="/home">Home</Button>
합성으로 drilling 줄이기중간 컴포넌트가 쓰지도 않는 props를 계속 넘길 때<PageLayout userLink={<Avatar user={user} />} />

예: Dialog

특수화와 children을 함께 쓰는 전형적인 예다. Dialog는 제목·메시지를 props로 받아 특수화되고, 안쪽 입력 영역은 children으로 받는다.

function Dialog({ title, message, children }) {
  return (
    <div className="dialog">
      <h2>{title}</h2>
      <p>{message}</p>
      {children}
    </div>
  )
}
 
function SignUpDialog() {
  const [login, setLogin] = useState('')
  return (
    <Dialog title="회원 가입" message="어떻게 불러 드릴까?">
      <input value={login} onChange={(e) => setLogin(e.target.value)} />
      <button onClick={() => signUp(login)}>가입</button>
    </Dialog>
  )
}

as prop 직접 만들기

as는 JSX 문법이 아니라 라이브러리 관례다(styled-components·react-bootstrap·Chakra UI는 as, MUI는 component). 받은 값을 대문자 변수에 담아 렌더링하면 된다.

type TextProps = { as?: React.ElementType; children: React.ReactNode }
 
function Text({ as: Component = 'p', children, ...rest }: TextProps) {
  return <Component className="text" {...rest}>{children}</Component>
}
 
<Text as="h2">제목</Text> // <h2 class="text">제목</h2>
  • 같은 스타일을 시맨틱에 맞는 다른 태그(h2, label, a)에 쓸 수 있다.
  • React.ElementType으로만 받으면 넘긴 요소의 props(as="a"의 href)는 검사하지 않는다. 요소에 맞춰 props까지 검사하는 제네릭 타입은 React와 TypeScript의 「제네릭 컴포넌트」에 있다.
  • 직접 만든 컴포넌트를 넘기면(<Form.Control as={StudentInput} />) 받은 props가 그대로 전달된다. 복잡한 입력 부분을 별도 파일로 빼는 데에도 쓴다.

합성으로 props drilling 줄이기

깊은 곳에 값을 전달하려고 Context로 바로 가기 전에 합성을 먼저 고려한다. 값을 쓰는 컴포넌트를 상위에서 만들어 엘리먼트로 내리면, 중간의 PageLayout·NavigationBar는 user를 알 필요가 없다. 대신 상위 컴포넌트가 복잡해진다.

정리

  • 내용을 모르는 박스는 children, 구멍이 여럿이면 이름 있는 props로 엘리먼트를 받는다.
  • 특수한 컴포넌트는 범용 컴포넌트를 렌더링하며 props로 구체화한다.
  • as(MUI는 component) prop은 스타일·동작을 유지한 채 그릴 요소만 바꾼다.
  • 상속은 쓰지 않는다. props와 합성만으로 모양과 동작을 명시적으로 바꿀 수 있고, UI가 아닌 로직 재사용은 모듈이나 커스텀 훅으로 한다.

관련 노트: 컴포넌트와 Props, 컴포넌트를 분리하는 기준과 방법, Context, 재사용 컴포넌트를 위한 리액트 패턴 5가지

원본 기록1

댓글

GitHub 계정으로 댓글을 남길 수 있어요.