props — 부모가 자식에게 건네는 값
Article
지금까지 만든 컴포넌트는 항상 같은 화면만 그렸다. <Greeting />을 열 번 써도 열 번 다 똑같다.
상품 카드를 재사용하려면 상품마다 다른 이름과 가격을 넣어줘야 한다. 그 값을 props라 부른다.
함수의 매개변수와 똑같다
자바스크립트 편 10강에서 함수는 인자를 받았다. 컴포넌트는 함수이므로 당연히 인자를 받는다.
function Greeting(props) {
return <p>안녕, {props.name}님</p>;
}
function App() {
return (
<>
<Greeting name="지원" />
<Greeting name="민수" />
</>
);
}
<Greeting name="지원" />에서 name="지원"이 인자다. 컴포넌트 안에서는 props.name으로 꺼낸다.
props는 객체 하나다. 넘긴 속성들이 전부 그 안에 담긴다.
<Greeting name="지원" age={27} isAdmin={true} />
속성 이름이 키가 되고, 값이 값이 된다
- name
- "지원"
- age
- 27
- isAdmin
- true
구조분해로 꺼낸다
props. 를 계속 붙이면 지겹다. 자바스크립트 편 18강의 구조분해를 쓴다.
function Greeting({ name, age }) {
return (
<p>
안녕, {name}님 ({age}세)
</p>
);
}
매개변수 자리에서 바로 분해했다. 실무 코드는 대부분 이렇게 생겼다. 이 컴포넌트가 무엇을 받는지 첫 줄만 보면 안다.
기본값도 그대로 쓴다.
function Greeting({ name = "손님", age = 0 }) {
return <p>안녕, {name}님</p>;
}
<Greeting />이라고 쓰면 "손님"이 나온다. 자바스크립트 편 18강에서 본 대로 기본값은 undefined일 때만 쓰인다.
무엇이든 넘길 수 있다
문자열과 숫자만이 아니다.
function ProductCard({ product, onAdd, isNew }) {
return (
<div className="card">
{isNew && <span className="badge">NEW</span>}
<h3>{product.name}</h3>
<p>{product.price.toLocaleString()}원</p>
<button onClick={onAdd}>담기</button>
</div>
);
}
function App() {
const laptop = { name: "노트북", price: 1200000 };
return (
<ProductCard
product={laptop}
onAdd={() => console.log("담았다")}
isNew={true}
/>
);
}
객체도, 배열도, 함수도 넘긴다. 자바스크립트 편 11강에서 "함수는 값"이라고 했으니 당연한 일이다. onAdd는 자식이 나중에 호출할 콜백이다.
| 넘기는 것 | 쓰는 법 | 비고 |
|---|---|---|
| 문자열 | name="지원" | 중괄호 없이 가능 |
| 숫자 | age={27} | 중괄호 필수 |
| boolean | isNew={true} | isNew 만 써도 true |
| 객체 · 배열 | product={obj} | 중괄호 필수 |
| 함수 | onAdd={handleAdd} | 괄호 없이 넘긴다 |
props 는 읽기 전용이다
가장 중요한 규칙이다. 자식은 받은 props를 절대 고칠 수 없다.
function Greeting({ name }) {
name = "민수";
return <p>안녕, {name}님</p>;
}
에러는 안 나지만 의미가 없다. 부모의 값은 그대로다. 다음 렌더링에서 다시 "지원"이 들어온다.
객체를 고치면 더 나쁘다.
function ProductCard({ product }) {
product.price = 0;
return <p>{product.price}원</p>;
}
자바스크립트 편 4강에서 본 참조 타입이다. 부모의 객체가 진짜로 바뀐다. 부모는 자기 데이터가 왜 바뀌었는지 모른다. 추적 불가능한 버그다.
데이터는 아래로만 흐른다
React의 핵심 원칙이다. 단방향 데이터 흐름이라 부른다.
- App데이터를 가진 부모
- ProductList받아서 아래로 전달
- ProductCard받아서 화면에 그린다
자식이 부모의 데이터를 직접 바꿀 방법은 없다. 그럼 "담기" 버튼을 누르면 부모의 장바구니는 어떻게 바뀔까.
부모가 함수를 내려보낸다.
function App() {
const handleAdd = name => console.log(`${name} 담김`);
return <ProductCard name="노트북" onAdd={handleAdd} />;
}
function ProductCard({ name, onAdd }) {
return <button onClick={() => onAdd(name)}>담기</button>;
}
데이터는 아래로, 이벤트는 함수를 통해 위로. 자식은 "이런 일이 일어났다"고 알릴 뿐, 무엇을 할지는 부모가 정한다.
데이터 ↓
props
- 부모가 값을 준다
- 자식은 읽기만 한다
name={laptop.name}
이벤트 ↑
콜백 함수
- 부모가 함수를 준다
- 자식이 그 함수를 부른다
onAdd={handleAdd}
이 구조 덕분에 값이 어디서 왔는지 추적할 수 있다. 화면이 이상하면 위로 거슬러 올라가면 된다.
props 를 통째로 넘긴다
같은 이름의 속성을 여러 개 넘길 때는 스프레드를 쓴다.
const product = { name: "노트북", price: 1200000, image: "/p1.png" };
<ProductCard {...product} />
// 위 한 줄은 아래와 같다
<ProductCard name="노트북" price={1200000} image="/p1.png" />
자바스크립트 편 18강의 스프레드다. 편하지만 무엇이 넘어가는지 보이지 않는다. 남용하면 컴포넌트가 무엇을 받는지 알 수 없어진다.
흔한 실수
- 01
props 를 구조분해하지 않고 이름만 씀
function Greeting(name)은 틀렸다. 첫 인자는 항상 props 객체 하나다.function Greeting({ name }) { ... } - 02
숫자를 따옴표로 넘김
count="3"은 문자열이라count + 1이"31"이 된다. 자바스크립트 편 7강의 형변환이다.<Counter count={3} /> - 03
boolean 을 문자열로 넘김
isNew="false"는 비어 있지 않은 문자열이라 truthy 다. 항상 참이 된다.<Card isNew={false} />
세 번째가 특히 고약하다. isNew="false"는 화면에 배지를 계속 띄운다. 자바스크립트 편 7강에서 "0"도 truthy라고 했던 그 함정이다.
정리하면
- props는 함수의 매개변수다. 컴포넌트는
props객체 하나를 받는다. - 매개변수 자리에서 구조분해로 꺼낸다. 무엇을 받는지 한눈에 보인다.
- 문자열이 아니면 중괄호로 넘긴다.
age={27},isNew={false}. - 함수를 넘길 때 괄호를 붙이지 않는다. 붙이면 즉시 실행된다.
- props는 읽기 전용이다. 고치면 안 된다. 바뀌어야 하는 값은 state다.
- 데이터는 아래로, 이벤트는 콜백으로 위로. 단방향 흐름이 추적을 가능하게 한다.
다음 강의에서는 태그 사이에 넣은 내용을 받는 특별한 props, children을 본다.