이론 (Front-end)/React

[React] Props, Memo, Prop Types

이우열 2023. 7. 13. 02:54
728x90

✅ Props란?

부모 컴포넌트에서 자식 컴포넌트로 데이터를 보낼 수 있게 해주는 방법

 

✏️ Props가 필요한 이유

예제를 통해서 알아봅시다.

function SaveBtn() {
  return (
    <button
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
      }}
    >
      Save Changes
    </button>
  );
}

function ConfirmBtn() {
  return (
    <button
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
      }}
    >
      Confirm
    </button>
  );
}

function App() {
  return (
    <div>
      <SaveBtn />
      <ConfirmBtn />
    </div>
  );
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);

위의 코드는 SaveBtn 컴포넌트와 ConfirmBtn 컴포넌트가 존재하는 코드입니다.

두 개의 컴포넌트는 각각 버튼을 나타내고 다른 점이라고는 버튼 안에 있는 텍스트의 내용 뿐입니다.

 

이런 버튼들이 필요할 때마다 같은 코드를 반복해서 사용한다면 복잡한 코드가 될 것이고 버튼 디자인을 수정하기 위해서는 모든 컴포넌트의 내용을 각각 수정해주어야 합니다.

 

즉, 하나의 컴포넌트를 만들어 컴포넌트를 재사용하여 코드의 길이를 줄이고 텍스트 내용만을 다르게 설정하는 것이 좋은 코드입니다.

 

 

✏️ Props 적용 후

function Btn(props) {
  console.log(props);
  return (
    <button
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
      }}
    >
      {props.text}
    </button>
  );
}

function App() {
  return (
    <div>
      <Btn text="Save Changes" />
      <Btn text="Continue" />
    </div>
  );
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);

Btn이라는 하나의 컴포넌트로 코드를 수정하고 파라미터(보통 props로 사용)를 통해 값을 받도록 하였습니다.

모든 컴포넌트는 생성될 때 인자를 함께 작성하여 생성해야 합니다.

 

React.js는 자동으로 컴포넌트를 생성할 때 넣은 모든 property(prop)들을 하나의 Object로 만들어서 컴포넌트의 첫 번째 인자로 넣어줍니다.

두 번째 인자는 없고 첫 번째 인자가 처음이자 유일한 인자입니다.

 

 

<Btn text="Save Changes" />Btn({ text: "Save Changes" })로 함수를 호출하는 것과 같습니다.

 

 

Btn 컴포넌트에서 props를 콘솔 로그로 출력해보면 다음과 같습니다.

 

 

✏️ Props ShortCut

property를 Object로부터 꺼내는 방식

 

function Btn({ text }) {
  return (
    <button
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
      }}
    >
      {text}
    </button>
  );
}

 

props 안에 text가 있다는 사실을 알고 있기 때문에 중괄호를 사용하여 바로 꺼내서 사용할 수 있음

 

 

✏️ Props 응용

function Btn({ text, big }) {
  console.log(text, big);
  return (
    <button
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
        fontSize: big ? 18 : 16,
      }}
    >
      {text}
    </button>
  );
}

function App() {
  return (
    <div>
      <Btn text="Save Changes" big={true} />
      <Btn text="Continue" />
    </div>
  );
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);

 

Btn 컴포넌트 안에서 지정한 style 중 fontSize 부분을 보면 big을 통한 if-else 구문을 통해 폰트 크기를 지정할 수 있습니다.

 

첫 번째로 만든 Btn 컴포넌트는 big의 값이 true이기 때문에 fontSize가 18이 되지만

두 번째로 만든 Btn 컴포넌트는 big을 인자로 넘겨주지 않았기 때문에 undefined가 됩니다.

 

 

✏️ Props 함수 전달

컴포넌트 안에 함수를 전달하는 방법

 

function Btn({ text, onClick }) {
  return (
    <button
      onClick={onClick}
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
        fontSize: 16,
      }}
    >
      {text}
    </button>
  );
}

