웹 개발 Spring Day7 Paging , OFFSET

김지원·2022년 8월 4일
0

WebDevelop2

목록 보기
26/34

먼저 전체 문의 내역의 갯수를 받아오자.

→ InquiryService > getTotalCount

  • 전에 작성한 이 Dao문에서 select되는 것은 모든 문의 내역이 select된다.
    그런데 여기서 for문을 돌리지 않고 왜 resultSet.next() 을 사용해야할까?

resultSet.next()

resultSet도 set 종류중 하나이다.

Iterator

resultSet.next()  resultSet포인터 			                데이터
true			    			     index     created_at           writer   content
				 					 31        2022.01.01 00:00:00  작성자     문의내용
                  ->                 ...
                                     ...
                                     ...
...                                  ...
false가 된다.
ResultSet resultSet = preparedStatement.executeQuery()

위 사진 속 Dao에서 resultSet은 내부적으로 전체 문의 내용을 다 가지고 있다.
resultSet에 인덱스 번호를 줘서 값을 불러온 것(getInt, getTimeStamp)이 아니다. 만약 인덱스로 불러왔다면 resultSet[0] 이런식으로 값을 불러왔을것이다. "set" 이라는것은 인덱스가 없다.

포인터가 어떠한 레코드도 가르키고 있지 않은 상태에서 getInt, getString을 하면 오류가 발생하게 된다. 포인터를 어떻게 내리는가?

resultSet.next() 메서드를 호출하면 포인터가 다음 것으로 내려가게 되고 ( → ) 그 상태에서 getInt(index), getString(string)하면 그 열에 해당하는 값을 주게 된다.

while에 들어올 수 있는 조건은 boolean타입일 때만 가능하기 때문에 next()메서드의 반환타입은 boolean이라고 예측이되며 boolean이 맞다.

while의 조건으로 받아들여져서

next() 는 내가 현재 가르키고 있는 레코드의 다음 것이 존재하면 true를 준다. false가 뜨면 while문을 빠져나가는 형식으로 돌아가게 된다.


  • COUNT를 세면 이 쿼리에 의해 반환되는 레코드의 갯수는 언제나 1개이상이다. 반드시 꼭 하나 돌아오게 되며 값이 없어도 0으로 레코드 하나를 돌려준다.
  • COUNT(0)을 하든 COUNT(*)을 하든 결과에는 차이가 없다. 그런데 왜 COUNT(0)을 사용하냐면 COUNT(*)을 하게되면 *에 대한 SELECT 결과를 싹 가지고 와서 결과에 의한 레코드 갯수를 센다. COUNT(0)보다 느리기 때문에 0을 사용한다.

❗️ resultSet의 쓰임새

갯수(COUNT)를 셀때는 어떤 조건에서든 항상 하나의 레코드가 돌아오기 때문에 무조건 true가 뜨기 때문에 while문을 사용하지 않는다.

select는 몇개가 돌아올지도, 레코드가 돌아올지 돌아오지 않을 수도 있기 때문에 while을 쓰는 것이다.


→ InquiryDao > selectCountTotal
전체 개수를 가져오면 되기 때문에 selectCountTotal에는 매개변수가 필요없다.

  • 위에서 말했다시피 갯수를 센다는 건 어찌됐든 최소 하나의 레코드가 돌아오기 때문에 while을 사용하지 않는다.
    반드시 한줄의 레코드가 나오기 때문에 resulSet.next()은 한번만 해주면 된다.

 count = resultSet.getInt("count");
  • AS 뒤에 있는 별명을 가져와야 값이 불러지게 된다.
    물론 0을 적어도 되지만 유지보수가 힘들기 때문에 별명을 적어준다.

→ InquiryService 전체 문의 갯수

→ HomeController

이제 페이징을 보여줘야하기 때문에 Controller가 그 값을 html에 넘겨줘야한다. modelAndView.addObject

modelAndView.addObject("paging", paging);

GET에 의해서 보여지는 페이지에서 작성자랑 문의내역을 작성하고 문의 남기기를 누르면 POST로 넘어간다. POST요청으로 들어오게 되어 들어온 문의를 추가하고 추가한 문의 내용을 select해서 싹 가지고 와서 modelAndView 추가해주고 똑같은 페이지로 가게 된다.

POST에도 페이지 번호와 optional추가를 해야하는데 일이 많아지니
게시글 작성하고 난 다음에 root페이지로 redirect 시키자.

