day35 - 0914
회원 관련 기능
- 회원 가입 : day29 - 0902
- 회원 목록 : day32 - 0907
- 회원 상세 정보 : day32 - 0907
- 회원 정보 수정 (관리자 기능) : day 33 - 0908
- 회원 탈퇴 (관리자 기능) : day 33 - 0908
- 로그인/로그아웃 : day34 - 0913
- 마이페이지 : day34 - 0913
** 회원 Interceptor 설정 : day34 - 0913
** 관리자 Interceptor 설정 : day34 - 0913
준비
- MEMBER 테이블 생성
- 회원 아이디 : member_id, 5 ~ 20자 영문 소문자, 숫자와 특수기호(_), (-)만 사용 가능 - 회원 비밀번호 : member_pw, 8~16자 영문 대 소문자, 숫자, 특수문자(!, @, #, $)를 각각 1개 이상 사용 - 닉네임 : member_nick, 한글로 시작하며 한글 or 숫자 가능, 총 10자 이내 - 생년월일 : member_birth, YYYY-MM-DD, 날짜 형식 - 전화번호 : member_tel, 대시 제외하고 010XXXXXXXX 형태 - 이메일 : member_email, 100byte 이내, @ 반드시 포함 - 주소 - 우편 주소 : member_post, 5 ~ 6자리 - 기본 주소 : member_base_address, 한글 50자 - 상세 주소 : member_detail_address, 한글 50자 - 포인트 : member_point, 0 이상 설정 - 등급 : member_grade, 일반, VIP, 관리자 중 하나 - 가입일시 : member_join, 날짜이며 현재 시간으로 자동 설정 - 접속일시 : member_login, 로그인 할 때의 시간으로 자동 설정 ** 회원 아이디가 기본키 ** 전화번호, 이메일, 주소는 선택 입력 가능 ** 접속일시는 가입시 설정하지 않음 |
-- 테이블 생성
create table member(
member_id varchar2(20) primary key
check(regexp_like(member_id, '^[a-z0-9-_]{5,20}$')),
member_pw varchar2(16) not null check(
regexp_like(member_pw, '^[a-zA-Z0-9!@#$]{8,16}$')
and
regexp_like(member_pw, '[a-z]')
and
regexp_like(member_pw, '[A-Z]')
and
regexp_like(member_pw, '[0-9]')
and
regexp_like(member_pw, '[!@#$]')
),
member_nick varchar2(30) not null unique check(regexp_like(member_nick, '^[가-힣][가-힣0-9]{0,9}$')),
member_birth date not null,
member_tel char(11) check(regexp_like(member_tel, '^010[0-9]{8}$')),
member_email varchar2(100) check(regexp_like(member_email, '@')),
member_post varchar2(6) check(regexp_like(member_post, '^[0-9]{5,6}$')),
member_base_address varchar2(150),
member_detail_address varchar2(150),
member_point number default 0 not null check(member_point >= 0),
member_grade varchar2(9) default '일반' not null check(member_grade in ('일반', 'VIP', '관리자')),
member_join date default sysdate not null,
member_login date
);
- Model이 될 클래스 정의 (DTO 또는 VO)
package com.kh.springhome.entity;
import java.sql.Date;
public class MemberDto {
// 필드
private String memberId;
private String memberPw;
private String memberNick;
private Date memberBirth;
private String memberTel;
private String memberEmail;
private String memberPost;
private String memberBaseAddress;
private String memberDetailAddress;
private int memberPoint;
private String memberGrade;
private Date memberJoin;
private Date memberLogin;
// 생성자
public MemberDto() {
super();
}
// getter & setter
public String getMemberId() {
return memberId;
}
public void setMemberId(String memberId) {
this.memberId = memberId;
}
public String getMemberPw() {
return memberPw;
}
public void setMemberPw(String memberPw) {
this.memberPw = memberPw;
}
public String getMemberNick() {
return memberNick;
}
public void setMemberNick(String memberNick) {
this.memberNick = memberNick;
}
public Date getMemberBirth() {
return memberBirth;
}
public void setMemberBirth(Date memberBirth) {
this.memberBirth = memberBirth;
}
public String getMemberTel() {
return memberTel;
}
public void setMemberTel(String memberTel) {
this.memberTel = memberTel;
}
public String getMemberEmail() {
return memberEmail;
}
public void setMemberEmail(String memberEmail) {
this.memberEmail = memberEmail;
}
public String getMemberPost() {
return memberPost;
}
public void setMemberPost(String memberPost) {
this.memberPost = memberPost;
}
public String getMemberBaseAddress() {
return memberBaseAddress;
}
public void setMemberBaseAddress(String memberBaseAddress) {
this.memberBaseAddress = memberBaseAddress;
}
public String getMemberDetailAddress() {
return memberDetailAddress;
}
public void setMemberDetailAddress(String memberDetailAddress) {
this.memberDetailAddress = memberDetailAddress;
}
public int getMemberPoint() {
return memberPoint;
}
public void setMemberPoint(int memberPoint) {
this.memberPoint = memberPoint;
}
public String getMemberGrade() {
return memberGrade;
}
public void setMemberGrade(String memberGrade) {
this.memberGrade = memberGrade;
}
public Date getMemberJoin() {
return memberJoin;
}
public void setMemberJoin(Date memberJoin) {
this.memberJoin = memberJoin;
}
public Date getMemberLogin() {
return memberLogin;
}
public void setMemberLogin(Date memberLogin) {
this.memberLogin = memberLogin;
}
// toString 오버라이딩 - memberPw(비밀번호)는 보안을 위해 제외할 것
@Override
public String toString() {
return "MemberDto [memberId=" + memberId
+ ", memberNick=" + memberNick
+ ", memberBirth=" + memberBirth
+ ", memberTel=" + memberTel
+ ", memberEmail=" + memberEmail
+ ", memberPost=" + memberPost
+ ", memberBaseAddress=" + memberBaseAddress
+ ", memberDetailAddress=" + memberDetailAddress
+ ", memberPoint=" + memberPoint
+ ", memberGrade=" + memberGrade
+ ", memberJoin=" + memberJoin
+ ", memberLogin=" + memberLogin + "]";
}
}
회원 비밀번호 변경 (회원 기능)
- View에서 기존 비밀번호(pwNow) 뿐만 아니라 새로운 비밀번호(pwNew), 비밀번호 확인(pwNewCheck)를 입력받는다
- Controller에서 해당 값들을 받을 수 있도록 DTO를 생성한다 (PasswordDto)
- 가장 먼저 새로운 비밀번호(pwNew)와 비밀번호 확인(pwNewCheck)의 일치 여부를 검사한다
- 일치한다면 기존 비밀번호와 새로운 비밀번호의 일치 여부를 검사해야 한다
- HttpSession에서 현재 로그인 중인 아이디를 반환한 후 이를 이용하여 단일 조회를 해야 한다
- 단일 조회의 결과(memberDto)에서 비밀번호(memberPw)를 반환하여 기존 비밀번호(pwNow)와의 일치 여부를 검사한다
- 일치한다면 memberDto의 비밀번호를 새로운 비밀번호로 설정한다
- 설정이 끝난 memberDto를 매개변수로 하여 수정(UPDATE)를 실행한다
PasswordDto
- 기존 비밀번호(pwNow), 새로운 비밀번호(pwNew), 비밀번호 확인(pwNewCheck)
package com.kh.springhomeSelfStudy.entity;
public class PasswordDto {
// 필드
String pwNow; // 기존 비밀번호
String pwNew; // 새로운 비밀번호
String pwNewCheck; // 비밀번호 확인
// 생성자
public PasswordDto() {
super();
}
// getter & setter
public String getPwNow() {
return pwNow;
}
public void setPwNow(String pwNow) {
this.pwNow = pwNow;
}
public String getPwNew() {
return pwNew;
}
public void setPwNew(String pwNew) {
this.pwNew = pwNew;
}
public String getPwNewCheck() {
return pwNewCheck;
}
public void setPwNewCheck(String pwNewCheck) {
this.pwNewCheck = pwNewCheck;
}
}
MemberDao
public interface MemberDao {
// 추상 메소드 - 비밀번호 변경(회원 기능)
boolean changePassword(String memberId, String memberPw);
}
MemberDaoImpl
@Repository
public class MemberDaoImpl implements MemberDao {
// 의존성 주입
@Autowired
private JdbcTemplate jdbcTemplate;
// 추상 메소드 오버라이딩 - 비밀번호 변경(회원 기능)
@Override
public boolean changePassword(String memberId, String memberPw) {
String sql = "update member set member_pw = ? where member_id = ?";
Object[] param = {memberPw, memberId};
return jdbcTemplate.update(sql, param) > 0;
}
}
MemberController
- View로부터 PasswordDto의 형태로 값을 받는다 (@ModelAttribute)
@Controller
@RequestMapping("/member")
public class MemberController {
// 의존성 주입
@Autowired
private MemberDao memberDao;
// 회원 비밀번호 변경
// 회원 비밀번호 변경 Mapping
@GetMapping("/password")
public String changePassword() {
// 회원 비밀번호 변경 페이지(password.jsp)로 연결
return "/member/password";
}
// 9. 회원 비밀번호 변경
// 9-1. 회원 비밀번호 변경 Mapping
@GetMapping("/password")
public String changePassword() {
// 회원 비밀번호 변경 페이지(password.jsp)로 연결
return "/member/password";
}
// 9-2. 회원 비밀번호 변경 Mapping에 DB처리
@PostMapping("/password")
public String changePassword(HttpSession session, @ModelAttribute PasswordDto passwordDto) {
// HttpSession의 loginId에 저장된 값(로그인 중인 아이디)을 반환
String memberId = (String) session.getAttribute("loginId");
// 반환된 아이디로 단일 조회 selectOne(String memberId) 실행
MemberDto memberDto = memberDao.selectOne(memberId);
// 단일 조회의 결과로 얻은 memberDto에서 비밀번호(memberPw) 반환
String memberPw = memberDto.getMemberPw();
// i) passwordDto의 변경 비밀번호(pwNew)와 비밀번호 확인(pwNewCheck)가 같은지 판별
boolean pwNewEqual = passwordDto.getPwNew().equals(passwordDto.getPwNewCheck());
// ii) memberDto의 비밀번호(memberPw)와 passwordDto의 기존 비밀번호(pwNow)가 같은지 판별
boolean pwEqual = memberPw.equals(passwordDto.getPwNow());
if(pwNewEqual && pwEqual) { // i)와 ii)가 모두 true라면
// memberDto에서 비밀번호(memberPw)만 passwordDto의 변경 비밀번호(pwNew)로 설정
memberDto.setMemberPw(passwordDto.getPwNew());
// 설정이 끝난 memberDto를 매개변수로 하여 수정(UPDATE) 실행
memberDao.update(memberDto);
// 수정(UPDATE) 실행 후 회원 비밀번호 변경 완료 Mapping으로 강제 이동(redirect)
return "redirect:password_change";
}
else { // 그렇지 않다면
// 회원 비밀번호 변경 Mapping으로 강제 이동(redirect)
return "redirect:password";
}
}
// 9-3. 회원 비밀번호 변경 완료 Mapping
@GetMapping("/password_change")
public String changePasswordSuccess() {
// 회원 비밀번호 변경 페이지(passwordSuccess.jsp)로 연결
return "/member/passwordSuccess";
}
}
password.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<jsp:include page = "/WEB-INF/views/template/header.jsp">
<jsp:param name = "title" value = "비밀번호 변경"/>
</jsp:include>
<h1>비밀번호 변경</h1>
<form action = "password" method = "post">
현재 비밀번호 : <input type = "password" name = "pwNow" placeholder = "현재 비밀번호"><br>
변경 비밀번호 : <input type = "password" name = "pwNew" placeholder = "변경 비밀번호"><br>
비밀번호 확인 : <input type = "password" name = "pwNewCheck" placeholder = "비밀번호 확인"><br>
<button type = "submit">변경</button>
</form>
<jsp:include page = "/WEB-INF/views/template/footer.jsp"></jsp:include>
개인정보 변경(회원기능)
- HttpSession에서 현재 로그인 중인 아이디를 반환한 후 이를 이용하여 단일 조회를 해야 한다
- 단일 조회의 결과를 View에 전달하여 <form>으로 비밀번호를 제외한 수정 전 값을 표시한다
- View의 사용자 입력을 MemberDto의 형태로 받는다 (@ModelAttribute)
- 단일 조회의 결과인 findDto와 사용자 입력인 inputDto의 비밀번호(memberPw)를 각각 반환하여
두 값이 일치할 경우 수정(UPDATE)을 실행한다
- 수정할 수 없는 회원 정보가 있다
1) 회원 아이디(memberId), 생년월일(memberBirth), 회원 가입일(memberJoin)은 수정할 수 없다
2) 포인트(memberPoint), 회원 등급(memberGrade)는 관리자가 수정해야 할 항목이다
3) 마지막 로그인(memberLogin)은 로그인할 때만 갱신되어야 한다
MemberDao
public interface MemberDao {
// 추상 메소드 - 개인정보 변경(회원 기능)
boolean changeInformation(MemberDto memberDto);
}
MemberDaoImpl
@Repository
public class MemberDaoImpl implements MemberDao {
// 의존성 주입
@Autowired
private JdbcTemplate jdbcTemplate;
// 추상 메소드 오버라이딩 - 개인정보 변경(회원 기능)
@Override
public boolean changeInformation(MemberDto memberDto) {
String sql = "update member set member_nick = ?, "
+ "member_tel = ?, "
+ "member_email = ?, "
+ "member_post = ?, "
+ "member_base_address = ?, "
+ "member_detail_address = ? "
+ "where member_id = ?";
Object[] param = {memberDto.getMemberNick(),
memberDto.getMemberTel(),
memberDto.getMemberEmail(),
memberDto.getMemberPost(),
memberDto.getMemberBaseAddress(),
memberDto.getMemberDetailAddress(),
memberDto.getMemberId()};
return jdbcTemplate.update(sql, param) > 0;
}
}
MemberController
// 10. 개인정보 변경
// 10-1. 개인정보 변경 Mapping
@GetMapping("/information")
public String information(HttpSession session, Model model) {
// HttpSession의 loginId에 저장된 값(로그인 중인 아이디)을 반환
String memberId = (String) session.getAttribute("loginId");
// 반환된 아이디로 단일 조회 selectOne(String memberId) 실행
MemberDto memberDto = memberDao.selectOne(memberId);
// model에 단일 조회의 결과를 첨부
model.addAttribute("memberDto", memberDto);
// 회원 정보 변경 페이지(information.jsp)로 연결
return "/member/information";
}
// 10-2. 개인정보 변경 Mapping에 DB처리
// - View에서 입력받은 inputDto에는 회원 아이디(memberId)가 없다
@PostMapping("/information")
public String information(HttpSession session, @ModelAttribute MemberDto inputDto) {
// 비밀번호 일치 여부 판별
// HttpSession의 loginId에 저장된 값(로그인 중인 아이디)을 반환
String memberId = (String) session.getAttribute("loginId");
// 반환된 아이디로 단일 조회 selectOne(String memberId) 실행
MemberDto findDto = memberDao.selectOne(memberId);
// 단일 조회의 결과로 얻은 findDto와 View에서 입력받은 inputDto에서
// 각각 비밀번호(memberPw)를 반환하여 그 값이 같은지 판별
boolean passwordMatch = inputDto.getMemberPw().equals(findDto.getMemberPw());
if(passwordMatch) { // 비밀번호가 일치할 경우
// inputDto의 회원 아이디(memberId)를 HttpSession에서 반환한 아이디로 설정
inputDto.setMemberId(memberId);
// inputDto를 매개변수로 하여 수정(UPDATE) 실행
memberDao.changeInformation(inputDto);
// 수정(UPDATE) 실행 후 회원 마이페이지 Mapping으로 강제 이동(redirect)
return "redirect:mypage";
}
else { // 비밀번호가 틀린 경우
// 정보 변경 실패 Mapping으로 강제 이동(redirect)
return "redirect:information?error";
}
}
// 10-3. 정보 변경 실패 Mapping
@GetMapping("/information_fail")
public String informationFail() {
// 수정 실패 페이지(editFail.jsp)으로 이동 (jsp 재활용)
return "redirect:editFail";
}
회원 탈퇴(회원기능)
- View에서 회원 비밀번호(memberPw), 비밀번호 확인(memberPwCheck)를 입력받는다 (@RequestParam)
- 회원 비밀번호(memberPw)와 비밀번호 확인(memberPwCheck)의 일치 여부를 판별한다
- 두 값이 일치한다면 회원 탈퇴(관리자 전용) 메소드인 delete(String memberId)를 실행한다
- 메소드 실행 후 로그아웃시킨다
(비회원은 회원 관련 모든 페이지에서 Interceptor의 간섭을 받으므로 회원 탈퇴 후 로그아웃을 시켜야 한다)
- 회원 탈퇴 결과 Mapping은 로그아웃을 위해 Interceptor의 간섭을 받지 않도록 해야 한다
MemberDao, MemberDaoImpl
- 기존에 작성했던 회원 탈퇴(관리자 전용) 메소드인 delete(String memberId)을 사용한다
MemberController
@Controller
@RequestMapping("/member")
public class MemberController {
// 의존성 주입
@Autowired
private MemberDao memberDao;
// 11. 회원 탈퇴 (회원 기능)
// 11-1. 회원 탈퇴 Mapping
@GetMapping("/goodbye")
public String goodbye() {
// 회원 탈퇴 페이지(goodbye.jsp)로 연결
return "/member/goodbye";
}
// 11-2. 회원 탈퇴 Mapping에서 DB 처리
@PostMapping("/goodbye")
public String goodbye(HttpSession session, @RequestParam String inputPw, @RequestParam String inputPwCheck) {
// 1) 입력 비밀번호 일치 여부
boolean inputMatch = inputPw.equals(inputPwCheck);
// 2) 비밀번호 일치 여부
// HttpSession의 loginId에 저장된 값(로그인 중인 아이디)을 반환
String memberId = (String) session.getAttribute("loginId");
// 반환된 아이디로 단일 조회 selectOne(String memberId) 실행
MemberDto memberDto = memberDao.selectOne(memberId);
// 단일 조회의 결과로 얻은 memberDto의 비밀번호(memberPw)와 입력 비밀번호(inputPw)가 같은지 판별
boolean passwordMatch = memberDto.getMemberPw().equals(inputPw);
if(inputMatch && passwordMatch) { // 둘 다 일치할 경우
// memberId를 매개변수로 하여 삭제(DELETE) 실행
memberDao.delete(memberId);
// HttpSession의 모든 정보(로그인 회원 아이디, 회원 등급) 삭제
session.removeAttribute("loginId");
session.removeAttribute("mg");
// 회원 탈퇴 성공 페이지 Mapping으로 강제 이동(redirect)
return "redirect:goodbye_result";
}
else { // 그렇지 않을 경우
// 회원 탈퇴 에러 Mapping으로 강제 이동(redirect)
return "redirect:goodbye?error";
}
}
// 11-3. 회원 탈퇴 성공 페이지
@GetMapping("/goodbye_result")
public String goodbyeResult() {
// 회원 탈퇴 페이지(goodbyeResult.jsp)로 연결
return "/member/goodbyeResult";
}
}
goodbye.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:include page = "/WEB-INF/views/template/header.jsp">
<jsp:param name = "title" value = "회원 탈퇴"/>
</jsp:include>
<div align = "center">
<h1>회원 탈퇴</h1><br><br>
<form action = "goodbye" method = "post">
비밀번호 : <input type = "password" name = "inputPw" required placeholder = "비밀번호"><br><br>
비밀번호 확인 : <input type = "password" name = "inputPwCheck" required placeholder = "비밀번호 확인"><br><br>
<button type = "submit">탈퇴하기</button>
</form>
</div>
<jsp:include page = "/WEB-INF/views/template/footer.jsp"></jsp:include>
Interceptor 설정 추가
- 회원 탈퇴 결과 Mapping 제외(exclude)
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {
// 의존성 주입
@Autowired
TestInterceptor testInterceptor;
// 의존성 주입
@Autowired
MemberInterceptor memberInterceptor;
// 의존성 주입
@Autowired
AdminInterceptor adminInterceptor;
// 추상 메소드 오버라이딩 - addInterceptors(InterceptorRegistry registry)
@Override
public void addInterceptors(InterceptorRegistry registry) {
// TestInterceptor에 대한 설정
registry.addInterceptor(testInterceptor)
.addPathPatterns("/**");
// MemberInterceptor에 대한 설정
registry.addInterceptor(memberInterceptor)
.addPathPatterns(
"/pocketmon/**", // 포켓몬 전부
"/music/detail", // 음원 상세
"/member/**" // 회원 전체
)
.excludePathPatterns(
"/member/join", // 회원가입
"/member/insert_success", // 회원 가입 완료
"/member/login", // 로그인
"/member/goodbye_result" // 탈퇴 완료
);
// AdminInterceptor에 대한 설정
registry.addInterceptor(memberInterceptor)
.addPathPatterns(
"/guestbook/edit", // 방명록 수정
"/guestbook/delete", // 방명록 상세
"/music/**", // 음원 전체
"/member/list", // 회원 목록
"/member/detail", // 회원 상세
"/member/delete", // 회원 삭제
"/member/edit*" // 회원 정보 수정
)
.excludePathPatterns(
"/music/list", // 음원 목록
"/music/detail" // 음원 상세
);
}
}
관리자페이지
- 관리자만 접근할 수 있는 페이지
- 관리자는 회원 기능 뿐만 아니라 관리자만의 기능을 사용할 수 있다
- 관리자 페이지는 관리자 외에는 접근하지 못하도록 Interceptor로 차단해야 한다
AdminController
@Controller
@RequestMapping("/admin")
public class AdminController {
@GetMapping("/home")
public String home() {
return "admin/home";
}
}
home.jsp
- home에서 관리자 페이지로 이동할 수 있도록 하이퍼링크 설정
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>
<c:choose>
<%-- title이라는 변수의 값이 있다면 title에 입력될 값은 해당 title 변수의 값으로 --%>
<c:when test = "${param.title != null}">
${param.title}
</c:when>
<c:otherwise>
홈페이지
</c:otherwise>
</c:choose>
</title>
</head>
<body>
<div align = "center">
<%-- 로그인 상태인지를 판별 --%>
<c:set var = "login" value = "${loginId != null}"></c:set>
<%-- 로그인한 회원의 등급이 '관리자'인지 판별 --%>
<c:set var = "admin" value = "${mg == '관리자'}"></c:set>
<%-- 로그인을 할 경우에만 보이는 메뉴 --%>
<c:choose>
<%-- 회원에게만 보이는 메뉴 --%>
<c:when test = "${login}">
<a href = "/">홈</a>
<a href = "/guestbook/list">방명록</a>
<a href = "/pocketmon/list">포켓몬스터</a>
<a href = "/music/list">음원관리</a>
<a href = "/member/logout">로그아웃</a>
<a href = "/member/mypage">마이페이지</a>
</c:when>
<c:otherwise>
<a href = "/">홈</a>
<a href = "/guestbook/list">방명록</a>
<a href = "/member/join">회원가입</a>
<a href = "/member/login">로그인</a>
</c:otherwise>
</c:choose>
<%-- 관리자에게만 보이는 메뉴 --%>
<c:if test = "${login && admin}">
<a href="/admin/home">관리자페이지</a>
</c:if>
</div>
<hr>
<%-- 미리 <div> 태그를 써서 Main Content는 자동으로 가운데 정렬이 되도록 한다 --%>
<%-- 나머지 </div>는 footer에 작성하여 Main Content를 감싸도록 한다 --%>
<div align = "center" style = "min-height : 400px">
Interceptor 설정 추가
- 관리자 페이지 Mapping에 대한 Interceptor 추가
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {
// 의존성 주입
@Autowired
TestInterceptor testInterceptor;
// 의존성 주입
@Autowired
MemberInterceptor memberInterceptor;
// 의존성 주입
@Autowired
AdminInterceptor adminInterceptor;
// 추상 메소드 오버라이딩 - addInterceptors(InterceptorRegistry registry)
@Override
public void addInterceptors(InterceptorRegistry registry) {
// TestInterceptor에 대한 설정
registry.addInterceptor(testInterceptor)
.addPathPatterns("/**");
// MemberInterceptor에 대한 설정
registry.addInterceptor(memberInterceptor)
.addPathPatterns(
"/pocketmon/**", // 포켓몬 전부
"/music/detail", // 음원 상세
"/member/**" // 회원 전체
)
.excludePathPatterns(
"/member/join", // 회원가입
"/member/insert_success", // 회원 가입 완료
"/member/login", // 로그인
"/member/goodbye_result" // 탈퇴 완료
);
// AdminInterceptor에 대한 설정
registry.addInterceptor(memberInterceptor)
.addPathPatterns(
"/guestbook/edit", // 방명록 수정
"/guestbook/delete", // 방명록 상세
"/music/**", // 음원 전체
"/member/list", // 회원 목록
"/member/detail", // 회원 상세
"/member/delete", // 회원 삭제
"/member/edit*", // 회원 정보 수정
"/admin/**" // 관리자 페이지
)
.excludePathPatterns(
"/music/list", // 음원 목록
"/music/detail" // 음원 상세
);
}
}
관리자템플릿페이지
adminHeader.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%-- 기존의 Header를 재사용 --%>
<jsp:include page="/WEB-INF/views/template/header.jsp">
<jsp:param name = "title" value = "${param.title}"/>
</jsp:include>
<%--관리자 메뉴 추가 --%>
<a href = "/member/list">회원목록</a>
<a href = "/admin/pocketmon">포켓몬 현황</a>
<a href = "/admin/music/play">음원 재생 순위</a>
<a href = "/admin/music/release">월별 판매 현황</a>
<hr>
adminFooter.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%-- 기존의 Footer를 재사용 --%>
<jsp:include page = "/WEB-INF/views/template/footer.jsp"></jsp:include>
DTO (Data Transfer Object)
- 계층 간(Controller, View, Business Layer) 데이터 교환을 위한 객체
- field, getter & setter 메소드, 기본 생성자만 가진 클래스
VO (Value Object)
- DTO와 비슷하나 내부의 값을 변경할 수 없는(immutable) 읽기 전용(read-only) 객체
- field, getter 메소드, 기본 생성자만 가진 클래스
(과정에서는 DB 테이블의 전체 컬럼 중 일부의 값만을 가지고 올 때 VO를 사용하였다)
VO 활용 1. 관리자 페이지에 속성별 포켓몬의 수 조회
- 타입(type)과 타입별 수(cnt)를 조회하여 View에 표시한다
- 테이블의 전체 컬럼 중 일부 컬럼의 값만 조회할 때 VO를 정의하여 사용한다
PocketMonsterVO
public class PocketMonsterCountVO {
// 필드
private String type;
private int cnt;
// 생성자
public PocketMonsterCountVO() {
super();
}
// getter & setter
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
// toString 오버라이딩
@Override
public String toString() {
return "PocketMonsterCountVO [type=" + type + ", cnt=" + cnt + "]";
}
}
PocketMonsterDao
public interface PocketMonsterDao {
// 추상 메소드 - 타입별 포켓몬 수
List<PocketMonsterCountVO> selectCountList();
}
PocketMonsterDaoImpl
@Repository
public class PocketMonsterDaoImpl implements PocketMonsterDao {
// 의존성 주입
@Autowired
private JdbcTemplate jdbcTemplate;
// PocketMonsterCountVO에 대한 RowMapper
private RowMapper<PocketMonsterCountVO> countMapper = new RowMapper<>() {
@Override
public PocketMonsterCountVO mapRow(ResultSet rs, int rowNum) throws SQLException {
PocketMonsterCountVO vo = new PocketMonsterCountVO();
vo.setType(rs.getString("type"));
vo.setCnt(rs.getInt("cnt"));
return vo;
}
};
// 추상 메소드 오버라이딩 - 타입별 포켓몬 수
@Override
public List<PocketMonsterCountVO> selectCountList() {
String sql = "select type, count(*) cnt from pocket_monster group by type";
return jdbcTemplate.query(sql, countMapper);
}
}
PocketMonsterController
@Controller
@RequestMapping("/admin")
public class AdminController {
// 의존성 주입
@Autowired
private PocketMonsterDao pocketMonsterDao;
// 포켓몬 현황 Mapping
@GetMapping("/pocketmon")
public String pocketmon(Model model) {
// model에 타입별 포켓몬 수 조회 결과를 첨부
model.addAttribute("list", pocketMonsterDao.selectCountList());
// 포켓몬 현황 페이지(pocketmon.jsp)로 연결
return "admin/pocketmon";
}
}
pocketmon.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:include page = "/WEB-INF/views/template/adminHeader.jsp">
<jsp:param name = "title" value = "관리자 페이지"/>
</jsp:include>
<h1>포켓몬 현황</h1>
<table border = "1" width = "300">
<thead>
<tr>
<th>순위</th>
<th>속성</th>
<th>개체수</th>
</tr>
</thead>
<tbody align = "center">
<c:forEach var = "vo" items = "${list}" varStatus = "status">
<tr>
<td>${status.count}</td>
<td>${vo.getType()}</td>
<td>${vo.cnt}</td>
</tr>
</c:forEach>
</tbody>
</table>
<jsp:include page = "/WEB-INF/views/template/adminFooter.jsp"></jsp:include>
- <c:forEach></c:forEach>의 varStatus 속성을 사용하면 반복문의 상태를 알 수 있다
count | 출력 중인 데이터의 순서(갯수, 1번부터 시작) |
index | 출력 중인 데이터의 인덱스(위치, 0번부터 시작) |
first | 처음인지 아닌지 반환 |
last | 마지막인지 아닌지 반환 |