1cm

자바 프로그래밍_Day_98_Framework : MyBatis 동적 쿼리 / 게시글 조회 본문

국비지원_Java/Java Programming_2

자바 프로그래밍_Day_98_Framework : MyBatis 동적 쿼리 / 게시글 조회

dev_1cm 2022. 1. 12. 04:50
반응형

2022. 01. 05

 

동적쿼리 / 게시글 조회 실습


> BoardServiceTest.java

@ParameterizedTest
@MethodSource
위 테스트에 파라미터를 전해줄 메소드를 만드는 작업

그리고 밑 부분에 메소드를 만들어 줌

 public static Stream<Arguments> filterProvider() {
 	// Stream 만들어서 리턴, 안에는 파라미터로 쓸 Arguments 리스트를 만들어서 넘겨줌
    return Stream.of(
    	Arguments.arguments((Object) new String[]{"B2", "B3"}),
        Arguments.arguments((Object) new String[]{"B1"})
        ); 
}

여기서 반환해주는 값을 가지고 파라미터로 쓸 것임

	@ParameterizedTest
	@MethodSource("filterProvider")
	public void getBoardCountTest(String[] filters) {
		int count = 0;
		
		// 현재 게시글의 갯수 가져오기
		count = service.getBoardCount(filters);
		
		System.out.println(count);
		
		// 게시글의 숫자는 양수이면서, 0보다 크다는 것이라고 가정
		assertThat(count).isPositive().isGreaterThan(0);
	}

그 다음 윗 부분 getBoardCountTest 수정


그리고 테스트 진행
파라미터가 여러 개 있으면 arguments 밑에 여러 개 만들어주면 된다.

@ParameterizedTest 
@MethodSource("filterProvider")
public void findAllTest(String[] filters) {
	// 배열로 데이터 가져오기 
    // filters에는 B2, B3 값을 가지고 있다고 가정할 수 있음 
    // String[] filters = new String[] {"B2", "B3"}; 
    	// -> request.getParameterValues("filter"); 
    List<Board> list = null; 
    
    // 리스트 얻어오기 (from. service 객체) 
    list = service.findAll(filters); 
    
    // System.out.println(list); 
    
    assertThat(list).isNotNull(); 
}

findAllTest도 수정해줬다.


- 페이징 적용
기존에는 rownum과 서브쿼리로 만들었는데, 조회되는 데이터가 바뀌면 쿼리문 여러 곳 수정해야한다는 불편함이 있었음
MyBatis에서는 RowBounds라는 클래스를 제공해줘서 그 클래스를 활용해 페이징 처리가 가능하다.

 @ParameterizedTest
 @MethodSource("filterProvider") 
 public void findAllTest(String[] filters) {
     // 배열로 데이터 가져오기 
     // filters에는 B2, B3 값을 가지고 있다고 가정할 수 있음 
     // String[] filters = new String[] {"B2", "B3"}; 
		// -> request.getParameterValues("filter"); 
    List<Board> list = null; 
    PageInfo pageInfo = null; 
    int listCount = 0; 
    
    listCount = service.getBoardCount(filters); 
    
    // 페이지 info가져오기(현재 페이지, 페이징 몇개 보여줄지, 전체 게시글의 수, 한 페이지에서 보여줄 수)
    pageInfo = new PageInfo(1, 10, listCount, 10);
    
    // 리스트 얻어오기 (from. service 객체) 
    list = service.findAll(filters, pageInfo); 
    
    // System.out.println(list); 
    
    System.out.println(list.size()); assertThat(list).isNotNull();
}

기존에 있던 파라미터에 파라미터 값만 추가시켜주면 됨


따로 쿼리문을 건들이지 않고 작업한다.

> BoardService.java

 public List<Board> findAll(String[] filters,PageInfo pageInfo) {
 	List<Board> list = null; 
    SqlSession session = getSession(); 
    
    // dao에게 findAll 요청 하면서 매개값 session, filter 넘겨줌 (추가로 pageInfo도) 
    list = dao.findAll(session, filters, pageInfo); 
    
    return list; 
}

pageInfo를 파라미터 값에 추가시켜주고

에러 해결을 위해

Dao의 파라미터 값도 추가시켜준다.

 @ParameterizedTest 
 @MethodSource("filterProvider")
 public void findAllTest(String[] filters) {
 	// 배열로 데이터 가져오기 
    // filters에는 B2, B3 값을 가지고 있다고 가정할 수 있음 
    // String[] filters = new String[] {"B2", "B3"};
    	// -> request.getParameterValues("filter"); 
    List<Board> list = null; 
    PageInfo pageInfo = null; 
    int listCount = 0; 
    
    listCount = service.getBoardCount(filters); 
    
    // 페이지 info가져오기(현재 페이지, 페이징 몇개 보여줄지, 전체 게시글의 수, 한 페이지에서 보여줄 수) 
    pageInfo = new PageInfo(1, 10, listCount, 10); 
    
    // 리스트 얻어오기 (from. service 객체) 
    list = service.findAll(filters, pageInfo); 
    
    // System.out.println(list); 
    System.out.println(list.size()); assertThat(list).isNotNull(); 
}




> PageInfo.java에 getter 추가시켜줌 or lombok @Getter를 달아준다.

 public int getListLimit() {
 	return listLimit; 
}
@Getter
public class PageInfo {
	private int currentPage;
    
    private int pageLimit; 
    
    private int listCount; 
    
    private int listLimit; 
    
    ...


다시 BoardDao.java로 넘어와서 코드작성해준다.

    /*
     * 기존에 Servlet/JSP에서는 쿼리문에서 rownum과 서브쿼리를 통해서 페이징 처리를 하였다.
     * 하지만 Mybatis에서는 페이징 처리를 위해 RowBounds라는 클래스를 제공해준다.
     * -> 쿼리문으로 조회되는 결과를 활용해서 RowBounds로 원하는 데이터만 가져옴
     * 
     * RowBounds의 객체를 생성할 때 offset과 limit 값을 전달해서 페이징 처리를 쉽게 구현한다.
     * (offset 만큼 건너 뛰고 limit만큼 가져온다.)
     * 
     * offset = 0, limit = 10
     * 	- 0개를 건너뛰고, 10개를 가져온다. (1번째 결과 ~ 10번째 결과까지)
     * offset = 10, limit = 10
     * 	- 10개를 건너뛰고, 10개를 가져온다. (11 ~ 20번째 데이터까지)
     * offset = 20, limit = 10
     * 	- 20개를 건너뛰고, 10개를 가져온다. (21 ~ 30번째 데이터까지)
     * 
     */

