2021. 12. 11. 19:12ㆍ스프링부트
store 폴더 아래에 jsp, css, js를 추가합니다
search.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="/WEB-INF/view/include/link.jsp" %> <link rel="stylesheet" href="/css/layout/nav.css" > <link rel="stylesheet" href="/css/store/search.css" > <link rel="stylesheet" href="/css/store/store-li.css"> <%@ include file="/WEB-INF/view/include/header.jsp" %> <main> <form action="/store/search" method="get" onsubmit="return check()" > <div class="input_box"> <div> <label for="submit"> <i class="fas fa-search"></i> </label> <input type="submit" id="submit"> </div> <div> <input type="text" class="search" name="keyword" maxlength="33" value="${keyword }" placeholder="어떤 가게를 찾으시나요?" autofocus > <div class="info">현재 주소지를 기준으로 검색됩니다.</div> <input type="hidden" value="${BMaddress.address1 }" name="address1" id="deleveryAddress1"> <%@ include file="/WEB-INF/view/include/modifyAddress.jsp" %> </div> <div> <button type="button" class="word_delete"><i class="fas fa-times"></i></button> </div> </div> </form> <div class="search_word_head"> <h2>최근 검색어</h2> <button>전체삭제</button> </div> <div class="search_word"> <ul> <c:if test="${!empty keywordList }"> <c:forEach items="${keywordList }" var="keywordList"> <li> <span>${keywordList }</span> <button><i class="fas fa-times"></i></button> </li> </c:forEach> </c:if> </ul> </div> <div class="box"> <c:if test="${noSearch }"> <div class="no_search">검색 결과가 없습니다</div> </c:if> <ul class="store"> <c:forEach items="${storeList }" var="storeList" > <%@ include file="/WEB-INF/view/store/store-li.jsp" %> </c:forEach> </ul> </div> </main> <%@ include file="/WEB-INF/view/include/nav.jsp" %> <%@ include file="/WEB-INF/view/include/footer.jsp" %> <script type="text/javascript" src="/js/store/search.js" ></script> </body> </html>
search.css
main { width: 100%; max-width: 1200px; margin: 0 auto; min-height: calc(100vh - 312px); } main form { margin: 20px auto 50px; } main form .input_box { width: 50%; height: 45px; background: #F6F6F6; border-radius: 10px; margin: 0 auto; display: flex; } main form .input_box>div:first-child { width: 50px; display: flex; align-items: center; } main form .input_box>div:nth-child(2) { width: 100%; } main form .input_box>div:nth-child(3) { width: 50px; display: flex; align-items: center; justify-content: center; } /* 검색창 텍스트 지우기버튼 */ main form .input_box>div:nth-child(3) button { width: 20px; height: 20px; border: none; color: #fff; background: #999999; border-radius: 50%; display: none; } /* main form .input_box>div:nth-child(3) button i { position: relative; top: 1px; } */ main form input[type=text] { height: 100%; width: 100%; font-size: 1.8rem; background: none; border: none; padding-left: 5px; } main .info { text-align: center; font-weight: bold; margin-top: 5px; } .search_word_head { margin-left: 15px; } .search_word { margin-bottom: 40px; box-shadow: 0px 2px 3px 0px rgb(0 0 0/ 25%); border-bottom: 1px solid #ddd; } i.fa-search { font-size: 1.8rem; color: #999999; margin-left: 15px; } main form input[type=submit] { display: none; } .search_word_head { display: flex; margin-bottom: 5px; } .search_word_head button { border: none; border-radius: 20px; background: #F6F6F6; padding: 5px 10px 5px 10px; margin-left: 10px; } .search_word ul { width: 98%; margin: 10px auto; white-space: nowrap; overflow-x: auto; overflow-y: hidden; } .search_word ul::-webkit-scrollbar { background: none; } .search_word li { border-radius: 20px; background: #ECFAFA; margin: 0 5px 5px 5px; padding: 5px 10px 5px 10px; overflow-x: auto; display: inline-block; } .search_word li span, .search_word li i { color: #30A9A6; cursor: pointer; } .search_word li button { border: none; background: none; margin-left: 15px; } .no_search { font-size: 2.3rem; text-align: center; } @media ( max-width :1024px) { main { width: 100%; } main form .input_box { width: 70%; } .box .store li { width: 100%; margin: 20px auto 0px; border: none; border-radius: 0; border-bottom: 2px solid #ddd; } } @media ( max-width :767px) { main { min-height: calc(100vh - 247px); } main form .input_box { width: 95%; } main form .input_box>div:first-child { width: 40px; } i.fa-search { font-size: 1.6rem; } .search_word { margin-bottom: 15px; } }
search.js
inputCheck(); $("input[name='keyword']").keyup(function(key){ inputCheck(); // 모바일 검색버튼 if(key.keyCode == 13) { $("#submit").click(); } }) // 검색창 텍스트 지우기버튼 $(".word_delete").click(function(){ $("input[name='keyword']").val(""); inputCheck(); }) // 검색어 전체삭제 $(".search_word_head button").click(function(){ $.ajax({ url : "/store/keyword-all", type : "DELETE", success : function(){ $(".search_word li").css("display" , "none"); } // success }); // ajax }) // 검색어 1개 삭제 $(document).on("click", ".search_word li button", function(){ const keyword = $(this).siblings().text(); const index = $(this).parent("li").index(); $.ajax({ url : "/store/keyword-one", type : "DELETE", data : {keyword : keyword}, success : function(){ $(".search_word li").eq(index).remove(); } // success }); // ajax }) // 최근 검색어 클릭시 재검색 $(document).on("click" ,".search_word span" , function(){ $(".search").val($(this).text()); $("#submit").click(); inputCheck(); }) function inputCheck(){ $("input[name='keyword']").val() == "" ? $(".word_delete").css("display" , "none") : $(".word_delete").css("display" , "block") } function check() { const keyword = $(".search").val().replaceAll(" ",""); if(keyword == "" ) { return false; } if($("#deleveryAddress1").val() == "" ) { modifyAddress(); swal({ title: "주소를 입력해주세요", text: "현재 주소지를 기준으로 검색됩니다." }); return false; } return true; }
StoreController
@GetMapping("/store/search") public String search(Integer address1, String keyword, Model model) { if(keyword != null) { // 쿠키저장 // db 불러오기 } return "store/search"; }
keyword가 없을땐 빈 페이지를 보여주고 있으면 검색어를 쿠키에 저장 후 db에서 가게 이름에 keyword가 포함된 가게목록을 불러옵니다
먼저 이전에 만든 CookieManager클래스에 검색어를 저장할 메서드를 추가합니다
public LinkedHashSet<String> saveKeyword(String keyword) throws Exception { final String KEYWORD = "KEYWORD"; final int LIST_SIZE = 5; String keywordList = findCookie(KEYWORD); LinkedHashSet<String> set = new LinkedHashSet<>(); if(keywordList == null) { set.add(keyword); addCookie(KEYWORD, set.toString()); return set; } set.add(keyword); StringTokenizer st = new StringTokenizer(keywordList, ", "); while(st.hasMoreTokens() && set.size() < LIST_SIZE) { String key = st.nextToken(); set.add(key); } addCookie(KEYWORD, set.toString()); return set; }
이전에 만든 likes메서드와 비슷하지만 여기서는
중복된 검색어는 저장하지 않고 순서를 유지하기 위해 LinkedHashSet을 사용했습니다
그리고 이 목록을 화면에 출력하기 위해 LinkedHashSet을 반환합니다
컨트롤러에서 메서드를 실행합니다
@GetMapping("/store/search") public String search(Integer address1, String keyword, Model model) throws Exception { CookieManager cm = new CookieManager(); if(keyword != null) { LinkedHashSet<String> keywordList = cm.saveKeyword(keyword); System.out.println("검색어 목록 : " + keywordList); model.addAttribute("keywordList", keywordList); } else { String[] keywordList = cm.findCookie("KEYWORD").split(", "); model.addAttribute("keywordList", keywordList); } return "store/search"; }
검색어가 있을 땐 retrurn된 set을 화면에 출력하고, 없을 땐 쿠키에 저장된 list문자열을 배열로 변환해 출력합니다

db에서 검색목록을 가져올 수 있게 수정합니다
StoreController
@GetMapping({"/store/search", "/store/search/{page}"}) public String search(Integer address1, String keyword, @PathVariable(required = false) Integer page, Model model) throws Exception { CookieManager cm = new CookieManager(); if(keyword != null) { LinkedHashSet<String> keywordList = cm.saveKeyword(keyword); model.addAttribute("keywordList", keywordList); Page p = new Page(page); List<Store> storeList = storeService.storeSearch(keyword, address1 / 100, p); model.addAttribute("keyword", keyword); if(storeList.size() == 0) { model.addAttribute("noSearch", true); } else { p.totalPage(storeList.get(0).getListCount()); model.addAttribute("page", p); model.addAttribute("storeList", storeList); } } else { String key = cm.findCookie("KEYWORD"); if(key != null) { String[] keywordList = key.split(", "); model.addAttribute("keywordList", keywordList); } } return "store/search"; }
페이징을 하기위해 매핑을 하나 더 추가했고
검색어와 페이지를 같이 화면에 전달합니다
Store클래스에 매장 수를 받을 필드를 추가합니다
private int listCount; // 매장 수
StoreService
List<Store> storeSearch(String keyword, int address, Page p);
StoreServiceImp
@Override public List<Store> storeSearch(String keyword, int address, Page p) { Map<String, Object> map = new HashMap<>(); map.put("keyword", keyword); map.put("address", address); map.put("firstList", p.getFirstList()); map.put("lastList", p.getLastList()); return storeDAO.storeSearch(map); }
StoreDAO
List<Store> storeSearch(Map<String, Object> map);
StoreDAOImp
@Override public List<Store> storeSearch(Map<String, Object> map) { return sql.selectList("store.storeSearch", map); }
StoreMapper
<select id="storeSearch" resultType="Store"> WITH R_COUNT AS ( SELECT R.STORE_ID ,ROUND(R.SCORE, 1) SCORE ,R.REVIEW_COUNT ,R.BOSS_COMMENT_COUNT FROM (SELECT STORE_ID ,AVG(SCORE) SCORE ,COUNT(REVIEW_CONTENT) REVIEW_COUNT ,COUNT(BOSS_COMMENT) BOSS_COMMENT_COUNT FROM BM_REVIEW GROUP BY STORE_ID ) R ), STORE AS ( SELECT S.* ,T.* ,CASE WHEN MOD(24 - S.OPENING_TIME + S.CLOSING_TIME, 24) != 0 THEN MOD(24 - S.OPENING_TIME + S.CLOSING_TIME, 24) ELSE 24 END BS_TIME FROM BM_STORE S LEFT JOIN R_COUNT T ON S.ID = T.STORE_ID WHERE STORE_ADDRESS1 LIKE '${address}%' AND STORE_NAME LIKE '%${keyword}%' ) SELECT * FROM (SELECT ROWNUM RN, COUNT(*) OVER() list_count, RESULT.* FROM (SELECT C.* ,'true' IS_OPEN FROM STORE C WHERE TO_CHAR(SYSTIMESTAMP, 'HH24') BETWEEN OPENING_TIME AND OPENING_TIME + BS_TIME UNION ALL SELECT C.* ,'false' IS_OPEN FROM STORE C WHERE TO_CHAR(SYSTIMESTAMP, 'HH24') NOT BETWEEN OPENING_TIME AND OPENING_TIME + BS_TIME ) RESULT ) WHERE RN BETWEEN #{firstList } AND ${lastList } </select>


jsp에 pageBox.jsp를 추가합니다
<c:if test="${!empty storeList }"> <%@ include file="/WEB-INF/view/include/pageBox.jsp" %> </c:if>


페이지 버튼은 생겼지만 페이지를 이동하게되면 쿼리스트링이 사라지게 됩니다
${requestScope['javax.servlet.forward.query_string']}를 이용하면 현재 쿼리스트링을 가져올 수 있습니다
쿼리스트링을 가져올 수 있게 pageBox를 수정합니다
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fm" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <style> .page_box {display: flex; justify-content: center; margin: 20px 0; } .page_box li { border: 1px solid #999; border-right: none; width: 35px; height: 35px; text-align: center; line-height: 35px; } .page_box li:last-child { border-right: 1px solid #999; } .page_box li a { display: block; width: 100%; height: 100%; } .now_page { background: #30DAD9; color: #fff; cursor: default; } .now_page:hover { color: #fff; } @media(max-width :767px) { .page_box { margin-top: 20px; } .page_box li { width: 25px; height: 25px; line-height: 25px; font-size: 12px; } } </style> <c:set var="queryString" value="${requestScope['javax.servlet.forward.query_string']}" /> <c:if test="${!empty queryString}"> <c:set var="queryString" value="${'?'}${queryString}" /> </c:if> <c:set var="uri" value="${requestScope['javax.servlet.forward.request_uri']}" /> <c:set var="pathValiable" value="${'/' }${page.nowPage }" /> <c:set var="path" value="${fn:replace(uri, pathValiable, '') }${'/' }" /> <ul class="page_box"> <c:if test="${page.pageCount < page.firstPage }"> <li><a href="${path }${page.prevPage }${queryString }">이전</a></li> </c:if> <c:forEach begin="${page.firstPage }" end="${page.firstPage + page.pageCount - 1 }" var="i"> <c:if test="${i <= page.totalPage}"> <c:if test="${i != page.nowPage }"> <li><a href="${path }${i }${queryString }">${i }</a></li> </c:if> <c:if test="${i == page.nowPage }"> <li><a class="now_page" onclick="return false;" href="${path }${i }${queryString }">${i }</a></li> </c:if> </c:if> </c:forEach> <c:if test="${page.firstPage + page.pageCount <= page.totalPage }"> <li><a href="${path }${page.nextPage }${queryString }">다음</a></li> </c:if> </ul>

페이지 이동 시에도 쿼리스트링을 유지 할 수 있게되었습니다
매장상세페이지로 이동할수 있게 store-li.jsp를 수정합니다
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fm" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <c:set var="uri" value="${requestScope['javax.servlet.forward.request_uri']}" /> <c:choose> <c:when test="${fn:split(uri, '/')[0] == 'admin'}"> <c:set var="path" value="/admin/management" /> </c:when> <c:otherwise> <c:set var="path" value="/store" /> </c:otherwise> </c:choose> <li> <div class="img_box"> <a href="${path }/detail/${storeList.id }"><img src="${storeList.storeImg }" alt="이미지"></a> </div> <div class="info_box"> <h2><a href="${path }/detail/${storeList.id }">${storeList.storeName }</a></h2> <a href="${path }/detail/${storeList.id }"> <span> <span>평점 ${storeList.score }</span> <span class="score_box"> <c:forEach begin="0" end="4" var="i"> <c:if test="${Math.round(storeList.score) > i }"> <i class="far fas fa-star"></i> </c:if> <c:if test="${Math.round(storeList.score) <= i }"> <i class="far fa-star"></i> </c:if> </c:forEach> </span> </span> <span> <span>리뷰 ${storeList.reviewCount }</span> <span>사장님 댓글 ${storeList.bossCommentCount }</span> </span> <span> <span>최소주문금액 <fm:formatNumber value="${storeList.minDelevery }" pattern="###,###" />원</span> <span>배달팁 <fm:formatNumber value="${storeList.deleveryTip }" pattern="###,###" />원</span> </span> <span>배달시간 ${storeList.deleveryTime }분</span> </a> </div> <c:if test="${!storeList.isOpen}"> <div class="is_open"> <a href="/store/detail/${storeList.id }">지금은 준비중입니다</a> </div> </c:if> </li>
'스프링부트' 카테고리의 다른 글
스프링부트+jsp로 배달사이트 만들기-28 관리자페이지(가게 정보 수정, 메뉴 추가) (0) | 2021.12.17 |
---|---|
스프링부트+jsp로 배달사이트 만들기-27 관리자 가게상세/AOP (1) | 2021.12.17 |
스프링부트+jsp로 배달사이트 만들기-24 찜하기(비회원) (0) | 2021.12.10 |
스프링부트+jsp로 배달사이트 만들기-23 찜한 가게 목록 (0) | 2021.12.10 |
스프링부트+jsp로 배달사이트 만들기-22 찜하기(회원) (0) | 2021.12.10 |