오늘은 회원의 정보를 수정할수 있는 회원정보수정 페이지를 구현해보도록 하겠습니다
일단 화면을 구현하기 위한 파일들을 추가해주도록 합시다
이번 포스팅하고 다음포스팅에 쓸 파일들까지 모두 포함되어 있습니다
이번에 구현할 화면은 myInfo.jsp이며 views/user/find폴더에 있는 jsp파일들은
모두 다음 포스팅에서 구현할 화면들입니다
//회원 정보 수정 페이지
@GetMapping("/user/myInfo")
public String myInfo() {
return "user/myInfo";
}
@Autowired
BCryptPasswordEncoder encodePwd;
@Autowired
AuthService authService;
@Autowired
FindService findService;
// 내 비밀번호, 닉네임 수정하기
@PatchMapping("/api/user/info")
public ResponseEntity<String> modifyInfo(String value, String valueType, String prevPassword,
@AuthenticationPrincipal CustomUserDetails principal, HttpSession session) {
// value = 변경할 값
// valueType = password, nickname, phone
String username = principal.getUsername();
String msg = "";
switch (valueType) {
case "password":
if(!encodePwd.matches(prevPassword, principal.getPassword())) {
return ResponseEntity.badRequest().body("비밀번호가 일치하지 않습니다");
}
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, principal, session);
return ResponseEntity.ok().body(msg);
}
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);
userMapper.modifyInfo(map);
}
// 유저정보 수정
public void modifyInfo(Map<String, Object> map);
<update id="modifyInfo">
UPDATE DL_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>
...
else if(valueType.equals("point")) {
int point = customUserDetails.getPoint() + Integer.parseInt(value);
customUserDetails.setPoint(point);
}
//추가 핸드폰번호 변경시 스프링시큐리티 세션에 핸드폰번호 업데이트
else if(valueType.equals("phone")) {
customUserDetails.setPhone(value);
}
//추가 끝
...
SecurityContext sc = SecurityContextHolder.getContext();
화면을 보면 비밀번호, 닉네임, 전화번호를 변경할수 있는데 각각의 버튼을 통해
한번에 한개의 요소만 변경이 가능합니다. JS에서 AJAX요청을 통해 변경하고자
하는 요소와 값을 받아 switch문을 통하여 처리합니다.
비밀번호를 변경하고자 할경우 BCryptPasswordEncoder의 matches메서드를 통해
현재비밀번호와 입력한 비밀번호가 일치하는지 확인한후 일치할경우 변경하고자 하는
비밀번호를 encode메서드를 통해 암호화시키고 DB에 업데이트 해줍니다.
그 이후 스프링시큐리티 세션의 정보를 업데이트 해줍니다
닉네임의 경우는 단순히 사용자로부터 입력받은 값을 DB에 업데이트 해주기만 하면 됩니다
휴대폰번호의 경우 조금 복잡합니다. 휴대폰번호를 변경하고자 할 경우 인증번호를
전송해야하는데 실제 휴대폰에 문자를 보내려면 유료Api를 사용해야하므로 여기서는 단순히
console창에 인증번호를 표시하도록 하겠습니다.
인증번호 전송시스템을 구현하기 위해서 타이머를 사용할겁니다
타이머를 사용하는 이유는 인증번호를 전송할경우 그 인증번호를 세션에 저장해야하고
사용자가 인증번호 입력시 세션에서 인증번호를 꺼내어 서로 비교해야하는데
시간을 정해두지 않으면 세션이 계속해서 쌓이고 보안적인 측면에서도 좋지 않기 때문입니다
타이머를 사용하기 위해 util.js에 코드를 추가해줬습니다 (파일에 이미 추가 되어 있습니다)
이제 인증번호를 생성하고 전송하기 위한 코드를 추가해줘야합니다
// 인증번호 보내기
@PostMapping("/api/user/sendAuthNum")
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("이메일로 인증번호 보내기");
//findService.sendAuthNum(email, authNum);
}
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);
}
일단 인증번호의 경우 핸드폰전송과 이메일전송 두가지로 나눌겁니다.
핸드폰전송의 경우 유료Api를 사용하지 않으므로 단순하게 console창에 뜨도록 할것이고
이메일전송의 경우 이메일로 인증번호를 보낼것입니다.
회원정보에서 핸드폰번호 변경시에는 핸드폰전송만 사용하며 이메일전송의 경우
다음포스팅에서 구현할 ID찾기 기능에서 사용됩니다. 따라서 이메일 인증번호 전송의 경우
현재 주석처리를 해놓았으니 다음 포스팅에서 주석을 해제해주시길 바랍니다
사용자가 인증번호 보내기 버튼을 클릭할경우 6자리의 랜덤한 숫자를 생성합니다
이 6자리숫자와 현재시간, 만료시간(현재시간+5분)을 Map에 넣어 세션에 저장하도록 합니다
이제 console창에 떠 있는 인증번호를 입력하고 인증버튼을 누르면 우리가 위에서 세션에
저장한 번호와 일치하는지 확인해야 합니다 그것을 수행할 코드를 추가해주도록 합시다
// 인증번호가 맞는지 확인
@PostMapping("/api/user/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);
}
}
세션에 "authNum"라는 이름으로 저장되어 있는 인증번호를 꺼내어 사용자가 입력한
인증번호와 일치하는지를 확인합니다. 세션이 비여있을경우 인증번호가 제대로
전송이 되지 않은것이므로 통신실패를 알리고 만약 인증시간인 5분을 초과하였을경우는
세션에 저장한 인증번호를 지워줍니다
인증번호가 일치할경우 modifyInfo메서드를 실행하고 DB에 폰번호를 업데이트합니다
session에 저장된 인증번호를 제거하고 시큐리티세션에 접근해 사용자의 폰번호를 변경합니다
순서를 정리하자면 패스워드, 닉네임 변경의 경우
사용자 입력 -> AJAX요청 -> modifyInfo(회원정보수정) -> DB업데이트
핸드폰 번호 변경의 경우
사용자 입력 -> 타이머 실행 -> AJAX요청 -> authNum(인증번호전송) -> AJAX요청
-> authNumCheck(인증번호확인) -> AJAX요청 -> modifyInfo(회원정보수정) -> DB업데이트