    int offset = (pageInfo.getCurrentPage() - 1) * pageInfo.getListLimit();
    int limit = pageInfo.getListLimit();
    RowBounds rowBounds = new RowBounds(offset, limit);

    return session.selectList("boardMapper.selectBoardListByFilters", map, rowBounds);
}

 



- 페이지 정보 가져올 수 있는 provider 생성
> BoardServiceTest.java

// 페이지 정보를 받아올 수 있는 Provider
public static Stream<Arguments> listProvider() {
    // Stream 만들어서 리턴, 안에는 파라미터로 쓸 Arguments 리스트를 만들어서 넘겨줌
    return Stream.of(
            // 페이지번호, 게시글 
        Arguments.arguments((Object) new String[]{"B2", "B3"}, 1, 5),
        Arguments.arguments((Object) new String[]{"B1"}, 1, 10),
        Arguments.arguments((Object) new String[]{"B1"}, 16, 2)
        );
}


좀 전에 작성했던 filterProvider 수정

@ParameterizedTest
@MethodSource("listProvider")
public void findAllTest(String[] filters, int currentPage, int expected) {
    // 배열로 데이터 가져오기
    // filters에는 B2, B3 값을 가지고 있다고 가정할 수 있음
    // String[] filters = new String[] {"B2", "B3"};	
        // -> request.getParameterValues("filter");
    List<Board> list = null;
    PageInfo pageInfo = null;
    int listCount = 0;

    listCount = service.getBoardCount(filters);

    // 페이지 info가져오기(현재 페이지, 페이징 몇개 보여줄지, 전체 게시글의 수, 한 페이지에서 보여줄 수)
    pageInfo = new PageInfo(currentPage, 10, listCount, 10);

    // 리스트 얻어오기 (from. service 객체)
    list = service.findAll(filters, pageInfo);

    assertThat(list).isNotNull();
    assertThat(list.size()).isPositive().isEqualTo(expected);
}



- 게시글 상세 조회

댓글에 대한 클래스를 model.vo 밑에 생성해준다.


그리고 이전에 mvc2 할때 썼던 클래스문 가져오기

package com.kh.mybatis.board.model.vo;

import java.util.Date;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Reply {
	private int no;
	
	private int boardNo;
	
	private int writerNo;
	
	private String writerId;
	
	private String content;	
	
	private Date createDate;
	
	private Date modifyDate;
}

Date 부분 넣을 때는 java.util에서 넣어주기

BoardServiceTest.java에 테스트 메소드 작성

 //게시글 상세 데이터 조회를 위한 테스트 메소드 생성 
 public void findBoardByNoTest() {
     // Board 객체 조회(service에서 findBoardByNo()메소드를 통해서) 
     Board board = null;

     // 보드 조회 (내 DB에 있는 보드 확인) 
     board = service.findBoardByNo(157); 

     // 검증
     assertThat(board).isNotNull(); 
 }

findBoardByNoTest에서 발생하는 에러 없애주기 (ctrl+1)
-> BoardService.java에 메소드 생성 후 Dao를 통해서 가져올 객체를 만들어준다.

public Board findBoardByNo(int no) {
    // 단건 조회
    Board board = null;
    SqlSession session = getSession();

    // board객체는 Dao를 통해서 가져옴
    board = dao.findBoardByNo(session, no);

    session.close();

    return board;
}



그 후 findBoardByNoTest에서 발생하는 에러 없애주기
BoardDao.java에도 메소드 생성


BoardDao.java

리턴은 나중에 수정

주석으로 막아뒀던 List<Reply> replies; 해제 시켜주고

java.util안에 있는 List를 import시켜준다. (같은 패키지 내에 있어서 import 필요 없다.)

- 기본적인 세팅 끝

다시 BoardDao.java로 와서 return값 수정

public Board findBoardByNo(SqlSession session, int no) {

    return session.selectOne("boardMapper.selectBoardByNo", no);
}



> board-mapper.xml

<!-- 게시글 조회 쿼리문 -->
<select id="selectBoardByNo" parameterType="_int" resultMap="boardDetailResultMap">
    <include refid="boardListSql" />
    <!-- B.NO가 파라미터로 전달되는 no와 같은 것만 조회 -->
    AND B.NO = #{no}
</select>


그리고 BoardServiceTest.java 에서 no값 일치하는 게시글 조회할 수 있게 수정

 //게시글 상세 데이터 조회를 위한 테스트 메소드 생성 
 public void findBoardByNoTest() {
     // Board 객체 조회(service에서 findBoardByNo()메소드를 통해서)
     Board board = null; 

     // 보드 조회 (내 DB에 있는 보드 확인)
     board = service.findBoardByNo(157); 

     // 검증 (no 값이 157과 동일한지 테스트 확인) 
     assertThat(board).isNotNull().extracting("no").isEqualTo(157); 
 }



- 댓글 포함해서 가져오기

Board.java에서의 Reply는 댓글 객체를 요소로 가지는 list로 가져오게 된다.
-> 조회되는 결과가 있으면 List객체에 요소를 담아주고, 없으면 빈 list객체를 가져온다.

BoardServiceTest.java에 assertThat 추가시켜주고 테스트 진행시 통과하지 않게 됨

-> BoardServiceTest에서 해줄 건 없고, board-mapper.xml에서 작업해준다.
방법 1) 쿼리문 각각 생성 (댓글, 게시글 쿼리문 따로) 후 하나의 ResultMap에서 mapping 작업
방법 2) 하나의 쿼리문으로 ResultMap에서 mapping 작업

config파일에서 댓글 데이터 타입에 대한 별칭을 부여해준다.


