개발자

React의 Reconciliation 동작에서 children에 key를 추가했을 때, 트리의 변환 작업이 효율적이게 되는 이유를 모르겠습니다.

2023년 01월 22일조회 237

안녕하세요. React의 Reconciliation에 대해서 공부중인 FE 개발자입니다. https://ko.reactjs.org/docs/reconciliation.html 위 공식 문서를 보다보면, "자식에 대한 재귀적 처리"에 대한 내용이 나오는데요, 여기서 children에 key를 추가하면 트리의 변환 작업을 효율적으로 할 수 있다고 설명을 하고 있습니다만, 과연 이게 실제로도 효율적으로 처리되는지가 궁금합니다. (key가 없을 때 새 항목이 상단부터 추가되는 경우 의도한대로 동작하지 않는다는 것은 이미 알고 있습니다.) 아래는 제가 테스트한 코드인데요, 제가 생각했을 때, key가 없을 때는 counts 배열에 요소를 추가할 때마다 기존 ChildComponent 컴포넌트들이 모두 언마운트되고 다시 생성되는 등의 비효율적인 작업이 있거나, 그게 아니더라도 뭔가 key가 있을 때보다 매 클릭 시 렌더링이 느려야할 것으로 생각을 했었는데, key가 있든 없든 상관없이 기존 ChildComponent는 재생성되지 않고 그대로 사용되고(useEffect 훅이 최초 렌더링 이후에는 전혀 실행되지 않음), 렌더링 속도도 별 차이가 없는 것 같습니다. 혹시 제가 놓치고 있는 부분이 있을까요? key의 존재 유무에 따른 트리 변환 작업 효율성 테스트를 어떻게 해보아야 제대로 이해할 수 있을까요?

이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.
profile picture
익명님의 질문

답변 1

인기 답변

성원님의 프로필 사진

안녕하세요. 우선 테스트하신 코드에 관해서 Lists의 아이템에 키를 명시하지 않으면 인덱스를 기본 값으로 사용하는 것으로 알고있습니다. 따라서 작성자님이 테스트하신 코드에서는 배열 값이 재정렬 되지 않는다면 인덱스키에 의해 '재조정'이 효율적으로 일어날 것 같습니다, "If you choose not to assign an explicit key to list items then React will default to using indexes as keys." 참고 (https://reactjs.org/docs/lists-and-keys.html) "Reconciliation 동작에서 children에 key를 추가했을 때, 트리의 변환 작업이 효율적이게 되는 이유" 리액트는 자식 노드를 렌더링 할 때 기존, 현재의 돔 트리를 순차적으로 비교한 뒤 차이가 있을 경우 현재 돔 트리에 변화를 일으킨다고 합니다. 이 때 자식 엘리먼트가 추가되는 위치에 따라 의도되지 않은 UI를 그려낼 수 있습니다. 자식 노드에 Key가 있으면 Key를 기준으로 두 트리를 비교합니다. 키를 통해 엘리먼트를 식별하기 때문에 불분명한 순서에 따른 UI 오류를 방지할 수 있습니다.

profile picture

익명

작성자

2023년 01월 24일

안녕하세요 성원님, 친절한 답변 감사드립니다. 다만 아직도 key의 존재 유무에 따른 이전대비 새 돔 트리를 구성할 때의 효율성 차이가 이해되질 않습니다. https://reactjs.org/docs/reconciliation.html 위 링크 Recursing On Children의 두번째 예제에서 “React will mutate every child instead of realizing it can keep the <li>Duke</li> and <li>Villanova</li> subtrees intact. This inefficiency can be a problem.”와 같은 문구가 나오는데요, 여기서 mutate를 한다는 의미가 정확히 해석되지 않습니다. 저는 이전의 돔 트리를 부수고 다시 만든다는 의미로 받아들였었는데, 본문 코드로 테스트해 본 결과로는 그렇지 않은 것 같아서요!

성원님의 프로필 사진

성원

Software Engineer2023년 01월 25일

본문 코드에서는 mount 시점에 콘솔을 찍고있어서 테스트가 어려울 것 같습니다. Press me는 이미 마운트가 된 App과 ChildComponent의 리렌더를 초래하기 것이기에 라이프 사이클에서 render 시점에 해당합니다. Key는 re-render 발생시에 어떤 부분이 변경됐는지 계산하는 단계에서 사용될 것 같습니다 (render 페이즈). (언급해주신 부분에서 "mutate"에 해당하는 부분은 commit 페이즈를 말하는 것 같습니다, 확실하지는 않으나 추후에 정확한 정보를 알게되신다면 저도 알려주시면 감사하겠습니다!) *참고 (https://beta.reactjs.org/learn/render-and-commit) "During a re-render, React will calculate which of their properties, if any, have changed since the previous render. It won’t do anything with that information until the next step, the commit phase."

profile picture

익명

작성자

2023년 01월 25일

답변 감사드립니다. 일단 "mutate"의 의미 자체만 놓고 보면, 성원님께서 말씀하신대로 실제 DOM을 업데이트하는 commit 페이즈의 의미가 맞는 것 같습니다. 다만 제가 계속 집착했던 것은 "key에 값을 넣지 않았을 때(말씀하신대로 default로 index를 사용), 과연 실제로 비효율적인 재조정(render -> commit)이 일어나는가"였습니다. 와중에 제가 좀 더 고민 후, 다시 테스트를 해보았는데요, 혹시 아래 내용이 타당한지 한 번 확인해주시면 정말 감사드리겠습니다. 먼저 재테스트한 코드는 아래 링크에 있습니다. https://gist.github.com/sekhyuni/ef4293b84c6914baa6bbf530a01c3abb 자식 요소를 컴포넌트로 만들고 React.memo로 감싼 뒤에 key 존재 유무에 따른 리렌더링을 비교해본 결과, key가 index가 아닌 unique한 값인 경우, 앞단에 새 item이 추가될 때 기존 컴포넌트들은 리렌더링되지 않는다는 사실을 확인하였습니다. (key가 없을 때는 기존 컴포넌트들이 리렌더링됨. key로 사용된 index에 해당하는 기존 컴포넌트들의 props 값이 변경되었을 것이므로) 이로써 유추해보건대, 자식 요소를 컴포넌트로 만들지 않고 순수한 DOM 요소를 사용한 경우 또한 마찬가지로 key가 unique한 값으로 존재할 때는 기존 요소를 그대로 사용할 것이며, key가 없을 때는 key로 사용된 index에 해당하는 기존 요소들의 innerText 값이 변경되었을 것이므로 리렌더링이 발생할 것으로 보입니다. 일단 제 생각은 여기까지입니다만, 혹시라도 성원님께서 보시기에 조금이라도 이상한 점이 있으시면 가차없이 지적해주세요! 정말 많은 도움이 될 것 같습니다. 감사합니다.

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

목록으로
키워드로 질문 모아보기

실무, 커리어 고민이 있다면

새로운 질문 올리기

지금 가입하면 모든 질문의 답변을 볼 수 있어요!