돌멩이 하나/에러는 미래의 연봉

React의 key에 index를 사용하면 안 되는 이유 (feat. 무한스크롤)

미래에서 온 개발자 2024. 2. 25. 18:03

지난주부터 회사 프로젝트에서 다른 팀원 분이 담당하던 feature를 내가 맡게 되었다. 인계를 받으면서 이슈 중 하나가 무한스크롤 기능 중 스크롤을 위로 올려 이전 페이지 아이템을 가져올 때 사용자가 현재 보고 있는 아이템이 화면에 유지되지 않고 새로 받아온 페이지의 첫번째 아이템으로 화면이 튀는 현상이었다. 반면 스크롤을 아래쪽으로 내려서 다음 페이지 아이템을 가져올 때는 현재 보고 있는 아이템이 화면에 그대로 유지되었다. 

 

인계를 해준 팀원 분은 이 이슈를 스크롤 앵커링 문제라고 생각했고, 바닐라 JS를 사용하거나 React에서도 useRef를 사용해서 직접 DOM 조작을 할 때는 위쪽이나 아래쪽 어디로 스크롤을 하든지 스크롤 앵커링이 정상적으로 동작하며, React의 상태를 UI에 렌더링하는 경우 스크롤 앵커링이 정상적으로 동작하지 않는다는 이슈 리포트를 작성해서 인계해주었다.

 

하지만 코드를 직접 돌려본 결과, 무한스크롤 할 아이템을 담은 상태에 map 메소드를 돌려서 <Row /> 컴포넌트를 호출하는 과정에서 Row 컴포넌트의 keyindex를 사용하는 것이 문제였다. 인덱스를 key로 사용했기 때문에 이전 페이지 데이터를 요청해서 상태 배열의 앞부분에 새로운 데이터를 삽입하는 과정에서 항목의 순서가 바뀔 때 key 또한 바뀌게 되기 때문에 사용자가 현재 보고 있던 아이템이 뒤로 밀리고 새로 받아온 데이터가 그 자리를 대체하는 것이었다. 

 

const [segments, setSegments] = useState([]);

// before
  segments
  .map((segment, index) => {
      return <Row rowIndex={index} key={index} segment={segment} />;
  })

// after
  segments
  .map((segment, index) => {
      return <Row rowIndex={index} key={segment.id} segment={segment} />;
  })

 

 

디버깅을 할 때 기본부터 차근차근 돌아봐야 한다는 걸 다시 한 번 생각하게 되었다. 

 

 

📚 같이 읽으면 좋은 포스팅

 

[React] map() 메소드로 여러 개의 html 엘리먼트 표시할 때 JSX key 속성과 싸운 이야기

지난주 금요일 리액트에 입문하고 리액트를 학습한지 D+6일차가 되었다. 지금까지 React intro / SPA / state & props 총 3개의 과제를 하면서 바닐라 JS를 쓰는 것보다 리액트를 쓸 때의 간편함을 경험할

bttrthn-ystrdy.tistory.com