React의 key

devAnderson·2022년 2월 1일
0

TIL

목록 보기
47/106

key란?

리엑트 개발을 하다보면 map을 이용하여 element들의 배열을 만들어낸 후 이것을 트리에 삽입하려고 하면 이러한 문구를 마주하게 된다.

Warning: Each child in a list should have a unique "key" prop

공식문서상 리엑트가 말하는 key의 의미는 아래와 같다.

스크린샷 2022-02-01 오전 11 59 38

즉, 키를 사용하는 이유는 리스트들의 각각에게 의미있는 증표를 부여하여

  • 어떤 element가 자식배열에 추가되고 있는지
  • 어떤 element가 자식 배열에 업데이트되고있는지
  • 어떤 element가 자식배열에서 삭제되었는지

를 확인해준다.

이런 절차가 왜 필요한지에 대해서는 배열의 비교를 생각해보면 고개가 끄덕여진다.

일반적으로 배열에서 특정 요소를 찾아내는 것은 자원이 많이 필요한 비싼 작업이다. 배열의 자식에 10개 20개 정도만 있다면 별로 상관없지만,

만약 자식 element가 담겨있는 해당 배열이 수백만개가 존재한다고 가정했을 때,
그 중에 단 하나의 자식을 찾으려고 배열 전체를 돌아보게 하는 것은 상당히 비효율적이다.

하지만, 만약 각 요소들에 대해 특정할 수 있는 특별한 "key"가 존재한다면, 손쉽고 효과적으로 배열에서 어떤 부위에 변화가 생기고 있는지를 확인할 수 있다.

리엑트의 비교절차

스크린샷 2022-02-01 오후 12 17 15

솔직히 말하면 내부적으로는 엄청나게 복잡한 메서드들과 개념들이 비교를 하고 있지만, 아주 간단하게 비교의 원칙만 따지고 본다면 아래와 같다.

  1. 리엑트는 메모리에 실제 DOM을 본딴 virtualDOM을 탑재하고 있다.
  2. 내부적으로 변화가 일어나면 아래와 같은 diffing 알고리즘으로 최대한 빠르고 효율적인 비교를 하기 시작한다
a1. 해당 태그 타입을 비교했을 때, 서로의 태그가 달라졌나요?
= 그렇다면 뒤도 돌아볼 것 없이 모든 자식을 통틀어 해당 컴포넌트는 전체적으로 리 랜더링이 되어야 합니다.
// virtual DOM
<div><MyComponent/></div>
// real DOM
<span><MyComponent/></span>


a2. 서로 같다면, 혹시 attribute의 내용물이 달라지진 않았나요?
= 만약 그렇다면, 뒤도 돌아볼 것 없이 모든 자식을 통틀어 해당 컴포넌트는 전체적으로 리 랜더링이 되어야 합니다.
// virtual DOM
<div className="after" title="stuff" />
// real DOM
<div className="before" title="stuff" />

그 이후로 차례대로 비교 알고리즘에 따라 최적화된 비교의 리스트를 하나하나 체크해가며 변경점을 확인해본다.
만약 변경점이 없는 요소라면, 그 요소를 새롭게 만들지 않고 그 요소의 메모리에 찾아가 그 내용물을 그대로 가져다가 새로운 virtualDOM에서 이용한다.

참고로 여담이지만, attribute의 내용물이 객체일 때 얕은 비교를 하고있으면 내용물의 안쪽이 달라졌을때 리랜더링이 안될거라고 생각했는데
스크린샷 2022-02-01 오후 12 41 14

스크린샷 2022-02-01 오후 12 41 22

저 상태로 객체쪽 데이터의 값을 바꾸어도 리랜더링이 되는 것을 확인하였기에 요소들 비교는 깊은비교를 하는 것으로 판단하고 있다.

즉, 그것은 리스트에 있어서도 마찬가지가 된다. 다만, 이때

// virtual DOM
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

// real DOM
<ul>
  <li>first</li>
  <li>second</li>
</ul>

요소가 저렇게 리스트의 끝에 삽입되는 경우라면 나머지 first와 second는 내용이 달라지지 않았으므로 변경할 필요가 없지만,
요소의 첫번째로 업데이트가 되면 배열 입장에서는 각 index에 들어가있는 값이 전부 다 달라진것이나 다름없으므로
전체 요소를 새롭게 만들어 virtual dom을 만들게 된다. 이것은 몹시 비효율적인 일이다.

하지만 각 요소들에게 명확하게 구분이 되는 key가 존재한다면, 리엑트는 비교를 할 때 이 key를 우선하게 바라보고 기존의 virtualDOM에서 해당 키를 가지고 있는 요소와, 새로 만들어진 virtualDOM에서의 해당 키를 가진 요소를 빠르게 찾아서 diffing 알고리즘으로 체킹하면서 비교해나간다.

참고로, 설정해주지 않으면 index를 디폴트값으로 사용하기 때문에 꼭 리스트에는 넣어주는 것이 좋다.

여담으로, 해당 key가 요구가 되는 것은 이렇게 자식의 순서가 빈번하게 바뀔 가능성이 있는 list에서 요구된다.

profile
자라나라 프론트엔드 개발새싹!

0개의 댓글