> board-mapper.xml

 <!-- 
 게시글 상세 보기 (댓글 포함) 
 
 1. 쿼리문을 각각 만들어서 하나의 ResultMap에서 매핑하는 방법 
 2. 하나의 쿼리문을 만들어서 하나의 ResultMap에서 매핑하는 방법 
 --> 
 <!-- 게시글 조회 쿼리문 --> 
 <select id="selectBoardByNo" parameterType="_int" resultMap="boardListResultMap"> 
	<include refid="boardListSql" />
    <!-- B.NO가 파라미터로 전달되는 no와 같은 것만 조회 -->
	AND B.NO = #{no}
 </select> 
     
 <!-- 댓글 조회 쿼리문 -->
 <select id="selectRepliesByBoardNo" parameterType="_int" resultType="Reply">
 <!-- 객체 조회 --> 
     SELECT R.NO, 
            R.BOARD_NO, 
            R.CONTENT, 
            M.ID, 
            R.CREATE_DATE, 
            R.MODIFY_DATE 
       FROM REPLY R 
       JOIN MEMBER M ON (R.WRITER_NO = M.NO)
      WHERE R.STATUS = 'Y' AND BOARD_NO=#{boardNo} 
      ORDER BY R.NO DESC
 </select>

selectRepliesByBoardNo의 resultType을 만들어준 별칭 Reply로 바꿔준다.

게시글 조회 쿼리문의 resultMap을 boardDetailResultMap라는 이름으로 수정해준 뒤 새로운 resultMap을 위쪽에 만들어준다.

<!-- 위에서 작성한 ResultMap을 extends를 활용해서 상속 후 확장하여 사용해준다. -->
<!-- 
    1. 쿼리문을 각각 만들어서 하나의 ResultMap에서 매핑하는 방법
        - extends 속성 : 다른 resultMap을 상속하는 속성이다.
        - collection : collection 태그는 컬렉션에 해당하는 필드에 조회 결과를 매핑할 때 사용한다.
                     : replies(Board.java)라는 필드가 collection으로 되어있어서 여기에 객체를 매핑하기 위한 태그
            - javaType 속성 : 어떤 자바 컬렉션 타입인지 명시해준다.
            - select 속성 : 컬렉션을 조회하고자 하는 쿼리문의 ID를 적어준다.
                         : 댓글 쿼리문 Id명
            - column 속성 : 컬렉션을 조회하고자 하는 쿼리문에 파라미터를 전달할 때 사용한다.

            * 1:1 관계인 객체를 조회해 오고 싶다면 association 태그를 사용해서 매핑해주면 된다.
<resultMap type="Board" extends="boardListResultMap" id="boardDetailResultMap">
    <collection property="replies" javaType="arraylist" select="selectRepliesByBoardNo" column="NO" />
    <!-- <association property="reply" javaType="Reply" select="..." column=".." /> -->
</resultMap>


조회된 값을 보면 댓글에 대한 객체가 list가 만들어져서 들어가는데 mapping이 되어있지 않은 것들이 있음
-> why? 조회하는 결과의 컬럼명과 매핑하려는 객체의 필드명과 다르기 때문
-> resultMap 만들어서 mapping 해주면 된다.

resultType -> resultMap으로 수정 후 replyResultMap으로 수정해줌
그리고 수정한 replyResultMap으로 새로운 resultMap 생성해준다.

<!-- Reply에서 매핑 안되는 부분들 mapping 시켜줌 -->
<resultMap type="Reply" id="replyResultMap">
    <id property="no" column="NO"/>
    <result property="boardNo" column="BOARD_NO"/>
    <result property="writerId" column="ID"/>
    <result property="content" column="CONTENT"/>
    <result property="createDate" column="CREATE_DATE"/>
    <result property="modifyDate" column="MODIFY_DATE"/>
</resultMap>




방법2) 하나의 쿼리문을 만들어서 ResultMap에서 매핑하는 방법

 <!-- 2. 하나의 쿼리문을 만들어서 하나의 ResultMap에서 매핑하는 방법 --> 
 <select id="selectBoardByNo" parameterType="_int" resultMap="boardDetailResultMap">
     SELECT B.NO AS B_NO, 
            B.TITLE AS B_TITLE, 
            M.ID AS B_ID,
            B.READCOUNT AS B_READCOUNT, 
            B.ORIGINAL_FILENAME AS B_ORIGINAL_FILENAME, 
            B.RENAMED_FILENAME AS B_RENAMED_FILENAME,
            B.CONTENT AS B_CONTENT,
            B.TYPE AS B_TYPE, 
            B.CREATE_DATE AS B_CREATE_DATE, 
            B.MODIFY_DATE AS B_MODIFY_DATE,
            R.NO AS R_NO, 
            R.BOARD_NO AS R_BOARD_NO, 
            R.CONTENT AS R_CONTENT,
            M2.ID AS R_ID,
            R.CREATE_DATE AS R_CREATE_DATE, 
            R.MODIFY_DATE AS R_MODIFY_DATE 
     FROM BOARD B 
     JOIN MEMBER M ON(B.WRITER_NO = M.NO)
     LEFT OUTER JOIN REPLY R ON(B.NO = R.BOARD_NO)
     LEFT OUTER JOIN MEMBER M2 ON(R.WRITER_NO = M2.NO)
     WHERE B.STATUS = 'Y' AND B.NO = #{no}
 </select>

SELECT 쿼리문을 위처럼 만들어 준 뒤 resultMap을 만들어준다.

 <!-- 
 	2. 하나의 쿼리문을 만들어서 하나의 ResultMap에서 매핑하는 방법
     <resultMap type="Board" id="boardDetailResultMap">
         <id property="no" column=""B_NO/> 
         <result property="title" column="B_TITLE"/> 
         <result property="writerId" column="B_ID"/>
         <result property="readCount" column="B_READCOUNT"/>
         <result property="originalFileName" column="B_ORIGINAL_FILENAME"/>
         <result property="renamedFileName" column="B_RENAMED_FILENAME"/> 
         <result property="content" column="B_CONTENT"/>
         <result property="type" column="B_TYPE"/> 
         <result property="createDate" column="B_CREATE_DATE"/> 
         <result property="modifyDate" column="B_MODIFY_DATE"/>

         <!-- 
            - 댓글에 대한 list를 받아와야 하기 때문에 collection을 추가해준다. (여러 개 값 조회) 
            - 댓글에 대한 resultMap을 만들어줘야 하기 때문에 replyResultMap을 새로 만들어 준다. 
            - B로 시작하는 애들은 한 번만 매핑이 되고, R로 시작하는 애들은 Reply객체로 매핑 후, 
                얘네를 담고 있는 arraylist로 만들어서 replies로 담아준다. 그 때 참고하는 것이 replyResultMap이다.
         --> 
         <collection property="replies" javaType="arraylist" resultMap="replyResultMap" /> 
 </resultMap> 
 
 <!-- 댓글에 대한 새로운 ResultMap, mapping 작업 -->
 <resultMap type="Reply" id="replyResultMap"> 
     <id property="no" column="R_NO"/>
     <result property="boardNo" column="R_BOARD_NO"/> 
     <result property="writerId" column="R_ID"/> 
     <result property="content" column="R_CONTENT"/> 
     <result property="createDate" column="R_CREATE_DATE"/> 
     <result property="modifyDate" column="R_MODIFY_DATE"/>
 </resultMap>
 -->


