4강. Props (속성)
1. Props (속성)
부모 컴포넌트가 자식 컴포넌트에게 데이터를 전달하는 방법.
1.1. Props의 개념
Props는 Properties(속성)의 줄임말로, 부모 컴포넌트가 자식 컴포넌트에게 데이터를 전달할 때 사용하는 방법이다.
컴포넌트를 함수라고 생각하면, Props는 그 함수에 전달하는 **매개변수(파라미터)**다.
// 일반 자바스크립트 함수function greet(name) { // name은 매개변수 return `안녕, ${name}!`;}greet("철수"); // "철수"는 인자
// React 컴포넌트function Greeting(props) { // props는 매개변수 return <h1>안녕, {props.name}!</h1>;}<Greeting name="철수" /> // name="철수"는 prop1.1.1. 데이터 흐름: 단방향 (Top-Down)
React의 핵심 원칙: 단방향 데이터 흐름
데이터는 오직 위에서 아래로만 흐른다. 물이 높은 곳에서 낮은 곳으로 흐르는 것과 같다.
// 할아버지 → 아버지 → 자식 순서로 흐름function Grandparent() { const data = "중요한 정보"; return <Parent info={data} />; // 아래로 전달}
function Parent(props) { return <Child info={props.info} />; // 더 아래로 전달}
function Child(props) { return <div>{props.info}</div>; // 최종 사용}⚠️ 자식이 부모에게 직접 데이터를 전달할 수는 없다! (나중에 배울 “콜백 함수”를 통해 간접적으로 가능)
1.1.2. Props의 핵심 특징
- 읽기 전용 (Read-Only): 자식 컴포넌트는 받은 Props를 절대 수정할 수 없다. 오직 읽기만 가능하다.
- 재사용성: 같은 컴포넌트에 다른 Props를 전달하여 다양한 결과를 만들 수 있다.
- 타입 제한 없음: 문자열, 숫자, 불리언, 객체, 배열, 함수 등 모든 자바스크립트 값을 전달 가능하다.
요약
왜 Props를 수정하면 안 될까?
React는 Props가 변하지 않는다고 가정하고 최적화를 수행한다. Props를 수정하면 예측 불가능한 버그가 발생하고 성능이 저하된다.
// ❌ 절대 하면 안 되는 것function Profile(props) { props.name = "다른 이름"; // 오류! Props는 읽기 전용! return <h1>{props.name}</h1>;}
// ✅ 올바른 방법: 새로운 변수를 만들어 사용function Profile(props) { const displayName = props.name.toUpperCase(); return <h1>{displayName}</h1>;}1.2. Props 전달하고 받기
1.2.1. Props 전달 방법 (부모 → 자식)
HTML 속성을 쓰는 것과 비슷하게 컴포넌트 태그에 속성을 추가한다.
// App.jsx (부모 컴포넌트)function App() { return ( <div> {/* 문자열 prop: 따옴표 사용 */} <Greeting name="김철수" />
{/* 숫자 prop: 중괄호 사용 */} <Age value={25} />
{/* 불리언 prop */} <User isAdmin={true} /> <User isAdmin={false} /> <User isAdmin /> {/* true는 생략 가능 */}
{/* 배열 prop */} <List items={[1, 2, 3, 4, 5]} />
{/* 객체 prop */} <Profile user={{ name: "철수", age: 25 }} /> </div> );}포인트
- 문자열: 따옴표로 감싸기 →
name="철수" - 그 외 모두: 중괄호로 감싸기 →
age={25},isActive={true}
// ❌ 잘못된 예<Age value="25" /> // 숫자가 아니라 문자열 "25"가 전달됨!
// ✅ 올바른 예<Age value={25} /> // 숫자 25가 전달됨1.2.2. Props 받기 (자식 컴포넌트)
자식 컴포넌트 함수의 첫 번째 매개변수로 props 객체를 받는다.
// Profile.jsx (자식 컴포넌트)function Profile(props) { // props는 객체 형태로 전달됨 console.log(props); // { name: "김철수", age: 25, isMember: true }
return ( <div className="border border-gray-300 p-5 m-2 rounded"> <h2>이름: {props.name}</h2> <p>나이: {props.age}세</p> <p>회원여부: {props.isMember ? "정회원" : "준회원"}</p>
{/* props를 이용한 조건부 렌더링 */} {props.isMember && <span className="text-green-500">✓ 인증됨</span>} </div> );}
export default Profile;1.2.3. Props 사용 예제: 재사용 가능한 버튼
- Button.jsx
- Button.css
- App.jsx
import './Button.css';.btn {import Button from './Button';
import './Button.css';
function Button(props) { // props로 버튼의 모양과 동작을 커스터마이징 return ( <button className={`btn ${props.size === 'large' ? 'btn-lg' : ''}`} style={{ backgroundColor: props.color || '#007bff' }} onClick={props.onClick} disabled={props.disabled} > {props.text} </button> );}
export default Button;
.btn { color: white; padding: 10px 20px; border: none; border-radius: 5px; font-size: 14px; cursor: pointer;}
.btn-lg { font-size: 18px;}
import Button from './Button';
function App() { return ( <div> <Button text="기본 버튼" /> <Button text="빨간 버튼" color="red" /> <Button text="큰 버튼" size="large" color="#28a745" /> </div> );}
1.3. 구조 분해 할당 (Destructuring)
1.3.1. 왜 구조 분해 할당을 사용할까?
매번 props.name, props.age처럼 props.를 반복하는 것은 번거롭다. 구조 분해 할당을 사용하면 코드가 훨씬 간결해진다.
정보
Before (props 객체 사용)
function Profile(props) { return ( <div> <h2>{props.name}</h2> <p>나이: {props.age}</p> </div> );}
After (구조 분해 할당 사용)
function Profile({ name, age }) { return ( <div> <h2>{name}</h2> <p>나이: {age}</p> </div> );}
1.4. Children Props
1.4.1. Children이란?
Children은 컴포넌트 태그 사이에 넣은 내용을 전달하는 특별한 prop이다. 일반 props와 달리 속성으로 전달하지 않는다.
정보
일반 Prop vs Children Prop
// 일반 Props: 속성으로 전달<Button text="클릭" />
// Children: 태그 사이에 전달<Button>클릭</Button>
1.4.2. Children의 활용
Card 안에 Button을 넣어서 컴포넌트를 조합하는 예제이다.
- Button.jsx
- Card.jsx
- App.jsx
- Button.css
- Card.css
import './Button.css';import './Card.css';import Card from './Card';.btn {.card {
import './Button.css';
function Button({ children, color }) { return ( <button className="btn" style={{ backgroundColor: color || '#007bff' }} > {children} </button> );}
export default Button;
import './Card.css';
function Card({ children }) { return ( <div className="card"> {children} </div> );}
export default Card;
import Card from './Card';import Button from './Button';
function App() { return ( <div> {/* Card 안에 텍스트 + Button 조합 */} <Card> <h2>회원가입</h2> <p>아래 버튼을 눌러 가입하세요.</p> <Button>가입하기</Button> </Card>
<Card> <h2>설정</h2> <p>계정을 관리합니다.</p> <Button color="#28a745">저장</Button> <Button color="red">삭제</Button> </Card> </div> );}
.btn { color: white; padding: 10px 20px; border: none; border-radius: 5px; font-size: 14px; cursor: pointer; margin-right: 8px;}
.card { border: 1px solid #ddd; border-radius: 10px; padding: 20px; margin: 10px; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);}
요약
Children을 활용하면 Card 안에 Button, Button 안에 아이콘 등 컴포넌트를 자유롭게 조합할 수 있다. 이것이 React의 합성(Composition) 패턴이다.