리액트 컴포넌트는 서로 통신을 하기 위하여 props를 사용해요. 모든 부모 컴포넌트는 자식 컴포넌트에게 props를 내려서 정보를 전달해요. props라고 하면 HTML 속성을 생각하겠지만 props를 통해서는 객체, 배열 그리고 함수를 포함한 그 어떤 자바스크립트 값을 전달할 수 있어요.
이 페이지에서는
- 컴포넌트에 props를 어떻게 전달하는지
- 컴포넌트에서 props를 어떻게 읽는지
- props의 기본값을 어떻게 지정하는지
- JSX를 컴포넌트에 어떻게 전달하는지
- 시간이 지나면 props가 어떻게 변하는지
를 알아볼 거예요.
Familiar props | 익숙한 props
props는 JSX 태그에 전달하는 정보예요. 예를 들어, className
, src
, alt
, width
그리고 height
는 <img>
로 전달할 수 있는 props예요.
<img>
태그에 전달할 수 있는 props는 미리 정의되어 있어요. (ReactDOM은 표준 HTML을 따라요.) 그러나 <Avatar>
와 같은 여러분이 직접 만든 컴포넌트를 커스터마이징 하기 위해서는 어떤 props든 전달할 수 있어요. 이제 어떻게 전달하는지 알아볼게요.
Passing props to a component | 컴포넌트에 props 전달하기
아래 코드에서 Profile
컴포넌트는 자식 컴포넌트인 <Avatar>
에 아무것도 넘기지 않아요.
export default function Profile() {
return (
<Avatar />
);
}
아래 두 단계를 통해 Avatar
에 props를 전달할 수 있어요.
Step 1: Pass props to the child component | 1단계: 자식컴포넌트에 props 전달하기
먼저 Avatar
에 props를 전달하세요. (객체 타입인) person
과 (숫자 타입인) size
를 전달해볼게요.
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
NOTE
만약 person= 다음에 오는 이중 괄호가 헷갈린다면, 그것들은 단지 JSX 괄호 안의 객체일 뿐이다는 것을 기억해주세요.
이제, Avatar
컴포넌트 안에서 이 props를 읽을 수 있어요.
Step 2: Read props inside the child component | 2단계: 자식 컴포넌트에서 props 읽기
function Avatart
바로 뒤에 오는 ({
과 })
안에서 반점으로 분리하여 person, size
와 같은 이름을 나열하면 props를 읽을 수 있어요. 이렇게 하면 Avatar
코드 내에서 변수처럼 사용할 수 있어요.
function Avatar({ person, size }) {
// person과 size는 여기서 사용할 수 있어요.
}
person
과 size
props를 사용하여 Avatar
에 렌더링을 위한 로직을 추가하면 완료돼요.
이제 다른 props를 사용해서 여러가지 각기 다른 방법으로 렌더링하는 Avatar
를 구성할 수 있어요. 값을 바꿔보세요!
props는 부모와 자식 컴포넌트를 독립적으로 생각하도록 만들어줘요. person
이나 size
props를 Profile
안에서 Avatar
가 이들을 어떻게 사용하는지는 고려하지 않고 바꿀 수 있어요. 비슷하게 Avatar
가 어떻게 이 props를 사용할지도 Profile
을 보지 않고 바꿀 수 있어요.
prop를 조절할 수 있는 "손잡이" 정도로 생각하세요. 함수에 전달되는 인자와 동일한 역할을 수행해요. 사실, 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; // ... }
Specifying a default value for a prop | prop에 기본 값 설정하기
값이 지정되지 않았을 때 prop에 대한 기본 값을 지정하고 싶다면 매개변수 바로 뒤에 =
와 기본 값을 넣어서 구조분해하세요.
function Avatar({ person, size = 100 }){
// ...
}
이제 <Avatar person={...} />
가 size
prop 없이 렌더링 된다면 size
는 180
으로 설정될 거예요.
기본값은 size
prop이 없거나 size={undefined}
가 전달되었을 때만 사용돼요. 하지만 만약 size={null}
이나 size={0}
이 전달된다면 기본 값을 사용되지 않아요.
Forwarding props with the JSX spread syntax | JSX 스프레드 구문으로 prop 전달하기
때때로 props를 전달하는 것은 굉장히 반복적인 작업이 될 때가 있어요.
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
반복적인 코드가 잘못된 건 아니에요. 오히려 더 읽기 쉬울 수도 있어요. 하지만 때때로 간결함에 더 높은 우선순위를 둘 때도 있어요. 어떤 컴포넌트들은 여기서 Avatar
에게 Profile
이 그러는 것처럼, 그들의 모든 props를 자식 컴포넌트에 전달하기도 해요. 이런 컴포넌트들은 내부에서 직접 props를 사용하지 않기 때문에 더욱 간결한 "스프레드" 구문을 사용하는 것이 합리적이에요.
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
여기서 Profile
의 모든 props는 각각의 이름을 전부 나열하지 않은 채로 Avatar
로 전달돼요.
스프레드 구문을 무조건적으로 사용하지 마세요. 만약 모든 컴포넌트에서 이 구문을 사용한다면 무언가 잘못된 거예요. 이는 종종 컴포넌트를 분할하고 JSX로 자식을 전달해야 한다는 것을 나타내요. 다음에 더 알아보도록 해요.
Passing JSX as children | children으로 JSX 전달하기
내장된 브라우저 태그는 흔히들 중첩해서 사용해요.
<div>
<img />
</div>
같은 방법으로 직접 만든 컴포넌트를 중첩해서 사용하고 싶을 때도 있을 거예요.
<Card>
<Avatar />
</Card>
만약 JSX 태그 안에서 콘텐츠를 중첩하고 싶다면 부모 컴포넌트는 children
이라고 불리는 prop에 콘텐츠를 받을 거예요. 예를 들어서 아래의 Card
컴포넌트는 <Avatar />
로 설정된 children
prop을 받고 div로 감싸서 렌더링해요.
Card
컴포넌트가 어떤 중첩된 콘텐츠도 감쌀 수 있는지를 보기 위해 <Card>
안의 <Avatar>
를 일반 텍스트로 변환해보세요. 이 컴포넌트는 내부에서 무언이 렌더링 되고 있는지는 굳이 "알" 필요는 없어요. 이 유연한 패턴은 많은 곳에서 볼 수 있어요.
children
prop을 가진 컴포넌트를 부모 컴포넌트가 임의의 JSX로 "채울 수 있는 구멍"을 갖고 있다고 생각하세요. children
prop은 종종 패널이나 그리드와 같이 시각적으로 감쌀 때 사용돼요.
Illustrated by Rachel Lee Nabors (출처: react.dev)
How props change over time | 시간에 따라 props는 어떻게 변할까요?
아래의 Clock
컴포넌트는 부모 컴포넌트로부터 color
과 time
이라는 두 가지 props를 받아요. (부모 컴포넌트의 코드는 우리가 아직 제대로 알아보지 않은 상태를 사용해서 생략했어요.)
아래의 셀렉트 박스에서 색상을 바꿔보세요.
이 예시는 컴포넌트는 시간이 지날 때마다 다른 props를 받을 수도 있다는 것을 설명하고 있어요. Props는 항상 정적인 값이 아니에요! 여기서 time
prop은 매 초마다 바뀌고, color
prop은 사용자가 다른 색상을 선택하면 바뀌어요. props는 시작 시점 뿐 아니라 어떤 시점의 컴포넌트 데이터를 반영해요.
그러나 props는 불변해요. 컴포너 과학에서는 "변할 수 없다"는 것을 의미해요. (예를 들어 사용자와의 상호작용이나 새 데이터에 대한 반응으로) 컴포넌트가 props를 바꾸어야 한다면 부모 컴포넌트에게 새로운 객체인다른 props를 줄 수 있는지 "물어"보아야해요. 그리고 나서 이전의 props는 제쳐지고 결국 자바스크립트 엔진은 이들에게 할당되었던 메모리를 재사용해요.
"props를 바꾸려고" 하지 마세요. (선택된 색상을 변경하는 것처럼) 사용자의 입력에 대응해야한다면, "상태 설정"이 필요해요. 이는 상태: 컴포넌트의 메모리에서 배울 거예요.
Recap | 요약
- props를 전달하기 위해서는 HTML 속성처럼 JSX 안에 props를 넣어야해요.
- props를 읽으려면
function Avatar({ person, size })
와 같은 구조분해 구문을 사용하세요. size = 100
과 같은 기본 값을 정할 수 있고 이 값은 props가 없거나undefined
일 때 사용돼요.<Avatar {...props} />
와 같은 JSX 스프레드 구문을 통해 모든 props를 전달할 수는 있지만 남용하지 마세요.<Card><Avatar /></Card>
와 같은 중첩된 JSX는Card
컴포넌트의children
prop인 것처럼 보일 거예요.- props는 읽기 전용 스냅샷이에요. 매 렌더링마다 새로운 props를 받아요.
- props를 바꿀 수는 없어요. 상호작용이 필요하다면 상태를 설정하세요.
Challenge | 도전 문제
1. 컴포넌트 추출하기
이 Gallery
컴포넌트는 두 개의 프로필에서 굉장히 비슷한 마크업을 포함하고 있어요. Profile
컴포넌트로 이것들 추출하고 중복을 줄여보세요. 어떤 props를 전달해야할지는 선택해야해요.
한 과학자의 마크업을 추출하는 것부터 시작해보세요. 그리고나서 두 번째 예사와 일치하지 않는 것을 찾고 이들로 props를 구성해보세요.
이 답안에서 Profile
컴포넌트는 다중 props를 받고 있어요. props는 imageId
(문자열), name
(문자열), profession
(문자열), awards
(문자열 배열), discovery
(문자열) 그리고 imageSize
(숫자)가 있어요.
imageSize
prop은 기본 값이 있어서 컴포넌트에 전달해주지 않았어요.
만약 awards
가 배열이라면 별개의 awardCount
prop이 필요하지 않다는 점에 주목하세요. 수상한 상의 개수를 세려면 awards.length
를 사용할 수 있어요. props는 어떤 값도 될 수 있고 배열도 포함할 수 있다는 사실을 기억하세요!
이 페이지의 처음에 나왔던 예시와 더 비슷한 다른 방법은 사람에 대한 모든 정보를 하나의 객체로 묶어서 하나의 prop으로 그 객체를 전달하는 방법이에요.
문법이 살짝 달라보여도 JSX 속성의 컬렉션 대신 자바스크립트 객체의 속성을 설명하고 있기 때문에 이 예시들은 거의 동일해요. 여러분이 원하는대로 둘 중 아무거나 고르면 돼요.
2. prop에 기반하여 사진 크기 조정하기
이 예시에서 Avatar
는 <img>
의 높이와 너비를 지정해주는 숫자 size
prop을 받아요. 이 예시에서 size
prop은 40
으로 정해져 있어요. 그러나 만약 사진을 새 탭에서 연다면 이미지는 (160
픽셀로) 더 크다는 것을 알 수 있어요. 실제 이미지 크기는 요청한 썸네일 사이즈에 따라 결정돼요.
Avatar
컴포넌트를 size
prop에 기반하여 가장 가까운 사진의 크기를 요청하도록 변경해보세요. 구체적으로 만약 size
가 90
보다 작다면 b
(big)가 아니라 s
(small)을 getImageUrl
함수에 전달하세요. 여러분이 바꾼 결과가 잘 동작하는지 보려면 아바타를 다른 size
prop의 값으로 렌더링하고 이미지를 새 탭에서 열어보세요.
여기 어떻게 해야하는지를 알려줄게요.
또한 window.devicePixelRatio를 고려하여 높은 DPI 화면을 위해 더 선명한 이미지를 보여줄 수 있어요.
props는 Avatar
컴포넌트의 안처럼 로직을 캡슐화하도록 만들어주기 때문에 (그리고 필요하다면 나중에 바꿀 수 있기 때문에) 이미지가 어떻게 요청되고 재조정되는지를 생각하지 않다도 누구나 <Avatar>
컴포넌트를 사용할 수 있어요.
3. children
prop에 JSX 전달하기
Card
컴포넌트를 아래 마크업에서 추출하고 children
prop을 사용해서 다른 JSX를 Card
에 전달하세요.
컴포넌트의 태그 내부에 넣는 어떤 JSX도 children
prop으로 컴포넌트에 전달돼요.
Card
컴포넌트는 양쪽 모두에서 사용될 수 있어요.
또한 Card
가 항상 제목을 갖고 있다면 title
이라는 prop을 따로 분리할 수도 있어요.
'리액트 공식문서 | React Docs > Learn > Learn React' 카테고리의 다른 글
[Describing the UI] Rendering Lists | 리스트 렌더링하기 (0) | 2024.02.14 |
---|---|
[Describing the UI] Conditional Rendering | 조건부 렌더링 (1) | 2024.02.13 |
[Describing the UI] JavaScript in JSX with Curly Braces | 중괄호로 JSX에 자바스크립트 넣기 (0) | 2024.02.13 |
[Describing the UI] Writing Markup with JSX | JSX로 마크업 작성하기 (1) | 2024.02.10 |
[Describing the UI] Importing and Exporting Components | 컴포넌트 불러오기와 내보내기 (1) | 2024.02.10 |