Skip to content

데이터베이스: 페이지네이션

소개

다른 프레임워크에서는 페이지네이션이 매우 고통스러울 수 있습니다. 라라벨의 페이지네이션 접근 방식이 신선한 공기가 되기를 바랍니다. 라라벨의 페이지네이터는 쿼리 빌더Eloquent ORM과 통합되어 있으며, 제로 구성으로 데이터베이스 레코드의 편리하고 사용하기 쉬운 페이지네이션을 제공합니다.

기본적으로 페이지네이터에서 생성된 HTML은 Tailwind CSS 프레임워크와 호환됩니다. 하지만 부트스트랩 페이지네이션 지원도 사용할 수 있습니다.

Tailwind JIT

라라벨의 기본 Tailwind 페이지네이션 뷰와 Tailwind JIT 엔진을 사용하는 경우, 애플리케이션의 tailwind.config.js 파일의 content 키가 라라벨의 페이지네이션 뷰를 참조하여 해당 Tailwind 클래스가 제거되지 않도록 해야 합니다.

content: [
'./resources/**/*.blade.php',
'./resources/**/*.js',
'./resources/**/*.vue',
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
],

기본 사용법

쿼리 빌더 결과 페이징 처리

아이템을 페이징 처리하는 여러 가지 방법이 있습니다. 가장 간단한 방법은 쿼리 빌더 또는 Eloquent 쿼리에서 paginate 메서드를 사용하는 것입니다. paginate 메서드는 사용자가 보고 있는 현재 페이지를 기반으로 쿼리의 "limit" 및 "offset"을 자동으로 설정합니다. 기본적으로 현재 페이지는 HTTP 요청의 page 쿼리 문자열 인수의 값으로 감지됩니다. 이 값은 Laravel에서 자동으로 감지되며, 페이저에 의해 생성된 링크에도 자동으로 삽입됩니다.

이 예제에서 paginate 메서드에 전달되는 유일한 인수는 "페이지당" 표시하고 싶은 아이템 수입니다. 이 경우 페이지당 15개의 아이템을 표시하도록 지정하겠습니다.

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* 모든 애플리케이션 사용자를 표시합니다.
*/
public function index(): View
{
return view('user.index', [
'users' => DB::table('users')->paginate(15)
]);
}
}

단순 페이징

paginate 메서드는 데이터베이스에서 레코드를 검색하기 전에 쿼리와 일치하는 총 레코드 수를 계산합니다. 이는 페이저가 총 몇 페이지의 레코드가 있는지 알 수 있도록 하기 위함입니다. 그러나 애플리케이션의 UI에서 총 페이지 수를 표시할 계획이 없다면 레코드 수 쿼리는 불필요합니다.

따라서 애플리케이션의 UI에 간단한 "다음" 및 "이전" 링크만 표시해야 하는 경우, simplePaginate 메서드를 사용하여 효율적인 단일 쿼리를 수행할 수 있습니다.

$users = DB::table('users')->simplePaginate(15);

Eloquent 결과 페이징 처리

Eloquent 쿼리를 페이징 처리할 수도 있습니다. 이 예제에서는 App\Models\User 모델을 페이징 처리하고 페이지당 15개의 레코드를 표시할 계획임을 나타냅니다. 보시다시피 구문은 쿼리 빌더 결과를 페이징 처리하는 것과 거의 동일합니다.

use App\Models\User;
 
$users = User::paginate(15);

물론 where 절과 같이 쿼리에 다른 제약 조건을 설정한 후에 paginate 메서드를 호출할 수 있습니다.

$users = User::where('votes', '>', 100)->paginate(15);

Eloquent 모델을 페이징 처리할 때 simplePaginate 메서드를 사용할 수도 있습니다.

$users = User::where('votes', '>', 100)->simplePaginate(15);

마찬가지로, cursorPaginate 메서드를 사용하여 Eloquent 모델을 커서 페이징 처리할 수 있습니다.

$users = User::where('votes', '>', 100)->cursorPaginate(15);

페이지당 여러 페이저 인스턴스

