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';
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>
);
}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';
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);
}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) 패턴이다.