5. 페이징 처리를 위한 TodoMapper

  • Todo 데이터 수가 많아지면 목록 페이지를 가져올 때 많은 시간과 자원이 소모됨
  • 많은 데이터를 보여주는 작업은 페이징 처리를 해서 최소한의 데이터들을 보여주는 방식 선호
  • 페이징 처리에서 중요한 것은 데이터베이스에서 필요한 데이터만 가져오도록 하는 것
  • MySQL, MariaDB에서는 limit라는 기능을 이용해서 비교적 쉽게 페이징 처리 구현 가능

 

 1) 페이징을 위한 SQL 연습

  - 더미 데이터 추가: 기존에 데이터베이스에 있던 데이터를 여러번 복사하여 생성

insert into tbl_todo (title, dueDate, writer) (select title, dueDate, writer from tbl_todo);

 

  - limit 실습

  • MariaDB / MySQL에서 페이징 처리를 위해 select의 마지막 부분에 limit 처리를 이용
  • limit뒤에는 하나 또는 두 개의 값 전달
    • 첫번째 값: 건너뛰는 데이터 수(skip)
    • 두번째 값: 가져오는 데이터 수(fetch)
    • 하나의 값만 전달할 때: 가져오는 데이터 수만 전달
  • 먼저 가장 마지막에 등록된 데이터부터 순차적으로 보여지도록 tno를 기준으로 내림차순 정렬
select * from tbl_todo order by tno desc limit 10;

  • 처음 10개를 건너뛰고 나온 다음 10개를 출력하면 처음 10개의 마지막 숫자 4579 다음의 숫자인 4578에서 시작
select * from tbl_todo order by tno desc limit 10, 10;

  • 위와 같은 규칙으로
    • 1페이지: limit 10;
    • 2페이지: limit 10, 10;
    • 3페이지: limit 20, 10;
    • 4페이지: limit 30, 10;
    • ....

 

  - limit의 단점

  • limit 뒤에 식 사용 불가, 오직 값만 주어야 함(limit (2-1)*10, 10; 등의 식은 실행되지 않음)

 

  - count의 필요성

  • 페이징 처리를 하기 위해 전체 데이터의 개수도 필요
  • 전체 데이터의 개수는 페이지 번호를 구성할 때 필요(30개의 데이터가 있으면 3페이지까지 출력해야하는 등)

 

 2) 페이지 퍼리를 위한 DTO

  • 페이지 퍼리는 현재 페이지 번호, 한 페이지당 보여주는 데이터 개수가 기본적으로 필요
  • 2개의 숫자를 매번 전달할 수도 있지만 확장을 고려하여 별도의 DTO로 만들어 두는 것이 좋음
  • dto 패키지에 PageRequestDTO 클래스 정의
// PageRequestDTO
package org.zerock.springex.dto;

import lombok.*;

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;

  // limit에서 사용하는 건너뛰기의 수
  public int getSkip() {
    return (page-1) * 10;
  }
}

 

 3) TodoMapper의 목록 처리

  • TodoMapper 인터페이스는 PageRequestDTO를 파라미터로 처리하는 selectList()를 추가
// TodoMapper
package org.zerock.springex.mapper;

import ...

public interface TodoMapper {
  
  ...

  List<TodoVO> selectList(PageRequestDTO pageRequestDTO);
}
<!-- TodoMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTO Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "org.zerock.springex.mapper.TodoMapper">
    
    ...

    <select id = "selectList" resultType = "org.zerock.springex.domain.TodoVO">
        <!-- MyBatis는 기본적으로 getXXX, setXXX를 통해 작동하므로 #{skip}은 getSkip()을 호출하게됨 -->
        select * from tbl_todo order by tno desc limit #{skip}, #{size}
    </select>

</mapper>

 

  • TodoMapperTests에 테스트 코드를 작성하여 selectList() 작동 확인
// TodoMapperTests
@Test
public void testSelectList() {
	PageRequestDTO pageRequestDTO = PageRequestDTO.builder()
    	.page(1)
    	.size(10)
    	.build();
	List<TodoVO> voList = todoMapper.selectList(pageRequestDTO);
	voList.forEach(vo -> log.info(vo));
}

 

 4) TodoMapper의 count 처리

  • 화면에 페이지 번호들을 구성하기 위해 전체 데이터수 확인 필요
  • TodoMapper에 getCount() 추가하고, 검색을 대비해 PageRequestDTO를 파라미터로 받도록 설계
