오늘은 매장 검색 기능을 구현해보도록 하겠습니다
매장 검색의 경우 매장목록페이지와 대부분 동일합니다
기존 매장목록페이지는 홈화면에서 특정 메뉴 클릭시(치킨, 또는 피자등)
메뉴코드와 사용자의 주소코드로 DB에서 데이터를 가져와 화면에 뿌려줍니다
매장검색의 경우 메뉴코드 대신 사용자가 입력한 키워드와 주소코드를
가지고 DB에서 데이터를 가지고 올것이기 때문에 기존 매장목록을 가져오는
코드에서 메뉴코드만 키워드로 바꿔주시면 됩니다
//매장검색
@GetMapping("/store/search")
public String search(String keyword, Integer address1, Model model) {
if(keyword != null) {
List<StoreDto> storeList = storeService.storeSearch(keyword, address1 / 100);
model.addAttribute("storeList", storeList);
model.addAttribute("keyword", keyword);
}
return "store/search";
}
//매장 검색 페이징
@GetMapping("/api/store/storeSearch")
public ResponseEntity<List<StoreDto>> storeSearch(String keyword, int address1, int page) {
List<StoreDto> storeList = storeService.storeSearch(keyword, address1 / 100, page);
return ResponseEntity.ok().body(storeList);
}
매장검색페이지 또한 무한스크롤을 이용한 페이징처리를 할것이기 때문에
Controller와 ApiController를 둘다 이용합니다
매장검색시 최초 1~10개의 데이터는 Controller를 타며 그 이후 데이터는
Ajax를 이용해 ApiController를 타고 비동기적으로 화면에 데이터를 뿌려줍니다
//매장검색
@Transactional
public List<StoreDto> storeSearch(String keyword, int address){
return storeSearch(keyword, address, 1);
}
//매장검색 무한스크롤 페이징
@Transactional
public List<StoreDto> storeSearch(String keyword, int address1, int page) {
Page p = new Page(page, 10);
Map<String, Object> map = new HashMap<>();
map.put("keyword", keyword);
map.put("address1", address1);
map.put("firstList", p.getFirstList());
map.put("lastList", p.getLastList());
System.out.println("페이지 시작 = " + p.getFirstList() + " 페이지 끝 = " + p.getLastList());
return storeMapper.storeSearch(map);
}
//매장 검색
public List<StoreDto> storeSearch(Map<String, Object> map);
<select id="storeSearch" resultType="com.han.delivery.dto.StoreDto">
WITH R_COUNT AS (
SELECT STORE_ID
,ROUND(AVG(SCORE), 1) SCORE
,COUNT(REVIEW_CONTENT) REVIEW_COUNT
,COUNT(BOSS_COMMENT) BOSS_COMMENT_COUNT
FROM DL_REVIEW
GROUP BY STORE_ID
),
STORE AS (
SELECT S.*,
T.*
FROM DL_STORE S
LEFT JOIN R_COUNT T
ON S.ID = T.STORE_ID
WHERE STORE_NAME LIKE '%${keyword}%'
AND STORE_ADDRESS1 LIKE '${address}%')
SELECT * FROM
(SELECT ROWNUM RN,
RESULT.*
FROM
(SELECT C.*
,'true' IS_OPEN
FROM STORE C
WHERE TO_CHAR(SYSTIMESTAMP, 'HH24') BETWEEN OPENING_TIME AND CLOSING_TIME
UNION ALL
SELECT C.*
,'false' IS_OPEN
FROM STORE C
WHERE TO_CHAR(SYSTIMESTAMP, 'HH24') NOT BETWEEN OPENING_TIME AND CLOSING_TIME
) RESULT
)
WHERE RN BETWEEN #{firstList } AND ${lastList }
</select>
매장목록을 뿌려주는 storeList쿼리에서 WHERE절만 변경하였고 그외에는
변경사항이 없으므로 설명은 생략하도록 하겠습니다
이제 기존 static폴더와 views폴더안에 store폴더에 화면을 구성하기 위해
search라는 이름으로 css, js, jsp를 추가해주세요
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/views/layout/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/views/layout/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="" name="address1" id="deliveryAddress1">
</div>
<div>
<button type="button" class="word_delete"><i class="fas fa-times"></i></button>
</div>
</div>
</form>
<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/views/store/store-li.jsp" %>
</c:forEach>
</ul>
</div>
</main>
<%@ include file="/WEB-INF/views/layout/nav.jsp" %>
<%@ include file="/WEB-INF/views/layout/footer.jsp" %>
<script type="text/javascript" src="/js/store/search.js" ></script>
<script type="text/javascript" src="/js/home.js" ></script>
</body>
</html>
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;
}
}
function check() {
const keyword = $(".search").val().replaceAll(" ","");
if(keyword == "" ) {
return false;
}
if($("#deliveryAddress1").val() == "" ) {
modifyAddress();
swal({
title: "주소를 입력해주세요",
text: "현재 주소지를 기준으로 검색됩니다."
});
return false;
}
return true;
}
$(document).ready(function() {
let winHeight = 0;
let docHeight = 0;
let page = 1;
let keyword = $(".search").val().replaceAll(" ","");
let address1 = $("#deliveryAddress1").val();
let run = false;
$(window).scroll(function(){
winHeight = $(window).height();
docHeight = $(document).height();
const top = $(window).scrollTop();
if(docHeight <= winHeight + top + 10 ) {
if(run) {
return;
}
console.log("페이지 추가");
page++;
run = true;
const data = {
page : page,
keyword : keyword,
address1 : address1
}
$.ajax({
url: "/api/store/storeSearch",
type: "GET",
data : data
})
.done(function(result){
const storeHtml = storeList(result);
$(".store").append(storeHtml);
if(storeHtml != "") {
run = false;
}
})
.fail(function(data, textStatus, errorThrown){
swal("다시 시도해주세요");
})
} // if
}) // scroll
function storeList(result){
let html = "";
for(var i=0;i<result.length;i++) {
const store = result[i];
const id = store.id;
const storeImg = store.storeImg;
const storeThumb = store.storeThumb;
const storeName = store.storeName;
const deliveryTime = store.deliveryTime;
const mindelivery = store.mindelivery;
const deliveryTip = store.deliveryTip;
const score = store.score.toFixed(1);
const reviewCount = store.reviewCount;
const bossCommentCount = store.bossCommentCount;
const openingTime = store.openingTime;
const closingTime = store.closingTime;
let scoreHtml = "";
for(var j=0;j<5;j++) {
if(Math.round(score) > j) {
scoreHtml += "<i class='fas fa-star'></i> ";
} else {
scoreHtml += "<i class='far fa-star'></i> ";
}
}
let isOpenHtml = "";
if(store.isOpen == "false") {
isOpenHtml = `<div class="is_open">
<a href="/store/${id }/detail">지금은 준비중입니다</a>
</div>`;
}
html +=
`<li >
<div class="img_box">
<a href="/store/${id }/detail"><img src="${storeImg }" alt="이미지"></a>
</div>
<div class="info_box">
<h2><a href="/store/${id }/detail">${storeName }</a></h2>
<a href="/store/${id }/detail">
<span>
<span>평점 ${score }</span>
<span class="score_box">
${scoreHtml}
</span>
</span>
<span>
<span>리뷰 ${reviewCount }</span>
<span>사장님 댓글 ${bossCommentCount }</span>
</span>
<span>
<span>최소주문금액 ${mindelivery }원</span>
<span>배달팁 ${deliveryTip }원</span>
</span>
<span>배달시간 ${deliveryTime }분</span>
</a>
</div>
${isOpenHtml}
</li>`;
}
return html;
}
});
네이버로 메일보내드렸습니다 확인한번 해주시면 감사하겠습니다 .