[angular, spring] 게시글 페이징 처리 Ngx-pagination
출저 및 참고 사이트 :
https://www.npmjs.com/package/ngx-pagination
서론
앵귤러 프론트 웹에서 게시글 페이징 처리를 할려고 material pagination 을 알아봤는데, 페이지의 previous, next 만 있고, 페이지를 고를 수 있는 페이지 버튼이 없어서 ngx-editor 로 알아보았다. (Angular Bootstrap 에도 페이징 처리하는 라이브러리가 있다고 한다.)
Ngx-Pagination
- 프로젝트 터미널에서 다음과 같은 명령어를 입력해 ngx-pagination 모듈을 설치한다.
npm install ngx-pagination --save
- app-modules.ts의 Imports 항목에 다음과 같이 추가한다.
imports : [ NgxPaginationModule ]
Ngx-Pagination 사용하기
컴포넌트 TS 파일
- TotalLength :any 변수 선언 (전체 게시글 받을 갯수)
- Page : number 변수 선언 (페이지)
- TotalLength = HTTP 통신으로 받아온 게시글 length
- HTML 템플릿에서 div 속성에 ngFor 반복문을 이용해 paginate : { itemPerpage : 총 페이지, currentPage : 현재페이지, totalItems:받아온 게시글 총갯수 } 를 작성한다.
- HTML 템플릿에서 페이지네이션 컨트롤을 작성한다. ```html
<pagination-controls (pageChange)=”p = $event”>
<br/>
<br/>
## ngx-pagination 예제 소스
TS파일 소스
```javascript
export class MyComponent {
p: number = 1;
collection: any[] = someArrayOfThings;
}
HTML파일 소스
<ul>
<li *ngFor="let item of collection | paginate: { itemsPerPage: 10, currentPage: p }"> ... </li>
</ul>
<pagination-controls (pageChange)="p = $event"></pagination-controls>
PaginationControl 속성
<pagination-controls id="some_id"
(pageChange)="pageChanged($event)"
(pageBoundsCorrection)="pageChanged($event)"
maxSize="9"
directionLinks="true"
autoHide="true"
responsive="true"
previousLabel="Previous"
nextLabel="Next"
screenReaderPaginationLabel="Pagination"
screenReaderPageLabel="page"
screenReaderCurrentLabel="You're on page">
</pagination-controls>
-
id [string]: 한 번에 둘 이상의 페이지 매김 인스턴스를 지원해야 하는 경우 ID를 설정하고 PaginatePipe 구성에 설정된 ID와 일치하는지 확인하십시오.
- pageChagne [event handler]: 지정된 식은 페이지 매김 컨트롤 중 하나를 클릭하여 페이지가 변경될 때마다 호출됩니다. $event 인수는 새 페이지의 번호가 됩니다. 이것은 PaginatePipe에 전달된 currentPage 변수의 값을 업데이트하는 데 사용해야 합니다.
- pageChange =”페이지 클릭시 처리메소드 ($event)”
- 매개변수 $event : 페이지 클릭했을때의 번호. next 누르면 next 클릭했을때의 이동할 page 번호
-
pageBoundsCorrection [evnet handler] : 지정된 표현식은 currentPage 값이 범위를 벗어났을 때 호출됩니다(예: 컬렉션 크기 감소). $event 인수는 가장 가까운 유효한 페이지의 번호입니다.
-
maxSize [number] : 표시할 최대 페이지 링크 수를 정의합니다. 기본값은 7입니다.
-
directionLinks [bollean] : false로 설정하면 “이전” 및 “다음” 링크가 표시되지 않습니다. 기본값은 true입니다.
-
autoHide [boolean] : true로 설정하면 컬렉션의 모든 항목이 첫 번째 페이지에 맞을 때 페이지 매김 컨트롤이 표시되지 않습니다. 기본값은 false 입니다.
-
responsive [boolean] : true로 설정하면 개별 페이지 링크가 작은 화면에 표시되지 않습니다. 기본값은 false 입니다.
-
previousLabel [string] : “이전” 링크에 표시된 레이블입니다.
-
nextLabel [string]: “다음” 링크에 표시되는 레이블입니다.
-
screenReaderPaginationLabel [string] : 화면 판독기용 컨트롤에 레이블을 지정하는 데 사용되는 “페이지 매김”이라는 단어입니다.
-
screenReaderPageLabel [string] : 스크린 리더용으로 생성된 특정 문자열에 사용되는 “페이지”에 대한 단어, 예: “다음 페이지”.
- screenReaderCurrentLabel [string] : 화면 판독기의 현재 페이지를 나타내는 문구, 예: “페이지에 있습니다”.
Mat-Table 과 Ngx-Editor 같이 사용하는 방법
- <mat-table> 속성에 paginate: { } 속성을 추가한다.
- dataSource의 데이터를 TS파일에서 MatTableDataSource 가 아닌 , data: any = []; 자료형으로 바꾼다.
<!--POST 등록시 중괄호 사용시에 빌드 오류 걸려서 이스케이프 문자 \ 붙임. 실제 예제에서는 \ 제거후 사용-->
<mat-table [dataSource]="boardList | paginate: \{itemsPerPage: 10, currentPage: page, totalItems: 100\}">
</mat-table>
DB에서 원하는 데이터 섹션만 가져오기 (Paging) SQL
- Limit : 몇개 가져올건지
- Offset : 몇번 부터 해서
끝번호부터 역순으로 10개씩 가져오는 SQL 예제)
SELECT idx, title, writer, writeDate, hit, state
FROM lee_board
WHERE state = 1
order by idx desc
LIMIT 10 OFFSET 0
HTTP GET 으로 통신하려면 Page 모델 vo 필요. (Page_LIMIT, Page_OFFSET)-
Page 모델로 요청받고, Response 할때는 Board 모델로 응답하는 형태. - GET 요청을 할때는 json body 사용은 지양 해야 한다.
- url의 Params로 GET 요청하고, 받을때는 Board VO 모델에 메타데이터 헤더를 달아서 받는 형식
- 메타데이터 예시)
- TotalCount: DB에 있는 게시글의 총 갯수
- Offset : 어디서 부터 가져올건지
- List<Board> : 게시글들의 리스트
Board.xml
<select id="selectAllPost" resultType="StudyBoard">
SELECT idx, title, writer, writeDate, hit, state
FROM lee_board
WHERE state = 1
order by idx desc
LIMIT #{page_limit} OFFSET #{page_offset}
</select>
HTTP Get으로 스프링 API에서 게시글 리스트 원하는 파트만 요청 응답 (Params 이용)
- HTTP GET 통신은 BODY 사용을 지양한다.
- 앵귤러 HTTP client module 에 get요청에서 body 를 사용하는 메소드는 없고, url params 를 쓰는 건 있다.
- 따라서 API 에서도 request 를 받을때 params 로 처리하도록 했다.
BackBoardController.java
@GetMapping("/getBoardList")
public List<StudyBoard> getBoardList(@RequestParam("page_limit") int page_limit, @RequestParam("page_offset") int page_offset){
return studyBoardService.getBoardList(page_limit, page_offset);
}
//게시글 리스트 조회 (page_limit, page_offset) 이용
Get으로 API에 게시글 리스트를 요청 할때는 board[] 위에 메타데이터 값을 갖는 헤더가 필요하다.
- BoardList의 VO 모델의 전체적인 모양은 이렇게 된다.
- DB에 존재하는 게시글 총 갯수. Totalcount
- Totalcount 를 구하기위해, API에서는 게시글들의 총 갯수를 구하는 SQL과, 서비스 구현이 필요하다.
- Totalcount 를 구하는 맵퍼.xml 의 resultType값은 INTGER 이여야 한다.
- 어디서 부터 짤라서 가져올건지 기준. offset
- Board 들의 리스트값을 넣는 items : Board[]
프론트 게시글VO 예제)
export interface Board {
idx: number;
title:string;
writer:string;
content:string;
writeDate : string
hit : number
}
export interface ListResponse {
items: Board[];
total: number;
offset: number;
}
백엔드 게시글VO 예제)
package kr.co.test.study.api.model.study;
import lombok.Builder;
import lombok.Data;
import java.util.List;
/**
* items: Board[];
* total: number;
* offset: number;
*/
@Data
@Builder
public class StudyBoardHeader {
int total;
int offset;
List<StudyBoard> items;
//items 에는 Board VO들의 값이 들어있는 리스트가 들어간다
}
프론트 Web에서 Paging 처리
- page 클릭시, TS파일에서 page_limit, page_offset 계산해서 api에 리스트 요청
<div class="pagination_button_wrapper">
<pagination-controls class="pagination_button"
(pageChange)="movePage($event)"
nextLabel="다음"
previousLabel="이전">
</pagination-controls>
</div>
MovePage 이벤트 처리 TS
movePage(page : any) : void {
// 클릭한 page 로 url 이동
this.page = page;
// 하단의 page 버튼 select 상태를 클릭한 page 번호로 변경 (currnetPage : page)
this.requestPage(page);
//가져올 페이지의 limit와 offset 을 계산해서 API 에 게시글 리스트 요청
this.router.navigate(['/BoardList/page', page]);
// URL을 클릭한 page 번호로 변경
}
requestPage(page : any) : void{
// 페이지 클릭시, 해당 page의 게시글을 api에 요청한다.
// offset ~ limit 까지의 게시글을 요청한다.
this.page_limit = 10;
// page_limit = 10 -> 게시글을 10개씩 갖고온다
this.page_offset = 10*(page-1);
// offset 값은 10개씩 가져온다고 했을때 (10*(1-1), 10*(2-1), 10*(3-1)...10*(page-1)) 으로 정해진다. 10*(page-1).
this.boardService.getBoardList(this.page_limit, this.page_offset).subscribe(data =>{
this.boardList = data.items;
this.listTotalCount= data.total;
})
}
게시글 page 리스트 조회 service.ts
public getBoardList(page_limit : number, page_offset : number, search?: string) : Observable<ListResponse>{
const options = {
params: {
page_limit,
// page_limit : page_limit 와 똑같은 기능. key 이름과, 매개변수의 이름이 같으면 value 를 적지 않아도 된다.
page_offset,
search: search ?? ''
},
};
return this.http.get<ListResponse>('/api/api/back/board/getBoardList', options);
// page_limit , page_offset url 파라미터로 API 에 게시글 리스트를 조회한다.
}