프로그래밍/ReactJS

React 개발의 핵심: 상속보다 컴포지션을 선택해야 하는 이유

shimdh 2025. 9. 23. 14:05
728x90

ReactJS 애플리케이션을 개발할 때, 많은 개발자들이 전통적인 객체 지향 프로그래밍의 상속 개념과 컴포넌트 기반의 컴포지션 접근 방식 사이에서 고민하곤 합니다. 하지만 React 생태계에서는 명확하게 컴포지션(Composition) 이 상속(Inheritance)보다 훨씬 선호됩니다. 왜 그럴까요? 🤔

이 글에서는 React 개발에서 컴포지션이 상속을 대체하는 강력한 이유와 그 실용적인 이점을 깊이 있게 탐구해보고자 합니다.


상속과 컴포지션, 그 차이는?

소프트웨어 개발, 특히 객체 지향 프로그래밍에서 상속은 기존 클래스의 기능을 확장하는 전통적인 방법으로 사용되어 왔습니다. 부모 클래스의 속성과 메서드를 자식 클래스가 물려받아 재사용성과 기능 확장을 도모하는 것이죠.

반면에 컴포지션은 '가지고 있다(has-a)' 관계를 기반으로 합니다. 간단한 컴포넌트들을 결합하여 더욱 복잡한 컴포넌트를 만드는 방식입니다. 이는 마치 레고 블록을 조립하여 다양한 형태의 구조물을 만드는 것과 유사합니다. 🧱 각 블록은 독립적인 기능을 수행하며, 이들을 조합하여 훨씬 더 크고 유연한 구조를 만들 수 있습니다.


728x90

React에서 컴포지션이 선호되는 이유

React는 UI를 컴포넌트 단위로 분리하고 관리하는 데 최적화된 라이브러리입니다. 이러한 철학에 상속보다는 컴포지션이 훨씬 더 잘 부합합니다.

1. 유연성과 재사용성의 극대화

컴포지션의 가장 큰 장점은 바로 유연성재사용성입니다. 특정 스타일이 필요한 버튼 컴포넌트 예시를 생각해봅시다. 상속을 사용한다면 PrimaryButton, SecondaryButton과 같이 각 스타일별로 새로운 하위 클래스를 생성해야 합니다. 이는 새로운 버튼 유형이 추가될 때마다 코드의 양이 증가하고 불필요한 상속 계층을 만듭니다.

하지만 컴포지션을 사용하면 하나의 Button 컴포넌트가 props를 통해 다양한 스타일을 유연하게 처리할 수 있습니다.

const Button = ({ styleType, children }) => {
  const className = styleType === 'primary' ? 'btn-primary' : 'btn-secondary';
  return <button className={className}>{children}</button>;
};

// 사용 예시
<Button styleType="primary">기본 버튼</Button>
<Button styleType="secondary">보조 버튼</Button>

이 방식은 코드의 확장성을 높이고, 특정 컴포넌트가 다른 컴포넌트의 기능을 ‘포함’함으로써 재사용 가능한 기능 조각들을 생성할 수 있게 합니다.

2. 엄격한 계층 구조로부터의 자유로움

상속은 종종 깊은 계층 구조를 야기하며, 이는 코드를 읽기 어렵게 만들고 유지 관리 및 테스트를 더 어렵게 만듭니다. 부모 클래스의 변경이 자식 클래스에 예상치 못한 영향을 미칠 수 있는 ‘취약한 상위 클래스 문제(fragile base class problem)’ 또한 상속의 단점으로 지적됩니다.

React 생태계에서는 이러한 복잡성을 피하고, 컴포넌트 간의 의존성을 낮춰 개별 컴포넌트의 독립성을 확보하는 것이 중요합니다. 컴포지션은 엄격한 계층 구조에 얽매이지 않고 컴포넌트들을 자유롭게 조합하고 변경할 수 있게 하여, 애플리케이션의 요구사항 변화에 더 쉽게 대응할 수 있도록 합니다.

3. 관심사의 명확한 분리 (Separation of Concerns)

컴포지션을 활용하면 각 컴포넌트가 독립적으로 자체 로직을 처리하면서도 다른 컴포넌트들과 상호 작용할 수 있습니다. 예를 들어, Card 컴포넌트가 이미지와 제목만 렌더링하고, 필요에 따라 Footer 컴포넌트를 조합하여 기능을 확장할 수 있습니다.

const Card = ({ imageSrc, title }) => (
  <div className="card">
    <img src={imageSrc} alt="" />
    <h2>{title}</h2>
  </div>
);

const CardWithFooter = ({ imageSrc, title, footerContent }) => (
  <div className="card-container">
    <Card imageSrc={imageSrc} title={title} />
    <footer>{footerContent}</footer>
  </div>
);

이는 코드의 응집도를 높이고 결합도를 낮춰, 개발자가 특정 기능에만 집중할 수 있도록 돕습니다. 각 컴포넌트의 책임이 명확해지므로 코드를 이해하고 디버깅하기가 훨씬 쉬워집니다.

4. 쉬운 테스트 및 유지 보수

상속된 구조에 비해 컴포넌트 간의 의존성이 적기 때문에 개별적으로 테스트하기가 더 쉽습니다. 특정 기능을 수정할 때 다른 부분에 미치는 영향을 최소화할 수 있어, 코드의 안정성을 높이고 장기적인 유지 보수를 용이하게 합니다. 모듈식 코드는 팀 프로젝트에서도 협업 효율성을 높이는 데 기여합니다. 🚀


결론

ReactJS에서 애플리케이션을 개발할 때 상속보다는 컴포지션을 채택하는 것은 단순히 트렌드를 따르는 것을 넘어, 더 깔끔한 아키텍처와 향상된 유지 보수성으로 이어지는 현명한 선택입니다. 클래스나 직접적인 상속 패턴을 통한 계층적 관계에 크게 의존하기보다, 작고 기능적인 컴포넌트들을 조합하여 활용함으로써 프로젝트 내의 생산성과 명확성을 모두 향상시킬 수 있습니다.

컴포지션은 React의 핵심 철학인 '컴포넌트 중심 개발' 을 완벽하게 구현하는 방법이며, 변화에 유연하게 대응하고 확장 가능한 애플리케이션을 구축하는 데 필수적인 접근 방식입니다. 지금 바로 여러분의 React 프로젝트에 컴포지션의 힘을 불어넣어 보세요! ✨

728x90