[React] 04. Components와 Props

이예빈·2022년 4월 25일
0
React 공식사이트의 주요개념을 보고 정리한 글 입니다.
( 주의! 중간중간 작성자의 마음의 소리?가 들어갈 수 있습니다.)

Components and Props

Components는 UI를 독립적이며, 재사용 가능한 조갇들로 나누며, 각각의 조각들을 개별적으로 생각할 수 있게 합니다. 이 페이지에선 components의 개념을 소개하며, 더 자세한 component API문서는 이곳에서 확인할 수 있습니다.

개념적으로 components는 자바스크립트 함수와 유사합니다. 컴포넌트는 "props"이라 불리는 임의의 input을 받아 스크린에 보여져야하는것들을 나타낸 React elements를 리턴합니다.

Function and Class Components

가장 쉽게 컴포넌트를 정의하는 방법은 JavaScript function을 작성하는 것입니다. :

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

이 function은 유효한 React 컴포넌트 이지만 단 하나의 "props"오브젝트만 받아드리며 단 한개의 React element만을 리턴합니다.
우린 이러한 컴포넌트를 "function 컴포넌트"라고 부르기로 했습니다. 정말 말그대로 Javascript function이기 때문이죠.

뿐만아니라 ES6 class를 이용한 컴포넌트를 사용할 수도 있습니다.:

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

위의 두 컴포넌트는 React의 관점으로 보았을 때 동일합니다.

Function컴포넌트와 Class 컴포넌트 모두 몇가지 다른 추가적인 특징이 있는데, 그것은 다음 섹션에서 알아보겠습니다.

Rendering a Component

이전엔, 오직 DOM 태그만을 나타내는 React element만을 접했었습니다. :

const element = <div />;

하지만, element는 아래와 같이 사용자가 정의한 컴포넌트도 나타낼 수 있습니다.

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

React는 사용자가 정의한 컴포넌트를 나타내는 element를 보게되면, JSX 속성과 자식들을 이 컴포넌트에 단일 오브젝트로 전달하고 우리는 이 오브젝트를 "props"라 칭합니다.

예를들어, 아래의 코드는 "Hello, Sara"를 페이지에 렌더링하게 됩니다 :

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

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

위 예시에서 어떤 일이 발생하는지 요약해봅시다 :

  1. root.render()<Welcome name="Sara" /> element와 함께 호출합니다.
  2. React는 props으로 {name: 'Sara'}와 함께 Welcome컴포넌트를 호출합니다.
  3. 우리의 Welcome컴포넌트는 결과로 <h1>Hello, Sara</h1> element를 반환합니다.
  4. React DOM은 효율적으로 DOM을 <h1>Hello, Sara</h1>가 되도록 업데이트합니다.

Note : 컴포넌트의 이름은 항상 대문자로 시작하세요
React는 소문자로 시작하는 컴포넌트는 DOM 태그로 다룹니다. 예를들어 <div />는 HTML div태그를 나타내지만, <Welcome />은 컴포넌트를 나타내며 범위 안에 Welcome이 있어야 합니다.

Composing Components

컴포넌트는 자신의 output안에서 다른 컴포넌트를 참조할 수 있습니다.
이는 우리가 어떤 레벨의 세부사항에서든 같은 컴포넌트를 사용할 수 있게합니다.
React앱에 있는 button, form, dialog, screen 등 이런 모든것들은 흔히 컴포넌트로 표현됩니다.

예를들어, Welcome을 여러번 보여주는 App 컴포넌트를 만들었다고 합시다.:

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

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

일반적으로, 새로운 React 앱은 최상단에서 단 하나의 App 컴포넌트를 갖습니다.
그러나, 기존의 앱을 React로 통합하는 것이라면, 아마 Button과 같은 작은 컴포넌트에서 시작해 아래에서 위로 올라가는 식으로 작업하여 계층구조의 최상단까지 단계적으로 해야할 것입니다.

Extracting Components

컴포넌트를 더 작은 컴포넌트로 나누는 것을 두려워하지 마세요~

예를 들어, 아래 Comment라는 컴포넌트가 있다고 합시다.:

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

이 컴포넌트는 props으로 object인 author를, string인 text를 date인 date를 받아 소셜미디어 웹사이트상의 코멘트를 나타냅니다.

이 컴포넌트는 구성요소들이 모두 중첩 구조로 이루어져 있어서 변경하기 어려울 수 있으며, 각 구성요소를 개별적으로 재사용하기도 힘듭니다. 이 컴포넌트에서 몇 가지 컴포넌트를 추출하겠습니다.

먼저 Avatar를 추출하겠습니다.

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

AvatarComment안에서 렌더링 된다는 것을 알 필요가 없습니다.
이것이 우리가 prop을 author가 아닌 user라는 더 일반적인 이름으로 명명한 이유입니다.

우리는 props의 이름을 컴포넌트가 사용되어지는 context보다는 컴포넌트 자체의 관점에서 명명하는 것을 권장합니다.

이젠 우린 Comment컴포넌트를 좀 더 간소화 할 수 있습니다. :

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, Panel, Avatar), UI 일부가 자체적으로 복잡한 (App, FeedStory, Comment) 경우에는 별도의 컴포넌트로 만드는 게 좋습니다.

Props are Read-Only

컴포넌트를 function으로 만들든 class로 만들던 절대로 props자체를 수정해서는 안됩니다.
sum 함수를 살펴봅시다.

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

이러한 function을 "pure"하다고 하는데 이러한 것들은 inputs을 바꾸려하지 않고 항상 같은 inputs에 대해 같은 결과를 리턴하기 때문입니다.

반대로 아래와 같은 function은 input자체를 변경하기 때문에 impure합니다.:

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

React는 꽤 유연하지만서도 한가지 엄격한 규칙이 있습니다.

All React components must act like pure functions with respect to their props.
(모든 React 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야 합니다.)

물론, 어플리케이션은 동적이며 매번 변화합니다. 다음 섹션에서는 "state"라는 새로운 컨셉을 소개할 것입니다. State는 React 컴포넌트가 위의 규정을 위반하지 않고도 유저의 actions에 대해서, network에 대한 응답으로 그들의 output을 바꿀 수 있게 해줍니다.

참고
https://reactjs.org/docs/components-and-props.html

0개의 댓글