스프링부트+jsp로 배달사이트 만들기-37 내 정보 수정하기

2021. 12. 27. 21:27스프링부트

myPage 에서 내 닉네임을 클릭했을 때 회원정보 수정페이지로 이동합니다

 

 

UserController에 내정보 메서드를 추가합니다

@GetMapping("/user/myInfo")
public String myInfo() {
	return "user/myInfo";
}

 

user패키지에 jsp, css, js를 추가합니다

myInfo.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 <%@ include file="/WEB-INF/view/include/link.jsp" %>
<link rel="stylesheet" href="/css/layout/nav.css" >
<link rel="stylesheet" href="/css/user/myInfo.css" >
<%@ include file="/WEB-INF/view/include/header.jsp" %>


	<div class="wrap">
	
		<section class="title">
			<h1>내 정보 수정</h1>
		</section>
		
	    <main>
		    <div class="info_box">
		    	
				<h2>닉네임</h2>
				<div class="user_nickname">${SPRING_SECURITY_CONTEXT.authentication.principal.user.nickname }</div>		    	
		    	
	    		<h2>비밀번호</h2>
		    	<div>
		    		<div class="input_box">
			    		<div>
			    			<span>현재 비밀번호</span>
			    			<input class="present_password" type="password" placeholder="현재 비밀번호" >
			    		</div>
			    		
			    		<div>
			    			<span>신규 비밀번호</span>
			    			<input class="new_password" type="password" placeholder="신규 비밀번호" maxlength="20" >
			    		</div>
			    		
			    		<div>
			    			<span>신규 비밀번호 확인</span>
			    			<input class="new_password_check" type="password" placeholder="신규 비밀번호 확인" maxlength="20" >
			    		</div>
		    		</div>
		    		
		    		<div class="btn_box">
			    		<button type="button" class="pwd_modify">변경</button>
		    		</div>
		    	</div>
		    	
		    
            	<h2>닉네임 변경</h2>
				
				<div>
		             <div class="input_box">
		            	<input type="text" class="nickname" name="nickname" required placeholder="변경하실 닉네임을 입력해 주세요">
					</div>
					
					<div class="btn_box">
						<button type="button" class="nickname_modify">수정</button>
					</div>
				</div>
				
				
				<h2>휴대폰 번호 변경</h2>
				<div>
					<div class="input_box">
		            	<div>
		            		<input type="number" class="phone" name="phone" required placeholder="전화번호를 입력해 주세요" onkeypress="return lenthCheck(this, 11);" >
		            	</div>
		            	
		            	<div class="auth_num_box">
		            		<input type="text" class="phone_auth_num" name="authNum" required placeholder="인증번호 입력">
		            		<span class="timer_box">
		            			<span>남은시간</span>
		            			<span class="timer"></span>
		            		</span>
		            	</div>
		            	
		           	</div>
		           	
		           	

		           	<div class="btn_box">
						<div>
							<button type="button" class="auth_num_send">인증번호 전송</button>
						</div>
						
						<div class="auth_num_box">
							<button type="button" class="phone_auth_btn">인증</button>
						</div>
		           	</div>
		           	
				</div>
			</div>
	    </main>
    </div>
    
    
   	<%@ include file="/WEB-INF/view/include/nav.jsp" %>
	<%@ include file="/WEB-INF/view/include/footer.jsp" %>
	
	<script type="text/javascript" src="/js/user/myInfo.js" ></script>

    
</body>
</html>

 

 

myInfo.css

@charset "utf-8";
.wrap {
	width: 90%;
	max-width: 1000px;
	margin: 0 auto;
	flex-wrap: wrap;
}

section.title {
	width: 100%;
}

section.title h1 {
	text-align: center;
	margin: 20px 0 20px 0;
}

/* main */
main {
	width: 80%;
	margin: 40px auto;
	border: 1px solid #ddd;
	border-radius: 15px;
	padding-bottom: 40px;
}

.info_box {
	margin: 0px auto;
	width: 90%;
	font-size: 1.5rem;
}

.info_box>div {
	display: flex;
	margin: 10px 0;
}

.info_box h2 {
	text-align: center;
	margin-top: 10px;
}

.info_box .input_box {
	width: 80%;
	text-align: right;
	margin-right: 5px;
}

.info_box .input_box > div,
.info_box .btn_box > div {
	margin-top: 10px;
}

.info_box .user_nickname {
	justify-content: center;
	font-size: 1.8rem;
}

.info_box input {
	padding: 10px;
	font-size: 1.5rem;
	width: 100%;
	border: 1px solid #ddd;
	box-sizing: border-box;
}

