(Spring)쇼핑 페이지 예제 클론코딩
쇼핑 페이지 예제 클론코딩
수업시간에 진행한 쇼핑페이지 예제입니다.
깃허브 repository : shopping page practice
목차
- 메인페이지(main.jsp)
- 로그인 창(login.jsp)
- 회원가입 창(join.jsp)
- 관리자 페이지
- 작가 테이블 구성, 푸터, 로고, 관리자 페이지
- footer영역 제작
- 상품, 작가 테이블 CRUD
- 각페이지 생성
- 작가 CRUD
현재 로그인, 회원가입 기능 구현 완료
5/10
- 관리자 페이지, 인터셉터 제작중
메인페이지(main.jsp)
로그인 페이지(login.jsp)
회원가입 페이지(join.jsp)
관리자 페이지(admin.jsp)
메인페이지(main.jsp)
- 기능
-
top_gnb: Ajax의 비동기 시스템을 통해 로그아웃 버튼 누를시 페이지 리로드 없이 top_gnb가 로그인 여부에 따라 바뀜
-
login button: 로그인 창으로 이동
- join button: 회원가입 창으로 이동
- top_gnb:
- 일반 회원 로그인 시: 로그아웃, 마이룸, 장바구니, 고객센터
- 관리자 로그인 시 : 관리자 페이지, 로그아웃, 마이룸, 장바구니, 고객센터
- 비로그인 시: 로그인, 회원가입
- 여기서의 로그인은 ajax의 비동기 방식을 사용해서 “post”방식으로 페이지를 이동함
-
로그인 창(login.jsp)
- 기능
- 로그인 실패시 로그인 버튼 위에 로그인 실패 문장 나타남
- BCryptPasswordEncoder 라이브러리를 통해 암호화된 사용자 비밀번호 검사후 일치 하면 로그인 성공.
-> main페이지로 이동
회원가입 창(join.jsp)
- 기능
- 아이디: 아이디 중복 확인, 유효성 검사
- 비밀번호 + 비밀번호 확인: 유효성검사, 일치 확인
- 이메일:
- 이메일 확인: 이메일 전송 라이브러리를 이용해 확인 메일을 보냄. 확인형식은 6자리 난수를 보내 그 수를 메세지 확인창에 입력하는 방식
- 메세지 확인창: 수신 받은 6자리 임의의 수를 입력
- 주소:
- 카카오 주소록API를 사용.
- 주소록 API를 입력하고나면 상세주소 창에 focus()
- 회원가입
- 데이터베이스에 정보입력
- 비밀번호는 BCryptPasswordEncoder를 통해 암호화 하여 데이터 베이스에 저장
관리자 페이지
- 기능
- 상품 등록 (5/10 구현중)
- 상품 목록 (5/10 구현중)
- 작가 등록 (5/10 구현중)
- 작가 관리 (5/10 구현중)
- 회원 관리 (5/10 구현중)
- top_gnb: 메인페이지, 로그아웃, 고객센터
- 인터셉터 기능 구현 (5/10
구현중완료)
- 인터셉터
- spring web 라이브러리 추가
- spring web 라이브러리 추가
- src/main/java 밑에 com.shop.interceptor 패키지 생성 후 AdimInterceptor.java, LoginInterceptor.java 생성
- servlet-context.xml에 <Interpretors> 태그 삽입
<!-- 인터셉터 적용 -->
<interceptors>
<interceptor>
<mapping path="/member/login.do"></mapping>
<beans:bean id="loginInterceptor" class="com.shop.interceptor.LoginInterceptor"></beans:bean>
</interceptor>
<interceptor>
<mapping path="/admin/**"></mapping>
<beans:bean id="adminInterceptor" class="com.shop.interceptor.AdminInterceptor"></beans:bean>
</interceptor>
</interceptors>
- AdimInterceptor.java, LoginInterceptor.java에서 상속받음
implements HandlerInterceptor
관리자 창에서 관리자가 아니거나 로그인 상태가 아닐경우 접속을 혀용하지 않고 메인페이지로 돌려보내기 위해 Interceptor 사용
- AdimInterceptor.java
public class AdminInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession();
MemberVO lvo = (MemberVO)session.getAttribute("member");
if(lvo == null || lvo.getAdminCk() == 0) { //로그인 상태가 아니거나 관리자 계정이 아닌경우
response.sendRedirect("/main"); // 메인페이지로 리다이렉트
return false;
}
return true;
}
}
- LoginInterceptor.java
public class LoginInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession();
session.invalidate();
return true;
}
}
5/11
작가 테이블 구성, 푸터, 로고, 관리자 페이지
작가 테이블 생성
create table jm_nation(
nationId varchar(2) PRIMARY key,
nationName varchar(50)
);
insert into jm_nation values('01', '국내');
insert into jm_nation values('02', '국외');
create sequence author_seq start with 1 increment by 1;
create table jm_author(
authorId number primary key,
authorName varchar2(50),
nationId varchar2(2),
authorIntro long,
foreign key (nationId) references jm_nation(nationId)
);
insert into jm_author(authorId, authorName, nationId, authorIntro) values(author_seq.nextval,'유홍준', '01', '작가 소개입니다' );
insert into jm_author(authorId, authorName, nationId, authorIntro) values(author_seq.nextval,'김난도', '01', '작가 소개입니다' );
insert into jm_author(authorId, authorName, nationId, authorIntro) values(author_seq.nextval,'폴크루그먼', '02', '작가 소개입니다' );
그리고 이미지 폴더 생성
메인페이지(main.jsp) logo삽입
<div class="logo_area">
<a href="/main"><img src="resources/img/logo.png"></a>
</div>
-삽입 완료
footer영역 제작
*일반 메인페이지(main.jsp)와 관리자 페이지의 메인(main.jsp) 둘다 동일한 푸터 적용
-main.jsp
<!-- footer_nav 영역 -->
<div class="footer_nav">
<div class="footer_nav_container">
<ul>
<li>회사소개</li>
<span class="line">|</span>
<li>이용약관</li>
<span class="line">|</span>
<li>고객센터</li>
<span class="line">|</span>
<li>광고문의</li>
<span class="line">|</span>
<li>채용정보</li>
<span class="line">|</span>
</ul>
</div>
</div>
<!-- footer 영역 -->
<div class="footer">
<div class="footer_container">
<div class="footer_left">
<img src="resources/img/logo.png">
</div>
<div class="footer_right">
(주)BookShop 대표이사 : 임종민
<br>
사업자 등록번호 : 000-12-00000
<br>
대표전화 : 1588-1234(발신자 부담전화)
<br><br>
COPYRIGHT(C) <strong>molban2j.github.io</strong> ALL RIGHTS
</div>
<div class="clearfix"></div>
</div>
</div>
-main.css
/* footer nav 영역*/
.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: 130px;
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;
}
footer적용된 모습
상품, 작가 테이블 CRUD
먼저 작가 테이블을 만들었으니 AuthorVO 생성
-상품 등록, 상품 관리, 작가 등록, 작가 관리, 회원 관리 url 매핑 매서드 작성(AdminController.java)
/* 상품 등록 페이지 접속 */
@GetMapping("/goodsManage")
public void goodsManageGET() throws Exception{
logger.info("상품 등록 페이지 접속");
}
/* 상품 등록 페이지 접속 */
@GetMapping("/goodsEnroll")
public void goodsEnrollGET() throws Exception{
logger.info("상품 등록 페이지 접속");
}
/* 작가 등록 페이지 접속 */
@GetMapping("/autorEnroll")
public void authorEnrollGET() throws Exception{
logger.info("작가 등록 페이지 접속");
}
/* 작가 관리 페이지 접속 */
@GetMapping("/autorManage")
public void authorManageGET() throws Exception{
logger.info("작가 관리 페이지 접속");
}
-관리자 페이지 <a> 태그 추가
변경전
<!-- 네비 영역 -->
<div class="admin_navi_wrap">
<ul>
<li><a class="admin_list_01">상품 등록</a></li>
<li><a class="admin_list_02">상품 목록</a></li>
<li><a class="admin_list_03">작가 등록</a></li>
<li><a class="admin_list_04">작가 관리</a></li>
<li><a class="admin_list_05">회원 관리</a></li>
</ul>
</div>
변경후
<!-- 네비 영역 -->
<div class="admin_navi_wrap">
<ul>
<li><a class="admin_list_01" href="/admin/goodsEnroll">상품 등록</a></li>
<li><a class="admin_list_02" href="/admin/goodsManage">상품 관리</a></li>
<lI><a class="admin_list_03" href="/admin/authorEnroll">작가 등록</a></lI>
<lI><a class="admin_list_04" href="/admin/authorManage">작가 관리</a></lI>
<lI><a class="admin_list_05">회원 관리</a></lI>
</ul>
</div>
<a>태그 색 변경 css
.admin_navi_wrap li a:link {color: black;}
.admin_navi_wrap li a:visited {color: black;}
.admin_navi_wrap li a:active {color: black;}
.admin_navi_wrap li a:hover {color: black;}
각페이지 생성
jsp 생성
css 생성
*각각의 jsp는 main.jsp에서 조금씩 수정하는 방향으로 제작
기존 jm_author table 수정
alter table jm_author add regDate date default sysdate;
alter table jm_author add updateDate date default sysdate;
기존 데이터 삭제후 다시 삽입
그리고 테이블이 수정됐으니 AuthorVO객체도 수정해줍니다.
작가 CRUD
작가 CRUD를 위한 mapper 인터페이스와 xml을 만들어줍니다.
AuthorMapper.java
AuthorMapper.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.shop.mapper.AuthorMapper">
<insert id="authorEnroll">
insert into jm_author(authorId, authorName, nationId, authorIntro)
values(author_seq.nextval,#{authorName},#{nationId},#{authorIntro} )
</insert>
</mapper>
매퍼를 작성 했으니 그 다음 AuthorMapperTest.java를 만들어 테스트 해줍니다.
JunitTest
테이블에도 잘 삽입된걸 확인했으니 service인터페이스를 작성
AuthorService.java
AuthorServiceImpl.java
service test 패키지를 만들어 테스트를 한번 더 해줍니다
오류 발생!
bean객체를 제데로 인식하지 못하는 에러가 발생
AuthorService.java에 service라는 걸 인지시키기 위해 어노테이션 추가
(추가) 그래도 똑같이 bean객체를 찾지 못하는 에러가 발생. AuthorServiceImpl.java에도 똑같이 @Service 어노테이션을 붙여주도록 하자
-AdminController.java에 작가등록 매핑 작성
@PostMapping("authorEnroll.do")
public String authorEnrollPOST(AuthorVO vo, RedirectAttributes rttr) throws Exception{
//작가 등록 쿼리 실행
authorService.authorEnroll(vo);
rttr.addFlashAttribute("enroll_result", vo.getAuthorName());
return "redirect:/admin/authorManage";
}
-adminEnroll.jsp에 <form> 태그 작성
<div class="admin_content_wrap">
<div class="admin_content_subject">
<span>작가 등록</span>
<div class="admin_content_main">
< action="/admin/authorEnroll.do" method="post">
<div class="form_section">
<div class="form_section_title">
<label>작가 이름</label>
</div>
<div class="form_section_content">
<input name="authorName">
</div>
</div>
<div class="form_section">
<div class="form_section_title">
<label>소속 국가</label>
</div>
<div class="form_section_content">
<select name="nationId">
<option value="none" selected>=== 선택 ===</option>
<option value="01">국내</option>
<option value="02">국외</option>
</select>
</div>
</div>
<div class="form_section">
<div class="form_section_title">
<label>작가소개</label>
</div>
<div class="form_section_content">
<input name="authorIntro" type="text">
</div>
</div>
<div class="btn_section">
<button id="cancelBtn" class="btn">취 소</button>
<button id="enrollBtn" class="btn enroll_btn">등 록</button>
</div>
</form>
</div>
</div>
</div>
버튼 액션을 위한 스크립트 처리
<script>
/*등록 버튼*/
$("#enrollBtn").click(function(){
$("#enrollForm").submit();
});
/*취소 버튼*/
$("#cancelBtn").click(function(){
location.href="/admin/authorManage"
});
</script>
adminManage.jsp에서 작가 등록에 성공하면 메세지를 띄우도록 스크립트 처리
<script>
$(document).ready(function(){
let result = "${enroll_result}";
checkResult(result);
function checkResult(result){
if(result == ''){
return;
}
alert("작가 '${enroll_result}'을 등록하였습니다.");
}
});
</script>
현재까지의 구현
5/11 오후
authorManage.jsp의 스크립트 부분은 작가 등록 정보가 노출이 될수있어 공격받을 수 있다고 한다. 그래서 jstl을 이용해 다음과 같이 고쳐줬다
<script>
$(document).ready(function(){
let result = '<c:out value="${enroll_result}"/>'';
checkResult(result);
function checkResult(result){
if(result == ''){
return;
}
alert("작가 '${enroll_result}'을 등록하였습니다.");
}
});
</script>
adminEnroll.jsp <from> 부분 css 적용
adminEnroll.css
/* 관리자 컨텐츠 메인 영역 */
.form_section{
width: 95%;
margin-left: 2%;
margin-top: 20px;
border: 1px solid #dbdde2;
background-color: #efefef;
}
.form_section_title{
padding: 20px 35px;
}
.form_section_title label{
display: block;
font-size: 20px;
font-weight: 800;
}
.form_section_content{
padding: 20px 35px;
border-top: 1px solid #dbdde2;
}
.form_section_content input{
width: 98%;
height: 25px;
font-size: 20px;
padding: 5px 1%;
}
.form_section_content select{
width: 98%;
height: 35px;
font-size: 20px;
text-align-last: center;
}
/* 입력란 공란 경고 태그 */
.form_section_content span{
display: none;
padding-top: 10px;
text-align: center;
color: #e05757;
font-weight: 300;
}
/* 버튼 영역 */
.btn_section{
text-align: center;
margin: 80px 0;
}
.btn{
min-width: 180px;
padding: 4px 30px;
font-size: 25px;
font-weight: 600;
line-height: 40px;
}
.enroll_btn{
background-color: #dbdde2;
margin-left:15px;
}
현재까지 만든것
그리고 등록버튼을 누를때 입력폼 유효성 검사를 위해 스크립트를 좀 변경해줍니다.
<script>
/*등록 버튼 */
$("#enrollBtn").click(function() {
/*검사 통과 유무 변수*/
let nameCheck = false; // 작가 이름
let nationCheck = false; // 소속 국가
let introCheck = false; // 작가 소개
/*입력값 변수*/
let authorName = $('input[name=authorName]').val();
let nationId = $('select[name=nationId]').val();
let authorIntro = $('input[name=authorIntro]').val();
/*공란 경고 span태그*/
let wAuthorName = $('#warn_authorName');
let wNationId = $('#warn_nationId');
let wAuthorIntro = $('#warn_authorIntro');
/*작가 이름 공란 체크*/
if (authorName === '') {
wAuthorName.css('display', 'block');
nameCheck = false;
} else {
wAuthorName.css('display', 'none');
nameCheck = "true";
}
/*작가 국적 공란 체크*/
if (nationId === 'none') {
wNationId.css('display', 'block');
nationCheck = false;
} else {
wNationId.css('display', 'none');
nationCheck = "true";
}
/*작가 소개 공란 체크*/
if (authorIntro === '') {
wAuthorIntro.css('display', 'block');
introCheck = false;
} else {
wAuthorIntro.css('display', 'none');
introCheck = "true";
}
/*최종 검사*/
if (nameCheck && nationCheck && introCheck) {
$("#enrollForm").submit();
} else {
return;
}
});
/*취소 버튼*/
$("#cancelBtn").click(function() {
$("#enrollForm")[0].reset();
// location.href="/admin/authorManage"
});
</script>