새벽에 디스코드에서 이야기를 하다가 list와 map으로 여러개의 component를 렌더링할때 key를 왜 넣어줘야하는가?에 대한 이야기가 나왔었다.
어렴풋이만 알고있었고 왜 그런겁니다! 라고 명확하게 설명해드리지 못했어서 나도 한번 찾아봤다.
일단 콘솔창에 시뻘겋게 Warning : Each Child in a list should have a unique "key" prop.
이렇게 나오지만! 페이지는 정상적으로 작동한다 😱
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
export default function List() {
const listItems = people.map(person =>
<li>{person}</li>
);
return <ul>{listItems}</ul>;
}
위 코드처럼 list에 있는 데이터들을 렌더링할 수 있다.
다만 key를 추가하지 않아서 당연히 warning은 발생한다 :)
그러면 해당 warning을 없애려면 어떻게 해야하나? key
를 넣어주기만 하면된다.
단, string
혹은 number
형태의 유니크한 값을(고유하게 식별할 수 있는) key
로 넣어주면 된다.
<li key={person.id}>...</li>
리액트의 장점은 필요한 부부만 렌더링
한다는 것이다.
리스트를 생성할 때도 마찬가지로 배열 전체를 렌더링 하는게 아니라 업데이트된 부분만 계산해서 렌더링을 한다.
리스트가 [X, Y]
를 차례대로 렌더링한다고 가정해보자,
그 다음 Z라는 값이 추가 되어서 리스트가 [Z, X, Y]
순서로 변경되었다고 할때,
새로 들어온 Z라는 엘리먼트가 맨뒤가아닌 맨처음에 추가되면서 리스트의 순서가 변경되어 버린다.
(X, Y의 index가 1씩 밀리까)
사실상 X, Y의 값은 그대로이고 Z만 추가된건데, 순서가 뒤바뀌니 React는 이를 인식하지 못하고 리스트 전체를 다시 렌더링
해버리고, 이는 비효율적이라고 볼 수 있다.
key
는 리액트가 어떤 항목을 변경, 추가 또는 삭제할지 식별 하는것을 돕는다.
key
는 엘리먼트에 안정적인 고유성을 부여하기 위해서 배열 내부의 엘리먼트에 지정해야 한다.
key
를 선택하는 가장 좋은 방법은 리스트의 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 문자(또는 숫자)를 사용해야한다는 것! (대부분의 경우는 data의 id를 key로 사용한다)
우리가 컴퓨터를 할때, 바탕화면에 폴더를 여러개 만들어 두고 사용하지 않나?
근데 이때 폴더 이름을 다 숫자 1,2,3,4,...n 이런식으로 이름을 바꾸자고 가정해보자 (끔찍하네 ㅋㅋ)
이 상태에서 폴더가 추가되거나 삭제된다고 생각해보자.
그럼 숫자로된 폴더들이 어떤게 어떤 폴더였는지 내가 잘 기억할 수 있을까???
폴더 3개가 중간에 추가된다고 해보자, 그럼 내가 원하는 폴더가 어떤건지 명확하게 알 수 없을거 아닌가!
즉 key는 폴더의 이름과 같은 역할을 해준다고 볼 수 있다.
즉, 고유한 key
를 통해서 엘리먼트가 이미 존재하는지, 새로 추가되었는지, 업데이트 되었는지 등등 판단하기 때문에 리스트 순서가 변경되어도 필요한 부분만 렌더링할 수 있게 된다.
key
데이터베이스에서 데이터를 갖고올때
- 데이터가 데이터베이스로 부터 오는 경우라면, 데이터베이스의 keys또는ID를 유니크한 key로 사용할 수 있다.
로컬에서 생성된 데이터
- 데이터가 로컬에서 생성되고 유지되는 경우(ex: 메모앱) crypto.randomUUID()
또는 uuid
같은 페키지를 사용할 수 있다.
렌더링한 항목에 대해 안정적인 고유한 id가 없다면 최후의 수단으로 항목의 index를 key로 사용할 수 있다.
- 항목의 순서가 바뀔 수 있는경우 Key에 index를 사용하는것은 권장하지 않는다고함
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.'}
];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Blog posts={posts} />);