김미썸코딩

Spring MVC 로 무한 스크롤, 검색 구현하기 본문

빅데이터 플랫폼 구축을 위한 자바 개발자 양성과정/랜선여행 커뮤니티 프로젝트

Spring MVC 로 무한 스크롤, 검색 구현하기

김미썸 2021. 4. 22. 00:23
728x90

사진자랑 게시판(게시판명: picture)의 '무한스크롤', '검색' 기능을 예로 들었다.

검색한 경우에도 똑같이 무한 스크롤이 적용되어야 하므로 무한스크롤과 검색 기능을 같이 구현하였다.


*** 순서 ***

1. 구현 완료 모습

2. 코드

           2-1. JSP

           2-2. Controller

           2-3. DAO

           2-4. TO

           2-5. SQL(mapper)


1. 구현 완료 모습

1) 무한 스크롤

2) 검색

검색 결과 갯수와 검색 결과가 출력된다. 

검색 결과가 많은 경우, 무한스크롤로 구현된다. 

 

 

2. 코드

2-1. JSP

> views > picture > picture_list.jsp

▶ 검색란, 무한스크롤로 가져온 리스트가 들어갈 부분 html

<div id="card-search" class="card-search" >
    <!-- 검색 버튼과 form -->
    <form action="./picture_list.do" method="get">
    <div class="row justify-content-md-center" id="search">
        <div class="col-1"></div>
            <div class="col-1">
                <div class="value" id="condition_pick" style="text-align:right;">
                    <select id="condition" name="condition" class="form-select">
                        <option value="subject" ${condition eq 'subject' ? 'selected' : '' }>제목</option>
                        <option value="content" ${condition eq 'content' ? 'selected' : '' }>내용</option>
                        <option value="writer" ${condition eq 'writer' ? 'selected' : '' }>작성자</option>
                        <option value="location" ${condition eq 'location' ? 'selected' : '' }>위치</option>
                    </select>
                </div>
            </div>
            <div class="col-md-8" style="padding-left:0px; text-align:left;">
                <input value="${keyword }" type="text" name="keyword" placeholder="검색어를 입력해주세요" class="form-control"
                style="padding-left:0px;">

            </div>
            <div class="col-md-2" style="padding:0px; text-align:left;">
                <span><button class="btn btn-success" type="submit">검색</button></span>
                <span id="writebox">
                    <c:if test="${!empty sessionScope.nick}">
                        <c:choose>
                            <c:when test="${empty sessionScope.nick}">
                                <button type="button"
                                    class="btn btn--radius-2 btn--blue-2"
                                    onclick="javascript:alert('로그인을 하셔야합니다.')">글쓰기</button>
                            </c:when>
                            <c:otherwise>
                                <button type="button"
                                    class="btn btn--radius-2 btn--blue-2"
                                    onclick="location.href='./picture_write.do'">글쓰기</button>
                            </c:otherwise>
                        </c:choose>
                    </c:if>
                </span>
            </div>
        </div>
    </form>
</div>

<%-- 만일 검색 키워드가 존재한다면 몇개의 글이 검색 되었는지 알려준다. --%>
<c:if test="${not empty keyword }">
    <div class="alert text-center">
        <strong>${totalRow }</strong> 개의 자료가 검색되었습니다.
    </div>
</c:if>


<!-- ======= card Section ======= -->
<section id="card-list" class="card-list">
    <div class="container">
        <div class="row card-list-container thumbnails"></div>

    </div>
</section>
<!-- ======= card Section 끝 ======= -->



<div class="back-drop">
    <!-- cpath/ 에서 '/'는 webapp을 의미한다. 웹앱 폴더의 svg폴더 안에 spinner-solid.svg가 들어있다.  -->
    <img src="./svg/spinner-solid.svg"/> 
</div>

 

 

 

위에서 검색란 부분 코드이다.

<div id="card-search" class="card-search" >
    <!-- 검색 버튼과 form -->
    <form action="./picture_list.do" method="get">
    <div class="row justify-content-md-center" id="search">
        <div class="col-1"></div>
            <div class="col-1">
                <div class="value" id="condition_pick" style="text-align:right;">
                    <select id="condition" name="condition" class="form-select">
                        <option value="subject" ${condition eq 'subject' ? 'selected' : '' }>제목</option>
                        <option value="content" ${condition eq 'content' ? 'selected' : '' }>내용</option>
                        <option value="writer" ${condition eq 'writer' ? 'selected' : '' }>작성자</option>
                        <option value="location" ${condition eq 'location' ? 'selected' : '' }>위치</option>
                    </select>
                </div>
            </div>
            <div class="col-md-8" style="padding-left:0px; text-align:left;">
                <input value="${keyword }" type="text" name="keyword" placeholder="검색어를 입력해주세요" class="form-control"
                style="padding-left:0px;">

            </div>
            <div class="col-md-2" style="padding:0px; text-align:left;">
                <span><button class="btn btn-success" type="submit">검색</button></span>
                <span id="writebox">
                    <c:if test="${!empty sessionScope.nick}">
                        <c:choose>
                            <c:when test="${empty sessionScope.nick}">
                                <button type="button"
                                    class="btn btn--radius-2 btn--blue-2"
                                    onclick="javascript:alert('로그인을 하셔야합니다.')">글쓰기</button>
                            </c:when>
                            <c:otherwise>
                                <button type="button"
                                    class="btn btn--radius-2 btn--blue-2"
                                    onclick="location.href='./picture_write.do'">글쓰기</button>
                            </c:otherwise>
                        </c:choose>
                    </c:if>
                </span>
            </div>
        </div>
    </form>
</div>

 

 

검색 결과 갯수 출력부분 코드이다.

<%-- 만일 검색 키워드가 존재한다면 몇개의 글이 검색 되었는지 알려준다. --%>
<c:if test="${not empty keyword }">
    <div class="alert text-center">
        <strong>${totalRow }</strong> 개의 자료가 검색되었습니다.
    </div>
</c:if>

 

검색 전, 후 무한스크롤로 카드리스트를 가져와서 넣는 부분이다.

<!-- ======= card Section ======= -->
<section id="card-list" class="card-list">
    <div class="container">
        <div class="row card-list-container thumbnails"></div>

    </div>
</section>
<!-- ======= card Section 끝 ======= -->

 

로딩 이미지 부분이다. 스크롤이 맨끝에 닿고 다음 페이지를 가져올때 동안 띄워진다.

<div class="back-drop">
    <!-- cpath/ 에서 '/'는 webapp을 의미한다. 웹앱 폴더의 svg폴더 안에 spinner-solid.svg가 들어있다.  -->
    <img src="./svg/spinner-solid.svg"/> 
