Java/Spring

Spring 기초(paging, search) - AWS 풀스택 과정 67일차

awspspgh 2024. 10. 30. 18:06
목차
1. modify, delete
2. paging, search

 

1. modify, delete

▣ modify.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c "%>
<jsp:include page="../layout/header.jsp"/>
<body>
<div class="container-md">
	<h1>Board Modify Page...</h1>
	<form action="/board/update" method="post">
	<div class="mb-3">
	  <label for="n" class="form-label">no.</label>
	  <input type="hidden" name="bno" value="${bvo.bno }">
	  <input type="text" class="form-control" id="n" value="${bvo.bno}" readonly>
	</div>
	<div class="mb-3">
	  <label for="t" class="form-label">title</label>
	  <input type="text" class="form-control" id="t" name="title" value="${bvo.title}">
	</div>
	<div class="mb-3">
	  <label for="w" class="form-label">writer</label>
	  <input type="text" class="form-control" id="w" value="${bvo.writer}" readonly>
	  <span class="bodge text-bg-Info">${bvo.regDate}</span>
	</div>
	<div class="mb-3">
	  <label for="c" class="form-label">content</label>
	  <textarea class="form-control" id="c" rows="3" name="content">${bvo.content}</textarea>
	</div>
	
		<button type="submit" class="btn btn-success">modify</button>
		<a href="/board/list"><button type="button" class="btn btn-danger">list</button></a>
	</form>
	</div>
</body>
<jsp:include page="../layout/footer.jsp"/>

 

▣ detail.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c "%>
<jsp:include page="../layout/header.jsp"/>
<body>
<div class="container-md">
	<h1>Board Detail Page...</h1>
	<div class="mb-3">
		  <label for="t" class="form-label">title</label>
		  <input type="text" class="form-control" id="t" value="${bvo.title}" readonly>
		</div>
		<div class="mb-3">
		  <label for="w" class="form-label">writer</label>
		  <input type="text" class="form-control" id="w" value="${bvo.writer}" readonly>
		  <span class="bodge text-bg-Info">${bvo.regDate}</span>
		</div>
		<div class="mb-3">
		  <label for="c" class="form-label">content</label>
		  <textarea class="form-control" id="c" rows="3" readonly>${bvo.content}</textarea>
		</div>
		
		<a href="/board/modify?bno=${bvo.bno }"><button type="button" class="btn btn-success">modify</button></a>
		<a href="/board/delete?bno=${bvo.bno }"><button type="button" class="btn btn-danger">delete</button></a>
	</div>
</body>
<jsp:include page="../layout/footer.jsp"/>

 

▷ 출력

modify & delete

 

2. paging, search

[day03]
detail :
list 페이지에서 bno 하나를 전달하여 해당 객체(DB에서)를 detail 페이지로 전송

list => pagination
한 페이지에 목록을 다 나태내기 힘들 때 사용
- 한 페이지에 10개의 게시물 표현
- 전체 게시글의 수에 계산되어 페이지네이션 값들이 결정이 됨

DB 상에서 => limit 시작번지, 개수
1 => limit 0, 10 => 0번지부터 10개 => 0번지부터 9번지까지
2 => limit 10, 10 => 10번지부터 10개 => 10번지부터 19번지까지
3 => limit 20, 10 => 20번지부터 10개 => 20번지부터 29번지까지
...
pageNo 페이지네이션 번호
getPageStart =>시작번지
qty => 개수

----------------------
PagingHandler
한 페이지에 나오는 페이지네이션 개수 : 10개 => qty
한 페이지에 나오는 페이지네이션 시작번호 : 1 11 21 ... 181
한 페이지에 나오는 페이지네이션 끝번호: 10 20 30 ... 181
이전, 다음의 노출여부 boolean
전체 페이지 수 : DB에서 조회해 와야하는 값 (매개변수로 받아오기)
현재 페이지 번호 : pagingVO pageNo 사용 (매개변수로 받아오기)
진짜 마지막 페이지 끝 번호
----------------------
select * from board where is_del = 'N' and #{type} like '%"${keyword}"%' 
order by bno desc limit #{pageStart}, #{qty}

