React Props

김소희·2025년 11월 3일

Props란?

리액트에서 부모 컴포넌트가 자식 컴포넌트에 데이터를 전달할 때 사용하는 것이 Props이다.
(ref와 key 속성은 예외적으로 전달할 수 없었지만, 19버전에서 ref는 전달받게 변경되었다.)

Props 객체는 컴포넌트를 HTML 태그처럼 사용해 값을 속성 형태로 전달한다.
여러 데이터를 전달하고 싶을 때는 속성을 여러 개 추가하면 된다.

Props의 핵심 특성

  1. 읽기 전용(Immutable): Props는 자식 컴포넌트에서 절대 수정할 수 없다.
  2. 단방향 데이터 흐름(One-way Data Flow): 부모에서 자식으로만 데이터가 전달된다.
  3. 타입 안정성: PropTypes나 TypeScript로 타입을 명시하는 것이 실무에서 권장된다.

기본 사용법

문자열 리터럴 전달

function Car(props) {
  return <h2>I am a {props.brand}!</h2>;
}

function Garage() {
  return (
    <>
      <h1>Who lives in my garage?</h1>
      <Car brand="Ford" />
    </>
  );
}

변수로 전달

function Car(props) {
  return <h2>I am a {props.brand}!</h2>;
}

function Garage() {
  const carName = "Ford";
  return (
    <>
      <h1>Who lives in my garage?</h1>
      <Car brand={carName} />
    </>
  );
}

객체 통째로 전달

데이터마다 개별 props로 지정할 수 있지만, 데이터 개수가 많다면 배열이나 객체로 묶어 전달하는 것이 깔끔하고 효율적이다.

function Car(props) {
  // props.brand가 객체이므로 내부 속성에 접근
  return <h2>I am a {props.brand.model}!</h2>;
}

function Garage() {
  const carInfo = { name: "Ford", model: "Mustang" };
  return (
    <>
      <h1>Who lives in my garage?</h1>
      <Car brand={carInfo} />
    </>
  );
}

Props 받는 방법: 그대로 vs 구조 분해

props는 단일 객체이므로 부모 컴포넌트에 속성이 몇십 개 지정되어도 결국 하나의 props 객체로 전달된다.
이때 구조 분해 할당을 이용하면 객체의 속성을 더 간단하게 변수에 할당할 수 있다.

Props 객체 그대로 받기

function MyCom(props) {
  return <div>{props.name}</div>;
}

장점: 어떤 props가 오든 한 함수 시그니처로 모두 접근 가능
단점: 매번 props.xxx로 접근해야 해서 장황해질 수 있음

구조 분해 할당으로 받기

props가 객체이므로, 필요한 속성 이름만 뽑아 바로 변수로 받을 수 있다.
React에서 이 패턴은 매우 자주 쓰이며, 컴포넌트가 어떤 props를 사용하는지 시그니처만 봐도 한눈에 드러나기 때문에 가독성이 올라간다.

function MyCom({ name }) {
  return <div>{name}</div>;
}

장점: 필요한 값만 변수로 바로 꺼내서 간결
단점: 어떤 prop을 쓰는지 시그니처에 명시해야 함

주의: ({props})는 "props라는 이름의 prop"을 구조 분해하려는 문법이므로 보통 의도와 다르다. 대부분은 { name, age }처럼 개별 prop 이름을 직접 꺼낸다.


기본값(Default Props) 설정

Props가 전달되지 않았을 때 사용할 기본값을 설정할 수 있다.

function MyComponent({ name = "Guest", age = 0 }) {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
    </div>
  );
}

// 사용
<MyComponent /> // Name: Guest, Age: 0
<MyComponent name="John" /> // Name: John, Age: 0

여러 Props 전달하기

개별 속성으로 전달

function App() {
  return (
    <MyComponent
      name="John Doe"
      age={30}
      email="john@example.com"
      address="123 Main St"
    />
  );
}

function MyComponent({ name, age, email }) {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
      <p>Email: {email}</p>
    </div>
  );
}

Spread 연산자로 전달

객체의 모든 속성을 props로 펼쳐서 전달할 수 있다.

function App() {
  const userProps = { 
    name: "John Doe", 
    age: 30, 
    email: "john@example.com" 
  };
  
  return <MyComponent {...userProps} />;
}

function MyComponent({ name, age, email }) {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
      <p>Email: {email}</p>
    </div>
  );
}

나머지 Props 받기(Rest) — 디버깅/프록시 용도

function MyComponent({ name, age, ...rest }) {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
      <p>Other props: {JSON.stringify(rest)}</p>
    </div>
  );
}

rest에는 email, address처럼 명시하지 않은 나머지 prop이 객체로 들어온다.
실무에서는 남용보다는 필요한 곳에서만 사용 권장.


중첩 객체 Props 다루기

필요한 속성만 뽑아서 새 객체로 전달