</div>

 

▶ 스크롤시 이벤트 처리 javascript

//페이지가 처음 로딩될 때 1page를 보여주기 때문에 초기값을 1로 지정한다.
let currentPage=1;
//현재 페이지가 로딩중인지 여부를 저장할 변수이다.
let isLoading=false;

//웹브라우저의 창을 스크롤 할 때 마다 호출되는 함수 등록
$(window).on("scroll",function(){
    //위로 스크롤된 길이
    let scrollTop=$(window).scrollTop();
    //웹브라우저의 창의 높이
    let windowHeight=$(window).height();
    //문서 전체의 높이
    let documentHeight=$(document).height();
    //바닥까지 스크롤 되었는 지 여부를 알아낸다.
    let isBottom=scrollTop+windowHeight + 10 >= documentHeight;

    if(isBottom){
        //만일 현재 마지막 페이지라면
        if(currentPage == ${totalPageCount} || isLoading){
            return; //함수를 여기서 끝낸다.
        }
        //현재 로딩 중이라고 표시한다.
        isLoading=true;
        //로딩바를 띄우고
        $(".back-drop").show();
        //요청할 페이지 번호를 1 증가시킨다.
        currentPage++;
        //추가로 받아올 페이지를 서버에 ajax 요청을 하고
        console.log("inscroll"+currentPage);
        GetList(currentPage);

    }; 
});

 

▶ 카드 리스트를 가져오는 함수 javascript

const GetList = function(currentPage){
    console.log("inGetList"+currentPage);

    // 무한 스크롤
    $.ajax({
        url:"ajax_page.do",
        method:"GET",
        //검색기능이 있는 경우 condition과 keyword를 함께 넘겨줘야한다. 안그러면 검색결과만 나와야하는데 다른것들이 덧붙여져 나온다.
        data:"pageNum="+currentPage+"&condition=${condition}&keyword=${keyword}",
        //ajax_page.jsp의 내용이 data로 들어온다.
        success:function(data){
            console.log(data);
            //응답된 문자열은 html 형식이다.(picture/ajax_page.jsp에 응답내용이 있다.)
            //해당 문자열을 .card-list-container  div에 html로 해석하라고 추가한다.
            $(".card-list-container").append(data);
            //로딩바를 숨긴다.
            $(".back-drop").hide();
            //로딩중이 아니라고 표시한다.
            isLoading=false;
            console.log("ajax");

            $(".heart-click").unbind('click');
            // 로그인 한 상태에서 하트를 클릭했을 때 (로그인한 상태인 하트의 <a></a> class명: heart-click)
            $(".heart-click").click(function() {

                // 게시물 번호(no)를 idx로 전달받아 저장합니다.
                let no = $(this).attr('idx');
                console.log("heart-click");

                // 빈하트를 눌렀을때
                if($(this).children('svg').attr('class') == "bi bi-suit-heart"){
                    console.log("빈하트 클릭" + no);

                    $.ajax({
                        url : 'saveHeart.do',
                        type : 'get',
                        data : {
                            no : no,
                        },
                        success : function(pto) {
                            //페이지 새로고침
                            //document.location.reload(true);

                            let heart = pto.heart;

                            // 페이지, 모달창에 하트수 갱신
                            $('#m_heart'+no).text(heart);
                            $('#heart'+no).text(heart);

                            console.log("하트추가 성공");
                        },
                        error : function() {
                            alert('서버 에러');
                        }
                    });
                    console.log("꽉찬하트로 바껴라!");

                    // 꽉찬하트로 바꾸기
                    $(this).html("<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' class='bi bi-suit-heart-fill' viewBox='0 0 16 16'><path d='M4 1c2.21 0 4 1.755 4 3.92C8 2.755 9.79 1 12 1s4 1.755 4 3.92c0 3.263-3.234 4.414-7.608 9.608a.513.513 0 0 1-.784 0C3.234 9.334 0 8.183 0 4.92 0 2.755 1.79 1 4 1z'/></svg>");
                    $('.heart_icon'+no).html("<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' class='bi bi-suit-heart-fill' viewBox='0 0 16 16'><path d='M4 1c2.21 0 4 1.755 4 3.92C8 2.755 9.79 1 12 1s4 1.755 4 3.92c0 3.263-3.234 4.414-7.608 9.608a.513.513 0 0 1-.784 0C3.234 9.334 0 8.183 0 4.92 0 2.755 1.79 1 4 1z'/></svg>");

                // 꽉찬 하트를 눌렀을 때
                }else if($(this).children('svg').attr('class') == "bi bi-suit-heart-fill"){
                    console.log("꽉찬하트 클릭" + no);

                    $.ajax({
                        url : 'removeHeart.do',
                        type : 'get',
                        data : {
                            no : no,
                        },
                        success : function(pto) {
                            //페이지 새로고침
                            //document.location.reload(true);

                            let heart = pto.heart;
                            // 페이지, 모달창에 하트수 갱신
                            $('#m_heart'+no).text(heart);
                            $('#heart'+no).text(heart);

                            console.log("하트삭제 성공");
                        },
                        error : function() {
                            alert('서버 에러');
                        }
                    });
                    console.log("빈하트로 바껴라!");

                    // 빈하트로 바꾸기
                    $(this).html('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-suit-heart" viewBox="0 0 16 16"><path d="M8 6.236l-.894-1.789c-.222-.443-.607-1.08-1.152-1.595C5.418 2.345 4.776 2 4 2 2.324 2 1 3.326 1 4.92c0 1.211.554 2.066 1.868 3.37.337.334.721.695 1.146 1.093C5.122 10.423 6.5 11.717 8 13.447c1.5-1.73 2.878-3.024 3.986-4.064.425-.398.81-.76 1.146-1.093C14.446 6.986 15 6.131 15 4.92 15 3.326 13.676 2 12 2c-.777 0-1.418.345-1.954.852-.545.515-.93 1.152-1.152 1.595L8 6.236zm.392 8.292a.513.513 0 0 1-.784 0c-1.601-1.902-3.05-3.262-4.243-4.381C1.3 8.208 0 6.989 0 4.92 0 2.755 1.79 1 4 1c1.6 0 2.719 1.05 3.404 2.008.26.365.458.716.596.992a7.55 7.55 0 0 1 .596-.992C9.281 2.049 10.4 1 12 1c2.21 0 4 1.755 4 3.92 0 2.069-1.3 3.288-3.365 5.227-1.193 1.12-2.642 2.48-4.243 4.38z" /></svg>');

                    $('.heart_icon'+no).html('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-suit-heart" viewBox="0 0 16 16"><path d="M8 6.236l-.894-1.789c-.222-.443-.607-1.08-1.152-1.595C5.418 2.345 4.776 2 4 2 2.324 2 1 3.326 1 4.92c0 1.211.554 2.066 1.868 3.37.337.334.721.695 1.146 1.093C5.122 10.423 6.5 11.717 8 13.447c1.5-1.73 2.878-3.024 3.986-4.064.425-.398.81-.76 1.146-1.093C14.446 6.986 15 6.131 15 4.92 15 3.326 13.676 2 12 2c-.777 0-1.418.345-1.954.852-.545.515-.93 1.152-1.152 1.595L8 6.236zm.392 8.292a.513.513 0 0 1-.784 0c-1.601-1.902-3.05-3.262-4.243-4.381C1.3 8.208 0 6.989 0 4.92 0 2.755 1.79 1 4 1c1.6 0 2.719 1.05 3.404 2.008.26.365.458.716.596.992a7.55 7.55 0 0 1 .596-.992C9.281 2.049 10.4 1 12 1c2.21 0 4 1.755 4 3.92 0 2.069-1.3 3.288-3.365 5.227-1.193 1.12-2.642 2.48-4.243 4.38z" /></svg>');
                }



            });


            // 로그인 안한 상태에서 하트를 클릭하면 로그인해야한다는 알림창이 뜹니다.
            // (로그인한 상태인 하트의 <a></a> class명: heart-notlogin)
            $(".heart-notlogin").unbind('click');
            $(".heart-notlogin ").click(function() {
                alert('로그인 하셔야 하트를 누를수 있습니다!');
            });


            // 댓글아이콘을 클릭했을때 댓글 리스트 함수를 호출
            $(".open_reply_list").unbind('click');
            $(".open_reply_list").click(function(){
                let no = $(this).attr('idx');
                // 게시물에 no에 해당하는 댓글 리스트를 가져오는 함수	
                ReplyList(no);
            });

            // 댓글 달기 버튼 클릭시  실행
            $(".write_reply").unbind('click');
            $(".write_reply").click(function(){
                // 게시물 번호
                let no = $(this).attr('idx');
                //책갈피
                // 댓글 입력란의 내용을 가져온다.
                let content = $("#input_reply" + no).val();

                // 앞뒤 공백을 제거한다.(띄어쓰기만 입력했을때 댓글작성안되게 처리하기위함)
                content = content.trim();

                console.log(content);

                if(content == ""){	// 입력된게 없을때
                    alert("글을 입력하세요!");
                }else{	
                    // 입력란 비우기
                    $("#input_reply" + no).val("");

                    // reply+1 하고 그 값을 가져옴
                    $.ajax({
                        url : 'picture_write_reply.do',
                        type : 'get',
                        data : {
                            no : no,
                            content: content
                        },
                        success : function(pto) {

                            let reply = pto.reply;
                            // 페이지, 모달창에 댓글수 갱신
                            $('#m_reply'+no).text(reply);
                            $('#reply'+no).text(reply);

                            console.log("댓글 작성 성공");

                            // 댓글리스트를 새로 받아오기
                            ReplyList(no);
                        },
                        error : function() {
                            alert('서버 에러');
                        }
                    });

                }

            });
        }

    });
}

 