function App() {
  const [value, setValue] = React.useState("Save Changes");
  const changeValue = () => {
    setValue("Revert Changes");
  };
  return (
    <div>
      <Btn text={value} onClick={changeValue} />
      <Btn text="Continue" />
    </div>
  );
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);

 

컴포넌트 안에 함수를 전달할 수도 있습니다.

Btn 컴포넌트를 생성할 때 textonClickprop으로 넘겨주는 방식으로 사용합니다.

 

이 때 onClick이벤트리스너가 아니라 그냥 prop의 이름입니다.

HTML 요소(button 태그) 안에 onClick을 넣었을 경우에는 이벤트리스너지만 커스텀 컴포넌트에 넣었을 때는 prop가 됩니다.

 

prop에 뭐든지 넣을 수 있지만 사용하기 위해서는 직접 꺼내서 return 안에 써줘야 합니다.

button 태그에 이벤트리스너 onClick 함수에 prop으로 전달받은 onClick을 넣어줍니다.

 


 

✅ Memo

function Btn({ text, changeValue }) {
  console.log(text, "was rendered");
  return (
    <button
      onClick={changeValue}
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
        fontSize: 16,
      }}
    >
      {text}
    </button>
  );
}

function App() {
  const [value, setValue] = React.useState("Save Changes");
  const changeValue = () => {
    setValue("Revert Changes");
  };
  return (
    <div>
      <Btn text={value} changeValue={changeValue} />
      <Btn text="Continue" />
    </div>
  );
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);

 

버튼이 렌더링되는 것을 확인해보면 부모 컴포넌트의 state가 변경되었을 때 버튼이 리렌더링 됩니다.

즉, 부모 컴포넌트의 state자식 컴포넌트가 변경하고 부모 컴포넌트를 다시 렌더링하는 것입니다.

 

만약 Save Changes의 텍스트를 가지는 Btn 컴포넌트를 클릭할 경우 텍스트가 Revert Changes로 변하며 부모 컴포넌트가 리렌더링됩니다.

이 때, 변하지 않은 Continue의 텍스트를 가지는 Btn 컴포넌트 또한 리렌더링됩니다.

 

만약 props가 변하지 않는다면 우리는 컴포넌트를 다시 그릴지 말지를 정할 수 있습니다.

 

첫 번째 버튼의 propsstate와 연결되어 있기 때문에 state가 변하면 무조건 리렌더링을 해야 합니다.

두 번째 버튼의 경우 props절대 변하지 않기 때문에 리렌더링을 할 필요가 없습니다.

 

 

✏️ Memo 사용

function Btn({ text, changeValue }) {
  console.log(text, "was rendered");
  return (
    <button
      onClick={changeValue}
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
        fontSize: 16,
      }}
    >
      {text}
    </button>
  );
}

const MemorizedBtn = React.memo(Btn);

function App() {
  const [value, setValue] = React.useState("Save Changes");
  const changeValue = () => {
    setValue("Revert Changes");
  };
  return (
    <div>
      <MemorizedBtn text={value} changeValue={changeValue} />
      <MemorizedBtn text="Continue" />
    </div>
  );
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);

React.memo라는 것을 사용하여 props의 변화가 없다면 리렌더링을 스킵할 수 있습니다.

 

Btn 컴포넌트를 React.memo를 사용하여 MemorizedBtn로 다시 선언하고

MemorizedBtn을 사용하여 컴포넌트를 생성하면 변화가 없는 컴포넌트는 다시 렌더링 되지 않습니다.

 


 

✅ Prop Types

만약 prop가 많아진다면 타입에 대한 오류를 피할 수 없습니다.

이를 극복하기 위해 Prop Types를 사용합니다.

 

function Btn({ text, fontSize }) {
  return (
    <button
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
        fontSize,
        // fontSize: fontSize,
      }}
    >
      {text}
    </button>
  );
}

