Components와 Props

growdeveloper ㅣ 2023. 1. 30. 17:07

Components와 Props

 

컴포넌트를 통해 UI를 재사용 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 살펴볼 수 있습니다. 이 페이지에서는 컴포넌트의 개념을 소개합니다.

 

https://ko.reactjs.org/docs/react-component.html

 

React.Component – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

개념적으로 컴포넌트는 Javascript 함수와 유사합니다. "props"라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환합니다.


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

컴포넌트를 정의하는 가장 간단한 방법은 JavaScript 함수를 작성하는 것 입니다.

 

이 함수는 데이터를 가진 하나의 "props"(props는 속성을 난타내는 데이터입니다) 객체 인자를 받은 후 React 엘리먼트를 반환하므로 유요한 React 컴포넌트입니다. 이러한 컴포넌트는 JavaScript 함수이기 때문에 말 그대로 "함수 컴포넌트"라고 호칭 합니다.

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

또한 ES6 class를 사용하여 컴포넌트를 정의할 수 있습니다.

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

React 관점에서 볼 대 위 두 가지 유형의 컴포넌트는 동일합니다.

 

▶ 컴포넌트 렌더링

이전까지는 DOM 태그만을 사용해 React 엘리먼트를 나타냈습니다.

const element = <div />;

 

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

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

 

▶props

react가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면 JSX어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달합니다. 이 객체를 "props"라고 합니다

 

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

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

이 예시에서는 다음과 같은 일들이 일어납니다.

 

  1. <Welcome name="Sara" /> 엘리먼트로 root.render()를 호출합니다.
  2. React는 {name: 'Sara'}를 props로 하여 Welcome 컴포넌트를 호출합니다.
  3. Welcome 컴포넌트는 결과적으로 <h1>Hello, Sara</h1> 엘리먼트를 반환합니다.
  4. React DOM은 <h1>Hello, Sara</h1> 엘리먼트와 일치하도록 DOM을 효율적으로 업데이트합니다.
주의 : 컴포넌트의 이름은 항상 대문자로 시작합니다.
React는 소문자로 시작하는 컴포넌트를 DOM 태그로 처리합니다. 예를 들어<div /> html div태그를 나타내지만, <Welcome />은 컴포넌트를 나타내며 범위 안에 Welcome이 있어야 합니다.
이 규칙에 대한 자세한 내용은 여기를 클릭해 주세요

 컴포넌트 합성

 

컴포넌트는 자신의 출력에 다른 컴포넌트를 참조할 수 있습니다. 이는 모든 세부 단계에서 동일한 추상 컴포넌트를 사용할 수 있음을 의미합니다. React 앱에서는 버튼, 폼, 다이얼로그, 화면 등의 모든 것들이 흔히 컴포넌트로 표현됩니다.

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과 같은 작은 컴포넌트부터 시작해서 뷰 계층의 상단으로 올라가면서 점진적으로 작업해야 할 수 있습니다.

 

▶ 컴포넌트 추출

컴포넌트를 여러 개의 작은 컴포넌트로 나누는 것을 두려워 하지마세요.

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

이 컴포넌트는 author(객체), text(문자열) 및 date(날자)를 props로 받은 후 소셜 미디어 웹사이트의 코멘트를 나타냅니다.

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

 

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

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

Avatar는 자신이 Component 내에서 렌더링 된다는 것을 알 필요가 있습니다. 따라서 props의 이름을 author에서 더욱 일반화된 user로 변경하였습니다.

props의 이름은 사용될 context가 아닌 컴포넌트 자체의 관점에서 짓는 것을 권장합니다.

 

이제 Component가 살짝 단순해 졌습니다.

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

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

function formatDate(date) {
  return date.toLocaleDateString();
}

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

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

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

const comment = {
  date: new Date(),
  text: 'I hope you enjoy learning React!',
  author: {
    name: 'Hello Kitty',
    avatarUrl: 'http://placekitten.com/g/64/64'
  }
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Comment
    date={comment.date}
    text={comment.text}
    author={comment.author} />
);