'%"${keyword}"%' => '% "${keyword}" %'
concat('%', "${keyword}", '%')

 

▣ BoardController.java

package com.ezen.spring.controller;

import java.util.List;

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 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;

@Slf4j
@RequiredArgsConstructor
@RequestMapping("/board/*")
@Controller
public class BoardController {
	// 생성자 주입시 객체는 final로 생성
	private final BoardService bsv;
	
	// return void => 온 경로 그대로 리턴 /board/register => /board/register.jsp
	@GetMapping("/register")
	public void register() {}
	
	@PostMapping("/insert")
	public String insert(BoardVO bvo) {
		log.info(">>> insert bvo > {}", bvo);
		int isOk = bsv.insert(bvo);
		log.info(">>> insert > {}", isOk > 0 ? "OK" : "FAIL");
		// /WEB-INF/views/.jsp (x)
		// 컨트롤러의 mapping 위치로 연결할 때 redirect:/board/list
		return "redirect:/";
	}
	
	@GetMapping("/list")
	public String list(Model m, PagingVO pgvo) {
		// request.setAttrbute()
		// Model 객체가 해당 일을 대신해줌.
//		PagingVO pgvo = new PagingVO();
		List<BoardVO> list = bsv.getList(pgvo);
		// totalcount 구해서 PagingHandler에 매개변수로 전달
		int totalCount = bsv.getTotal(pgvo);
		PagingHandler ph = new PagingHandler(totalCount, pgvo);
		log.info(">>> totalCount > {}", totalCount);
		m.addAttribute("list",list);
		m.addAttribute("ph",ph);
		return "/board/list";
	}
	
	@PostMapping("/update")
	public String update(BoardVO bvo) {
		int isOk = bsv.modify(bvo);
		log.info(">>> update > {}", isOk > 0 ? "OK" : "FAIL");
		
		// detail.jsp로 이동 X => controller detail mapping으로 이동 => redirect:/
		return "redirect:/board/detail?bno="+bvo.getBno();
	}
	
	// @ requestParam("bno") int bno => 전달되는 파라미터가 여러 개일 경우 이름을 명시
	// return void : 요청 경로로 응답을 그대로 보냄. /board/detail => /board/detail.jsp
	@GetMapping({"/detail", "/modify"})
	public void detail(int bno, Model m) {
		// bno에 해당하는 BoardVO 객체를 DB에서 가져와서 모델로 전달
		BoardVO bvo = bsv.getDetail(bno);
		m.addAttribute("bvo", bvo);
//		return "/board/detail";
	}
	
	@GetMapping("/delete")
	public String delete(int bno) {
		int isOk = bsv.delete(bno);
		log.info(">>> delete > {}", isOk > 0 ? "OK" : "FAIL");
		return "redirect:/board/list";
	}
	
	
}

 

▣ BoardService.java

package com.ezen.spring.service;

import java.util.List;

import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.domain.PagingVO;

public interface BoardService {

	int insert(BoardVO bvo);

	List<BoardVO> getList(PagingVO pgvo);

	BoardVO getDetail(int bno);

	int modify(BoardVO bvo);

	int delete(int bno);

	int getTotal(PagingVO pgvo);

}

 

▣ BoardServiceImpl.java

package com.ezen.spring.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.ezen.spring.dao.BoardDAO;
import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.domain.PagingVO;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RequiredArgsConstructor
@Slf4j
@Service
public class BoardServiceImpl implements BoardService{
	private final BoardDAO bdao;

	@Override
	public int insert(BoardVO bvo) {
		// TODO Auto-generated method stub
		return bdao.insert(bvo);
	}

	@Override
	public List<BoardVO> getList(PagingVO pgvo) {
		// TODO Auto-generated method stub
		return bdao.getList(pgvo);
	}

	@Override
	public BoardVO getDetail(int bno) {
		// TODO Auto-generated method stub
		return bdao.getDetail(bno);
	}

	@Override
	public int modify(BoardVO bvo) {
		// TODO Auto-generated method stub
		return bdao.update(bvo);
	}

	@Override
	public int delete(int bno) {
		// TODO Auto-generated method stub
		return bdao.delete(bno);
	}

