1. HTML 파일 생성
–
‘First’, ‘Next’, ‘►►’ 기능은 HTML 파일에서 제외됩니다.
<html layout:decorate="~{layout}">
<div layout:fragment="content">
<table class="table">
<tr>
<td>no</td>
<td>title</td>
<td>date</td>
<td>name</td>
<td>like</td>
<td>read</td>
</tr>
<tr th:each="row : ${list}">
//${list}: controller에서 mv에 list의 key/value 넣어줌
<td th:text="${row.bno}"></td>
<td>
<a th:href="http://jenny-s.m/@{/detail(bno=${row.bno})}" th:text="${row.b_title}"></a>
</td>
<td th:text="${#dates.format(row.b_date, 'yyyy-MM-dd HH:mm:ss')}"></td>
<td th:text="${row.member.member_name}"></td> <!
-- Member.java에 member클래스 안에 member_name이 있음 -->
<td th:text="${row.b_like}"></td>
<td th:text="${row.b_read}"></td>
</tr>
</table>
<div th:if="${!
list.isEmpty()}"> <!
-- list가 비어있지 않다면 -->
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="${!
list.hasPrevious} ? 'disabled'"><!
-- disabled: 부트스트랩 들어가면 먹는 기능 -->
<a class="page-link" th:href="@{|?pageNo=${list.number}|}">이전</a>
</li>
<!
--1부터 list.totalPages까지 숫자 찍기-->
<li class="page-item" th:each="page : ${#numbers.sequence(1, list.totalPages)}"
th:if="${page >= list.number and page <= list.number+10}"
th:classappend="${page == list.number} ? 'active'"><!
-- active: 부트스트랩 들어가면 먹는 기능 -->
<a class="page-link" th:text="${page}" th:href="@{|?pageNo=${page}|}"></a><!
-- ${page}: each의 변수명 -->
</li>
<li class="page-item" th:classappend="${!
list.hasNext} ? 'disabled'">
<a class="page-link" th:href="@{|?pageNo=${list.number+2}|}">다음</a>
</li>
</ul>
</div>
<button class="btn btn-primary" onclick="location.href="http://jenny-s.write"">글쓰기</button>
</div>
</html>
맨 위!
* #숫자.시퀀스()
– Thymeleaf에서 숫자 시퀀스를 만드는 도우미 메서드입니다.
– 주어진 시작 번호와 끝 번호 사이의 일련 번호를 생성합니다.
– 위의 코드에서 1은 시작 번호이고 list.totalPages는 종료 번호입니다.
* ${페이지 == 목록.번호} ? ‘활동적인’
– page와 list.number가 동일한 경우 활성 클래스 추가
– 활성: 클래스 이름만. 일반적으로 활성 상태를 표시합니다(현재 표시된 메뉴 항목 또는 현재 활성 탭 등을 표시할 때).
– active라는 이름은 많은 개발자들이 공통적으로 사용하는 클래스 이름 중 하나입니다.
* th:each=”page : ${#numbers.sequence(1, list.totalPages)}”와 th:each=page : ${list.totalPages}”의 차이점은 무엇입니까?
– 전자: #numbers 개체에서 제공하는 sequence 함수를 사용하여 1에서 list.totalPages까지의 범위를 만듭니다.
만들다반복으로 요소, 각 번호를 페이지 변수에 할당
– 후자: list.totalPages는 단일 숫자이므로 해당 값을 반복합니다.
(단수일 경우 각각 불필요)
*#지불하다
– 타임리프에서 제공하는 소모품
– 숫자와 관련된 몇 가지 유용한 기능을 수행합니다.
– 예. #numbers.sequence(from, to)는 지정된 범위의 정수 시퀀스를 반환합니다.
2. 컨트롤러 만들기
@RequiredArgsConstructor
@Controller
public class IndexController {
//controller -> service -> repository -> DB
@Autowired
private final IndexService indexService;
@GetMapping("/board")
public ModelAndView board(@RequestParam(value="pageNo", defaultValue="1") int pageNo) {
ModelAndView mv = new ModelAndView("board"); //board.html
//페이징 추가하기
Page<Board> list = this.indexService.boardList(pageNo);
//boardList 메서드에 pageNo라는 파라미터를 전달
//boardList 메서드는 페이지네이션을 적용한 게시글 리스트를 반환
//pageNo는 조회할 페이지 번호를 나타냄
//즉,pageNo에 해당하는 페이지의 게시글 리스트를 조회하여 list 변수에 할당함
mv.addObject("list",list);
return mv;
}
맨 위!
* (@RequestParam(value=”pageNo”, defaultValue=”1″) int pageNo)
– int pageNo: 매개변수로 사용
* 책 페이지
– JPA에서 제공하는 인터페이스
– 페이지가 매겨진 데이터를 처리하는 기능 제공
– (측면 인터페이스) Iterable 인터페이스를 상속하고 페이징된 데이터를 반복할 수 있습니다.
– 다양한 방법을 제공합니다.
대표 getTotalPages(), getContent(), getNumber() 등
– Pageble을 매개변수로 얻은 결과는 Page 형식이 됩니다.반환
– Page 형태로 반환> 대부분의 행을 가져오기 때문에 * 페이지
– 여기는찾고 있는 게시물 정보가 포함된 엔터티 클래스입니다.
게시물의 제목, 내용, 작성 날짜 등의 정보를 필드로 가지고 있습니다.
– 페이지페이지에 표시할 게시물을 포함하는 개체입니다.
즉, 페이징된 게시물 목록은 페이지 유형의 목록 변수에 저장됩니다.저장되었습니다.
(Spring Data Jpa에서 제공하는 메소드)
*페이징
-페이징 정보와의 인터페이스(페이징 처리에 사용)
– Spring JPA의 DB 쿼리에서 제한 쿼리를 쉽고 유연하게 사용 가능
– JPA 사용 시 Pageable 타입의 변수를 자동으로 넘기면 JPA는 DB 접근 및 데이터 조회 시 자동으로 경계조건 데이터를 import한다.
– PageRequest 클래스로 구현* @PageableDefault
– Pageable 개체의 기본값 설정
(컨트롤러의 Pageable 객체를 매개변수로 받는 메소드에서 주석을 사용하면
API를 요청할 때 페이징 가능한 객체에 대한 매개변수를 전달하지 않고 페이징 가능한 유형 매개변수에 기본값이 자동으로 제공됩니다.
– 기본 페이지 크기 및 정렬 순서와 같은 값을 설정할 수 있습니다.* getTotalElements()
– 쿼리 결과의 총 데이터 수를 반환(총 항목 수)
– Pageable의 조건으로 limit 키워드가 입력되지 않은 쿼리 결과의 개수입니다.
한 가지 유의할 점은 모든 데이터가 아니라 쿼리 결과의 수만 검색된다는 것입니다.
– 게시판 기능 사용하기 좋습니다.
나. 데이터의 총 개수를 사용자에게 알리기 위해* getTotalPages()
– 총 페이지 수를 반환합니다.
– 쿼리를 통해 조회된 항목을 크기별로 페이징했을 때 반환되는 총 페이지 수입니다.
– 이를 통해 손쉽게 사이드 버튼을 생성할 수 있습니다.* getSize()
– 페이지 크기를 반환합니다.
– 페이지는 요청된 전체 데이터를 특정 숫자로 나누고 특정 숫자의 크기로 구성됩니다.* getNumber()
– 현재 페이지 번호 반환(0부터 시작)* getNumberOfElements()
– 현재 페이지의 항목 수를 반환합니다.
– 최대 크기만큼 나올 수 있습니다.
3. 서비스 생성
@Service
@RequiredArgsConstructor //생성자기반
public class IndexService {
//IndexService: 데이터베이스와의 상호작용을 위한 메서드를 포함하고,
//해당 메서드를 사용하여 (indexRepository를 통해)게시물 데이터를 가져오는 기능을 수행
private final IndexRepository indexRepository;
private final MemberRepository memberRepository;
public Page<Board> boardList(int pageNo) {
List<Sort.Order> sort = new ArrayList<Sort.Order>();
//Sort 객체는 JPA에서 데이터를 조회할 때, 정렬을 위해 사용됩니다.
Order는 정렬 순서를 정하는 클래스
sort.add(Sort.Order.desc("bno")); //JPA는 _를 인식못함 //이 컬럼기준 역순정렬
//1부터 시작하기 위해
Pageable pageable = PageRequest.of(pageNo -1, 10,Sort.by(sort));
//pageNo:controller에서 defaultValue="1"로 설정해놔서 값은1. pageble은 0부터 셀수있도록 pageNo -1로 설정 //10개씩 출력
return this.indexRepository.findAll(pageable);
//select기능 //pageble을 repository로 보내기
//findAll(): 데이터베이스에 저장된 모든 게시물을 가져온 후, 이를 List<BoardDTO> 형태로 반환
//모든 엔티티 레코드를 조회하기 위한 메서드. 리턴 타입은 해당 엔티티의 리스트
}
맨 위!
* @RequiredArgsConstructor와 @Autowired의 차이점
: 두 설명 모두 Spring Framework에서 제공하는 Dependency Injection과 관련이 있습니다.(@RequiredArgsConstructor)
– 생성자를 자동으로 생성하는 Lombok 주석
– 이 주석이 있는 클래스의 생성자는 클래스에서 최종으로 선언된 필드만 매개변수로 받습니다.
– 이 경우 생성자를 직접 작성하는 것보다 코드를 간결하게 작성할 수 있습니다.
(@Autowired)
– Spring 컨테이너에서 관리하는 Bean 아래에 적절한 타입의 Bean을 자동으로 주입하는 Annotation
– 이 Annotation이 있는 필드나 생성자의 파라미터에 해당하는 Bean이 존재하지 않으면 에러가 발생한다.즉, @RequiredArgsConstructor는 자동 생성자 생성을 위한 주석입니다.
@Autowired는 의존성 주입을 위한 주석입니다.
* 목록
정렬 = newArrayList ();
– Sort.Order: 정렬 조건을 목록에 삽입하기 위한 객체
– 정렬: 정렬 조건을 나타내는 클래스. JPA에서 제공하는 findAll 메소드를 호출하면 데이터 정렬이 가능하다.
* PageRequest.of(pageNo -1, 10,Sort.by(정렬));
: 정렬 목록에 지정된 속성별로 정렬하여 pageNoth 페이지에 10개의 게시물을 표시합니다.
– Pageable을 구현하는 개체를 만듭니다.
– 첫 번째 인수: 찾을 페이지 번호(pageNo)에서 1을 뺍니다.
– 두 번째 인수: 페이지당 표시할 게시물 수
– 마지막 인수: 정렬 방법을 지정하는 정렬 객체* Sort.by(속성)
– 특정 속성을 기준으로 정렬하는 Sort 개체를 만듭니다.
4. 저장소 생성
public interface IndexRepository extends JpaRepository<Board,Integer> {
//JpaRepository<사용될entity클래스,ID값>
//ID는 repository에서 @ID로 설정
Page<Board> findAll(Pageable pageable); //Pageable : service에서 가져옴
//Board:(@Entity있는 dto역할)
//findTop5() : 5개만 조회
}
맨 위!
– JpaRepository만 상속하면 되므로 @Repository를 선언할 필요가 없습니다.*Jpa저장소
인터페이스를 통해 CRUD(만들기, 읽기, 업데이트, 삭제) 작업을 수행할 수 있습니다.
– PagingAndSortingRepository 및 CrudRepository 인터페이스 상속
– findAll(), findById(), save() 및 delete()와 같은 메소드를 자동으로 지원합니다.* 모두 찾기()
– 모든 엔터티 레코드를 가져오는 방법
– 반환 유형: 해당 엔터티 목록
5. 보드 엔티티 생성
(질문하는 역할)
@Getter
@Setter
@Entity //@Entity는 JPA에서 Entity 클래스임을 나타내는 어노테이션
@Table(name="board")
public class Board {
@Id
@Column(name="b_no") //컬럼명
//JPA는 _를 인식못해서, 컬럼명의 _빼고 bno로 변수지정(b_no -> bno)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int bno; //b_no의 변수명
}
맨 위!
* 수업 게시판
– 보드는 테이블 이름입니다.
– 클래스명이 테이블명과 다를 경우 @Table(name=”board”)로 매핑
– board.class의 int bno* @ID
– 필드가 기본 키임을 나타냅니다.
– In repository는 JpaRepository의 정수입니다.ID를 직접 입력하는 필드(@Id로 불러옴) * @Created값()
– ID 값을 따로 설정하지 않으면 자동으로 ID 값(래퍼 클래스: 정수 등)을 호출
– 기본 키 값 생성 전략에 대한 참고 사항* (전략 = GenerationType.IDENTITY)
– MySQL에서 자동 증가 값을 사용한다는 의미
참조
https://velog.io/@albaneo0724/Spring-Pagination%EA%B3%BC-Page-%EA%B7%B8%EB%A6%AC%EA%B3%A0-Pageable