처음에는 컴포넌트를 추출하는 작업이 지루해 보일 수 있습니다 .하지만 재사용 가능한 컴포넌트를 만들어 놓는 것은 더 큰 앱에서 작업할 때 두각을 나타냅니다. UI 일부가 여러 번사용되거나 (Button, Panel, Avatar), UI 일부가 자체적으로 복잡한(APP, FeedStory, Comment) 경우에는 별도의 컴포넌트로 만드는 게 좋습니다.

 

▶Props는 읽기 전용 입니다.

함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안 됩니다. 다음 sum 함수를 살펴봅시다.

 

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

이런 함수들은 순수 함수라고 호칭합니다. 입력값을 바꾸려 하지 않고 항상 동일한 입력값에 대해 동일한 결과를 반환하기 떄문입니다.

 

반면에 다음 함수는 자신의 입력값을 변경하기 때문에 순수 함수가 아닙니다.

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

React는 매우 유연하지만 한 가지 엄격한 규칙이 있습니다.

모든 React 컴포넌트는 자신의 Props를 다룰 때 반드시 순수 함수처럼 동작해야 합니다.


 

 

Hooks 이전

▶ 컴포넌트 내부에 상태가 있다면?

  • class

▶컴포넌트 내부에 상태가 없다면?

  • 라이프 사이클을 사용해야 한다면?
    • class
  • 라이프 사이클에 관계 없다면?
    • function

▷ Class 컴포넌트

import React from 'react';

//정의
class ClassComponent extends React.Component {
	render() {
    	return (<div>Hello<div>);
    }
}

//사용
<ClassComponent/>

 


Hooks 이후

  • class
  • function
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin:0;
      padding: 0;
      border: 0;
    }

    #root p {
      color: white;
      font-size: 20px;
      background-color: green;
      text-align: center;
      width: 200px;
    }

    #btn_plus {
      background-color: red;
      border: 2px solid #000000;
      font-size: 15px;
      width: 200px;
    }
  </style>
  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script type="text/babel">
  //-----------------------------------------------------------------
  //정의 : 아래는 class 으로 사용하는 방식
  //-----------------------------------------------------------------
  class ClassComponent extends React.Component {

    //항상리턴을 해줘야하며 React.element여야 한다.
    render() {
      return <div>hello</div>;
    }
  }

  // 사용
  ReactDOM.render(
    <ClassComponent/>,
    document.querySelector('#root')
  )
  //-----------------------------------------------------------------
  //아래는 function 으로 사용하는 방식
  //-----------------------------------------------------------------
  const FunctionComponenet = () => <div>Hello</div>;

  ReactDOM.render(<FunctionComponenet/>,document.querySelector('#root'));
</script>
</html>

▶ React.createElement로 컴포넌트 만들기

-> 태그 이름의 문자열과 React 컴포넌트 문자열과 Fragement의 차이를 이해하시길 바랍니다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
 
  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script type="text/javascript">

  // React.createElement(
  //   type, // 태그 이름 문자열 | 문자열 컴포넌트 | React.Fragment
  //   [props], //리엑트 컴포넌트에 넣어주는 데이터 객체
  //   [...children] //자식으로 넣어주는 요소들
  // );

  //1. 태그 이름 문자열 type
  // ReactDOM.render(
  //   React.createElement('h1',null,`type 이 "태그 이름 문자열" 입니다.`),
  //   document.querySelector('#root')
  // );

  //2. 리액트 컴포넌트 type

  // const Component = () => {
  //   return React.createElement('p',null,`type 이 "React 컴포넌트" 입니다.`);
  // }

  // //<Component></Component> => <Component/> => <p>type 이 "React 컴포넌트" 입니다.</p>
  // ReactDOM.render(
  //   React.createElement(Component, null, null),
  //   document.querySelector('#root')
  // );

  // 3. React.Fragment
  ReactDOM.render(
    React.createElement(
      React.Fragment,
      null,
      `type 이 "React Fragment 입니다"`,
      `type 이 "React Fragment 입니다"`,
      `type 이 "React Fragment 입니다"`
    ),
    document.querySelector('#root')
  )

</script>
</html>

'React' 카테고리의 다른 글

엘리멘트 렌더링  (0) 2023.02.02
Event Handling  (0) 2023.02.01
Props 와 State  (0) 2023.02.01
JSX  (0) 2023.01.30
시작하기  (0) 2023.01.30