프로젝트소개
프로젝트를 위해 무엇을 해볼까 생각해 보다가, 실용적이고 많이 쓰이는 의류 쇼핑몰 사이트를 만들어 보았습니다.
기본적인 회원 기능과 관리자 기능을 넣어 만들어 보았습니다.
메인 페이지
Database
테이블 구성도
테이블 관계도
프로젝트 구현과정
Member
MemberLoginCommand
public class MemberLoginCommand implements MemberCommand {
@Override
public Map<String, Object> execute(SqlSession sqlSession, Model model) {
Map<String, Object> map = model.asMap();
MemberDto memberDto = (MemberDto) map.get("memberDto");
HttpServletRequest request = (HttpServletRequest) map.get("request");
HttpSession session = request.getSession();
MemberDao memberDao = sqlSession.getMapper(MemberDao.class);
CartDao cartDao = sqlSession.getMapper(CartDao.class);
Map<String, Object> resultMap = new HashMap<String, Object>();
String m_id = memberDto.getM_id();
String m_pw = Sha256.sha256(memberDto.getM_pw());
if (memberDao.login(m_id, m_pw) != null) {
resultMap.put("loginResult", 1); // 로그인 성공
session.setAttribute("loginDto", memberDao.login(m_id, m_pw));
// 장바구니 개수
int m_no = memberDao.login(m_id, m_pw).getM_no();
session.setAttribute("crt_count", cartDao.cartCount(m_no));
// TEST 코드
System.out.println("로그인한 회원 DB: " + memberDao.login(m_id, m_pw));
} else if (memberDto.getM_id() == "") {
resultMap.put("loginResult", 0); // 아이디 항목 공백
} else if (memberDto.getM_pw() == "") {
resultMap.put("loginResult", -1); // 패스워드 항목 공백
} else if (memberDto.getM_pw().length() < 8) {
resultMap.put("loginResult", -2); // 패스워드 항목 8자 미만 입력
} else {
resultMap.put("loginResult", -3); // 아이디 패스워드 불일치
}
return resultMap;
}
}
loginPage.jsp
function login() {
var m_id = $('#m_id').val();
var m_pw = $('#m_pw').val();
var sendByIdPw = {
"m_id" : m_id,
"m_pw" : m_pw
}
$.ajax({
url: 'login.do',
type: 'post',
data: JSON.stringify(sendByIdPw), // JSON -> String 형변환 후 컨트롤러로 보내는 String 타입의 JSON 데이터
contentType: 'application/json', // 컨트롤러로 보내는 데이터의 타입을 알려주기 위해
dataType: 'json',
success: function(responseObj) { // 성공하면 받아오는 데이터
if (responseObj.loginResult == 1) {
alert(m_id + '님 환영합니다.')
location.href = '${referer}';
} else if (responseObj.loginResult == 0) {
alert('아이디 항목은 필수 입력값입니다.')
$('#m_id').focus();
} else if (responseObj.loginResult == -1) {
alert('패스워드 항목은 필수 입력값입니다.')
$('#m_pw').focus();
} else if (responseObj.loginResult == -2) {
alert('패스워드 항목이 8자(개) 이상으로 해주십시오.')
$('#m_pw').focus();
} else if (responseObj.loginResult == -3) {
alert('아이디 또는 비밀번호가 일치하지 않습니다.')
$('#m_pw').focus();
location.href='loginPage.do';
} else {
alert('loginResult == null')
}
},
error: function() {
alert('AJAX FAIL')
}
});
}
로그인 화면
비밀번호 찾기 인증요청메일
PRODUCT
기본적으로 로그인, 사이즈 선택 안하고 구매, 카트 누르면 알림
카트에 담기, 구매하기 클릭시
재고가 없는 경우 (옵션에 품절 표시됨) 및 알림 : 제이쿼리 사용
Ajax로 카트 현황 파악하여 중복 알림
구성
사이즈 S와 free인 제품들만 프로덕트 리스트에 보여지므로
사이즈에 상관없이 카트나 오더, 리뷰에서 제품을 클릭했을 때 뷰 페이지 연결되도록 커맨드 작성
컨트롤러에 상품 아래 리뷰 페이지 연결되도록 리뷰 리스트 커맨드 추가
Cart
카트 목록에서 수량 변경, 삭제, 주문 가능
수량변경은 ajax 사용
주문은 체크박스 선택해서 선택 주문하거나 전체주문 클릭해서 전체 주문 가능
Target에 제품번호를 넘겨서 이동하는데 전체 주문할 경우 target을 all로 임의로 설정해 카트에 있는 모든 제품을 불러옴
Order
구성
주소 api 다음에서 제공하는 api 사용하여 적용
결제 시 적립금 제이쿼리로 연결하여 실시간 적용
구매수량별 가격 계산
결제수단에 따라 내용 변경..
결제하기 누르면 해당 제품 재고 감소, 사용한 포인트 감소
구성
주문조회에서 주문한 건 별로 리스트 출력
주문번호 클릭 시 주문 건에 대한 내용 조회
주문 취소가능
주문 취소하면 재고, 포인트 복구
Search
search 부분 ajax
$.ajax({
url : 'productSearch.do',
type : 'post',
data : JSON.stringify(obj),
contentType : 'application/json',
dataType : 'json',
success : function(responseObj) {
$('#searchButton').empty();
$('#searchResult').empty();
$('#noData').empty();
$('<strong>').html(responseObj.search_count).appendTo('#searchResult');
if (responseObj.result) {
searchList(responseObj.list);
} else {
$('<strong>')
.html('검색결과가 없습니다')
.append($('<ul>'))
.append($('<li>').html('검색어/제외검색어의 입력이 정확한지 확인해 보세요.'))
.append($('<li>').html('두 단어 이상의 검색어인 경우, 띄어쓰기를 확인해 보세요.'))
.append($('<li>').html('검색 옵션을 다시 확인해 보세요.'))
.appendTo('#noData');
}
var paging = responseObj.paging;
$('#paging').empty();
if (paging.beginPage <= paging.pagePerBlock) {
// class 의미
// disable : css (클릭 안 되는 건 실버색) 적용하려고
$('#paging').append('<div class="disable"><a>◀</a></div>');
} else {
// class 의미
// 1) prev-block : 이전(◀)으로 이동하려고
// 2) go-page : css (cursor: pointer) 적용하려고
$('#paging').append('<div class="prev-block go-page" data-page="' + (paging.beginPage - 1) + '"><a>◀</a></div>');
}
// 1 2 3 4 5
for (let p = paging.beginPage; p <= paging.endPage; p++) {
if (paging.page == p) { // 현재페이지는 링크가 안 됩니다.
// class 의미
// now-page : css (현재 페이지는 녹색) 적용하려고
$('#paging').append('<div class="now-page"><a>' + p + '</a></div>')
} else {
// class 의미
// go-page : css (cursor: pointer) 적용하려고
$('#paging').append('<div class="go-page" data-page="' + p + '"><a>' + p + '</a></div>');
}
}
// ▶
if (paging.endPage >= paging.totalPage) {
// class 의미
// disable : css (클릭 안 되는 건 실버색) 적용하려고
$('#paging').append('<div class="disable"><a>▶</a></div>');
} else {
// class 의미
// 1) next-block : 다음(▶)으로 이동하려고
// 2) go-page : css (cursor: pointer) 적용하려고
$('#paging').append('<div class="next-block go-page" data-page="' + (paging.endPage + 1) + '"><a>▶</a></div>');
}
},
error : function() {
alert('AJAX FAIL');
}
});
위의 if (responseObj.result) { searchList(responseObj.list);
이부분이
아래부분으로 갑니다.
function searchList(list) {
$.each(list, function(index, item) {
var s_List = "";
s_List += '<li id="anchorBoxId_212" class="xans-record-">';
s_List += ' <div class="thumbnail">';
s_List += '<a href="productViewPage.do?p_No=' + item.p_No + '">';
s_List += '<img src="' + item.p_Image + '"></a></div>';
s_List += '<div class="description">';
s_List += '<strong class="name" style="font-weight:normal;text-align:left">';
s_List += '<a href="productViewPage.do?p_No=' + item.p_No + '">';
s_List += '<span class="title displaynone"><span style="font-size:12px;color:#000000;">상품명</span> </span>';
s_List += '<span style="font-size:12px;color:#000000;">' + item.p_Name + '[' + item.p_Color + ']' + '</span></a></strong>';
s_List += '<ul class="xans-element- xans-search xans-search-listitem spec"><li class=" xans-record-">';
s_List += '<strong class="title displaynone"><span style="font-size:12px;color:#000000;">판매가</span> </strong>';
s_List += '<span style="font-size:12px;color:#000000;">₩' + item.p_Price + '</span><span id="span_product_tax_type_text" style=""> </span></li>';
s_List += '</ul> </li>';
var $searchButton = $("#searchButton");
$searchButton.append(s_List);
});
}
ajax로 값을 받아온후 result값이 true면 searchList를 불러옵니다
상품 정렬기준 설정
검색어와 선택한 option의 값이 모두 일치할때 나타나는 list 값이 달라집니다
Search 버튼 누른 이후 구성
option 과 검색어 입력까지 완료후 search 버튼을 누르면 list 가 나타납니다
command에서 받아온 list의 갯수를 받아오는 search_count값을 입력하여 총 갯수를 나타내줍니다
페이징 처리
Boards
1
- 작성페이지는 EDITOR를 사용하여 CONTENT에 설정을 넣을 수 있도록 하였습니다.
- EDITOR는 CSS부분과 JS부분으로 나눌수있는데 CSS부분은 기본을 그대로 사용하였고, JS부분만 내용을 가져왔습니다.
- 탬플릿과 같은 구성이기에 각주가 걸려있는부분과, 아래의 완료버튼과 연결되어있는 click function만 수정하면 손쉽게 사용할 수 있습니다.
- 또한 첨부를 다중첨부를 할 수 있도록 구현하였고, 이미지일 경우 이미지를 작성된 게시물에 자동으로 보여지게끔 구현하였습니다.
- 완료버튼 클릭 시 게시물이 작성되고 게시판의 가장 첫페이지로 넘어가게되며, 목록보기로 넘어갈경우 기존에 게시물 작성을 누르기전의 페이지를 기억하여 그곳으로 넘어갑니다.
2
- 작성페이지는 EDITOR를 사용하여 CONTENT에 설정을 넣을 수 있도록 하였습니다.
- EDITOR는 CSS부분과 JS부분으로 나눌수있는데 CSS부분은 기본을 그대로 사용하였고, JS부분만 내용을 가져왔습니다.
- 탬플릿과 같은 구성이기에 각주가 걸려있는부분과, 아래의 완료버튼과 연결되어있는 click function만 수정하면 손쉽게 사용할 수 있습니다.
- 또한 첨부를 다중첨부를 할 수 있도록 구현하였고, 이미지일 경우 이미지를 작성된 게시물에 자동으로 보여지게끔 구현하였습니다.
- 완료버튼 클릭 시 게시물이 작성되고 게시판의 가장 첫페이지로 넘어가게되며, 목록보기로 넘어갈경우 기존에 게시물 작성을 누르기전의 페이지를 기억하여 그곳으로 넘어갑니다.
3
댓글과 대댓글의 작성은 게시물 고유번호와 댓글의 순번, 그리고 대댓글의 일련번호로 로직이 이루어져있습니다.
댓글의 수정과 삭제는 ajax를 이용하지않고 게시판의 수정과 삭제와 동일한 방식으로 되지만, 구현되는 페이지상에서는 동기식처럼 구현이 되도록 jquery와 js를 사용하였습니다.
토글(checkbox)을 이용해 댓글의 수정 혹은 대댓글 작성을 버튼의 클릭에 따라 조정되도록 구현하였습니다.
4
게시물의 수정은 기본적으로 게시물작성과 동일한 상태로 작성된 데이터들만을 가져오는 방식으로 되어있습니다.
모든부분이 동일하지만,
수정되는 첨부파일은 기존에 가지고있던 첨부파일이름을 새 첨부파일로 전부 덮어쓰기하는 방식으로 진행이되었습니다.삭제는 기본적인 구성을 하고있습니다만, 작성게시물의 첨부와, 댓글은 유지하지않고 모두 선삭제후 게시물이 삭제되도록 구현되어있습니다.
5
- QNA와 리뷰게시판의경우 추가적인 기능이있습니다.
품목선택 기능과 비밀글기능이 바로 그것인데 품목선택의경우 게시판에서 작성이라면, 주문을한 제품에 대해서만 작성을 할 수 있게끔 되어있습니다. - 비밀글의경우 체크가 기본으로 되어있으며 체크시 비밀번호를 작성할수있고, 비밀글로 작성된글은 자물쇠표시가 달리게됩니다.
- 로그인한사람과 글쓴이가 동일하다면 바로 view페이지로 넘어가게되며, 아닐경우엔 password입력페이지로 넘어가게됩니다.
Admin
admin 에서는 관리자 라는 아이디로 접속 할 경우에만 위에 Admin 버튼이 생기고 회원 관리를 할 수 있게 만들었습니다
관리자 아이디: admin1
관리자 비밀번호: 1q2w3e4r