SpringBoot 배달의민족-4 메인화면, 매장목록 구현 (다음 우편번호 API)

hanteng·2022년 5월 18일
2
post-thumbnail

저번시간에 회원가입, 로그인까지 구현을 완료했으니 이번에는 로그인 성공시 보여줄
메인화면과 음식점들을 보여줄 매장목록을 구현해보도록 하겠습니다
제일 먼저 사용자에게 메인화면을 보여주기 위해 controller패키지에
HomeController 클래스를 하나 추가해줍시다

HomeController.java 전체코드

package com.han.delivery.controller;

@Controller
public class HomeController {
	
	
	@GetMapping( {"/", "/home"} )
	public String signin() {
	    return "home";
	}
	
	

}

사용자가 아무주소도 입력하지 않거나 /home으로 접근시 메인페이지로 이동할수
있도록 해줍니다. 이제 views폴더안에 home.jsp를 하나 추가해주세요

home.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/home.css">
 
<%@ include file="/WEB-INF/views/layout/header.jsp" %>
 	<!-- 콘텐츠 -->
    <div class="wrap">
        <main>
            <section class="address_search">
                 <div id="search_box">
                     <div>
                         <input type="hidden" id="deliveryAddress1" placeholder="우편번호" value="" name="address1" readonly>
                         <input type="text" value= "" onclick="execution_daum_address()"
                             id="deliveryAddress2" readonly placeholder="주소를 입력해 주세요" name="address2"><br>
                     </div>
 
                     <div class="search_btn">
                         <label for="search_btn">
                             <i class="fas fa-search"></i>
                         </label>
 
                         <input type="button" name="search" id="search_btn">
 
                     </div>
 
                 </div>
            </section>
            <section class="category_box">
                <div class="box">
                    <ul class="category">
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/pizza2.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">피자</div>
                                </li>
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/chicken1.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">치킨</div>
                                </li>
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/hamburger4.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">패스트푸드</div>
                                </li>
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/bunsik1.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">분식</div>
                                </li>
                                
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/dessert2.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">카페/디저트</div>
                                </li>
                                
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/cutlet1.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">돈까스/일식</div>
                                </li>
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/chinese1.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">중국집</div>
                                </li>
                                
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/jockbal1.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">족발/보쌈</div>
                                </li>
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/jockbal2.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">야식</div>
                                </li>
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/bibimbap.jpg" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">한식</div>
                                </li>
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/jockbal3.png" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">1인분</div>
                                </li>
                    
                                <li>
									<div>
										<div class="img_box">
                                           <img src="/img/dosirac.jpg" alt="이미지">
										</div>
                                    </div>
                                    <div class="name">도시락</div>
                                </li>
                            </ul>
                	</div>
            </section>
        </main>
    </div>
    <!-- 콘텐츠 -->
 
 
    <!-- 하단 메뉴 -->
	<%@ include file="/WEB-INF/views/layout/nav.jsp" %>
    <!-- 하단 메뉴 -->
 
    <!-- 푸터 -->
    <%@ include file="/WEB-INF/views/layout/footer.jsp" %>
    <!-- 푸터 -->
 
 
 
</body>
<script src="/js/home.js"></script>
</html>

우리는 사용자가 주소입력을 하지 않으면 매장목록을 볼 수 없도록 할것입니다
따라서 제일 먼저 주소를 입력해야하며 이때 주소는 다음 우편번호 서비스를 사용하여
검색할수 있도록 다음 주소 API를 추가해줄겁니다 static폴더안에 js폴더를 하나
만들고 home.js를 추가해주세요

home.js 전체코드