// TodoMapper
package org.zerock.springex.mapper;

import ...

public interface TodoMapper {
  
  ...
  
  int getCount(PageRequestDTO pageRequestDTO);
}
  • TodoMappe.xml은 우선 전체 개수를 반환하도록 구성
<!-- TodoMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTO Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "org.zerock.springex.mapper.TodoMapper">
    
    ...

    <select id = "getCount" resultType = "int">
        select count(tno) from tbl_todo
    </select>
</mapper>

 

 

6. 목록 데이터를 위한 DTO와 서비스 계층

  • TodoMapper에서 TodoVO의 목록과 전체 데이터 수를 가져온다면 이를 서비스 계층에서 한 번에 담아서 처리하도록 DTO를 구성하는 것이 좋음
  • PageResponseDTO라는 이름으로 생성하고 다음의 데이터와 기능을 가지도록 구성
    • TodoDTO 목록
    • wjscp epdlxj tn
    • 페이지 번호 처리를 위한 데이터(시작 페이지 번호 / 끝 페이지 번호)
// PageResponseDTO
package org.zerock.springex.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;
}
  • 제네릭을 이용하는 이유는 나중에 다른 종류의 객체를 이용해서 PageResponseDTO를 구성할 수 있도록 하기 위함

 

  • PageResponseDTO는 여러 정보를 생성자를 이용해서 받아서 처리하는 것이 안전
    예를 들어, PageRequestDTO에 있는 page, size 값이 필요하고, TodoDTO 목록 데이터와 전체 데이터 개수도 필요
// PageResponseDTO
package org.zerock.springex.dto;

import ...

public class PageResponseDTO<E> {

  ...

  // PageResponseDTO의 생성자
  @Builder(builderMethodName = "withAll")
  public PageResponseDTO(PageRequestDTO pageRequestDTO, List<E> dtoList, int total) {
  
    this.page = pageRequestDTO.getPage();
    this.size = pageRequestDTO.getSize();

    this.total = total;
    this.dtoList = dtoList;
    
  }
}

 

 1) 페이지 번호의 계산

  • 페이지 번호를 계산하려면 우선 현재 페이지의 번호(page)가 필요
  • 현재 페이지가 1~10 사이인 경우, 시작 페이지는 1, 마지막 페이지는 10
  • 현재 페이지가 11~20 사이인 경우, 시작 페이지는 11, 마지막 페이지는 20

 

  - 마지막 페이지 / 시작 페이지 번호의 계산

  • 마지막 페이지 먼저 구하기
// 현재 페이지(page)를 10으로 나눈 값을 올림 처리한 후 * 10
this.end = (int)(Math.ceil(this.page / 10.0)) * 10;

// 결과
1 / 10 ====> 0.1 =="올림"==> 1 =="*10"==> 10
11 / 10 ====> 1.1 =="올림"==> 2 =="*10"==> 20
10 / 10 ====> 1 =="올림"==> 1 =="*10"==> 10
  • 시작 페이지는 마지막 페이지에서 - 9
this.start = this.end - 9;
  • 마지막 페이지의 경우 전체 개수(total)를 고려
    게시물을 10개씩 보여주는 경우, 전체 개수가 75라면 마지막 페이지는 8이 되어야 함
int last = (int)(Math.ceil(total / (double)size));

// 결과
123 / 10.0 ====> 12.3 =="올림"==> 13
100 / 10.0 ====> 10.0 =="올림"==> 10
75 / 10.0 ====> 7.5 =="올림"==> 8
  • 마지막 페이지(end)가 last보다 크면 last가 end값으로 되어야 함
this.end = end > last ? last : end

 

  - 이전(prev) / 다음(next)의 계산

  • 이전 페이지의 존재 여부는 다음 페이지(start)가 1이 아니면 무조건 true
  • 다음 페이지(next)는 마지막 페이지(end)와 페이지당 개수(size)를 곱한 값보다 전체 개수가 더 많은지를 보고 판단
