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