티스토리 뷰

01) NoticeController

@GetMapping(value = "/list.do")
public String paging() {

	return "noticeList";
}

 

일단 resources/templates/noticeList.html을 불러옴

 

02) Notice.html

 

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>게시판</title>
    <link rel="stylesheet" href="/webjars/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body class="container">

    <div class="jumbotron">
        <h2>공지사항</h2>
    </div>

    <table class="table">
        <tr>
            <th>글 번호</th>
            <th>글쓴이</th>
            <th>글 제목</th>
        </tr>
        <tr th:each="i: ${#numbers.sequence(1, 10)}">
            <td th:text="${i}"></td>
            <td th:text="글쓴이 + ${i}"></td>
            <td th:text="제목 + ${i}"></td>
        </tr>
    </table>


    <nav style="text-align: center;">
        <ul class="pagination">
            <li class="disabled">
                <a href="#" aria-label="First">
                    <span aria-hidden="true">First</span>
                </a>
            </li>
            <li class="disabled">
                <a href="#" aria-label="Previous">
                    <span aria-hidden="true">&lt;</span>
                </a>
            </li>
            <li class="active"><a href="#">1</a></li>
            <li><a href="#">2</a></li>
            <li><a href="#">3</a></li>
            <li><a href="#">4</a></li>
            <li><a href="#">5</a></li>
            <li>
                <a href="#" aria-label="Next">
                    <span aria-hidden="true">&gt;</span>
                </a>
            </li>
            <li>
                <a href="#" aria-label="Last">
                    <span aria-hidden="true">Last</span>
                </a>
            </li>
        </ul>
    </nav>

</body>
</html>

 

th 속성은 Thymeleaf에서 제공하는 기능으로 JSP로 치면 <c:forEach>와 같음.

 

아래 코드를 통해 table에 일일이 tr+td태그를 넣지 않아도 화면에 띄울 수 있음.

 

<tr th:each="i: ${#numbers.sequence(1, 10)}">
	<td th:text="${i}"></td>
	<td th:text="글쓴이 + ${i}"></td>
	<td th:text="제목 + ${i}"></td>
</tr>

 

 

03) NoticeController 수정

 

Pageable은 Spring에서 페이징 기능을 위한 파라미터들을 추상화 시킨 인터페이스임. 이 Pageable을 아래와 같이 Controller의 RequestMapping 메서드 인자로 넣을 수 있음.

 

@GetMapping(value = "/list.do")
public String paging(@PageableDefault Pageable pageRequest) {

	return "noticeList";
}

 

Pageable을 디버그 모드로 확인하면 pageable 객체에 page, size, sort 프로퍼티가 있는 것을 확인 가능.

 

04) Repository 생성

 

- NoticeRepository 생성함.

 

@Repository
public interface NoticeRepository extends JpaRepository<Notice, Long> {

}

 

05) Service 생성

 

- NoticeService

 

@Service
public class NoticeService {
	
	private NoticeRepository noticeRepository;
	
	public NoticeService(NoticeRepository noticeRepository) {
		this.noticeRepository = noticeRepository;
	}
	
	public Page<Notice> getNoticeList(Pageable pageable) {
		
		int page = (pageable.getPageNumber() == 0) ? 0 : (pageable.getPageNumber() - 1); // page는 index 처럼 0부터 시작
        pageable = PageRequest.of(page, 10, Sort.Direction.ASC, "seq"); // Sort 추가

        return noticeRepository.findAll(pageable);
	}

 

noticeRepository.findAll 메서드의 파라미터로 Pageable 객체를 사용할 수 있음.

Page 객체를 리턴하면 Pageable과 마찬가지로 페이징 기능을 위해 추상화 시킨 인터페이스임.

 

int page = (pageable.getPageNumber() == 0) ? 0 : (pageable.getPageNumber() - 1);

 

위 부분에서는 Pageable의 page는 index 처럼 0부터 시작임.

하지만 주로 게시판에서는 1부터 시작하기 때문에 사용자가 보려는 페이지에서 -1처리를 해준 것임.

 

pageable = PageRequest.of(page, 10);

 

Pageable 인터페이스를 확인해보면 알겠지만 getter는 있지만 setter는 없음.

그래서 PageRequest.of 메서드를 사용하여 새로운 pageable 객체를 생성함.

 

return noticeRepository.findAll(pageable);

 

사용하는 SQL의 문법에 맞게 페이징 쿼리를 만들어서 DB에 데이터를 가져옴.

 

06) noticeList.html 수정

 

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="board/layout/basic">
<head>
<meta charset="UTF-8">
<title>일더하기 Work+</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
<script src="/webjars/bootstrap/3.3.4/dist/js/bootstrap.min.js"></script>
</head>
<body>

<h1>공지사항</h1>

<table class="table table-striped">
	<thead>
	    <tr>
	      <th scope="col">번호</th>
	      <th scope="col">제목</th>
	      <th scope="col">조회수</th>
	      <th scope="col">작성자</th>
	      <th scope="col">작성일자</th>
	    </tr>
	  </thead>
	  <tbody>
	    <tr th:each="notice:${noticeDtoList}">
			<td>
				<span th:text="${notice.seq}"></span>
			</td>
			<td>
				<a th:href="|@{/workplus/notice/view.do}?noticeSeq=${notice.seq}|" th:text="${notice.title}"></a>
			</td>
			<td>
				<span th:text="${notice.viewCnt}"></span>
			</td>
			<td>
				<span th:text="${notice.writer}"></span>
			</td>
			<td>
				<span th:text="${notice.createdDate}"></span>
			</td>
		</tr>
 	</tbody>
