구름톤 유니브☁️ 4기 FE 스터디

서론

이번 포스팅에서는 리액트에서Composition과 Inheritance를 비교하여 알아보고자 합니다.


본론

1. Composition

📖 Composition란?

여러 개의 컴포넌트를 조합해 새로운 컴포넌트를 만드는 기법으로
리액트는 컴포넌트를 가볍고 독립적인 단위로 보고, 이들을 조합해 UI를 구성하도록 설계되어 있다.

 

✅ Composition 방법

📌 Containment

// children prop을 사용한 FancyBorder 컴포넌트
function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}
// WelcomeDialog 컴포넌트
function WelcomeDialog(props) {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        어서오세요
      </h1>
      <p className="Dialog-message">
        우리 사이트에 방문하신 것을 환영합니다!
      </p>
    </FancyBorder>
  );
}

하위 컴포넌트를 children 프로퍼티로 포함하는 가장 기본적인 합성 방법이다.

FancyBorder안의 컴포넌트들이 children이라는 props로 전달되는 것이다.

// SplitPane 컴포넌트
function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}

// App 컴포넌트에서 SplitPane 사용 예시
function App(props) {
  return (
    <SplitPane
      left={
        <Contacts />
      }
      right={
        <Chat />
      }
    />
  );
}

만약 여러 개의 children 집합이 필요한 경우라면, 위 코드 처럼 left와 right라는 두 개의 props를 정의하여

각각 분리해서 렌더링하게 된다.

 

📌 Specialization

// Dialog 컴포넌트
function Dialog(props) {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        {props.title}
      </h1>
      <p className="Dialog-message">
        {props.message}
      </p>
    </FancyBorder>
  );
}

// Dialog를 특수화한 WelcomeDialog 컴포넌트
function WelcomeDialog(props) {
  return (
    <Dialog
      title="어서 오세요"
      message="우리 사이트에 방문하신 것을 환영합니다!"
    />
  );
}

범용 컴포넌트(개념)을 구별이 되도록 구체화하는 방법이다.

위 예시의 경우 WelcomeDialog(구체화된 개념)은 Dialog(범용적인 개념)의 특별한 케이스다.

기존 객체지향 개념에서는 상속을 사용하나 리액트에서는 Composition을 사용하여 Specialization을 한다.

 

✅ Containment와 Specialization 같이 사용하기

// 범용 Dialog 컴포넌트 (children을 사용하도록 확장)
function Dialog(props) {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        {props.title}
      </h1>
      <p className="Dialog-message">
        {props.message}
      </p>
      {props.children}
    </FancyBorder>
  );
}
// Dialog를 사용한 SignUpDialog 컴포넌트
function SignUpDialog(props) {
  const [nickname, setNickname] = useState('');

  const handleChange = (event) => {
    setNickname(event.target.value);
  };

  const handleSignUp = () => {
    alert(`어서 오세요, ${nickname}님!`);
  };

  return (
    <Dialog
      title="환성 탐사 프로그램"
      message="닉네임을 입력해 주세요."
    >
      <input
        value={nickname}
        onChange={handleChange}
      />
      <button onClick={handleSignUp}>
        가입하기
      </button>
    </Dialog>
  );
}

✅ Inheritance

📖 Inheritance(상속)이란?
상속, 다른 컴포넌트로부터 상속을 받아서 새로운 컴포넌트를 만드는 것

 

복잡한 컴포넌트를 쪼개서 여러개의 컴포넌트로 만들고 만든 컴포넌트를 조합해서 새로운 컴포넌트를 만들자!

라는 개념을 이해하고만 가자

 

📌 Composition vs Inheritance

상속은 객체지향 프로그래밍에서 클래스 기반 상속을 통해 기능을 확장하는 방식인데,

이는 리액트에서는 권장되지 않는 방식이고 리액트에서는 합성 방식을 권장한다.

왜 그럴까?

 

리액트에서 상속을 잘 사용하지 않는 이유

  • 복잡도 증가: 컴포넌트 간 의존성이 높아져 유지보수 어려움
  • 재사용성 증가: 자식 클래스에 의존적인 API 설계 필요
  • 컨텍스트 공유 어려움: props·state 조합이 복잡
class BaseButton extends React.Component {
  render() {
    return <button {...this.props} className="base-button" />;
  }
}

class IconButton extends BaseButton {
  render() {
    return (
      <BaseButton onClick={this.props.onClick}>
        <Icon name={this.props.icon} />
        {this.props.children}
      </BaseButton>
    );
  }
}

위 코드처럼 상속을 사용하면, IconButton이 BaseButton 구현에 종속적이어서 변화에 약하다는 문제점이 존재한다.


마무리

이번 글에서는 리액트에서 컴포넌트를 조합하는 기법인 Composition과 Inheritance에 대해 다뤄보았는데

전체적인 내용을 요약하자면

  Composition Inheritance
유연성 매우 높음 (런타임에 조합 가능) 낮음 (컴파일/설계 시 결정)
재사용성 범용 컴포넌트 + 슬롯 구조 클래스 계층 설계 필요
유지보수성 낮은 결합도 → 용이 높은 결합도 → 어려움
결론 ✅ 권장 ❌ 비권장

 

오늘도 읽어주셔서 감사합니다 🫡

'TIL > React' 카테고리의 다른 글

[React] Handling Events & Conditional Rendering & List and Keys  (1) 2025.05.06
[React] useMemo(), useCallback(), useRef()  (0) 2025.04.14
[React] JSX & Rendering Elements  (0) 2025.04.08
[React] React.js란?  (0) 2025.04.08

+ Recent posts