본문 바로가기

자바 웹을 다루는 기술

[페이징 - 상속관계없는][제목으로 게시글 검색하기]

페이징 정리~

 

페이징을 위한 DTO에 선언 되어야하는 변수 정리~

 

 

//페이징
private int pageNo = 1; //현재 페이지 번호
private int totalCount; // 전체건수
private int totalPageSize; // 전체 페이지 수
private int pageLength = 10; // 한페이지의 길이
private int navSize = 10; //페이지 하단에 출력되는 페이지의 항목수
private int navStart = 0; //페이지 하단에 출력되는 페이지 시작번호
private int navEnd = 0; // 페이지 하단에 출력되는 페이지 끝 번호

 

 

전체 게시글의 갯수를 가져와서 전체 페이지 건수를 계산해주고 네비게이터를 계산해준다. 

 

<setTotalCount(int totalCount)>

전체 페이지 건수  = 전체게시글 갯수 / 한 페이지의 길이(기본값 : 10)

네비게이터 시작페이지 = ((현 페이징번호 - 1)/ 페이지하단의 페이지항목수) * 페이지하단 페이지 항목수 + 1

     ex) 현 페이지번호 16 -> 16 - 1 / 10 -> 1 *10 -> 10+1 -> 11

네비게이터 끝 페이지 = ((현 페이징번호 - 1)/ 페이지 하단의 페이지 항목수 + 1)* 페이지 하단의 항목수;

     ex) 현 페이지 번호 16 -> (16 - 1) / 10 -> 1 + 1 -> 2*10 -> 20

 

※ (현 페이지 번호 - 1) 은 마지막번호인 20, 30 이런 숫자가 현 페이지 번호가 됐을때 마지막 계산 + 1 하면 페이징 한 애들이 다음 10의 자리 수로 넘어가기 때문이다.

 

마지막 페이징 번호는 주의 해야할것이 아무것도 없는데 번호만 나오면 안되니까 만약에 마지막 번호가 전체 페이징한 숫자보다 크다면 전체 페이지 값으로 변경한다.

 

public void setTotalCount(int totalCount) {
    this.totalCount = totalCount;
    //페이지 건수 계산
    totalPageSize = (int) Math.ceil((double) totalCount/ pageLength);
    //페이지 네비게이터 시작 페이지를 계산한다.
    navStart = ((pageNo - 1)/ navSize)* navSize +1;
    //페이지 네비게이터 끝 페이지를 계산한다.
    navEnd = ((pageNo-1)/navSize + 1)* navSize;
    //전체 페이지보다 크면 전체 페이지값으로 변경한다.
    if(navEnd >= totalPageSize) {
        navEnd = totalPageSize;
    }
}

 

<getStartNo() , getEndNo()>

현재 페이징번호를 기준으로 현재 페이징된 번호에 출력되어야하는 게시글의 첫 rownum과 끝 rownum을 계산하기 위한 함수이다. sql문에서 rownum을 기준으로 게시글을 잘라서 가져올 때 prepareStatement나 callableStatement를 통해서 sql문의 매개변수 ? 에 대입해야하는 값들이다. 

 

//해당 페이지의 게시글 처음 시작 번호
public int getStartNo() {
    return (pageNo - 1) * pageLength + 1;
}
//해당 페이지의 게시글 마지막 번호
public int getEndNo() {
    return pageNo * pageLength;
}

 

DAO에서는 게시글의 총 갯수를 구하는 sql 문, 페이지별로 게시글을 끊어서 가져올 sql문이 필요하다.

게시글을 검색하는 부분도 나중에는 추가해줄것이기 때문에 like를 통해서 비교해주는 동적 쿼리를 선언해 주었다.

<getTotalCount(Board)>

boardDTO에 있는 setTotalCount를 계산해주려면 전체 게시글의 갯수가 필요하다! 전달을 위해 선언해준다.

select count(*) from board where boardtype='게시판'
		and title like concat(concat('%','검색할 게시글의 제목'),'%');
//게시글의 총 갯수를 구하는 sql
public int getTotalCount(Board board) {
    Connection conn = ConnectionHelper.getConnection("oracle");
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    int count = 0;
    String sql = "select count(*) from board where boardtype=?";




    try {
        if(board.getSearchTitle()!=null) {
            sql += " and title like concat(concat('%',?),'%')";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, board.getBoardtype());
            pstmt.setString(2, board.getSearchTitle());
            rs = pstmt.executeQuery();
        }else {
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, board.getBoardtype());
            rs = pstmt.executeQuery();
        }
        if(rs.next()) {
            count = rs.getInt(1);
        }
    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }finally {
        ConnectionHelper.close(rs);
        ConnectionHelper.close(pstmt);
        ConnectionHelper.close(conn);
    }
    return count;
}

 

 

