회원정보 수정 구현 -1-

jihan kong·2023년 8월 22일
0
post-thumbnail

지난번에 회원들마다 회원 정보를 조회할 수 있는 마이페이지를 구현한 적이 있었다. 그러나, 마이페이지는 보통 회원정보 조회뿐만 아니라 회원정보를 수정할 수 있는 기능 또한 갖추고 있는 것이 보통이다. 이번에는 마이페이지를 통해 회원 정보를 수정하였을 때, 바뀐 정보를 DB update하는 로직까지 구현해볼 것이다.

회원정보 수정 (update)


1. 회원 비밀번호 입력 후, 변경 페이지 접속

회원정보를 변경하기 위해 먼저 구현하고 싶었던 것은 회원정보 변경 페이지에는 올바른 회원 비밀번호를 입력해야만 접근할 수 있도록 하는 것이다.
실제로 많은 웹 사이트에서 회원정보를 변경하기 위해 패스워드를 한번 더 받는 절차를 진행하고 있다. 이렇게 하는데에는 여러가지 이유가 있겠지만 보안 이슈가 가장 클 것이다.

구현을 위해서 View를 먼저 만들어야 감이 잡히는 법. 마이페이지에서 회원정보 변경 버튼을 눌렀을 때, 비밀번호를 입력하는 창을 띄워보도록 했다.

위의 회원정보 변경 버튼을 누르게 되면...

비밀번호를 체크하는 폼을 볼 수 있다.

2. MemberController

View를 생성했으니 이제 통로를 만들 차례이다. 회원 관련이기 때문에
MemberController에 checkPwdView() 라는 메서드를 만들어보았다.

MemberController.java

// import 생략

@RequestMapping("/members")
@Controller
@RequiredArgsConstructor
public class MemberController {


	/* 회원 수정하기 전 비밀번호 확인 */
    @GetMapping("/checkPwdForm")
    public String checkPwdView() {
        return "member/passwordCheckForm";
    }

passwordCheckForm.
여기서 Ajax 통신을 통해 비밀번호를 체크할 수 있게 해야 했는데, 이는 다음과 같이 구현했다.

passwordCheckForm.html

<script type="text/javascript">
    $(document).on('click', '#checkPwd', function() {
        const checkPassword = $('#password').val();
        if(!checkPassword || checkPassword.trim() === ""){
            alert("비밀번호를 입력하세요.");
        } else{
            $.ajax({
                type: 'GET',
                url: '/members/checkPwd',
                data: {'checkPassword': checkPassword},
                datatype: "text"
            }).done(function(result){
                console.log(result);
                if(result){
                    console.log("비밀번호 일치");
                    window.location.href="/members/updateForm";
                } else if(!result){
                    console.log("비밀번호 틀림");
                    // 비밀번호가 일치하지 않으면
                    alert("비밀번호가 맞지 않습니다.");
                    window.location.reload();
                }
            }).fail(function(error){
                alert(JSON.stringify(error));
            })
        }
    });
    </script>
  • 패스워드 입력 칸에 비밀번호를 입력했는지를 먼저 체크한다.
  • ajax에서 GET 방식으로 지정한 URL에서 사용자가 입력한 패스워드를 data로 받아온다.
  • ajax가 통신 후, result로 받는 것은 boolean 인데, 만약 true 이면 비밀번호가 일치하므로 회원 수정 페이지로 이동시킨다.
  • 결과가 false 이면 비밀번호가 틀렸다는 메시지를 전달하고 페이지를 reload 한다.

위의 checkPwdView() 메서드와 Ajax를 통해 정상적으로 작동한다면 회원 비밀번호를 입력하는 폼에 접속하고 입력값을 얻어올 수 있게 된 것이다. 이제 실제로 비밀번호를 비교하고 비밀번호가 맞는지 틀린지를 비교하는 로직을 구현해보자. 마찬가지로 MemberController 에 메서드를 생성한다.

MemberController.java

