SPACE RUMI

Hi, I am rumi. Let's Splattack!

[STUDY] 스터디/React Docs

React docs beta 한글 번역 : Passing Props to a Component - 컴포넌트에 Props 전달하기

백루미 2023. 2. 19. 18:45
반응형

UI 묘사 : 컴포넌트에 Props 전달하기

React 컴포넌트는 props를 통해 서로 소통합니다.
모든 부모 컴포넌트는 자식 컴포넌트에 props를 전달하여 일부 정보를 전달할 수 있습니다.
props는 HTML 어트리뷰트를 떠올리게 할 수 있지만 객체, 배열, 함수를 포함한 모든 자바스크립트 값을 전달할 수 있습니다.

 

학습 내용

  • 컴포넌트에 props를 전달하는 방법
  • 컴포넌트에서 props를 읽는 방법
  • props의 기본값을 지정하는 방법
  • 컴포넌트에 일부 JSX를 전달하는 방법
  • 시간이 지남에 따라 props가 어떻게 변하는지

 

친숙한 props

Props는 JSX태그에 전달하는 정보입니다. 예를 들어, <img>에 전달할 수 있는 몇 가지 props로는 className, src, alt, width, height 등이 있습니다.

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> 태그에 전달할 수 있는 프로퍼티는 미리 정의되어 있습니다(ReactDOM은 HTML 표준을 준수합니다).
하지만 <Avatar>와 같은 자체 컴포넌트에 어떤 props든 사용자 정의 props를 전달할수 있습니다. 방법은 다음과 같습니다!

 

컴포넌트에 props 전달하기

이 코드에서 Profile 컴포넌트는 하위 컴포넌트인 Avatar에 어떤 props도 전달하지 않고 있습니다.

export default function Profile() {
  return (
    <Avatar />
  );
}

2 단계를 거쳐 Avatar에게 props를 전달할 수 있습니다.

 

1단계: 자식 컴포넌트에 Props 전달하기

먼저 Props를 Avatar에 전달합니다. 예를 들어 person(object)과 size(number) 두 가지 props를 전달해 보겠습니다:

export default function Profile() {
  return (
    <Avatar
      person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
      size={100}
    />
  );
}

person= 뒤에 오는 이중 중괄호가 헷갈린다면 JSX 중괄호 안의 객체일 뿐이라는 점을 기억하세요.
이제 Avatar 컴포넌트 내에서 이 props들을 읽을 수 있습니다.

 

2단계: 하위 컴포넌트 내부의 Props 읽기

이러한 props는 Avatar 함수 바로 뒤에오는 ({  })안에 쉼표로 구분된 person, size를 읽을 수 있습니다. 이렇게 하면 변수를 사용할 때처럼 Avatar 코드 내에서 사용할 수 있습니다.

function Avatar({ person, size }) {
  // person, size는 여기서 사용 가능합니다.
}

Avatar 렌더링에 person 및 size props를 사용하는 로직을 추가하면 됩니다.

이제 다양한 props를 사용하여 다양한 방식으로 렌더링하도록 Avatar 컴포넌트를 구성할 수 있습니다. 값을 조정해 보세요!

import { getImageUrl } from './utils.js';

function Avatar({ person, size }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <div>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi', 
          imageId: 'YfeOqp2'
        }}
      />
      <Avatar
        size={80}
        person={{
          name: 'Aklilu Lemma', 
          imageId: 'OKS67lh'
        }}
      />
      <Avatar
        size={50}
        person={{ 
          name: 'Lin Lanying',
          imageId: '1bX5QH6'
        }}
      />
    </div>
  );
}

props를 사용하면 부모와 자식 컴포넌트를 독립적으로 생각할 수 있습니다. 예를 들어, Avatar가 props를 사용하는 방식을 고려하지 않고도 Profile에서 person이나 size를 변경할 수 있습니다. 마찬가지로 Profile 보지 않고도 Avatar가 이러한 props를 사용하는 방식을 변경할 수 있습니다.

props는 조절할 수 있는 '손잡이'라고 생각하면 됩니다. props는 함수의 인자와 같은 역할을 합니다. 사실 props는 컴포넌트에 대한 유일한 인자입니다! React 컴포넌트 함수는 하나의 인자, 즉 props 객체를 받습니다:

