22.10.28 - Clone_Project -1 (Cookie, 페이징 처리, 글 상세 → 이전 글 목록 페이지)
IntelliJ 단축키
함수 안에 for문이나 함수로 뺄 수 있는거 Ctrl+ Alt + m
Clone
https://github.com/soongu/spring_webprj4.git
Cookie
Service
package com.project.web_prj.board.service;
import com.project.web_prj.board.domain.Board;
import com.project.web_prj.board.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.util.WebUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@Service
@Log4j2
@RequiredArgsConstructor
public class BoardService {
private final BoardRepository repository;
// 게시물 등록 요청 중간 처리
public boolean saveService(Board board) {
log.info("save service start - {}", board);
return repository.save(board);
}
// 게시물 전체 조회 요청 중간 처리
public List<Board> findAllService() {
log.info("findAll service start");
List<Board> boardList = repository.findAll();
// 목록 중간 데이터처리
processConverting(boardList);
return boardList;
}
private void processConverting(List<Board> boardList) {
for (Board b : boardList) {
convertDateFormat(b);
substringTitle(b);
}
}
private void convertDateFormat(Board b) {
Date date = b.getRegDate();
SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd a hh:mm");
b.setPrettierDate(sdf.format(date));
}
private void substringTitle(Board b) {
// 만약에 글제목이 5글자 이상이라면
// 5글자만 보여주고 나머지는 ...처리
String title = b.getTitle();
if (title.length() > 5) {
String subStr = title.substring(0, 5);
b.setShortTitle(subStr + "...");
} else {
b.setShortTitle(title);
}
}
// 게시물 상세 조회 요청 중간 처리
@Transactional
public Board findOneService(Long boardNo, HttpServletResponse response, HttpServletRequest request) {
log.info("findOne service start - {}", boardNo);
Board board = repository.findOne(boardNo);
// 해당 게시물 번호에 해당하는 쿠키가 있는지 확인
// 쿠키가 없으면 조회수를 상승시켜주고 쿠키를 만들어서 클라이언트에 전송
makeViewCount(boardNo, response, request);
return board;
}
private void makeViewCount(Long boardNo, HttpServletResponse response, HttpServletRequest request) {
// 쿠키를 조회 - 해당 이름의 쿠키가 있으면 쿠키가 들어오고 없으면 null이 들어옴
Cookie foundCookie = WebUtils.getCookie(request, "b" + boardNo);
if (foundCookie == null) {
repository.upViewCount(boardNo);
Cookie cookie = new Cookie("b" + boardNo, String.valueOf(boardNo));// 쿠키 생성
cookie.setMaxAge(60); // 쿠키 수명 설정
cookie.setPath("/board/content"); // 쿠키 작동 범위
response.addCookie(cookie); // 클라이언트에 쿠키 전송
}
}
// 게시물 삭제 요청 중간 처리
public boolean removeService(Long boardNo) {
log.info("remove service start - {}", boardNo);
return repository.remove(boardNo);
}
// 게시물 수정 요청 중간 처리
public boolean modifyService(Board board) {
log.info("modify service start - {}", board);
return repository.modify(board);
}
}
페이징처리
SQL
--MySQL, MariaDB <select id="findAll" resultMap="boardMap"> SELECT * FROM tbl_board ORDER BY board_no DESC LIMIT #{start},#{amount}; </select> -- 10페이지씩 0부터 10까지 --#{start} => getStart 메소드를 호출한다.Page.java
package com.project.web_prj.common.paging; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.ToString; @ToString @Getter @AllArgsConstructor // 페이지 정보 클래스 public class Page { private int pageNum; // 페이지 번호 private int amount; // 한 페이지당 배치할 게시물 수 public Page() { /* 아래의 코드를 설정을 안하면 500에러 즉 sql문법 오류가 뜸=> 값이 나오지 않음 */ this.pageNum = 1; this.amount = 10; } public int getStart(){ return (pageNum -1) * amount; } // URL 주소창에 http://localhost:8183/board/list?pageNum=2으로 입력하면 setter가 작동 //왜 굳이 lombok으로 설정을 안했는가? if문을 처리하기 위해 public void setPageNum(int pageNum) { if (pageNum <= 0 || pageNum > Integer.MAX_VALUE) { this.pageNum = 1; return; } this.pageNum = pageNum; } public void setAmount(int amount) { if (amount < 10 || amount > 100) { this.amount = 10; return; } this.amount = amount; } }PageMaker.java
package com.project.web_prj.common.paging; import lombok.Getter; import lombok.Setter; import lombok.ToString; // 페이지 렌더링 정보 생성 @Setter @Getter @ToString public class PageMaker { // 한번에 그려낼 페이지 수 private static final int PAGE_COUNT = 10; // 렌더링시 페이지 시작값, 페이지 끝값 private int beginPage, endPage; // 이전, 다음 버튼 활성화 여부 private boolean prev, next; private Page page; // 현재 위치한 페이지 정보 private int totalCount; // 총 게시물 수 public PageMaker(Page page, int totalCount) { this.page = page; this.totalCount = totalCount; makePageInfo(); } // 페이지 정보 생성 알고리즘 private void makePageInfo() { //1. endPage 계산 // 올림처리 (현재 위치한 페이지번호 / 한 화면에 배치할 페이지수 ) * 한 화면에 배치할 페이지 수 this.endPage = (int) (Math.ceil(page.getPageNum() / (double)PAGE_COUNT) * PAGE_COUNT); //2. beginPage 계산 this.beginPage = endPage - PAGE_COUNT + 1; /* - 총 게시물수가 237개고, 한 화면당 10개의 게시물을 배치하고 있다면 페이지 구간은 1 ~ 10페이지 구간 : 게시물 100개 11 ~ 20페이지 구간: 게시물 100개 21 ~ 24페이지 구간: 게시물 37개 - 마지막 페이지 구간에서는 보정이 필요함. - 마지막 구간 끝페이지 보정 공식: 올림처리(총 게시물 수 / 한 페이지당 배치할 게시물 수) */ int realEnd = (int)Math.ceil((double) totalCount / page.getAmount()); // 그러면 끝 페이지보정은 언제 일어나야 하는가 // 마지막 페이지 구간에서 일어날수도 있고 아닐수도 있다 if (realEnd < endPage) { this.endPage = realEnd; } // 이전 버튼 활성화 여부 this.prev = beginPage > 1; // 다음 버튼 활성화 여부 this.next = endPage < realEnd; } }Service.java
// 게시물 전체 조회 요청 중간 처리 with paging public Map<String, Object> findAllService(Page page) { log.info("findAll service start"); Map<String, Object> findDataMap = new HashMap<>(); List<Board> boardList = repository.findAll(page); // 목록 중간 데이터 처리 processConverting(boardList); findDataMap.put("bList", boardList); findDataMap.put("tc", repository.getTotalCount()); return findDataMap; }Controller.java
// 게시물 목록 요청 @GetMapping("/list") public String list(Page page, Model model) { log.info("controller request /board/list GET! - page: {}", page); Map<String, Object> boardMap = boardService.findAllService(page); log.debug("return data - {}", boardMap); // 페이지 정보 생성 PageMaker pm = new PageMaker(page, (Integer) boardMap.get("tc")); model.addAttribute("bList", boardMap.get("bList")); model.addAttribute("pm", pm); return "board/board-list"; }board-list.jsp
<!-- 페이지 버튼 영역 --> <nav aria-label="Page navigation example"> <ul class="pagination pagination-lg pagination-custom"> <c:if test="${pm.prev}"> <li class="page-item"><a class="page-link" href="/board/list?pageNum=${pm.beginPage - 1}&amount=${pm.page.amount}">prev</a></li> </c:if> <!--for(int n=1; n<=10; n++)--> <c:forEach var="n" begin="${pm.beginPage}" end="${pm.endPage}" step="1"> <li data-page-num="${n}" class="page-item"> <a class="page-link" href="/board/list?pageNum=${n}&amount=${pm.page.amount}">${n}</a> </li> </c:forEach> <c:if test="${pm.next}"> <li class="page-item"><a class="page-link" href="/board/list?pageNum=${pm.endPage + 1}&amount=${pm.page.amount}">next</a></li> </c:if> </ul> </nav>
글 상세에서 이전 목록으로
Controller
// 게시물 상세 조회 요청 @GetMapping("/content/{boardNo}") public String content(@PathVariable Long boardNo , Model model, HttpServletResponse response, HttpServletRequest request , @ModelAttribute("p") Page page //받은걸 바로 p에 담는다 ) { log.info("controller request /board/content GET! - {}", boardNo); Board board = boardService.findOneService(boardNo, response, request); log.info("return data - {}", board); model.addAttribute("b", board); return "board/board-detail"; }detail.jsp
//목록버튼 $listBtn.onclick = e => { location.href = '/board/list?pageNum=${p.pageNum}&amount=${p.amount}'; };
'Program > Spring' 카테고리의 다른 글
| Clone_Project - 3(파일 업로드, 비동기 파일 업로드) (0) | 2022.12.30 |
|---|---|
| Clone_Project - 2(게시물 목록 요청 ,동적 SQL, 댓글) (0) | 2022.12.30 |
| VO/DTO, @ToString, 생성자/빌더, 단위 테스트, 생성자주입VS필드 주입, Controller VS Service, Log4j2, JSP 분리 (0) | 2022.12.30 |
| 객체 지향 설계 원칙(SOLID 원칙) ,OCP, MVC (0) | 2022.12.27 |
| Spring Boot(SPA, REACT) (0) | 2022.12.27 |