this.prev = this.start > 1;
this.next = total > this.end * this.size;

 

  - 최종 PageResponseDTO 코드

// PageResponseDTO
package org.zerock.springex.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

import java.util.List;

@Getter
@ToString
// 제네릭을 이용해서 설계
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;

  // PageResponseDTO의 생성자
  @Builder(builderMethodName = "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;
  }
}

 

 

 2) TodoService / TodoServiceImpl

  • TodoService와 TodoServiceImpl에서 PageResponseDTO를 반환 타입으로 지정해서 getList() 구성(기존의 getAll()을 대체)
// TodoService
package org.zerock.springex.service;

import org.zerock.springex.dto.TodoDTO;
import org.zerock.springex.dto.PageRequestDTO;
import org.zerock.springex.dto.PageResponseDTO;
// import java.util.List;

// register는 여러 개의 파라미터 대신 TodoDTO로 묶어서 전달받음
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);
}
// TodoServiceImpl
package org.zerock.springex.service;

import ...
public class TodoServiceImpl implements TodoService {

  ...

//  @Override
//  public List<TodoDTO> getAll() {
//    // stream의 map()을 이용해서 TodoVOfmf TodoDTO로 변경
//    // collect()를 이용해서 List<TodoDTO>로 묶어줌
//    List<TodoDTO> dtoList = todoMapper.selectAll().stream()
//        .map(vo -> modelMapper.map(vo, TodoDTO.class))
//        .collect(Collectors.toList());
//    return dtoList;
//  }

  @Override
  public PageResponseDTO<TodoDTO> getList(PageRequestDTO pageRequestDTO) {
    List<TodoVO> voList = todoMapper.selectList(pageRequestDTO);
    List<TodoDTO> dtoList = voList.stream()
        .map(vo -> modelMapper.map(vo, TodoDTO.class))
        .collect(Collectors.toList());

    int total = todoMapper.getCount(pageRequestDTO);

    PageResponseDTO<TodoDTO> pageResponseDTO = PageResponseDTO.<TodoDTO>withAll()
        .dtoList(dtoList)
        .total(total)
        .pageRequestDTO(pageRequestDTO)
        .build();

    return pageResponseDTO;
  }
}

 

  - 테스트

  • TodoServiceTests에서 다음의 코드 작성
  • TodoController에서 getAll()을 사용하는 부분 삭제 후 테스트 진행
// TodoController
package org.zerock.springex.controller;

import ...

public class TodoController {

  ...
  
  @RequestMapping("/list")
  public void list(Model model) {
    log.info("todo list.......");
    // model.addAttribute("dtoList", todoService.getAll());
  }

  ...
  
}
// TodoServiceTests
package org.zerock.springex.service;

import ...

public class TodoServiceTests {

  ...

  @Test
  public void testPaging() {
    PageRequestDTO pageRequestDTO = PageRequestDTO.builder().page(1).size(10).build();
    PageResponseDTO<TodoDTO> responseDTO = todoService.getList(pageRequestDTO);
    log.info(responseDTO);
    responseDTO.getDtoList().stream().forEach(todoDTO -> log.info(todoDTO));
  }
}

  • 1 페이지이므로 마지막 페이지(end)는 10, 이전 페이지(prev)는 없음, 다음 페이지(next)는 있음

 

 3) TodoController와 JSP 처리

  • TodoController의 list()에서 PageRequestDTO를 파라미터로 처리
  • Model에 PageResponseDTO의 테이처들을 담을 수 있도록 변경
// TodoController
package org.zerock.springex.controller;

import ...

public class TodoController {

  ...
  
  // Valid를 이용해 잘못된 파라미터 값들이 들어오는 경우 page는 1, size는 10으로 고정된 값을 처리하도록 구성
  @RequestMapping("/list")
  public void list(@Valid PageRequestDTO pageRequestDTO, BindingResult bindingResult, Model model) {
    log.info(pageRequestDTO);
    
    if(bindingResult.hasErrors()) {
      pageRequestDTO = PageRequestDTO.builder().build();
    }
    
    model.addAttribute("responseDTO", todoService.getList(pageRequestDTO));
  }