.info_box input[type=password] {
	width: 75%;
}

.info_box button {
	padding: 11px;
	font-size: 1.5rem;
	background: #2AC1BC;
	color: #fff;
	border: none;
	border-radius: 10px;
	width: 100%;
}

.info_box .btn_box {
	width: 18%;
	display: flex;
	flex-direction: column;
	justify-content: end;
}


.input_box .auth_num_box {
	position: relative;
}

.auth_num_box {
	display: none;	
}

.input_box .timer_box {
	position: absolute;
	left: 0;
	bottom: 0;
    transform: translate(10%, 120%);
}




@media ( max-width :1024px) {
	main {
		width: 90%;
	}
	.info_box input[type=password] {
		width: 70%;
	}
}

@media ( max-width : 767px) {
	.wrap {
		width: 100%;
	}
	main {
		width: 100%;
	}
	.info_box {
		width: 95%;
	}

	.info_box input[type=password] {
		width: 70%;
	}
}


@media ( max-width : 600px) {
	.info_box {
		width: 100%;
	    margin-left: 10px;
	}
	.info_box input[type=password] {
		width: 60%;
	}

	.info_box .btn_box {
		width: 20%;
	} 
	
	.info_box .input_box {
		width: 75%;
	}
	
} 

@media ( max-width : 480px) {

	.info_box .btn_box {
		width: 25%;
	} 
	
	.info_box .input_box {
		width: 70%;
	}
	
}

 

 

myInfo.js

$(document).ready(function(){
	

// 유저 정보 변경하기
function infoModify(data) {
	$.ajax({
		url: "/user/info",
		type: "PATCH",
		data: data
	})
	.then(function(result){
		swal(result);
		$("input").val("");
		
	})
	.fail(function(){
		alert("에러가 발생했습니다");
	})
} 

	


$(".pwd_modify").click(function() {
	const prevPassword = $(".present_password").val().replaceAll(" ", "");
	const newPassword = $(".new_password").val().replaceAll(" ", "");
	const newPasswordCheck = $(".new_password_check").val().replaceAll(" ", "");
	
	if(!prevPassword || !newPassword) {
		return;
	}
	
	if(newPassword != newPasswordCheck) {
		swal('변경하실 비밀번호를 확인해 주세요');
		return;
	}
	
	swal({
		text: '비밀번호를 변경하시겠습니까?',
		buttons: ['취소', '변경하기']
	})
	.then(function(value){
		if(value) {
			const data = {
				value: newPassword,
				valueType: "password",
				prevPassword : prevPassword
			};
		
			infoModify(data);
		}
	})
})



$(".nickname_modify").click(function() {
	const nickname = $(".nickname").val();

	if (!nickname) {
		return;
	}

	if (!nicknameCheck(nickname)) {
		swal('닉네임은 한글, 영어, 숫자만 4 ~10자리로 입력 가능합니다');
		return;
	}
	
	
	const data = {
		value: nickname,
		valueType: "nickname"
	};
				

	$.ajax({
		url: "/overlapCheck",
		type: "get",
		data: data,
	})
	.then(function(result){
		if (result != 0) {
			swal('이미 사용중인 닉네임입니다');
		} else {
			swal('닉네임을 ' + nickname + '으로 변경하시겠습니까?', {
				buttons: ['취소', '변경하기'],
			})
			.then(function(value){
				if (value) {
					infoModify(data);
					$(".user_nickname").text(nickname);
				}
			})
		}
	})
	.fail(function(){
		alert("에러가 발생했습니다");
	})
}); 






// 인증번호 전송
$(".auth_num_send").click(function(){
	const phone = $(".phone").val();
	if (!phoneCheck(phone)) {
		swal('휴대폰번호를 정확히 입력해주세요');
		return;
	}
	if(!timer.status().isStart) {
		swal(timer.status().restartTime +' 초 후에 전송가능합니다');
		return;
	}
	
	$.ajax({
		url: "/send/authNum",
		type: "POST",
		data: {phone : phone}
	})
	.then(function(result){
		swal(result);
		$(".auth_num_box").fadeIn(100);
		timer.start();
	})
	.fail(function(result){
		alert("에러")	;
	})
})


$(".phone_auth_btn").click(function(){
	 const authNum = $(".phone_auth_num").val().replaceAll(" ", "");
	 if(!authNum) {
		return;
	}
	
	const data = {
		authNum : authNum
	}	
	
	$.ajax({
		url: "/send/authNumCheck",
		type: "POST",
		data: data 
	})
	.then(function(){
		swal({
			text: "전화번호를 변경하시겠습니까?",
			buttons: ['취소', '변경']
		})
		.then(function(value){
			if(value) {
				const data = {
					value: $(".phone").val(),
					valueType: "phone"
				};
				infoModify(data);
				$(".auth_num_box").fadeOut(100);
			}
		})
	})
	.fail(function(result){
		swal(result.responseText);
	})
})





})

 

 

 

 

 

