[react]map 함수 적용시 key props를 부여하는 이유

Minju Kim·2022년 3월 20일
0

React

목록 보기
10/15
post-thumbnail

🙋‍♀️ map함수로 컴포넌트 여러 개 반환하기

리액트를 아주 처음 배우는 이 시점에서, '리액트 우오오오오!🤩'하게 되었던 포인트 중 하나가, map함수와 컴포넌트를 사용한 비교적 짧은 코드로 배열을 반복적으로 화면에 그려줄 수 있다는 것이었다. 일례로, 자바스크립트에서 배열을 화면에 그려주려면 querySelector부터 createElement, appendChild까지의 반복문을 돌려줘야 했고, 그 방법을 할 때면 더 쉬운 방법이 없는지, DOM을 새로 생성시켜주면 성능상의 문제가 발생하지는 않을지 고민하게 되었었다. 이걸 간단하게 해결해 버린 React..! 정말 쓸만한 놈이다.

기본적으로, 컴포넌트화 하지 않고 리액트에서 map함수를 사용하는 방법은 다음과 같다.
주어진 배열에 따라서 렌더링 되어야 할 부분에서 {}를 사용해 자바스크립트를 사용할 것을 명시해주고, map함수를 통해 렌더링 될 부분을 작성해주면 된다.

	    <div className="feed__comments">
        {commentsArr.map((item, index) => {
         return (
            <div className="feed__comment__item">
              <span className="avatar__id">canon_mj</span>
              <span>{item}</span>    
        })}
      </div>

🛑 그러나 위 코드를 실행하면, 리스트 각 항목에 key값을 넣어야 한다는 경고가 표시된다. 그럼 그 key값은 무엇이고 왜 넣어야 할까?

🙋‍♀️ Key란 React의 항목 변경을 돕는 식별자

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

리액트 공식문서에 따르면, key는 (point 1! 엘리먼트에 안정적인 고유성을 부여하기 위해) (point2! 배열 내부의 엘리먼트에 지정)해야 한다고 한다. 이게 무슨말일까?

🙋‍♀️ 엘리먼트에 안정성 부여

엘리먼트에 안정성을 부여하는게 무슨 말인지 이해하려면, 리액트가 DOM 노드의 자식들을 재귀적으로 처리할 때 어떤 식으로 처리하는지 알아야 한다. 리액트는 DOM트리에 변경이 있으면, 이전 것을 다 지우고 처음부터 그리는 것이 아니라, 새로 그려야 하는 트리와 이전 트리를 비교한 후 차이점이 있으면 변경을 생성한다.
// 전
<ul>
  <li>Dog</li>
  <li>Cat</li>
</ul>

//후
<ul>
  <li>Dog</li>
  <li>Cat</li>
  <li>Rabbit</li>
</ul>

위의 경우처럼 맨 마지막에 새로운 리스트가 생겨야 한다면, React는 두 트리에서 <li>Dog</li><li>Cat</li>이 일치하는지 확인 후 마지막으로 첫 번째 트리에 <li>Rabbit</li>이 없으므로 추가해준다. 여기까지 보면 무엇이 크게 무리가 없어 보이나, 문제는 리스트의 맨 앞에 엘리먼트가 추가되는 경우이다.

// 전
<ul>
  <li>Dog</li>
  <li>Cat</li>
</ul>


//후
<ul>
  <li>Rabbit</li>
  <li>Dog</li>
  <li>Cat</li> 
</ul>

위와 같이 엘리먼트가 추가되는 경우, 리액트는 차이점이 발생하는 <li>Rabbit</li>만 앞에 넣어주는 것이 아니라, <li>Dog</li> <li>Cat</li> 종속 트리는 유지하면서 그 자식들만 변경한다. 즉, Rabbit의 자식들이 Dog안으로 들어가며, Dog의 자식들은 Cat안으로, 그리고 마지막으로 Cat안에 있던 자식들이 새로 생겨난 트리 안으로 들어가게 되는 거대한 비효율이 발생하게 되는 것이다.
위와 같은 문제를 해결하기 위해 생겨난 것이 바로 이 Key라는 친구인 셈이다.

자식들이 Key를 가지고 있다면, React는 key를 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인하기 때문에, 변환작업이 우리가 예상한 대로 수월하게 진행될 수 있다.

🙋‍♀️ 배열 내부에 key를 부여해야 한다!

키는 주변 배열의 context에서만 의미가 있기 때문에, 배열 내부에 key를 부여해야 한다. 예를 들어, Comment 컴포넌트를 추철 한 경우, Comment안에 있는 div 엘리먼트가 아니라 배열의 Comment 엘리먼트가 key를 가지고 있어야 한다.
import Comment from "Comment.js";

  <div className="feed__comments">
        {commentsArr.map((item, index) => {
          return <Comment key={index} comment={item} />;
        })}
      </div>

// Comment.js
const Comment = ({ comment }) => {
  return (
    <div className="comment">
      <span className="avatar__id">canon_mj</span>
      <span>{comment}</span>
      <span className="feed__comment__reactions">
        <i className="fa-solid fa-x" />
        <i className="fa-regular fa-heart" />
      </span>
    </div>
  );
};

위 작성 예시처럼 key값은 각각의 컴포넌트 엘리먼트에 주는 대신, 배열의 Comment안에 부여해야 한다는 것이다. 위의 경우 props은 {key, comment}를 가지고 있는 객체가 되며, 구조분해할당 문법으로 받아올 때에는 {comment}로 반환할 내용을 가져와줬다.

🙋‍♀️ 그래서 사용법은?

1. Best Practice : 내 데이터 구조에 고유한 key 값 추가해주기

가장 좋은 방법은, "데이터 구조를 key값을 포함한 객체가 들어있는 배열의 구조로 만들어주는 것"이다.
const mydata = [ "text1", "text2", "text3"]

const mydata = [{text: "text1", id : 0}, {text: "text2", id : 1}, {text: "text3", id : 1}

2. 어쩔수 없다면.. map함수의 index값 사용

만약 key값을 넣을 수 없다면, 최후의 수단으로 map함수의 두번째 인자값으로 제공되는 index를 사용할 수 있는데, 이건 정말 최후의 수단으로 사용해야 한다.

인덱스를 key로 사용 중 배열이 재배열되면 컴포넌트의 state와 관련된 문제가 발생할 수 있습니다. 컴포넌트 인스턴스는 key를 기반으로 갱신되고 재사용됩니다. 인덱스를 key로 사용하면, 항목의 순서가 바뀌었을 때 key 또한 바뀔 것입니다. 그 결과로, 컴포넌트의 state가 엉망이 되거나 의도하지 않은 방식으로 바뀔 수도 있습니다.

위 인용문이 무슨 말인지는 아래 리액트 공식문서에서 제공하는 예제를 보면 더 쉽게 이해할 수 있다.
위쪽의 사진처럼 key에 고유한 id를 부여한 경우, 값을 앞에서부터 추가해도 id값과 추가하는 값이 일치되지만 아래쪽의 사진처럼 key에 index번호를 부여한 경우, 값을 앞에서부터 추가하려면 컨텐츠가 꼬이게 되는 현상이 발생한다.


💻 마무리하며

이번 과제를 진행하며 key값을 배열이 아닌 각각의 컴포넌트 엘리먼트에 부여해서 시간을 꽤 많이 보냈다. 그것 외에도 말 그대로 삽질한 것들이 있는데 이는 Westagram 총정리 글로 추후 다시 기록해보고자 한다. 나는 다 안다고 생각한 개념들이지만 막상 실제로 작성해보면 모를때가 훨씬 더 많다. 리액트는 나를 참 겸손케 한다 ^^.. 이번 과제를 진행하며 가장 크게 느낀 것은 _"뭐니뭐니해도 공식문서"_이다. 방법을 모르겠을 때에는 공식문서를 먼저 샅샅이 뒤져봐야 한다. 왜냐면 공식문서에 다~ 나와있기 때문.ㅎ.ㅎ... 앞으로 과제를 진행할 때에는 반드시 먼저 공식문서를 이해하는 것으로 진행하리라 다짐한다. 아직은 덜 친한 리액트지만, 삽질하며 하나하나씩 깊은 깨달음을 얻는 재미가 아주 쏠쏠하다. (뿌듯하고 보람찬 주말을 보내고 있는 나 칭찬해👏👏👏)
profile
⚓ A smooth sea never made a skillful mariner

0개의 댓글