https://bobo12.tistory.com/323
6. 댓글의 페이징 처리
▷ 현재까지 작성된 예제는 해당 게시물의 전체 댓글을 가져와서 화면에 출력했을 때 문제는 댓글의 숫자가 많을 경우
▷ 댓글의 숫자가 많다면 데이터베이스에서 많은 양의 데이터를 가져와야하고, 이는 성능상의 문제를 가져올 수 있음
▷ 이런 문제를 페이징 처리를 이용해서 처리함
(1) 데이터베이스의 인덱스 설계
▷ 댓글에 대해서 우선적으로 고려해야하는 일은 tbl_reply 테이블을 접근할 때 댓글의 번호(rno)가 중심이 아니라 게시물의 번호(bno)가 중심이 된다는 점임
▷ 댓글을 조회할 때에는 해당 게시물의 댓글을 가져오기 때문에 'tbl_reply where bno = 200 order by rno asc'와 같은 방식으로 접근함
▷ tbl_reply 테이블의 PK는 rno 이므로 위와 같은 방식으로 쿼리를 실행하면 테이블을 접근하는 방식은 아래와 같음
▷ bno 값이 100번인 게시물의 댓글은 PK_REPLY를 이용해 검색하므로 중간에 다른 게시물 번호들을 건너 뛰어 특정 게시물의 댓글을 찾아야함, 데이터가 많아진다면 성능에 문제가 생길 수 있음
▷ 효율을 높이려면 번호에 맞게 댓글들을 모아서 빠르게 찾을 수 있는 구조로 만드는 것이 좋음
▷ 아래의 그림은 bno 별로 댓글들을 모았고 특정 게시물의 댓글을 찾을 때 모여 있는 부분만 찾을 수 있음
▷ 위와 같은 구조를 이용하게 되면 'bno=200 order by rno asc'와 같은 쿼리를 실행 할 때 왼쪽 구조에서 200에 해당하는 범위만 찾아서 사용하게 됨
▷ 이러한 구조를 생성하는 것을 인덱스 생성이라고 하고 SQL은 아래와 같습니다.
create index idx_reply on tbl_reply (bno desc, rno asc);
(2) 인덱스를 이용한 페이징 쿼리
▷ 인덱스를 이용하는 이유 중 하나는 정렬을 피할 수 있기 때문임
특정한 게시물의 rno의 순번대로 데이터를 조회하려면 다음과 같은 쿼리를 작성하게 됨
select /*+INDEX(tbl_reply idx_reply) */ rownum rn, bno, rno, reply, replyer, replyDate, updatedate from tbl_reply where bno = 3145745 --(게시물 번호) and rno > 0
▷ SQL의 실행 계획은 다음과 같이 처리됨
▷ 실행된 결과를 보면 IDX_REPLY를 이용해서 테이블에 접근하는 것을 볼 수 있고 ROWNUM은 가장 낮은 rno 값을 가지는 데이터가 1번이 되게 된다.
▷ ROWNUM이 원하는 순서대로 나오기 때문에 페이징 처리는 이전에 게시물 페이징과 동일한 형태로 작성할 수 있음
예) 10개씩 2페이지를 가져온다면 아래와 같은 쿼리를 작성하게 된다.
select rno, bno, reply, replyer, replydate, updatedate from ( select /*+INDEX(tbl_reply idx_reply) */ rownum rn, bno, rno, reply, replyer, replyDate, updatedate from tbl_reply where bno = 게시물번호 and rno > 0 and rownum <= 20 ) where rn > 10 ;
◎ ReplyMapper.xml 내용 수정
... (생략) ... <select id="getListWithPaging" resultType="org.codehows.domain.ReplyVO"> <![CDATA[ select rno, bno, reply, replyer, replyDate, updatedate from ( select /*+INDEX(tbl_reply idx_reply) */ rownum rn, rno, bno, reply, replyer, replyDate, updatedate from tbl_reply where bno = #{bno} and rno > 0 and rownum <= #{cri.pageNum} * #{cri.amount} ) where rn > (#{cri.pageNum} -1) * #{cri.amount} ]]> </select> ... (생략) ...
◎ ReplyMapperTests.java 메소드 추가
... (생략) ... @Test public void testList2() { Criteria cri = new Criteria(2, 10); List<ReplyVO> replies = mapper.getListWithPaging(cri, 194L); replies.forEach(reply -> log.info(reply)); } }
(3) 댓글의 숫자 파악
▷ 댓글들을 페이징 처리하기 위해서는 해당 게시물의 전체 댓글의 숫자를 파악해서 화면에 보여줄 필요가 있음
▷ ReplyMapper 인터페이스에는 getCountByBno()를 추가함
◎ ReplyMapper.java 인터페이스 메소드 추가
... (생략) ... public List<ReplyVO> getListWithPaging( @Param("cri") Criteria cri, @Param("bno") Long bno); public int getCountByBno(Long bno); }
◎ ReplyMapper.xml에 id 속성값이 getCountByBno인 <select> 추가
... (생략) ... </select> <select id="getCountByBno" resultType="int"> <![CDATA[ select count(rno) from tbl_reply where bno = #{bno} ]]> </select> ... (생략) ...
(4) ReplyServiceImpl에서 댓글과 댓글 수 처리
▷ 댓글 페이징 처리가 필요한 경우 댓글 목록과 함께 전체 댓글의 수를 같이 전달
▷ ReplyService 인터페이스와 구현 클래스인 ReplyServiceImpl 클래스는 List<ReplyVO>와 댓글의 수를 같이 전달할 수 있는 구조로 변경함
우선 org.codehows.domain 패키지에 두 가지 정보를 담을 수 있는 ReplyPageDTO 클래스를 정의함
◎ ReplyPageDTO 클래스 생성
package org.codehows.domain; import java.util.List; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; @Data @AllArgsConstructor @Getter public class ReplyPageDTO { private int replyCnt; private List<ReplyVO> list; }
ReplyPageDTO는 객체 생성 시에 편리하도록 @AllArgsConstructor를 이용해서 replyCnt와 list를 생성자의 파라미터로 처리함
◎ ReplyService 인터페이스 수정
▷ ReplyService 인터페이스와 ReolyServiceImpl 클래스에는 ReplyPageDTO를 반환하는 메서드를 추가함
package org.codehows.service; import java.util.List; import org.codehows.domain.Criteria; import org.codehows.domain.ReplyPageDTO; import org.codehows.domain.ReplyVO; public interface ReplyService { ... (생략) ... public ReplyPageDTO getListPage(Criteria cri, Long bno); }
◎ ReplyServiceImpl 클래스 수정
package org.codehows.service; import java.util.List; import org.codehows.domain.Criteria; import org.codehows.domain.ReplyPageDTO; import org.codehows.domain.ReplyVO; import org.codehows.mapper.ReplyMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import lombok.Setter; import lombok.extern.log4j.Log4j; @Service @Log4j public class ReplyServiceImpl implements ReplyService { @Setter(onMethod_ = @Autowired) private ReplyMapper mapper; ... (생략) ... @Override public ReplyPageDTO getListPage(Criteria cri, Long bno) { return new ReplyPageDTO( mapper.getCountByBno(bno), mapper.getListWithPaging(cri, bno)); } }
(5) ReplyController 수정
▷ ReplyController에서는 ReplyService 에 새롭게 추가된 getListPage()를 호출하고 데이터를 전송하는 형태로 수정함
... (생략) ... @GetMapping(value = "/pages/{bno}/{page}", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE }) public ResponseEntity<ReplyPageDTO> getList(@PathVariable("page") int page, @PathVariable("bno") Long bno) { Criteria cri = new Criteria(page, 10); log.info("get Reply List bno: " + bno); log.info("cri : " + cri); return new ResponseEntity<>(service.getListPage(cri, bno),HttpStatus.OK); } ... (생략) ...
기존과 동일하게 JSON 데이터를 전송하지만 ReplyPageDTO 객체를 JSON으로 전송하게 되므로, 특정 게시물의 댓글 목록을 조회하려면 아래와 같이 replyCnt와 list라는 이름의 속성을 가지는 JSON 문자열이 전송됨
댓글의 페이징 처리를 통해 원하는 페이지를 출력하고 해당 페이지의 댓글들을 나타낼 수 있었습니다.
다음 글에서는 댓글 페이지의 화면 처리하는 것을 구현해볼게요!!
많은 분들의 피드백은 언제나 환영합니다! 많은 댓글 부탁드려요~~
'BackEnd > Spring' 카테고리의 다른 글
[코드로 배우는 스프링 웹 프로젝트] ch17 Ajax 댓글 처리 8 (0) | 2023.04.03 |
---|---|
[코드로 배우는 스프링 웹 프로젝트] ch17 Ajax 댓글 처리 6 (이벤트 처리, HTML 처리) (0) | 2023.04.03 |
[코드로 배우는 스프링 웹 프로젝트] ch17 Ajax 댓글 처리 5 (댓글의 목록, 삭제 및 갱신, 수정, 조회 처리) (0) | 2023.04.03 |
[코드로 배우는 스프링 웹 프로젝트] ch17 Ajax 댓글 처리 4 (JavaScript의 모듈화) (0) | 2023.04.02 |
[코드로 배우는 스프링 웹 프로젝트] ch17 Ajax 댓글 처리 3 (ReplyController를 이용한 CRUD) (0) | 2023.04.02 |