modelAndView.addObject("inquiries", this.inquiryService.get());

postIndex에서 이 부분이 필요없어졌다.

modelAndView.setViewName("redirect:/");
  • 보여주는 페이지를 이렇게 설정하게 되면 결론적으로는 getIndex메서드가 실행이 되는 것이다.
    redirect: → 콜론(:)뒤에 있는 곳으로 이동하게 된다.

이전에는 POST로 페이지를 보여주고 새로고침하면 양식을 다시 제출하시겠습니까? 라는 창이 떴었다. redirect를 써버리면 새로고침해도 아무런 일도 일어나지 않는다.
redirect:/ 이것은 localhost:8080/ 을 주소창에 치고 enter치라는 것과 같은 말이기 때문에 그렇다. 즉, GET 방식이며 getIndex메서드가 다음으로 실행이 된다.

절차가 POST에서 GET으로 바로 올라가진 않는다.
POST에서 redirect:/ 자체가 클라이언트한테 응답으로 나간다. 클라이언트의 브라우저는 서버가 보내주는 결과(응답 결과=페이지를 표시해라) 가 /(root) 로 가라고 하네? 라고 받아들여서 브라우저 자체적으로 /로 옮기게 된다.

redirect:https://www.google.com/

이렇게 설정했다면 문의남기기를 눌렀을 때 구글페이지로 이동하게 된다.
왔다갔다를 두번해야되서 속도는 미세하게 떨어질수 있지만 개발이 편하기 때문에 사용한다.


index.html 로 넘어가자.
페이징하는 버튼인 a태그가 반복이 되어서 하나로 적자.

th:each를 통해 하나의 태그를 반복하게 만들자.

th:class="내용"

: 해당 태그의 Class 속성 값을 내용으로 한다.

th:href="링크"

: 해당 태그의 href 속성 값을 링크로 한다.

@{}

: 링크를 표시하고자할 때 사용한다.
가령, @{'/' (n1=1, n2=2)} 로 걸린 링크는 /?n1=&n2=2 로 연결된다.

#numbers

: org.thymeleaf.expression.Numbers 타입 객체. 숫자와 관련된 도구.

#numbers.sequence

  • sequence 메서드를 보니 오버로딩이 되어있고 매개변수를 보면 어디부터 어디까지 몇씩 증가 시킬 것이냐에 대한 내용을 적을 수 있게 되있다.

예를 들어

#numbers.sequence(1,5) // {1, 2, 3, 4, 5} 이러한 배열을 주고
#numbers.sequence({1, 2, 3, 4, 5}); 자체가 이렇게 변하게 된다.
#numbers.sequence(1,5,2) // {1, 3, 5} 
#numbers.sequence({1, 3, 5});

그렇게 되면 {1, 2, 3, 4, 5} 배열안에 있는 각 원소인 1page, 2page...가 생겨서 반복이 된다.

th:class="${'page'}"

th:class 를 한다는건 자바 문법을 이용해서 class를 정의하겠다는건데 선택된 페이지를 표시해주기 위한 조건(selected)이 필요하기 때문에 th:class을 사용한다.

th:class="${'page' + (page == paging.requestPage ? ' selected ' : '')}"

page는 무조건 와야하고 뒤에 each문을 돌린 page변수 하나하나와 요청한 페이지가 같다면 으로 selected를 붙이는 조건을 적는다.

  • 둘다 정수이기 때문에 == 비교 가능하다.

  • 문의 내용 갯수에 맞춰 4개의 페이지만 보이게 된다.
  • 페이지를 클릭하면 주소에 이렇게 찍히게 되고 페이지 이동이 된다.
  • 개발자 도구에서도 selectd 속성이 2번쨰 페이지에 부여된걸 확인할 수 있다.

컨트롤러에서 각각의 if문 조건에 따른 totalRowCount(진짜 전체 게시글 개수)를 달리해줘야한다.
-> HomeController

int totalRowCount = this.inquiryService.getTotalCount();
  • 이렇게 적은 것은 검색을 하지 않았을 때만 유효하게 되기 때문에 초기화한거 지운다.
int totalRowCount;

  • 첫번째 if문이 ture일 때 즉, 검색조건이 null이고 검색 keyword가 null일 때 전체 내용을 불러오도록 작성했다.
totalRowCount = this.inquiryService.getTotalCount();

