livewire 에서 api LengthAwarePaginator 데이터를 받아서 바로 사용하는 방법
Updated on
livewire 를 프론트엔드로 작업 하고 api endpoint 들은 백엔드 laravel 에서 분리 작업하는 경우들이 있다.
프론트엔드 livewire 에서는 최대한 api endpoint 에 있는데로 사용하길 원하는데, 이때 이 방법을 사용해서 하면 된다.
예전에 https://ggami.net/posts/234 해당 포스팅에 내용을 적긴 했었는데, 이 버전은 livewire v2 였었고, 지금은 livewire v3 이다. 생각보다 너무 많은 시간이 흘렀고, 많은 변경점들이 있었다.
아무튼 정리해보자면,
$items = $query->paginate(perPage: $perPage, columns: ['*'], page: $page, total: $total); return LocalResource::collection($items); return new LocalResource($items);
백엔드에서는 기존과 동일한 방식으로 pagination 을 만들어서 내려주면 된다.
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\Paginator; final class HelperUtil { public static function makeLengthAwarePaginator(array|null $lengthAwarePaginatorArray, string $pageName = 'page'): LengthAwarePaginator|null { if (empty($lengthAwarePaginatorArray)) { return null; } $meta = $lengthAwarePaginatorArray['meta'] ?? []; $perPage = $lengthAwarePaginatorArray['per_page'] ?? $meta['per_page'] ?? 15; $total = $lengthAwarePaginatorArray['total'] ?? $meta['total'] ?? 0; $items = collect($lengthAwarePaginatorArray['data'] ?? []); $page = Paginator::resolveCurrentPage(pageName: $pageName, default: 1); $path = Paginator::resolveCurrentPath(); $paginator = new LengthAwarePaginator( items: $items->forPage(1, $perPage), total: $total, perPage: $perPage, currentPage: $page, ); if (!blank($path)) { $paginator = $paginator->setPath($path); } return $paginator; } }
프론트엔드에서는 이렇게 makeLengthAwarePaginator
함수를 만들어서 사용해야한다.
예제를 더 작성하자면,
protected function requestGetLocalData(): LengthAwarePaginator|null { try { $data = $this->client->getLocalData([ ...$this->filter, 'page' => $this->page, ]); } catch (ClientException $e) { if ($e->getCode() === 422) { $responseError = json_decode($e->getResponse()->getBody()->getContents(), true); $this->setErrorBag(new MessageBag($responseError['errors'] ?? [])); return null; } throw $e; } catch (Exception $e) { throw $e; } return HelperUtil::makeLengthAwarePaginator($data, 'p'); }
이렇게 api request 에서 받아온 paginator response 를 그대로 makeLengthAwarePaginator
에 넣어주면 된다.
public function render() { $data = $this->requestGetLocalData(); return view('livewire.local-data', [ 'itemPaginator' => $data, ]); }
그리고 그렇게 받아온 LengthAwarePaginator
를 render 로 넘겨준다.
@foreach ($itemPaginator ?? [] as $index => $item) <tr> <td>{{ $item['id'] }}</td> </tr> @endforeach <div class="mt-4"> {{ $itemPaginator?->links('vendor.livewire.tailwind') }} </div>
프론트엔드 blade.php 에서는 이렇게 작성해주면 된다.
그런데 여기서 livewire v3 로 업데이트 되면서 변경된 점들이 있는데, 이런 부분까지 모두 작업을 해줘야만 한다.
use WithPagination { setPage as withPaginationSetPage; } #[Url(as: 'p', except: 1)] public $page = 1; public array $filter = [ 'search' => '', 'order' => 'id.desc', ]; public function setPage(int $page): void { $this->withPaginationSetPage(page: $page, pageName: 'p'); $this->page = $page; }
setPage
를 추가해줘야만한다. ($this->page = $page 도 넣어줘야한다…)
그런데 여기까지 작업을 했는데, {{ $itemPaginator?->links('vendor.livewire.tailwind') }}
여기에 문제가 있었다.
Previous
Next
버튼을 클릭했을때 문제가 있었다.
wire:click="previousPage('{{ $paginator->getPageName() }}')" wire:click="nextPage('{{ $paginator->getPageName() }}')"
왜냐하면, $items->forPage(1, $perPage),
여기 때문인건데, page를 1로 고정해야지만 데이터를 정상적으로 출력해줄수가 있다. ($page 를 넣으면 안됨)
그러다보니 next는 2가 되고 previous는 1이 된다.
그래서 이 문제를 해결하려면, tailwind.blade.php
파일을 수정해주면 된다.
wire:click="gotoPage({{ $paginator->currentPage() - 1 }}, '{{ $paginator->getPageName() }}')" wire:click="gotoPage({{ $paginator->currentPage() + 1 }}, '{{ $paginator->getPageName() }}')"
이렇게 수정을 해주면, api request 에서 paginate
를 그대로 사용해서 livewire frontend 에서도 사용할 수 있다.
참고로 ‘p’ 는 pageName
이다. queryString
에 맞게 설정해주면 된다.
생각보다 이렇게 작업해서 하는게 편리하다.
이유는 언제든지 livewire 에서 vue 든 react 로 넘어갈 수 있기 때문이다.
백엔드와 프론트(livewire) 를 분리해서 작업하는게 생각보다 시간은 오래 걸리지만, 유지보수 측면이나 미래를 위해서는 필요하다고 생각된다.