React 공식문서 - 리스트와 Key
리액트로 컴포넌트를 구현하다보면, 배열 데이터에 map으로 반복해서 컴포넌트를 만들어줄 때가 있다.
key값을 넣어주지 않으면 아래와 같은 오류를 만날 수 있다.
key 값으로는 중복되지 않는 고유한 값을 넣는 것이 권장된다.
그렇다면 key값은 왜 사용해야하는 걸까?
리액트에서 배열을 렌더링할 때 key값을 넣어줘야 하는 이유
React에서 key값은 DOM 요소를 식별하기 위해 사용하는 유일한 값이다.
그래서 React는 이 key값을 가지고 기존 트리와 이후의 트리가 일치하는지 확인하기 때문에 배열을 이용하여 렌더링을 할 때 key값은 반드시 적어주어야 한다. 만약, key값이 없다면 변경이 없는 트리 전체를 리렌더링해야 하기 때문에 비효율적일 것이다.
배열을 렌더링할 때 key값이 없다면, 배열이 변경되었을 때 어떤 과정을 거쳐서 변경된 것인지를 알 수 없다.
아래처럼 배열이 변경되었다면 변경된 과정을 여러 가지로 추측할 수 있다.
[1, 2, 3] => [1, 3, 4 ]
- 2를 삭제한 후, 맨 뒤에 4를 추가한다.
- 2를 3으로 변경하고, 3을 4로 변경한다.
하지만 각 배열에 고유한 키 값이 있다면 어떤 요소가 어떻게 변경된 것인지를 알 수 있게 됨으로 배열의 변화를 리액트에 정확하게 전달할 수 있다.
{ {
key : 1 , value : 1 key : 1, value : 1
key : 2, value : 2 => key : 3, value : 3
key : 3, value : 3 key : 4, value : 4
} }
index가 유효한 key값이 아닌 이유
리액트의 공식문서에서는 항목의 순서가 바뀔 수 있는 경우 key값에 index를 사용하는 것을 지양하라고 하고 있다.
key값으로 index를 사용하면 성능이 저하되거나, 컴포넌트의 state와 관련된 문제가 발생할 수 있다.
앞서 말했듯, key는 React가 DOM요소를 식별하기 위해 사용하는 것이다. 하지만 key값으로 index를 사용하게 되면 아래의 예시처럼 변경 전과 후가 같은 key값을 가지기 때문에 React는 변화를 인지하지 못한다.
[1, 2, 3] => [1, 3, 4 ]
(1)(2)(3) (1)(2)(3)
//분명히 다른 요소인데, 인덱스로 인한 key값은 같다.
그렇다면 항상 배열 데이터를 이용한 렌더링에 id값을 생성해줘야할까?
Robin Pokorny’s의 글에서는 아래의 세 가지 기준을 제시했고, 아래의 기준에 부합한다면 index를 사용해도 된다고 이야기하고 있다.
- 배열의 요소들은 정적이고, 변경되지 않는다.
- 요소에 id값이 없다. (key값으로 사용하기 적합한 id)
- 배열의 순서가 바뀌거나, 필터링되지 않는다. (있는 그 자체로 사용한다)
유효한 key값 만들어주기
const stackList: ["React", "Node.js", "Figma"];
function StackBadgeList(){
return(
<div className="flex flex-wrap gap-1">
{stackList.map((stack) => (
<StackBadge key={?} stack={stack} />
))}
</div>
)
}
stackList는 문자열 배열로 이루어져 있어 데이터 자체에 유효한 id값이 없다.
저 배열은 읽기만 가능하고, 삭제 및 수정은 이루어지지 않지만 유효한 id 값을 별도로 생성해서 부여해줄 것이다.
고유한 id값을 생성하는 방법이 뭐가 있을까?
Math.random( )
첫 번째로 떠올랐던 방법은 Math.random 을 사용하는 것이었다. 하지만 이는 공식문서에서 적절하지 않은 방법의 예시 중 하나이고, 그 이유는 아래와 같다.
key는 반드시 변하지 않고, 예상 가능하며, 유일해야 합니다.
변하는 key(Math.random( )으로 생성된 값 등)을 사용하면 많은 컴포넌트 인스턴스와 DOM노드를
불필요하게 재생성하여 성능이 나빠지거나 자식 컴포넌트의 state가 유실될 수 있습니다.
그렇다면 무슨 방법으로 생성할 수 있을까?
UUID (Universal Unique Identifier : 범용 고유식별자)
MDN - UUID
위키피디아 UUID
UUID란?
범용 고유 식별자는 리소스를 고유하게 식별하는 데 사용되는 id라고 생각하면 된다.
컴퓨터 시스템은 매우 큰 난수를 사용하여 로컬에서 UUID를 생성하는데, 이론적으로는 고유하지 않을 수 있지만 실제로 사용할 때는 중복될 일이 거의 없다고 한다.
그럼 이 같은 UUID를 내 코드에 적용하기 위해서는 어떤 걸 이용할 수 있을까?
이를 도와주는 리액트의 라이브러리가 있다.
라이브러리를 이용하여 적용해보기
uuid - npm
설치하기
npm install uuid
사용하기
UUID의 버전은 여러 가지가 있고 그에 따라서 UUID 생성 방법이 다르다.
uuid 라이브러리의 경우 RFC4122의 1,3,4, 5 버전을 지원한다.
- version 1 : 타임 스탬프를 기준으로 생성 (호스트 ID, 시퀸스 번호 및 현재 시각으로 UUID 발급)
- version 3 : MD5 (이름공간 식별자 [UUID] 및 이름 [문자열]의 해시 기반)
- version 4 : 랜덤 생성 (무작위 UUID 생성)
- version 5 : SHA-1 (이름공간 식별자 [UUID] 및 이름 [문자열]의 해시기반) 해쉬를 이용해 생성
가장 많이 사용하는 버전은 version1과 version4인데, 그 중에서도 4를 많이 사용한다.
**애플도 4버전 사용
version1은 호스트ID를 가지고 발급하기 때문에 유출 가능성이 있어 보안상의 이유로 4를 더 많이 사용하는 것 같다.
React에서는 아래와 같이 모듈 방식으로 불러와서 사용하면 된다.
import { v4 as uuidv4 } from 'uuid';
uuidv4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
const stackList: ["React", "Node.js", "Figma"];
function StackBadgeList(){
return(
<div className="flex flex-wrap gap-1">
{stackList.map((stack) => (
<StackBadge key={uuidv4()} stack={stack} />
))}
</div>
)
}
이전처럼 유효하지 않은 값을 key값으로 사용할 때보다 훨씬 간편하고 확실하게 할 수 있어서 만족스럽다.
물론, 지금 보여지는 코드의 경우는에는 index를 key값으로 사용해도 되는 경우에 해당하지만 그렇지 않은 경우에는 이처럼 UUID를 발급받아서 key값을 주면 좋을 것 같다.
마무리하며
React에서 key prop이 어떤 역할을 하는지, 왜 유효한 값을 사용해야하는지, 적절한 id값이 없는 경우 어떻게 대체할 수 있는지에 대해서 알아보았다. 단순히 오류를 없애는데 집중하지 않고, 왜 일어난 오류인지를 원리부터 들여다보니 더욱 재미있는 것 같다.
그럼 오늘은 이만 !
안뇽 ~!
'🗂️ 개발 이모저모' 카테고리의 다른 글
[FeedB] invalidateQueries가 동작하지 않았던 이유 (with. NextJS) (0) | 2024.07.05 |
---|---|
[FeedB] 기획 소개 및 컨벤션 (feat.구현계획) (0) | 2024.06.17 |
[CSS] Box Model (1) | 2024.05.29 |
Redux의 기본 구조와 사용법을 익혀보자! (0) | 2024.05.06 |
[Next.js] SSR, Hydration (0) | 2024.03.31 |