extends(상속) 적용하려는 경우

 <resultMap type="Board" id="boardListResultMap"> 
     <id property="no" column="NO"/>
     <result property="title" column="TITLE"/>
     <result property="writerId" column="ID"/>
     <result property="readCount" column="READCOUNT"/> 
     <result property="originalFileName" column="ORIGINAL_FILENAME"/> 
     <result property="renamedFileName" column="RENAMED_FILENAME"/> 
     <result property="content" column="CONTENT"/> 
     <result property="type" column="TYPE"/>
     <result property="createDate" column="CREATE_DATE"/>
     <result property="modifyDate" column="MODIFY_DATE"/>
 </resultMap> 
 
 <!-- 댓글에 대한 새로운 ResultMap, mapping 작업 -->
 <resultMap type="Reply" id="replyResultMap"> 
     <id property="no" column="NO"/>
     <result property="boardNo" column="BOARD_NO"/> 
     <result property="writerId" column="ID"/>
     <result property="content" column="CONTENT"/>
     <result property="createDate" column="CREATE_DATE"/> 
     <result property="modifyDate" column="MODIFY_DATE"/>
 </resultMap>

기존에 있었던 Board 아래에 댓글에 대한 mapping 작업해줌
그 다음 boardListResultMap을 상속하는 resultMap을 만들어준다.

	<!-- 상속 추가 후 기존 쿼리문 collection 제외 후 삭제 -->
	<resultMap type="Board" extends="boardListResultMap" id="boardDetailResultMap">
		<collection property="replies" javaType="arraylist" columnPrefix="R_" resultMap="replyResultMap" />
	</resultMap>


방법 1을 쓸 경우
장점 : 관리 편리, 쿼리문 수정 시 편하게 수정 가능
단점 : select 두 번 수행함

방법 2를 쓸 경우
장점 : select 한 번 수행
단점 : 쿼리문이 어려움 -> 유지보수 어려움


테스트 코드 수정
BoardServiceTest.java

 @ParameterizedTest @ValueSource(ints= {156, 157})
 //게시글 상세 데이터 조회를 위한 테스트 메소드 생성 
 public void findBoardByNoTest(int no) {
     // Board 객체 조회(service에서 findBoardByNo()메소드를 통해서) 
     Board board = null; 
     
     // 보드 조회 (내 DB에 있는 보드 확인) 
     board = service.findBoardByNo(no); 
     
     // 검증 (no 값이 157과 동일한지 테스트 확인) 
     assertThat(board).isNotNull().extracting("no").isEqualTo(no);
     assertThat(board.getReplies()).isNotNull(); 
     assertThat(board.getReplies().size()).isNotNull().isGreaterThan(0); 
 }




전체 코드

> mybatis-config.xml

... 
	<!-- typeAliases : Mybatis에서 사용하는 데이터 타입에 별칭 부여 -->
	<!-- VO/DTO 객체의 타입에 별칭을 선언하는 영역 -->
	<typeAliases>
		<typeAlias type="com.kh.mybatis.member.model.vo.Member" alias="Member"/>
		<typeAlias type="com.kh.mybatis.board.model.vo.Board" alias="Board"/>
		<typeAlias type="com.kh.mybatis.board.model.vo.Reply" alias="Reply"/>
	</typeAliases>
...




> BoardServiceTest.java

package com.kh.mybatis.board.model.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

import java.util.List;
import java.util.stream.Stream;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

import com.kh.mybatis.board.model.vo.Board;
import com.kh.mybatis.common.util.PageInfo;

class BoardServiceTest {
	private BoardService service;
	
	@BeforeEach
	public void setUp() throws Exception {
		// @BeforeEach : 아래 테스트 케이스들 작성 시 한번씩 수행
		// 그럴때마다 새로운 service 객체를 생성 후 넘겨주기
		service = new BoardService();
	}

	@Test
	@Disabled
	public void nothing() {
	}
	
	@Test
	@Disabled
	public void create() {
		// 정상적으로 setUp()에서 객체가 만들어져서 대입이 됐는지 확인 후 @Disabled
		assertThat(service).isNotNull();
	}
	
	@ParameterizedTest
	@CsvSource(
		value = {
			"is, null, null, 1",
			"null, 22, null, 2",
			"null, null, 테스트, 1",
			"null, null, null, 157"},
		nullValues = "null"
	)
	// Board 객체를 요소로 담고있는 list를 반환해주는 함수
	public void findAllTest(String writer, String title, String content, int expected) {
		List<Board> list = null;
		
		list = service.findAll(writer, title, content);
		
		// 각각 파라미터를 받는 것 외에는 Null 처리를 한다고 가정
		// title을 받을 때 writer, content는 null로 출력
        // 전부 null일 경우 다중<if>구문 에서 그다음에 있는 B.STATUS='Y'로 넘어가서 전체 데이터 조회된다.
//		String writer = null;
//		String title = null;
//		String content = "테스트";
		// findAllTest()의 파라미터 값으로 보내준 뒤 없애도 된다.
		
		assertThat(list).isNotNull();
		assertThat(list.size()).isPositive().isEqualTo(expected);
	}
	

	@ParameterizedTest
	@MethodSource("filterProvider")
	public void getBoardCountTest(String[] filters) {
		int count = 0;
		
		// 현재 게시글의 갯수 가져오기
		count = service.getBoardCount(filters);
		
		System.out.println(count);
		
		// 게시글의 숫자는 양수이면서, 0보다 크다는 것이라고 가정
		assertThat(count).isPositive().isGreaterThan(0);
	}
	