</table>
 
 <nav>
	<div th:with="start=${T(Math).floor(noticeDtoList.number/10)*10 + 1},
                    last=(${start + 9 < noticeDtoList.totalPages ? start + 9 : noticeDtoList.totalPages})">
		    <a th:href="@{list1.do(page=1)}" aria-label="First">
		        <span aria-hidden="true">First</span>
		    </a>
		    <a th:class="${noticeDtoList.first} ? 'disabled'" th:href="${noticeDtoList.first} ? '#' :@{list1.do(page=${noticeDtoList.number})}" aria-label="Previous">
		        <span aria-hidden="true">&lt;</span>
		    </a>
		    <a th:each="page: ${#numbers.sequence(start, last)}" th:class="${page == noticeDtoList.number + 1} ? 'active'" th:text="${page}" th:href="@{list1.do(page=${page})}"></a>
		    <a th:class="${noticeDtoList.last} ? 'disabled'" th:href="${noticeDtoList.last} ? '#' : @{list1.do(page=${noticeDtoList.number + 2})}" aria-label="Next">
		        <span aria-hidden="true">&gt;</span>
		    </a>
		    <a th:href="@{list1.do(page=${noticeDtoList.totalPages})}" aria-label="Last">
		        <span aria-hidden="true">Last</span>
		    </a>
	</div>
</nav>

<div>
	<button onclick="location.href='/workplus/notice/write.do'">글작성</button>
</div>

</body>

</html>

 

변수 설정

 

<ul class="pagination"
        th:with="start=${T(Math).floor(noticeList.number/10)*10 + 1},
                    last=(${start + 9 < noticeList.totalPages ? start + 9 : noticeList.totalPages})">

 

th:with는 해당 태그를 scope로 갖는 변수를 선언할 수 있게 해주는 속성임.

start=${T(Math).floor(noticeList.number/10)*10 + 1} : 현재 페이지를 통해 현재 페이지 그룹의 시작 페이지를 구하는 로직이다.

last=(${start + 9 < noticeList.totalPages ? start + 9 : noticeList.totalPages}) : 전체 페이지 수와 현재 페이지 그룹의 시작 페이지를 통해 현재 페이지 그룹의 마지막 페이지를 구하는 로직이다.

 

 <a th:href="@{list1.do(page=1)}" aria-label="First">
 	<span aria-hidden="true">First</span>
 </a>

 

위는 첫 페이지로 가는 버튼이며, href 속성을 위와 같이 th:href로 줄 수 있다.

href를 사용하지 않고 th:href를 사용한 이유는 c:url과 같이 컨텍스트명이 추가되더라도 리소스 path에 컨텍스트명을 붙여 리소스를 가져오기 때문에 컨텍스트명 유무에 상관없이 항상 정상적으로 리소스를 가져올 수 있게 해주는 속성이다.

그리고 파라미터는 (key=value) 형식으로 표기한다.

 

<a th:class="${noticeDtoList.first} ? 'disabled'" th:href="${noticeDtoList.first} ? '#' :@{list1.do(page=${noticeDtoList.number})}" aria-label="Previous">
	<span aria-hidden="true">&lt;</span>
</a>

 

위는 이전 페이지로 가기 버튼이며 th:class는 위에 보이는 것처럼 조건을 통해 class를 지정할 수 있다.

위에서는 현재 페이지가 첫번째 페이지면 disabled를 걸어놓는 코드이다.

th:href 역시 조건을 통해 href를 지정할 수 있는데 위에서는 삼항연산자를 사용함.

첫 페이지라면 href에 #을 지정하고 아니라면 현재 페이지의 page number를 지정함.

현재 페이지의 page number를 지정하는 이유는 page number는 index 처럼 0에서 시작하기 때문에 현재 페이지 -1이 자동 적용됨.

 

<a th:each="page: ${#numbers.sequence(start, last)}" th:class="${page == noticeDtoList.number + 1} ? 'active'" th:text="${page}" th:href="@{list1.do(page=${page})}"></a>

 

위는 현재 페이지 그룹의 페이지 나열이며 th:each를 사용해서 현재 페이지 그룹의 페이지를 나열한다.

이때 th:class 속성에서 현재페이지일 경우 'active' class를 추가하는 로직이 있다.

 

<a th:class="${noticeDtoList.last} ? 'disabled'" th:href="${noticeDtoList.last} ? '#' : @{list1.do(page=${noticeDtoList.number + 2})}" aria-label="Next">
	<span aria-hidden="true">&gt;</span>
</a>

 

위는 다음 페이지로 가기 버튼으로 이전 페이지로 가기 버튼을 만들때와 비슷하다.

 

<a th:href="@{list1.do(page=${noticeDtoList.totalPages})}" aria-label="Last">
	<span aria-hidden="true">Last</span>
</a>

 

위는 마지막 페이지로 가기 버튼이다.

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함