업무일지_231013
오늘의 할 일
- 회원정보변경 내용(+프로필사진) 화면에 띄어주기
- 회원정보변경 정규표현식 + unique컬럼 중복체크
- 비밀번호 변경 모달창(정규표현식)
- 회원 등급 및 쿠폰 조회 화면 틀잡기
- 회원 등급 조회
달성률 : 30%
오늘 한 일
- 회원정보변경 내용 화면에 띄어주기
- 회원정보변경 정규표현식 + unique컬럼 중복체크
- 비밀번호 변경 모달창(정규표현식)
오늘의 문제점
- 제약조건 변경(ALTER)시 삭제(DROP)후 변경(MODIFY)!
- 회원정보변경 후 로그인이 풀림
- 같은 기능을 사용한다면 어떤 방식으로 사용할지 정해놓아야 함
(전에 사용했던 기능을 다시 사용할때 ajax인지 jsp방식으로 보냈는지에 따라 다름)
- input요소에 value는 잘 들어가는데 placeholder에는
바로 loginMember.getMemName() 뽑아버리니까 안됨
=> body영역에 include 밑에
<%
String memId = loginMember.getMemId();
String memName = loginMember.getMemName();
String memNickname = loginMember.getMemNickname();
%>
이런식으로 뽑으면 됨 (엥 또 되네?)
하지만 value에 넣었지
/헤더 바꾸기
controller 합쳐서 해보기
ajax이용
memberUpdateForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원정보변경</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
<style>
/* Bordered form */
form {
border: 3px solid #f1f1f1;
}
/* Full-width inputs */
input[type=text], input[type=password] {
width: 100%;
padding: 12px 20px 12px 20px;
margin: 0px 0px 0px 0px;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
/* Set a style for all buttons */
button {
background-color: rgb(88, 87, 87);
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
}
/* Add a hover effect for buttons */
button:hover {
opacity: 0.8;
}
/* Extra style for the cancel button (red) */
.cancelbtn {
width: auto;
padding: 10px 18px;
background-color: #f44336;
}
/* Add padding to containers */
.container {
padding: 100px 300px;
}
/* The "Forgot password" text */
span.psw {
float: right;
padding-top: 16px;
}
/* Change styles for span and cancel button on extra small screens */
@media screen and (max-width: 300px) {
span.psw {
display: block;
float: none;
}
.cancel-btn {
width: 100%;
}
}
#title{
padding-top: 100px;
display : flex;
align-items: center;
justify-content: center;
}
.enroll-checkbox{
border : none;
margin : 20px 0px;
}
.enroll-checkbox > div{
border : none;
}
.enroll-checkbox > div > a{
margin-right : 20px;
color: black;
}
input[type="checkbox"]{
margin : 5px;
}
.container > label{
font-size: 12px;
margin : 0px 0px 10px 0px;
}
.tag{
margin : 10px 0px;
}
.compare{
display : none;
}
</style>
</head>
<body>
<!-- header부분 (상단 메인 메뉴바) -->
<%@ include file="/views/common/header.jspf" %>
<form enctype="multipart/form-data" action="<%= contextPath %>/yrmemberUpdate.me" method="post">
<h1 id="title"><b>회원정보변경</b></h1>
<!-- 사진을 바꿀 회원의 번호를 hidden으로 같이 넘겨주기 -->
<input type="hidden" name="memberNo" value="<%= loginMember.getMemNo() %>">
<!-- 사진 -->
<div class="container">
<% if(loginMember.getMemPicture() != null) { %>
<img src="<%= contextPath %>/<%= loginMember.getMemPicture() %>" alt="프로필사진" id="profileImg" width="150" height="150">
<% } else { %>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSiJ77jbjsG1bGoS5Kn6gm83uk-iiWcuMLRzw&usqp=CAU" alt="프로필사진" id="profileImg" width="150" height="150">
<% } %>
<input type="file" name="profileInput" id="profileInput" onchange="loadImg(this);">
</div>
<!-- 정보 내용 변경 -->
<div class="container">
<!-- value에 loginUser를 update된 걸 넣음 -->
<p class="tag">이름</p>
<input type="text" value="<%=loginMember.getMemName() %>" name="memberName" id="memberName" maxlength="5" required>
<label for="memberName">* 한글 2 ~ 5자로 입력 가능합니다.</label>
<p class="tag">닉네임(활동명)</p>
<input type="text" value="<%= loginMember.getMemNickname() %>" name="memberNickname" id="memberNickname" maxlength="8" required>
<label for="memberNickname">* 영문, 한글, 숫자 3 ~ 8자로 입력 가능합니다. </label>
<p class="tag">아이디(변경불가)</p>
<input type="text" value="<%= loginMember.getMemId() %>" readonly name="memberId" id="memberId" maxlength="20" required>
<label for="memberId">* 영문, 숫자 5 ~ 20자로 입력 가능합니다.</label>
<p class="tag">이메일</p>
<input type="text" value="<%= loginMember.getMemEmail() %>" name="memberEmail" id="memberEmail" maxlength="50" required>
<label for="memberEmail">* 인증받을 이메일을 입력해 주세요.</label>
<!-- 현재 로그인된 값 비교할 수 있게 값 뽑기 -->
<p class="compare" id="loginMemNickname"><%= loginMember.getMemNickname() %></p>
<p class="compare" id="loginMemEmail"><%= loginMember.getMemEmail() %></p>
<!-- 제출버튼!!!!!!!!!!!!!!!!!! onclick = "return validate();"-->
<button type="submit" id="submitBtn" >변경하기</button>
<div class="enroll-checkbox">
<!-- 비밀번호 변경 모달창-->
<div>
<a data-toggle="modal" href="#agreeSiteModal">비밀번호 변경</a>
<div class="modal" id="agreeSiteModal">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title">비밀번호 변경</h4>
<button type="button" class="close" data-dismiss="modal" id="close">×</button>
</div>
<!-- Modal body -->
<!-- <form action=""> -->
<!-- 전에 비밀번호 변경도 ajax로 했으니 이것도 ajax로 해주자 -->
<div class="modal-body">
<label for="close"></label>
<input type="password" placeholder="비밀번호" name="memberPwd" id="memberPwd" maxlength="20">
<label for="memberPwd">* 영문, 숫자, 특수문자(!@#$+^*) 포함 8 ~ 20자로 입력 가능합니다.</label>
<input type="password" placeholder="비밀번호 확인" name="memberPwdCheck" id="memberPwdCheck" maxlength="20">
<label for="memberPwdCheck">* 비밀번호가 일치하지 않습니다.</label>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<!-- 아이디 같이 보내주기 -->
<!-- <button type="submit" action="<%= contextPath %>/yrupdateMemberPwd.me?memberId=<%= loginMember.getMemId() %>" method="post"></button> -->
<button type="button" class="btn btn-danger" id="updatePwdBtn" onclick="updateMemberPwd();" data-dismiss="modal" >비밀번호 변경</button>
</div>
<!-- </form> -->
</div>
</div>
</div>
</div>
</div>
</div>
</form>
<!-- footer 푸터영역 -->
<%@ include file="/views/common/footer.jspf" %>
<script>
// 필요한 변수 선언
const profileImg = document.getElementById('profileImg');
const profileInput = document.getElementById('profileInput');
// 사진을 클릭하면 프로필 사진 변경
function loadImg(inputFile){
// 파일을 첨부했을 때
if(inputFile.files.length == 1){
// 파일을 읽어올 객체 생성
let reader = new FileReader();
// 파일의 긴 url을 읽어옴
reader.readAsDataURL(inputFile.files[0]);
// 파일읽기가 완료되면 익명함수 호출
reader.onload = function(e){
// $('#profileImg').attr('src', e.target.result);
// 이벤트가 발생한곳의 result에 담겨있는 url을 파일 src값으로 넣어줌
console.log(profileImg);
profileImg.src = e.target.result;
}
// 취소(파일 없음)
} else {
// 새로 등록된 사진이 없으므로 기존의 사진 등록
const noImg = 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSiJ77jbjsG1bGoS5Kn6gm83uk-iiWcuMLRzw&usqp=CAU';
// https://usagi-post.com/wp-content/uploads/2020/05/no-image-found-360x250-1.png
// https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSiJ77jbjsG1bGoS5Kn6gm83uk-iiWcuMLRzw&usqp=CAU
// https://s3.orbi.kr/data/file/united2/9f61edb66efb4ff982a2675ec5e9e806.png
profileImg.src = noImg;
}
};
// 파일첨부 input요소를 숨기고 사진을 클릭했을 때 input요소 클릭
profileInput.style.display = 'none';
profileImg.addEventListener('click', function(){
profileInput.click();
});
// 최대한 javascript로도 해보자
/*
$(function(){
$('#profileInput').hide();
$('#profileImg').click(function(){
$('#profileInput').click();
})
})
*/
// 비밀번호 변경 모달창 ajax
const $memberId = $('#memberId');
const $memberPwd = $('#memberPwd');
const $memberPwdCheck = $('#memberPwdCheck');
// 비밀번호 재설정 유효성 검사
// 비밀번호 (영문 숫자 특수문자 !@#$+^* 포함 8 ~ 20자)
$(function(){
$('input[name*=memberPwd]').keyup(function(){
var $regExp = /^[a-zA-Z0-9!@#$+^*]{8,20}$/;
// 비밀번호 정규표현식 불일치 시
if(!$regExp.test($('input[name=memberPwd]').val())){
$('label[for="memberPwd"]').css('color', 'red');
$('#updatePwdBtn').attr('disabled', true);
// 비밀번호확인 불일치 시 (정규표현식을 일치)
} else if($memberPwd.val() != $memberPwdCheck.val()){
$('label[for="memberPwdCheck"]').text('* 비밀번호가 일치하지 않습니다.').css('color', 'red');
$('label[for="memberPwd"]').css('color', 'black');
$('#updatePwdBtn').attr('disabled', true);
// 둘다 일치
} else {
$('label[for="memberPwdCheck"]').text('').css('color', 'black');
$('label[for="memberPwd"]').css('color', 'black');
$('#updatePwdBtn').attr('disabled', false);
}
})
});
function updateMemberPwd(){
if($memberPwd.val() != $memberPwdCheck.val()){
// $('label[for="memberPwdCheck"]').text("* 비밀번호가 일치하지 않습니다.").css('color', 'red');
Swal.fire({
title: '비밀번호 재설정 실패',
text: "비밀번호가 변경되지 않았습니다.",
icon: 'error',
})
} else {
// $('label[for="memberPwdCheck"]').text("").css('color', 'black');
$.ajax({
url : 'yrupdateMemberPwd.me',
data : {memberId : $memberId.val(),
memberPwd : $memberPwd.val()
},
// ajax통신 성공
success : function(result){
if(result == 'S'){
Swal.fire({
title: '비밀번호 재설정 성공',
text: "비밀번호가 변경되었습니다.",
icon: 'success',
})
} else {
Swal.fire({
title: '비밀번호 재설정 실패',
text: "비밀번호가 변경되지 않았습니다.",
icon: 'error',
})
}
}
// ajax통신 실패
});
}
}
// 정규표현식
$(function() {
// ★★★★★★★★★★★★★★★★★★클릭한거 뒤에 있는 label의 속성값 바꾸기~_231010
$('input').keyup(function(){
var $errorCheck = $('label[for="' + $(this).attr('id') + '"]');
// 정규표현식 초기화
var $regExp = /^/;
// 1) 이름 (2 ~ 6자 이내)
if($(this)[0] == $('#memberName')[0]) {
var $regExp = /^[가-힣]{2,6}$/;
}
// 2) 닉네임 (3 ~ 8자)
if($(this)[0] == $('#memberNickname')[0]) {
var $regExp = /^[a-z0-9가-힣]{3,8}$/;
// 중복체크 호출(현재 로그인된 값은 중복체크X)
if($('#loginMemNickname').text() != $('#memberNickname').val())
nicknameCheck();
$errorCheck.text("* 영문, 한글, 숫자 3 ~ 8자로 입력 가능합니다. ").css('color', 'black');
};
// 3) 이메일 (이메일앞부분 6 ~ 24자 + @6 ~ 14자, . 2 ~ 6자가 들어간 형식)
if($(this)[0] == $('#memberEmail')[0]) {
var $regExp = /^[a-z0-9]+@[a-z]+\.[a-z]{2,6}$/;
// 중복체크 호출(현재 로그인된 값은 중복체크X)
if($('#loginMemEmail').text() != $('#memberEmail').val())
emailCheck();
$errorCheck.text("인증받으실 이메일을 입력해 주세요.").css('color', 'black');
};
// 정규표현식의 결과를 반환해줄 변수설정
var eachResult = {};
var eventThis = $(this).attr('id');
eachResult['eachResult'+eventThis] = 0;
// 각 정규표현식에 따른 결과
// 비밀번호는 모달창에서 따로 진행, 다시 초기화된 값으로 조건처리되므로 제외시킴
if($(this)[0] != $memberPwd[0]){
if(!$regExp.test($(this).val())){
$errorCheck.css('color', 'red');
eachResult['eachResult'+eventThis] = 0;
} else{
$errorCheck.css('color', 'black');
eachResult['eachResult'+eventThis] = 1;
};
}
// 모든 조건을 만족할 때 결과
var submitResult = 1;
// 모든 유효성검사 통과 시 버튼 활성화!!!!!!!!!!!!!
var list = ['memberName', 'memberNickname', 'memberEmail'];
for(var a of list){
// 리스트에 속성값이 초기화되지 않은 경우 NaN이 발생 => 조건처리
if(!isNaN(eachResult['eachResult' + a])){
submitResult *= eachResult['eachResult' + a];
}
}
// 조건 만족 시 버튼 활성화 여부
if(submitResult > 0){
$('#submitBtn').attr('disabled', false);
} else{
$('#submitBtn').attr('disabled', true);
}
});
})
// ajax를 이용하여 닉네임 중복체크
function nicknameCheck(){
$.ajax({
url : 'yrnicknameCheck.me',
data : {checkNickname : $('#memberNickname').val()},
// 중복체크 조회 성공 시
success : function(result) {
// 중복된 아이디
const $nicknameLabel = $('label[for="memberNickname"]');
if(result == 'NNNNN'){
$nicknameLabel.text("* 이미 존재하는 닉네임입니다!").css('color', 'red');
// 사용가능한 아이디
} else{
$('#submitBtn').attr('disabled', false);
}
},
// 중복체크 조회 실패 시
error : function(){
console.log('닉네임 중복체크 AJAX통신 실패!');
}
})
};
// ajax를 이용하여 이메일 중복체크
function emailCheck(){
$.ajax({
url : 'yremailCheck.me',
data : {checkEmail : $('#memberEmail').val()},
// 중복체크 조회 성공 시
success : function(result) {
// 중복된 아이디
if(result == 'NNNNN'){
$('label[for="memberEmail"]').text("* 이미 사용하고 있는 이메일입니다!").css('color', 'red');
$('#submitBtn').attr('disabled', true);
return false;
// 사용가능한 아이디
} else{
$('#submitBtn').attr('disabled', false);
return true;
}
},
// 중복체크 조회 실패 시
error : function(){
console.log('이메일 중복체크 AJAX통신 실패!');
}
})
}
</script>
</body>
</html>
controller
package com.kh.semi.member.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import com.kh.semi.common.MyFileRenamePolicy;
import com.kh.semi.member.model.service.MemberService;
import com.kh.semi.member.model.vo.Member;
import com.oreilly.servlet.MultipartRequest;
/**
* Servlet implementation class MemberUpdateController
*/
@WebServlet("/yrmemberUpdate.me")
public class MemberUpdateController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public MemberUpdateController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
if(ServletFileUpload.isMultipartContent(request)) {
// 파일 크기 10MB
int maxSize = 1024 * 1024 * 10;
// 저장경로
String savePath = request.getSession().getServletContext().getRealPath("/resources/profile_upfiles");
// HttpServletRequest request 객체를 MultipartRequest 변환
// MultipartRequest 객체 생성 시 파일 업로드
// MyFileRenamePolicy()는 파일 이름 수정
MultipartRequest multiRequest = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy());
// 값 가져오기
// int memberNo = Integer.parseInt(multiRequest.getParameter("memberNo"));
String memberName = multiRequest.getParameter("memberName");
String memberNickname = multiRequest.getParameter("memberNickname");
String memberEmail = multiRequest.getParameter("memberEmail");
// 회원정보변경 후 다시 회원 조회를 위한 id, pwd값
String memberId = multiRequest.getParameter("memberId");
String memberPwd = multiRequest.getParameter("memberPwd");
// 가져온 값들 객체로 Service 넘길 것
Member m = new Member();
// m.setMemNo(memberNo);
m.setMemId(memberId);
m.setMemName(memberName);
m.setMemNickname(memberNickname);
m.setMemEmail(memberEmail);
// 현재 파일수정을 눌렀다면 무조건 사진이 있음. 근데 기본값은 null임
String memberPicture = null;
// 첨부파일이 있다면
System.out.println("확인");
System.out.println(multiRequest.getOriginalFileName("profileInput"));
if(multiRequest.getOriginalFileName("profileInput") != null) {
// 파일경로 + 파일수정명을 넘겨줄거임(DB에 MEM_PICTURE컬럼에 저장)
memberPicture = "/resources/profile_upfiles/" + multiRequest.getFilesystemName("profileInput");
}
// Service로 수정할 회원 정보와 파일정보를 넘겨 요청
int result = new MemberService().memberUpdate(m, memberPicture);
// update 성공 시
if(result > 0) {
// 회원의 정보를 다시 SELECT해와야함
// => 왜냐면 loginMember에 들어있는 정보는 update후 갱신이 안되어 있음
Member updatedMember = new MemberService().loginMember(memberId, memberPwd);
System.out.println("여기야???");
System.out.println(updatedMember); // null
request.getSession().setAttribute("loginMember", updatedMember);
// 나중에 마이페이지 메인으로 바꿀 것
// update를 한 걸 업데이트가 돼야 하니까 sendRedirect로 가야하나? => 로그인이 풀려버림
// request.getRequestDispatcher(request.getContextPath()).forward(request, response);
// 현재 blog.me를 찾을 수 없음 임시로 변경
// response.sendRedirect(request.getContextPath() + "/blog.me");
response.sendRedirect("views/myPage/memberPage.jsp");
// request.getRequestDispatcher("views/myPage/memberPage.jsp").forward(request, response);
} else { // 실패 시
request.getSession().setAttribute("memberUpdateError", "변경에 실패하였습니다.");
request.getRequestDispatcher(request.getContextPath() + "/yrmemberUpdateConfirmForm.me").forward(request, response);
}
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}