컴포넌트에 Props 전달하기

고성인·2025년 2월 13일

React

목록 보기
2/17

컴포넌트에 Props 전달하기

props는 jsx태그에 전달하는 정보이다.
기본적으로 존재하는 html태그에 전달할 수 있는 props는 미리 정의되어있지만, 커스텀 컴포넌트에 대해서는 어떤 props를 전달받을지 설정할 수 있다.

import { ReactNode } from "react";

interface Props {
  children: ReactNode;
  onSelect: () => void;
  isSelected: boolean;
}

export default function TabButton({ children, onSelect, isSelected }: Props) {
  console.log("TABBUTTON COMPONENT EXECUTING");
  return (
    <li>
      <button className={isSelected ? "active" : undefined} onClick={onSelect}>
        {children}
      </button>
    </li>
  );
}

위의 코드를 보면 컴포넌트를 선언할 때 매개변수로 어떠한 props들을 전달받을지 미리 선언해 둔 것을 볼 수 있다.
해당하는 커스텀 컴포넌트를 호출할 때 다음과 같이 props들을 넘겨줄 수 있다.

<TabButton isSelected={selectedTopic === "components"} onSelect={() => handleSelect("components")}>
      Components
</TabButton>

spread 문법으로 props 전달하기

위에서는 구조분해할당을 통해 어떤 props를 전달받을지 미리 설정해두었기 때문에 해당하는 props를 제외한 데이터는 넘겨줄 수 없다.
하지만 spread문법을 통해 props를 전달하게 되면 조금 더 유연하게 전달이 가능하다.

import { ButtonHTMLAttributes, ReactNode } from "react";

interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
  children: ReactNode;
  isSelected: boolean;
}

export default function TabButton({ children, isSelected, ...props }: Props) {
  console.log("TABBUTTON COMPONENT EXECUTING");
  return (
    <li>
      <button className={isSelected ? "active" : undefined} {...props}>
        {children}
      </button>
    </li>
  );
}

이전의 코드를 위와같이 변경하게되면 ...props를 통해 받아온 props들을 button태그로 넘겨줄 수 있게 된다.
현재 typescript를 사용하고 있기 때문에 interface Props extends ButtonHTMLAttributes<HTMLButtonElement>를 통해 미리 타입을 설정해주고있는데, extends ButtonHTMLAttributes<HTMLButtonElement>이 부분을 통해 버튼요소에 전달할 수 있는 모든 props들에 대한 타입을 가져와주고있다.

일반적인 태그들은 HTMLAttributes<HTMLElement>를 사용한다.

children prop

children property는 특수한 property로 자식을 jsx요소로 전달해주는 프로퍼티이다.
children에는 부모컴포넌트에서 값이나 컴포넌트 여러 콘텐츠 받아올 수 있기 때문에 잘 사용해야한다.

여러 jsx전달하기

위에서 children 프로퍼티를 통해 jsx요소를 전달할 수 있다고 하였는데, children prop은 특수한 프로퍼티라 한번밖에 사용하지 못한다.
그렇기때문에 wrapper컴포넌트를 선언할때는 특수한 패턴이 사용되게된다.
바로 jsx요소를 children이 아닌 props로 넘겨주는 패턴이다.

import { useState } from "react";
import TabButton from "./TabButton";
import { EXAMPLES } from "../data";
import Section from "./Section";

const Examples = () => {
  const [selectedTopic, setSelectedTopic] = useState<"components" | "jsx" | "props" | "state">();

  function handleSelect(selectedButton: "components" | "jsx" | "props" | "state") {
    // selectedButton => 'components', 'jsx', 'props', 'state'
    setSelectedTopic(selectedButton);
    // console.log(selectedTopic);
  }

  console.log("APP COMPONENT EXECUTING");

  let tabContent = <p>Please select a topic.</p>;

  if (selectedTopic) {
    tabContent = (
      <div id="tab-content">
        <h3>{EXAMPLES[selectedTopic].title}</h3>
        <p>{EXAMPLES[selectedTopic].description}</p>
        <pre>
          <code>{EXAMPLES[selectedTopic].code}</code>
        </pre>
      </div>
    );
  }
  return (
    <Section title="Examples" id="examples">
      <menu>
        <TabButton isSelected={selectedTopic === "components"} onClick={() => handleSelect("components")}>
          Components
        </TabButton>
        <TabButton isSelected={selectedTopic === "jsx"} onClick={() => handleSelect("jsx")}>
          JSX
        </TabButton>
        <TabButton isSelected={selectedTopic === "props"} onClick={() => handleSelect("props")}>
          Props
        </TabButton>
        <TabButton isSelected={selectedTopic === "state"} onClick={() => handleSelect("state")}>
          State
        </TabButton>
      </menu>
      {tabContent}
    </Section>
  );
};

export default Examples;

위의 코드에서

      <menu>
        <TabButton isSelected={selectedTopic === "components"} onClick={() => handleSelect("components")}>
          Components
        </TabButton>
        <TabButton isSelected={selectedTopic === "jsx"} onClick={() => handleSelect("jsx")}>
          JSX
        </TabButton>
        <TabButton isSelected={selectedTopic === "props"} onClick={() => handleSelect("props")}>
          Props
        </TabButton>
        <TabButton isSelected={selectedTopic === "state"} onClick={() => handleSelect("state")}>
          State
        </TabButton>
      </menu>
      {tabContent}

