리액트 컴포넌트는 서로 통신하는 데에 props를 사용한다. 모든 부모 컴포넌트는 어떤 정보를 그 자식 컴포넌트에게 props를 전달하는 것으로 전달할 수 있다. Props는 HTML 속성을 떠올리게 할 지 모르지만, 객체, 배열, 함수를 포함하는 어떠한 자바스크립트 값도 전달할 수 있다.
배울 것
- 컴포넌트에 props를 전달하는 방법
- 컴포넌트에서 props를 읽는 방법
- props의 기본 값을 정하는 방법
- 컴포넌트에 JSX를 전달하는 방법
- 시간에 따른 props의 변경 방법
props는 JSX태그에 전달하는 정보이다. 예를 들어 className
, src
, alt
, width
, height
은 <img>
에 전달할 수 있는 props이다.
//@App.js
function Avatar() {
return (
<img
className="avatar"
src="https://i.imgur.com/1bX5QH6.jpg"
alt="Lin Lanying"
width={100}
height={100}
/>
);
}
export default function Profile() {
return (
<Avatar />
);
}
<img>
태그에 전달할 수 있는 props는 미리 정의되어 있다. (React DOM은 HTML 표준을 준수한다.) 하지만 커스텀하기 위해 <Avatart>
와 같은 컴포넌트에는 어떠한 props도 전달할 수 있다.
이 코드에서 Profile
컴포넌트는 그 자식 컴포넌트인 Avatar
로 어떠한 props도 전달하지 않는다.
export default function Profile() {
return (
<Avatar />
);
}
두 가지 과정을 거쳐서 props를 전달할 수 있다.
첫째로 Avatar
에 props를 전달해라. 예를 들어, person
이라는 객체와 size
라는 숫자를 props로 전달한다고 생각해보자.
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
person=
뒤의 중괄호가 겹쳐 나오는 것이 혼란스럽다면, JSX 괄호 안에 있는 객체일 뿐이라는 것을 다시 한 번 떠올려라.
Avatar
함수 바로 뒤에 있는 ({
, })
안의 콤마로 구분된 person``size
의 이름을 나열하여 이러한 props를 읽을 수 있다. 이것은 변수를 사용하는 것처럼 Avatar
코드 안에서 그것들을 사용할 수 있게 된다.
렌더링을 위해 Avatar
에 person
, size
props를 사용하는 로직을 더하면 끝이다.
이제 다양한 props로 다양한 방법으로 렌더링 하도록 Avatar
를 구성할 수 있다.
props는 부모 컴포넌트와 자식 컴포넌트를 독립적으로 생각할 수 있게 해준다. 예를 들어, Avatar
가 사용하는 방법을 고려하지 않고도 Profile
내에서 person
이나 size
를 변경할 수 있다. 유사하게, Profile
을 보지 않고도 Avatar
가 이러한 props를 사용하는 방법을 변경할 수 있다.
props는 스스로 조정할 수 있는 "knobs"같은 것과 같은 것으로 생각할 수 있다. 그것들은 함수에 대한 인수 역할과 동일한 역할을 한다. 실제로 props는 컴포넌트의 유일한 인자이다. 리액트 컴포넌트 함수는 단일 인수인 props 객체를 허용한다.
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
대개 전체 props 객체가 필요하지 않을 것이라서 각 props를 구조 분해 하면 된다.
props를 선언할 때
()
안에{}
를 잊으면 안된다.function Avatar({ person, size }) { // ... }
이 문법은 "구조 분해 할당"이라고 하고, 함수의 매개 변수에서 속성을 읽는 것과 같다.
function Avatar(props) { let person = props.person; let size = props.size; // ... }
값을 지정하지 않은 상태에서 prop에 기본 값을 주고싶을 경우, 매개 변수 바로 뒤에 =
와 기본 값을 입력하여 구조 분해 할당을 할 수 있다.
function Avatar({ person, size = 100 }) {
// ...
이제 만약 <Avatar person={...} />
이 size
prop 없이 렌더링 되면, size
는 100
으로 지정될 것이다.
기본 값은 size
prop이 없거나, size={undefined}
를 전달한 경우만 사용된다. 하지만 size={null}
이나 size={0}
을 전달한다면 기본 값은 사용되지 않을 것이다.
가끔, props를 전달하는 것은 매우 반복적이 된다.
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
반복적인 코드에는 문제가 없다 - 읽기 더 쉬울 수도 있다. 하지만 때로는 간결성을 중요시 할 수 있다. 일부 컴포넌트들은 Profile
이 Avatar
에 수행하는 방식과 같이 모든 props를 자녀 컴포넌트에 전달한다. 그들은 어떤 props도 직접 사용하지 않기 때문에 더 간결한 "전개" 문법을 사용하는 것이 의미 있을 수 있다.
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
그러면 Profile
의 모든 props가 이름을 나열하지 않고 Avatar
로 전달된다.
제한이 있는 전개 문법을 사용하라. 만약 당신이 그것을 다른 모든 컴포넌트에서 사용하고 있다면, 뭔가 잘못된 것이다. 종종 컴포넌트를 쪼개고 children을 JSX로 전달해야 한다.
내장된 브라우저 태그를 중첩하는 것은 일반적이다.
<div>
<img />
</div>
JSX 태그 안에서 콘텐츠를 중첩시키면, 부모 컴포넌트는 children
이라고 불리는 prop을 받는다. 예를 들어, 아래의 Card
컴포넌트는 Avatar
로 설정된 children
prop을 받아서 div
로 감싸진 안에 렌더링 할 것이다.
//@App.js
import Avatar from './Avatar.js';
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
</Card>
);
}
//@Avatar.js
import { getImageUrl } from './utils.js';
export default function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
//@utils.js
export function getImageUrl(person, size = 's') {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
Card
컴포넌트가 중첩된 컨텐츠를 어떻게 랩핑할 수 있는지 확인하려면 <Card>
안에 <Avatar>
를 일부 텍스트로 대체해 보면 된다. 내부에서 렌더링되는 내용을 "알" 필요는 없다. 이러한 유연한 패턴을 많은 곳에서 볼 수 있을 것이다.
children
prop을 가지고 있는 컴포넌트를 임의의 JSX로 부모 컴포넌트에 의해 "채울 수 있는" "구멍" 이 있다고 생각할 수 있다. 시각적인 wrapper (panels, grid 등) 를 children
prop으로 사용할 경우가 종종 있을 것이다.
아래의 Clock
컴포넌트는 부모 컴포넌트로부터 color
, time
이라는 두 개의 prop을 전달 받는다. ( 부모 컴포넌트의 코드는 생략한다)
아래 셀렉트 박스에서 컬러를 바꾸려고 해봐라.
export default function Clock({ color, time }) {
return (
<h1 style={{ color: color }}>
{time}
</h1>
);
}
이 예시는 컴포넌트가 시간이 지남에 따라 다른 props를 받을 수 있다는 것을 보여준다. props가 항상 정적인 것은 아니다! 여기 time
prop은 1초마다 변경되며, 다른 색을 선택하면 color
prop이 변경된다. props는 컴포넌트 처음의 데이터만 반영하는 것이 아니라 특정 시점에 반영한다.
하지만, props는 불변성을 가진다. 컴퓨터 과학적인 의미로 "변화하지 않는" 이라는 뜻을 의미한다. 컴포넌트가 props를 변경해야 할 경우 (예를 들어, 유저 상호작용이나 새로운 데이터에 대한 응답), 부모 컴포넌트가 다른 props - 새 객체 - 를 전달하도록 "요청" 해야 한다. 오래된 props는 버려지고, 결국 자바스크립트 엔진은 그들이 가져간 메모리를 회수할 것이다.
props를 바꾸려고 하지 마라. 선택한 색상을 변경하는 것과 같이 사용자 입력에 대해 응답해야 할 때는 State: A Component’s Memory 에서 배운 "set State"를 사용해야 한다.