React 공식 문서 해석하며 공부하기 : Lists and Keys

배지로·2021년 8월 27일
0
post-thumbnail

해석하며 공부하는 것을 목적으로 하기 때문에 다수의 의역, 오역이 있음을 미리 밝힙니다.
순서로는 main concept 8번에 속하며, 순서대로 읽으면서 공부하고 있는 과정이 아니기 때문에 무작위로 해석할 예정입니다.

리스트와 키

먼저, 자바스크립트에서 리스트를 어떻게 변환하는지 살펴봅시다.

아래 주어진 코드는 map()함수를 가지고 numbers 배열의 값을 두 배로 만듭니다. map()함수로 리턴된 새로운 배열을 doubled라는 이름의 변수에 할당하고 doubled를 로그로 찍어서 확인합니다:

const numbers=[1,2,3,4,5];
const doubled=numbers.map((number)=>number*2);
console.log(doubled);

이 코드는 콘솔에 [2,4,6,8,10]의 로그를 남깁니다.

리액트에서는, 배열을 element 의 리스트로 변환하는 것은 거의 동일합니다.

여러 개의 컴포넌트를 렌더링하기

엘리먼트들의 콜렉션을 만들고 {}을 사용해서 엘리먼트들을 jsx에 포함시킬 수 있습니다.

아래의 코드를 보면, 자바스크립트 map() 함수를 사용해서numbers배열을 반복 실행합니다. 각 아이템들이 <li> 엘리먼트에 반환됩니다. 결과적으로 만들어진 엘리먼트의 배열이 listItems에 할당됩니다:

const numbers=[1,2,3,4,5];
const listItems=numbers.map((number)=>
  <li>{number}</li>
);

우리는 listItems 배열 전체를 <ul> 에 포함시킬 수 있고, 그것이 DOM으로 렌더링됩니다:

ReactDom.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);

기본적인 리스트 컴포넌트

보통 컴포넌트 안의 리스트를 렌더링하게 됩니다.

이전 예제의 numbers배열을 받고 elements의 리스트를 출력하는 컴포넌트로 리팩토링 해보겠습니다.

function NumberList(props){
  const numbers=props.numbers;
  const listItmes=numbers.map((number)=>
  	<li>{number}</li>
  );
  return(
  	<ul>{listItmes}</ul>
  );
}
const numbers=[1,2,3,4,5];
ReactDom.render(
  <NumberList numbers={numbers}/>,
  document.getElementById('root')
);

이 코드를 실행하면, 리스트 아이템들에 key가 주어져야 한다는 경고를 받을 것입니다. key는 엘리먼트 리스트를 만들 때 포함시켜줘야 하는 특별한 문자열 속성입니다. 다음 섹션에서 왜 이것이 중요한지 다뤄보겠습니다.

numbers.map()안의 리스트 아이템들에 key를 할당하고, key가 없는 문제를 해결해봅시다.

function NumberList(props){
  const numbers=props.numbers;
  const listItmes=numbers.map((number)=>
  	<li key={number.toString()}>
    	{number}
    </li>
  );
  return(
  	<ul>{listItmes}</ul>
  );
}
const numbers=[1,2,3,4,5];
ReactDom.render(
  <NumberList numbers={numbers}/>,
  document.getElementById('root')
);

Key

key는 리액트가 어떤 아이템이 바뀌었고, 추가되었고, 삭제되었는지 확인하는 것을 도와줍니다. 각 엘리먼트들이 안정되게 식별되게 하기 위해서 배열 안의 엘리먼트들에게 key가 부여됩니다 :

const numbers=[1,2,3,4,5];
const listItems=numbers.map((number)=>
	<li key={number.toString()}>
    	{number}
    </li>
);                     

key를 고르기 위한 최고의 방법은 형제 사이에서 리스트 아이템들을 고유하게 식별할 수 있는 문자열을 사용하는 것입니다. 가장 자주 쓰이는 방법은 데이터의 ID를 key로 사용하는 것입니다 :

const todoItmes=todos.map((todo)=>
	<li key={todo.id}>
    	{todo.text}
    </li>                        
);

렌더링된 아이템에게 안정된 ID가 없다면, 최후의 수단으로 아이템의 인덱스를 key로 사용해야 합니다 :

const todoItmes=todos.map((todo,index)=>
	<li key={index}>
    	{todo.text}
    </li>                        
);

