Props 다루기
2. Props 다루기
Props는 "properties"의 줄임말로, 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하는 방법입니다. Props를 사용하면 컴포넌트를 재사용 가능하고 동적으로 만들 수 있습니다.
2.1 Props란 무엇인가요?
Props는 컴포넌트에 전달되는 읽기 전용 데이터입니다. 함수의 매개변수와 비슷한 역할을 합니다.
2.1.1 Props 없는 컴포넌트
function UserCard() {
return (
<div>
<h3>김개발</h3>
<p>나이: 25세</p>
<p>직업: 프론트엔드 개발자</p>
</div>
);
}
2.1.2 Props를 사용한 컴포넌트
function UserCard(props) {
return (
<div>
<h3>{props.name}</h3>
<p>나이: {props.age}세</p>
<p>직업: {props.job}</p>
</div>
);
}
// 사용하기
function App() {
return (
<div>
<UserCard name="김개발" age={25} job="프론트엔드 개발자" />
<UserCard name="이디자인" age={28} job="UI/UX 디자이너" />
<UserCard name="박백엔드" age={30} job="백엔드 개발자" />
</div>
);
}
2.2 Props의 기본 사용법
2.2.1 문자열 Props 전달
function Greeting(props) {
return <h1>안녕하세요, {props.name}님!</h1>;
}
function App() {
return (
<div>
<Greeting name="김개발" />
<Greeting name="이디자인" />
</div>
);
}
2.2.2 숫자와 불린 Props 전달
function Counter(props) {
return (
<div>
<h2>카운터: {props.count}</h2>
<p>활성화 상태: {props.isActive ? '활성' : '비활성'}</p>
</div>
);
}
function App() {
return (
<div>
<Counter count={10} isActive={true} />
<Counter count={0} isActive={false} />
</div>
);
}
2.2.3 배열과 객체 Props 전달
function SkillList(props) {
return (
<div>
<h3>{props.title}</h3>
<ul>
{props.skills.map((skill, index) => (
<li key={index}>{skill}</li>
))}
</ul>
</div>
);
}
function UserProfile(props) {
return (
<div>
<h2>{props.user.name}</h2>
<p>이메일: {props.user.email}</p>
<SkillList title="보유 기술" skills={props.user.skills} />
</div>
);
}
function App() {
const userData = {
name: '김개발',
email: 'kim@example.com',
skills: ['JavaScript', 'React', 'Node.js']
};
return (
<div>
<UserProfile user={userData} />
</div>
);
}
2.3 Props 구조 분해 할당
Props를 더 깔끔하게 사용하기 위해 구조 분해 할당을 사용할 수 있습니다.
2.3.1 기본 방법
function UserCard(props) {
return (
<div>
<h3>{props.name}</h3>
<p>나이: {props.age}세</p>
<p>직업: {props.job}</p>
</div>
);
}
2.3.2 구조 분해 할당 사용
function UserCard({ name, age, job }) {
return (
<div>
<h3>{name}</h3>
<p>나이: {age}세</p>
<p>직업: {job}</p>
</div>
);
}
2.3.3 기본값과 함께 구조 분해 할당
function UserCard({ name, age = 0, job = '미정', isVip = false }) {
return (
<div>
<h3>{name} {isVip && '👑'}</h3>
<p>나이: {age}세</p>
<p>직업: {job}</p>
{isVip && <p>VIP 회원</p>}
</div>
);
}
function App() {
return (
<div>
<UserCard name="김개발" age={25} job="프론트엔드 개발자" isVip={true} />
<UserCard name="이신입" /> {/* 기본값 사용 */}
<UserCard name="박경력" age={35} job="시니어 개발자" />
</div>
);
}
2.4 children Props
특별한 prop인 children을 사용하면 컴포넌트 사이에 JSX를 전달할 수 있습니다.
2.4.1 기본 children 사용법
function Card({ children, title }) {
return (
<div>
{title && <h3>{title}</h3>}
<div>{children}</div>
</div>
);
}
function App() {
return (
<div>
<Card title="공지사항">
<p>새로운 기능이 출시되었습니다!</p>
<button>자세히 보기</button>
</Card>
<Card title="사용자 정보">
<h4>김개발</h4>
<p>프론트엔드 개발자</p>
<ul>
<li>JavaScript</li>
<li>React</li>
<li>TypeScript</li>
</ul>
</Card>
<Card>
<p>제목 없는 카드입니다.</p>
<img src="https://via.placeholder.com/200x100" alt="placeholder" />
</Card>
</div>
);
}
2.4.2 복잡한 children 활용
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return (
<div>
<div>
<button onClick={onClose}>
×
</button>
{children}
</div>
</div>
);
}
function App() {
return (
<div>
<Modal isOpen={true} onClose={() => console.log('모달 닫기')}>
<h2>알림</h2>
<p>정말로 삭제하시겠습니까?</p>
<div>
<button>취소</button>
<button>
삭제
</button>
</div>
</Modal>
</div>
);
}
2.5 Props 검증과 기본값
2.5.1 PropTypes를 사용한 검증 (선택사항)
// PropTypes 설치 필요: npm install prop-types
import PropTypes from 'prop-types';
function UserCard({ name, age, email, skills = [] }) {
return (
<div>
<h3>{name}</h3>
<p>나이: {age}세</p>
<p>이메일: {email}</p>
<div>
<h4>보유 기술:</h4>
<ul>
{skills.map((skill, index) => (
<li key={index}>{skill}</li>
))}
</ul>
</div>
</div>
);
}
UserCard.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
email: PropTypes.string.isRequired,
skills: PropTypes.arrayOf(PropTypes.string)
};
2.6 실습: 제품 카드 컴포넌트
Props를 활용한 제품 카드 컴포넌트를 만들어봅시다:
function ProductCard({
name,
price,
originalPrice,
image,
rating = 0,
reviewCount = 0,
isOnSale = false,
badgeText
}) {
const formatPrice = (price) => {
return price.toLocaleString() + '원';
};
const generateStars = (rating) => {
return '⭐'.repeat(Math.floor(rating)) +
(rating % 1 !== 0 ? '✨' : '') +
'☆'.repeat(5 - Math.ceil(rating));
};
const discountRate = originalPrice ?
Math.round(((originalPrice - price) / originalPrice) * 100) : 0;
return (
<div>
{/* 배지 */}
{badgeText && (
<div>
{badgeText}
</div>
)}
{/* 상품 이미지 */}
<div>
{image}
</div>
{/* 상품명 */}
<h3>
{name}
</h3>
{/* 평점 */}
{rating > 0 && (
<div>
<span>{generateStars(rating)}</span>
<span>
({rating}) · {reviewCount}개 리뷰
</span>
</div>
)}
{/* 가격 */}
<div>
{isOnSale && originalPrice && (
<div>
{formatPrice(originalPrice)}
</div>
)}
<div>
<span>
{formatPrice(price)}
</span>
{discountRate > 0 && (
<span>
{discountRate}% 할인
</span>
)}
</div>
</div>
{/* 버튼 */}
<button>
장바구니 추가
</button>
</div>
);
}
function App() {
const products = [
{
name: 'MacBook Pro 16인치',
price: 3190000,
originalPrice: 3590000,
image: '💻',
rating: 4.8,
reviewCount: 1234,
isOnSale: true,
badgeText: 'BEST'
},
{
name: 'iPad Air',
price: 779000,
image: '📱',
rating: 4.6,
reviewCount: 856,
isOnSale: false
},
{
name: 'AirPods Pro',
price: 329000,
originalPrice: 359000,
image: '🎧',
rating: 4.5,
reviewCount: 2103,
isOnSale: true,
badgeText: '신상품'
}
];
return (
<div>
<h1>
온라인 스토어
</h1>
<div>
{products.map((product, index) => (
<ProductCard key={index} {...product} />
))}
</div>
</div>
);
}
export default App;
2.7 Props 사용 시 주의사항
2.7.1 Props는 읽기 전용
// ❌ 잘못된 예 - Props 수정 시도
function UserCard({ name, age }) {
name = name.toUpperCase(); // 이렇게 하면 안됩니다!
return <div>{name}</div>;
}
// ✅ 올바른 예 - 새로운 변수에 저장
function UserCard({ name, age }) {
const displayName = name.toUpperCase();
return <div>{displayName}</div>;
}
2.7.2 적절한 Props 이름 사용
// ❌ 의미 없는 이름
<UserCard data1="김개발" data2={25} />
// ✅ 명확한 이름
<UserCard name="김개발" age={25} />
2.7.3 Props 전개 연산자 활용
const userInfo = {
name: '김개발',
age: 25,
job: '프론트엔드 개발자'
};
// Props를 하나씩 전달
<UserCard name={userInfo.name} age={userInfo.age} job={userInfo.job} />
// 전개 연산자로 한 번에 전달
<UserCard {...userInfo} />
2.8 정리
Props의 핵심 내용을 학습했습니다:
- Props는 부모에서 자식으로 데이터를 전달하는 방법입니다
- 구조 분해 할당으로 Props를 깔끔하게 사용할 수 있습니다
- 기본값 설정으로 안정적인 컴포넌트를 만들 수 있습니다
- children Props로 컴포넌트 사이에 JSX를 전달할 수 있습니다
- Props는 읽기 전용이므로 직접 수정하면 안됩니다
다음 장에서는 Props를 활용한 더 다양한 예제들을 살펴보겠습니다.