유저정보 수정을 위한 컨트롤러 UserInfoController를 따로 만들어서 진행하였습니다

 

UserInfoController에 정보 수정 메서드를 추가합니다

@Autowired
private BCryptPasswordEncoder encodePwd;
// 내 비밀번호, 닉네임 수정하기
@PatchMapping("/user/info")
public ResponseEntity<String> modifyInfo(String value, String valueType, String prevPassword, 
        @AuthenticationPrincipal LoginService user, HttpSession session) {
    // value = 변경할 값
    // valueType = password, nickname, phone
    String username = user.getUser().getUsername();
    String msg = "";
    
    switch (valueType) {
    case "password":
        if(!encodePwd.matches(prevPassword, user.getPassword())) {
            return new ResponseEntity<String>("현재 비밀번호가 일치하지 않습니다", HttpStatus.OK);
        } 
        value = encodePwd.encode(value);
        msg = "비밀번호가 변경되었습니다";
        break;
        
    case "nickname":
        msg = "닉네임이 변경되었습니다";
        break;
    case "phone":
        msg = "전화번호가 변경되었습니다";
        session.setMaxInactiveInterval(0);
        session.setAttribute("authNum", null);
        break;
    }
    
    userService.modifyInfo(username, valueType, value);
    UserInfoSessionUpdate.sessionUpdate(value, valueType, user);
    
    return new ResponseEntity<String>(msg, HttpStatus.OK);
}

한개의 컨트롤러에 넘어온 valueType에 따라 정보를 변경할 수 있게했습니다

정보를 변경 후 세션도 업데이트 해줍니다

 

Service와 DAO에 추가합니다

 

UserServiceImp

@Override
public void modifyInfo(String username, String valueType, String value) {
    Map<String, Object> map = new HashMap<>();
    map.put("username", username);
    map.put("valueType", valueType);
    map.put("value", value);
    userDAO.modifyInfo(map);
}

 

UserDAO

@Override
public void modifyInfo(Map<String, Object> map) {
    sql.update("user.modifyInfo", map);
}

 

UserMapper

<update id="modifyInfo">
    UPDATE BM_USER SET
    
    <if test="valueType == 'password'">
        PASSWORD = #{value }
    </if>
    
    <if test="valueType == 'nickname'">
        NICKNAME = #{value }		
    </if>
    
    <if test="valueType == 'phone'">
        PHONE = #{value }		
    </if>
    
    WHERE	USERNAME = #{username } 
</update>

 

 

휴대폰번호 변경은 인증번호를 휴대폰으로 보내야 하지만 문자보내기api가 유료라서 인증번호를 콘솔에서 확인하고 휴대폰번호를 변경 할 수 있게 했습니다

 

util.js에 타이머 함수를 추가합니다


const timer = (function(){
	let time;	// 타이머 시간
	let timerStart;	// setInterval 이름
	let timerArr = [];	// 실행중인 타이머
	let isStart = true;	// 재시작 가능여부 
	let restartTime;	// 재시작 가능한 시간
	
	let minute;
	let second;
	
	const start = function(){
		if(!isStart) {
			return false;
		}
		
		// 타이머 초기화하기
		const reset = function(){
			time = 300;
			restartTime = 30;
			
			minute = Math.floor(time / 60);
			second = time % 60;
		}
		
		// 배열이 사이즈가 0이면 처음 실행
		if(timerArr.length == 0) {
			reset();
		}
		
		// 배열이 사이즈가 1이면 재실행 
		if(timerArr.length == 1) {
			// 재실행시 실행중인 타이머를 종료하고 다시 실행
			clearInterval(timerArr.pop());
			reset();
		}
		
		isStart = false;
			
		$(".timer").text(minute + ' : ' + String(second).padStart(2,'0'));
		
		timerStart = setInterval(function(){
			if(0 < restartTime) {
				restartTime--;
			}
			
			if(restartTime == 0) {
				isStart = true;
			}
			
			if(second == 0) {
				minute--;
				if(minute == -1) {
					for(i=0;i<timerArr.length;i++) {
						clearInterval(timerArr[i]);
					}
					timerArr = [];
					return;
				}
				second = 60;
			}
			second--; 
			$(".timer").text(minute + ' : ' + String(second).padStart(2,'0'));
			
		}, 1000)
		timerArr.push(timerStart);
	}
	
	const status = function(){
		return {
			isStart : isStart,
			restartTime : restartTime,
		}
	}
		
	return {
		start : start,
		status : status,
	}
})();

 

 

