Technical Writing

스크롤과 페이징

김맷돌 2023. 11. 9. 20:24
반응형

(좌)페이징 (우)스크롤

일상에서 핸드폰 또는 컴퓨터를 통해 어떠한 정보를 얻고자 하는 상황을 떠올려보자.

그 정보는 보통 스크롤 또는 페이지네이션으로 제공되곤 한다.

방대한 양의 정보를 작은 화면에 모두 나타내기란 어려운 일이기 때문이다.

이 글에서는 우리에게 익숙한 두 기법을 UX의 관점기술의 관점으로 나누어 비교 분석해보고자 한다.

 


1. UX의 관점에서

1.1. 무한 스크롤

무한 스크롤은 마감선이 보이지 않는 상태에서 방대한 양의 콘텐츠를 스크롤할 수 있는 인터페이스 패턴이다. 이 기술을 구현한 페이지에서는 사용자가 스크롤하여 하단에 닿을 때 새로운 페이지가 로드된다.

 

장점

  • 뛰어난 사용자 인터랙션
    • 튜토리얼과 같이 연속적이고 긴 콘텐츠에서 뛰어난 사용성을 제공한다.
    • 페이징에서 보여줄 수 없는 역동성을 선보일 수 있다. (e.g., 애플 스크롤 인터랙션)
  • 모바일 친화적인 인터페이스
    • 모바일 디바이스의 제스처 제어는 스크롤을 사용하기에 직관적이고 편리하다.

단점

  • 표기할 수 없는 항목 위치
    • 특정 지점에서의 위치를 표시할 수 없으며, 특정 지점으로 이동하기 어렵다.
  • 무의미해진 스크롤바
    • 스크롤바가 실제 데이터 양을 반영하지 못한다.
    • 스크롤바의 크기와 위치가 고정되어있지 않고 하단에 도착할 때 마다 변경된다.
  • 도달할 수 없는 Footer
    • 방법 1: Footer를 상단 또는 사이드바에 배치한다.
    • 방법 2: 추가 로딩 버튼을 사용하여 요청 시 콘텐츠를 로딩하도록 한다.
     

1.2. 페이징

페이징은 콘텐츠를 별도의 페이지로 나누는 사용자 인터페이스 패턴이다. 사용자는 페이지 네비게이션을 통해 다른 페이지로 이동할 수 있다.

 

장점

  • 검색에 적합한 인터페이스
    • 사용자의 목적이 단순 탐색이 아닌, 특정 항목에 대한 검색일 때 유용하다.
      • "스크롤은 연속이고, 클릭은 결정이다." - Joshua Porter
    • 통제가능성
      • 사용자가 필요로 하는 정보의 양 또한 데이터로 제공할 필요가 있다.
        • "종료점에 도달하는 것은 통제력을 제공한다." - David Kieras의 인간 - 컴퓨터 상호작용 심리학
  • 표시 가능해진 항목 위치
    • 사용자는 항목의 위치를 알 수 있고, 특정 위치로 이동하기 쉽다.
    • 전자 상거래 서비스에 적합하다.

단점

  • 추가작업
    • 다음 페이지로 이동하기 위해서는 버튼을 클릭하고, 페이지의 로딩을 기다려야 한다.
    • 모바일 환경에서의 페이지 버튼은 사용성을 떨어뜨린다.

 

1.3. 정리

  • 무한 스크롤은 상대적으로 모바일 환경에, 페이징은 상대적으로 PC 환경에 적합하다.
  • 무한 스크롤은 방대한 양의 콘텐츠를 탐색할 때, 페이징은 특정 정보를 검색하고자 할 때 적합하다.
  • 무한 스크롤은 Twitter, Facebook, Pinterest, Instagram과 같이 사용자 생성 콘텐츠의 스트리밍 사이트 또는 앱에 가장 적합하다.
  • 페이징은 사용자가 특정 항목을 찾는 목표지향 사이트 및 앱에 적합하다.
  • 콘텐츠 유형에 따라 검색 방법을 선택하는 방법도 있다.
    • 구글이 좋은 예시가 될 수 있다.
      • 사용자는 텍스트보다 훨씬 빠르게 이미지를 스캔하고 처리하기 때문에 구글 이미지는 무한 스크롤을 사용한다.
      • 반면 검색 결과를 읽는 데는 훨씬 오래 걸리기 때문에 구글 검색 결과는 여전히 전통적인 페이징 방법을 사용하고 있다.

 

2. 기술의 관점에서

