React/React 문법

[React]Key 이해하기

DevStory 2021. 7. 11.

React에서 배열 요소를 렌더링하는 경우 map()을 사용합니다.

아래 예제는 header라는 array의 object 수만큼 <th>...</th>를 렌더링하는 예제입니다.

See the Pen by JaeSung2386 (@jaesung2386) on CodePen.

 

 

위 코드를 실행하면, 콘솔 창에서 아래 사진과 같은 경고문을 볼 수 있습니다.

※ CodePen에서는 에러 및 오류를 무시하는 경우가 있으므로 VSCode나 CodeSandBox와 같은 다른 툴에서 테스트하시면 됩니다.

「 list 내부 각 child는 고유한 "key" prop를 가져야 한다. 」

 

배열 요소를 map()을 사용하여 렌더링 하는 경우에는 key라는 prop가 필요합니다.


key란 무엇인가?

재조정(Reconciliation)이 나타난 이유

key를 설명하기 앞서서 Virtual DOM과 Real DOM에 대해 간략하게 짚고 넘어가도록 합시다.

React는 Virtual DOM을 Memory에 저장하고 Real DOM에 동기화합니다. 이 과정을 재조정(Reconciliation)이라고 합니다.

 

이 재조정(Reconciliation)이라는 개념이 나타난 이유는 하나의 DOM으로 state 또는 props가 변경되어 컴포넌트가 다시 렌더링 하여 UI를 변경하는 과정을 처리하기에는 성능적으로 많은 문제가 있습니다. React는 아래 두 가지 가정을 기반하여 O(n) 복잡도를 가지는 휴리스틱 알고리즘을 구현하였습니다.

 

1. 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다.

-> 하나의 DOM이 아닌 두 개의 DOM(Virtual DOM, Real DOM)을 비교

2. 개발자가 key prop을 통해, 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할지 표시해 줄 수 있다.

 

key의 필요성

2번 내용에 대해 좀 더 구체적으로 알아봅시다.

 

아래 코드에서 맨 마지막에 <li>C</li>를 추가합니다.

<ul>
  <li>A</li>
  <li>B</li>
</ul>

맨 마지막에 <li>C</li>가 추가되었습니다.

<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ul>

React는 렌더링 하기 전에 아래 그림과 같이 비교를 합니다.

순서

1. Virtual DOM의 <li>A</li>와 Real DOM의 <li>A</li>를 비교합니다.

변경 사항이 없으므로 다음 엘리먼트로 넘어갑니다.

2. Virtual DOM의 <li>B</li>와 Real DOM의 <li>B</li>를 비교합니다.

변경 사항이 없으므로 다음 엘리먼트로 넘어갑니다.

3. Real DOM에는 <li>C</li>가 없으므로 Real DOM에 <li>C</li>를 추가합니다.

 

맨 앞에 엘리먼트를 추가하는 경우 성능상 문제가 발생합니다.

아래 코드에서 맨 앞에 <li>C</li>를 추가합니다.

<ul>
  <li>A</li>
  <li>B</li>
</ul>

맨 앞에 <li>C</li>가 추가되었습니다.

<ul>
  <li>C</li>
  <li>A</li>
  <li>B</li>
</ul>

비교 과정입니다.

순서

1. Virtual DOM의 <li>C</li>와 Real DOM의 <li>A</li>를 비교합니다.

Real DOM의 <li>A</li>를 <li>C</li>로 변경합니다.

2. Virtual DOM의 <li>A</li>와 Real DOM의 <li>B</li>를 비교합니다.

Real DOM의 <li>B</li>를 <li>A</li>로 변경합니다.

3. Real DOM에는 <li>B</li>가 없으므로 Real DOM에 <li>B</li>를 추가합니다.

 

제가 생각하는 로직은 그림처럼 <li>C</li>를 맨 앞에 추가하고 기존 엘리먼트들이 이동이 될 거라고 생각을 하였지만, 실제로는 모든 엘리먼트들이 변경이 되는 대참사가 발생합니다.

내가 생각하는 로직

이러한 방식을 방지하고자 key가 도입되었습니다.

key를 사용하여 맨 앞에 엘리먼트를 추가하는 경우입니다.

 

아래 코드에서 맨 앞에 <li key='C'>C</li>를 추가합니다.

<ul>
  <li key='A'>A</li>
  <li key='B'>B</li>
</ul>

맨 앞에 <li key='C'>C</li>를 추가되었습니다.

<ul>
  <li key='C'>C</li>
  <li key='A'>A</li>
  <li key='B'>B</li>
</ul>

Real DOM에 <li key='C'>C</li>가 추가되는 과정입니다.

순서

1. key가 'C'인 li 엘리먼트가 존재하는지 확인 후 없으면, <li key='C'>C</li>를 추가합니다.

2. <li key='A'>A</li>는 변경되지 않았으므로 이동합니다.

3. <li key='B'>B</li>는 변경되지 않았으므로 이동합니다.


index는 key로 적절하지 않다.

index는 key로 적절한지 알아봅시다.

See the Pen by JaeSung2386 (@jaesung2386) on CodePen.

순서

실행 화면 → 값 입력 → 로우 추가 버튼 클릭

 

왜 "값 입력"은 이동이 되지 않았는지 그림으로 알아봅시다.

위 방식을 개선한 코드

id라는 properties를 추가하여 key로 적용합니다.

예제 코드가 올바른 방법은 아니지만, index와 비교하여 설명하기 위해 작성하였습니다.

See the Pen React_key_EX3 by JaeSung2386 (@jaesung2386) on CodePen.

 

React는 key가 동일할 경우 동일한 엘리먼트를 보여주기 때문에 예기치 않은 문제가 발생합니다. 

 

index가 key로 사용해도 되는 경우

  1. 정적이고 변경되지 않는 경우
  2. 정렬을 하지 않는 경우(정렬을 하면 index가 변경됩니다.)
  3. 고유한 ID가 없는 경우
  4. 필터링 기능이 없는 경우

위 4개의 조건을 만족하면, index를 key로 사용하여도 괜찮습니다.


적절한 Key의 값은 무엇인가?

UUID

- 랜덤으로 생성된 문자열

 

nanoId

- 랜덤으로 생성된 문자열(UUID보다 빠르다는 장점을 가짐)

 

DataBase에서 auto-increment 처리된 id

- 행을 추가할 때, DB에서 auto-increment 처리된 id를 추가된 행의 key로 설정합니다.

- 2개 이상의 행을 추가하는 경우에는 적절하지 않습니다.

 

위 3가지 사용 방법은 추후 포스팅하도록 하겠습니다.


참고

https://blog.woolta.com/categories/1/posts/210

 

React 에서 배열컴포넌트 사용시 key 에 배열의 index는 가급적 사용하지 말자.!!

woolta 블로그 ver2.0 기술 개발 블로그

blog.woolta.com

https://ko.reactjs.org/docs/reconciliation.html

 

재조정 (Reconciliation) – React

A JavaScript library for building user interfaces

ko.reactjs.org

https://adhithiravi.medium.com/why-do-i-need-keys-in-react-lists-dbb522188bbb

 

Why do I need Keys in React Lists?

Lists in React need to have unique keys. Did you know why?

adhithiravi.medium.com

 

반응형

댓글