function App() {
  return (
    <div>
      <Btn text="Save Changes" fontSize={18} />
      <Btn text={14} fontSize={"Continue"} />
    </div>
  );
}
const root = document.getElementById("root");
// ReactDOM.render(<App />, root);
ReactDOM.createRoot(root).render(<App />);

 

위와 같이 하나의 Btn에는 text에 문자열, fontSize에 숫자를 넣었고

다른 하나는 text에 숫자, fontSize에 문자열을 넣었습니다.

 

하지만 React.js는 오류를 출력해주지 않습니다.

코드 상에는 타입이 지정되어 있지 않기 때문입니다.

 

우리는 prop의 원하는 타입이 있기 때문에 오류를 출력해주기를 희망합니다.

따라서 오류를 출력하기 위해 Prop Types라는 패키지를 사용하면 됩니다.

 

 

✏️ Prop Types 사용

<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>
  </body>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  <script type="text/babel">
    function Btn({ text, fontSize }) {
      return (
        <button
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
            fontSize,
            // fontSize: fontSize,
          }}
        >
          {text}
        </button>
      );
    }

    Btn.propTypes = {
      text: PropTypes.string,
      fontSize: PropTypes.number,
    };

    function App() {
      return (
        <div>
          <Btn text="Save Changes" fontSize={18} />
          <Btn text={14} fontSize={"Continue"} />
        </div>
      );
    }
    const root = document.getElementById("root");
    // ReactDOM.render(<App />, root);
    ReactDOM.createRoot(root).render(<App />);
  </script>
</html>

 

<script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>

스크립트 태그로 패키지를 불러와서 사용합니다.

 

 

원하는 컴포넌트의 propTypes를 선언하여 타입을 지정해줍니다.

React에게 prop들의 type에 대해 설명해주는 것입니다.

Btn.propTypes = {
  text: PropTypes.string,
  fontSize: PropTypes.number,
};

 

위와 같은 경고를 볼 수 있고 이는 오류가 아닌 개발자들을 위한 단지 경고일 뿐입니다.

Btn의 'text' 타입은 'string'이지만 'number'가 들어와서 유효하지 않다는 뜻입니다.

 

 

✏️ Prop Types isRequired

꼭 필요한 prop이 있다면 isRequired를 붙여서 사용

 

function Btn({ text, fontSize }) {
  return (
    <button
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
        fontSize,
        // fontSize: fontSize,
      }}
    >
      {text}
    </button>
  );
}

Btn.propTypes = {
  text: PropTypes.string.isRequired,
  fontSize: PropTypes.number.isRequired,
};

function App() {
  return (
    <div>
      <Btn text="Save Changes" fontSize={18} />
      <Btn text={"Continue"} />
    </div>
  );
}
const root = document.getElementById("root");
// ReactDOM.render(<App />, root);
ReactDOM.createRoot(root).render(<App />);

 

Btn 컴포넌트에 'fontSize' prop이 없기 때문에 'undefined'는 오류라고 알려주고 있습니다.

 

 

✏️ Default 설정

function Btn({ text, fontSize = 12 }) {
  return (
    <button
      style={{
        backgroundColor: "tomato",
        color: "white",
        padding: "10px 20px",
        border: 0,
        borderRadius: 10,
        fontSize,
        // fontSize: fontSize,
      }}
    >
      {text}
    </button>
  );
}

Btn.propTypes = {
  text: PropTypes.string.isRequired,
  fontSize: PropTypes.number,
};

function App() {
  return (
    <div>
      <Btn text="Save Changes" fontSize={18} />
      <Btn text={"Continue"} />
    </div>
  );
}
const root = document.getElementById("root");
// ReactDOM.render(<App />, root);
ReactDOM.createRoot(root).render(<App />);

 

만약 prop 값을 넘겨주지 않았다면 기본적으로 값을 미리 설정해둘 수 있습니다.

Btn 컴포넌트 선언 부분에서 파라미터 값을 미리 설정해두는 방식이며

이 방식은 React에 관한 것이 아닌 JavaScript 문법이기 때문에 가능한 방식입니다.

728x90