페이지네이션

번역일: 2026년 6월 25일

페이지네이션

소개

다른 프레임워크에서 페이지네이션을 구현하는 일은 꽤 번거롭습니다. Laravel의 페이지네이터는 쿼리 빌더Eloquent ORM에 완전히 통합되어 있어, 별도의 설정 없이도 데이터베이스 레코드를 간편하게 페이지 단위로 나눌 수 있습니다.

기본적으로 페이지네이터가 생성하는 HTML은 Tailwind CSS와 호환됩니다. Bootstrap을 사용하는 프로젝트에서도 페이지네이션을 지원합니다.

Tailwind JIT 설정

Laravel 기본 Tailwind 페이지네이션 뷰를 사용하면서 Tailwind JIT 엔진도 함께 사용하는 경우, tailwind.config.jscontent 항목에 Laravel 페이지네이션 뷰 경로를 반드시 포함해야 합니다. 그렇지 않으면 JIT가 사용하지 않는 클래스로 판단해 해당 스타일을 제거할 수 있습니다.

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

기본 사용법

쿼리 빌더 결과 페이지네이션

가장 간단한 방법은 쿼리 빌더 또는 Eloquent 쿼리에서 paginate 메서드를 호출하는 것입니다. paginate 메서드는 HTTP 요청의 page 쿼리스트링 값을 자동으로 감지해, 쿼리의 LIMITOFFSET을 알아서 처리합니다. 개발자가 직접 계산할 필요가 없습니다.

아래 예시에서는 한 페이지에 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에 표시할 필요가 없다면 이 집계 쿼리는 불필요한 부담이 됩니다.

이런 경우 "이전"과 "다음" 링크만 표시하는 simplePaginate 메서드를 사용하면 단 하나의 효율적인 쿼리만 실행됩니다.

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

Eloquent 결과 페이지네이션

Eloquent 쿼리에도 동일하게 페이지네이션을 적용할 수 있습니다. 사용 문법은 쿼리 빌더와 거의 동일합니다.

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

물론 where 조건 등 다른 제약을 추가한 후에 paginate를 호출할 수도 있습니다.

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

Eloquent 모델에서도 simplePaginatecursorPaginate를 동일하게 사용할 수 있습니다.

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

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

한 페이지에 여러 페이지네이터 사용

하나의 화면에 두 개 이상의 페이지네이터를 렌더링해야 할 때가 있습니다. 기본적으로 두 페이지네이터 모두 page 쿼리스트링 파라미터를 사용하기 때문에 서로 충돌이 발생합니다. 이를 해결하려면 paginate, simplePaginate, cursorPaginate 메서드의 세 번째 인수로 각 페이지네이터가 사용할 쿼리스트링 파라미터 이름을 지정하면 됩니다.

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

커서 페이지네이션

paginatesimplePaginate가 SQL OFFSET 절을 사용하는 것과 달리, 커서 페이지네이션은 정렬 기준 컬럼 값을 비교하는 WHERE 절을 구성합니다. 이 방식은 Laravel의 페이지네이션 방법 중 가장 뛰어난 데이터베이스 성능을 제공하며, 대용량 데이터셋이나 무한 스크롤 UI에 특히 적합합니다.

오프셋 기반 페이지네이션이 URL에 페이지 번호(?page=2)를 포함하는 것과 달리, 커서 기반 페이지네이션은 인코딩된 "커서" 문자열을 쿼리스트링에 포함합니다. 커서에는 다음 쿼리가 시작해야 할 위치와 방향 정보가 담겨 있습니다.

http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0

cursorPaginate 메서드를 통해 커서 페이지네이터 인스턴스(Illuminate\Pagination\CursorPaginator)를 생성합니다.

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

커서 페이지네이터 인스턴스를 가져온 뒤에는 paginate, simplePaginate와 동일하게 결과를 표시할 수 있습니다. 커서 페이지네이터가 제공하는 인스턴스 메서드는 커서 페이지네이터 인스턴스 메서드 문서를 참고하세요.

WARNING

커서 페이지네이션을 사용하려면 쿼리에 반드시 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;

커서 페이지네이션의 장점은 다음과 같습니다.

  • 정렬 기준 컬럼에 인덱스가 있는 대용량 데이터셋에서 OFFSET보다 훨씬 빠릅니다. OFFSET은 건너뛸 행을 모두 스캔해야 하기 때문입니다.
  • 데이터 변경이 잦은 환경에서 오프셋 페이지네이션은 새 레코드 삽입·삭제 시 레코드가 중복 표시되거나 누락될 수 있지만, 커서 방식은 이런 문제가 발생하지 않습니다.

