리액트를 사용하다면 map 함수를 정말, 정말 많이 사용하게 된다. 그렇다면 이 map()
함수의 용도는 무엇일까? map() 함수는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환하는 함수이다.
arr.map(callback(currentValue[, index[, array]])[, thisArg])
위 구문과 같은 형태로 작성되며, 매개변수 callback
은 새로운 배열 요소를 생성하는 함수로 currentValue
(처리할 현재 요소), index
(처리할 현재 요소의 인덱스), array
(map()을 호출한 배열)의 세가지 인수를 가진다. 그리고 매개변수 thisArg
는 callback 함수 내에서 this로 사용될 값을 의미한다.
예를 들어, 간단하게 제곱근을 구하는 함수를 작성한다면
const numbers = [4,9,16,25,36];
const result = numbers.map(Math.sqrt);
console.log(result); // [ 2, 3, 4, 5, 6 ]
와 같이 작성하면 된다.
리액트에서 map()
함수를 자주 사용하는 이유는, 여러 컴포넌트를 불러올 수 있기 때문이다. 물론 단순 반복으로도 컴포넌트들을 가져와서 사용할 수는 있지만, map()
을 사용하면 보다 짧고 간결한 코드를 작성할 수 있고, 실제 데이터들을 사전에 한번에 정의하여 사용할 수 없기 때문에, map()
함수를 사용하는 편이 훨씬 좋다.
그러나 이 map()
함수로 컴포넌트들을 가져와서 사용하다 보면, 이렇게 key 값을 지정해주지 않으면 경고가 발생하는 것을 볼 수 있다.
실제로 해당 컴포넌트를 렌더링해보면, 정상적으로 작동하는 것 처럼 보이지만 콘솔창에도 역시 key를 지정하지 않았다는 경고가 뜬 것을 확인할 수 있다. 그렇다면 리액트에서 map()을 사용할 때 왜 key props를 부여해줘야 하는 걸까?
그렇다면 리액트에서 key의 역할이 무엇인지 알아보자.
Key는 React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는 역할을 한다. key는 엘리먼트에 안정적인 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야 하며, Key를 선택하는 가장 좋은 방법은 리스트의 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 문자열을 사용하는 것이다. 대부분의 경우 데이터의 ID를 key로 사용한다.
만약 map()을 사용할 때, 이 key props를 부여하지 않으면 리액트는 어떤 항목이 변경, 추가, 삭제되었는지 식별하는 데 어려움을 겪게 되고, 해당 이유로 다시 한번 전체를 리렌더링하게 된다. 즉, key props를 주었을 때는 변경된 부분을 식별해 필요한 부분만 리렌더링해주지만, key props를 주지 않는다면 불필요한 리렌더링이 발생하게 되는 것이다. 이는 속도 저하, 의도치 않은 오류등을 발생시킬 수 있다.
리액트 공식문서에서는 key props로 index를 주는 것을 권장하지 않고 있다. 또한 Robin Pokorny’s가 인덱스를 key로 사용할 경우 부정적인 영향에 대한 상세 설명 이라는 글 역시 존재한다.
항목의 순서가 바뀔 수 있는 경우 key에 인덱스를 사용하는 것은 권장하지 않습니다. 이로 인해 성능이 저하되거나 컴포넌트의 state와 관련된 문제가 발생할 수 있습니다.
그렇기 때문에 대부분의 상황에서는 데이터의 id 값을 key props로 주고, 렌더링 한 항목에 대한 안정적인 ID가 없다면 최후의 수단으로 항목의 인덱스를 key로 사용하도록 하자.