[Spring][쇼핑몰 프로젝트][41] 주문 구현(주문 페이지) - 3
프로젝트 Github : https://github.com/sjinjin7/Blog_Project
프로젝트 포스팅 색인(index) : https://kimvampa.tistory.com/188
목표
주문 구현 - 페이지 구현
서버로부터 전달받은 데이터(상품, 회원 정보)를 활용하여 '주문 페이지'를 구성해보겠습니다.
순서
1. 개요
2. 기본틀
3. 회원정보
4. 배송지 정보
1. 개요
이번 포스팅부터 본격적으로 "주문 페이지"를 만들어 보겠습니다. "주문 페이지"의 구성은 기본적으로 교보문고의 "주문 페이지"의 틀을 따라가고자 합니다.
교보문고의 "주문 페이지"를 보시면 크게 5가지 구성을 가집니다.
1. 회원 정보
- 회원의 이름, 연락처가 기재
2. 배송지 정보
- 회원 정보에 저장된 배송정보 or 직접 입력 배송정보
3. 주문 상품 정보
- 사용자가 주문하고자 하는 상품의 정보
4. 포인트 사용 우무
- 회원의 사용 가능 포인트 정보 & 사용하고자 하는 포인트 입력란
5. 최종 주문 정보
- 최정 결제 비용 정보(포인트 사용량에 따라 변화)
이본 포스팅에선 '회원 정보'와 '배송지 정보'를 구현해보겠습니다.
2. 기본 틀
order.css
main/webapp/resource/css 디렉토리 경로에 order.css 파일을 생성해주고 cart.css 파일의 코드를 모두 복사하여 붙여 주었습니다.
그리고 'content_middle_section', 'content_totalCount_section' <div> 태그와 관련된 모든 속성을 지워 주었습니다. (안 지워도 상관은 없습니다.)
order.css
@charset "UTF-8";
*{
margin: 0;
padding:0;
}
a{
text-decoration: none;
}
/* 화면 전체 렙 */
.wrapper{
width: 100%;
}
/* content 랩 */
.wrap{
width : 1080px;
margin: auto;
}
/* 홈페이지 기능 네비 */
.top_gnb_area{
width: 100%;
height: 50px;
background-color: #f0f0f1;
position:relative;
}
.top_gnb_area .list{
position: absolute;
top: 0px;
right: 0;
}
.top_gnb_area .list li{
list-style: none;
float : left;
padding: 13px 15px 0 10px;
font-weight: 900;
cursor: pointer;
}
/* 로고, 검색, 로그인 */
.top_area{
width: 100%;
height: 150px;
/* background-color: #f7f0b9; */
}
/* 로고 영역 */
.logo_area{
width: 25%;
height: 100%;
float:left;
}
.logo_area img{
width: 100%;
height: 100%;
}
/* 검색 박스 영역 */
.search_area{
width: 50%;
height: 100%;
float:left;
}
.search_wrap{
width: 100%;
height: 100%;
}
#searchForm{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.search_input{
display: flex;
height: 30%;
width: 80%;
}
.search_input select{
width: 20%;
text-align: center;
font-size: 15px;
}
.search_input input{
margin-left: 10px;
width: 57%;
font-size: 18px;
padding-left: 2%;
}
.search_btn{
margin-left: 10px;
width: 17%;
border-radius: 14px;
font-size: 17px;
font-weight: 600;
}
/* 로그인 버튼 영역 */
.login_area{
width: 25%;
height: 100%;
display: inline-block;
text-align: center;
}
.login_button{
height: 50%;
background-color: #D4DFE6;
margin-top: 30px;
line-height: 77px;
font-size: 40px;
font-weight: 900;
border-radius: 10px;
cursor: pointer;
}
.login_area>span{
margin-top: 10px;
font-weight: 900;
display: inline-block;
}
.login_button{
height : 50%;
background-color: #D4DFE6;
margin-top:30px;
}
/* 제품 목록 네비 */
.navi_bar_area{
width: 100%;
height: 70px;
background-color: #7696fd;
}
/* 홈페이지 메인 제품 목록 */
.content_area{
width: 100%;
min-height: 1000px;
}
table{
border-collapse: collapse;
width: 100%;
}
.table_text_align_center{
text-align: center;
}
caption{
visibility: hidden;
width: 0;
height: 0;
font-size: 0;
line-height: 0;
overflow: hidden;
}
.content_subject{
height: 110px;
line-height: 110px;
background-color: #5080bd;
margin-bottom: 60px;
}
.content_subject span{
padding-left: 30 px;
display: inline-block;
color: white;
font-size: 50px;
font-weight: bolder;
}
/* 로그인 성공 영역 */
.login_success_area{
height: 62%;
width: 80%;
border: 2px solid #7474ad;
border-radius: 15px;
margin: 5% auto;
padding-top: 5%;
}
.login_success_area>a{
font-size: 15px;
font-weight: 900;
display: inline-block;
margin-top: 5px;
background: #e1e5e8;
width: 82px;
height: 22px;
line-height: 22px;
border-radius: 25px;
color: #606267;
}
.login_success_area>span{
display : block;
text-align: left;
margin-left: 10%;
}
/* 검색결과 없음 */
.table_empty{
height: 50px;
text-align: center;
margin: 200px 0 215px 0px;
font-size: 25px;
}
/* 필터정보 */
.search_filter {
width: 85%;
margin: auto;
margin-top: 30px;
margin-bottom: 50px;
}
.filter_button_wrap {
width: 100%;
}
.filter_button_wrap button {
width: 50%;
}
.filter_button{
background-color: #04AA6D;
border: 1px solid green;
color: white;
padding: 10px 24px;
cursor: pointer;
float: left;
}
.filter_button_wrap:after {
content: "";
clear: both;
display: table;
}
.filter_button_wrap button:not(:last-child) {
border-right: none;
}
.filter_button:hover {
background-color: #3e8e41;
}
.filter_active{
background-color: #045d3c;
}
.filter_content{
padding:20px 50px 20px 50px;
border: 1px solid gray;
}
.filter_content a:not(:first-child){
margin-left: 10px;
}
.filter_a{
display: block;
}
.filter_b{
display: none;
}
/* footer navai 영역 */
.footer_nav{
width:100%;
height:50px;
}
.footer_nav_container{
width: 100%;
height: 100%;
background-color:#8EC0E4;
}
.footer_nav_container>ul{
font-weight : bold;
float:left;
list-style:none;
position:relative;
padding-top:10px;
line-height: 27px;
font-family: dotum;
margin-left: 10px;
}
.footer_nav_container>ul>li{
display:inline;
width: 45px;
height: 19px;
padding: 10px 9px 0 10px;
}
.footer_nav_container>ul>span{
margin: 0 4px;
}
/* footer 영역 */
.footer{
width:100%;
height:130px;
background-color:#D4DFE6;
padding-bottom : 50px;
}
.footer_container{
width: 100%;
height: 100%;
margin: auto;
}
.footer_left>img {
width: 150%;
height: 130px;
margin-left: -20px;
margin-top: -12px;
}
.footer_left{
float :left;
width: 170px;
margin-left: 20px;
margin-top : 30px;
}
.footer_right{
float :left;
width: 680px;
margin-left: 70px;
margin-top : 30px;
}
/* float 속성 해제 */
.clearfix{
clear: both;
}
order.jsp
cart.jsp 모든 코드를 복사하고 order.jsp 붙여줍니다.
cart.css로 연결되어 있는 코드를 order.css연결로 변경해줍니다.
'content_area' <div> 태그 내부 코드 중 'content_subject' <div> 태그를 제외한 모든 태그를 지워 줍니다.
<script> 태그 내부 코드를 지웁니다.
지금부터 본격적으로 주문 페이지 작업을 해보겠습니다.
"content_main"<div>태그를 작성합니다.
<div class="content_main">
<!-- 회원 정보 -->
<!-- 배송지 정보 -->
<!-- 상품 정보 -->
<!-- 포인트 정보 -->
<!-- 주문 종합 정보 -->
</div>
이 태그 내부에 차례로 '회원 정보', '배송지 정보', '상품 정보', '포인트 정보', '주문 종합 정보' 작업을 할 것입니다.
3. 회원 정보
회원 정보는 단순히 '회원의 이름'과 '회원 연락처(이메일)'을 출력시켜 줄 것입니다. 이를 위해 아래의 코드를 추가하였습니다.
<div class="member_info_div">
<table class="table_text_align_center memberInfo_table">
<tbody>
<tr>
<th style="width: 25%;">주문자</th>
<td style="width: *">${memberInfo.memberName} | ${memberInfo.memberMail}</td>
</tr>
</tbody>
</table>
</div>
추가한 태그들의 css 코드를 order.css 파일에 추가해줍니다.
.content_main{
min-height: 700px;
padding-right: 350px;
position: relative;
}
table{
border-collapse: collapse;
}
/* 사용자 정보 */
.memberInfo_table{
width: 100%;
border-spacing: 0;
border-top: 2px solid #363636;
border-bottom: 1px solid #b6b6b6;
}
.member_info_div td{
padding : 12px;
text-align: left;
}
서버를 구동시켜서 확인해 봅니다.
4. 배송지 정보
'배송지'는 회원 가입 때 기입 한 배송지를 선택할 수 있게 하거나 새로 입력할 수 있도록 선택지를 줄 것입니다. 따라서 입력란 상단 부분에 두 개의 버튼을 두고, 한 버튼을 사용하면 등록된 '배송지 정보'를 다른 한 버튼을 클릭하면 직접 입력 할 수 있는 입력란이 보이도록 할 것입니다.
따라서 먼저 버튼과 버튼에 따른 입력란이 보여지도록 해주는 틀을 만들어 보겠습니다. 아래의 코드를 추가해줍니다.
<div class="addressInfo_div">
<div class="addressInfo_button_div">
<button class="address_btn address_btn_1" style="background-color: #3c3838;">상용자 정보 주소록</button>
<button class="address_btn address_btn_2">직접 입력</button>
</div>
<div class="addressInfo_input_div_wrap">
<div class="addressInfo_input_div addressInfo_input_div_1" style="display: block">
</div>
<div class="addressInfo_input_div addressInfo_input_div_2">
</div>
</div>
</div>
'addressInfo_input_div_1', 'addressInfo_input_div_2' <div> 태그가 버튼의 클릭에 따라 보이거나 숨겨질 <div> 태그입니다. <div> 태그의 구분을 위해 의미 없는 더미 코드를 추가했습니다.
그리고 첫 번째 <div>에는 태그 속성으로서 "display:block"을 부여했습니다. (왜냐하면 css 파일의 css 속성에는 두 개의 <div> 태그를 모두 'display:none'으로 둘 것이기 때문입니다. 'block'은 태그에 직접 css 속성이 부여(인라인 방식) 되었기 때문에 우선순위로 인해 해당 <div> 태그는 노출됩니다.)
※ 'css 선택자 우선순위' 키워드 구글링 하시면 css 선택자 우선순위를 알 수 있습니다.
추가한 태그들의 css 설정 코드를 order.css 파일에 추가해줍니다. (더불어 'addressInfo_input_div' 태그 내부에 추가될 css 설정 코드까지 함께 추가하였습니다.
/* 사용자 정보 */
.memberInfo_table{
width: 100%;
border-spacing: 0;
border-top: 2px solid #363636;
border-bottom: 1px solid #b6b6b6;
}
.member_info_div td{
padding : 12px;
text-align: left;
}
/* 사용자 주소 정보 */
.addressInfo_div{
margin-top: 30px;
}
.addressInfo_input_div_wrap{
border-bottom: 1px solid #f3f3f3;
height: 225px;
}
.address_btn {
background-color: #555;
color: white;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
font-size: 17px;
width: 50%;
}
.address_btn:hover{
background-color: #777;
}
.addressInfo_button_div::after{
content:'';
display:block;
clear:both;
}
.addressInfo_input_div{
padding:12px;
text-align: left;
display: none;
line-height: 40px;
}
.addressInfo_input_div th{
border-color: transparent;
background-color: transparent;
}
.addressInfo_input_div th{
padding : 12px 5px 12px 20px;
vertical-align: top;
}
.addressInfo_input_div td{
padding : 8px 12px;
}
.addressInfo_input_div_2 input{
padding: 6px 5px;
}
.address_search_btn{
vertical-align: middle;
display: inline-block;
border: 1px solid #aaa;
width: 90px;
text-align: center;
height: 30px;
line-height: 30px;
color: #555;
cursor: pointer;
}
Javascript 코드 추가를 통해 버튼이 동작하도록 작업하겠습니다. 이번엔 평소와 다르게 메서드를 만들어서, 생성한 메서드를 태그의 onClick속성에 추가하는 방식으로 진행하겠습니다.
<script> 태그 내부에 함수를 선언합니다. 함수의 파라미터로 className이라고 부여를 해주었는데, 이 값은 '1' 혹은 '2'가 들어올 것입니다. 이 값은 'addressInfo_input_div_1', 'addressInfo_input_div_2' class 속성 명의 제일 마지막 숫자를 의미합니다.
/* 주소입력란 버튼 동작(숨김, 등장) */
function showAdress(className){
}
버튼을 클릭했을 때 'addressInfo_input_div_className' 태그가 보이도록 하는 코드를 추가합니다.
/* 주소입력란 버튼 동작(숨김, 등장) */
function showAdress(className){
/* 컨텐츠 동작 */
/* 모두 숨기기 */
$(".addressInfo_input_div").css('display', 'none');
/* 컨텐츠 보이기 */
$(".addressInfo_input_div_" + className).css('display', 'block');
}
사용자가 클릭 한 버튼을 구분할 수 있도록 해주기 위해 버튼의 css 속성이 변경되도록 해주는 코드를 추가합니다.
/* 주소입력란 버튼 동작(숨김, 등장) */
function showAdress(className){
/* 컨텐츠 동작 */
/* 모두 숨기기 */
$(".addressInfo_input_div").css('display', 'none');
/* 컨텐츠 보이기 */
$(".addressInfo_input_div_" + className).css('display', 'block');
/* 버튼 색상 변경 */
/* 모든 색상 동일 */
$(".address_btn").css('backgroundColor', '#555');
/* 지정 색상 변경 */
$(".address_btn_"+className).css('backgroundColor', '#3c3838');
}
'addressInfo_input_div_1', 'addressInfo_input_div_2' <div> 태그에 onClick 속성을 추가해주고 우리가 작성한 함수를 값으로 부여합니다. 'addressInfo_input_div_1' 에는 1 인수를 부여하고, 'addressInfo_input_div_2' 에는 2 인수를 부여합니다.
서버를 구동시켜서 잘 동작하는지 확인합니다.
4.1 'addressInfo_input_div_1' (사용자 정보 주소록)
'addressInfo_input_div_1'<div> 태그에는 회원이 등록한 '배송지 정보'가 출력되도록 할 것입니다. 'addressInfo_input_div_1<div> 태그 내부에 아래의 코드를 추가해줍니다.
<table>
<colgroup>
<col width="25%">
<col width="*">
</colgroup>
<tbody>
<tr>
<th>이름</th>
<td>
${memberInfo.memberName}
</td>
</tr>
<tr>
<th>주소</th>
<td>
${memberInfo.memberAddr1} ${memberInfo.memberAddr2}<br>${memberInfo.memberAddr3}
</td>
</tr>
</tbody>
</table>
페이지로 이동하여 정상적으로 출력되는지 확인합니다.
4.2 'addressInfo_input_div_2' (직접 입력)
'addressInfo_input_div_2'는 사용자가 직접 입력할 수 있도록 할 것입니다. 더불어서 주소는 회원가입 했을 때와 같이 다음 주소록을 활용 하여 입력 할 수 있도록 할 것입니다. 구현 방법은 아래의 포스팅 것을 그대로 따랐습니다.
[Spring][쇼핑몰 프로젝트][5] 주소록 API 연동
'addressInfo_input_div_2' <div> 태그 내부에 아래의 코드를 추가해줍니다.
<table>
<colgroup>
<col width="25%">
<col width="*">
</colgroup>
<tbody>
<tr>
<th>이름</th>
<td>
<input class="addressee_input">
</td>
</tr>
<tr>
<th>주소</th>
<td>
<input class="address1_input" readonly="readonly"> <a class="address_search_btn" onclick="execution_daum_address()">주소 찾기</a><br>
<input class="address2_input" readonly="readonly"><br>
<input class="address3_input" readonly="readonly">
</td>
</tr>
</tbody>
</table>
<head> 태그에 다음 주소록 API를 사용하기 위한 CDN 코드를 추가해줍니다.
<!-- 다음주소 -->
<script src="https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script> 태그에 아래의 주소록 연동을 위한 코드를 추가해주었습니다.
/* 다음 주소 연동 */
function execution_daum_address(){
console.log("동작");
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 += ' ';
}
// 제거해야할 코드
// 우편번호와 주소 정보를 해당 필드에 넣는다.
$(".address1_input").val(data.zonecode);
$(".address2_input").val(addr);
// 커서를 상세주소 필드로 이동한다.
$(".address3_input").attr("readonly", false);
$(".address3_input").focus();
}
}).open();
}
페이지로 이동하여 정상적으로 동작하는지 확인합니다.
REFERENCE
DATE
- 2020.12.13