/* 다음 주소 연동 */
function execution_daum_address() {
	new daum.Postcode({
		oncomplete : function(data) {
			// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분입니다.

			// 각 주소의 노출 규칙에 따라 주소를 조합한다.
			// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
			var addr = ''; // 주소 변수
			var extraAddr = ''; // 참고항목 변수

			//사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
			if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
				addr = data.roadAddress;
			} else { // 사용자가 지번 주소를 선택했을 경우(J)
				addr = data.jibunAddress;
			}

			// 사용자가 선택한 주소가 도로명 타입일때 참고항목을 조합한다.
			if (data.userSelectedType === 'R') {
				// 법정동명이 있을 경우 추가한다. (법정리는 제외)
				// 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
				if (data.bname !== '' && /[동|로|가]$/g.test(data.bname)) {
					extraAddr += data.bname;
				}
				// 건물명이 있고, 공동주택일 경우 추가한다.
				if (data.buildingName !== '' && data.apartment === 'Y') {
					extraAddr += (extraAddr !== '' ? ', '
							+ data.buildingName : data.buildingName);
				}
				// 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
				if (extraAddr !== '') {
					extraAddr = ' (' + extraAddr + ')';
				}
				// 추가해야할 코드
				// 주소변수 문자열과 참고항목 문자열 합치기
				addr += extraAddr;

			} else {
				addr += ' ';
			}
			setCookie("deliveryAddress1", data.zonecode, 30);
			setCookie("deliveryAddress2", addr, 30);
			
			$("#deliveryAddress1").val(data.zonecode);
			$("#deliveryAddress2").val(addr);
			

		}
	}).open();

}


//쿠키에 주소 저장 function
var setCookie = function(name, value, exp) {
	var date = new Date();
	date.setTime(date.getTime() + exp*24*60*60*1000);
	document.cookie = name + '=' + value + ';expires=' + date.toUTCString() + ';path=/';
};

//쿠키에서 주소 읽어오기 function
var getCookie = function(name) {
	var value = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
	return value? value[2] : null;
};



//주소 입력 확인
$(".category li").click(function(){
	let address1 = $("#deliveryAddress1").val();
	if(!address1) {
		swal("배달 받으실 주소를 입력해 주세요");
		return false;
	}
	
	const index = $(this).index();
	
	location.href = "/store/" + (100+index) + "/" +address1;
})


//페이지 진입시 쿠키에서 주소정보 읽어오기
$("#deliveryAddress1").val(getCookie("deliveryAddress1"));
$("#deliveryAddress2").val(getCookie("deliveryAddress2"));


다음 Api를 사용하여 그 결과를 우편번호 / 주소를 나눠서 저장할겁니다
이때 메인화면에 주소입력창에 당연히 검색한 주소값이 value값으로 들어가겠지만
한가지 더 신경써야할 부분이 있습니다. 우리가 배달사이트를 사용한다고 생각해보면
접속할때마다 주소를 입력해야 하는건 좋은방법이 아닙니다 따라서 검색한 결과를
다음번 접속시에도 사용할수 있도록 해줘야 하는데 여러가지 방법이 있습니다

첫번째로 우리가 이전에 생성한 회원테이블에 주소를 저장할 칼럼을 만들어 거기에
저장하는 방법입니다 하지만 이방법은 사용자가 접속할때마다 테이블을 참조해야 하며
비록 지금은 비회원의 사이트 접근을 막았지만 이후에 비회원도 접근이 가능하게 했을때
비회원의 주소를 저장할 테이블이 존재하지 않습니다

따라서 우리는 서버가 아닌 사용자의 쿠키에 주소를 저장해놓을겁니다
이 쿠키는 30일동안 유지되도록 할것이고 사용자가 메인화면에 접근시
해당 쿠키에서 데이터를 확인하여 이미 저장된 주소가 존재하면 그 값을
주소로 넣어줄겁니다

이제 사용자가 해당 메뉴를 클릭하면 현재 사용자가 입력한 주소와 가까이에 있는
매장들의 목록을 보여줘야 합니다 우리는 각각의 메뉴를 카테고리 코드로 구별할것이며
인덱스를 기준으로 피자는 100번 , 치킨은 101번과 같은 코드를 가지게 됩니다
사용자가 만약 피자를 클릭했고 사용자가 입력한 우편번호가 13541이라면
store/100/13541의 주소로 이동할것이며 우리는 13541를 100으로 나누어
DB에서 우편번호의 앞3자리가 이 135와 일치하는 매장들만 목록에 보여줄겁니다
일단 매장정보를 저장할 테이블을 하나 생성해줍시다

오라클 매장테이블 추가

create table DL_STORE (
    id NUMBER primary key,
    category number NOT NULL,
    store_name varchar2(100) NOT NULL,
    store_address1 varchar2(200) NOT NULL,
    store_address2 varchar2(200) NOT NULL,
    store_address3 varchar2(200),
    store_phone varchar2(20) NOT NULL,
    store_img varchar2(200),
    store_thumb varchar2(200),
    opening_time number DEFAULT 0,
    closing_time number DEFAULT 0,
    min_delivery number DEFAULT 0,
    delivery_time number DEFAULT 0,
    delivery_tip number  DEFAULT 0,
    store_des varchar2(1000) DEFAULT '가게소개가 없습니다'
);
 	//시퀀스
    CREATE SEQUENCE STORE_ID_SEQ
       INCREMENT BY 1
       START WITH 1
       MINVALUE 1
       MAXVALUE 99999999999
       NOCYCLE
       NOCACHE
       NOORDER;