	@ParameterizedTest
	@MethodSource("listProvider")
	public void findAllTest(String[] filters, int currentPage, int expected) {
		// 배열로 데이터 가져오기
		// filters에는 B2, B3 값을 가지고 있다고 가정할 수 있음
		// String[] filters = new String[] {"B2", "B3"};	
			// -> request.getParameterValues("filter");
		List<Board> list = null;
		PageInfo pageInfo = null;
		int listCount = 0;
		
		listCount = service.getBoardCount(filters);
		
		// 페이지 info가져오기(현재 페이지, 페이징 몇개 보여줄지, 전체 게시글의 수, 한 페이지에서 보여줄 수)
		pageInfo = new PageInfo(currentPage, 10, listCount, 10);
		
		// 리스트 얻어오기 (from. service 객체)
		list = service.findAll(filters, pageInfo);
		
		assertThat(list).isNotNull();
		assertThat(list.size()).isPositive().isEqualTo(expected);
	}
	
	@ParameterizedTest
	@ValueSource(ints= {156, 157})
	//게시글 상세 데이터 조회를 위한 테스트 메소드 생성
	public void findBoardByNoTest(int no) {
		// Board 객체 조회(service에서 findBoardByNo()메소드를 통해서)
		Board board = null;
		
		// 보드 조회 (내 DB에 있는 보드 확인)
		board = service.findBoardByNo(no);
		
		// 검증 (no 값이 157과 동일한지 테스트 확인)
		assertThat(board).isNotNull().extracting("no").isEqualTo(no);
		assertThat(board.getReplies()).isNotNull();
		assertThat(board.getReplies().size()).isNotNull().isGreaterThan(0);
	}
	
	
	public static Stream<Arguments> filterProvider() {
		// Stream 만들어서 리턴, 안에는 파라미터로 쓸 Arguments 리스트를 만들어서 넘겨줌
		return Stream.of(
			Arguments.arguments((Object) new String[]{"B2", "B3"}),
			Arguments.arguments((Object) new String[]{"B1"})
			);
	}
	
	// 페이지 정보를 받아올 수 있는 Provider
	public static Stream<Arguments> listProvider() {
		// Stream 만들어서 리턴, 안에는 파라미터로 쓸 Arguments 리스트를 만들어서 넘겨줌
		return Stream.of(
				// 페이지번호, 게시글 
			Arguments.arguments((Object) new String[]{"B2", "B3"}, 1, 5),
			Arguments.arguments((Object) new String[]{"B1"}, 1, 10),
			Arguments.arguments((Object) new String[]{"B1"}, 16, 2)
			);
	}
}




