목차 | |
1. | paging |
1. paging
◈ BoardController.java
package com.ezen.spring.controller;
import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.domain.PagingVO;
import com.ezen.spring.handler.PagingHandler;
import com.ezen.spring.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@RequestMapping("/board/*")
@RequiredArgsConstructor
@Slf4j
@Controller
public class BoardController {
private final BoardService bsv;
@GetMapping("/register")
public String register(){
return"/board/register";
}
@PostMapping("/register")
public String register(BoardVO boardVO){
log.info(">>>> boardVO >>>> {}", boardVO);
int isOk = bsv.register(boardVO);
return "index";
}
@GetMapping("/list")
public String list(Model m, PagingVO pgvo){
// 전체 게시글 수 가져오기
int totalCount = bsv.getTotalCount(pgvo);
PagingHandler ph = new PagingHandler(pgvo, totalCount);
m.addAttribute("list", bsv.getList(pgvo));
m.addAttribute("ph", ph);
return"/board/list";
}
@GetMapping("/detail")
public String detail(@RequestParam("bno") long bno, Model m){
BoardVO bvo = bsv.getDetail(bno);
m.addAttribute("bvo", bvo);
return "/board/detail";
}
@PostMapping("/modify")
public String modify(BoardVO bvo, RedirectAttributes redirectAttributes){
int isOk = bsv.update(bvo);
redirectAttributes.addAttribute("bno", bvo.getBno());
return "redirect:/board/detail";
}
@GetMapping("/delete")
public String delete(@RequestParam("bno") long bno){
int isOk = bsv.delete(bno);
return "redirect:/board/list";
}
}
◈ BoardService.java
package com.ezen.spring.service;
import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.domain.PagingVO;
import java.util.List;
public interface BoardService {
int register(BoardVO boardVO);
List<BoardVO> getList(PagingVO pgvo);
BoardVO getDetail(long bno);
int update(BoardVO bvo);
int delete(long bno);
int getTotalCount(PagingVO pgvo);
}
◈ BoardServiceImpl.java
package com.ezen.spring.service;
import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.domain.PagingVO;
import com.ezen.spring.repository.BoardMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j
@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService{
private final BoardMapper boardMapper;
@Override
public int register(BoardVO boardVO) {
return boardMapper.register(boardVO);
}
@Override
public List<BoardVO> getList(PagingVO pgvo) {
return boardMapper.getList(pgvo);
}
@Override
public BoardVO getDetail(long bno) {
return boardMapper.getDetail(bno);
}
@Override
public int update(BoardVO bvo) {
return boardMapper.update(bvo);
}
@Override
public int delete(long bno) {
return boardMapper.delete(bno);
}
@Override
public int getTotalCount(PagingVO pgvo) {
return boardMapper.getTotalCount(pgvo);
}
}
◈ BoardMapper.java
package com.ezen.spring.repository;
import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.domain.PagingVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface BoardMapper {
int register(BoardVO boardVO);
List<BoardVO> getList(PagingVO pgvo);
BoardVO getDetail(long bno);
int update(BoardVO bvo);
int delete(long bno);
int getTotalCount(PagingVO pgvo);
}
◈ boardMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ezen.spring.repository.BoardMapper">
<insert id="register">
insert into board(title, writer, content)
values(#{title}, #{writer}, #{content})
</insert>
<select id="getList" resultType="com.ezen.spring.domain.BoardVO">
select * from board
<include refid="search"></include>
order by bno desc
limit #{startIndex}, #{qty}
</select>
<select id="getDetail" resultType="com.ezen.spring.domain.BoardVO">
select * from board where bno = #{bno}
</select>
<update id="update">
update board set title = #{title}, content = #{content}, reg_date = now()
where bno = #{bno}
</update>
<delete id="delete">
delete from board where bno = #{bno}
</delete>
<select id="getTotalCount" resultType="int">
select count(bno) from board
<include refid="search"></include>
</select>
<sql id="search">
<if test="type != null">
<trim prefix="where (" suffix=")" suffixOverrides="or">
<foreach collection="typeToArray" item="type">
<trim suffix="or">
<choose>
<when test="type=='t'.toString()">
title like concat('%', #{keyword}, '%')
</when>
<when test="type=='w'.toString()">
title like concat('%', #{keyword}, '%')
</when>
<when test="type=='c'.toString()">
title like concat('%', #{keyword}, '%')
</when>
</choose>
</trim>
</foreach>
</trim>
</if>
</sql>
</mapper>
◈ PagingVO.java
package com.ezen.spring.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@AllArgsConstructor
public class PagingVO {
private int pageNo; // 현재 페이지 번호
private int qty; // 한 페이지에 출력되는 게시글 개수
private String type;
private String keyword;
public PagingVO(){
pageNo = 1;
qty = 10;
}
public PagingVO(int pageNo, int qty){
this.pageNo = pageNo;
this.qty = qty;
}
public int getStartIndex(){
return(this.pageNo - 1) * this.qty;
}
public String[] getTypeToArray(){
return this.type == null ? new String[]{} : this.type.split("");
}
}
◈ PagingHandler.java
package com.ezen.spring.handler;
import com.ezen.spring.domain.PagingVO;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class PagingHandler {
private int startPage;
private int endPage;
private int realEndPage;
private boolean prev, next;
private int totalCount;
private PagingVO pgvo;
public PagingHandler(PagingVO pgvo, int totalCount){
this.pgvo = pgvo;
this.totalCount = totalCount;
// 1~10 => 10 / 11~20 => 20 / 21~30 => 30
// (현재 페이지번호 / 10) 올림 => * 10
this.endPage = (int)Math.ceil(pgvo.getPageNo() / 10.0)*10;
this.startPage = endPage - 9;
this.realEndPage = (int)Math.ceil(totalCount / (double)pgvo.getQty());
if(realEndPage < endPage){
this.endPage = realEndPage;
}
this.prev = this.startPage > 1; // 1 11 21
this.next = this.endPage < realEndPage;
}
}
◈ list.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<div layout:fragment="content" class="container-md">
<h1>Boot List page</h1>
<hr>
<!-- search line -->
<div class="container-fluid">
<form action="/board/list" method="get" class="d-flex" role="search">
<select class="form-select" name="type" id="inputGroupSelect01">
<option th:selected="${ph.pgvo.type == null ? 'true' : 'false' }">Choose...</option>
<option th:value="t" th:selected="${ph.pgvo.type == 't' ? 'true' : 'false' }">title</option>
<option th:value="w" th:selected="${ph.pgvo.type == 'w' ? 'true' : 'false' }">writer</option>
<option th:value="c" th:selected="${ph.pgvo.type == 'c' ? 'true' : 'false' }">content</option>
<option th:value="tw" th:selected="${ph.pgvo.type == 'tw' ? 'true' : 'false' }">title + writer</option>
<option th:value="wc" th:selected="${ph.pgvo.type == 'wc' ? 'true' : 'false' }">writer + content</option>
<option th:value="tc" th:selected="${ph.pgvo.type == 'tc' ? 'true' : 'false' }">content + title</option>
<option th:value="twc" th:selected="${ph.pgvo.type == 'twc' ? 'true' : 'false' }">all</option>
</select>
<input class="form-control me-2" name="keyword" type="text" placeholder="Search" th:value="${ph.pgvo.keyword}" aria-label="Search">
<input type="hidden" name="pageNo" th:value="1">
<input type="hidden" name="qty" th:value="${ph.pgvo.qty }">
<button type="submit" class="btn btn-primary position-relative">
search
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
[[${ph.totalCount}]]
</span>
</button>
</form>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>title</th>
<th>writer</th>
<th>regDate</th>
</tr>
</thead>
<tbody>
<tr th:each="bvo:${list}">
<td>[[${bvo.bno}]]</td>
<td><a th:href="@{/board/detail(bno=${bvo.bno})}">[[${bvo.title}]]</a></td>
<td>[[${bvo.writer}]]</td>
<td>[[${bvo.regDate}]]</td>
</tr>
</tbody>
</table>
[[${ph}]]
<!-- ${ } => jsp EL 방식 => 타임리프 th: 사용-->
<!-- th:classappend : 동적 클래스 추가 -->
<!-- 페이지네이션 라인 -->
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="${ph.prev eq false ? 'disabled' : ''}">
<a class="page-link" th:href="@{/board/list(pageNo=${ph.startPage-1}, qty=10, type=${ph.pgvo.type}, keyword=${ph.pgvo.keyword})}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<!-- ${#numbers.sequence(from,to)} -->
<th:block th:each="i:${#numbers.sequence(ph.startPage, ph.endPage)}">
<li class="page-item" th:classappend="${ph.pgvo.pageNo eq i ? 'active' : ''}">
<a class="page-link" th:href="@{/board/list(pageNo=${i}, qty=10, type=${ph.pgvo.type}, keyword=${ph.pgvo.keyword})}">[[${i}]]</a>
</li>
</th:block>
<li class="page-item" th:classappend="${ph.next eq false ? 'disabled' : ''}">
<a class="page-link" th:href="@{/board/list(pageNo=${ph.endPage+1}, qty=10, type=${ph.pgvo.type}, keyword=${ph.pgvo.keyword})}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
◈ detail.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<div layout:fragment="content" class="container-md">
<h1>Boot Detail page</h1>
<hr>
<form action="/board/modify" method="post" id="modForm">
<div class="mb-3">
<label for="r" class="form-label">Created At</label>
<input type="text" class="form-control" name="regDate" id="r" th:value="${bvo.regDate}" readonly>
</div><div class="mb-3">
<input type="hidden" class="form-control" name="bno" id="n" th:value="${bvo.bno}">
</div>
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" name="title" id="title" th:value="${bvo.title}" readonly>
</div>
<div class="mb-3">
<label for="writer" class="form-label">Writer</label>
<input type="text" class="form-control" name="writer" id="writer" th:value="${bvo.writer}" readonly>
</div>
<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control" name="content" id="content" rows="3" readonly>[[${bvo.content}]]</textarea>
</div>
<button type="button" id="listBtn" class="btn btn-primary">List</button>
<!-- de-tail page에서 modify 상태로 변경하기 위한 버튼 -->
<button type="button" id="modBtn" class="btn btn-warning">Modify</button>
<a th:href="@{/board/delete(bno=${bvo.bno})}">
<button type="button" id="delBtn" class="btn btn-danger">Delete</button>
</a>
</form>
<script th:src="@{/js/boardDetail.js}"></script>
</div>
◈ boardDetail.js
console.log("boardDetail.js in!!");
document.getElementById('listBtn').addEventListener('click', () => {
// 리스트로 이동
location.href="/board/list";
});
document.getElementById('modBtn').addEventListener('click', ()=>{
// title, content의 readonly를 해지 readOnly = true / false
document.getElementById('title').readOnly = false;
document.getElementById('content').readOnly = false;
// modBtn delBtn 삭제
document.getElementById('modBtn').remove();
document.getElementById('delBtn').remove();
// modBtn => submit
// <button></button>
let modBtn = document.createElement('button');
// <button type="submit"></button>
modBtn.setAttribute('type','submit');
// class="btn btn-warning"
modBtn.classList.add('btn','btn-outline-warning');
// <button type="submit" class="btn btn-outline-warning">submit</button>
modBtn.innerText="submit";
// form 태그의 자식 요소로 추가 - form에 가장 마지막에 추가
document.getElementById('modForm').appendChild(modBtn);
});
▷ 출력
'Java > Spring Boot' 카테고리의 다른 글
Spring Boot 기초(security) - AWS 풀스택 과정 81일차 (0) | 2024.11.20 |
---|---|
Spring Boot 기초(comment) - AWS 풀스택 과정 80일차 (0) | 2024.11.19 |
Spring Boot 기초(file) - AWS 풀스택 과정 79일차 (0) | 2024.11.18 |
Spring Boot 기초(DB) - AWS 풀스택 과정 77일차 (0) | 2024.11.14 |
Spring Boot 설정 및 기초 - AWS 풀스택 과정 76일차 (0) | 2024.11.13 |