이제 이 테이블에서 정보를 조회하여 데이터를 받아 화면에 뿌려줄 DTO를
하나 생성해주도록 합시다

StoreDto.java 전체코드

package com.han.delivery.dto;


@Data
public class StoreDto {
	
	private long id;
	private int category;
	private String storeName;
	private int storeAddress1;
	private String storeAddress2;
	private String storeAddress3;
	private String storePhone;
	private String storeImg;
	private String storeThumb;
	private int openingTime;
	private int closingTime;
	private int minDelivery;
	private int deliveryTime;
	private int deliveryTip;
	private String storeDes;
	
}

이제 controller, service , dao, mapper를 추가해주세요

StoreController.java 전체코드

package com.han.delivery.controller;


@Controller
public class StoreController {
	
	@Autowired
	StoreService storeService;
	
	@GetMapping("/store/{category}/{address1}")
	public String store(@PathVariable int category, @PathVariable int address1, Model model) {
		
		List<StoreDto> storeList = storeService.storeList(category, address1 / 100);
		model.addAttribute("storeList", storeList);
		
		return "store/store";
	}
}

사용자가 메인화면에서 메뉴를 클릭할시 store/100/13541와 같은 주소로 이동됩니다
우리는 @PathVariable을 사용하여 카테고리코드와 우편번호를 파라메터로 받아
DB에서 내 주위의 매장목록을 조회하기 위해 사용하도록 하겠습니다

StoreService.java 전체코드

package com.han.delivery.service;

@Service
public class StoreService {
	
	@Autowired
	StoreMapper storeMapper;

	

	@Transactional
	public List<StoreDto> storeList(int category, int address){
		
		return storeMapper.storeList(category, address);
	}

	
}

StoreMapper.java 전체코드

package com.han.delivery.dao;

@Mapper
public interface StoreMapper {
	
	public List<StoreDto> storeList(int category, int address);


}

StoreMapper.xml 전체코드

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.han.delivery.dao.StoreMapper">
   
	<select id="storeList" resultType="com.han.delivery.dto.StoreDto">
		select * from dl_store where category = #{category} and store_address1 like #{address}||'%'
	</select>

우리가 컨트롤러에서 받은 2개의 파라메터를 where절에 사용하며 매장의 우편번호가
사용자가 입력한 주소의 우편번호와 앞3자리가 일치하는지 확인하기 위해 like를
사용합니다 이때 가져오는 매장의 목록은 하나가 아니기 때문에 List로 받아야합니다

이제 views에 store폴더를 하나 만들고 2개의 jsp를 추가해주도록 합시다

store.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/store/store.css">
<link rel="stylesheet" href="/css/store/store-li.css">