function App() {
  // 원본 user 객체
  const user = { name: "John Doe", birthdate: "1990-01-01", age: 30 };

  // 필요한 age 속성만 포함한 객체 생성
  const ageOnlyUser = { age: user.age };

  return <MyComponent user={ageOnlyUser} />;
}

function MyComponent({ user: { age } }) {
  return (
    <div>
      <p>Age: {age}</p>
    </div>
  );
}

통째로 넘기되, 받는 쪽에서 특정 속성만 구조 분해

function App() {
  const user = { name: "John Doe", birthdate: "1990-01-01", age: 30 };
  return <MyComponent user={user} />;
}

function MyComponent({ user: { age } }) {
  return (
    <div>
      <p>Age: {age}</p>
    </div>
  );
}

function MyComponent({ user: { age } })는 "user prop 안의 age만 변수로 꺼내 쓰겠다"는 뜻이다.

중첩 구조 분해 시 주의사항

중첩 구조 분해는 객체가 항상 존재한다고 가정한다. 객체가 undefined일 경우 에러가 발생한다.

// ❌ user가 undefined면 에러 발생
function MyComponent({ user: { age } }) {
  return <div>{age}</div>;
}

// ✅ 안전한 방법 1: 기본값 제공
function MyComponent({ user: { age } = {} }) {
  return <div>{age}</div>;
}

// ✅ 안전한 방법 2: Optional Chaining 사용
function MyComponent({ user }) {
  const age = user?.age;
  return <div>{age}</div>;
}

// ✅ 안전한 방법 3: 기본값과 함께
function MyComponent({ user = {} }) {
  const { age } = user;
  return <div>{age}</div>;
}

Children Prop

children은 특별한 prop으로, 컴포넌트 태그 사이의 내용을 자동으로 전달받는다.

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

// 사용
function App() {
  return (
    <Card>
      <h1>Title</h1>
      <p>Content goes here</p>
    </Card>
  );
}

children을 활용하면 컴포넌트를 더 유연하고 재사용 가능하게 만들 수 있다.


함수를 Props로 전달하기

이벤트 핸들러나 콜백 함수를 props로 전달할 수 있다. 이를 통해 자식 컴포넌트에서 부모 컴포넌트의 상태를 변경하거나 이벤트를 알릴 수 있다.

function Button({ onClick, label }) {
  return <button onClick={onClick}>{label}</button>;
}

function App() {
  const handleClick = () => {
    alert("Button clicked!");
  };
  
  return <Button onClick={handleClick} label="Click Me" />;
}

부모 상태 업데이트 예시

function Counter({ count, onIncrement }) {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={onIncrement}>Increment</button>
    </div>
  );
}

function App() {
  const [count, setCount] = useState(0);
  
  return (
    <Counter 
      count={count} 
      onIncrement={() => setCount(count + 1)} 
    />
  );
}

Props 네이밍 컨벤션

일관된 네이밍은 코드 가독성을 높인다.

이벤트 핸들러

  • Props 이름: on으로 시작 (onClick, onChange, onSubmit)
  • 함수 이름: handle로 시작 (handleClick, handleChange, handleSubmit)
function SearchBar({ onSearch }) {
  const handleSubmit = (e) => {
    e.preventDefault();
    onSearch(e.target.value);
  };
  
  return <form onSubmit={handleSubmit}>...</form>;
}

Boolean 값

  • is, has, should 등의 접두사 사용
function Button({ isDisabled, hasIcon, shouldAutoFocus }) {
  return (
    <button 
      disabled={isDisabled}
      autoFocus={shouldAutoFocus}
    >
      {hasIcon && <Icon />}
      Click Me
    </button>
  );
}

Props 타입 검증

PropTypes 사용 (JavaScript)

import PropTypes from 'prop-types';

function MyComponent({ name, age, email }) {
  return <div>{name}, {age}, {email}</div>;
}

MyComponent.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number,
  email: PropTypes.string
};

MyComponent.defaultProps = {
  age: 0,
  email: 'N/A'
};

TypeScript 사용 (권장)

interface MyComponentProps {
  name: string;
  age?: number;
  email?: string;
}

function MyComponent({ name, age = 0, email = 'N/A' }: MyComponentProps) {
  return <div>{name}, {age}, {email}</div>;
}

실무에서는 TypeScript 사용이 강력히 권장된다. 컴파일 시점에 타입 오류를 잡을 수 있어 안정성이 높아진다.


요약

  • Props는 읽기 전용: 자식 컴포넌트에서 수정할 수 없다
  • 단방향 데이터 흐름: 부모 → 자식으로만 전달
  • 구조 분해 할당: 필요한 props만 간결하게 추출
  • 기본값 설정: 선택적 props에 기본값 제공
  • Children: 컴포넌트 합성을 위한 특별한 prop
  • 함수 전달: 이벤트 핸들러로 부모-자식 간 소통
  • 네이밍 컨벤션: 일관된 명명 규칙으로 가독성 향상
  • 타입 검증: PropTypes나 TypeScript로 안정성 확보

참고자료

profile
개발자 소희의 노트

0개의 댓글