애플리케이션에서 렌더링하는 단일 화면에서 두 개의 별도 페이저를 렌더링해야 할 수 있습니다. 그러나 두 페이저 인스턴스 모두 현재 페이지를 저장하기 위해 page 쿼리 문자열 매개변수를 사용하면 두 페이저가 충돌합니다. 이 충돌을 해결하려면 paginate, simplePaginatecursorPaginate 메서드에 제공된 세 번째 인수를 통해 페이저의 현재 페이지를 저장하는 데 사용할 쿼리 문자열 매개변수의 이름을 전달할 수 있습니다.

use App\Models\User;
 
$users = User::where('votes', '>', 100)->paginate(
$perPage = 15, $columns = ['*'], $pageName = 'users'
);

커서 페이징

paginatesimplePaginate는 SQL "offset" 절을 사용하여 쿼리를 생성하는 반면, 커서 페이징은 쿼리에 포함된 정렬된 열의 값을 비교하는 "where" 절을 구성하여 작동하며, Laravel의 모든 페이징 방법 중 가장 효율적인 데이터베이스 성능을 제공합니다. 이 페이징 방법은 특히 대규모 데이터 세트 및 "무한" 스크롤 사용자 인터페이스에 적합합니다.

페이저에서 생성된 URL의 쿼리 문자열에 페이지 번호를 포함하는 오프셋 기반 페이징과 달리, 커서 기반 페이징은 쿼리 문자열에 "커서" 문자열을 배치합니다. 커서는 다음 페이징 쿼리가 페이징을 시작해야 하는 위치와 페이징해야 하는 방향을 포함하는 인코딩된 문자열입니다.

http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0

쿼리 빌더에서 제공하는 cursorPaginate 메서드를 통해 커서 기반 페이지네이터 인스턴스를 생성할 수 있습니다. 이 메서드는 Illuminate\Pagination\CursorPaginator의 인스턴스를 반환합니다:

$users = DB::table('users')->orderBy('id')->cursorPaginate(15);

커서 페이지네이터 인스턴스를 가져온 후, paginatesimplePaginate 메서드를 사용할 때와 마찬가지로 페이지네이션 결과를 표시할 수 있습니다. 커서 페이지네이터에서 제공하는 인스턴스 메서드에 대한 자세한 내용은 커서 페이지네이터 인스턴스 메서드 문서를 참조하십시오.

exclamation

커서 페이지네이션을 활용하려면 쿼리에 "order by" 절이 포함되어야 합니다. 또한 쿼리가 정렬되는 열은 페이지네이션하는 테이블에 속해야 합니다.

커서 vs. 오프셋 페이지네이션

오프셋 페이지네이션과 커서 페이지네이션의 차이점을 설명하기 위해 몇 가지 SQL 쿼리 예시를 살펴보겠습니다. 다음 두 쿼리는 모두 id로 정렬된 users 테이블의 "두 번째 페이지" 결과를 표시합니다:

# 오프셋 페이지네이션...
select * from users order by id asc limit 15 offset 15;
 
# 커서 페이지네이션...
select * from users where id > 15 order by id asc limit 15;

커서 페이지 매김 쿼리는 오프셋 페이지 매김에 비해 다음과 같은 장점을 제공합니다.

  • 대규모 데이터 세트의 경우 "order by" 열에 인덱스가 있으면 커서 페이지 매김이 더 나은 성능을 제공합니다. 이는 "offset" 절이 이전에 일치된 모든 데이터를 스캔하기 때문입니다.
  • 쓰기가 빈번한 데이터 세트의 경우, 사용자가 현재 보고 있는 페이지에 최근에 결과가 추가되거나 삭제된 경우 오프셋 페이지 매김은 레코드를 건너뛰거나 중복을 표시할 수 있습니다.

그러나 커서 페이지 매김에는 다음과 같은 제한 사항이 있습니다.

  • simplePaginate와 마찬가지로 커서 페이지 매김은 "다음" 및 "이전" 링크를 표시하는 데만 사용할 수 있으며 페이지 번호가 있는 링크 생성을 지원하지 않습니다.
  • 정렬은 고유한 열 하나 이상 또는 고유한 열 조합을 기반으로 해야 합니다. null 값이 있는 열은 지원되지 않습니다.
  • "order by" 절의 쿼리 식은 별칭이 지정되고 "select" 절에도 추가된 경우에만 지원됩니다.
  • 매개변수가 있는 쿼리 식은 지원되지 않습니다.

