무한 스크롤 기능을 구현할 때는 데이터의 일관성과 동기화가 중요한 문제입니다. 이번 글에서는 데이터 구조와 조회, 삭제, 업데이트, 생성 등의 상황에서 발생할 수 있는 문제와 그 해결 방안에 대해 자세히 살펴보겠습니다.
데이터 구조
데이터는 다음과 같은 구조를 가집니다: [ [ { item }, {} ] -> (page),... ]
각 페이지는 여러 개의 아이템을 포함하고 있으며, 무한 스크롤을 통해 페이지를 순차적으로 불러오게 됩니다.
조회
중복 및 누락 데이터
다른 사용자들이 데이터를 생성하거나 삭제하는 과정에서, 동일한 데이터를 중복해서 불러오거나 일부 데이터를 놓칠 수 있습니다.
중복 데이터 처리
infinite 호출 한 쪽에서 data
를 리턴할 때 Set을 사용하여 중복된 데이터를 제거합니다.
중복 데이터가 발생했다는 것은 이전 페이지의 데이터가 변경되었음을 의미합니다. 하지만 여기서 싱크를 맞추기 위해 모든 페이지를 다시 불러오는 것은 비효율적이므로, 기존 캐시 데이터를 유지하는 것이 좋습니다.
* SWR 라이브러리를 사용할 경우, 기본 설정으로 첫 번째 페이지는 항상 최신화를 위해 다시 불러오게 설정되어 있습니다. 이를 막을지는 사용자 결정이 필요합니다. (revalidateFirstPage: false
)
다음 페이지 로드에서 발생하는 문제
- 이전 페이지 데이터 구조 변화로 인해 빈 배열이 반환될 수 있습니다. 하지만 이는 다음 페이지는 없는 걸로 판단되어 큰 문제가 아닙니다.
- 다른 사용자가 데이터를 생성하면서 같은 데이터가 중복되어 들어올 수 있습니다. 불러온 데이터가 이전 데이터와 모두 동일하다면, 다음 페이지를 로드할지는 사용자의 결정이 필요합니다.
- 중복된 데이터가 섞여서 들어오는 문제는 위와 동일하게 처리됩니다. (Set)
- totalCount와 모든 페이지의 아이템 개수가 맞지 않는 문제: 이 문제는 필요하다면 새로고침 버튼을 추가하는 것이 좋습니다.
누락 데이터 발생 방지
infinite 사용 한쪽에서 다시 모든 페이지를 mutate 하여 데이터의 일치성을 맞출 수 있지만, 이는 서버 비용이 증가하는 문제를 초래할 수 있습니다. 또한 여기서 다음 데이터를 불러올 때 같은 누락 및 중복 데이터 문제가 발생할 수 있습니다.
mutate 기준을 설정(싱크가 맞지 않다고 판단)하여 해당 조건에 부합할 때 자동으로
mutate를 수행하면 서버 비용이 증가할 수 있으므로, 사용자에게 새로고침 아이콘 버튼을 제공하는 것도 좋은 방법입니다.
싱크가 맞지 않다고 할 수 있는 기준점은?
totalCount가 이전과 다를 때 vs dulicate data가 발생 vs 판단할 필요가 없다
- totalCount가 이전과 다르다
revalidate나 moreRead 호출할 때 모두 하나의 조회 function에서 사용하기 때문에 해당 function에서는 어느 함수에서 호출되었는지 모릅니다. 다음은 totalCount 바뀐 것을 인지하는 방법입니다.- handleMoreRead 할 때 state를 바꾼다.
- handleMoreRead, moreReadValidating(state): true=> fetch logic => load finish [useEffect] => 이전 totalcount가 바뀌었는지 확인 및 moreReadValidating(state): false
- revalidate 상관없이 조회 function에서 해당 totalCount가 달라졌는지 확인한다.
- revalidate 하기 전에 데이터 개수를 맞췄지만 totalCount로 판단하기 때문에 다르다고 인지가 됩니다.
- handleMoreRead 할 때 state를 바꾼다.
- duplicate data가 발생
누락된 데이터나 생성 조건이 마지막 페이지에 데이터가 추가되는 거라면 싱크가 맞지 않다에 충족하지 못합니다. - 그냥 비교하지 않고 새로 고침 아이콘을 노출하여 사용하는 방법도 있습니다.
삭제
데이터 이동
삭제된 데이터 이후의 페이지 데이터를 당겨와서 맞출지, 삭제된 데이터만 제거할지 결정해야 합니다. 이미 Set으로 중복 데이터를 제거하고 있으므로 큰 문제가 없습니다.
해당 고민은 option으로 사용자가 결정하면 괜찮을 거 같습니다. (Set을 안 할 수 있기 때문)
revalidate를 하지 않을 경우
데이터를 삭제하면, 현재 조회된 페이지에서 데이터가 당겨지면서(option) 다음 페이지(존재한다면)의 데이터 하나가 추가되어야 합니다. 하지만 revalidate를 하지 않아 해당 데이터가 누락됩니다.
여기서 다음 페이지 조회 시 totalCount가 달라졌다는 걸 알 수 있습니다. 하지만 그전에는 toalCount는 그대로 유지되기 때문에 사용하는 쪽에서 조정이 필요할 수 있습니다.
revalidate를 할 경우
모든 페이지를 revalidate 하면 서버 비용이 커지기 때문에 마지막 페이지만 다시 revalidate 하는 방법이 있습니다.
그러나 다른 사용자가 데이터를 생성/삭제하면 데이터가 중복되거나 누락될 수 있습니다. 이러한 경우, 사용자 경험(UX)에 불편함을 줄 수 있으므로 이럴 경우 모든 페이지를 revalidate를 하는 게 좋아 마지막 페이지 revalidate도(다른 사용자 영향이 없는 api면 사용하는 것을 권장하기 때문에) option으로 사용자 결정으로 제공하는 것이 좋습니다.
정리
- 데이터 이동 (option)
- revalidate (option)
- revalidate를 하지 않으면 마지막 페이지의 데이터 누락이 된다.
- last page만 revalidate (otpion)
- last page만 revlidate 하면 해당 시점의 데이터가 일치하지 않을 수 있다.
업데이트
revalidate를 하지 않을 경우
API 요청 후 업데이트된 데이터를 받아 캐시 데이터에서 해당 아이디를 찾아 교체합니다. 업데이트된 데이터를 받지 못하는 상황이면 다시 모든 페이지를 revalidate 하거나 요청한 데이터를 사용할 수 있지만, 요청한 데이터를 사용할 경우 데이터 신뢰성이 떨어질 수 있습니다.
revalidate를 할 경우
삭제 revalidate를 할 경우에서
언급한 것과 같이 서버 부하를 줄이기 위해 API 요청 후 캐시 데이터에서 해당 아이디를 찾아 해당 부분만 페이지를 revalidate 합니다. 그러나 외부 사용자의 영향으로 데이터가 누락되거나 중복될 수 있습니다. 이럴 경우 revalidate 또한 option으로 모든 페이지 및 해당 페이지만 revalidate 할지 option으로 사용자 결정으로 제공하는 것이 좋습니다.
정리
- revalidate (option)
- revalidate를 하지 않으면서 대체 데이터가 서버에서 받은 데이터로 사용하지 않을 경우 데이터 신뢰성이 떨어진다.
- 업데이트된 페이지만 revalidate (option)
- 업데이트된 페이지만 revalidate 하면 해당 시점의 데이터가 일치하지 않을 수 있다.
생성
revalidate를 하지 않을 경우
API 요청 후 추가된 데이터를 받아 캐시 데이터에 추가할지, 별도로 합칠지 사용자가 결정해야 합니다.
- cache 데이터에서 데이터 추가
- 원하는 페이지에 데이터 추가(option)
- 개별로 합치기
- cache 데이터와 추가한 데이터를 합쳐 하나의 배열 데이터로 보여주고 해당 데이터 또한 Set 합니다. (다음 페이지에 등록된 데이터가 존재 가능성이 있기 때문에)
만약 api에서 데이터를 못 받는 상황이면 다시 revalidate 하거나 로컬에서 요청한 데이터로 교체해야 합니다. 하지만 로컬에서 요청한 데이터로 추가한다면 데이터 신뢰성이 떨어질 수 있습니다.
여기서 다음 페이지 조회 시 totalCount가 달라졌다는 걸 알 수 있습니다. 하지만 그전에는 toalCount는 그대로 유지되기 때문에 사용하는 쪽에서 조정이 필요할 수 있습니다.
revalidate를 할 경우
API 요청 후 모든 페이지를 revalidate 합니다. 추가되는 페이지를 알고 있다면 해당 페이지만 revalidate 하면 됩니다. 그러나 이 또한 외부 사용자의 영향으로 데이터가 누락되거나 중복될 수 있습니다. 이럴 경우 revalidate또한 option으로 모든 페이지 및 해당 페이지만 revalidate할지 option으로 사용자 결정으로 제공하는 것이 좋습니다.
정리
- 원하는 페이지에 데이터 추가 (option)
- revalidate (option)
- revalidate를 하지 않으면서 대체 데이터가 서버에서 받은 데이터로 사용하지 않을 경우 데이터 신뢰성이 떨어진다.
- 만약 캐시데이터에 데이터를 추가하지 않고 개별로 합칠경우 별도의 데이터를 만들어 추가된 데이터와 캐시데이터를 합쳐서 보여준다.(Set)
- 추가된 페이지만 revalidate (option)
- 업데이트된 페이지만 revalidate하면 해당 시점의 데이터가 일치하지 않을 수 있다.
결론
기존 데이터를 다시 revalidate 하는 것은 기존 데이터가 사라질 수 있는 현상을 초래할 수 있어 신중해야 합니다. revalidate가 필요한 경우 모든 영역에서 일관되게 적용해야 하며, 그렇지 않으면 데이터 일관성을 유지하기 어려울 수 있습니다.
무한 스크롤을 사용할 때는 조회하는 API가 단독 사용자이거나, 관리자가 많지 않아야 데이터 동기화 문제를 최소화할 수 있습니다. 데이터 동기화 문제는 여러 사용자가 데이터를 관리하는 경우에 발생하기 쉬우므로, 이를 최소화하는 방안을 고려해야 합니다.