본문 바로가기
[ JAVA ]/JAVA Spring

[ Spring ] 페이지네이션 코드 해석 및 기록

by 환이s 2023. 6. 19.


이전에 JSP 포스팅할 때 페이지 나누기 기능에 대해서 개념 및 기능 구현을 했습니다.

JSP 페이지 나누기 기능에 대해서 찾아보시는 분들은 아래 포스팅 참고해 주시면 감사합니다..!

 

 

[ JSP ] 페이지 나누기

오늘은 게시판 만들 때 꼭 필요한 페이지 나누는 기능을 포스팅해보겠습니다! ■ 페이지네이션 웹 사이트에는 게시판을 비롯한 여러 정보들을 페이지 단위로 보여줍니다. 따라서 원하는 자료가

drg2524.tistory.com

 

 

[ JSP ] 게시판 만들기 6 - 페이지 나누기 기능 구현

이전 포스팅에서 답변 기능을 추가로 구현했습니다. 오늘은 게시판에 여러 정보들을 페이지 단위로 보여주기 위해 페이지네이션 기능을 구현합니다. 게시판 기능/ 파일 구조 < 기능 > 1 ) CRUD(글

drg2524.tistory.com

 

오늘은 프로젝트에서 공통으로 들어가는 페이지 나누기 코드를 공통화시켜서 모든 기능에 적용할 수 있는  코드부터 mybatis까지 코드 흐름에 대해서 기록하는 포스팅을 해보겠습니다.

 

 

프로젝트 구조

 

 


BaseVO

 

package com.example.demo.common;

public class BaseVO {

    private String stype = ""; // 검색어 타입
    private String sdata = ""; // 검색어


    public String getStype() {
        return stype;
    }

    public void setStype(String stype) {
        this.stype = stype;
    }

    public String getSdata() {
        return sdata;
    }

    public void setSdata(String sdata) {
        this.sdata = sdata;
    }

}

 

BaseVO 클래스 파일은 다른 클래스에서 상속하여 사용될 수 있는 공통적인 기능과 속성을 정의하는 용도입니다.

 

private String stype = ""; : stype라는 String 타입의 속성을 선언하고, 빈 문자열로 초기화합니다. 이 속성은 검색어의 타입을 나타냅니다.

private String sdata = ""; : sdata라는 String 타입의 속성을 선언하고, 빈 문자열로 초기화합니다. 이 속성은 검색어를 나타냅니다.

public String getStype() : stype 속성의 값을 반환하는 getter 메서드입니다.

public void setStype(String stype) : stype 속성의 값을 설정하는 setter 메서드입니다.

public String getSdata() : sdata 속성의 값을 반환하는 getter 메서드입니다.

public void setSdata(String sdata) : sdata 속성의 값을 설정하는 setter 메서드입니다.

 

 

BaseVO 클래스는 상속을 통해 다른 클래스에서 확장하여 사용될 수 있으며, 'stype'과 'sdata' 속성을 통해 검색어 관련 정보를 저장하고 가져올 수 있습니다.

 

다음으로 BaseVO 클래스를 상속받는 PagingInfoVO 코드 입니다.


PaginInfoVO

 

package com.example.demo.common;

import java.util.List;

public class PagingInfoVO extends BaseVO {
  private int pageNum;
  private int pageTotalCount;
  private int pageRowCount = 9;
  private int lastPageNum = 0;

  public PagingInfoVO(){
    this.pageNum = 1;
    this.pageTotalCount = 0;
    this.lastPageNum = 0;
  }

  public PagingInfoVO(List list, int pageRowCount) {
    if (list != null && list.size() > 0){
      PagingInfoEntity info = (PagingInfoEntity)list.get(0);
      if (info != null){ // 게시글이 널이 아니면
        this.pageNum = info.getPageNum();
        this.pageTotalCount = info.getPageTotalCount();

        if(info.getPageTotalCount() % pageRowCount > 0){ //pageRowCount = 9 의 나머지 > 0
          this.lastPageNum = (info.getPageTotalCount() / pageRowCount) + 1; // +1
        }else{
          this.lastPageNum = info.getPageTotalCount() / pageRowCount;
        }
      }
      this.pageRowCount = pageRowCount;
    }
  }

  public PagingInfoVO(int pageNum, int pageTotalCount, int pageRowCount) {
    this.pageNum = pageNum;
    this.pageTotalCount = pageTotalCount;
    this.pageRowCount = pageRowCount;

    if (pageTotalCount > 0){ // 게시글 총 개수 > 0
      if(pageTotalCount % pageRowCount > 0){
        this.lastPageNum = (pageTotalCount / pageRowCount) + 1;
      }else{
        this.lastPageNum = pageTotalCount / pageRowCount;
      }

      this.pageRowCount = pageRowCount;
    }

  }

  public int getPageNum() {
    return pageNum;
  }

  public void setPageNum(int pageNum) {
    this.pageNum = pageNum;
  }

  public int getPageTotalCount() {
    return pageTotalCount;
  }

  public void setPageTotalCount(int pageTotalCount) {
    this.pageTotalCount = pageTotalCount;
  }

  public int getPageRowCount() {
    return pageRowCount;
  }

  public void setPageRowCount(int pageRowCount) {
    this.pageRowCount = pageRowCount;
  }

  public int getLastPageNum() {
    return lastPageNum;
  }

  public void setLastPageNum(int lastPageNum) {
    this.lastPageNum = lastPageNum;
  }
}

 

PagingInfoVO 클래스는 BaseVO 클래스를 상속받아 사용되며, 페이징 정보를 나타내는 속성과 메서드를 포함하고 있습니다.

 

private int pageNum; : pageNum이라는 int 타입의 속성을 선언합니다. 이 속성은 현재 페이지 번호를 나타냅니다.

private int pageTotalCount; : pageTotalCount라는 int 타입의 속성을 선언합니다. 이 속성은 전체 페이지 개수를 나타냅니다.

private int pageRowCount = 9; : pageRowCount라는 int 타입의 속성을 선언하고, 기본값으로 9를 할당합니다. 이 속성은 페이지당 행의 개수를 나타냅니다.

private int lastPageNum = 0; : lastPageNum이라는 int 타입의 속성을 선언하고, 기본값으로 0을 할당합니다. 이 속성은 마지막 페이지 번호를 나타냅니다.

public PagingInfoVO() : 기본 생성자로, pageNum을 1, pageTotalCount와 lastPageNum을 0으로 초기화합니다.

public PagingInfoVO(List list, int pageRowCount) : 리스트와 페이지당 행의 개수를 받아 페이징 정보를 설정하는 생성자입니다. 리스트의 첫 번째 요소인 PagingInfoEntity 객체에서 페이지 번호와 전체 페이지 개수를 가져와 설정합니다. 또한, 페이지 개수를 계산하여 lastPageNum을 설정합니다.

public PagingInfoVO(int pageNum, int pageTotalCount, int pageRowCount) : 페이지 번호, 전체 페이지 개수, 페이지당 행의 개수를 받아 페이징 정보를 설정하는 생성자입니다. 각각의 값을 속성에 할당하고, lastPageNum을 계산하여 설정합니다.

나머지 getter와 setter 메서드들은 각 속성의 값을 반환하거나 설정하는 역할을 합니다.

 

PagingInfoVO 클래스는 페이지 정보를 나타내는 객체로 사용됩니다. 페이징 처리를 위한 필요한 정보들을 가지고 있으며, 생성자를 통해 초기화할 수 있습니다.

 


PagingInfoEntity

 

package com.example.demo.common;

public class PagingInfoEntity {
    private Integer pageNum; // 게시글 번호
    private Integer recordNum; // 레코드 번호
    private Integer pageTotalCount; // 게시글 총 개수


    public Integer getPageNum() {
        return pageNum;
    }

    public void setPageNum(Integer pageNum) {
        this.pageNum = pageNum;
    }

    public Integer getRecordNum() {
        return recordNum;
    }

    public void setRecordNum(Integer recordNum) {
        this.recordNum = recordNum;
    }

    public Integer getPageTotalCount() {
        return pageTotalCount;
    }

    public void setPageTotalCount(Integer pageTotalCount) {
        this.pageTotalCount = pageTotalCount;
    }
}

 

PagingInfoEntity 클래스는 페이징 정보를 담는 속성과 메서드를 가지고 있습니다.

 

private Integer pageNum; : pageNum이라는 Integer 타입의 속성을 선언합니다. 이 속성은 게시글 번호를 나타냅니다.

private Integer recordNum; : recordNum이라는 Integer 타입의 속성을 선언합니다. 이 속성은 레코드 번호를 나타냅니다.

private Integer pageTotalCount; : pageTotalCount라는 Integer 타입의 속성을 선언합니다. 이 속성은 게시글 총개수를 나타냅니다.

public Integer getPageNum() {...} : pageNum 속성의 값을 반환하는 메서드입니다.

public void setPageNum(Integer pageNum) {...} : pageNum 속성의 값을 설정하는 메서드입니다.

public Integer getRecordNum() {...} : recordNum 속성의 값을 반환하는 메서드입니다.

public void setRecordNum(Integer recordNum) {...} : recordNum 속성의 값을 설정하는 메서드입니다.

public Integer getPageTotalCount() {...} : pageTotalCount 속성의 값을 반환하는 메서드입니다.

public void setPageTotalCount(Integer pageTotalCount) {...} : pageTotalCount 속성의 값을 설정하는 메서드입니다.

 

PagingInfoEntity 클래스는 페이징 정보를 담는 객체로 사용됩니다. 게시글 번호, 레코드 번호, 게시글 총 개수 등의 정보를 가지고 있으며, 각각의 값을 getter와 setter 메서드를 통해 설정하거나 반환할 수 있습니다.

 


ResponseResult

 

package com.example.demo.common;

import com.fasterxml.jackson.annotation.JsonRootName;

import java.util.HashMap;


//@JsonRootName ->
//Class, Enum, Interface위에 선언되어 해당 클래스가 wrapping 될 수 있다면, value가 root wrapper로 처리 되도록 한다.
//이 애노테이션은 ObjectMapper에서도 함께 enable.(SerializationFeature.WRAP_ROOT_VALUE) 처리해주어야 동작한다.
//주의할 점은 ObjectMapper에서만 처리하고 @JsonRootName 애노테이션을 붙여주지 않는다면 기본으로 root wrapper는 클래스의
// 이름으로 처리가 된다.(이러한 처리는 깔끔하지 못하다.)
@JsonRootName(value="responseResult")
public class ResponseResult {

    public static final String RESULT_SUCCESS = "success"; // 성공 시
    public static final String RESULT_FAIL = "fail"; // 실패 시
    public static final String RESULT_FAIL_VALIDATION = "failValidation"; // 실패 확인
    public static final String RESULT_FAIL_DUPLICATE = "failDuplicate"; // 중복 실패
    public static final String RESULT_FAIL_CONSTRAINT_VIOLATION = "failConstraintViolation"; // 실패 사항 제약 위반

    private String status = ResponseResult.RESULT_SUCCESS;
    private String msg;
    private HashMap<String, Object> meta;
    
    public void addMetaItem(String key, Object value){
    	if (meta == null){
    		meta = new HashMap<String, Object>();
    	}
    	meta.put(key, value);
    }
    //HashMap 순회하기
    //entrySet => key, value의 값이 모두 필요한 경우 사용
    //ketSet => key의 값만 필요한 경우 사용

    public HashMap<String, Object> getMeta() {
        HashMap<String, Object> returnMap = new HashMap<String, Object>();
        if (meta != null){
            for( String key : meta.keySet() ){ // key 값만
                returnMap.put(key, meta.get(key)); // key값과, value값 담아준다.
            }
        }

        return returnMap;
    }

    public void setMeta(HashMap<String, Object> meta) {
        HashMap<String, Object> returnMap = new HashMap<String, Object>();
        if (meta != null){
            for( String key : meta.keySet() ){ // keySet
                returnMap.put(key, meta.get(key));
            }
        }

        this.meta = returnMap; // 들어오는 값 HashMap에 저장
    }

    public String getStatus() {
        return status; // 성공 시
    }

    public void setStatus(String status) {
        this.status = status;

        if (this.status.equals(RESULT_FAIL_DUPLICATE)){ // 중복 시 안내 메시지
            this.msg = "Duplicated data.";
        }else if(this.status.equals(RESULT_FAIL_CONSTRAINT_VIOLATION)){ // 제약 위반 시 안내 메시지
            this.msg = "constraint violation data";
        }



    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

}

 

ResponseResult 클래스는 API 응답 결과를 나타내는 데 사용됩니다.

 

@JsonRootName(value="responseResult") : 이 애노테이션은 Jackson 라이브러리에서 JSON 직렬화/역직렬화 시 루트 엘리먼트의 이름을 설정하는 데 사용됩니다. 여기서는 "responseResult"라는 이름으로 설정되어 있습니다.

public static final String RESULT_SUCCESS = "success"; : 성공 시를 나타내는 상수입니다.

public static final String RESULT_FAIL = "fail"; : 실패 시를 나타내는 상수입니다.

public static final String RESULT_FAIL_VALIDATION = "failValidation"; : 실패 및 유효성 검사 오류를 나타내는 상수입니다.

public static final String RESULT_FAIL_DUPLICATE = "failDuplicate"; : 중복 실패를 나타내는 상수입니다.

public static final String RESULT_FAIL_CONSTRAINT_VIOLATION = "failConstraintViolation"; : 제약 위반 실패를 나타내는 상수입니다.

private String status = ResponseResult.RESULT_SUCCESS; : 응답 상태를 나타내는 속성입니다. 기본값은 RESULT_SUCCESS로 설정되어 있습니다.

private String msg; : 응답 메시지를 나타내는 속성입니다.

private HashMap <String, Object> meta; : 부가적인 메타 데이터를 담기 위한 해시맵 속성입니다.

public void addMetaItem(String key, Object value) {...} : 메타 데이터에 항목을 추가하는 메서드입니다. 지정된 키와 값으로 해시맵에 항목을 추가합니다.

public HashMap <String, Object> getMeta() {...} : 메타 데이터 해시맵을 반환하는 메서드입니다. 반환되는 해시맵은 새로운 객체로 생성되며, 원본 해시맵의 수정을 방지합니다.

public void setMeta(HashMap <String, Object> meta) {...} : 메타 데이터 해시맵을 설정하는 메서드입니다. 전달된 해시맵의 키와 값들을 복사하여 새로운 해시맵을 생성하여 속성에 할당합니다.

public String getStatus() {...} : 응답 상태를 반환하는 메서드입니다.

public void setStatus(String status) {...} : 응답 상태를 설정하는 메서드입니다. 상태에 따라 메시지도 자동으로 설정됩니다.

public String getMsg() {...} : 응답 메시지를 반환하는 메서드입니다.

public void setMsg(String msg) {...} : 응답 메시지를 설정하는 메서드입니다.

 

ResponseResult 클래스는 API 응답 결과를 표현하기 위한 클래스로 사용됩니다. 상태, 메시지, 메타 데이터 등의 정보를 가지고 있으며, getter와 settter 메서드를 통해 값을 설정하거나 반환할 수 있습니다. 또한 '@JsonRootName' 애노테이션을 통해 JSON 직렬화 시 루트 엘리먼트의 이름을 설정할 수 있습니다.

 


ResponseResultList

 

package com.example.demo.common;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ResponseResultList extends ResponseResult {
    private List body = null;

    private int egovExpressStartPageNum = 0;

    public int getEgovExpressStartPageNum() {
        return egovExpressStartPageNum;
    }

    public void setEgovExpressStartPageNum(int egovExpressStartPageNum) {
        this.egovExpressStartPageNum = egovExpressStartPageNum;
    }

    // addAll - ArrayList의 메소드로 인자로 전달되는 Collection 객체의 모든 아이템을 리스트에 추가한다.
    public List getBody() {
        List resultList = new ArrayList();
        resultList.addAll(body); // body에 들어오는 값을 resultList에 담아서 return 해준다.
//        for (int i=0;i<body.size();i++){
//            resultList.add(body.get(i));
//        }

        return resultList;
    }

    public void setBody(List body) {
        List resultList = new ArrayList();
        if (body != null){ // body가 null이 아니면
            resultList.addAll(body);
        }

        this.body = resultList; // body에 resultList 즉, null이 아닌 값을 body에 지정
    }

    public void setPagingInfo(PagingInfoVO vo) {
        if (this.getMeta() == null) {
            this.setMeta(new HashMap<String, Object>());
        }

//        if (vo.getPageNum() == null){
//            vo.setPageNum(0);
//        }
//
//        if (vo.getPageTotalCount() == null){
//            vo.setPageTotalCount(0);
//        }


        this.addMetaItem("pagingInfo", vo);
    }
}

 

ResponseResultList 클래스는 API 응답 결과에 목록 형태의 데이터를 추가로 표함 하는 데 사용됩니다.

 

private List body = null; : 목록 형태의 데이터를 담는 속성입니다. 초기값은 null로 설정되어 있습니다.

private int egovExpressStartPageNum = 0; : egovExpressStartPageNum을 나타내는 속성입니다.

public int getEgovExpressStartPageNum() {...} : egovExpressStartPageNum 값을 반환하는 메서드입니다.

public void setEgovExpressStartPageNum(int egovExpressStartPageNum) {...} : egovExpressStartPageNum 값을 설정하는 메서드입니다.

public List getBody() {...} : 목록 데이터를 반환하는 메서드입니다. 반환되는 목록은 새로운 ArrayList 객체로 생성되며, 원본 목록의 수정을 방지합니다.

public void setBody(List body) {...} : 목록 데이터를 설정하는 메서드입니다. 전달된 목록의 값을 새로운 ArrayList 객체에 복사하여 속성에 할당합니다.

public void setPagingInfo(PagingInfoVO vo) {...} : 페이징 정보를 설정하는 메서드입니다. 메타 데이터에 페이징 정보를 추가합니다.

 

ResponseResultLIst 클래스는 ResponseResult 클래스를 상속받아 API 응답 결과를 나타내는 데 사용됩니다.

추가로 목록 데이터와 페이징 정보를 처리할 수 있는 기능을 제공합니다.

 

지금까지 공통 파일로 사용되는 common 디렉터리 파일 구조 및 해석을 해보았습니다.

그럼 바로 Item 디렉터리 코드를 알아보겠습니다.

 


Bean (ItemSearchVO , ItemVO)

 

< ItemSearchVO >

@Data // @Data => getter,setter 등을 @Data 어노테이션 하나로 사용 (lombok)
@NoArgsConstructor
@AllArgsConstructor
public class ItemSearchVO extends PagingInfoVO {
    private long iidx = -2;
    private String title = "";
    private String content = "";
    private String author = "";
}

 

ItemSearchVO는 PagingInfoVO 클래스를 상속받습니다.

 

private long iidx = -2; : 아이템 ID를 나타내는 속성입니다. 기본값은 -2로 설정되어 있습니다.

private String title = ""; : 제목을 나타내는 속성입니다. 기본값은 빈 문자열("")로 설정되어 있습니다.

private String content = ""; : 내용을 나타내는 속성입니다. 기본값은 빈 문자열("")로 설정되어 있습니다.

private String author = ""; : 작성자를 나타내는 속성입니다. 기본값은 빈 문자열("")로 설정되어 있습니다.

 

ItemSearchVO 클래스는 검색 조건을 담기 위한 클래스로 사용됩니다. 위의 속성들은 검색 조건으로 활용될 수 있으며, Lombok의 어노테이션을 통해 간편하게 getter와 setter가 생성됩니다. 또한 부모 클래스인 PagingInfoVO에서 페이징 정보를 상속받아 페이징 처리를 할 수 있습니다.

 

 

<ItemVO>

@Data
public class ItemVO {
    private long iidx = -2;
    private String title = "";
    private String content = "";
    private String author = "";
}

 

ItemVO 클래스는 아이템에 대한 정보를 담기 위한 클래스로 사용됩니다. 

 


Controller

 

package com.example.demo.item.controller;

import com.example.demo.common.PagingInfoVO;
import com.example.demo.common.ResponseResultList;
import com.example.demo.item.bean.ItemSearchVO;

import com.example.demo.item.service.ItemService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;


@Controller
public class ItemController {

    @Autowired // DI
    private ItemService itemService;

    // List
    @RequestMapping(value = "/item/list", method = RequestMethod.GET) // get mapping
    public String ItemList(
            HttpServletRequest request,
            Model model,
            ItemSearchVO vo
    ){
        /*
        session example
        Session session = request.getSession();
        if(session == null) return "/login_fail";
         */

        ResponseResultList resultList = itemService.getItemList(vo);

        // Paging migration(이주) 필요 시 형변환으로 꺼내서 사용 가능
        PagingInfoVO pagingInfo = (PagingInfoVO) resultList.getMeta().get("pagingInfo");

        // Paging migration 필요 없을 시
        model.addAttribute("pagingInfo", resultList.getMeta().get("pagingInfo"));
        model.addAttribute("dataMap", resultList.getBody()); // 기존 데이터 리스트 출력

        return "/item/list";
    }

    // Search
    @RequestMapping(value = "/item/search", method = RequestMethod.GET)
    public String ItemSearch(
            HttpServletRequest request,
            Model model,
            ItemSearchVO vo
    ){
        /*
        session example
        Session session = request.getSession();
        if(session == null) return "/login_fail";
         */

        // 원래는 view에서 넘어온 검색어, 검색타입이 vo에 들어가있어야 함. 편의상 여기서 설정
        vo.setStype("제목");
        vo.setSdata("제목 예시입니다.");

        ResponseResultList searchItemList = itemService.getSearchItemList(vo); // 검색 타입이 들어오면 response 타입으로 보내줘야함
        model.addAttribute("pagingInfo", searchItemList.getMeta().get("pagingInfo"));
        model.addAttribute("dataMap", searchItemList.getBody());

        return "/search/list";
    }
}

 

1. ItemList 메서드:

  • URL 경로 "/item/list"에 GET 요청이 들어오면 실행됩니다.
  • HttpServletRequest, Model, ItemSearchVO 객체를 매개변수로 받습니다.
  • itemService.getItemList(vo)를 호출하여아이템 목록을 가져옵니다.
  • 가져온 결과를 ResponseResultList 객체인 resultList에 저장합니다.
  • 페이징 정보를 PagingInfoVO 객체로 형변환하여 pagingInfo에 저장합니다.
  • 모델에 페이징 정보와 데이터 리스트를 추가하고, "/item/list" 뷰로 이동합니다.

 

2. ItemSearch 메서드:

  • URL 경로 "/item/search"에 GET 요청이 들어오면 실행됩니다.
  • HttpServletRequest, Model, ItemSearchVO 객체를 매개변수로 받습니다.
  • vo 객체에 검색어와 검색 타입을 설정합니다.
  • itemService.getSearchItemList(vo)를 호출하여 검색된 아이템 목록을 가져옵니다.
  • 가져온 결과를 ResponseResultList 객체인 searchItemList에 저장합니다.
  • 모델에 페이징 정보와 검색된 데이터 리스트를 추가하고, "/search/list" 뷰로 이동합니다.

 


ItemEntity

 

package com.example.demo.item.entity;

import com.example.demo.common.PagingInfoEntity;
import com.example.demo.item.bean.ItemVO;
import jakarta.persistence.Entity;
import lombok.Getter;

@Entity
@Getter
public class ItemEntity extends PagingInfoEntity {

    private long iidx = -2;
    private String title = "";
    private String content = "";
    private String author = "";

    public ItemVO toItemVO() {
        ItemVO vo = new ItemVO();
        vo.setIidx(this.getIidx());
        vo.setTitle(this.getTitle());
        vo.setContent(this.getContent());
        vo.setAuthor(this.getAuthor());
        return vo;
    }
}

 

ItemEntity 클래스는 아이템을 나타내는 엔티티(Entity)로 동작합니다.

편의성을 위해 Lombok을 사용했고, ItemEntity 클래스는 PagingInfoEntity 클래스를 상속합니다.

 

  • 1. 필드:

    iidx: 아이템 ID를 나타내는 필드입니다.
    title: 아이템 제목을 나타내는 필드입니다.
    content: 아이템 내용을 나타내는 필드입니다.
    author: 아이템 작성자를 나타내는 필드입니다.

 


  • 2. toItemVO 메서드:

    ItemVO 객체로 변환하는 메서드입니다.
    ItemVO 객체를 생성하고, ItemEntity 객체의 필드 값을 ItemVO 객체에 설정합니다.
    설정된 ItemVO 객체를 반환합니다.

 

 

ItemEntity 클래스는 데이터베이스의 아이템 테이블과 매핑되는 엔티티로 사용됩니다. 필드는 아이템의 속성을 나타내며, 

'toItemVO' 메서드를 통해 엔티티 객체를 ItemVO 객체로 변환할 수 있습니다.

 


ItemServiceImpl

 

package com.example.demo.item.service;

import com.example.demo.common.PagingInfoVO;
import com.example.demo.common.ResponseResultList;
import com.example.demo.item.bean.ItemSearchVO;
import com.example.demo.item.bean.ItemVO;
import com.example.demo.item.dao.ItemDAO;
import com.example.demo.item.entity.ItemEntity;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ItemServiceImpl implements ItemService{

    @Autowired// DI
    private ItemDAO itemDAO;

    @Override
    public ResponseResultList getItemList(ItemSearchVO vo) {
        Map<String, Object> param = new HashMap<>();
        /*
            조건 있을시에는
            param.put("sdata", vo.getSdata())
         */
        Integer itemListTotalCount = itemDAO.getItemListTotalCount(param);
        List<ItemEntity> itemList = itemDAO.getItemList(param);

        // Paging
        PagingInfoVO pagingInfoVO = new PagingInfoVO(vo.getPageNum(), itemListTotalCount, vo.getPageRowCount());

        List<ItemVO> dataList = new ArrayList<>();

        for(ItemEntity ent : itemList) {
            // entity -> vo
            dataList.add(ent.toItemVO());
        }

        ResponseResultList resultList = new ResponseResultList();
        resultList.setBody(dataList);
        resultList.setPagingInfo(pagingInfoVO);
        return resultList;
    }

    @Override
    public ResponseResultList getSearchItemList(ItemSearchVO vo) {
        Map<String, Object> param = new HashMap<>();
        param.put("stype", vo.getStype());
        param.put("sdata", vo.getSdata());

        Integer itemSearchListTotalCount = itemDAO.getItemSearchListTotalCount(param);
        List<ItemEntity> itemSearchList = itemDAO.getItemSearchList(param);

        PagingInfoVO pagingInfoVO = new PagingInfoVO(vo.getPageNum(), itemSearchListTotalCount, vo.getPageRowCount());
        List<ItemVO> dataList = new ArrayList<>();

        for(ItemEntity ent : itemSearchList) {
            dataList.add(ent.toItemVO());
        }

        ResponseResultList resultList = new ResponseResultList();
        resultList.setBody(dataList);
        resultList.setPagingInfo(pagingInfoVO);
        return resultList;
    }
}

 

ItemServcieImpl 클래스는 아이템 관련 비즈니스 로직을 처리합니다.

 

  • 필드:

    ItemDAO itemDAO: 아이템 데이터에 접근하기 위한 DAO 객체입니다.

 

  • getItemList 메서드:

    아이템 목록을 조회하는 메서드입니다.
    조회 조건이 주어진다면 param 맵에 해당 조건을 설정합니다.
    itemDAO.getItemListTotalCount를 호출하여 아이템 목록의 총개수를 가져옵니다.
    itemDAO.getItemList를 호출하여 아이템 목록을 가져옵니다.
    페이징 정보를 생성하여 pagingInfoVO 객체에 저장합니다.
    아이템 목록을 ItemVO 객체로 변환하여 dataList에 추가합니다.
    ResponseResultList 객체를 생성하고, 데이터 목록과 페이징 정보를 설정합니다. 그리고 결과 객체를 반환합니다.

 

  • getSearchItemList 메서드:

    검색된 아이템 목록을 조회하는 메서드입니다.
    검색 조건을 param 맵에 설정합니다.
    itemDAO.getItemSearchListTotalCount를 호출하여 검색된 아이템 목록의 총개수를 가져옵니다.
    itemDAO.getItemSearchList를 호출하여 검색된 아이템 목록을 가져옵니다.
    페이징 정보를 생성하여 pagingInfoVO 객체에 저장합니다.
    검색된 아이템 목록을 ItemVO 객체로 변환하여 dataList에 추가합니다.
    ResponseResultList 객체를 생성하고, 데이터 목록과 페이징 정보를 설정합니다. 그리고 결과 객체를 반환합니다.

 

ItemServiceImpl 클래스는 ItemService 인터페이스를 구현하며, 실제 아이템 관련 비즈니스 로직을 처리합니다. ItemDAO를 사용하여 데이터베이스에서 아이템 목록을 조회하고 결과를 ResponseResultList 객체에 담아 반환합니다.

 


ItemDAO

 

package com.example.demo.item.dao;

import com.example.demo.item.entity.ItemEntity;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

@Repository
public interface ItemDAO {

    Integer getItemListTotalCount(Map param); //게시물 토탈 개수

    List<ItemEntity> getItemList(Map param); // 게시물 목록

    Integer getItemSearchListTotalCount(Map param); // 검색 게시물 토탈 개수

    List<ItemEntity> getItemSearchList(Map param); // 검색 게시물 목록
}

 

ItemDAO 인터페이스는 아이템 데이터에 접근하기 위한 DAO의 역할을 정의합니다.

 

getItemListTotalCount:

 

 게시물의 총 개수를 조회하는 메서드입니다. 

param 맵을 인자로 받아와서 필요한 조회 조건을 설정합니다. 

반환 값은 게시물의 총 개수를 나타내는 정수(Integer)입니다.



getItemList: 

 

게시물 목록을 조회하는 메서드입니다. 

param 맵을 인자로 받아와서 필요한 조회 조건을 설정합니다

. 반환 값은 ItemEntity 객체의 리스트(List)입니다.



getItemSearchListTotalCount: 

 

검색된 게시물의 총 개수를 조회하는 메서드입니다. 

param 맵을 인자로 받아와서 필요한 조회 조건을 설정합니다. 

반환 값은 검색된 게시물의 총 개수를 나타내는 정수(Integer)입니다.



getItemSearchList: 

 

검색된 게시물 목록을 조회하는 메서드입니다. 

param 맵을 인자로 받아와서 필요한 조회 조건을 설정합니다. 

반환 값은 ItemEntity 객체의 리스트(List)입니다.


ItemDAO 인터페이스는 @Repository 어노테이션을 사용하여 빈으로 등록되며, 데이터 베이스와의 상호작용을 담당합니다. 

 


mapper

 

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.gmedia.archives.item.dao.ItemDAO">

  <!-- 아이템 정보 -->
  <resultMap id="resultItem" type="com.example.demo.item.entity.ItemEntity">
    <result property="pageNum"          	                column="page_num"/>
    <result property="recordNum"        	                column="record_num"/>
    <result property="pageTotalCount"   	                column="page_totalcount"/>

    <result property="iidx"          	                    column="iidx"/>
    <result property="title"          	                    column="title"/>
    <result property="description"          	            column="description"/>
    <result property="author"          	                    column="author"/>
  </resultMap>

  <!-- 아이템 리스트 sql 생략 -->

  <!-- 검색 조건에 부합하는 아이템 갯수 -->
 <select id="getItemSearchListTotalCount" resultType="Integer">
    select count(distinct item.iidx) <!--중복된 값 제거 후 iidx 열에 대해 고유한 값의 개수를 반환-->
    from item
    where 1=1
    <if test="stype != null and stype != ''">
      <if test="stype == 'title'">
        AND item.title like CONCAT('%', #{sdata}, '%')
      </if>
      <if test="stype == 'content'">
        AND item.content like CONCAT('%', #{sdata}, '%')
      </if>
    </if>
 </select>

  <select id="getItemSearchList" resultType="resultItem">
    select
    *
    from item
    where 1=1
    <if test="stype != null and stype != ''">
      <if test="stype == 'title'">
        AND item.title like CONCAT('%', #{sdata}, '%')
      </if>
      <if test="stype == 'content'">
        AND item.content like CONCAT('%', #{sdata}, '%')
      </if>
    </if>
    order by item.iidx desc
    <if test="pageNum != null and pageNum != '' and pageRowCount != null and pageRowCount != ''">
      limit ${(pageNum - 1)*pageRowCount}, #{pageRowCount}
    </if>
  </select>





</mapper>

 

위 코드는 MyBatis의 매퍼 파일입니다. SQL 쿼리와 자바 객체 간의 매핑을 정의하는 역할을 합니다.

 

"getItemSearchListTotalCount" :

 

해당 쿼리는 검색 조건에 부합하는 아이템의 개수를 조회하는 역할을 합니다.

 

select 문: count(distinct item.iidx)를 사용하여 item 테이블의 iidx 열에서 중복된 값을 제거하고 고유한 값의 개수를 조회합니다. 이 값은 검색 조건에 부합하는 아이템의 개수를 나타냅니다.

from 절: item 테이블을 대상으로 조회를 수행합니다.

where 절: where 1=1은 항상 참인 조건으로, 다른 조건들과의 조합을 편리하게 하기 위해 사용됩니다.

동적 SQL (<if> 문): 검색 조건(stype과 sdata)이 존재하는 경우에만 조건을 추가합니다.

검색 조건이 title인 경우, item.title이 검색어(sdata)를 포함하는 경우를 조건으로 추가합니다.
검색 조건이 content인 경우, item.content가 검색어(sdata)를 포함하는 경우를 조건으로 추가합니다.

 

따라서 item 테이블에서 검색 조건에 부합하는 아이템의 개수를 조회합니다. 중복된 'iidx' 값을 제거하고 고유한 값의 개수를 반환합니다. 검색 조건은 title이나 content에 대한 부분 문자열 매칭으로 이루어집니다.

 


 

"getItemSearchList" :

 

해당 쿼리는 검색 조건에 부합하는 아이템의 목록을 조회하는 역할을 합니다. 

 

select 문: *을 사용하여 모든 열을 조회합니다.

from 절: item 테이블을 대상으로 조회를 수행합니다.

where 절: where 1=1은 항상 참인 조건으로, 다른 조건들과의 조합을 편리하게 하기 위해 사용됩니다.

동적 SQL (<if> 문): 검색 조건(stype과 sdata)이 존재하는 경우에만 조건을 추가합니다.

검색 조건이 title인 경우, item.title이 검색어(sdata)를 포함하는 경우를 조건으로 추가합니다.
검색 조건이 content인 경우, item.content가 검색어(sdata)를 포함하는 경우를 조건으로 추가합니다.
order by 절: item.iidx를 기준으로 내림차순으로 정렬합니다.

동적 SQL (<if> 문): 페이지 번호(pageNum)와 페이지 행 수(pageRowCount)가 존재하는 경우에만 limit 절을 추가합니다. pageNum과 pageRowCount를 사용하여 조회할 행의 범위를 지정합니다.

 

따라서 item 테이블에서 검색 조건에 부합하는 아이템의 목록을 조회합니다. 검색 조건은 title이나 content에 대한 부분 문자열 매칭으로 이루어집니다. 결과는 item.iidx를 기준으로 내림차순으로 정렬되며, 페이지 번호와 페이지 행 수에 따라 결과가 제한될 수 있습니다.

 


마치며

 

오늘은 Spring 프레임워크 기반으로 페이징 처리 공통 코드 해석에 대한 기록글을 작성했습니다.

다음 포스팅에서 뵙겠습니다.

728x90

'[ JAVA ] > JAVA Spring' 카테고리의 다른 글

[ Spring ] 싱글톤 컨테이너  (0) 2023.08.07
[ Spring ] MappingJacksonValue  (0) 2023.08.03
[ Spring ] Component Scan  (0) 2023.05.26
[ Spring ] AOP 개념 및 설정  (0) 2023.05.24
[ Spring Boot ] Spring boot 개요 및 설정  (0) 2023.05.08