반면 커서 페이지네이션의 제약 사항도 있습니다.

  • simplePaginate와 마찬가지로 "다음"과 "이전" 링크만 표시할 수 있으며, 페이지 번호 링크를 생성하는 기능은 지원하지 않습니다.
  • 정렬 기준이 최소 하나 이상의 유니크한 컬럼(또는 유니크한 컬럼 조합)이어야 합니다. null 값을 가진 컬럼은 지원하지 않습니다.
  • order by 절에 쿼리 표현식을 사용하는 경우, 해당 표현식에 별칭(alias)을 붙이고 select 절에도 포함해야 합니다.
  • 파라미터가 있는 쿼리 표현식은 지원하지 않습니다.

페이지네이터 직접 생성

이미 메모리에 로드된 배열 데이터를 가지고 페이지네이터 인스턴스를 직접 만들어야 할 때가 있습니다. 필요에 따라 Illuminate\Pagination\Paginator, Illuminate\Pagination\LengthAwarePaginator, Illuminate\Pagination\CursorPaginator 중 하나를 선택해 생성하면 됩니다.

각 클래스와 쿼리 빌더 메서드의 대응 관계는 다음과 같습니다.

클래스대응 메서드전체 개수 필요 여부
PaginatorsimplePaginate불필요
CursorPaginatorcursorPaginate불필요
LengthAwarePaginatorpaginate필요

PaginatorCursorPaginator는 전체 레코드 수를 알 필요가 없는 대신, 마지막 페이지 인덱스를 조회하는 메서드를 제공하지 않습니다. LengthAwarePaginatorPaginator와 거의 동일한 인수를 받지만, 전체 레코드 수를 반드시 전달해야 합니다.

WARNING

페이지네이터를 직접 생성할 때는 배열을 직접 슬라이싱해서 전달해야 합니다. 방법이 잘 떠오르지 않는다면 PHP의 array_slice 함수를 참고하세요.

페이지네이션 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를 추가하려면 다음과 같이 작성합니다.

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를 붙이려면 다음과 같이 호출합니다.

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

페이지네이션 결과 표시

paginate 메서드는 Illuminate\Pagination\LengthAwarePaginator 인스턴스를, simplePaginateIlluminate\Pagination\Paginator 인스턴스를, cursorPaginateIlluminate\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으로 자동 변환됩니다.

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

변환된 JSON에는 total, current_page, last_page 등의 메타 정보가 포함되며, 실제 레코드는 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":[
        {
            // 레코드...
        },
        {
            // 레코드...
        }
   ]
}

기본 페이지네이션 뷰는 Tailwind CSS를 기반으로 합니다. Tailwind를 사용하지 않는 프로젝트라면 links 메서드에 뷰 이름을 첫 번째 인수로 전달해 원하는 뷰를 직접 지정할 수 있습니다.

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

가장 편리한 커스터마이징 방법은 vendor:publish 명령어로 기본 뷰 파일을 프로젝트로 내보내는 것입니다.

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

이 명령어를 실행하면 resources/views/vendor/pagination 디렉터리에 뷰 파일이 생성됩니다. 이 디렉터리의 tailwind.blade.php 파일이 기본 페이지네이션 뷰이며, 이 파일을 수정해 원하는 HTML로 변경할 수 있습니다.

기본 페이지네이션 뷰로 다른 파일을 지정하고 싶다면 App\Providers\AppServiceProviderboot 메서드에서 defaultViewdefaultSimpleView 메서드를 호출하면 됩니다.

<?php namespace App\Providers; use Illuminate\Pagination\Paginator; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Paginator::defaultView('view-name'); Paginator::defaultSimpleView('view-name'); } }

Bootstrap 사용

Laravel은 Bootstrap CSS용 페이지네이션 뷰도 기본 제공합니다. 기본 Tailwind 뷰 대신 Bootstrap 뷰를 사용하려면 App\Providers\AppServiceProviderboot 메서드에서 useBootstrapFive 또는 useBootstrapFour 메서드를 호출하세요.

use Illuminate\Pagination\Paginator; /** * 애플리케이션 서비스를 부트스트랩합니다. */ 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을 반환합니다.

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 6월 25일