위의 함수(GetList)는 페이지가 맨첨에 열릴 때, 혹은 검색으로 인해 페이지가 리로드 될 때는 아래처럼 함수가 호출되고.. 

$(document).ready(function(){ 
	GetList(1);
});

그 이후 스크롤이 발생할 땐 위의 ▶ 스크롤시 이벤트 처리 javascript 에서 아래처럼 호출된다.

 

 

 

2-2. Controller

> PictureController.java

// 사진자랑 목록
@RequestMapping(value = "/picture_list.do")
public String picture_list(HttpServletRequest request, HttpSession session) {
    // 한 페이지에 몇개씩 표시할 것인지
    final int PAGE_ROW_COUNT = 12;

    // 보여줄 페이지의 번호를 일단 1이라고 초기값 지정
    int pageNum = 1;
    // 페이지 번호가 파라미터로 전달되는지 읽어와 본다.
    String strPageNum = request.getParameter("pageNum");
    // 만일 페이지 번호가 파라미터로 넘어 온다면
    if (strPageNum != null) {
        // 숫자로 바꿔서 보여줄 페이지 번호로 지정한다.
        pageNum = Integer.parseInt(strPageNum);
    }

    // 보여줄 페이지의 시작 ROWNUM - 0부터 시작
    int startRowNum = 0 + (pageNum - 1) * PAGE_ROW_COUNT;
    // 보여줄 페이지의 끝 ROWNUM
    int endRowNum = pageNum * PAGE_ROW_COUNT;

    int rowCount = PAGE_ROW_COUNT;

    /*
     * 검색 키워드 관련된 처리 - 검색 키워드가 파라미터로 넘어올 수도 있고 안넘어올 수도 있다.
     */
    String keyword = request.getParameter("keyword");
    String condition = request.getParameter("condition");
    // 만일 키워드가 넘어오지 않는다면
    if (keyword == null) {
        // 키워드와 검색 조건에 빈 문자열을 넣어준다.
        keyword = "";
        condition = "";
    }
    // 특수기호를 인코딩한 키워드를 미리 준비한다.
    String encodedK = URLEncoder.encode(keyword);

    // startRowNum 과 rowCount 를  PictureTO 객체에 담는다.
    PictureTO pto = new PictureTO();
    pto.setStartRowNum(startRowNum);
    pto.setEndRowNum(endRowNum);
    pto.setRowCount(rowCount);

    // ArrayList 객체의 참조값을 담을 지역변수를 미리 만든다.
    ArrayList<PictureTO> list = null;
    // 전체 row의 개수를 담을 지역변수를 미리 만든다. - 검색조건이 들어올 경우 '검색 결과 갯수'가 된다.
    int totalRow = 0;

    // 만일 검색 키워드가 넘어온다면
    if (!keyword.equals("")) { // 검색 조건이 무엇이냐에 따라 분기하기
        if (condition.equals("subject")) { // 제목 검색인 경우
            // 검색 키워드를 PictureTO에 담아서 전달한다.
            pto.setSubject(keyword);
        } else if (condition.equals("content")) { // 내용 검색인 경우
            pto.setContent(keyword);
        } else if (condition.equals("writer")) { // 작성자 검색인 경우
            pto.setWriter(keyword);
        } else if (condition.equals("location")) {	// 위치 검색인 경우
            pto.setLocation(keyword);
        } // 다른검색 조건을 추가하고 싶다면 아래 else if()를 계속 추가하면 된다.
    }

    // 위의 분기에 따라 pto에 담기는 내용이 다르고
    // 그 내용을 기준으로 조건이 다를때마다 다른 내용이 select 되도록 dynamic query를 구성한다.
    // 글 목록 얻어오기

    if (session.getAttribute("nick") == null) {
        // 로그인 상태가 아닐때
//			System.out.println("로그인 상태가 아닐때 ");
        // 사진 자랑 게시판 목록 가져오기

        list = pictureDao.boardList(pto);
    } else {
        // 로그인 상태일때
//			System.out.println("로그인 상태일때 ");

        // 현재사용자의 nick을 세팅
        pto.setNick((String) session.getAttribute("nick"));

        // 사진자랑 게시판 목록 가져오기
        list = pictureDao.boardListLogin(pto);

    }

    // 글의 개수
    totalRow = pictureDao.PictureCount(pto);

    // 전체 페이지의 갯수 구하기
    int totalPageCount = (int) Math.ceil(totalRow / (double) PAGE_ROW_COUNT);

    request.setAttribute("list", list);
    request.setAttribute("totalPageCount", totalPageCount);
    request.setAttribute("condition", condition);
    request.setAttribute("keyword", keyword);
    request.setAttribute("totalRow", totalRow);
    // pageNum 도 추가로 담아주기
    request.setAttribute("pageNum", pageNum);

    /////////////////////////////////////////////////////// 여기까지 picture_ajax_page와 동일
    /////////////////////////////////////////////////////// ajax_page는 스크롤을 내릴때 추가되는 게시물들을 가져오기 떄문에 
    /////////////////////////////////////////////////////// best5를 가져갈 필요가 없다. 

    // 사진자랑 좋아요(heart)순으로 상위5개 가져오기
    ArrayList<PictureTO> bestList = new ArrayList();
    bestList = pictureDao.bestList(pto);

    request.setAttribute("bestList", bestList);

    return "picture/picture_list";
}