	@Override
	public int getTotal(PagingVO pgvo) {
		// TODO Auto-generated method stub
		return bdao.getTotal(pgvo);
	}
}

 

▣ BoardDAO.java

package com.ezen.spring.dao;

import java.util.List;

import com.ezen.spring.domain.BoardVO;
import com.ezen.spring.domain.PagingVO;

public interface BoardDAO {

	int insert(BoardVO bvo);

	List<BoardVO> getList(PagingVO pgvo);

	BoardVO getDetail(int bno);

	int update(BoardVO bvo);

	int delete(int bno);

	int getTotal(PagingVO pgvo);

}

 

▣ boardMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
  <!-- DAOImpl() 없음. => namespace="다오인터페이스명" id="메서드명" -->
  <mapper namespace="com.ezen.spring.dao.BoardDAO">
  	<insert id="insert">
  		insert into board(title, writer, content)
  		values(#{title}, #{writer}, #{content})
  	</insert>
  	<select id="getList" resultType="com.ezen.spring.domain.BoardVO">
  		select * from board where is_del = 'N' 
  		<include refid="search"></include>
  		order by bno desc limit #{pageStart}, #{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} where bno = #{bno}
  	</update>
  	<update id="delete">
  		update board set is_del = 'Y' where bno = #{bno}
  	</update>
  	<select id="getTotal" resultType="int">
  		select count(bno) from board where is_del = 'N'
  		<include refid="search"></include>
  	</select>
  	
  	<!-- search 동적 쿼리 -->
  	
  	<!-- 
	  	select * from board 
	  	where is_del = 'N' and title like concat('%', keyword, '%') or writer like concat('%', keyword, '%') )
	  	order by bno desc 
	  	limit #{pageStart}, #{qty} 
  	-->
  	
  	<sql id="search">
  		<if test="type!=null">
  			<trim prefix="and(" 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()">
  								writer like concat('%', #{keyword}, '%')
  							</when>
  							<when test="type == 'c'.toString()">
  								content 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; // 한 페이지에 표시되는 페이지 수
	// search 변수 포함
	private String type;
	private String keyword;
	
	// 첫 리스트시 기본적으로 설정될 값
	public PagingVO() {
		this.pageNo = 1;
		this.qty = 10;
	}
	public PagingVO(int pageNo, int qty) {
		this.pageNo = pageNo;
		this.qty = qty;
	}
	
	// DB에서 사용될 시작번지 구하기
	// select * from board limit 번지, 개수
	// 1page limit 0, 10 => 2page limit 10, 10 => 3page limit 20, 10
	public int getPageStart() {
		return (this.pageNo - 1) * this.qty;
	}
	
	// type의 복합 타입을 각각 검색하기 위해 배열로 생성
	// type == twc [t, w, c]
	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 qty; // 한 페이지에 나오는 페이지네이션 개수 : 10개
	private int startPage; // 한 페이지에 나오는 페이지네이션 시작번호 : 1 11 21 ... 181
	private int endPage; // 한 페이지에 나오는 페이지네이션 끝번호: 10 20 30 ... 181
	private boolean prev;
	private boolean next;
	
	private int totalCount; // 전체 페이지 수 : DB에서 조회해 와야하는 값 (매개변수로 받아오기)
	private PagingVO pgvo; // 현재 페이지 번호 : pagingVO pageNo 사용 (매개변수로 받아오기)
	
	private int realEndPage;
	
	// 생성자에서 모든 값들이 계산되어 설정되어야 함.
	public PagingHandler(int totalCount, PagingVO pgvo) {
		this.qty = 10;
		this.pgvo = pgvo;
		this.totalCount = totalCount;
		
		// 1~10 / 11~20 / 21~30 ...
		// pageNo => 1 2 3 .. 10 => 1~10
		// pageNo => 11 12 13 ... 20 => 11~20
		// 1 2 3 ... 10 => endPage는 변함없이 10
		// 11 12 13 ... 20 => 20
		
		// 1 / 10개 => 0.1 결과를 올림 => 1 * 10 => 10
		// 11 / 10 => 1.1 결과를 올림(ceil) => 2 * 10 => 20
		// 정수 / 정수 => 정수 형변환을 해서 소수점을 유지
		this.endPage = (int)Math.ceil(pgvo.getPageNo() / (double)qty) * qty;
		this.startPage = endPage - 9;
		
		// 실제 마지막 페이지
		// 전체 글수 / 한 페이지에 표시되는 게시글 수 (올림)
		// 11 / 10 => 2페이지 1.1 (올림) => 2
		this.realEndPage = (int)Math.ceil(totalCount /(double)pgvo.getQty());
		
		// 이전, 다음 여부
		this.prev = this.startPage > 1; // 1 11 21 31
		this.next = this.endPage < this.realEndPage;
		
		if(endPage > realEndPage) {
			this.endPage = realEndPage;
		}
	
	}

}

 

