페이지 구분

페이지 매김은 결과를 한 번에 검색하는 것이 아니라 페이지로 검색하는 것을 의미합니다. 이는 일반적으로 사용자가 결과의 다음 또는 이전 페이지로 이동할 수 있는 사용자 인터페이스가 표시되는 큰 결과 집합에 대해 수행됩니다.

Warning

사용되는 페이지 매김 방법에 관계없이 항상 순서가 완전히 고유한지 확인합니다. 예를 들어 결과가 날짜별로만 정렬되지만 동일한 날짜의 결과가 여러 개 있을 수 있는 경우 페이지를 매긴 두 쿼리에서 다르게 정렬되므로 페이지를 매길 때 결과를 건너뛸 수 있습니다. 날짜 및 ID(또는 다른 고유한 속성 또는 속성 조합) 모두를 기준으로 순서를 지정하면 순서가 완전히 고유해지며 이 문제를 방지할 수 있습니다. 관계형 데이터베이스는 기본 키에서도 기본적으로 순서를 적용하지 않습니다.

참고 항목

Azure Cosmos DB에는 페이지 매김을 위한 고유한 메커니즘이 있으며, 전용 설명서 페이지를 참조하세요.

오프셋 페이지 매김

데이터베이스로 페이지 매김을 구현하는 일반적인 방법은 SkipTake LINQ 연산자(SQL의 OFFSETLIMIT)를 사용하는 것입니다. 페이지 크기의 결과가 10개인 경우 다음과 같이 세 번째 페이지를 EF Core로 가져올 수 있습니다.

var position = 20;
var nextPage = context.Posts
    .OrderBy(b => b.PostId)
    .Skip(position)
    .Take(10)
    .ToList();

아쉽게도 이러한 기술은 매우 직관적이지만 몇 가지 심각한 단점이 있습니다.

  1. 데이터베이스는 애플리케이션에 반환되지 않더라도 처음의 20개 항목을 처리해야 합니다. 그러면 건너뛰는 행 수에 따라 증가하는 상당한 계산 부하가 생성됩니다.
  2. 업데이트가 동시에 발생하면 페이지 매김이 특정 항목을 건너뛰거나 두 번 표시될 수 있습니다. 예를 들어 사용자가 2페이지에서 3페이지로 이동할 때 항목이 제거되면 전체 결과 집합이 "위로 이동"되고 하나의 항목을 건너뜁니다.

키 집합 페이지 매김

오프셋 기반 페이지 매김(키 집합 페이지 매김 또는 검색 기반 페이지 매김이라고도 함)에 대한 권장 대안은 단순히 WHERE 절을 사용하여 오프셋 대신 행을 건너뛰는 것입니다. 즉, 오프셋 대신 가져온 마지막 항목의 관련 값을 기억하고 해당 행 뒤에 다음 행을 요청합니다. 예를 들어 가져온 마지막 페이지의 마지막 항목에 ID 값이 55라고 가정하면 다음을 수행합니다.

var lastId = 55;
var nextPage = context.Posts
    .OrderBy(b => b.PostId)
    .Where(b => b.PostId > lastId)
    .Take(10)
    .ToList();

인덱스가 PostId에 정의되어 있다고 가정하면 이 쿼리는 매우 효율적이며 낮은 ID 값에서 발생하는 동시 변경 내용에도 민감하지 않습니다.

키 집합 페이지 매김은 사용자가 앞뒤로 이동하지만 사용자가 특정 페이지로 이동할 수 있는 임의 액세스를 지원하지 않는 페이지 매김 인터페이스에 적합합니다. 임의 액세스 페이지 매김을 사용하려면 위에서 설명한 대로 오프셋 페이지 매김을 사용해야 합니다. 오프셋 페이지 매김의 단점 때문에 사용 사례에 임의 액세스 페이지 매김이 실제로 필요한지 또는 다음/이전 페이지 탐색이 충분한지 신중하게 고려합니다. 임의 액세스 페이지 매김이 필요한 경우 강력한 구현은 다음/이전 페이지로 탐색할 때 키 집합 페이지 매김을 사용하고 다른 페이지로 건너뛸 때 탐색을 오프셋할 수 있습니다.

여러 페이지 매김 키

키 집합 페이지 매김을 사용하는 경우 두 개 이상의 속성으로 정렬해야 하는 경우가 자주 있습니다. 예를 들어 다음 쿼리는 날짜 및 ID별로 페이지를 매깁니다.

var lastDate = new DateTime(2020, 1, 1);
var lastId = 55;
var nextPage = context.Posts
    .OrderBy(b => b.Date)
    .ThenBy(b => b.PostId)
    .Where(b => b.Date > lastDate || (b.Date == lastDate && b.PostId > lastId))
    .Take(10)
    .ToList();

그러면 다음 페이지가 이전 페이지가 종료된 위치를 정확히 선택하게 됩니다. 더 많은 순서 지정 키가 추가되면 추가 절을 추가할 수 있습니다.

참고 항목

대부분의 SQL 데이터베이스는 행 값: WHERE (Date, Id) > (@lastDate, @lastId)을 사용하여 위의 보다 간단하고 효율적인 버전을 지원합니다. EF Core는 현재 LINQ 쿼리에서 이 표현을 지원하지 않습니다. 이는 #26822에서 추적됩니다.

인덱스

다른 쿼리와 마찬가지로 적절한 인덱싱은 좋은 성능을 위해 매우 중요하며, 페이지 매김 순서에 해당하는 인덱스가 있어야 합니다. 둘 이상의 열을 기준으로 정렬하는 경우 이러한 여러 열에 대한 인덱스를 정의할 수 있으며, 이를 복합 인덱스라고 합니다.

자세한 내용은 인덱스 설명서 페이지를 참조하세요.

추가 리소스