이 포스팅은 https://reactjs.org/docs/lists-and-keys.html 에 있는 포스팅을 번역한 것입니다. 오역이나 의역이 있을 수 있습니다. 지적해주시면 확인 후 바로 정정하겠습니다.
original source of this posting is from https://reactjs.org/docs/lists-and-keys.html If the original author requests deletion, it will be deleted immediately.
Translated by Jake Seo (서진규)
- https://velog.io/@jakeseo_me
- https://github.com/n00nietzsche
리액트 공식문서로 배워보자 #8, 리스트와 키
처음으로 우리가 어떻게 자바스크립트에서 리스트를 변환했는지 리뷰해봅시다.
아래 주어진 코드를 보면, 우리는 numbers
의 배열을 가져와서 그 값을 2배로 증가시키기 위해 map()
함수를 이용했습니다. 우리는 map()
메소드에 의해 반환된 새로운 배열을 double
에 할당하고 로깅했습니다.
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
배열을 <ui>
엘리먼트 내부에 포함시켰습니다. 그리고 DOM에서 렌더링 시켰습니다.
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
이 코드는 1에서 5까지의 bullet list를 보여줍니다.
주로 우리는 리스트를 컴포넌트 내부에 렌더링할 것입니다.
우리는 이전 예제를 numbers
배열을 받고 elements의 리스트를 출력하는 컴포넌트로 리팩토링해볼 것입니다.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((numbers) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root');
);
이 코드를 실행할 때, 리스트 아이템에 대한 key가 제공되어야 한다는 경고메시지를 받게될 것입니다. 엘리먼트의 리스트를 만들 때, "key"는 여러분이 포함시켜야 하는 특별한 문자열 속성입니다. key가 왜 중요한지는 다음 섹션에서 다뤄보겠습니다.
이제 key
를 numbers.map()
내부의 우리 리스트 아이템에 할당하고 키가 없다고 하는 이슈를 해결해봅시다.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
키는 리액트가 어떤 아이템이 추가, 변경, 삭제되었는지 구분하는 것을 도와줍니다. 엘리먼트에게 안정적인 구분자(identity)를 주기 위해 엘리먼트의 내부 배열에 주어지는 것이 좋습니다.
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
키를 고르는 최고의 방법은 동등한 레벨의 엘리먼트(siblings) 사이에서 유일하게 그 리스트 아이템을 식별하는 문자열을 사용하는 것입니다. 여러분은 아마 데이터에서 ID를 뽑아 key로 사용하게 될 것입니다.
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
만일 렌더링 하는 아이템에 대한 ID가 없다면, 마지막 방책으로 item index를 키로 사용할 수도 있습니다.
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
만일 아이템의 순서가 변할 수 있다면, index를 키로 사용하는 것을 권장하진 않습니다. 성능에 나쁘게 영향을 미칠 수도 있고 컴포넌트의 상태에 관련된 이슈를 초래할 수도 있습니다. Robin Pokorny의 아티클 (인덱스를 키로 사용하는 것이 왜 나쁜지에 대한 깊이 있는 설명)를 확인해보세요. 만일 명시적으로 키를 리스트 아이템에 할당하지 않기로 했다면, 리액트는 기본값으로 인덱스를 키로 사용할 것입니다.
여기 왜 키가 필수적인가에 대한 깊이 있는 설명이 있으니 확인해보세요.
키는 오직 감싸진 배열의 컨텍스트에서만 영향을 발휘합니다.
예를 들면, 여러분이 ListItem
컴포넌트를 추출한다면, 여러분은 키를 ListItem
자체 내부 <li>
엘리먼트에 갖고 있기 보다는 배열 속 <ListItem />
엘리먼트에 갖고 있어야 합니다.
function ListItem(props) {
const value = props.value;
return (
// Wrong! There is no need to specify the key here:
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Wrong! The key should have been specified here:
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
function ListItem(props) {
// Correct! There is no need to specify the key here:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<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()
호출 안에 있는 엘리먼트가 키를 요구하는 것입니다.
배열 내부에서 사용되는 키는 Sibling사이에서 유일해야 합니다. 하지만 키가 전역적으로 유일할 필요는 없습니다. 두 개의 다른 배열을 생성할 때, 우리는 같은 키를 사용할 수 있습니다.
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')
);
키는 리액트에 힌트로서 제공됩니다. 하지만 키는 컴포넌트로 넘어가지 않습니다. 만일 컴포넌트에 같은 값이 필요하다면, prop으로서 명시적으로 다른 이름으로 넘겨주세요.
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
위의 예제와 함께, Post
컴포넌트는 props.id
를 읽을 수 있습니다 하지만 props.key
는 못 읽습니다.
위의 예제에서, 우리는 분리된 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()
의 몸통부분이 너무 nested(들여쓰기가 많이 중첩) 되지는 않았는지 명심해야 합니다. 그렇다면 컴포넌트를 추출하기에 좋은 시기입니다.