이 부분을 새로운 컴포넌트인 Tabs로 정의한다고 해보자.

import { ReactNode } from "react";

interface Props {
  children: ReactNode;
}

const Tabs = ({ children }: Props) => {
  return (
    <>
      <menu>
        <TabButton isSelected={selectedTopic === "components"} onClick={() => handleSelect("components")}>
          Components
        </TabButton>
        <TabButton isSelected={selectedTopic === "jsx"} onClick={() => handleSelect("jsx")}>
          JSX
        </TabButton>
        <TabButton isSelected={selectedTopic === "props"} onClick={() => handleSelect("props")}>
          Props
        </TabButton>
        <TabButton isSelected={selectedTopic === "state"} onClick={() => handleSelect("state")}>
          State
        </TabButton>
      </menu>
      {children}
    </>
  );
};

export default Tabs;

Tabs를 위와 같이 구현하여 <Tabs>{tabContent}</Tabs>이렇게 사용하게될경우 TabButton컴포넌트에 전달해야할 정보들을 다 옮겨와야하며, 재사용이 힘들어진다.

        <TabButton isSelected={selectedTopic === "components"} onClick={() => handleSelect("components")}>
          Components
        </TabButton>
        <TabButton isSelected={selectedTopic === "jsx"} onClick={() => handleSelect("jsx")}>
          JSX
        </TabButton>
        <TabButton isSelected={selectedTopic === "props"} onClick={() => handleSelect("props")}>
          Props
        </TabButton>
        <TabButton isSelected={selectedTopic === "state"} onClick={() => handleSelect("state")}>
          State
        </TabButton>

위의 부분을 children으로 넘겨주려해도 이미 children prop은 사용중이라 추가적으로 사용할 수 없다.

이럴때 jsx요소를 children이 아닌 일반 prop으로 전달하는 패턴을 사용하게된다.

import { ReactNode } from "react";

interface Props {
  children: ReactNode;
  buttons: ReactNode;
}

const Tabs = ({ children, buttons }: Props) => {
  return (
    <>
      <menu>{buttons}</menu>
      {children}
    </>
  );
};

export default Tabs;

Tabs에서 property로 buttons라는것을 ReactNode타입으로 받아와준 뒤 Tabs컴포넌트에 prop으로 jsx코드를 넘겨주면 된다.

      <Tabs
        buttons={
          <>
            <TabButton isSelected={selectedTopic === "components"} onClick={() => handleSelect("components")}>
              Components
            </TabButton>
            <TabButton isSelected={selectedTopic === "jsx"} onClick={() => handleSelect("jsx")}>
              JSX
            </TabButton>
            <TabButton isSelected={selectedTopic === "props"} onClick={() => handleSelect("props")}>
              Props
            </TabButton>
            <TabButton isSelected={selectedTopic === "state"} onClick={() => handleSelect("state")}>
              State
            </TabButton>
          </>
        }
      >
        {tabContent}
      </Tabs>

컴포넌트 타입 동적으로 설정하기

이번에도 역시 Tabs컴포넌트를 수정해볼것이다.

import { ReactNode } from "react";

interface Props {
  children: ReactNode;
  buttons: ReactNode;
}

const Tabs = ({ children, buttons }: Props) => {
  return (
    <>
      <menu>{buttons}</menu>
      {children}
    </>
  );
};

export default Tabs;

위의 코드를 조금 더 유연하게 만들어볼것이다.
그러기위해 buttons를 감싸고있는 menu태그를 menu로 고정하는것이 아닌 동적으로 받아오려한다.
그렇게하기위해서는 다음과 같이 코드를 수정해주면 된다.

import { ElementType, ReactNode } from "react";

interface Props {
  children: ReactNode;
  buttons: ReactNode;
  ButtonContainer: ElementType;
}

const Tabs = ({ children, buttons, ButtonContainer }: Props) => {
  return (
    <>
      <ButtonContainer>{buttons}</ButtonContainer>
      {children}
    </>
  );
};

export default Tabs;

ElementType을 통해 jsx요소를 ButtonContainer에 동적으로 받아올 수 있게되고 Tabs컴포넌트를 사용할때는 다음과 같이 사용하면 된다.

      <Tabs
        ButtonContainer={"menu"}
        buttons={
          <>
            <TabButton isSelected={selectedTopic === "components"} onClick={() => handleSelect("components")}>
              Components
            </TabButton>
            <TabButton isSelected={selectedTopic === "jsx"} onClick={() => handleSelect("jsx")}>
              JSX
            </TabButton>
            <TabButton isSelected={selectedTopic === "props"} onClick={() => handleSelect("props")}>
              Props
            </TabButton>
            <TabButton isSelected={selectedTopic === "state"} onClick={() => handleSelect("state")}>
              State
            </TabButton>
          </>
        }
      >
        {tabContent}
      </Tabs>

여기서 중요한점은 기본적으로 존재하는 html요소의 경우 ""를 통해 문자열로 넘겨주어야하며, 커스텀컴포넌트의 경우 ""나 <>없이 그대로 넘겨주어야한다.ButtonContainer={Section}

0개의 댓글