<getBoardList(Board)>

페이지별 게시글을 끊어서 가져오기 위한 쿼리를 작성한 함수이다. 이 역시 나중에 게시글의 제목을 통해 검색을 해줄 동적쿼리를 추가한다

 

rownum은 조건에 일치하지 않으면 끊김  - > 아예 출력되지 않음

해결책
인라인뷰를 통해서 rownum을 가져와서 컬럼취급해주면 안씹힌다
인라인뷰안에는 검색조건이나 이런게 들어가고 밖의 where에는 건수만 들어간다.
select nrow, b.* from(
 select rownum nrow, a.* from (
select * 
from board 
where boardtype='게시판'
	and title like concat(concat('%','검색할 제목 일부'),'%')
order by title) a
where rownum <= 10) b 
where nrow between 1 and 10;
//게시글의 페이지별로 몇개씩 끊어서 가져오려는 sql문
public List<Board> getBoardList(Board board){
    Connection conn = ConnectionHelper.getConnection("oracle");
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    List<Board> boardList = new ArrayList<>();

    String sql = "select nrow, b.* from("
            + " select rownum nrow, a.* from ("
            + " select * "
            + " from board "
            + " where boardtype=? ";
            if(board.getSearchTitle()!=null) {
                sql += " and title like concat(concat('%',?),'%')";

            }
            sql += " order by title) a "
            + " where rownum <= "+ board.getEndNo() +") b "
            + " where nrow between " + board.getStartNo() +" and "+ board.getEndNo() +  " ";


    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, board.getBoardtype());
        if(board.getSearchTitle()!=null) {
            pstmt.setString(2, board.getSearchTitle());	
        }
        rs = pstmt.executeQuery();
        while(rs.next()) {
            boardList.add(Board.builder()
                                .boardid(rs.getInt("boardid"))
                                .title(rs.getString("title"))
                                .contents(rs.getString("contents"))
                                .regdate(rs.getDate("regdate"))
                                .hit(rs.getInt("hit"))
                                .boardtype(rs.getString("boardtype"))
                                .fixed_yn(rs.getString("fixed_yn"))
                                .memberid(rs.getString("memberid"))
                                .build());
        }
    }catch(Exception e) {
        e.printStackTrace();
    }finally {
        ConnectionHelper.close(rs);
        ConnectionHelper.close(pstmt);
        ConnectionHelper.close(conn);
    }
    return boardList;
}

 

 

BoardService에서 페이징 처리를 위해 controller가 불러줄 부분

 

<Map<String, Object> getBoardPageList(Board)>

전체 게시글의 건수를 얻어서 board의 setTotalCount()함수를 호출해서 페이징에 대한 정보를 저장해준다.  Map 에 넣어준 board를 통해서는 게시글 페이징에 대한 정보를 통해 네비게이터를 출력해준다. boardList에는  한페이지에 대한 게시글들을 저장해 jsp에서 출력해준다.

public Map<String, Object> getBoardPageList(Board board){
    //1. 전체 건수를 얻는다
    board.setTotalCount(boardDAO.getTotalCount(board));
    System.out.println("board = "+board);
    Map<String, Object> result = new HashMap<>();

    result.put("board", board);
    result.put("boardList", boardDAO.getBoardList(board));
    return result;
}

 

 

BoardController

 

<AllBoard(board,request,response)>

AllBoard()함수에서 loginMember를 가져와서 관리자인지 확인하고 관리자이면 check박스를 통해 게시글 삭제와 개별 삭제가 가능하도록한다.

setBoardtype은 제 테이블에서는 게시판과 공지사항이 하나로 이루어져있기때문입니다. 구분값이 boardtype입니다.

public String AllBoard(Board board ,HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpSession session = request.getSession();
    Member loginMember = (Member)session.getAttribute("loginMember");

    session.setAttribute("boardtype", "AllBoard.do");
    //boardtype도 setBoardtype으로 해서 넘겨야겠다 어차피 매개변수로 board받아오니가
    //Board board = new Board();
    board.setBoardtype("게시판");
    Map<String, Object> result = boardService.getBoardPageList(board);
    //request.setAttribute("boardList", boardList);
    request.setAttribute("result", result);

    boolean isAdmin = boardService.isAdmin(loginMember);
    request.setAttribute("isAdmin", isAdmin);


    return "board/allBoard.jsp";
}

 

jsp 에서 다음 페이지로 넘어갈때 서버에 넘겨줘야하는 form을 작성해준다.

