10.Composition vs Inheritance

(React 공식문서의 main concepts 번역 글입니다.)

함께보면 이해가 쏙쏙
https://youtu.be/T6Wj2pbkorA

compositioninheritance
component를 만드는 2가지 다른 패턴이다.
(https://www.youtube.com/watch?time_continue=238&v=wfMtDGfHWpA)
(위 영상을 참고하면 둘의 차이를 쉽게 이해할 수 있다.)
보충 설명을 하자면
composition은 component가 무엇을 하는지를 기준으로 선언한다.
반면 inheritance는 component가 무엇인지를 기준으로 선언한다.

React에서 inheritance를 활용하면 코드 재사용성이 낮다.
반면에, composition model은 재사용성이 높다.
inheritance로는 재사용할 수 없는 상황이 있기 때문이다.

바로 그 한계상황에 대해, 아래에서 살펴볼 것이다.
또한 composition으로 그 한계상황을 어떻게 극복할 수 있는지
그 해결방법도 함께 살펴 볼 것이다.

Containment

첫번째 한계상황인 containment이다.
어떤 component가 children 값을 미리 알 수 없는 경우가 있다.
보통 SidebarDialog가 이에 해당한다.
(일반적으로 박스 형태의 component가 이에 속한다.)

그런 경우에는 해당 component가 return하는 react element안에
직접 {children props}를 넣어주는 것이 좋다.
그 결과, 어떠한 children props를 받더라도 render가 가능해진다.

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}    </div>
  );
}

위 코드와 같이 return하는 element에
직접 {children props}를 넣으면
아래처럼 해당 component에게 다른 component가
tag 모음의 children을 props로 전달하더라도 render가 가능하다.

function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">        Welcome      </h1>      <p className="Dialog-message">        Thank you for visiting our spacecraft!      </p>    </FancyBorder>
  );
}

위에서 하이라이팅된 부분, 즉 tag 모음 뿐만아니라
어떠한 것도 <FancyBorder> JSX tag 로 감싸기만 하면 <FancyBorder>는 children props로 전달받을 수 있다.
<FancyBorder><div>로 감싸진 {props.children}을 return하므로
children props로 전달 받은 어떠한 것이든 render할 수 있다.

특수한 경우에, 여러개의 {props.children}가 필요할 수도 있다.
그러한 경우에는 children보다는 더 정확히 표현할 수 있는 이름을 사용한다.

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

function App() {
  return (
    <SplitPane
      left={
        <Contacts />      }
      right={
        <Chat />      } />
  );
}

<Contacts /><Chat />같은 react element는 객체이다.
따라서 react element들은 다른 data처럼 props로 전달할 수 있다.

Specialization

두번째 한계상황은 특별한 component가 필요한 경우이다.
이 경우, 일반적인 component를 수정하여 특별한 component를 만든다.

예를 들어 <Dialog>는 일반적인 component이다.
이를 수정하면 특별한 component<WelcomeDialog>를 만들 수 있다.

React에서 특별한 component는 composition으로 만들 수 있다.
즉 일반적인 component에게 props를 주어 특별한 component로 만든다.

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

function WelcomeDialog() {
  return (
    <Dialog
      title="Welcome"      message="Thank you for visiting our spacecraft!" />
  );
}

일반적인 component인 <Dialog>
title과 message props를 주어
특별한 component<WelcomeDialog>를 만들었다.

function component 뿐아니라 class component에서도
composition으로 동일하게 specialization을 할 수 있다.

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

class SignUpDialog extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.handleSignUp = this.handleSignUp.bind(this);
    this.state = {login: ''};
  }

  render() {
    return (
      <Dialog title="Mars Exploration Program"
              message="How should we refer to you?">
        <input value={this.state.login}               onChange={this.handleChange} />        <button onClick={this.handleSignUp}>          Sign Me Up!        </button>      </Dialog>
    );
  }

  handleChange(e) {
    this.setState({login: e.target.value});
  }

  handleSignUp() {
    alert(`Welcome aboard, ${this.state.login}!`);
  }
}

inheritance를 사용하면 어떻게 되지?

페이스북에서는 composition을 활용하여 component를 만든다.
왜냐하면 inheritance보다 효율이 좋기 때문이다.

props와 composition을 활용하면 일반적인 component
명백하고 안전하게, 특별한 component로 만들 수 있게 된다.
그 component는 임의의 props(primitive value, function,
심지어 react element까지)를 받아들일 수 있기 때문이다.

만약 UI로직과 관련이 없는 function을 재사용 하기 원한다면
해당 function을 extract하는 것을 추천한다.
특정 component가 해당 function이 필요하면 import해서 쓸 수 있다.
inheritance를 통해 function 코드를 재사용하는 대신에!


[david hwang]
Written by@[david hwang]
깊게 이해하고 넓게 소통합니다.

GitHubMedium