> board-mapper.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- Mapper 설정 파일임을 선언 -->
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="boardMapper">
	<sql id="boardListSql">
		SELECT	B.NO,
		        B.TITLE,
		        M.ID,
		        B.READCOUNT,
		        B.ORIGINAL_FILENAME,
		        B.RENAMED_FILENAME,
		        B.CONTENT,
		        B.TYPE,
		        B.CREATE_DATE,
		        B.MODIFY_DATE
		FROM BOARD B
		JOIN MEMBER M ON(B.WRITER_NO = M.NO)
		WHERE B.STATUS = 'Y'
	</sql>

	<resultMap type="Board" id="boardListResultMap">
		<id property="no" column="NO"/>
		<result property="title" column="TITLE"/>
		<result property="writerId" column="ID"/>
		<result property="readCount" column="READCOUNT"/>
		<result property="originalFileName" column="ORIGINAL_FILENAME"/>
		<result property="renamedFileName" column="RENAMED_FILENAME"/>
		<result property="content" column="CONTENT"/>
		<result property="type" column="TYPE"/>
		<result property="createDate" column="CREATE_DATE"/>
		<result property="modifyDate" column="MODIFY_DATE"/>
	</resultMap>
	
	<!-- 댓글에 대한 새로운 ResultMap, mapping 작업 -->
	<resultMap type="Reply" id="replyResultMap">
		<id property="no" column="NO"/>
		<result property="boardNo" column="BOARD_NO"/>
		<result property="writerId" column="ID"/>
		<result property="content" column="CONTENT"/>
		<result property="createDate" column="CREATE_DATE"/>
		<result property="modifyDate" column="MODIFY_DATE"/>
	</resultMap>
	
	<!-- 위에서 작성한 ResultMap을 extends를 활용해서 상속 후 확장하여 사용해준다. -->
	<!-- 
		1. 쿼리문을 각각 만들어서 하나의 ResultMap에서 매핑하는 방법
			- extends 속성 : 다른 resultMap을 상속하는 속성이다.
			- collection : collection 태그는 컬렉션에 해당하는 필드에 조회 결과를 매핑할 때 사용한다.
						 : replies(Board.java)라는 필드가 collection으로 되어있어서 여기에 객체를 매핑하기 위한 태그
				- javaType 속성 : 어떤 자바 컬렉션 타입인지 명시해준다.
				- select 속성 : 컬렉션을 조회하고자 하는 쿼리문의 ID를 적어준다.
						 	 : 댓글 쿼리문 Id명
				- column 속성 : 컬렉션을 조회하고자 하는 쿼리문에 파라미터를 전달할 때 사용한다.
				
				* 1:1 관계인 객체를 조회해 오고 싶다면 association 태그를 사용해서 매핑해주면 된다.
	<resultMap type="Board" extends="boardListResultMap" id="boardDetailResultMap">
		<collection property="replies" javaType="arraylist" select="selectRepliesByBoardNo" column="NO" />
		<!-- <association property="reply" javaType="Reply" select="..." column=".." /> -->
	</resultMap>
	
	<!-- Reply에서 매핑 안되는 부분들 mapping 시켜줌 -->
	<resultMap type="Reply" id="replyResultMap">
		<id property="no" column="NO"/>
		<result property="boardNo" column="BOARD_NO"/>
		<result property="writerId" column="ID"/>
		<result property="content" column="CONTENT"/>
		<result property="createDate" column="CREATE_DATE"/>
		<result property="modifyDate" column="MODIFY_DATE"/>
	</resultMap>
	-->
	
	<!-- 
		2. 하나의 쿼리문을 만들어서 하나의 ResultMap에서 매핑하는 방법
	<resultMap type="Board" id="boardDetailResultMap">
		<id property="no" column=""B_NO/>
		<result property="title" column="B_TITLE"/>
		<result property="writerId" column="B_ID"/>
		<result property="readCount" column="B_READCOUNT"/>
		<result property="originalFileName" column="B_ORIGINAL_FILENAME"/>
		<result property="renamedFileName" column="B_RENAMED_FILENAME"/>
		<result property="content" column="B_CONTENT"/>
		<result property="type" column="B_TYPE"/>
		<result property="createDate" column="B_CREATE_DATE"/>
		<result property="modifyDate" column="B_MODIFY_DATE"/>
		<!-- 
			- 댓글에 대한 list를 받아와야 하기 때문에 collection을 추가해준다. (여러 개 값 조회)
			- 댓글에 대한 resultMap을 만들어줘야 하기 때문에 replyResultMap을 새로 만들어 준다.
			- B로 시작하는 애들은 한 번만 매핑이 되고, R로 시작하는 애들은 Reply객체로 매핑 후,
				얘네를 담고 있는 arraylist로 만들어서 replies로 담아준다. 그 때 참고하는 것이 replyResultMap이다.
		 -->
		<collection property="replies" javaType="arraylist" resultMap="replyResultMap" />
	</resultMap>
	
	<!-- 댓글에 대한 새로운 ResultMap, mapping 작업 -->
	<resultMap type="Reply" id="replyResultMap">
		<id property="no" column="R_NO"/>
		<result property="boardNo" column="R_BOARD_NO"/>
		<result property="writerId" column="R_ID"/>
		<result property="content" column="R_CONTENT"/>
		<result property="createDate" column="R_CREATE_DATE"/>
		<result property="modifyDate" column="R_MODIFY_DATE"/>
	</resultMap>
	-->
	
	<!-- 상속 추가 후 기존 쿼리문 collection 제외 후 삭제 -->
	<resultMap type="Board" extends="boardListResultMap" id="boardDetailResultMap">
		<collection property="replies" javaType="arraylist" columnPrefix="R_" resultMap="replyResultMap" />
	</resultMap>
	
	
	<select id="selectAll" parameterType="map" resultType="boardListResultMap">
		<!-- 1. 다중 <if>를 활용한 검색 기능 구현
		SELECT	B.NO,
				B.TITLE,
				M.ID,
				B.READCOUNT,
				B.ORIGINAL_FILENAME,
				B.RENAMED_FILENAME,
				B.CONTENT,
				B.CREATE_DATE,
				B.MODIFY_DATE
		FROM BOARD B
		JOIN MEMBER M ON(B.WRITER_NO = M.NO)
		WHERE B.STATUS = 'Y'
		
		<if test="writer != null">
		  <!-- writer가 Null이 아니면 아래 쿼리문 추가 시키는 구문, null이면 동작하지 않음 -->
		  <!-- 검색하는 내용만 포함되면 조회가능 -->
		  AND M.ID LIKE '%' || #{writer} || '%'
		</if>
		<if test="title != null">
		  <!-- title 값이 Null이 아니면 아래 쿼리문 추가 시키는 구문, null이면 동작하지 않음 -->
		  AND B.TITLE LIKE '%' || #{title} || '%'
		</if>
		<if test="content != null">
		  <!-- content 값이 Null이 아니면 아래 쿼리문 추가 시키는 구문, null이면 동작하지 않음 -->
		  AND B.CONTENT LIKE '%' || #{content} || '%'
		</if>
		-->
		 <!-- 
			2. <where>와 다중 <if>를 활용한 검색 기능 구현

		 SELECT	B.NO,
				B.TITLE,
				M.ID,
				B.READCOUNT,
				B.ORIGINAL_FILENAME,
				B.RENAMED_FILENAME,
				B.CONTENT,
				B.CREATE_DATE,
				B.MODIFY_DATE
		FROM BOARD B
		JOIN MEMBER M ON(B.WRITER_NO = M.NO)
		<where>
			<if test="writer != null">
			  M.ID LIKE '%' || #{writer} || '%'
			</if>
			<if test="title != null">
			  AND B.TITLE LIKE '%' || #{title} || '%'
			</if>
			<if test="content != null">
			  AND B.CONTENT LIKE '%' || #{content} || '%'
			</if>
			AND B.STATUS = 'Y'
		</where>
		  -->
		 
		 <!-- 
		 	3.<trim>과 다중<if>를 활용한 검색 기능 구현
			SELECT	B.NO,
					B.TITLE,
					M.ID,
					B.READCOUNT,
					B.ORIGINAL_FILENAME,
					B.RENAMED_FILENAME,
					B.CONTENT,
					B.CREATE_DATE,
					B.MODIFY_DATE
			FROM BOARD B
			JOIN MEMBER M ON(B.WRITER_NO = M.NO)
		  <trim prefix="WHERE" prefixOverrides="AND|OR">
		  	<if test="writer != null">
			  M.ID LIKE '%' || #{writer} || '%'
			</if>
			<if test="title != null">
			  AND B.TITLE LIKE '%' || #{title} || '%'
			</if>
			<if test="content != null">
			  AND B.CONTENT LIKE '%' || #{content} || '%'
			</if>
			
			AND B.STATUS = 'Y'
		  </trim>
		  -->
		  
		  <!-- 
		  	4.<choose>, <when>, <otherwise>를 활용한 검색 기능 구현
		   -->
		   <include refid="boardListSql" />
		   <choose>
		   		<when test="writer != null">
		   			<!-- writer가 있으면 넘겨주는 값으로 해당하는 데이터만 조회되게 해준다. -->
		   			AND M.ID LIKE '%' || #{writer} || '%'
		   		</when>
		   		<when test="title != null">
		   			AND B.TITLE LIKE '%' || #{title} || '%'
		   		</when>
		   		<when test="content != null">
			  		AND B.CONTENT LIKE '%' || #{content} || '%'
		   		</when>
	   			<!-- 
		   		<otherwise>
		   			위의 조건 중 하나도 만족하지 않는 경우 포함될 쿼리 작성
		   		</otherwise>
		   		-->
		   </choose>
	</select>
	
	<select id="selectBoardListByFilters" parameterType="map" resultMap="boardListResultMap">
		<include refid="boardListSql" />
		<!-- filters가 null인지 확인 후 null이 아니면If내의 구문 실행 -->
		<if test="filters != null">
		
		  <!-- 
		  	AND B.TYPE IN ('B2', 'B3')
		  	
		  	위 결과를 만들기 위해서 foreach 태그를 사용한다.
		  		- collection : 파라미터로 넘어온 배열, 리스트를 지정한다.
		  		- item : 배열, 리스트의 각 요소들의 값이 들어가는 변수이다.
		  		- index : 반복 횟수 (0부터 시작한다.)
		  		- open : 반복문 시작 전 출력할 문자열을 지정한다.
		  		- close : 반복문 종료 전에 출력할 문자열을 지정한다.
		  		- separator : 반복할 때마다 반복을 구분할 구분자를 지정한다.
		   -->
		  AND B.TYPE IN 
		  <foreach collection="filters" item="filter" open="(" separator="," close=")">
		  	#{filter}
		  </foreach>
		</if>
	</select>
	
	<select id="getBoardCountByFilters" parameterType="map" resultType="_int">
		SELECT COUNT(*) 
		FROM BOARD 
		WHERE STATUS = 'Y'
		AND TYPE IN ('B2', 'B3')
		<if test="list != null">
			AND B.TYPE IN 
		  	<foreach collection="filters" item="filter" open="(" separator="," close=")">
		  		#{filter}
	  		</foreach>
		</if>
	</select>
	
	<!-- 
		게시글 상세 보기 (댓글 포함)
		
		1. 쿼리문을 각각 만들어서 하나의 ResultMap에서 매핑하는 방법
		
	<!-- 게시글 조회 쿼리문 -->
	<select id="selectBoardByNo" parameterType="_int" resultMap="boardDetailResultMap">
		<include refid="boardListSql" />
		<!-- B.NO가 파라미터로 전달되는 no와 같은 것만 조회 -->
		AND B.NO = #{no}
	</select>
	
	<!-- 댓글 조회 쿼리문 -->
	<select id="selectRepliesByBoardNo" parameterType="_int" resultMap="replyResultMap">
	<!-- 객체 조회 -->
		SELECT R.NO,
			   R.BOARD_NO,
			   R.CONTENT,
			   M.ID,
			   R.CREATE_DATE,
			   R.MODIFY_DATE
		  FROM REPLY R
	   	  JOIN MEMBER M ON (R.WRITER_NO = M.NO)
	     WHERE R.STATUS = 'Y' AND BOARD_NO=#{boardNo}
	  ORDER BY R.NO DESC
	</select>
	-->
	
	
	<!-- 
		2. 하나의 쿼리문을 만들어서 하나의 ResultMap에서 매핑하는 방법
			- B.(보드 관련 컬럼들), R.(댓글 관련 컬럼들)
	-->
	<select id="selectBoardByNo" parameterType="_int" resultMap="boardDetailResultMap">
      SELECT B.NO,
             B.TITLE,
             M.ID AS,
             B.READCOUNT,
             B.ORIGINAL_FILENAME,
             B.RENAMED_FILENAME,
             B.CONTENT,
             B.TYPE,
             B.CREATE_DATE,
             B.MODIFY_DATE,
             R.NO AS R_NO, 
             R.BOARD_NO AS R_BOARD_NO, 
             R.CONTENT AS R_CONTENT, 
             M2.ID AS R_ID, 
             R.CREATE_DATE AS R_CREATE_DATE, 
             R.MODIFY_DATE AS R_MODIFY_DATE
      FROM BOARD B
      JOIN MEMBER M ON(B.WRITER_NO = M.NO)
      LEFT OUTER JOIN REPLY R ON(B.NO = R.BOARD_NO)
      LEFT OUTER JOIN MEMBER M2 ON(R.WRITER_NO = M2.NO)
      WHERE B.STATUS = 'Y' AND B.NO = #{no}
	</select>