searchTitle은 검색을 위해 게시글 제목의 일부 값을 넘겨주는 항목이다.

pageNo는 다음에 이동할 페이지 번호, pageLength는 한페이지에 출력할 페이지길이 이다.

<form name="pageForm" id="pageForm" action="<c:url value='/board/AllBoard.do'/>" method="post">
    <input type="hidden" name="pageNo" id="pageNo" value="${result.board.pageNo}"/>
    <input type="hidden" name="searchTitle" id="searchTitle" value="${ result.board.searchTitle}"/>
    <input type="hidden" name="pageLength" id="pageLength" value="${result.board.pageLength} "/>

</form>

페이지 네비게이터를 출력할 forEach문를 수행한다. 페이지 번호에 js 함수로 통하는 링크를 걸어준다. 매개변수로는 해당 페이지 번호를 걸어준다.  > 를 누르면 다음 페이지로 이동하도록 현재 페이지에 +1 한 값을 넘겨주고 <를 누르면 이전 페이지로 이동하도록 현재페이지에 -1 한 값을 넣어준다. 단 1페이지 이거나 마지막 페이지 일때는 다음버튼이나 이전 버튼을 누르면 오류가 뜨니까 누르지 못하도록 그때는 '<' , ' >' 를 누를 수 없도록 c:if문을 통해 없애준다

c:choose는 선택한 페이지에 대해서는 글자를 굵게 나타내기위해서 사용해줬다.

 

' < '  -> &lt;  , ' >' -> &gt;

 

<div style='width: 65%;margin: 10px auto;'>
    <c:if test="${result.board.navStart != 1}">
        <a href = "javascript:jsPageNo(${result.board.navStart-1 })">&lt;</a>
    </c:if>
    <c:forEach var="item" begin="${result.board.navStart }" end="${result.board.navEnd }">
        <c:choose>
            <c:when test="${result.board.pageNo!= item }">
                <label><a href="javascript:jsPageNo(${item })">${item}</a></label>
            </c:when>
            <c:otherwise>
                <strong>${item}</strong>
            </c:otherwise>
        </c:choose>
    </c:forEach>
    <c:if test="${result.board.navEnd != result.board.totalPageSize }">
        <a href="javascript:jsPageNo(${result.board.navEnd+1 })">&gt;</a>
    </c:if>

</div>

선택된 페이지 번호에 대해서 저장하고 선택된 페이지의 길이를 저장한 다음에 form태그를 서버로 제출한다.

function jsPageNo(pageNo){
    document.querySelector("#pageForm > #pageNo").value = pageNo;
    //페이지번호가 계속 기본값 10으로 초기화되는 문제가 발생해서 그냥 직접 대입해줬음
    document.querySelector("#pageForm > #pageLength").value = ${result.board.pageLength};
    document.querySelector("#pageForm").submit();
}

 

 

 

검색필드를 이용해서 제목의 일부를 통해서 게시글 검색하기

 

 

 

boardDTO에 검색할 제목을 전달할 searchTitle값을 선언해준다.

//검색필드
private String searchTitle = "";

 

 

 

jsp 파일에 제목을 찾을 값을 입력할 form과 출력할 페이지의 길이를 입력할 <select>태그를 작성해준다. 그리고 현재 controller로부터 받아온 현재 출력할 페이지의 길이를 <select>에 선택되게끔 EL태그에 3항연산자를 통해서 작성해준다. 이 폼의 검색 버튼을 누르면 onSubmit에 등록된 js 함수로 넘어가도록 설정한다.

<div id="searchBoard">
	<form onSubmit="jsSearchValue()"name="mForm" id="mForm" action="<c:url value='/board/AllBoard.do'/>" method="post">
		<input type="hidden" name="pageNo" id="pageNo" value="${result.board.pageNo }"/>
            <div id="totalCount">
                <label>건수 : </label>
                <select name="pageLength" id="pageLength">
                    <option value="10" ${result.board.pageLength == 10? 'selected="selected"':'' }>10건</option>
                    <option value="20" ${result.board.pageLength == 20? 'selected="selected"':'' }>20건</option>
                    <option value="50" ${result.board.pageLength == 50? 'selected="selected"':'' }>50건</option>
                    <option value="100" ${result.board.pageLength == 100? 'selected="selected"':'' }>100건</option>
                </select>
             </div>
        	<div id="boardTitle" >
                <label>제목 : </label>
                <input type="text" name="searchTitle" id="searchTitle" value="${result.board.searchTitle }"/>
                <input type="submit" value="검색"/>
			</div>
	</form>
</div>

 

 

js로 넘겨받은후 form에 pageNo를 1로 설정해서 form을 서버로 보낸다. 이렇게 하면 검색한 결과가 1페이지부터 나온다.