function Avatar(props) {
  let person = props.person;
  let size = props.size;
  // ...
}

일반적으로 전체 props 오브젝트 자체는 필요하지 않으므로, 개별 props으로 분해하여 사용합니다.

 

함정

props를 선언할 때 (  ) 안에 {  } 중괄호를 잊지 마세요:

function Avatar({ person, size }) {
  // ...
}

이 구문을 "destructuring" (de + structuring) 구조분해할당 이라고 하며 함수 매개변수에서 props를 읽는 것과 동일합니다:

function Avatar(props) {
  let person = props.person;
  let size = props.size;
  // ...
}

 

Props의 기본값 지정하기 

값이 지정되지 않았을때 않았을 때 , props에 기본값을 지정하고 싶으면 매개변수 바로 뒤에 = 와 기본값을 넣어 디스트럭처링을 사용하면 됩니다:

function Avatar({ person, size = 100 }) {
  // ...
}

이제 <Avatar person={...} />가 size props 없이 렌더링되면 size가 100으로 설정됩니다.

기본값은 size props가 없거나 size={undefiend}를 전달한 경우에만 사용됩니다. 그러나 size={null} 또는 size={0}을 전달하면 기본값이 사용되지 않습니다.

 

JSX 스프레드 구문으로 props 전달하기 

때로는 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를 직접 사용하지 않기 때문에 보다 간결한 "Spread" 구문을 사용하는 것이 합리적일 수 있습니다:

function Profile(props) {
  return (
    <div className="card">
      <Avatar {...props} />
    </div>
  );
}

이렇게 하면 Profile의 모든 props가 각각의 이름을 나열하지 않고 Avatar로 전달됩니다.

스프레드 구문은 절제해서 사용하세요. 
다른 모든 컴포넌트에서 이 구문을 사용하고 있다면 뭔가 잘못된 것입니다. 컴포넌트를 분할하여 자식을 JSX로 전달해야 한다는 의미인 경우가 많습니다. 다음에 자세히 설명하겠습니다!

 

JSX를 자식으로 전달하기

기본 제공 브라우저 태그를 중첩하는 것이 일반적입니다:

<div>
  <img />
</div>

때로는 같은 방식으로 자체 컴포넌트를 중첩하고 싶을 때가 있습니다:

<Card>
  <Avatar />
</Card>

 

 

JSX 태그 안에 콘텐츠를 중첩하면, 부모 컴포넌트는 해당 콘텐츠를 children라는 props로 받습니다. 예를 들어 아래 Card 컴포넌트는 <Avatar />로 설정된 자식 props를 수신하여 래퍼 div에 렌더링합니다:

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>
  );
}

 

<Card> 안에 있는 <Avatar>를 텍스트로 바꿔서 카드 컴포넌트가 중첩된 콘텐츠를 어떻게 감싸는지 확인해 보세요. 그 안에 무엇이 렌더링되는지 알필요는 없습니다. 이 유연한 패턴은 여러 곳에서 볼 수 있습니다.

자식 props가 있는 컴포넌트는 부모 컴포넌트가 임의의 JSX로 "채울 수 있는" '공간'이 있다고 생각할 수 있습니다. 패널, 그리드 등 시각적 래퍼에 자식 props를 사용하는 경우가 많습니다.

 

Props가 시간에 따라 변하는 방식

아래의 Clock 컴포넌트는 부모 컴포넌트로부터 color과 time이라는 두 가지 props를 받습니다.
(부모 컴포넌트의 코드는, 아직 자세히 다루지 않을 state를 사용하기 때문에 생략했습니다.)

아래에서 color를 변경해 보세요:

export default function Clock({ color, time }) {
  return (
    <h1 style={{ color: color }}>
      {time}
    </h1>
  );
}

 

이 예시는 컴포넌트가 시간이 지남에 따라 다른 props를 받을 수 있음을 보여줍니다. props는 항상 고정되어 있지 않습니다! 여기서 시간 props은 매초마다 변경되고 color props는 다른 색상을 선택하면 변경됩니다. props는 컴포넌트의 데이터를 처음에만 반영하는 것이 아니라 모든 시점에 반영합니다.