// 사진자랑 목록 - 로딩으로 불러오는 게시물 리스트
@RequestMapping(value = "/ajax_page.do")
public String picture_ajax_page(HttpServletRequest request, HttpSession session) {
    // 한 페이지에 몇개씩 표시할 것인지
    final int PAGE_ROW_COUNT = 12;

    // 보여줄 페이지의 번호를 일단 1이라고 초기값 지정
    int pageNum = 1;
    // 페이지 번호가 파라미터로 전달되는지 읽어와 본다.
    String strPageNum = request.getParameter("pageNum");
    // 만일 페이지 번호가 파라미터로 넘어 온다면
    if (strPageNum != null) {
        // 숫자로 바꿔서 보여줄 페이지 번호로 지정한다.
        pageNum = Integer.parseInt(strPageNum);
    }

    // 보여줄 페이지의 시작 ROWNUM - 0부터 시작
    int startRowNum = 0 + (pageNum - 1) * PAGE_ROW_COUNT;
    // 보여줄 페이지의 끝 ROWNUM
    int endRowNum = pageNum * PAGE_ROW_COUNT;

    int rowCount = PAGE_ROW_COUNT;

    /*
     * 검색 키워드 관련된 처리 -검색 키워드가 파라미터로 넘어올 수도 있고 안넘어올 수도 있다.
     */
    String keyword = request.getParameter("keyword");
    String condition = request.getParameter("condition");
    // 만일 키워드가 넘어오지 않는다면
    if (keyword == null) {
        // 키워드와 검색 조건에 빈 문자열을 넣어준다.
        keyword = "";
        condition = "";
    }
    // 특수기호를 인코딩한 키워드를 미리 준비한다.
    String encodedK = URLEncoder.encode(keyword);

    // startRowNum 과 rowCount 를  PictureTO 객체에 담고
    PictureTO pto = new PictureTO();
    pto.setStartRowNum(startRowNum);
    pto.setEndRowNum(endRowNum);
    pto.setRowCount(rowCount);

    // ArrayList 객체의 참조값을 담을 지역변수를 미리 만든다.
    ArrayList<PictureTO> list = null;
    // 전체 row의 개수를 담을 지역변수를 미리 만든다.
    int totalRow = 0;

    // 만일 검색 키워드가 넘어온다면
    if (!keyword.equals("")) { // 검색 조건이 무엇이냐에 따라 분기하기
        if (condition.equals("subject")) { // 제목 검색인 경우
            // 검색 키워드를 PictureTO에 담아서 전달한다.
            pto.setSubject(keyword);
        } else if (condition.equals("content")) { // 내용 검색인 경우
            pto.setContent(keyword);
        } else if (condition.equals("writer")) { // 작성자 검색인 경우
            pto.setWriter(keyword);
        } else if (condition.equals("location")) {	// 위치 검색인 경우
            pto.setLocation(keyword);
        } // 다른검색 조건을 추가하고 싶다면 아래 else if()를 계속 추가하면 된다.
    }

    // 위의 분기에 따라 pto에 담기는 내용이 다르고
    // 그 내용을 기준으로 조건이 다를때마다 다른 내용이 select 되도록 dynamic query를 구성한다.
    // 글 목록 얻어오기

    if (session.getAttribute("nick") == null) {
        // 로그인 상태가 아닐때
        System.out.println("로그인 상태가 아닐때 ");
        // 사진 자랑 게시판 목록 가져오기

        list = pictureDao.boardList(pto);
    } else {
        // 로그인 상태일때
//			System.out.println("로그인 상태일때 ");

        // 현재사용자의 nick을 세팅
        pto.setNick((String) session.getAttribute("nick"));

        // 사진자랑 게시판 목록 가져오기
        list = pictureDao.boardListLogin(pto);

    }

    // 글의 개수
    totalRow = pictureDao.PictureCount(pto);

    // 전체 페이지의 갯수 구하기
    int totalPageCount = (int) Math.ceil(totalRow / (double) PAGE_ROW_COUNT);

    request.setAttribute("list", list);
    request.setAttribute("totalPageCount", totalPageCount);
    request.setAttribute("condition", condition);
    request.setAttribute("keyword", keyword);
    request.setAttribute("totalRow", totalRow);
    // pageNum 도 추가로 담아주기
    request.setAttribute("pageNum", pageNum);

    /////////////////////////////////////////////////////// 여기까지 picture_list와 동일
    /////////////////////////////////////////////////////// ajax_page는 스크롤을 내릴때 추가되는 게시물들을 가져오기 떄문에 
    /////////////////////////////////////////////////////// best5를 가져갈 필요가 없다. 

    return "picture/ajax_page";
}

 

페이지가 열릴때와 스크롤일때 요청을 나눠놨다.

