React | 컴포넌트(component)와 props

권은혜·2023년 6월 2일
post-thumbnail

컴포넌트(component)

개념상 컴포넌트는 자바스크립트 함수와 비슷합니다. “props”라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환합니다.

함수형 및 클래스 컴포넌트

컴포넌트를 정의하는 가장 간단한 방법은 자바스크립트 함수로 작성하는 것입니다.

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

이 함수는 단일 “props” (속성을 나타냄) 객체 인수를 받고 React 요소를 반환하기 때문에 유효한 React 컴포넌트입니다. 이러한 컴포넌트는 말 그대로 자바스크립트 함수이기 때문에 “함수형”이라고 부릅니다.

컴포넌트를 정의하기 위해 ES6 class 를 사용할 수도 있습니다.

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

컴포넌트 렌더링

이전에는 DOM 태그를 나타내는 React 요소만 있었습니다.

const element = <div />;

그러나, 요소에 유저가 정의한 컴포넌트를 나타낼 수도 있습니다.

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

React가 유저가 정의한 컴포넌트를 나타내는 요소를 볼 때 JSX 속성을 이 컴포넌트에 단일 객체로 전달합니다. 이 객체를 “props” 라고 부릅니다.

예를 들어, 이 코드는 “Hello, Sara”를 페이지에 렌더링합니다.

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

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

Try it on CodePen.
1. <Welcome name="Sara" /> 요소로 ReactDOM.render() 를 호출합니다.
2. React가 {name: 'Sara'} 를 props로 하여 Welcome 컴포넌트를 호출합니다.
3. Welcome 컴포넌트가 그 결과로 <h1>Hello, Sara</h1> 요소를 반환합니다.
4. React DOM이 <h1>Hello, Sara</h1> 과 일치하도록 DOM을 효율적으로 업데이트합니다.

Caveat:
컴포넌트 이름은 항상 대문자를 사용해야 합니다.
예를 들어 <div /> 는 DOM 태그를 나타내지만 <Welcome /> 은 컴포넌트를 나타내며 스코프에 Welcome 을 필요로 합니다.

컴포넌트 결합

컴포넌트는 출력될 때 다른 컴포넌트를 참조할 수 있습니다. 이를 통해 모든 세부 레벨에서 동일한 컴포넌트 추상화를 사용할 수 있습니다. React 앱에서 버튼, 폼, 다이얼로그, 스크린 같은 것들은 모두 일반적으로 컴포넌트로 표현됩니다.

예를 들어, 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>
  );
}

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

Try it on CodePen.

일반적으로, 새 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>
  );
}

Try it on CodePen.

이 컴포넌트는 author (객체), text (문자열), date (date)를 props로 받고, 소셜 미디어 웹사이트의 덧글을 나타냅니다.

이 컴포넌트는 중첩 때문에 변경하기 까다로울 수 있으며, 각 파트를 다시 사용하기도 어렵습니다. 여기에서 몇가지 컴포넌트를 추출해봅시다.

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

  );
}

AvatarComment 내에서 렌더링되는 지 알 필요가 없습니다. 따라서 속성을 author 대신 user 라는 더 일반적인 이름을 사용합니다. 컴포넌트가 사용되는 상황이 아닌 컴포넌트 자체 관점에서 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>
  );
}

Avartar 다음에 유저의 이름을 렌더링하는 UserInfo 컴포넌트를 추출

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

Try it on CodePen.

컴포넌트를 추출하는 건 처음에는 쓸데없는 일처럼 보일 수 있지만 재사용 가능한 컴포넌트 팔레트를 사용하면 큰 앱에서 비용을 줄입니다. 좋은 규칙은 UI의 일부가 여러번 사용되거나 (Button, Panel, Avatar), 자체적으로 충분히 복잡하면서 (App, FeedStory, Comment), 재사용 가능한 컴포넌트가 될 후보들 입니다.

props

props 는 properties 의 줄임말입니다. 우리가 어떠한 값을 컴포넌트에게 전달해줘야 할 때, props 를 사용합니다.

props 읽기 전용

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

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

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

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

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

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

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

props 의 기본 사용법

예를 들어서, App 컴포넌트에서 Hello 컴포넌트를 사용 할 때 name 이라는 값을 전달해주고 싶다고 가정해봅시다. 그러면, 이렇게 코드를 작성하면 됩니다.

App.js

import React from 'react';
import Hello from './Hello';

function App() {
  return (
    <Hello name="react" />
  );
}

export default App;

Hello.js name값 사용

import React from 'react';

function Hello(props) {
  return <div>안녕하세요 {props.name}</div>
}

export default Hello;

컴포넌트에게 전달되는 props 는 파라미터를 통하여 조회 할 수 있습니다. props 는 객체 형태로 전달되며, 만약 name 값을 조회하고 싶다면 props.name 을 조회하면 됩니다.

여러개의 props, 비구조화 할당

Hello 컴포넌트에 또 다른 props 를 전달해봅시다.

App.js

import React from 'react';
import Hello from './Hello';

function App() {
  return (
    <Hello name="react" color="red"/>	// color 값 생성
  );
}

export default App;

그 다음에는, Hello 컴포넌트에서 color 값을 조회해서 폰트의 색상으로 설정을 해보겠습니다.

Hello.js

import React from 'react';

function Hello(props) {
  return <div style={{ color: props.color }}>안녕하세요 {props.name}</div>
}

export default Hello;

props 내부의 값을 조회 할 때마다 props. 를 입력하고 있는데요, 함수의 파라미터에서 비구조화 할당 (혹은 구조 분해라고도 불립니다) 문법을 사용하면 조금 더 코드를 간결하게 작성 할 수 있습니다.

Hello.js

import React from 'react';

function Hello({ color, name }) {
  return <div style={{ color }}>안녕하세요 {name}</div>
}

export default Hello;

defaultProps 로 기본값 설정

컴포넌트에 props 를 지정하지 않았을 때 기본적으로 사용 할 값을 설정하고 싶다면 컴포넌트에 defaultProps 라는 값을 설정하면 됩니다.

Hello.js

import React from 'react';

function Hello({ color, name }) {
  return <div style={{ color }}>안녕하세요 {name}</div>
}

Hello.defaultProps = {
  name: '이름없음'
}

export default Hello;

App.js

import React from 'react';
import Hello from './Hello';

function App() {
  return (
    <>
      <Hello name="react" color="red"/>
      <Hello color="pink"/>		// 이름 없이 렌더링
    </>
  );
}

export default App;

profile
성장하는 프론트엔드 개발자

0개의 댓글