[React] 컴포넌트의 key는 렌더링에 영향을 준다

js43o·2023년 3월 28일
0
post-thumbnail
post-custom-banner

리액트 state들의 리스트에 map() 메소드를 활용하여 여러 개의 컴포넌트들을 생성하는 것은 매우 흔한 일이다. 특히 이때 각 컴포넌트의 고유한 값으로 key 프로퍼티를 지정해 주는 것이 권장된다.

그러나 만약 컴포넌트마다 가지는 고유한 값이 마땅히 없다면, 랜덤한 값을 생성하여 key로 넣어주는 방안을 떠올릴 수도 있다.
아래는 uuid 라이브러리의 v1() 함수를 호출하여 그 생성값을 key로 사용하는 '댓글 컴포넌트'의 예시이다.

한편, 댓글 작성란에 텍스트를 입력하는 행동은 이미 등록된 다른 댓글들과는 상관이 없으므로 불필요한 리렌더링을 일으키지 않아야 한다.
위 컴포넌트의 props인 comment, accessible은 단순 텍스트 입력에 의해 바뀌지 않는 값이며, handleComment 역시 페이지 컴포넌트에서 먼저 useCallback 처리가 된 후 전달된다.

물론 댓글 컴포넌트 자체도 React.memo로 감싸주었다. 즉, 예상대로라면 props가 바뀌지 않으므로 불필요한 리렌더링이 발생하지 않을 것이다.

하지만... 글자 하나를 입력할 때마다 리렌더링이 일어나는 것을 볼 수 있다. 각 댓글 컴포넌트의 편집/삭제 버튼과 프로필 이미지가 계속 깜빡이고 있다.

원인은 key의 값으로 넣어준 v1()이 매번 재실행되며 서로 다른 값을 반환하는데, 이것이 해당 컴포넌트 내부에서 props로 받아오는 값은 아니지만 렌더링에 영향을 미친다는 점을 간과한 것이다.

애초에 key는 리액트가 가상 DOM 트리를 비교할 때 참조하여 더욱 효율적인 리렌더링을 수행하기 위한 용도인데, 이것이 매번 새로운 값으로 변한다면 key를 쓰는 의미가 없어진다.
컴포넌트는 매번 비효율적으로 모든 비교를 거쳐 리렌더링될 것이고, 각 컴포넌트마다 uuid()를 호출하는 비용만 추가로 더해질 뿐이다.

랜덤값 생성 함수를 호출하는 대신, 고정된 값인 인덱스를 key로 사용하면 이러한 현상은 없어진다.
하지만 인덱스 역시 주의해야 할 점이 있는데, 순서가 바뀔 수 있는 컴포넌트 간에는 사용하면 안 된다는 것이다.

이 문제는 이미 예전에 겪어본 적이 있었다. 단순히 컴포넌트 간 순서를 바꾸기만 했는데도 똑같은 컴포넌트가 복사되는 등 비정상적인 동작이 일어나 당황했던 기억이 있다.

가장 좋은 방법은 역시 컴포넌트가 가진 고정된 고유한 값을 key로 사용하는 것이다. 스키마 정의 단계에서부터 id와 같은 식별자를 꼭 멤버로 포함시키도록 하자.

결론

  • key로 고유값 생성 함수를 써도 되는 경우: 값 생성에 드는 비용이 크지 않을 때, 리렌더링 여부가 중요하지 않을 때
    • key를 아예 안 쓰는 것보다 못하다. 이렇게 쓰지 말자!
  • key로 인덱스를 사용해도 되는 경우: 컴포넌트 간 순서가 바뀌거나 새로 추가/삭제될 일이 없고, 단순 렌더링을 위한 목적으로 쓰일 때
  • 웬만하면 반드시 컴포넌트가 가진 고정된 고유한 값을 사용하자!
    • 없으면 만들어서라도 쓰자. ex) DB 객체 스키마에 id 멤버 추가
profile
공부용 블로그
post-custom-banner

0개의 댓글