UserInfoController에 인증번호 보내기 메서드를 추가합니다

// 인증번호 보내기
@PostMapping("/send/authNum")
private ResponseEntity<String> authNum(String phone, String email, HttpSession session){
    String authNum = "";
    for(int i=0;i<6;i++) {
        authNum += (int)(Math.random() * 10);
    }
    
    System.out.println("인증번호 : " + authNum);
    
    // 전화번호로 인증번호 보내기 추가
    if(phone != null) {
        System.out.println("전화번호로 인증번호 보내기");

        
        
    // 이메일로 인증번호 보내기
    } else if(email != null) {
        System.out.println("이메일로 인증번호 보내기");
        
    }
    
    
    Map<String, Object> authNumMap = new HashMap<>();
    long createTime = System.currentTimeMillis(); // 인증번호 생성시간
    long endTime = createTime + (300 *1000);	// 인증번호 만료시간
    
    authNumMap.put("createTime", createTime);
    authNumMap.put("endTime", endTime);
    authNumMap.put("authNum", authNum);
    
    session.setMaxInactiveInterval(300);
    session.setAttribute("authNum", authNumMap);
    
    return new ResponseEntity<String>("인증번호가 전송되었습니다", HttpStatus.OK);
}

인증번호 6자리를 생성하고 생성시간, 만료시간을 map에 담아 세션에 저장합니다

 

 

 

전화번호 입력 후 인증번호 전송 버튼을 클릭하면 인증번호 입력박스와 타이머가 나타납니다

 

 

컨트롤러에 인증번호 확인 메서드를 추가합니다

// 인증번호가 맞는지 확인
@PostMapping("/send/authNumCheck")
private ResponseEntity<String> authNumCheck(String authNum, HttpSession session){
    Map<String, Object> sessionAuthNumMap = (Map<String, Object>) session.getAttribute("authNum");
    
    String msg = "";
    
    if(sessionAuthNumMap == null) {
        msg = "인증번호를 전송해주세요";
        return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
    }
    
    // 인증번호 만료시간
    long endTime = (long) sessionAuthNumMap.get("endTime");
    
    // 현재시간이 만료시간이 지났다면
    if(System.currentTimeMillis() > endTime) {
        msg = "인증시간이 만료되었습니다";
        session.setAttribute(authNum, null);
        session.setMaxInactiveInterval(0);
        return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
    }
    
    // 인증번호
    String sessionAuthNum = (String) sessionAuthNumMap.get("authNum");
    
    if(!authNum.equals(sessionAuthNum)) {
        msg = "인증번호가 일치하지 않습니다";
        return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
    } else {
        // 인증번호가 일치하면
        return new ResponseEntity<String>(HttpStatus.OK);
    }
}

세션에 저장된 인증번호Map을 꺼내 확인후 알맞는 메세지를 출력합니다

 

 

알맞는 인증번호 입력시 전화번호를 변경 할 수 있습니다

 

변경 후 세션에 전화번호도 업데이트 할 수 있게 if문을 하나 더 추가합니다

if문 아래에 세션에 저장하는 부분은 필요없기 때문에 주석처리 하였습니다

public class UserInfoSessionUpdate {

	public static void sessionUpdate(String value, String valueType, LoginService user) {
		ServletRequestAttributes attr = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
//		HttpSession session = attr.getRequest().getSession();
		
		LoginService loginService = (LoginService) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		
		if(valueType.equals("nickname")) {
			loginService.getUser().setNickname(value);
		} 
		else  if(valueType.equals("password")) {
			loginService.getUser().setPassword(value);
		}
		else if(valueType.equals("point")) {
			int point = loginService.getUser().getPoint() + Integer.parseInt(value);
			loginService.getUser().setPoint(point);
		}
		else if(valueType.equals("phone")) {
			loginService.getUser().setPhone(value);
		}

//		SecurityContext sc = SecurityContextHolder.getContext();
//
//		sc.setAuthentication(new UsernamePasswordAuthenticationToken(loginService, null, user.getAuthorities()));
//		
//		session.setAttribute("SPRING_SECURITY_CONTEXT", sc);
	}
}