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 |