수동으로 페이지 매김 생성

때로는 이미 메모리에 있는 항목 배열을 전달하여 수동으로 페이지 매김 인스턴스를 생성하고 싶을 수 있습니다. 필요에 따라 Illuminate\Pagination\Paginator, Illuminate\Pagination\LengthAwarePaginator 또는 Illuminate\Pagination\CursorPaginator 인스턴스를 생성하여 이를 수행할 수 있습니다.

PaginatorCursorPaginator 클래스는 결과 집합의 총 항목 수를 알 필요가 없습니다. 그러나 이로 인해 이러한 클래스에는 마지막 페이지의 인덱스를 검색하는 메서드가 없습니다. LengthAwarePaginatorPaginator와 거의 동일한 인수를 허용하지만 결과 집합의 총 항목 수에 대한 개수가 필요합니다.

즉, Paginator는 쿼리 빌더의 simplePaginate 메서드에 해당하고, CursorPaginatorcursorPaginate 메서드에 해당하며, LengthAwarePaginatorpaginate 메서드에 해당합니다.

exclamation

수동으로 페이지 매김 인스턴스를 생성하는 경우 페이지 매김에 전달하는 결과 배열을 수동으로 "슬라이스"해야 합니다. 이 작업을 수행하는 방법을 잘 모르는 경우 array_slice PHP 함수를 확인하십시오.

페이지 매김 URL 사용자 지정

기본적으로 페이지 매김에서 생성된 링크는 현재 요청의 URI와 일치합니다. 그러나 페이지 매김의 withPath 메서드를 사용하면 링크 생성 시 페이지 매김에서 사용하는 URI를 사용자 지정할 수 있습니다. 예를 들어 페이지 매김에서 http://example.com/admin/users?page=N과 같은 링크를 생성하려는 경우 /admin/userswithPath 메서드에 전달해야 합니다.

use App\Models\User;
 
Route::get('/users', function () {
$users = User::paginate(15);
 
$users->withPath('/admin/users');
 
// ...
});

쿼리 문자열 값 추가

appends 메서드를 사용하여 페이지 매김 링크의 쿼리 문자열에 추가할 수 있습니다. 예를 들어 각 페이지 매김 링크에 sort=votes를 추가하려면 다음과 같이 appends를 호출해야 합니다.

use App\Models\User;
 
Route::get('/users', function () {
$users = User::paginate(15);
 
$users->appends(['sort' => 'votes']);
 
// ...
});

현재 요청의 모든 쿼리 문자열 값을 페이지 매김 링크에 추가하려는 경우 withQueryString 메서드를 사용할 수 있습니다.

$users = User::paginate(15)->withQueryString();

해시 조각 추가

페이지 매김에서 생성된 URL에 "해시 조각"을 추가해야 하는 경우 fragment 메서드를 사용할 수 있습니다. 예를 들어 각 페이지 매김 링크 끝에 #users를 추가하려면 다음과 같이 fragment 메서드를 호출해야 합니다.

$users = User::paginate(15)->fragment('users');

페이지 매김 결과 표시

paginate 메서드를 호출하면 Illuminate\Pagination\LengthAwarePaginator의 인스턴스를 받고, simplePaginate 메서드를 호출하면 Illuminate\Pagination\Paginator의 인스턴스를 반환합니다. 마지막으로 cursorPaginate 메서드를 호출하면 Illuminate\Pagination\CursorPaginator의 인스턴스를 반환합니다.

이러한 객체는 결과 집합을 설명하는 여러 메서드를 제공합니다. 이러한 도우미 메서드 외에도 페이지 매김 인스턴스는 반복자이며 배열로 루프할 수 있습니다. 따라서 결과를 검색했으면 Blade를 사용하여 결과를 표시하고 페이지 링크를 렌더링할 수 있습니다.