</mapper>




> BoardDao.java

package com.kh.mybatis.board.model.dao;

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

import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;

import com.kh.mybatis.board.model.vo.Board;
import com.kh.mybatis.common.util.PageInfo;

public class BoardDao {
	
	public int getBoardCount(SqlSession session, List<String> filters) {
		
		
		return session.selectOne("boardMapper.getBoardCountByFilters", filters);
	}

	public List<Board> findAll(SqlSession session, String writer, String title, String content) {
		// map으로 여러 개의 키 값을 담아줄 수 있다.
		Map<String, String> map = new HashMap<>();
		
		map.put("writer", writer);
		map.put("title", title);
		map.put("content", content);
		
		return session.selectList("boardMapper.selectAll", map);
	}

	public List<Board> findAll(SqlSession session, String[] filters, PageInfo pageInfo) {
		/*
		 * List 타입이나 Array 타입의 데이터를 파라미터로 전달하면 내부적으로는 Map으로 타입이 변환되어서 저장되기 때문에
		 * Mapper에서는 list(또는 collection)나 array라는 이름으로 사용해야 한다.
		 * 
		 * Dao.java
		 * 	session.selectList("boardMapper.selectBoardListByFilters", filters);
		 * 
		 * Mapper.xml
		 * 	<if test="array != null">
		 * 		...
		 * 	</if>
		 * 
		 * 만약에 filters라는 이름을 Mapper에서 사용하고 싶으면 Map 객체에 담아서 파라미터로 전달한다.(아래 내용)
		 */
		Map<String, Object> map = new HashMap<>();
		
		map.put("filters", filters);
		
		/*
		 * 기존에 Servlet/JSP에서는 쿼리문에서 rownum과 서브쿼리를 통해서 페이징 처리를 하였다.
		 * 하지만 Mybatis에서는 페이징 처리를 위해 RowBounds라는 클래스를 제공해준다.
		 * -> 쿼리문으로 조회되는 결과를 활용해서 RowBounds로 원하는 데이터만 가져옴
		 * 
		 * RowBounds의 객체를 생성할 때 offset과 limit 값을 전달해서 페이징 처리를 쉽게 구현한다.
		 * (offset 만큼 건너 뛰고 limit만큼 가져온다.)
		 * 
		 * offset = 0, limit = 10
		 * 	- 0개를 건너뛰고, 10개를 가져온다. (1번째 결과 ~ 10번째 결과까지)
		 * offset = 10, limit = 10
		 * 	- 10개를 건너뛰고, 10개를 가져온다. (11 ~ 20번째 데이터까지)
		 * offset = 20, limit = 10
		 * 	- 20개를 건너뛰고, 10개를 가져온다. (21 ~ 30번째 데이터까지)
		 * 
		 */
		
		int offset = (pageInfo.getCurrentPage() - 1) * pageInfo.getListLimit();
		int limit = pageInfo.getListLimit();
		RowBounds rowBounds = new RowBounds(offset, limit);
		
		return session.selectList("boardMapper.selectBoardListByFilters", map, rowBounds);
	}