(좌)커서 페이징 (우)오프셋 페이징

앞에서는 스크롤과 페이징으로 설명했지만, 기술적인 관점에서 둘을 커서 페이징오프셋 페이징으로 칭할 수 있다.

앞에서 설명한 페이징의 경우 80페이지를 조회한다고 하면, 79페이지의 offset을 가지기 때문에 이를 오프셋 페이징이라고 한다.

커서 페이징의 경우 커서==포인터라고 생각하면 되는데, 특정 항목까지 조회했을 때 이 아이템을 커서로 가르켜놓고 그 다음 항목부터 조회해온다고 해서 커서 페이징이다.

 

2.1. 오프셋 페이징

오프셋 페이징은 DB의 offset 쿼리를 사용하여 페이지 단위로 요청 및 응답한다.

전통적인 방식의 페이징 쿼리는 일반적으로 다음과 같은 형태이다.

SELECT *
FROM items
WHERE 조건문
ORDER BY id DESC
OFFSET 건너뛸행
LIMIT 페이지사이즈

이와 같은 형태의 페이징 쿼리는 2가지 문제점을 가지고 있다.

  1. 데이터 중복 및 누락
    • 1페이지를 조회하는 중에 누군가가 3개의 데이터를 맨 앞에 추가할 경우, 2페이지로 넘어갔을 때 1페이지에서 보았던 마지막 3개 데이터를 2페이지에서 다시 만나게 된다.
    • 1페이지를 조회하는 중에 누군가가 첫 3개의 데이터를 삭제했을 경우, 2페이지로 넘어갔을 때 2페이지에서 조회했어야 할 첫 3개 데이터를 지나치게 된다.
    • 잦은 수정, 생성, 삭제가 반복되는 페이스북이나 인스타그램과 같은 서비스에는 부적합하다.
  2. offset 쿼리의 성능 이슈
    • offset 쿼리를 사용하면 뒤로 갈수록 페이지 조회에 드는 비용이 커진다. 
      • 예를 들어 offset 10000, limit 20이라 하면 최종적으로 10,020개의 행을 읽어야 한다. 그리고 이 중 앞의 10,000개 행을 버리게 된다. (실제 필요한건 마지막 20개 뿐이니) 뒤로 갈수록 버리지만 읽어야 할 행의 개수가 많아 뒤로 갈수록 느려지고, 낭비도 커진다.

이렇게 오프셋 페이징은 "n개의 row를 읽은 다음 20개 주세요"와 같은 쿼리문을 사용하기 때문에 성능 저하가 발생하는 것인데, 반면에 커서 페이징은 "이 row 다음부터 20개 주세요"와 같은 쿼리문을 사용하기 때문에 offset으로 인한 성능 저하가 없다.

즉, offset 페이징은 우리가 원하는 데이터가 몇 번째에 있는지에 집중하고 있다면, cursor 페이징은 우리가 원하는 데이터가 어떤 데이터의 다음에 오는지에 집중한다.

 

2.2. 커서 페이징

커서 페이징은 사용자에게 응답해준 마지막 데이터를 기준으로 다음 n개를 요청 및 응답한다.

SELECT *
FROM items
WHERE 조건문
AND id < 마지막조회ID # 직전 조회 결과의 마지막 id
ORDER BY id DESC
LIMIT 페이지사이즈

마지막 조회 결과의 ID를 조건문에 사용하기 때문에 쿼리를 실행하면 조회 시작 부분을 인덱스로 빠르게 찾아 매번 첫 페이지만 읽는다. 즉, 아무리 페이지가 뒤로 가더라도 처음 페이지를 읽은 것과 동일한 성능을 가지게 된다. 

성능면에서는 cursor 페이징이 offset 페이징보다 뛰어나지만, 다음과 같은 단점도 가지고 있다.

  1. 제한된 정렬 기능
    • 커서 페이징은 정렬할 컬럼에 중복된 값이 존재하면 안된다.
    • "이 레코드 다음 레코드를 조회해줘"라고 할 수 있도록, 특정 지점을 커서로 지정할 수 있어야 하기 때문이다.
      • 그래서 대부분의 커서 페이징은 timestamp 컬럼을 기준으로 한다. timestamp는 순차적이고 고유하기 때문이다.

 

3. Pagination 성능 최적화 방안

대부분의 경우에, 페이지 단위로 조회하는 메서드에서는 매번 count 조회와 data 조회가 함께 일어난다. 조회되는 총 건수를 알아야만 페이지 번호를 노출할 수 있기 때문이다.