그러나 props는 컴퓨터 과학에서 "변경할 수 없다"는 뜻의 불변성(immutable)을 가집니다. 컴포넌트가 사용자 상호작용이나 새로운 데이터에 대한 응답으로 props를 변경해야 하는 경우, 부모 컴포넌트에 다른 props, 즉 새로운 객체를 전달하도록 "요청"해야 합니다! 그러면 이전 props는 버려지고 결국 자바스크립트 엔진은 props가 차지했던 메모리를 되찾게 됩니다.

"props 변경"을 시도하지 마세요. 선택한 color를 변경하는 등 사용자 입력에 응답해야 하는 경우 "set state"를 해야 하며, 이에 대한 자세한 내용은 State를 배울때 확인할 수 있습니다. 상태: 컴포넌트의 메모리 챕터에서 배울 수 있습니다.

 

요약

  • props를 전달하려면 HTML 어트리뷰트를 사용할 때와 마찬가지로 JSX에 props를 추가합니다.
  • props를 읽으려면 Avatar({ person, size }) 함수 구조 분해 구문을 사용합니다.
    누락되거나 정의되지 않은 props에,  size = 100과 같은 기본값을 지정할 수 있습니다.
  • 모든 props를 <Avatar {...props} /> JSX 스프레드 구문을 사용할 수 있지만 과도하게 사용하지 마세요!
  • <Card><Avatar /></Card>와 같이 중첩된 JSX는 Card 컴포넌트의 자식 prop으로 표시됩니다.
  • props는 읽기 전용 스냅샷으로, 렌더링할 때마다 새로운 버전의 props를 받습니다.
    props를 변경할 수 없습니다. 상호작용이 필요한 경우 state를 설정해야 합니다.

 

과제 1 / 3: 컴포넌트 추출하기 
이 Gallery 컴포넌트에는 두 개의 프로필에 대한 매우 유사한 마크업이 포함되어 있습니다. 여기서 Profile 컴포넌트를 추출하여 중복을 줄이세요. 어떤 Props를 전달할지 선택해야 합니다.

 

과제 2 / 3: Props에 따라 이미지 크기 조정하기
이 예제에서 Avartar는 <Img> 너비와 높이를 결정하는 number타입의 size props를 받습니다. 이 예제에서는 size props가 40으로 설정되어 있습니다. 그러나 새 탭에서 이미지를 열면 이미지 자체가 160픽셀로 더 커진 것을 알 수 있습니다. 실제 이미지 크기는 요청하는 썸네일 크기에 따라 결정됩니다.

size props에 따라 가장 가까운 이미지 크기를 요청하도록 Avatar 컴포넌트를 변경하세요. 특히, 크기가 90보다 작으면 getImageUrl 함수에 'b'("big")가 아닌 's'("small")를 전달하세요. 다른 값의 size props로 Avatar 컴포넌트를 렌더링하고 새 탭에서 이미지를 열어 변경 사항이 제대로 작동하는지 확인합니다.

 

과제 3 / 3: 자식 props에 JSX 전달하기

마크업에서 Card 컴포넌트를 추출하고 자식 Props를 사용하여 다른 JSX를 전달합니다:

 

2주차 도전과제 모음

https://codesandbox.io/s/vigorous-newton-ypzb3u?file=/src/App.js

 

발표 요약)

1. 컴포넌트에 사용자정의 props를 전달할 수 있다.

2. props 자체는 하나의 객체이며, 컴포넌트 내에서 값을 꺼내 쓸 때 { } 중괄호를 사용하여 구조분해할당으로 꺼내는 것을 쓴다.
- 실제로 나는 혼재해서 쓰는 편인데, 어떤지? props.example1 props.example2 이런식으로 쓸때도 있고, 구조분해로 값을 꺼내서 쓸때도 있다. (객체 내 key가 꽤 길고 많을때 그렇게 썼던 것 같다)

3. props에 기본값을 지정해 줄수있다. 컴포넌트 내부에서 size = 100 이런식으로 지정.
** size={null} 또는 size={0}이 전달되면 기본값이 사용되지 않음에 주의!**

4. 중첩된 JSX, <Card><Avatar /></Card> 에서 Avatar는 Children prop으로 사용.
반응형