programming_kr/react

컴포넌트와 props

JSsunday 2021. 1. 13. 09:00
728x90

 

React의 기본 구조는 컴포넌트의 집합입니다. 컴포넌트란 UI를 재사용 가능하게끔 만들어 놓은 여러 조각입니다.

 

함수 컴포넌트와 클래스 컴포넌트

 

컴포넌트를 정의하는 간단한 방법은 javascript 함수를 만드는 것입니다.

 

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

 

이 함수는 데이터를 가진 하나의 props 파라미터를 받은 후 React 엘리먼트로 반환합니다. 이러한 컴포넌트는 javascript 함수이기 때문에 함수 컴포넌트라고 합니다.

 

또한 ES6의 클래스를 통해 컴포넌트를 정의할 수 있습니다.

 

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

 

React의 관점에서는 두 가지 유형의 컴포넌트는 동일합니다. class의 컴포넌트는 추가기능이 있는데 다음에 알아보도록 합니다.

 

컴포넌트 렌더링

 

React 엘리먼트는 DOM 태그로 나타낼수도 있짐나 사용자 정의 컴포넌트로도 나타낼 수 있습니다.

 

const element = <Welcome name="브니" />;

 

React가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하게 되면 JSX 속성과 자식을 해당 컴포넌트에 단일 객체로 전달합니다. 이 것을 props라고 합니다. 자식 컴포넌트에서는 부모컴포넌트에서 보낸 속성들을 props로 받는 것 입니다.

 

function Test(props) {
  return <h1>안녕, {props.name}</h1>;
}

const element = <Test name="브니" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

 

위의 코드를 실행하게 되면 아래와 같은 일이 발생합니다.

 

  1. <Test name="브니"/> 엘리먼트로 ReactDOM.render()를 호출합니다.
  2. React는 {name : "브니"}를 props로 하여 Test 컴포넌트를 호출합니다.
  3. Test 컴포넌트는 결과적으로 <h1>안녕, 브니</h1>엘리먼트를 반환합니다.
  4. ReactDOM은 <h1>안녕, 브니</h1>엘리먼트와 일치하도록 DOM을 효율적으로 업데이트(변경된 부분이 있으면 가상의 DOM과 비교해서 달라진 부분만 업데이트) 합니다.

 

컴포넌트의 이름은 항상 대문자로 시작해야 합니다.
<div/>는 HTML의 태그지만 <Test/>는 컴포넌트를 나타냅니다.

 

컴포넌트 합성

 

컴포넌트는 다른 컴포넌트를 참조할 수 있으므로 동일한 추상 컴포넌트를 사용할 수 있습니다. React 앱에서는 Button, Form, dialog, 화면 등의 모든 것들의 하나의 컴포넌트로 표현됩니다.

 

function Test(props) {
  return <h1>안녕, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Test name="브니" />
      <Test name="바니" />
      <Test name="보니" />
    </div>
  );
}

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

 

일반적으로 React 프로젝트는 만들게 되면 최상위로 App 컴포넌트를 가지고 있고 이 App 컴포넌트는 메인 페이지의 태그의 id가 root인 태그에 렌더링됩니다. 기존 앱에서 React를 통합하게 되는 경우 Button처럼 작은 컴포넌트부터 상단으로 올라가면서 작업해야 합니다.

 

컴포넌트 추출

 

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 컴포넌트는 부모 컴포넌트로 부터 author, text, date를 받고 있습니다. 이 컴포넌트는 모두 중첩 구조로 되어 있어 변경하기 어렵기 때문에 재사용도 힘듭니다. 이 컴포넌트를 작은 단위의 컴포넌트로 추출해보겠습니다.

 

className인 Avatar를 컴포넌트로 추출해보겠습니다.

 

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

 

Avatar는 자신이 Comment 내에서 렌더링 된다는 것을 알 필요 없습니다. 따라서 props의 이름을 author에서 일반화된 user로 변경했습니다. props의 이름은 사용될 context가 아닌 컴포넌트 자체의 관점에서 짓는 것을 권장합니다.

 

Avatar를 컴포넌트로 추출한 코드는 아래와 같습니다.

 

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 아래에 있는 사용자의 이름을 렌더링하는 UserInfo 컴포넌트를 추출하겠습니다.

 

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

 

Comment가 더욱 단순해졌습니다.

 

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>
  );
}

 

컴포넌트를 추출하는 작업이 번거로울 수도 있지만 재사용이 가능한 컴포넌트를 만들어 놓는 것은 큰 앱을 작업할 때 효과를 발휘하고 리액트가 가장 지향하는 방식입니다. UI의 일부가 여러 번 사용되거나(Button, Form, Panel 등), UI 일부가 자체적으로 복잡한 (App, FeedStory, Comment) 경우에는 별도의 컴포넌트로 만드는 게 좋습니다.

 

props는 읽기 전용입니다.

 

컴포넌트를 함수나 클래스로 선언했을 때 props를 수정할 수 없습니다.

 

//이 함수는 파라미터의 값이 변경되지 않습니다.
//순수한 함수입니다.
function sum(a,b){
	return a + b;
}

//이 함수는 파라미터인 a의 객체 프로퍼티 값이 변경됩니다.
//순수한 함수가 아닙니다.
function add(a,b){
	return a.sum + b;
}

 

모든 React 컴포넌트에서는 props와 관련한 동작을 할 때 순수 함수처럼 동작해야 하는 엄격한 규칙이 있습니다.

그럼 규칙을 어기지 않고 유저 액션, 네트워크 응답, 기타 등등에 대한 응답으로 시간 경과에 따라 출력을 변경을 할 때는 어떻게 해야 하나요?

다음 섹션에서 배워봅니다.

 

컴포넌트와 props
728x90