<div class="container">
@foreach ($users as $user)
{{ $user->name }}
@endforeach
</div>
 
{{ $users->links() }}

links 메서드는 결과 집합의 나머지 페이지들에 대한 링크를 렌더링합니다. 이러한 각 링크에는 이미 적절한 page 쿼리 문자열 변수가 포함되어 있습니다. links 메서드로 생성된 HTML은 Tailwind CSS 프레임워크와 호환된다는 점을 기억하십시오.

페이지네이터가 페이지 매김 링크를 표시할 때 현재 페이지 번호와 함께 현재 페이지의 이전 및 이후 3페이지에 대한 링크가 표시됩니다. onEachSide 메서드를 사용하면 페이지네이터에서 생성된 중간 슬라이딩 링크 창 내에서 현재 페이지의 각 측면에 표시되는 추가 링크 수를 제어할 수 있습니다.

{{ $users->onEachSide(5)->links() }}

JSON으로 결과 변환하기

Laravel 페이저 클래스는 Illuminate\Contracts\Support\Jsonable 인터페이스 계약을 구현하고 toJson 메서드를 노출하므로 페이징 결과를 JSON으로 변환하는 것이 매우 쉽습니다. 라우트 또는 컨트롤러 액션에서 페이저 인스턴스를 반환하여 JSON으로 변환할 수도 있습니다.

use App\Models\User;
 
Route::get('/users', function () {
return User::paginate();
});

페이저의 JSON에는 total, current_page, last_page 등과 같은 메타 정보가 포함됩니다. 결과 레코드는 JSON 배열의 data 키를 통해 사용할 수 있습니다. 다음은 라우트에서 페이저 인스턴스를 반환하여 생성된 JSON의 예입니다.

{
"total": 50,
"per_page": 15,
"current_page": 1,
"last_page": 4,
"first_page_url": "http://laravel.app?page=1",
"last_page_url": "http://laravel.app?page=4",
"next_page_url": "http://laravel.app?page=2",
"prev_page_url": null,
"path": "http://laravel.app",
"from": 1,
"to": 15,
"data":[
{
// Record...
},
{
// Record...
}
]
}

페이징 뷰 사용자 정의하기

기본적으로 페이징 링크를 표시하기 위해 렌더링되는 뷰는 Tailwind CSS 프레임워크와 호환됩니다. 그러나 Tailwind를 사용하지 않는 경우 이러한 링크를 렌더링하기 위해 자신의 뷰를 자유롭게 정의할 수 있습니다. 페이저 인스턴스에서 links 메서드를 호출할 때 메서드의 첫 번째 인수로 뷰 이름을 전달할 수 있습니다.

{{ $paginator->links('view.name') }}
 
<!-- 뷰에 추가 데이터 전달... -->
{{ $paginator->links('view.name', ['foo' => 'bar']) }}

하지만, 페이지네이션 뷰를 사용자 정의하는 가장 쉬운 방법은 vendor:publish 명령어를 사용하여 해당 뷰를 resources/views/vendor 디렉토리로 내보내는 것입니다:

php artisan vendor:publish --tag=laravel-pagination

이 명령어는 애플리케이션의 resources/views/vendor/pagination 디렉토리에 뷰를 배치합니다. 이 디렉토리 내의 tailwind.blade.php 파일은 기본 페이지 매김 뷰에 해당합니다. 이 파일을 편집하여 페이지 매김 HTML을 수정할 수 있습니다.

다른 파일을 기본 페이지 매김 뷰로 지정하려면 App\Providers\AppServiceProvider 클래스의 boot 메소드 내에서 페이저의 defaultViewdefaultSimpleView 메소드를 호출할 수 있습니다.

<?php
 
namespace App\Providers;
 
use Illuminate\Pagination\Paginator;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Paginator::defaultView('view-name');
 
Paginator::defaultSimpleView('view-name');
}
}

Bootstrap 사용하기