<%@ include file="/WEB-INF/views/layout/header.jsp" %>


    <!-- 콘텐츠 -->
    <main>
        <div class="container">
            <div class="category" data-category="${category }">
                <ul>
                    <li data-category ='100' onclick="location.href='/store/100/${address1 }'"><span>피자</span></li>
                    <li data-category ='101' onclick="location.href='/store/101/${address1 }'"><span>치킨</span></li>
                    <li data-category ='102' onclick="location.href='/store/102/${address1 }'"><span>패스트푸드</span></li>
                    <li data-category ='103' onclick="location.href='/store/103/${address1 }'"><span>분식</span></li>
                    <li data-category ='104' onclick="location.href='/store/104/${address1 }'"><span>카페/디저트</span></li>
                    <li data-category ='105' onclick="location.href='/store/105/${address1 }'"><span>돈까스/일식</span></li>
                    <li data-category ='106' onclick="location.href='/store/106/${address1 }'"><span>중국집</span></li>
                    <li data-category ='107' onclick="location.href='/store/107/${address1 }'"><span>족발/보쌈</span></li>
                    <li data-category ='108' onclick="location.href='/store/108/${address1 }'"><span>야식</span></li>
                    <li data-category ='109' onclick="location.href='/store/109/${address1 }'"><span>한식</span></li>
                    <li data-category ='110' onclick="location.href='/store/110/${address1 }'"><span>1인분</span></li>
                    <li data-category ='111' onclick="location.href='/store/111/${address1 }'"><span>도시락</span></li>
                </ul>
            </div>

			<input type="hidden" id="category" value="${category}" class="category">
			<input type="hidden" id="address" value="${address1}" class="address1">

           <div class="option">
                <ul>    
                	<li data-sort="기본순">기본순</li>
                    <li data-sort="배달 빠른 순">배달 빠른 순</li>
                    <li data-sort="배달팁 낮은 순">배달팁 낮은 순</li>
                    <li data-sort="별점 높은 순">별점 높은 순</li>
                    <li data-sort="리뷰 많은 순">리뷰 많은 순</li>
                    <li data-sort="최소 주문 금액 순">최소 주문 금액 순</li>
                </ul> 
           </div>
           
           

            <div class="box">
				
				<c:if test="${empty storeList }">
					<img class="temp_img" alt="이미지" src="/img/temp2.png">
					<style>main .box {background: #F6F6F6; max-width: 100%; }</style>
				</c:if>
				
				
                <ul class="store">
                	<c:set var="store_admin" value="/store" />
                	<c:forEach items="${storeList }" var="storeList">
                    	<%@ include file="/WEB-INF/views/store/store-li.jsp" %>
                    </c:forEach>
                </ul>
            </div>

        </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/store.js" ></script>
    
</body>
</html>

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" %>
    
<li>
		
		<div class="img_box">
			<a href="${store_admin }/detail/${storeList.id }"><img src="${storeList.storeImg }" alt="이미지"></a>
		</div>

		<div class="info_box">
		
			<h2><a href="${store_admin }/detail/${storeList.id }">${storeList.storeName }</a></h2>
			
			<a href="${store_admin }/detail/${storeList.id }">
		<i class="far fa-star"></i>
							</c:if>
						</c:forEach>
					</span>
				</span> --%>

			<span>
				<span>최소주문금액 <fm:formatNumber value="${storeList.minDelivery }" pattern="###,###" /></span>
				<span>배달팁 <fm:formatNumber value="${storeList.deliveryTip }" pattern="###,###" /></span>
			</span>
			<span>배달시간 ${storeList.deliveryTime }분</span>
			</a>
		</div>
		

</li>

이것으로 기본적인 매장목록 화면은 구현이 끝났습니다 아직 별점, 리뷰와 같은 기능이
존재하지 않기 때문에 매장목록에는 보이지 않으므로 진행하면서 추가하도록 하겠습니다

일단 현재 진행하면서 나중에 수정해야할 부분이 몇가지 있습니다

첫번째로 현재 쿠키에 저장한 주소가 문제가 있습니다 비회원일경우 상관없지만
한명의 유저가 여러개의 아이디를 가지고 있을시에 어떤 아이디로 접속해도 똑같은 주소가
입력되어 있게 됩니다. 그 이유는 쿠키에 값을 저장할때 deliveryAddress1,
deliveryAddress2라는 이름으로 저장했기 때문에 아이디를 변경하더라도
똑같은 쿠키값에 접근하게 됩니다 이부분은 간단하게 쿠키에 값을 저장할때 key값을
username+"Address"와 같은식으로 지정하면 쉽게 해결이 가능합니다

두번째는 현재 매장목록을 가져올때 조건에 일치하는 매장을 한번에 모두 다 가져와서
화면에 뿌려주고 있습니다 만약 조건에 일치하는 매장이 수천개면 이 데이터를 한번에
다가져와서 화면에 다 뿌려줘야하기 때문에 페이지 로딩이 느려지게 되고 작업이
끝날때까지 다른 기능을 사용하지 못합니다 따라서 페이징기능을 사용하여
예를들어 한번에 10개의 매장정보만 가져오고 사용자가 스크롤을 밑으로 내렸을시
추가적으로 10개의 매장정보를 다시 가져와서 동적으로 매장정보를 페이지에
추가해주는식으로 구현할 예정입니다. 다만 사용자가 리뷰순 / 별점순 / 주문순
과 같은 특정조건으로 검색할수 있도록 위의 기능들이 모두 구현이 끝나면
그때 수정하도록 하겠습니다.

profile
이메일 : ehfvndcjstk@naver.com

1개의 댓글

comment-user-thumbnail
2022년 7월 20일

감사합니다. 잘보고 갑니다.

답글 달기