  ...

}
  • Model에 responseDTO라는 이름으로 PageResponseDTO를 담아주었기 때문에 list.jsp는 기존의 코드를 많이 수정해야 함
<!-- list.jsp -->
<!-- 목록을 출력하는 부분에서 dtoList가 아니라 responseDTO.dtoList의 형태로 변경 -->
<c:forEach items = "${responseDTO.dtoList}" var = "dto">
<tr>
    <th scope = "row"><c:out value = "${dto.tno}"/></th>
    <td><a href = "/todo/read?tno=${dto.tno}" class = "text=decoration-none"><c:out value = "${dto.title}"/></a></td>
    <td><c:out value = "${dto.writer}"/></td>
    <td><c:out value = "${dto.dueDate}"/></td>
    <td><c:out value = "${dto.finished}"/></td>
</tr>
</c:forEach>

 

  • 프로젝트 실행 뒤 '/todo/list' 경로에서 1페이지에 해당하는 데이터들이 출력되는 것을 확인

 

 4) 페이지 이동 확인

  • 화면을 추가로 개발하기 전에 'todo/list?page=xx&size=xx'를 호출해서 결과가 정상적으로 처리되는지 확인

  • 'todo/list?page=12'를 호출하면 그냥 '/todo/list'를 호출했을 때 4588로 시작하는 1페이지가 출력되는 것과 다르게 4478로 시작하는 12페이지가 출력되는 것을 확인

 

  • size까지 설정하여 '/todo/list/page=12&size=20'을 호출한 결과 4478로 시작하는 12페이지에 20개의 데이터가 출력되는 것을 확인

 

  - 화면에 페이지 이동을 위한 번호 출력

  • 부트스트랩의 pagination 컴포넌트 적용
  • list.jsp의 <table> 태그가 끝난 후에 <div> 구성하여 다음과 같이 화면 작성
<!-- list.jsp -->
</table>
<div class = "float-end">
    <ul class = "pagination flex-wrap">
        <c:forEach begin = "${responseDTO.start}" end = "${responseDTO.end}" var = "num">
            <li class = "page-item"><a class = "page-link" href = "#">${num}</a></li>
        </c:forEach>
    </ul>
</div>

 

  - 화면에서 prev / next / 현재 페이지 표시

<!-- list.jsp -->
<div class = "float-end">
    <ul class = "pagination flex-wrap">
    
    	<!-- previous 버튼 -->
        <c:if test = "${responseDTO.prev}">
            <li class = "page-item">
                <a class = "page-link">Previous</a>
            </li>
        </c:if>
        
        <!-- 페이지 버튼 -->
        <c:forEach begin = "${responseDTO.start}" end = "${responseDTO.end}" var = "num">
            <!-- ${responseDTO.page == num? "active":""} 를 추가하여 현재 페이지 표시 처리 -->
            <li class = "page-item ${responseDTO.page == num? "active":""}"><a class = "page-link" href = "#">${num}</a></li>
        </c:forEach>
        
        <!-- next 버튼 -->
        <c:if test = "${responseDTO.next}">
            <li class = "${responseDTO.next}">
                <a class = "page-link">Next</a>
            </li> 
        </c:if>
        
    </ul>
</div>

 

  • 1페이지 ~ 10페이지는 Previous 버튼은 없고 Next 버튼은 출력됨

 

  • 11페이지부터는 Previous 버튼과 Next 버튼이 모두 출력됨

 

  • 마지막 페이지에는 Previous 버튼만 출력됨

 

  - 페이지의 이벤트 처리

  • 페이지의 번호를 누르면 이동하는 처리는 자바스크립트 이용
<ul class = "pagination flex-wrap">

    <!-- previous 버튼 -->
    <c:if test = "${responseDTO.prev}">
        <li class = "page-item">
        	<!-- Previous 버튼에는 data-num - 1의 값이 저장되도록 설정 -->
            <a class = "page-link" data-num = "${responseDTO.start - 1}">Previous</a>
        </li>
    </c:if>
    
    <!-- 페이지 버튼 -->
    <c:forEach begin = "${responseDTO.start}" end = "${responseDTO.end}" var = "num">
        <!-- "${responseDTO.page == num? "active":""}" 를 추가하여 현재 페이지 표시 처리 -->
        <!-- data-num이라는 속성을 추가하여 페이지 번호를 보관하도록 구성 -->
        <li class = "page-item ${responseDTO.page == num? "active":""}"><a class = "page-link" data-num = "${num}">${num}</a></li>
    </c:forEach>
    
    <!-- next 버튼 -->
    <c:if test = "${responseDTO.next}">
        <li class = "${responseDTO.next}">
        	<!-- Next 버튼에는 data-num + 1의 값이 저장되도록 설정 -->
            <a class = "page-link" data-num = "${responseDTO.end + 1}">Next</a>
        </li>
    </c:if>