function jsSearchValue(){
    document.querySelector("#mForm > #pageNo").value = "1";

    return true;
}

 

 

 

 


수업내용  , 필기 내용

 

 

 

rownum은 조건에 일치하지 않으면 끊김

 

해결책

인라인뷰를 통해서 rownum을 가져와서 컬럼취급해주면 안씹힌다

인라인뷰안에는 검색조건이나 이런게 들어가고 밖의 where에는 건수만 들어간다.

https://myjamong.tistory.com/170

 

[Oracle] 오라클 페이징 쿼리 쉽게 만들기 Row Limiting Clause 사용 :: 마이자몽

오라클 페이징 오라클 데이터베이스 페이징 쿼리는 어떻게 작성할까요? 포털 사이트에서 검색을 했을 때, 게시판 형태의 웹사이트에서 결과를 볼때, 한번에 모든 결과를 볼 수 없기 때문에 페이

myjamong.tistory.com

 

이제 java에서 처리해야할 코드 list를 실행할때마다 page번호가 넘어오고 검색도 가능하다.

//검색필드

 

//페이징

board dTO에서 사용할 변수 설정

 

한페이지 길이 기본 10 

boardDAO인터페이스

impl에서 전체 페이지 건수를 계산하기 위함

 

 

service

여기까지가 전체 건수 얻으려고 한짓

 

 

2. 전체 페이지 계산

 

 

3. 페이지 네비게이터 시작페이지를계산한다.

4. 끝 페이지를 계산한다

5. 전체 페이지 보다 크면 값을 전체페이지 값으로 변경한다.


근데 위치가 중요하다. 이 위치는 ㅡㅆ레기

notice를 불러다 사용하기 때문에 페이지 계산 함수를 notice로 옮긴다.

service에서는 notice의 계산하는 한가지 함수만 호출한ㄷ. 단. 데이터베이스에서 가져와야하는 한가지 매개변수만 가져옴

 

service에서는

 

이제 페이징한것에 해당하는 페이지 목록을 가져온다.

 

 

impl

페이지 ->>dao에서 함수 새로 생성해서 정의 해줌

DTO에서

 

impl에서 편리하게 사용하기위함

 

이제 리턴!

service에ㅓㅅ

 

 

 

controller

 

걍 이렇게 쓰면 나오네;;

 

list.jsp에서 검색된 글이 없을 때 예외처리

네비게이터 출력

 

 

현재 존재하고 있는 페이지 는 못가도록하려고

이제 링크달기

js함수에 페이지 번호 전달

js코드

페이지번호를 값으로 넣어놓는다.

 

mForm을 ......안넘어가면 document.queryselector.submit해준다.

 

네비게이터의 이전 페이지와 다음 페이지

 

< &lt , > &gt

 

 

start - 1 , end +1

 

 

 

 

 

게시글 검색

 

검색 버튼 만든다

버튼 value 값으로 현재 입력받는 애를 준다.

 

 

검색하는 놈을 가져 와야함!!!

DTO에 선언한다

submit으로 날리는 이벤트 주기

 

 

타이틀이 있을때만 리스트를 긁어오는 함수에 where절이 있어야한다.

 

작은 따옴표가 들어가는 sql문은 injection을 당할 수 있어 보안상 위험하다 그래서 select concat(concat('%','aaaa'),'%')from dual -- > 해킹이 안되는구문

 

 조건이 이렇게 들어갈 수있다 sql문사이에 검색 조건 설정하는 부분을 넣어준다.

검색을 완료하거나 다시 되돌아 갔을 떄 1페이지 부터 뜨도록 해야한다.

 

searchTitle - > 입력한 키워드

 

페이지를 검색하려고 글자 입력후 검색을 누르기 전에 페이지를 누르면 검색과 같이 동작한다.

 

form 을 나눈다.!

그리고 hidden으로 감춰준다.

 

pagNo아이디가 같은 것이 두개 있을 수 있다. 그래서 >표시로 상세히 나타낸다

페이지 번호가 두군데 다 있어서 양쪽에 둘다 쓰면 안된다.

 

 

 

함수에서 중요한건 리턴 매개변수

논리는 안바뀐다. 코드는 바뀌어도

if문이 많이 들어가면 로직이 꼬이므로 if문 없이가자

 

 

 

 

출력건수를 선택할 수있도록 추가하자

 

 

 

 

디폴트 상태를 만들어 줘야함 selected를 사용해야함 태그 안에서 그리고 다음 선택에서는 selected를사용해

이구문 이 맞을때가 잇고 안맞을 때가 잇음

그래서 이런식으로 3항연산자