그러나, 조회 건수에 따라 count 쿼리는 실제 데이터 조회만큼 오래 걸릴 수도 있다. 총 몇 건인지 확인하기 위해 전체를 확인해야하기 때문이다.

💡 데이터 조회는 limit 10 등으로 지정된 사이즈만큼 읽고 나서는 더이상 읽지 않아도 되지만 count는 끝까지 읽어서 몇 건인지 확인해야 한다.

count 쿼리로 인한 성능 이슈를 첫 페이지 조회 결과를 caching 함으로써 해결할 수 있다. 처음 검색시 조회된 count 결과를 응답결과로 내려주어 JS에서 이를 캐싱하고, 매 페이징 버튼마다 count 결과를 함께 내려주는 것이다. 그리고 서버에서는 요청에 넘어온 항목 중, 캐싱된 count 값이 있으면 이를 재사용하고, 없으면 count 쿼리를 수행한다.

이 방식은 다음과 같은 상황에서 도움이 된다.

  • 조회 요청이 검색 버튼과 페이지 버튼 모두에서 골고루 발생하고
  • 실시간으로 데이터가 적재되지 않으며, 마감된 데이터를 사용할 경우

이럴 경우에 사용한다면 매 페이지 버튼 클릭시마다 발생하는 count 쿼리를 처음 1회만 호출하고 이후부터는 호출하지 않아 count 성능을 향상시킬 수 있다. 물론 JS에서 캐싱하고 있기 때문에 브라우저를 새로고침하게 되면 count는 다시 초기화되어 이후 첫 조회시 다시 쿼리가 수행되게 된다.

 

현재 우리 회사에서 내가 목격한 모든 페이지 조회 기능에는 최소 2개의 쿼리가 발생하고 있었다. (데이터 count + 데이터 조회) 따라서 사내 서비스의 실시간성이 별로 중요하지 않은 페이지에 count 캐싱 기능을 추가한다면 성능상의 이점을 누릴 수 있겠다.

 

정리하면, 한번 조회된 동일 조건의 count에 대해서는 클라이언트 영역에서 캐싱함으로써 추가적인 쿼리 요청을 최소화할 수 있다. 반면, 다음과 같은 단점도 있다.

  • 첫 페이지 조회가 대부분일 경우 효과가 없다.
    • 추가적인 페이지 조회가 필요하지 않으면 결국 매번 첫 조회라서 cache count를 사용할 수 없다.
  • 실시간으로 잦은 데이터 수정이 일어나 페이지 버튼 변경이 반영되어야 하는 경우 사용할 수 없다.
    • 마감된 데이터 혹은 실시간을 유지할 필요가 없는 경우에만 사용할 수 있다.

 


 

 

UX: Infinite Scrolling vs. Pagination

by Nick Babich

uxplanet.org

 

OFFSET is bad for skipping previous rows

OFFSET doesn’t deliver stable results and makes the query slow. Key-set pagination does neither.

use-the-index-luke.com

 

Why Order By With Limit and Offset is Slow - Faster Pagination in Mysql

Paging using LIMIT and OFFSET clauses in MySQL can be very slow. In this article we describe the seek method that allows a faster, more stable paging performance.

www.eversql.com

 

1. 페이징 성능 개선하기 - No Offset 사용하기

일반적인 웹 서비스에서 페이징은 아주 흔하게 사용되는 기능입니다. 그래서 웹 백엔드 개발자분들은 기본적인 구현 방법을 다들 필수로 익히시는데요. 다만, 그렇게 기초적인 페이징 구현 방

jojoldu.tistory.com

 

3-2. 페이징 성능 개선하기 - 첫 페이지 조회 결과 cache 하기

모든 코드는 Github에 있습니다. 지난 시간에 이어 count와 관련된 2번째 개선 방법은 첫 번째 쿼리의 결과를 Cache하기 인데요. 방법은 간단합니다. 처음 검색시 조회된 count 결과를 응답결과로 내려

jojoldu.tistory.com

 

왜 오프셋 페이징보다 커서 페이징일까?

Is offset pagination dead? Why cursor pagination is taking over Facebook’s developer page said it best: uxdesign.cc ※ 이 글은 위 글을 의역한 글입니다. ※ 제가 이해한 것을 토대로 약간 수정했습니다. 커서 기반 페이징

bbbicb.tistory.com

반응형

'Technical Writing' 카테고리의 다른 글

Heap Inspection  (1) 2023.11.13