</ul>
</div>
<!-- 페이지 번호 눌렀을 때 이벤트 처리 -->
<script>
    document.querySelector(".pagination").addEventListener("click", function(e) {
      e.preventDefault()
      e.stopPropagation()

      const target = e.target

      if(target.tagName !== 'A') {
        return
      }
      const num = target.getAttribute("data-num")

      self.location = `/todo/list?page=\${num}` // ``를 이용해서 템플릿 처리
    }, false)
</script>

 

  • 브라우저에서 각 페이지 번호의 data-num부분에 각 페이지 번호 값이 저장됨을 확인
  • Next 버튼에는 그 다음의 페이지 번호가 저장됨을 확인

 

  • 페이지 번호를 눌러 각 페이지로 이동 가능

 

 

  - 조회 페이지로의 이동

  • 기존에는 목록에서 제목을 눌러 조회 페이지로 이동
  • 이때 단순히 tno만 전달하여 '/todo/read?tno=1'과 같은 방식으로 이동
  • 페이지 번호가 붙을 때는 page와 size를 같이 전달해주어야 조회 페이지에서 다시 목록으로 이동할 때 기존 페이지를 볼 수 있게 됨
// PageRequestDTO
package org.zerock.springex.dto;

import ...

public class PageRequestDTO {

  ...
  
  private String link;

  public int getSkip() {
    return (page-1) * 10;
  }
  
  // GET 방식으로 페이지 이동에 필요한 링크 생성
  public String getLink() {
    if(link == null) {
      StringBuilder builder = new StringBuilder();
      builder.append("page=" + this.page);
      builder.append("&size=" + this.size);
      link = builder.toString();
    }
    return link;
  }
}
<!-- list.jsp -->
<c:forEach items = "${responseDTO.dtoList}" var = "dto">
<tr>
    <th scope = "row"><c:out value = "${dto.tno}"/></th>
    <!-- 링크 주소에 PageRequestDTO에서 생성한 link부분 추가 -->
    <td><a href = "/todo/read?tno=${dto.tno}&${pageRequestDTO.link}" class = "text=decoration-none"><c:out value = "${dto.title}"/></a></td>
    <td><c:out value = "${dto.writer}"/></td>
    <td><c:out value = "${dto.dueDate}"/></td>
    <td><c:out value = "${dto.finished}"/></td>
</tr>
</c:forEach>

 

  • 코드 수정 후 4페이지의 4554번 데이터를 조회하면 주소에 다음과 같이 page=4&size=10이 같이 전달됨

 

  - 조회에서 목록으로

  • 4페이지의 데이터를 조회한 후 다시 목록으로 돌아갈 때, 1페이지 목록이 아닌 4페이지 목록으로 돌아갈 수 있도록 설정
  • 조회 화면에서는 기존과 달리 PageRequestDTO를 추가로 이용하도록 TodoController를 수정해야함
// TodoController
package org.zerock.springex.controller;

import ...

public class TodoController {

  ...

  // read() 메서드에 PageRequestDTO 파라미터를 추가
  @GetMapping({"/read", "/modify"})
  public void read(Long tno, PageRequestDTO pageRequestDTO, Model model) {
    TodoDTO todoDTO = todoService.getOne(tno);
    log.info(todoDTO);
    model.addAttribute("dto", todoDTO);
  }

  ...
  
}

 

  • read.jsp에서 List 버튼의 링크도 다시 처리
<!-- list.jsp -->
...
<script>
    
    ...
    
    document.querySelector(".btn-secondary").addEventListener("click", function(e){
      self.location = "/todo/list?${pageRequestDTO.link}";
    }, false)
