[TIL] List와 고유한 Key, 그것이 뭣이 중헌디! 🔑

June hyoung Park·2020년 8월 22일
0

React

목록 보기
2/19
post-thumbnail

List & Map()🗂

리액트에선 데이터 배열을 Mapping 하여 각각의 데이터를 갖고있는 컴포넌트 배열로 변환할 수 있다. 이는 반복되는 각각의 컴포넌트에 데이터가 배열의 각 값을 할당해주는 수고스러움을 덜어 줄수있을뿐아니라, 배열에 데이터가 추가됨에 따라 자동으로 데이터가 담긴 컴포넌트를 생성해주는 자동화를 만들어줄 수 있다.

이를 위해선 먼저 JavaScript에서 리스트를 어떻게 변환하는지 알아야한다.

map() 메소드는 파라미터로 전달 된 함수를 통하여 배열 내의 각 요소를 프로세싱 하여 새로운 배열을 반환하는데,
아래는 map()함수를 이용하여 숫자가 담긴 배열의 값을 두배로 만든 후 map()에서 반환하는 새 배열을 doubled 변수에 할당하는 코드이다.

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

이 예제에서 상수 'doubled'는 [2, 4, 6, 8, 10]를 출력하게되며, React에서 배열을 엘리먼트 리스트로 만드는 방식은 위와 거의 동일하다.

위 예제와 동일한 배열을 mapping 하여 컴포넌트 배열로 변환하면 아래와 같다.

import React from "react";
import "./App.css";

const App = () => {
  const numbers = [1, 2, 3, 4, 5];
  return (
    <ul className="App">
      {numbers.map((item) => (
        <li>{item}</li>
      ))}
    </ul>
  );
};

export default App;

1부터 5까지의 숫자로 이루어진 리스트가 Dom트리에 잘 생성되는것을 알수있다!

그러나 브라우저 내의 콘솔창을 확인해보면 에러가 뜨는것을 볼 수 있는데...🤷🏻‍♂️



Key Error Handling

Key는 React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕습니다. key는 엘리먼트에 안정적인 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야 합니다. - React 공식 가이드 -

리액트는 배열 mapping을 통한 컴포넌트 반환 시 생성된 엘리먼트에 안정적인 고유성 부여를 위해 각 엘리먼트 내에 'key'값을 할당해줄것을 권하고있다. 위 에러는 리스트의 각요소들에게 고유한 "key"값을 설정 해주지 않았기 때문에 일어나는 에러 메세지이다.

이는 기존의 jQuery나 바닐라 JS에서 직접적으로 dom오브젝트를 제어하여 전체 UI dom트리를 업데이트하며 발생하는 퍼포먼스적인 문제를 보안하고자 만들어진 개념인 'Virtual DOM'과 연관이 깊은데,

리액트나 Vue같은 UI 라이브러리에서는 DOM을 직접적으로 제어하지않고 Virtual DOM을 생성해서 업데이트된 부분과 기존의 돔을 비교하는 과정을 통해 변경된 ui부분을 업데이트한다. 그리고 그 과정 즉, 어떠한 부분이 기존의 돔과 비교하여 변경이 되었는지 확인하기 위해각 엘리먼트에 중복성이 없는 유일한 값을 key값으로 할당하길 요구하는것이다.

const App = () => {
  const numbers = [1, 2, 3, 4, 5];
  return (
    <ul className="App">
      {numbers.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
};

위 코드를 보면 알 수 있듯이, <li>태그에 <li key={item}> 이런 방식으로 key값을 할당 해주었다. 고유한 값을 할당하기위해 'numbers'배열의 각 값들을 할당해주었다.

에러가 사라졌다.🙇‍♂️

왜 map 메소드의 인자인 index는 고유한 Key가 될 수 없을까?🙅‍♂️

Key값 할당에 있어서 몇가지 잘못된 방식들이 있다. 이중 가장 많이 저지르는 실수는 map메소드의 두번째 인자인 index의 값을 key값으로 할당하는 것인데, 이는 굉장히 위험한 방법이라고 한다.

먼저 JavaScript의 map 메소드의 구문을 살펴보자.

map() 구문

arr.map(callback(currentValue[, index[, array]])[, thisArg])

매개변수

callback
새로운 배열 요소를 생성하는 함수. 다음 세 가지 인수를 가집니다.
currentValue
처리할 현재 요소.
index Optional
처리할 현재 요소의 인덱스.
array Optional
map()을 호출한 배열.
thisArg Optional
callback을 실행할 때 this로 사용되는 값.

만일 map의 두번째 인자인 index로 키 값을 할당하게 된다면, 마치 index가 고유값을 보장하는것처럼 보일 수 있지만, 재사용 가능한 컴포넌트가 계속 사용되다 보면 하나의 UI컴포넌트안에 중복이 일어날 수 있다고 한다.

물론 위 예시와 같은 단순한 배열에선 문제를 일으키지않을 수 있지만, 간단한 예시로 Todo list로 봤을때, 엘리먼트를 추가하거나, 삭제하면서 리 렌더링이 발생할 시 index가 0부터 다시 mapping 되면서 엉뚱한 요소에 데이터가 맵핑되는 경우가 있을수 있다.

또한, 복잡한 코드에선 대부분 fetch나 api를 통한 연산 이후 데이터를 렌더링하게 되는데, 맵핑 내에서 들어가는 function들의 작업들은 대부분 비동기로 이루어지기 때문에 그 과정을 처리하는 컴포넌트가 두개 이상 나오게되면 경쟁 상태 (Race condition) 충돌이 일어날 수 있고, 이로 인해 인덱스의 고유값이 깨지게 되고 이는 예기치못한 결과로 이어질 수도 있다고 한다.

그렇기에 이런 사태를 겪지않기 위해 고유성이 보장되는 Database의 id나, global 변수를 이용해 id를 생성하고 이를 key로 사용해주는것이 좋다.

profile
Take me home~~~~

0개의 댓글