강백호같은개발자

React Components and Props 리액트 컴포넌트와 props 본문

- 배움은 개발을 거들뿐(TIL)

React Components and Props 리액트 컴포넌트와 props

쨜리 2020. 7. 8. 20:50
컴포넌트를 통해 UI를 재사용 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 살펴볼 수 있습니다. 
개념적으로 컴포넌트는 JavaScript 함수와 유사합니다.
'props'라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환합니다.

 

 

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

 

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

function Welcome(props){
  return <h1>Hello, World!</h1>
}

이 함수는 데이터를 가진 하나의 'props'(props는 속성을 나타내는 데이터) 객체 인자를 받은 후

React 엘리먼트를 반환하므로, 유효한 React 컴포넌트입니다. 이러한 컴포넌트는 JavaScript함수이기 때문에 

말 그대로 '함수 컴포넌트'라고 합니다.

 

또한 이를 ES6 Class를 이용해서 컴포넌트를 정의할 수 있습니다.

class Welcome extends React.Component {
  render(){
    return <h1>Hello, World!</h1>;
  }
}

리액트 관점에서 두가지 컴포넌트 유형은 동일합니다.

 

 

컴포넌트 렌더링

이전까지 React 엘리먼트를 DOM태그로 나타냈습니다.

const element = <div />;

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

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

React가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면, 

JSX 어트리뷰트(속성)과 자식을 해당 컴포넌트에 단일 객체로 전달합니다.

이 객체를 'props'라고 합나다.

 

즉, 사용자 정의 컴포넌트로 사용하게되면,

props는 컴포넌트 Class의 instance가 된다고 생각할 수 있겠습니다.

 

Hello, Sara 를 렌더링하는 예시입니다.

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

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

ReactDOM.render(
  element, document.getElementById('root')
);

 

위의 코드는 다음과 같은 흐름으로 진행됩니다.

1. <Welcom name="Sara" />엘리먼트로 ReactDOM.render() 를 호출합니다. 

2. React는 {name: 'Sara'}를 props로 하여 Welcome 컴포넌트를 호출합니다. ==> 속성을 객체로하는 props를 컴포넌트에 전달.

3. Welcome 컴포넌트는 결과적으로 <h1>Hello, Sara</h1> 엘리먼트를 반환한다. 

4. ReactDOM은 <h1>Hello, Sara</h1>엘리먼트와 일치하도록 DOM을 효율적으로 업데이트합니다. 

 

결국 element를 사용자 정의 컴포넌트로 나타내면,

나타낼때의 속성과 자식이 담긴 객체가 props로 Class 컴포넌트로 전달되고

Class 컴포넌트에서 element가 반환되어, ReactDOM.render()로 호출되는 것입니다.

결국 미리 정해져있는 Class 컴포넌트가 불러와지는 것인데,

사용자 정의 컴포넌트로 element를 사용하면서 속성이나 자식을 element로 활용할 수 있겠습니다.

 

*주의
컴포넌트의 이름은 항상 대문자로 시작합니다.(function Welcome(props){})
React는 소문자로 시작하는 컴포넌트를 DOM태그로 처리합니다.
예를 들어 <div />는 HTML div 태그를 나타내지만,
<Welcome />은 컴포넌트를 나타내며 범위 안에 Welcome이 있어야 합니다.

 

컴포넌트 합성

컴포넌트는 자신의 출력에 다른 컴포넌트를 참조할 수 있습니다.

이는 모든 세부 단계에서 동일한 추상 컴포넌트를 사용할 수 있음을 의미합니다. 

React 앱에서는 버튼, 폼, 다이얼로그, 화면 등 모든 것들이 컴포넌트로 표현됩니다.

 

예를 들어 Welcome을 여러번 렌더링하는 App컴포넌트를 만들 수 있습니다.

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

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

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

 

 

컴포넌트 추출

아래는 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>
  );
}

우선 가장 하부에 있는 엘리먼트인 Avatar 클래스를 컴포넌트 추출해보겠습니다.

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

여기서 author가 user로 변경된 이유는, 사실 Avatar 컴포넌트에서는 author라는 개념이 불필요하고 이에 맞는 맥락(context)가 없기 때문에 이를 그냥 user라고 치환한 것입니다. 아마도 컴포넌트 합성을 할때 props의 속성으로 해당 user에 대한 키값을 줘야할 것입니다.

다음으로는 Avatar를 감싸고 있는 UserInfo 클래스를 컴포넌트화 해보겠습니다.

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

UserInfo 컴포넌트도 마찬가지로, 맥락상 author가 사용되는 단이 아니기 때문에, 일반 명사인 user로 치환하였습니다.

그리고 모든 컴포넌트의 이름은 대문자로 시작되어야 합니다. 

 

이것으로 표현한 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>
  );
}

 

props는 읽기 전용입니다.

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

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

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

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

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

 

React에는 엄격한 규칙이 하나 있습니다.

 

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

Comments