Laravel은 Bootstrap CSS를 사용하여 빌드된 페이지 매김 뷰를 포함합니다. 기본 Tailwind 뷰 대신 이러한 뷰를 사용하려면 App\Providers\AppServiceProvider 클래스의 boot 메소드 내에서 페이저의 useBootstrapFour 또는 useBootstrapFive 메소드를 호출하면 됩니다.

use Illuminate\Pagination\Paginator;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Paginator::useBootstrapFive();
Paginator::useBootstrapFour();
}

Paginator / LengthAwarePaginator 인스턴스 메소드

각 페이저 인스턴스는 다음 메소드를 통해 추가적인 페이지 매김 정보를 제공합니다.

메소드 설명
$paginator->count() 현재 페이지의 항목 수를 가져옵니다.
$paginator->currentPage() 현재 페이지 번호를 가져옵니다.
$paginator->firstItem() 결과에서 첫 번째 항목의 결과 번호를 가져옵니다.
$paginator->getOptions() 페이저 옵션을 가져옵니다.
$paginator->getUrlRange($start, $end) 페이지 매김 URL 범위를 만듭니다.
$paginator->hasPages() 여러 페이지로 분할할 항목이 충분한지 확인합니다.
$paginator->hasMorePages() 데이터 저장소에 더 많은 항목이 있는지 확인합니다.
$paginator->items() 현재 페이지의 항목을 가져옵니다.
$paginator->lastItem() 결과에서 마지막 항목의 결과 번호를 가져옵니다.
$paginator->lastPage() 마지막으로 사용 가능한 페이지의 페이지 번호를 가져옵니다. (simplePaginate를 사용할 때는 사용할 수 없습니다.)
$paginator->nextPageUrl() 다음 페이지의 URL을 가져옵니다.
$paginator->onFirstPage() 페이저가 첫 번째 페이지에 있는지 확인합니다.
$paginator->perPage() 페이지당 표시할 항목 수입니다.
$paginator->previousPageUrl() 이전 페이지의 URL을 가져옵니다.
$paginator->total() 데이터 저장소에서 일치하는 총 항목 수를 확인합니다. (simplePaginate를 사용할 때는 사용할 수 없습니다.)
$paginator->url($page) 주어진 페이지 번호의 URL을 가져옵니다.
$paginator->getPageName() 페이지를 저장하는 데 사용되는 쿼리 문자열 변수를 가져옵니다.
$paginator->setPageName($name) 페이지를 저장하는 데 사용되는 쿼리 문자열 변수를 설정합니다.
$paginator->through($callback) 콜백을 사용하여 각 항목을 변환합니다.

Cursor Paginator 인스턴스 메소드

각 커서 페이저 인스턴스는 다음 메소드를 통해 추가적인 페이지 매김 정보를 제공합니다.

메소드 설명
$paginator->count() 현재 페이지의 항목 수를 가져옵니다.
$paginator->cursor() 현재 커서 인스턴스를 가져옵니다.
$paginator->getOptions() 페이저 옵션을 가져옵니다.
$paginator->hasPages() 여러 페이지로 분할할 항목이 충분한지 확인합니다.
$paginator->hasMorePages() 데이터 저장소에 더 많은 항목이 있는지 확인합니다.
$paginator->getCursorName() 커서를 저장하는 데 사용되는 쿼리 문자열 변수를 가져옵니다.
$paginator->items() 현재 페이지의 항목을 가져옵니다.
$paginator->nextCursor() 다음 항목 집합의 커서 인스턴스를 가져옵니다.
$paginator->nextPageUrl() 다음 페이지의 URL을 가져옵니다.
$paginator->onFirstPage() 페이저가 첫 번째 페이지에 있는지 확인합니다.
$paginator->onLastPage() 페이저가 마지막 페이지에 있는지 확인합니다.
$paginator->perPage() 페이지당 표시할 항목 수입니다.
$paginator->previousCursor() 이전 항목 집합의 커서 인스턴스를 가져옵니다.
$paginator->previousPageUrl() 이전 페이지의 URL을 가져옵니다.
$paginator->setCursorName() 커서를 저장하는 데 사용되는 쿼리 문자열 변수를 설정합니다.
$paginator->url($cursor) 주어진 커서 인스턴스의 URL을 가져옵니다.