프로그래밍/ReactJS

React 개발의 핵심: 컴포지션 vs. 상속, 언제 무엇을 사용할까?

shimdh 2025. 9. 23. 20:12
728x90

React 개발에서 강력하고 유지보수 가능한 애플리케이션을 구축하기 위한 핵심 원칙 중 하나는 바로 '컴포지션(Composition)''상속(Inheritance)' 의 개념을 깊이 이해하는 것입니다. 이 두 가지 디자인 패턴은 모두 재사용 가능한 컴포넌트를 생성하는 데 기여하지만, 그 접근 방식과 지향하는 목표에는 상당한 차이가 있습니다. 오늘은 이 두 가지 개념을 심층적으로 비교 분석하며, React 프로젝트에서 언제 어떤 방식을 선택해야 할지 명확한 가이드를 제시하고자 합니다.


컴포지션: 유연성과 재사용성의 열쇠 🔑

컴포지션은 '더 간단한 컴포넌트들을 조합하여 복잡한 컴포넌트를 구축하는' 디자인 원칙입니다. 객체 지향 프로그래밍의 상속처럼 기본 클래스를 확장하는 대신, 여러 컴포넌트들을 함께 구성하여 더 복잡한 구조를 형성하는 방식입니다. 이 방법은 특정 계층 구조에 얽매이지 않고 다양한 컴포넌트를 필요에 따라 자유롭게 조합하고 재사용할 수 있도록 해주기 때문에 React 개발에서 특히 유연성재사용성을 극대화합니다.

컴포지션의 실제 예시: 'AlertButton'

간단한 예를 통해 컴포지션의 강력함을 이해해봅시다. 'Button'과 'Alert'이라는 두 가지 독립적인 컴포언트가 있다고 가정해봅시다. 이들을 조합하여 'AlertButton'이라는 새로운 컴포넌트를 만들 수 있습니다.

function Button({ label, onClick }) {
  return <button onClick={onClick}>{label}</button>;
}

function Alert({ message }) {
  return <div>{message}</div>;
}

function AlertButton() {
  const handleClick = () => alert("Button clicked!");

  return (
    <>
      <Alert message="This is an alert!" />
      <Button label="Click Me" onClick={handleClick} />
    </>
  );
}

이 예시에서 컴포지션의 장점은 명확합니다.

  • 재사용성: 'Button'과 'Alert' 컴포넌트는 완전히 독립적이며, 'AlertButton' 외에도 애플리케이션의 다른 어떤 곳에서도 자유롭게 재사용될 수 있습니다. 이는 코드 중복을 최소화하고 유지보수를 용이하게 만듭니다.
  • 유연성: 버튼 클릭 시 발생하는 동작을 쉽게 변경하거나, 경고 메시지를 수정하는 등 컴포넌트의 특정 부분을 변경하더라도 애플리케이션의 다른 부분에 영향을 주지 않습니다. 이는 변화하는 요구사항에 민첩하게 대응할 수 있는 기반을 제공합니다.

상속: 계층적 관계와 제한된 유연성 ⛓️

반면, 상속은 '기존 클래스를 확장하여 새로운 클래스를 생성하는' 방식입니다. 이 접근 방식은 클래스 사이에 부모-자식 관계를 설정하며, 자식 클래스는 부모 클래스로부터 속성과 메서드를 물려받습니다. 상속은 특정 상황에서 코드 재사용을 단순화할 수 있지만, 애플리케이션이 성장함에 따라 관리하기 어려운 경직된 계층 구조로 이어질 위험이 있습니다.

상속의 사용 사례: 언제 상속을 고려할까?

React에서 컴포지션이 일반적으로 선호되는 방식이지만, 상속이 합리적으로 사용될 수 있는 시나리오도 존재합니다.

  1. 컴포넌트 간의 공유 동작: 여러 컴포넌트가 개별 함수나 훅(Hook) 내에 쉽게 캡슐화되지 않는 유사한 동작이나 상태 관리 로직을 필요로 할 때 유용할 수 있습니다. 예를 들어, 공통적인 로깅 또는 데이터 가져오기 로직을 여러 컴포넌트가 공유해야 하는 경우 BaseComponent를 상속받아 사용할 수 있습니다.

    class BaseComponent extends React.Component {
      commonMethod() {
        console.log("Common functionality");
      }
    }
    
    class ComponentA extends BaseComponent {
      render() {
        this.commonMethod();
        return <div>Component A</div>;
      }
    }
    
    class ComponentB extends BaseComponent {
      render() {
        this.commonMethod();
        return <div>Component B</div>;
      }
    }
  2. 레거시 코드베이스: 기존에 상속을 기반으로 구축된 오래된 코드베이스를 다룰 때는 상속을 사용하는 것이 불가피할 수 있습니다.

  3. 서드파티 라이브러리 확장: 객체 지향 프로그래밍(OOP) 원칙에 크게 의존하는 서드파티 라이브러리를 통합할 때, 해당 라이브러리에서 제공하는 클래스를 서브클래싱(subclassing)해야 할 수도 있습니다.


컴포지션 vs. 상속: 장점 및 단점 비교

두 가지 접근 방식의 핵심적인 차이를 비교표로 정리하면 다음과 같습니다.

측면 컴포지션 상속
재사용성 높음 보통
유연성 매우 높음 강한 결합으로 인해 낮음
복잡성 관리 용이함 복잡한 계층 구조로 이어질 수 있음
테스트 격리를 통한 테스트 용이함 의존성으로 인해 어려움

결론: React에서는 컴포지션이 왕! 👑

요약하자면, 컴포지션과 상속은 프로그래밍 패러다임에서 각자의 고유한 위치를 가지고 있습니다. 그러나 React 개발의 맥락에서는 유연성과 재사용성 측면에서의 명확한 장점 때문에 '컴포지션' 이 훨씬 선호되는 방법으로 두드러집니다.

상속된 구조에만 의존하기보다 더 작고 기능적인 단위들을 조합함으로써, 개발자는 요구사항이 변화함에 따라 시간이 지남에 따라 더 잘 적응하는 더 깔끔한 아키텍처를 만들 수 있습니다. 컴포지션을 강조하는 것은 관심사 간의 명확한 분리를 유지하는 데 도움이 되며, 이는 궁극적으로 더 관리하기 쉽고 확장성이 뛰어난 코드베이스로 이어집니다.

새로운 React 프로젝트를 시작하거나 기존 프로젝트를 리팩토링할 때, '상속보다는 컴포지션' 이라는 원칙을 마음속에 새기고 유연하고 견고한 애플리케이션을 구축하시길 바랍니다.

728x90