3.Components and Props

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

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

Components는 props를 argument로 받아
element를 리턴하는 function이다.
(여기서 props는 element가 보여줄 data이다)

compoents를 활용하여 element를 독립적으로 만들 수 있다.
이는 element의 재사용성을 높여준다.(마치 super class 처럼)

Component 선언 방법 2가지

일반적으로 component를 선언할 때 이름은 대문자로 시작한다.
선언 방법은 functional componentclass component 2가지가 있다.

Functional component

component는 아래처럼 function으로 만들 수 있다.

function Welcome(props) {
  return <h1>Hello, David</h1>;
}

react component가 되려면 아래 2가지 기준을 만족해야 한다.

  • 하나의 props(property의 약자로 data object)를 argument로 받는다.
  • react element를 return 한다.

위에 function Welcome은 2가지 조건을 만족하기 때문에 component이다.

Class component

또한 ES6의 class keyword를 사용해서 component를 만들 수도 있다.

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

function과 class 두가지 방법은
동일하게 react component를 만들 수 있다.

두가지 방법 사이 유일한 차이점은 state 관리 기능이었다.
(과거에는 class만 state 관리 가능)
하지만 react hooks가 등장하면서
functional component에서도 state 관리가 가능 해졌다.

Rendering a component

element 역할의 변화

이전에 react element는 단순히 HTML tag로 만들어졌다.

const element = <div />;

그러나 component를 활용해서 아래처럼 element를 만들게 되었다.

const element = <Welcome name="david" />;

component rendering 과정

react가 이러한 component를 실행할 때,
JSX attribute(예를 들어, 위 코드에서 name)을
single object ({name: "david"})의 형태로
component에게 전달한다.
이 object가 바로 props이다.

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

위 코드를 실행하면 아래의 과정을 거친다.

  1. ReactDOM.render( ~~~ )를 실행한다.
  2. render()안에 element인자인 Welcome comoponent를 실행한다. (이때 props인자는 {name: “david”} object)
  3. Welcome component는 <h1>Hello, david</h1>라는 element를 return한다.
  4. ReactDOM은 element의 달라진 부분(name의 value)이 일치하도록 DOM을 업데이트한다.

component 구성하기

component는 다른 component를 조합하여 구성할 수 있다.
그 결과 component는 거꾸로 추출할 수도 있다.
(부모 component에게 소속된 자식 component를 만들 수 있으므로)

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="David" />
      <Welcome name="Paul" />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

위 코드에서 처럼 App component를 구성할 때
그 안에서 많은 Welcome component들을 조합하여 구성할 수 있다.

처음부터 react로 app을 만드는 경우
최상위 component인 App component부터 가장 먼저 만들게 된다.
하지만 기존 앱을 react로 바꿀때는 더 작은 component부터 만들기도 한다.

component 추출하기

component는 핵심 component를 추출할수록 간단해진다.
(이때 추출한 component가 리턴한 object는
OOP에서 super class와 비슷하다.)

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

Comment component는 재사용하기 어렵다.
너무 많은 component들을 special하게 갖고 있기 때문이다.
component를 추출할 때 그 기준은 재사용성이 높은 코드인가? 이다. (추출한 component는 재사용성이 높아서 코드의 중복을 제거함)

가장 먼저, 하이라이팅된 Avatar component를 추출하자.

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />

  );
}

원래 Avatar component의 props 이름은 author였다.
하지만 이름을 user로 바꾸었다.
그 결과, author 뿐만 아니라 다른 직업도 Avatar component를 활용할 수 있게 되었다.

component의 props 이름은 해당 재사용성을 높이는 방향으로 짓자.
(역할을 구체적이고 명확하게 고려해서 짓지 말자 ❌)

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <Avatar user={props.author} />        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

위와 같이 Avatar component의 props이름이 user가 되었고,
comment component는 조금 간단해졌다.

다음은 UserInfo component를 추출해보자.

function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avatar user={props.user} />
      <div className="UserInfo-name">
        {props.user.name}
      </div>
    </div>
  );
}

UserInfo 또한 comment component 외에 다른 component에서 재사용 가능하므로
추출해서 중복된 코드를 제거한다.

function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} />
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

최종적으로 comment component는 위와 같이 간단해졌다.
재사용할 수 있는 component들을 추출했기 때문이다.

component 추출은 마치 generalization과 비슷하다. 공유되는 특성을 추출하는 것과 비슷하게 중복되는 로직을 추출하기 때문이다.

Props 는 읽기만 가능!

function 또는 class로 component를 만든 경우, 절대 주어진 props를 수정할 수 없다.
아래 function을 보자.

function sum(a, b) {
  return a + b;
}

sum function은 pure하다.
왜냐하면 argument로 주어진 a,b값을 수정하지 않기 때문이다.
그 결과, 언제나 동일한 값을 return한다.

반대로 아래 function은 pure하지 않다.

function withdraw(account, amount) {
  account.total -= amount;
}

왜냐하면 위 function은 argument로 주어진 account의 값을 바꾸기 때문이다.
React에서는 절대적인 규칙이 한가지 있다.

모든 component는 props 값을 수정하지 않는 pure function이다.

물론 UI는 동적이어서 나타내는 값이 변한다.
이와 관련된 state라는 개념을 다음 블로깅에서 만나보자. state는 component가 React의 절대적인 규칙을 지키면서
동시에 event, server와의 통신 등 상호작용을 통해
UI가 나타내는 값이 변할 수 있도록 해준다.


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

GitHubMedium