Spring (Todo 조회 기능 개발, TodoService / TodoServiceImpl의 개발, TodoController의 개발, 수정 / 삭제를 위한 링크 처리, list.jsp의 링크 처리, Todo의 삭제 기능 개발, checkbox 포맷 추가, TodoController의 modify(), 페이징 처리를 위한 TodoMapper, 페이지 처리를 위한 DTO, TodoMapper의 count 처리, TodoService / TodeServ
1. Todo 조회 기능 개발
package com.example.spring_project_02.mapper;
import com.example.spring_project_02.domain.TodoVO;
import java.util.List;
public interface TodoMapper {
String getTime();
void insert(TodoVO todoVO);
List<TodoVO> selectAll();
TodoVO selectOne(Long tno);
}
- TodoMapper.java에 selectOne 추가
<?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.example.spring_project_02.mapper.TodoMapper">
<select id="getTime" resultType="string">
select now()
</select>
<insert id="insert">
INSERT INTO tbl_todo (title, dueDate, writer) VALUES (#{title}, #{dueDate}, #{writer})
</insert>
<select id="selectAll" resultType="com.example.spring_project_02.domain.TodoVO">
select * from tbl_todo order by tno desc
</select>
<select id="selectOne" resultType="com.example.spring_project_02.domain.TodoVO">
select * from tbl_todo WHERE tno = #{tno}
</select>
</mapper>
- TodoMapper.xml에 selectOne 추가
@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
public class TodoMapperTest {
@Autowired(required = false)
private TodoMapper todoMapper;
@Test
public void testGetTimer() {
log.info(todoMapper.getTime());
}
@Test
public void testInsert() {
TodoVO todoVO = TodoVO.builder()
.title("스프링 테스트")
.dueDate(LocalDate.of(2022, 10, 10))
.writer("user00")
.build();
todoMapper.insert(todoVO);
}
@Test
public void testSelectAll() {
List<TodoVO> todoVOs = todoMapper.selectAll();
for(TodoVO todoVO : todoVOs) {
log.info(todoVO);
}
}
@Test
public void testSelectOne() {
TodoVO todoVO = todoMapper.selectOne(2L);
log.info(todoVO);
}
}
- TodoMapperTest.java에 testSelectOne 추가
- 테스트 정상 작동 확인
2. TodoService / TodoServiceImpl의 개발
import com.example.spring_project_02.dto.TodoDTO;
import java.util.List;
public interface TodoService {
void register(TodoDTO todoDTO);
List<TodoDTO> getAll();
TodoDTO getOne(Long tno);
}
- TodoService.java에 코드 추가
@Service
@Log4j2
@RequiredArgsConstructor // 생성자 객체 주입. private final로 선언된 참조변수에 객체를 저장하는 생성자 작성.
public class TodoServiceImpl implements TodoService {
private final TodoMapper todoMapper;
private final ModelMapper modelMapper;
@Override
public void register(TodoDTO todoDTO) {
// 1) todoDTO를 전달 받아 2) todoDTO를 todoVO로 변환 후 3) dao의 insert() 호출
log.info(todoDTO);
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
log.info(todoVO);
todoMapper.insert(todoVO);
}
@Override
public List<TodoDTO> getAll() {
List<TodoVO> voList = todoMapper.selectAll(); // dto에서 데이터베이스에서 들고온 VO리스트를 리턴
List<TodoDTO> dtoList = new ArrayList<>();
for (TodoVO todoVO : voList) {
// 개별 VO를 DTO로 변환.
TodoDTO todoDTO = modelMapper.map(todoVO, TodoDTO.class);
dtoList.add(todoDTO); // DTO리스트에 저장.
}
return dtoList;
}
@Override
public TodoDTO getOne(Long tno) {
TodoVO todoVO = todoMapper.selectOne(tno);
TodoDTO todoDTO = modelMapper.map(todoVO, TodoDTO.class);
return todoDTO;
}
}
- TodoServiceImpl에 위 코드 추가
3. TodoController의 개발
@Controller
@RequestMapping("/todo")
@Log4j2
@RequiredArgsConstructor
public class TodoController {
private final TodoService todoService;
@RequestMapping("/list")
public void list(Model model) {
log.info("todo list...");
model.addAttribute("dtoList", todoService.getAll());
}
// @RequestMapping(value = "/register", method = RequestMethod.GET)
@GetMapping("/register")
public void registerGET() {
log.info("GET todo register...");
}
@PostMapping("/register")
public String registerPOST(@Valid TodoDTO todoDTO, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
log.info("POST todo register...");
if (bindingResult.hasErrors()) {
log.info("has error ...");
redirectAttributes.addFlashAttribute("errors", bindingResult.getAllErrors());
return "redirect:/todo/register";
}
log.info(todoDTO);
todoService.register(todoDTO);
return "redirect:/todo/list";
}
@GetMapping("/read")
public void read (Long tno, Model model) {
TodoDTO todoDTO = todoService.getOne(tno);
log.info(todoDTO);
model.addAttribute("dto", todoDTO);
}
}
- TodoController에 코드 추가
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<div class="container-fluid">
<div class="row">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link active" aria-current="page" href="#">Home</a>
<a class="nav-link" href="#">Features</a>
<a class="nav-link" href="#">Pricing</a>
<a class="nav-link disabled">Disabled</a>
</div>
</div>
</div>
</nav>
<div class="card">
<div class="card-header">
Featured
</div>
<div class="card-body">
<div class="input-group mb-3">
<span class="input-group-text">Tno</span>
<input type="text" name="tno" class="form-control" value="${dto.tno}" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Title</span>
<input type="text" name="title" class="form-control" value="${dto.tno}" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">DueDate</span>
<input type="date" name="dueDate" class="form-control" value="${dto.dueDate}" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Writer</span>
<input type="text" name="writer" class="form-control" value="${dto.writer}" readonly>
</div>
<div class="form-check">
<label class="form-check-label">
Finished
</label>
<input class="form-check-input" type="checkbox" name="finished" ${dto.finished ? "checked" : ""} disabled>
</div>
<div class="my-4">
<div class="float-end">
<button type="button" class="btn btn-primary">Modify</button>
<button type="button" class="btn btn-secondary">List</button>
</div>
</div>
</div>
</div>
<div class="row content">
<h1>Content</h1>
<div class="row footer">
<!-- <h1>Footer</h1>-->
<div class="row fixed-bottom" style="z-index: -100">
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
crossorigin="anonymous"></script>
</body>
</html>
- view > todo > read.jsp에 파일 추가 후 위 코드 작성
- 서버 재실행 후 위 URL로 접속해서 화면 확인
4. 수정 / 삭제를 위한 링크 처리
<div class="my-4">
<div class="float-end">
<button type="button" class="btn btn-primary">Modify</button>
<button type="button" class="btn btn-secondary">List</button>
</div>
</div>
<script>
document.querySelector(".btn-primary").addEventListener("click", function () {
self.location = "/todo/modify?tno=" + ${dto.tno};
});
document.querySelector(".btn-secondary").addEventListener("click", function () {
self.location = "/todo/list";
})
</script>
- read.jsp 아래에 script 추가
5. list.jsp의 링크 처리
<tbody>
<c:forEach var="dto" items="${dtoList}">
<tr>
<th scope="row">${dto.tno}</th>
<td><a href="/todo/read?tno=${dto.tno}" class="text-decoration-none"> ${dto.title}</a></td>
<td>${dto.writer}</td>
<td>${dto.dueDate}</td>
<td>${dto.finished}</td>
</tr>
</c:forEach>
</tbody>
- list.jsp에 위 코드 추가
6. Todo의 삭제 기능 개발
@GetMapping({"/read", "/modify"})
public void read (Long tno, Model model) {
// 1) request로 전달 받은 tno를 서비스에 전달해서 2) TodoDTO를 반환받아서 3) View에 전달
TodoDTO todoDTO = todoService.getOne(tno);
log.info(todoDTO);
model.addAttribute("dto", todoDTO);
}
}
- TodoController에 { } 하고 "/modify" 추가
- read.jsp 복사 해서 modify.jsp로 생성
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<div class="container-fluid">
<div class="row">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link active" aria-current="page" href="#">Home</a>
<a class="nav-link" href="#">Features</a>
<a class="nav-link" href="#">Pricing</a>
<a class="nav-link disabled">Disabled</a>
</div>
</div>
</div>
</nav>
<div class="card">
<div class="card-header">
Featured
</div>
<div class="card-body">
<form action="/todo/modify" method="post">
<div class="input-group mb-3">
<span class="input-group-text">Tno</span>
<input type="text" name="tno" class="form-control" value="${dto.tno}" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Title</span>
<input type="text" name="title" class="form-control" value="${dto.title}">
</div>
<div class="input-group mb-3">
<span class="input-group-text">DueDate</span>
<input type="date" name="dueDate" class="form-control" value="${dto.dueDate}">
</div>
<div class="input-group mb-3">
<span class="input-group-text">Writer</span>
<input type="text" name="writer" class="form-control" value="${dto.writer}" readonly>
</div>
<div class="form-check">
<label class="form-check-label">
Finished
</label>
<input class="form-check-input" type="checkbox" name="finished" ${dto.finished ? "checked" : ""}>
</div>
<div class="my-4">
<div class="float-end">
<button type="button" class="btn btn-danger">Remove</button>
<button type="button" class="btn btn-primary">Modify</button>
<button type="button" class="btn btn-secondary">List</button>
</div>
</div>
</form>
<script>
document.querySelector(".btn-primary").addEventListener("click", function () {
self.location = "/todo/modify?tno=" + ${dto.tno};
});
document.querySelector(".btn-secondary").addEventListener("click", function () {
self.location = "/todo/list";
})
</script>
</div>
</div>
<div class="row content">
<h1>Content</h1>
<div class="row footer">
<!-- <h1>Footer</h1>-->
<div class="row fixed-bottom" style="z-index: -100">
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
crossorigin="anonymous"></script>
</body>
</html>
- modify.jsp에 위 코드 작성
- 서버 재 실행 후 URL 기입 시 위 화면 출력 확인
<script>
const frmModify = document.querySelector('form');
document.querySelector('.btn-danger').addEventListener('click', function () {
frmModify.action = '/todo/remove';
frmModify.method = 'post';
frmModify.submit();
})
</script>
- modify.jsp에 script 위 코드로 수정
- Run > edit configuration > Update classes and resources로 둘 다 설정 확인
@PostMapping("/remove")
public String remove(Long tno, RedirectAttributes redirectAttributes) {
log.info("------------remove------------");
log.info("tno: " + tno);
return "redirect:/todo/list";
}
- TodoController에 위 코드 추가
- 서버 재 가동 후 remove 눌렀을 때, list 페이지로 이동되고, log에 remove 출력되는지 확인
package com.example.spring_project_02.mapper;
import com.example.spring_project_02.domain.TodoVO;
import java.util.List;
public interface TodoMapper {
String getTime();
void insert(TodoVO todoVO);
List<TodoVO> selectAll();
TodoVO selectOne(Long tno);
void delete(Long tno);
}
- TodoMapper에 delete 코드 추가
<?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.example.spring_project_02.mapper.TodoMapper">
<select id="getTime" resultType="string">
select now()
</select>
<insert id="insert">
INSERT INTO tbl_todo (title, dueDate, writer) VALUES (#{title}, #{dueDate}, #{writer})
</insert>
<select id="selectAll" resultType="com.example.spring_project_02.domain.TodoVO">
select * from tbl_todo order by tno desc
</select>
<select id="selectOne" resultType="com.example.spring_project_02.domain.TodoVO">
select * from tbl_todo WHERE tno = #{tno}
</select>
<delete id="delete">
DELETE FROM tbl_todo WHERE tno = #{tno}
</delete>
</mapper>
- TodoMapper.xml에 delete 코드 추가
@Test
public void testDelete() {
// 1) tno로 데이터를 변환해서 정상 출력 확인 2) 삭제 3) 다시 tno로 데이터를 변환해서 삭제 확인
Long tno = 2L;
TodoVO todoVO = todoMapper.selectOne(tno);
log.info(todoVO);
todoMapper.delete(tno);
todoVO = todoMapper.selectOne(tno);
log.info(todoVO);
}
}
- TodoMapperTest에 delete 테스트 추가
- 삭제돼서 null 값 확인
public interface TodoService {
void register(TodoDTO todoDTO);
List<TodoDTO> getAll();
TodoDTO getOne(Long tno);
void remove(Long tno);
}
- TodoService에 remove 추가
@Service
@Log4j2
@RequiredArgsConstructor // 생성자 객체 주입. private final로 선언된 참조변수에 객체를 저장하는 생성자 작성.
public class TodoServiceImpl implements TodoService {
private final TodoMapper todoMapper;
private final ModelMapper modelMapper;
@Override
public void register(TodoDTO todoDTO) {
// 1) todoDTO를 전달 받아 2) todoDTO를 todoVO로 변환 후 3) dao의 insert() 호출
log.info(todoDTO);
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
log.info(todoVO);
todoMapper.insert(todoVO);
}
@Override
public List<TodoDTO> getAll() {
List<TodoVO> voList = todoMapper.selectAll(); // dto에서 데이터베이스에서 들고온 VO리스트를 리턴
List<TodoDTO> dtoList = new ArrayList<>();
for (TodoVO todoVO : voList) {
// 개별 VO를 DTO로 변환.
TodoDTO todoDTO = modelMapper.map(todoVO, TodoDTO.class);
dtoList.add(todoDTO); // DTO리스트에 저장.
}
return dtoList;
}
@Override
public TodoDTO getOne(Long tno) {
TodoVO todoVO = todoMapper.selectOne(tno);
TodoDTO todoDTO = modelMapper.map(todoVO, TodoDTO.class);
return todoDTO;
}
@Override
public void remove(Long tno) {
todoMapper.delete(tno);
}
}
- TodoService에 remove 추가
@PostMapping("/remove")
public String remove(Long tno, RedirectAttributes redirectAttributes) {
log.info("remove()....");
log.info("tno: " + tno);
todoService.remove(tno);
return "redirect:/todo/list";
}
- TodoController에 코드 수정
- modify 페이지에서 Remove 클릭 후 제대로 삭제되는 거 확인
public interface TodoMapper {
String getTime();
void insert(TodoVO todoVO);
List<TodoVO> selectAll();
TodoVO selectOne(Long tno);
void delete(Long tno);
void update(TodoVO todoVO);
}
- TodoMapper.java에 update 코드 추가
<?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.example.spring_project_02.mapper.TodoMapper">
<select id="getTime" resultType="string">
select now()
</select>
<insert id="insert">
INSERT INTO tbl_todo (title, dueDate, writer) VALUES (#{title}, #{dueDate}, #{writer})
</insert>
<select id="selectAll" resultType="com.example.spring_project_02.domain.TodoVO">
select * from tbl_todo order by tno desc
</select>
<select id="selectOne" resultType="com.example.spring_project_02.domain.TodoVO">
select * from tbl_todo WHERE tno = #{tno}
</select>
<delete id="delete">
DELETE FROM tbl_todo WHERE tno = #{tno}
</delete>
<update id="update">
UPDATE `tbl_todo` SET `title` = #{title}, `dueDate` = #{dueDate}, `finished` = #{finished}
WHERE `tno` = #{tno}
</update>
</mapper>
- TodoMapper.xml에 update 코드 추가
@Test
public void testUpdate() {
Long tno = 12L;
log.info(todoMapper.selectOne(tno));
TodoVO todoVO = TodoVO.builder()
.tno(tno)
.title("update test")
.dueDate(LocalDate.parse("2030-12-12"))
.finished(true)
.build();
todoMapper.update(todoVO);
log.info(todoMapper.selectOne(tno));
}
- tsetUpdate 코드 추가
- 테스트 실행 후 에러 없는 지 확인
public interface TodoService {
void register(TodoDTO todoDTO);
List<TodoDTO> getAll();
TodoDTO getOne(Long tno);
void remove(Long tno);
void modify(TodoDTO todoDTO);
}
- TodoService에 modify 코드 추가
@Override
public void modify(TodoDTO todoDTO) {
log.info("modify()...");
// vo 객체를 생성하면서 매개변수로 받은 (값이 입력되어 있는) dto와 vo를 맵핑을 해서 vo에 값을 입력.
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
log.info(todoVO);
todoMapper.update(todoVO);
}
- TodoServiceImpl.java에 위 코드 추가
7. checkbox 포맷 추가
- 체크 박스 포매터 파일 추가
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.example.spring_project_02.controller.formatter.LocalDateFormatter"/>
<bean class="com.example.spring_project_02.controller.formatter.CheckboxFormatter"/>
</set>
</property>
</bean>
- servlet-context.xml에 set 안에 bean 한 줄만 더 추가
8. TodoController의 modify()
@PostMapping("/modify")
public String modify(@Valid TodoDTO todoDTO,
BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) { // 유효성 검사 결과 에러가 있으면 수정페이지로 되돌아감
log.info(("has error..."));
redirectAttributes.addFlashAttribute("errors", bindingResult.getAllErrors());
redirectAttributes.addAttribute("tno", todoDTO.getTno()); // tno가 쿼리스트링으로 전달
return "redirect:/todo/modify";
}
log.info(todoDTO);
todoService.modify(todoDTO);
return "redirect:/todo/list";
}
- TodoMapperTest.java에 위 코드 추가
<script>
const serverValidResult = {};
<c:forEach items="${errors}" var="error">
serverValidResult[`${error.getField()}`] = '${error.defaultMessage}';
</c:forEach>
console.log(serverValidResult);
</script>
<script>
const frmView = document.querySelector("form");
document.querySelector('.btn-danger').addEventListener('click', function () {
frmView.action = '/todo/remove';
frmView.method = 'post';
frmView.submit();
})
document.querySelector('.btn-primary').addEventListener('click', function () {
frmView.action = '/todo/modify';
frmView.method = 'post';
frmView.submit();
})
</script>
- modify.jsp에 위 스크립트 코드 추가
- 글 insert 후 modify로 수정해서 수정 되는 지 확인
9. 페이징 처리를 위한 TodoMapper
- limit 라는 기능을 이용해서 비교적 쉽게 처리가 구현 가능
- 1) 페이징을 위한 SQL 연습
insert into tbl_todo (title, dueDate, writer) (select title, dueDate, writer from tbl_todo);
- 위에 SQL 쿼리로 데이터 복사
- 2) limit 실습
select * from tbl_todo order by tno desc;
- tno 내림차순으로 조회
select * from tbl_todo order by tno desc limit 0,10;
- 가장 최근 데이터 10개를 가져옴
select * from tbl_todo order by tno desc limit 10, 10;
- limit에 두 개의 값을 전달하는 경우 limit (skip), (fetch)가 됨.
- 10개를 건너뛰고, 다음 10개를 가져와야 하는 경우 위 처럼 작성
- 3) count()의 필요성
- 페이징 처리를 하기 위해서는 전체 데이터의 개수가 필요.
- 전체 데이터의 개수는 페이지 번호를 구성할 때 필요
- 예를 들어 전체 데이터가 30개이면 3 페이지까지만 출력해야 하는 작업에서 사용.
select count(tno) from tbl_todo;
- 전체 데이터 개수 확인 쿼리
10. 페이지 처리를 위한 DTO
- 파일 생성
package com.example.spring_project_02.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Positive;
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageRequestDTO {
@Builder.Default
@Min(value = 1)
@Positive
private int page = 1;
@Builder.Default
@Min(value = 10)
@Max(value = 100)
@Positive
private int size = 10;
public int getSkip() {
return (page - 1) * size;
}
}
- 생성한 파일에 위 코드 작성
void update(TodoVO todoVO);
List<TodoVO> selectList(PageRequestDTO pageRequestDTO);
- TodoMapper에 위 코드 추가
<select id="selectList" resultType="com.example.spring_project_02.domain.TodoVO">
select * from tbl_todo order by tno desc limit #{skip}, #{size}
</select>
- TodoMapper.xml에 위 코드 추가
@Test
public void testSelectList() {
PageRequestDTO pageRequestDTO = PageRequestDTO.builder()
.page(5)
.size(20)
.build();
List<TodoVO> todoVOList = todoMapper.selectList(pageRequestDTO);
for(TodoVO todoVO : todoVOList) {
log.info(todoVO);
}
todoVOList.forEach(item -> log.info(item));
}
- Test 코드 추가
- 테스트 실행 후 이상없는지 확인
11. TodoMapper의 count 처리
List<TodoVO> selectList(PageRequestDTO pageRequestDTO);
int getCount(PageRequestDTO requestDTO);
- TodoMapper.java에 int getCount 추가
<select id="getCount" resultType="int">
SELECT COUNT(*) FROM tbl_todo
</select>
- TodoMapper.xml에 getCount 추가
@Test
public void testGetCount() {
log.info(todoMapper.getCount(PageRequestDTO.builder().build()));
}
- Test 코드 추가
- 실행해서 출력 로그 확인
11. 목록 데이터를 위한 DTO와 서비스 계층
- 파일 생성
package com.example.spring_project_02.dto;
import java.util.List;
public class PageResponseDTO<E> {
private int page;
private int size;
private int total;
// 시작 페이지 번호
private int start;
// 끝 페이지 번호
private int end;
// 이전 페이지의 존재 여부
private boolean prev;
// 다음 페이지의 존재 여부
private boolean next;
private List<E> dtoList;
}
- 위 코드 작성
public class PageResponseDTO<E> {
@Builder(builderClassName = "withAll")
public PageResponseDTO(PageRequestDTO pageRequestDTO, List<E> dtoList, int total) {
this.page = pageRequestDTO.getPage();
this.size = pageRequestDTO.getSize();
this.total = total;
this.dtoList = dtoList;
}
- PageResponseDTO에 위 코드 추가
- 1) 페이지 번호 계산
- 페이지 번호를 계산하려면 우선 현재 페이지의 번호 page가 필요.
- * 현재 page가 1인 경우 : 시작 페이지 start는 1, 마지막 페이지 end는 10
- 2) 이전 prev / 다음 next의 계산
public class PageResponseDTO<E> {
@Builder(builderClassName = "withAll")
public PageResponseDTO(PageRequestDTO pageRequestDTO, List<E> dtoList, int total) {
this.page = pageRequestDTO.getPage();
this.size = pageRequestDTO.getSize();
this.total = total;
this.dtoList = dtoList;
this.end = (int)(Math.ceil(this.page / 10.0)) * 10;
this.start = this.end - 9;
int last = (int)(Math.ceil((total/(double)size)));
this.end = end > last ? last : end;
this.prev = this.start > 1;
this.next = total > this.end * this.size;
}
- PageResponseDTO에 코드 수정
12. TodoService / TodeServiceImpl
public interface TodoService {
void register(TodoDTO todoDTO);
// List<TodoDTO> getAll();
PageResponseDTO<TodoDTO> getList(PageRequestDTO pageRequestDTO);
TodoDTO getOne(Long tno);
void remove(Long tno);
void modify(TodoDTO todoDTO);
}
- TodoService 위 코드로 변경
package com.example.spring_project_02.service;
import com.example.spring_project_02.domain.TodoVO;
import com.example.spring_project_02.dto.PageRequestDTO;
import com.example.spring_project_02.dto.PageResponseDTO;
import com.example.spring_project_02.dto.TodoDTO;
import com.example.spring_project_02.mapper.TodoMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
@Log4j2
@RequiredArgsConstructor // 생성자 객체 주입. private final로 선언된 참조변수에 객체를 저장하는 생성자 작성.
public class TodoServiceImpl implements TodoService {
private final TodoMapper todoMapper;
private final ModelMapper modelMapper;
@Override
public void register(TodoDTO todoDTO) {
// 1) todoDTO를 전달 받아 2) todoDTO를 todoVO로 변환 후 3) dao의 insert() 호출
log.info(todoDTO);
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
log.info(todoVO);
todoMapper.insert(todoVO);
}
@Override
public PageResponseDTO<TodoDTO> getList(PageRequestDTO pageRequestDTO) {
List<TodoVO> voList = todoMapper.selectList(pageRequestDTO);
List<TodoDTO> dtoList = new ArrayList<>();
for(TodoVO todoVO : voList) {
dtoList.add(modelMapper.map(todoVO, TodoDTO.class));
}
int total = todoMapper.getCount(pageRequestDTO);
PageResponseDTO<TodoDTO> pageResponseDTO = PageResponseDTO.<TodoDTO>withAll()
.dtoList(dtoList)
.total(total)
.pageRequestDTO(pageRequestDTO)
.build();
return pageResponseDTO;
}
// @Override
public List<TodoDTO> getAll() {
List<TodoVO> voList = todoMapper.selectAll(); // dto에서 데이터베이스에서 들고온 VO리스트를 리턴
List<TodoDTO> dtoList = new ArrayList<>();
for (TodoVO todoVO : voList) {
// 개별 VO를 DTO로 변환.
TodoDTO todoDTO = modelMapper.map(todoVO, TodoDTO.class);
dtoList.add(todoDTO); // DTO리스트에 저장.
}
return dtoList;
}
@Override
public TodoDTO getOne(Long tno) {
TodoVO todoVO = todoMapper.selectOne(tno);
TodoDTO todoDTO = modelMapper.map(todoVO, TodoDTO.class);
return todoDTO;
}
@Override
public void remove(Long tno) {
todoMapper.delete(tno);
}
@Override
public void modify(TodoDTO todoDTO) {
log.info("modify()...");
// vo 객체를 생성하면서 매개변수로 받은 (값이 입력되어 있는) dto와 vo를 맵핑을 해서 vo에 값을 입력.
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
log.info(todoVO);
todoMapper.update(todoVO);
}
}
- TodoServiceImpl.java에 위 코드 생성
@GetMapping("/list")
public void list(@Valid PageRequestDTO pageRequestDTO, BindingResult bindingResult, Model model) {
log.info(pageRequestDTO);
if(bindingResult.hasErrors()) {
pageRequestDTO = PageRequestDTO.builder().build();
}
model.addAttribute("dtoList", todoService.getList(pageRequestDTO));
}
- TodoController.java에 list 메서드 위 코드로 수정
<tbody>
<c:forEach var="dto" items="${responseDTO.dtoList}">
<tr>
<th scope="row">${dto.tno}</th>
<td><a href="/todo/read?tno=${dto.tno}" class="text-decoration-none"> ${dto.title}</a></td>
<td>${dto.writer}</td>
<td>${dto.dueDate}</td>
<td>${dto.finished}</td>
</tr>
</c:forEach>
</tbody>
- list.jsp에 위 코드 부분 수정
- 서버 재 실행 후 URL 기입해서 10개만 출력되는 지 확인
- page URL을 접속했을 때, Tno 번호 줄어드는 것을 확인
https://getbootstrap.com/docs/5.1/components/pagination/
- 접속해서 페이징 번호 코드 복
</table>
<div class="float-end">
<ul class="pagination flex-wrap">
<c:if test="${responseDTO.prev}">
<li class="page-item">
<a class="page-link">Previous</a>
</li>
</c:if>
<c:forEach var="num" begin="${responseDTO.start}" end="${responseDTO.end}">
<li class="page-item"><a class="page-link" href="#">#{num}</a></li>
</c:forEach>
<c:if test="${responseDTO.next}">
<li class="page-item">
<a class="page-link">Next</a>
</li>
</c:if>
</ul>
</div>
</div>
- list.jsp </table> 끝나는 부분에 위 코드 추가
- 페이지가 10 넘어가면 Previous 나오는 거 확인
- 마지막 페이지 갔을 때, Next 버튼 없는 거 확인
공부 과정을 정리한 것이라 내용이 부족할 수 있습니다.
부족한 내용은 추가 자료들로 보충해주시면 좋을 것 같습니다.
읽어주셔서 감사합니다 :)