	public Board findBoardByNo(SqlSession session, int no) {

		return session.selectOne("boardMapper.selectBoardByNo", no);
	}

}



> Board.java

package com.kh.mybatis.board.model.vo;

import java.util.Date;
import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Board {
	private int no;
	
	private int rowNum;
	
	private int writerNo;
	
	private String writerId;
	
	private String title;
	
	private String content;
	
	private String type;
	
	private String originalFileName;
	
	private String renamedFileName;
	
	private int readCount;
	
	private String status;
	
	private List<Reply> replies;
	
	private Date createDate;
	
	private Date modifyDate;
	
}



> Reply.java

package com.kh.mybatis.board.model.vo;

import java.util.Date;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Reply {
	private int no;
	
	private int boardNo;
	
	private int writerNo;
	
	private String writerId;
	
	private String content;	
	
	private Date createDate;
	
	private Date modifyDate;
}



> BoardService.java

package com.kh.mybatis.board.model.service;

import java.util.Arrays;
import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.kh.mybatis.board.model.dao.BoardDao;
import com.kh.mybatis.board.model.vo.Board;
import com.kh.mybatis.common.util.PageInfo;

import static com.kh.mybatis.common.template.SqlSessionTemplate.getSession;

public class BoardService {
	// Dao 객체에게 데이터 조회해달라고 요청
	private BoardDao dao = new BoardDao();
	
	public int getBoardCount(String[] filters) {
		// count라는 값을 반환
		int count = 0;
		SqlSession session = getSession();
		
		// dao에서 게시글 갯수 구해오기
		count = dao.getBoardCount(session, Arrays.asList(filters));
		
		// 조회되는 값이 있으면 session close
		session.close();
		
		return count;
	}
	
	public List<Board> findAll(String writer, String title, String content) {
		// Board객체를 가지는 list를 만들겠다- 선언
		List<Board> list = null;
		SqlSession session = getSession();
		
		// dao에게서 리스트를 받을 것임
		list = dao.findAll(session, writer, title, content);
		
		session.close();
		
		return list;
	}

	public List<Board> findAll(String[] filters,PageInfo pageInfo) {
		List<Board> list = null;
		SqlSession session = getSession();
		
		// dao에게 findAll 요청 하면서 매개값 session, filter 넘겨줌 (추가로 pageInfo도)
		list = dao.findAll(session, filters, pageInfo);
		
		
		return list;
	}

	public Board findBoardByNo(int no) {
		// 단건 조회
		Board board = null;
		SqlSession session = getSession();
		
		// board객체는 Dao를 통해서 가져옴
		board = dao.findBoardByNo(session, no);
		
		session.close();
		
		return board;
	}

}




> PageInfo.java

package com.kh.mybatis.common.util;

import lombok.Getter;

@Getter
public class PageInfo {
	private int currentPage;
	
	private int pageLimit;
	
	private int listCount;
	
	private int listLimit;	
	
	/**
	 * @param currentPage 현재 페이지
	 * @param pageLimit 한 페이지에 보이는 페이지의 수 
	 * @param listCount 전체 리스트의 수
	 * @param listLimit 한 페이지에 표시될 리스트의 수
	 */
	public PageInfo(int currentPage, int pageLimit, int listCount, int listLimit) {
		this.currentPage = currentPage;
		this.pageLimit = pageLimit;
		this.listCount = listCount;
		this.listLimit = listLimit;
	}
	
	/** 	
	 * @return 전체 페이지 중 가장 마지막 페이지
	 */
	public int getMaxPage() {
		/*
		 	listCount = 100, listLimit = 10
		 	100 / 10 = 10.0		=> 10페이지
		 	101 / 10 = 10.1		=> 11페이지
		 	103 / 10 = 10.3		=> 11페이지
		 	109 / 10 = 10.9		=> 11페이지
		 	110 / 10 = 11.0		=> 11페이지
		 	111 / 10 = 11.1		=> 12페이지
		 */
		return (int) Math.ceil((double) this.listCount / this.listLimit);
	}
	
	/**
	 * 
	 * @return 페이징 된 페이지 중 시작 페이지
	 */
	public int getStartPage() {
		/*	
			< 1 2 3 4 5 6 7 8 9 10 >
			< 11 12 13 14 15 16 17 18 19 20 >
			< 21 22 23 24 25 26 27 28 29 30 >
			
			1, 11, 21, 31, .... => (10 * n) + 1 (n >= 0)
			
			1 ~ 10 : n = 0
			11 ~ 20 : n = 1
			21 ~ 30 : n = 2
			31 ~ 40 : n = 3 
			.... 
			n = (currentPage - 1) / pageLimit
			
			(10 * ((currentPage - 1) / pageLimit)) + 1 (n >= 0)
		 */
		return (this.pageLimit * ((this.currentPage - 1) / this.pageLimit)) + 1;
	}

	/**
	 * 
	 * @return 페이징 된 페이지 중 마지막 페이지
	 */ 
	public int getEndPage() {
		// 10, 20, 30, 40, .... 
		
		int endPage = this.getStartPage() + this.pageLimit - 1;
		
		return endPage > this.getMaxPage() ? this.getMaxPage() : endPage;
	}	
	
	/**
	 * 
	 * @return 현재 페이지
	 */ 
	public int getCurrentPage() {
		return this.currentPage;
	}
	
	/**
	 * 
	 * @return 이전 페이지
	 */ 
	public int getPrevPage() {
		int prevPage = this.getCurrentPage() - 1;
		
		return prevPage < 1 ? 1 : prevPage;
	}
	
	/**
	 * 
	 * @return 다음 페이지
	 */ 
	public int getNextPage() {
		int nextPage = this.getCurrentPage() + 1;
		
		return nextPage > this.getMaxPage() ? this.getMaxPage() : nextPage;
	}
	
	/**
	 * 
	 * @return 페이지의 시작 리스트 
	 */ 	
	public int getStartList() {
		return (this.getCurrentPage() - 1) * this.listLimit + 1;
	}
	
	/**
	 * 
	 * @return 페이지의 마지막 리스트
	 */ 	
	public int getEndList() {
		int endList = this.getStartList() + this.listLimit - 1;
		
		return endList > this.listCount ? this.listCount : endList;
	}
	
}
반응형
Comments