그런데 백종원이라고 검색을 했을 때 문의 내용글을 몇개 나오지 않지만 버튼은 4개가 존재하게 된다.
아래의 조건에서도 totalRowCount에 대한 값을 따로 지정해주자.
작성자 기준으로 검색을 했을 때 나오는 총 갯수를 세서 페이징해야한다.

작성자 기준 총 갯수 / 내용 기준 총 갯수 세서 페이징을 해보자.

-> InquiryService

-> HomeController

-> InquiryDao

-> InquiryService > getCountByWriter / getCountByContent

  • 작성자 / 문의 내용으로도 문의 내용 갯수만큼 페이징 되는 것을 확인할 수 있다.

페이징이 된 상태에서 페이지 버튼을 클릭하게 되면 페이징이 풀려서 전체로 돌아오게 된다.

th:href=@{'./' (page = ${page})}"
  • 이렇게 처리해줬기 때문에 버튼을 클릭하면 전체 내용이 다 보이게 되는 것이다.
th:if="criteria == null || keyword == null
th:if="criteria != null || keyword != null
  • 이 두가지 조건을 통해서
criteria = ${criteria}, keyword = ${keyword}

로 경로를 설정하게 되게 할지 안되게 할지 걸러지게 된다.
크게 좋은 방법은 아니라서 하나의 a태그로 합쳐서 리터럴하게 처리해도 되긴한다.

  • 검색상황에서 페이지 버튼을 누르면 뒤에 사용자가 검색한 그대로 주소가 추가가 되어 그 페이지 상태를 유지할 수 있도록 한다.

시간의 시분초를 지워보자.

#dates

: org.thymeleaf.expression.Dates 타입 객체. 날짜/시간과 관련된 도구.

  • 원하는 대로 사용이 가능하다. (yyyy-MM-dd HH:mm:SS)

  • 검색한 것에 따른 검색결과가 없을 때 버튼이 보이지 않아야는데 이상하게 된다.

  • 계산에 따르면 maxPage 는 0이 된다.
    내용이 없다면 requestPage도 0이 된다.
this.requestPage = requestPage; // 0
this.boundStartPage = (this.requestPage / this.paginationCount) * this.paginationCount + 1; // 1
this.boundEndPage = Math.min(this.maxPage, (this.requestPage / this.paginationCount) * this.paginationCount + this.paginationCount); // 0
<div class="page-container" th:if="${inquiries.size() > 0}">


rowCountPerPage : 한 페이지당 10개가 나오게 하자.

OFFSET

  • 10개만 나오며 1 페이지에 표시되어야할 내용이 select된다.
  • 2페이지를 표시하려면 10개가 걸리지고 그 다음 10개만 표시되면 된다.

LIMIT 10 OFFSET 1

  • 1개가 걸러지고 10개가 선택이 된다. OFFSET뒤에 적은 갯수만큼 거르고 SELECT 해준다.
SELECT `index` AS `index`,
       `created_at` AS `createdAt`,
       `writer` AS `writer`,
       `content` AS `content`
FROM `study`.`inquiries`
ORDER BY `index` DESC
LIMIT ? OFFSET ?
  • 쿼리는 ?로 해서 Dao에서 해결해주자.

  • ? 에 순서대로 들어갈 내용의 공식을 이렇게 세우면 될 것 같다. Dao로 넘어가서 짜보도록 하자.

-> InquiryDao 추가 수정

  • 필요한 것들이 PagingModel이 다가지고 있기 때문에 매개변수는 PaginModel로 한다.

-> InquiryService 도 수정해주자.

-> HomeController 가 난리났다. > getIndex

  • totalRowCount를 초기화 시켜주던 if문 아래에 PagingModel을 객체화하였기 때문에 if 문 안에서 PagingModel을 get()메서드에 전달할 수가 없다.
    → if문을 두개로 나누어 하나에는 totalRowCount 값을, 그 다음에 PagingModel을 객체화 한 후 다시 if문을 구현하여 그 안에 get메서드를 호출하여 PagingModel을 전달한다.

  • 한페이지당 10개씩 잘나오게 된다.

검색결과에 대한 페이징도 필요하다.

-> InquiryDao > selectByWriter, selectByContent

  • 추가해준다.

-> InquiryService

-> HomeController > getIndex

  • paging 추가

  • 검색을 했을 때도 10개씩 페이징이되며 다음 페이지를 눌르면 다른 내용으로 바뀐 것을 볼 수 있다.
profile
Software Developer : -)

0개의 댓글