	@GetMapping("/checkPwd")
    public boolean checkPassword(Principal principal, Member member,
                                 @RequestParam String checkPassword,
                                 Model model){

        String loginId = principal.getName();
        Member memberId = memberRepository.findByEmail(loginId);
        // memberRepository에서 이메일을 찾는다.
        return memberService.checkPassword(memberId, checkPassword);
    }

메서드 이름은 무난하게 checkPassword라고 했고, 파라미터로는 지금까지 회원 관련 로그인 정보를 알고자 할 때 유용하게 사용했던 Principal 객체와 실제 member의 Id를 비교할 것이기 때문에 member Entity를 받는다.

그리고 Form에서 Controller로 checkPassword 라는 변수에 패스워드 값을 받아 데이터를 요청해줄 것이기 때문에 HttpServletRequest 객체와 같은 역할을 하는 @RequestParam 어노테이션의 기능이 필요하다. checkPassword 앞에 붙여준다.

메소드의 return 값으로 memberService를 호출해 파라미터를 전달하여 실제 DB와 상호작용하게끔 한다면 Controller에서 할 일은 끝났다. 이제 Service 단을 구현해보자.


3. MemberService

MemberService.java

// ..import 생략

@Service
@Transactional                                              
@RequiredArgsConstructor                                    
public class MemberService implements UserDetailsService {

	/** 비밀번호 일치 확인 **/
    @ResponseBody
    public boolean checkPassword(Member member, String checkPassword) {
        Member findMember = memberRepository.findByEmail(member.getEmail());
        if(findMember == null) {
            throw new IllegalStateException("없는 회원입니다.");
        }
        String realPassword = member.getPassword();
        boolean matches = passwordEncoder.matches(checkPassword, realPassword);
        System.out.println(matches);
        return matches;
    }
  • memberRepository 에서 실제 회원을 찾고 없다면 예외처리
  • Form으로부터 받아온 checkPassword 와 실제 회원 비밀번호 realPassword 를 암호화하고 이 둘을 PasswordEncoder 클래스에서 제공하는 matches() 함수를 통해 비교한다.
  • Ajax에서 boolean 값을 통해 로직을 처리하기 때문에 boolean 타입의 matches 를 return 값으로 설정해준다.

Done!

이제 실행해보자.

4. ⚠️ 오류 발생과 해결

오늘도 평화로운 코딩나라(?)...
다음과 같은 에러메시지에 직면하고 말았다.

Unknown return value type...?
"리턴값이 확인할 수 없는 값" 이라는 것에서 서버에서 사용자가 요청한 값 자체를 인식하지 못하는게 아닐까 추측했다.

그리고..

Yeji's Tech 님의 블로그를 통해 해결책을 찾게 되었다!

서버에서 클라이언트로 response를 전송하기 위해서
@ResponseBody 를 사용해 HTTP요청 본문에 담긴 값들을 자바 객체로 변환시켜 객체에 저장합니다.

결론부터 말하면 ResponseBody 어노테이션이 없었고, 이 때문에 오류가 발생한 것이었다.

다른 해결 방안으로는 @Controller -> @RestController 로 변경해주면된다. 나의 경우는 Controller를 처음부터 Restful 하게 설계하진 않았으니 Controller에 @ResponseBody 를 붙이는 것으로 해결했다.


MemberController.java

	@GetMapping("/checkPwd")
    @ResponseBody
    public boolean checkPassword(Principal principal, Member member,
                                 @RequestParam String checkPassword,
                                 Model model){

        String loginId = principal.getName();

        Member memberId = memberRepository.findByEmail(loginId);
        System.out.println(memberId.getPassword());
        return memberService.checkPassword(memberId, checkPassword);
    }

5. 동작화면 🕹️

회원정보 수정을 위해 회원 정보 변경 버튼을 누르면 다음과 같이
비밀번호 입력 창이 뜬다.


비밀번호 확인 창에서 잘못된 비밀번호를 입력하면 다음과 같이 경고 메세지가 뜨면서 리다이렉트.

만약, 올바른 비밀번호를 입력하게 되면 다음과 같이 입력 성공시 이동하는 URL(members/updateForm) 으로 이동하게 된다.


이렇게해서 회원정보 변경을 위한 비밀번호 확인 기능은 만들었다. 이제 본격적으로 회원정보를 변경하는 기능을 구현해보자. (다음 포스팅에서 계속...)

기능 구현을 위해 참고한 블로그 :)
https://velog.io/@jyleedev/%ED%9A%8C%EC%9B%90-%EC%A0%95%EB%B3%B4-%EC%88%98%EC%A0%95

profile
학습하며 도전하는 것을 즐기는 개발자

0개의 댓글