페이지가 열릴때는 검색후 리로드되는 것도 포함하며 스크롤일때와 달리 검색결과에 맞는 best5를 추가로 가져온다는 차이가 있다.

 

2-3. DAO

> PictureDAO.java

// 로그인 전 게시판 list
public ArrayList<PictureTO> boardList(PictureTO to) {

  // 게시물 리스트 가져오기
  ArrayList<PictureTO> list = (ArrayList) sqlSession.selectList("picture_list", to);

  return list;
}

// 로그인 후 게시판 list
public ArrayList<PictureTO> boardListLogin(PictureTO to) {

  //게시물 리스트 가져오기 - 현재사용자의 nick이 nick에 세팅된 to를 넘긴다.
  ArrayList<PictureTO> list = (ArrayList) sqlSession.selectList("picture_list_login", to);

  return list;
}

// 게시물 갯수 가져오기
public int PictureCount(PictureTO to) {

  // 게시물 갯수를 구한다.
  // 검색 키워드가 들어온 경우 검색결과의 게시물갯수가 된다.
  int result = sqlSession.selectOne("picture_count", to);

  return result;
}



// 베스트5 list
public ArrayList<PictureTO> bestList(PictureTO to) {

  ArrayList<PictureTO> bestList = (ArrayList) sqlSession.selectList("best_picture_list", to);
  //System.out.println(bestList);
  return bestList;
}

 

2-4. TO

> PictureTO.java

package com.exam.model1.picture;

import java.util.ArrayList;

import com.exam.model1.pictureReply.ReplyTO;

public class PictureTO {
	private String no;
	private String subject;
	private String content;
	private String writer;
	private String wdate;
	private String hit;
	private String location;
	private String media;
	private String reply;
	private String heart;
	
	// 현재사용자가 좋아요 누른건지 아닌지
	private String hno;
	// 현재사용자가 즐겨찾기 누른건지 아닌지
	private String fno;
	// 글쓴이 프로필 사진 
	private String profile;
	// 현재 사용자 id
	private String nick;
	
	// 시작 게시물 번호
	private int startRowNum;
	// 끝 게시물 번호
	private int endRowNum;
	// 가져갈 게시물 갯수
	private int rowCount;
	
	
	
	
	