▣ list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<jsp:include page="../layout/header.jsp"/>
<div class="container-md">
<h1>Board 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 ${ph.pgvo.type == null ? 'selected' : '' }>Choose...</option>
	    <option value="t" ${ph.pgvo.type eq 't' ? 'selected' : '' }>title</option>
	    <option value="w" ${ph.pgvo.type eq 'w' ? 'selected' : '' }>writer</option>
	    <option value="c" ${ph.pgvo.type eq 'c' ? 'selected' : '' }>content</option>
	    <option value="tw" ${ph.pgvo.type eq 'tw' ? 'selected' : '' }>title + writer</option>
	    <option value="wc" ${ph.pgvo.type eq 'wc' ? 'selected' : '' }>writer + content</option>
	    <option value="tc" ${ph.pgvo.type eq 'tc' ? 'selected' : '' }>content + title</option>
	    <option value="twc">all</option>
 	</select>
       <input class="form-control me-2" name="keyword" type="search" placeholder="Search" value="${ph.pgvo.keyword}" aria-label="Search">
       <input type="hidden" name="pageNo" value="1">
       <input type="hidden" name="qty" 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>
     <%-- ${ph } --%>
</div>

<table class="table table-hover">
 <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">title</th>
      <th scope="col">writer</th>
      <th scope="col">regDate</th>
      <th scope="col">readCount</th>
    </tr>
  </thead>
  <tbody>
  <c:forEach items="${list}" var="bvo">
  	<tr>
	  	<td>${bvo.bno }</td>
	  	<td><a href="/board/detail?bno=${bvo.bno }">${bvo.title }</a></td>
	  	<td>${bvo.writer }</td>
	  	<td>${bvo.regDate }</td>
	  	<td>${bvo.readCount }</td>
  	</tr>
  	</c:forEach>
  </tbody>
</table>

<!-- pagination line -->
<!-- << >> : 값이 false disabled -->
<nav aria-label="Page navigation example">
  <ul class="pagination justify-content-center">
    <li class="page-item ${ph.prev eq false ? 'disabled' : ''}">
      <a class="page-link" href="/board/list?pageNo=${ph.startPage-1 }&qty=${ph.pgvo.qty}&type=${ph.pgvo.type}&keyword=${ph.pgvo.keyword}" aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>
    <c:forEach begin="${ph.startPage }" end="${ph.endPage }" var="i">
    <li class="page-item ${ph.pgvo.pageNo eq i ? 'active' : ''}"><a class="page-link" href="/board/list?pageNo=${i }&qty=${ph.pgvo.qty}&type=${ph.pgvo.type}&keyword=${ph.pgvo.keyword}">${i }</a></li>
    </c:forEach>
   <!--  <li class="page-item"><a class="page-link" href="#">2</a></li>
    <li class="page-item"><a class="page-link" href="#">3</a></li> -->
    <li class="page-item ${ph.next eq false ? 'disabled' : ''}" >
      <a class="page-link" href="/board/list?pageNo=${ph.endPage+1}&qty=${ph.pgvo.qty}&type=${ph.pgvo.type}&keyword=${ph.pgvo.keyword}" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  </ul>
</nav>

<a href="/"><button type="submit" class="btn btn-success">list</button></a>

</div>
<jsp:include page="../layout/footer.jsp"/>

 

▷ 출력

Paging & Search

 

참고 사이트 : https://blog.naver.com/momonocha/223640024858