아이템들의 순서가 바뀔 수 있기 때문에 인덱스를 사용하는 방법은 권장하지 않습니다. 이것은 성능에 부정적으로 영향을 끼칠 수 있고, 컴포넌트의 state에 문제가 발생할 수 있습니다. 인덱스를 키로 사용함으로 인한 부정적 영향에 대해 심층적으로 설명한 Robin Pokorny의 글을 확인해보세요. 만약 리스트 아이템에 확실한 키를 할당하지 않는다면, 리액트는 기본으로 인덱스를 키로 사용할 것입니다.

더 배우고 싶다면, 왜 키가 필요한 것인지에 대한 심층적 설명을 확인해보세요.

키를 이용해서 컴포넌트 추출하기

key는 주변 배열의 컨텍스트에서만 의미가 있습니다.

예를 들어 ListItem컴포넌트를 추출하고자 한다면, ListItem컴포넌트 안의<li>엘리먼트의 키보다는 배열 안의 <ListItem/>엘리먼트의 key를 가져야 합니다.

예시: 잘못된 Key 사용

function ListItem(props) {
  const value = props.value;
  return (
    //틀렸어요! 여기에 키를 명시할 필요가 없습니다
    <li key={value.toString()}>
      {value}
    </li>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems=numbers.map((number)=>
    //틀렸어요! 키는 여기에 명시되었어야 했습니다                          
    <ListItem value={number}/>                           
  );                            
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

예시 : 정확한 Key 사용

function ListItem(props) {
  const value = props.value;
  return (
    //정답이에요! 키는 여기에 명시될 필요가 없습니다:
    <li>{props.value}</li>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems=numbers.map((number)=> 
    //정답이에요! 키는 배열 안에 명시되어야만 합니다.                          
    <ListItem key={number.toString()} value={number}/>                         
  );                            
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

경험해보니, map() 함수 내의 엘리먼트는 key가 필요하다는 것을 알 수 있습니다.

키는 형제 사이에서 유일해야 한다

배열에서 쓰이는 key는 형제(같은 레벨의 엘리먼트들) 사이에서 유일해야만 합니다. 그렇지만 전역적으로 유일할 필요는 없습니다. 서로 다른 두개의 배열을 만들 때는 같은 key를 사용할 수 있습니다.

function Blog(props){
  const sidebar=(
    <ul>
      {props.posts.map((post)=>
      	<li key={post.id}>
       	  {post.title}
        </li>
      )}
    </ul>
  );
  const content=props.posts.map((post)=>
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
   );                      
  return(
    <div>
      {sidebar}                             
      <hr/>
      {content}                                                        
    </div> 
  );
}

const posts=[
  {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
  {id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
  <Blog posts={posts} />,
  document.getElementById('root')
);

리액트에서 key는 힌트로 작용하지만 컴포넌트들에게 전달되지는 못합니다. 만약 같은 값이 컴포넌트에 필요하다면 명시적으로 다른 이름의 prop으로 key를 전달하세요.

const content=posts.map((post)=>
  <Post
    key={post.id}
    id={post.id}
    title={post.title}/>
);

위의 예시에서 보면, Post컴포넌트는 props.id는 읽을 수 있지만 props.key는 읽을 수 없습니다.

JSX에 map() 포함시키기

위의 예시에서 별도의 ListItems 변수를 선언하고 그것을 JSX에 포함했습니다.

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <ListItem key={number.toString()}
              value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );

JSX는 {}안의 어떤 표현도 포함시키기 때문에 map() 또한 포함시키는 것이 가능합니다.

function NumberList(props) {
  const numbers=props.numbers;
  return(
    <ul>
      {numbers.map((number)=>
        <ListItem key={number.toString()} value={number}/>
      )}
    </ul>
  );
}

이 방법은 가끔 더 깔끔한 코드를 작성할 수 있게 하기도 하지만, 이 스타일은 오용되기도 합니다. 자바스크립트와 마찬가지로 가독성을 위해서 변수를 추출해낼 것인지 아닌지 결정하는 것은 당신에게 달렸습니다. 만약 map() 의 body에 중첩이 심하다면 컴포넌트를 추출해내기 좋은 타이밍이라는 것을 명심하세요.

profile
웹 프론트엔드 새싹🌱

0개의 댓글