	public int getRowCount() {
		return rowCount;
	}
	public void setRowCount(int rowCount) {
		this.rowCount = rowCount;
	}
	public int getStartRowNum() {
		return startRowNum;
	}
	public void setStartRowNum(int startRowNum) {
		this.startRowNum = startRowNum;
	}
	public int getEndRowNum() {
		return endRowNum;
	}
	public void setEndRowNum(int endRowNum) {
		this.endRowNum = endRowNum;
	}
	public String getNick() {
		return nick;
	}
	public void setNick(String nick) {
		this.nick = nick;
	}
	public String getProfile() {
		return profile;
	}
	public void setProfile(String profile) {
		this.profile = profile;
	}
	public String getHno() {
		return hno;
	}
	public void setHno(String hno) {
		this.hno = hno;
	}
	public String getFno() {
		return fno;
	}
	public void setFno(String fno) {
		this.fno = fno;
	}
	public String getNo() {
		return no;
	}
	public void setNo(String no) {
		this.no = no;
	}
	public String getSubject() {
		return subject;
	}
	public void setSubject(String subject) {
		this.subject = subject;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public String getWriter() {
		return writer;
	}
	public void setWriter(String writer) {
		this.writer = writer;
	}
	public String getWdate() {
		return wdate;
	}
	public void setWdate(String wdate) {
		this.wdate = wdate;
	}
	public String getHit() {
		return hit;
	}
	public void setHit(String hit) {
		this.hit = hit;
	}
	public String getLocation() {
		return location;
	}
	public void setLocation(String location) {
		this.location = location;
	}
	public String getMedia() {
		return media;
	}
	public void setMedia(String media) {
		this.media = media;
	}
	public String getReply() {
		return reply;
	}
	public void setReply(String reply) {
		this.reply = reply;
	}
	public String getHeart() {
		return heart;
	}
	public void setHeart(String heart) {
		this.heart = heart;
	}
	

}

 

2-5. SQL(mapper)

> mapper.xml

<!-- 게시물 갯수 가져오기  - 검색기능이 추가되었을 때 sql문이 바껴야한다. -->
<select id="picture_count" parameterType="com.exam.model1.picture.PictureTO" resultType="int">
    select count(no) 
    from p_board
    <choose>
        <!-- if문과 비슷한 구조임, subject가 둘 null이 아니라면, 제목 검색 -->
        <when test="subject != null">
            where (subject like CONCAT('%',#{subject},'%'))
        </when>
        <!--content가 null이 아니라면-->
        <when test="content != null">
            where (content like CONCAT('%',#{content},'%'))
        </when>
        <!--writer가 null이 아니라면-->
        <when test="writer != null">
            where (writer like CONCAT('%',#{writer},'%'))
        </when>
        <!--location이 null이 아니라면-->
        <when test="location != null">
            where (location like CONCAT('%',#{location},'%'))
        </when>
    </choose>
</select>

<select id="picture_favorite_count" parameterType="com.exam.model1.pictureHeart.PictureHeartTO" resultType="int">
  select count(*) 
  from p_heart
  where userid like '${ userid }'
</select>

<!-- 로그인 전 사진자랑 리스트 -->
<select id="picture_list" resultType="com.exam.model1.picture.PictureTO">
    select b.no, subject, content, writer, date_format(wdate,'%Y-%m-%d') wdate, hit, location, media, reply, heart, u.profile
    from p_board b left outer join user u
    on b.writer = u.nick
    <choose>
        <!-- if문과 비슷한 구조임, subject가 둘 null이 아니라면, 제목 검색 -->
        <when test="subject != null">
            where (subject like CONCAT('%',#{subject},'%'))
        </when>
        <!--content가 null이 아니라면-->
        <when test="content != null">
            where (content like CONCAT('%',#{content},'%'))
        </when>
        <!--writer가 null이 아니라면-->
        <when test="writer != null">
            where (writer like CONCAT('%',#{writer},'%'))
        </when>
        <!--location이 null이 아니라면-->
        <when test="location != null">
            where (location like CONCAT('%',#{location},'%'))
        </when>
    </choose>
    order by b.no desc
    limit #{startRowNum}, #{rowCount}
</select>

<!-- 로그인 후 사진자랑 리스트 -->
<select id="picture_list_login" parameterType="com.exam.model1.picture.PictureTO" resultType="com.exam.model1.picture.PictureTO">
    select b.no, subject, content, writer, date_format(wdate,'%Y-%m-%d') wdate, hit, location, media, reply, heart, h.hno, f.fno, u.profile
    from p_board b left outer join p_heart h
    on #{nick} = h.userid and b.no = h.bno
    left outer join p_favorite f
    on #{nick} = f.userid and b.no = f.bno
    left outer join user u
    on b.writer = u.nick 
    <choose>
        <!-- if문과 비슷한 구조임, subject가 둘 null이 아니라면, 제목 검색 -->
        <when test="subject != null">
            where (subject like CONCAT('%',#{subject},'%'))
        </when>
        <!--content가 null이 아니라면-->
        <when test="content != null">
            where (content like CONCAT('%',#{content},'%'))
        </when>
        <!--writer가 null이 아니라면-->
        <when test="writer != null">
            where (writer like CONCAT('%',#{writer},'%'))
        </when>
        <!--location이 null이 아니라면-->
        <when test="location != null">
            where (location like CONCAT('%',#{location},'%'))
        </when>
    </choose>
    order by b.no desc
    limit #{startRowNum}, #{rowCount}
</select>

<!-- 사진자랑 BEST5 리스트 -->
<select id="best_picture_list" parameterType="com.exam.model1.picture.PictureTO" resultType="com.exam.model1.picture.PictureTO">
    select no, writer, hit, location, media, reply, heart
    from p_board
    <choose>
        <!-- if문과 비슷한 구조임, subject가 둘 null이 아니라면, 제목 검색 -->
        <when test="subject != null">
            where (subject like CONCAT('%',#{subject},'%'))
        </when>
        <!--content가 null이 아니라면-->
        <when test="content != null">
            where (content like CONCAT('%',#{content},'%'))
        </when>
        <!--writer가 null이 아니라면-->
        <when test="writer != null">
            where (writer like CONCAT('%',#{writer},'%'))
        </when>
        <!--location이 null이 아니라면-->
        <when test="location != null">
            where (location like CONCAT('%',#{location},'%'))
        </when>
    </choose>
    order by heart desc
    limit 5
</select>

 

 

 

 

 

 

 

*** 참고 ***

> views > picture > ajax_page.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>



<c:forEach var="tmp" items="${list }">

	<div class="col-3 card-inner">
		<div class="thumb">
			<div class="card-img" type="button" idx="${tmp.no }" data-bs-toggle="modal" data-bs-target="#viewModal${tmp.no }">
				<div class="box">
					<img src="./upload/picture/${tmp.media }" class="card-img-top  img-wrapper"alt="...">
				</div>
			</div>
			<div class="card-body">
				<div class="row">
					<div class="col-xsm card-title">${tmp.writer }</div>
					<div class="col-sm card-heart" id="card-heart">
						<c:choose>
							<%-- 로그인 상태일때 - 하트 클릭 되게 --%>
							<c:when test="${not empty sessionScope.nick}">
								<c:choose>
									<c:when test="${empty tmp.hno}">
										<%-- 빈 하트일때 --%>
										<span> <a idx="${tmp.no }" href="javascript:"
											class="heart-click heart_icon${tmp.no }"><svg
													xmlns="http://www.w3.org/2000/svg" width="16" height="16"
													fill="currentColor" class="bi bi-suit-heart"
													viewBox="0 0 16 16">
							  							  <path
														d="M8 6.236l-.894-1.789c-.222-.443-.607-1.08-1.152-1.595C5.418 2.345 4.776 2 4 2 2.324 2 1 3.326 1 4.92c0 1.211.554 2.066 1.868 3.37.337.334.721.695 1.146 1.093C5.122 10.423 6.5 11.717 8 13.447c1.5-1.73 2.878-3.024 3.986-4.064.425-.398.81-.76 1.146-1.093C14.446 6.986 15 6.131 15 4.92 15 3.326 13.676 2 12 2c-.777 0-1.418.345-1.954.852-.545.515-.93 1.152-1.152 1.595L8 6.236zm.392 8.292a.513.513 0 0 1-.784 0c-1.601-1.902-3.05-3.262-4.243-4.381C1.3 8.208 0 6.989 0 4.92 0 2.755 1.79 1 4 1c1.6 0 2.719 1.05 3.404 2.008.26.365.458.716.596.992a7.55 7.55 0 0 1 .596-.992C9.281 2.049 10.4 1 12 1c2.21 0 4 1.755 4 3.92 0 2.069-1.3 3.288-3.365 5.227-1.193 1.12-2.642 2.48-4.243 4.38z" />
														</svg></a>
										</span>
									</c:when>
									<c:otherwise>
										<%-- 꽉찬 하트일때 --%>
										<span> <a idx="${tmp.no }" href="javascript:"
											class="heart-click heart_icon${tmp.no }"><svg
													xmlns="http://www.w3.org/2000/svg" width="16" height="16"
													fill="currentColor" class="bi bi-suit-heart-fill"
													viewBox="0 0 16 16">
														  <path
														d="M4 1c2.21 0 4 1.755 4 3.92C8 2.755 9.79 1 12 1s4 1.755 4 3.92c0 3.263-3.234 4.414-7.608 9.608a.513.513 0 0 1-.784 0C3.234 9.334 0 8.183 0 4.92 0 2.755 1.79 1 4 1z" />
														</svg></a>
										</span>
									</c:otherwise>
								</c:choose>
							</c:when>
							<%-- 로그인 상태가 아닐때  - 하트클릭 안되게 --%>
							<c:otherwise>
								<span> <a href="javascript:" class="heart-notlogin">
										<svg class="heart3" xmlns="http://www.w3.org/2000/svg"
											width="16" height="16" fill="currentColor"
											class="bi bi-suit-heart" viewBox="0 0 16 16">
							  					  <path
												d="M8 6.236l-.894-1.789c-.222-.443-.607-1.08-1.152-1.595C5.418 2.345 4.776 2 4 2 2.324 2 1 3.326 1 4.92c0 1.211.554 2.066 1.868 3.37.337.334.721.695 1.146 1.093C5.122 10.423 6.5 11.717 8 13.447c1.5-1.73 2.878-3.024 3.986-4.064.425-.398.81-.76 1.146-1.093C14.446 6.986 15 6.131 15 4.92 15 3.326 13.676 2 12 2c-.777 0-1.418.345-1.954.852-.545.515-.93 1.152-1.152 1.595L8 6.236zm.392 8.292a.513.513 0 0 1-.784 0c-1.601-1.902-3.05-3.262-4.243-4.381C1.3 8.208 0 6.989 0 4.92 0 2.755 1.79 1 4 1c1.6 0 2.719 1.05 3.404 2.008.26.365.458.716.596.992a7.55 7.55 0 0 1 .596-.992C9.281 2.049 10.4 1 12 1c2.21 0 4 1.755 4 3.92 0 2.069-1.3 3.288-3.365 5.227-1.193 1.12-2.642 2.48-4.243 4.38z" />
												</svg>
								</a>
								</span>
							</c:otherwise>
						</c:choose>
						<span id="heart${tmp.no }">${tmp.heart }</span>

						<%-- 댓글 아이콘 --%>
						<span> <svg xmlns="http://www.w3.org/2000/svg" width="16"
								height="16" fill="currentColor" class="bi bi-chat-dots"
								viewBox="0 0 16 16">
											<path
									d="M5 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" />
											<path
									d="M2.165 15.803l.02-.004c1.83-.363 2.948-.842 3.468-1.105A9.06 9.06 0 0 0 8 15c4.418 0 8-3.134 8-7s-3.582-7-8-7-8 3.134-8 7c0 1.76.743 3.37 1.97 4.6a10.437 10.437 0 0 1-.524 2.318l-.003.011a10.722 10.722 0 0 1-.244.637c-.079.186.074.394.273.362a21.673 21.673 0 0 0 .693-.125zm.8-3.108a1 1 0 0 0-.287-.801C1.618 10.83 1 9.468 1 8c0-3.192 3.004-6 7-6s7 2.808 7 6c0 3.193-3.004 6-7 6a8.06 8.06 0 0 1-2.088-.272 1 1 0 0 0-.711.074c-.387.196-1.24.57-2.634.893a10.97 10.97 0 0 0 .398-2z" />
										</svg>
						</span> <span id="reply${tmp.no }">${tmp.reply }</span>

						<%-- 눈깔 아이콘 --%>
						<span> <svg xmlns="http://www.w3.org/2000/svg" width="16"
								height="16" fill="currentColor" class="bi bi-eye"
								viewBox="0 0 16 16">
											<path
									d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z" />
											<path
									d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z" />
										</svg>
						</span> <span id="hit${tmp.no }">${tmp.hit }</span>
					</div>
				</div>
			</div>
		</div>
	</div>

	<!-- view Modal -->
	<div class="modal fade" id="viewModal${tmp.no }" tabindex="-1"
		aria-labelledby="exampleModalLabel" aria-hidden="true" role="dialog">
		<div class="modal-dialog modal-lg">
			<div class="modal-content">
				<div class="modal-header">
					<h5 class="modal-title" id="m_subject">[${tmp.location}]&nbsp;${tmp.subject }</h5>
					<button type="button" class="btn-close" data-bs-dismiss="modal"
						aria-label="Close"></button>
				</div>
				<div class="modal-body">
					<section class="modal-section">
						<!-- 글쓴이 프로필 사진이 원형으로 나오는 부분 -->
						<div class="row">
							<div class="col-10">
								<span id="m_writer_profile">
									<a href="other_profile.do?other_nick=${tmp.writer }">
										<img id="profileImage" src='./upload/profile/${tmp.profile }' />
									</a>
								</span>&nbsp;&nbsp;<span id="m_writer">${tmp.writer }</span>
							</div>
							<div class="col-2">${tmp.wdate }</div>
						</div>
					</section>
					<section class="modal-section-media">
						<div class="container" id="m_media">
							<img class="w-100" id="media-image"
								src='./upload/picture/${tmp.media }' />
						</div>
					</section>

					<section class="modal-section">
						<div id="m_heart_reply_hit">
							<span id="m_heart_icon"> <c:choose>
									<%-- 로그인 상태일때 - 하트 클릭 되게 --%>
									<c:when test="${not empty sessionScope.nick}">
										<c:choose>
											<c:when test="${empty tmp.hno}">
												<%-- 빈 하트일때 --%>
												<a idx="${tmp.no}" href="javascript:"
													class="heart-click heart_icon${tmp.no }"><svg
														xmlns="http://www.w3.org/2000/svg" width="16" height="16"
														fill="currentColor" class="bi bi-suit-heart"
														viewBox="0 0 16 16">
							  							  <path
															d="M8 6.236l-.894-1.789c-.222-.443-.607-1.08-1.152-1.595C5.418 2.345 4.776 2 4 2 2.324 2 1 3.326 1 4.92c0 1.211.554 2.066 1.868 3.37.337.334.721.695 1.146 1.093C5.122 10.423 6.5 11.717 8 13.447c1.5-1.73 2.878-3.024 3.986-4.064.425-.398.81-.76 1.146-1.093C14.446 6.986 15 6.131 15 4.92 15 3.326 13.676 2 12 2c-.777 0-1.418.345-1.954.852-.545.515-.93 1.152-1.152 1.595L8 6.236zm.392 8.292a.513.513 0 0 1-.784 0c-1.601-1.902-3.05-3.262-4.243-4.381C1.3 8.208 0 6.989 0 4.92 0 2.755 1.79 1 4 1c1.6 0 2.719 1.05 3.404 2.008.26.365.458.716.596.992a7.55 7.55 0 0 1 .596-.992C9.281 2.049 10.4 1 12 1c2.21 0 4 1.755 4 3.92 0 2.069-1.3 3.288-3.365 5.227-1.193 1.12-2.642 2.48-4.243 4.38z" />
														</svg></a>

											</c:when>
											<c:otherwise>
												<%-- 꽉찬 하트일때 --%>
												<a idx="${tmp.no}" href="javascript:"
													class="heart-click heart_icon${tmp.no }"><svg
														xmlns="http://www.w3.org/2000/svg" width="16" height="16"
														fill="currentColor" class="bi bi-suit-heart-fill"
														viewBox="0 0 16 16">
														  <path
															d="M4 1c2.21 0 4 1.755 4 3.92C8 2.755 9.79 1 12 1s4 1.755 4 3.92c0 3.263-3.234 4.414-7.608 9.608a.513.513 0 0 1-.784 0C3.234 9.334 0 8.183 0 4.92 0 2.755 1.79 1 4 1z" />
														</svg></a>
											</c:otherwise>
										</c:choose>
									</c:when>
									<%-- 로그인 상태가 아닐때  - 하트클릭 안되게 --%>
									<c:otherwise>
										<a href="javascript:" class="heart-notlogin"> <svg
												class="heart3" xmlns="http://www.w3.org/2000/svg" width="16"
												height="16" fill="currentColor" class="bi bi-suit-heart"
												viewBox="0 0 16 16">
							  					  <path
													d="M8 6.236l-.894-1.789c-.222-.443-.607-1.08-1.152-1.595C5.418 2.345 4.776 2 4 2 2.324 2 1 3.326 1 4.92c0 1.211.554 2.066 1.868 3.37.337.334.721.695 1.146 1.093C5.122 10.423 6.5 11.717 8 13.447c1.5-1.73 2.878-3.024 3.986-4.064.425-.398.81-.76 1.146-1.093C14.446 6.986 15 6.131 15 4.92 15 3.326 13.676 2 12 2c-.777 0-1.418.345-1.954.852-.545.515-.93 1.152-1.152 1.595L8 6.236zm.392 8.292a.513.513 0 0 1-.784 0c-1.601-1.902-3.05-3.262-4.243-4.381C1.3 8.208 0 6.989 0 4.92 0 2.755 1.79 1 4 1c1.6 0 2.719 1.05 3.404 2.008.26.365.458.716.596.992a7.55 7.55 0 0 1 .596-.992C9.281 2.049 10.4 1 12 1c2.21 0 4 1.755 4 3.92 0 2.069-1.3 3.288-3.365 5.227-1.193 1.12-2.642 2.48-4.243 4.38z" />
												</svg></a>
									</c:otherwise>
								</c:choose>
							</span> <span id="m_heart${tmp.no }">${tmp.heart }</span>

							<%-- 댓글 수 --%>
							<span> <a idx="${tmp.no}" href="javascript:"
								class="open_reply_list" data-bs-toggle="collapse"
								data-bs-target="#reply_card${tmp.no }" aria-expanded="false"
								aria-controls="collapseExample"> <svg
										xmlns="http://www.w3.org/2000/svg" width="16" height="16"
										fill="currentColor" class="bi bi-chat-dots"
										viewBox="0 0 16 16">
													<path
											d="M5 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" />
													<path
											d="M2.165 15.803l.02-.004c1.83-.363 2.948-.842 3.468-1.105A9.06 9.06 0 0 0 8 15c4.418 0 8-3.134 8-7s-3.582-7-8-7-8 3.134-8 7c0 1.76.743 3.37 1.97 4.6a10.437 10.437 0 0 1-.524 2.318l-.003.011a10.722 10.722 0 0 1-.244.637c-.079.186.074.394.273.362a21.673 21.673 0 0 0 .693-.125zm.8-3.108a1 1 0 0 0-.287-.801C1.618 10.83 1 9.468 1 8c0-3.192 3.004-6 7-6s7 2.808 7 6c0 3.193-3.004 6-7 6a8.06 8.06 0 0 1-2.088-.272 1 1 0 0 0-.711.074c-.387.196-1.24.57-2.634.893a10.97 10.97 0 0 0 .398-2z" />
												</svg>
							</a>
							</span> <span id="m_reply${tmp.no }">${tmp.reply }</span>

							<%-- 조회수 --%>
							<span> <svg xmlns="http://www.w3.org/2000/svg" width="16"
									height="16" fill="currentColor" class="bi bi-eye"
									viewBox="0 0 16 16">
													<path
										d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z" />
													<path
										d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z" />
												</svg>
							</span> <span id="m_hit${tmp.no }">${tmp.hit }</span>
						</div>
					</section>
					<section class="modal-section">
						<div id="m_content">${tmp.content }</div>
					</section>
				</div>

				<div>
					<!-- 댓글  -->
					<div class="collapse" id="reply_card${tmp.no }">
						<section class="modal-section">
							<div class="card card-body">
								<!-- 댓글 목록 -->
								<div class="reply-list reply-list${tmp.no }">
									<!-- 댓글이 목록이 들어가는 곳 -->
								</div>
								<!-- 댓글 작성 => 로그인한 상태여야만 댓글작성 칸이 나온다. -->
								<c:if test="${not empty sessionScope.nick }">
									<div class="row reply_write">
										<div class="col-1">
											<a href="other_profile.do?other_nick=${tmp.writer }">
												<img id="write_reply_profileImage"
													src="./upload/profile/${sessionScope.profile }" />
											</a>
										</div>
										<div class="col-8" class="input_reply_div">
											<input class="w-100 form-control" id="input_reply${tmp.no}"
												type="text" placeholder="댓글입력...">
										</div>
										<div class="col-3 ">
											<button type="button" idx="${tmp.no }"
												class="btn btn-success mb-1 write_reply">댓글&nbsp;달기</button>
										</div>
									</div>
								</c:if>
							</div>
						</section>
					</div>
				</div>
				<div id="modify_delete">
					<%-- 수정/삭제버튼 --%>
					<c:if test="${not empty nick and tmp.writer eq sessionScope.nick}">
						<div class='modal-footer'>
							<button type="button" class="btn btn-secondary"
								data-bs-toggle="modal" data-bs-target="#modifyModal${tmp.no}">수정</button>
							<button type="button" class="btn btn-dark" data-bs-toggle="modal"
								data-bs-target="#deleteModal${tmp.no}">삭제</button>
						</div>
					</c:if>
				</div>
			</div>
		</div>
	</div>
	<%-- view Modal 끝 --%>

	<!-- modify Modal -->
	<div class="modal fade" id="modifyModal${tmp.no}" tabindex="-1"
		aria-labelledby="exampleModalLabel" aria-hidden="true">
		<div class="modal-dialog">
			<div class="modal-content">
				<div class="modal-header">
					<h5 class="modal-title" id="exampleModalLabel">modify confirm</h5>
					<button type="button" class="btn-close" data-bs-dismiss="modal"
						aria-label="Close"></button>
				</div>
				<div class="modal-body">수정페이지로 이동 하시겠습니까?</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-secondary"
						data-bs-dismiss="modal">아니요</button>
					<button type="button" class="btn btn-primary"
						onclick="location.href='./picture_modify.do?no=${tmp.no}'">네</button>
				</div>
			</div>
		</div>
	</div>
	<!-- modify Modal 끝 -->

	<!-- delete Modal -->
	<div class="modal fade" id="deleteModal${tmp.no}" tabindex="-1"
		aria-labelledby="exampleModalLabel" aria-hidden="true">
		<div class="modal-dialog">
			<div class="modal-content">
				<div class="modal-header">
					<h5 class="modal-title" id="exampleModalLabel">delete confirm</h5>
					<button type="button" class="btn-close" data-bs-dismiss="modal"
						aria-label="Close"></button>
				</div>
				<div class="modal-body">정말 삭제하시겠습니까?!!</div>
				<div class="modal-footer">
					<button type="button" class="btn btn-secondary"
						data-bs-dismiss="modal">아니요</button>
					<button type="button" class="btn btn-primary"
						onclick="location.href='javascript:PictureDelete(${tmp.no})'">네</button>
				</div>
			</div>
		</div>
	</div>
	<!-- delete Modal 끝 -->

</c:forEach>
728x90
Comments