</script>
...

 

 

  - 조회에서 수정으로

  • 조회화면에서 수정화면으로 이동할 때도 현재 페이지 정보를 유지해야해서 링크 처리 부분 수정
<!-- list.jsp -->
...
<script>
    
    document.querySelector(".btn-primary").addEventListener("click", function(e){
      self.location = `/todo/modify?tno=${dto.tno}&${pageRequestDTO.link}`
    }, false)
    
    ...
    
</script>
...

 

  • Modify 버튼 클릭 시 page 번호와 size 까지 같이 전달된 주소로 이동

 

  - 수정 화면에서의 링크 처리

  • 수정 화면에서 다시 목록으로 돌아가는 링크 처리
  • TodoController의 read() 메서드는 GET 방식으로 동작하는 'todo/modify'에 동일하게 처리하게 되므로 JSP에서 PageRequestDTO를 사용할 수 있음
<!-- modify.jsp -->
<script>

    ...

    document.querySelector(".btn-secondary").addEventListener("click", function(e) {
        e.preventDefault()
        e.stopPropagation()
		
        // List 버튼을 누르는 자바스크립트 이벤트 부분을 다음과 같이 변경
        self.location = `todo/list${pageRequestDTO.link}`
    }, false);
    
</script>

 

 

  - 수정 / 삭제 처리 후 페이지 이동

  • 실제 수정 / 삭제 작업은 POST 방식으로 처리되고 삭제 처리된 후에는 다시 목록으로 이동
  • 수정 화면에서 <form> 태그로 데이터를 전송할 때 페이지와 관련된 정보를 같이 추가해서 전달해야함
  • modify.jsp의 <input type = 'hidden'>을 이용
<!-- modify.jsp -->
...

<form action = "/todo/modify" method = "post">
    <input type = "hidden" name = "page" value = "${pageRequestDTO.page}">
    <input type = "hidden" name = "size" value = "${pageRequestDTO.size}">

...

 

  • TodoController에서 POST 방식으로 이루어지는 삭제처리에도 PageRequestDTO를 이용해서 <form>태그로 전송되는 태그들을 수집
  • 수정 후 목록 페이지로 이동할 때 page는 무조건 1페이지로 이동해서 size 정보를 활용
// TodoController
package org.zerock.springex.controller;

import ...

public class TodoController {

  ...

  @PostMapping("/remove")
  public String remove(Long tno, PageRequestDTO pageRequestDTO, RedirectAttributes redirectAttributes) {
    log.info("----------remove----------");
    log.info("tno: "+tno);

    todoService.remove(tno);

    redirectAttributes.addAttribute("page", 1);
    redirectAttributes.addAttribute("size", pageRequestDTO.getSize());
    return "redirect:/todo/list";
  }

  ...

}

 

  • 정상적으로 삭제되고 삭제 후에는 목록 페이지로 이동됨을 확인

 

  - 수정 처리 후 이동

  • 수정 후에 목록으로 이동할 때는 페이지 정보를 이용해야 하므로 TodoController의 modify()에서는 PageRequestDTO를 받아서 처리하도록 변경
/ TodoController
package org.zerock.springex.controller;

import ...

public class TodoController {

  ...
  @PostMapping("/modify")
  // PageRequestDTO를 파라미터로 추가
  public String modify(PageRequestDTO pageRequestDTO,
                       @Valid TodoDTO todoDTO,
                       BindingResult bindingResult,
                       RedirectAttributes redirectAttributes) {
    if(bindingResult.hasErrors()) {
      log.info("has errors.......");
      redirectAttributes.addFlashAttribute("errors", bindingResult.getAllErrors());
      redirectAttributes.addAttribute("tno", todoDTO.getTno());
      return "redirect:/todo/modify";
    }

    log.info(todoDTO);
    todoService.modify(todoDTO);
    
    // page와 size를 받아 리다이렉트 되도록 처리
    redirectAttributes.addAttribute("page", pageRequestDTO.getPage());
    redirectAttributes.addAttribute("size", pageRequestDTO.getSize());
    return "redirect:/todo/list";
  }
}

 

  • 수정 후, 정상적으로 원래 목록 페이지로 이동하는지 확인

+ Recent posts