4/21/2018

(구버전) 스프링 게시판 만들기 #15. 회원 탈퇴 기능 구현

'(구버전) 스프링 게시판 만들기'는 내용이 부족하다고 판단하여
스프링 게시판 만들기를 새로 작성하였습니다.

링크 및 참조용으로 현재 게시물은 남겨두겠지만,
가급적이면 새로운 스프링 게시판 만들기를 참조해주시기 바랍니다.

깃허브 링크

회원가입, 로그인, 회원정보 수정을 구현했으니 마지막인 회원 탈퇴입니다.

회원기능과 게시판을 비교하자면, 회원가입-게시물 작성, 로그인-게시물 조회, 회원정보 수정-게시물 수정, 회원 탈퇴-게시물 삭제와 같습니다.

스프링에서 작업하기 전, 임시 데이터를 입력한 뒤

정상적으로 삭제되는 쿼리를 작성하여 확인합니다.

이런식으로 테이블의 값이 변경되었다면 꼭 commit; 을 해둡니다.

정상적으로 작동되는 쿼리를 매퍼에 추가합니다.

<!-- 회원 탈퇴 -->
<delete id="withdrawal">
 delete from myMember
     where userId = #{userId}
         and userPass = #{userPass}
</delete>

회원 탈퇴시 비밀번호를 입력하여 사용자를 인증하게되는데, 이때 기존 비밀번호가 필요하므로 로그인시 작동되는 쿼리문에 userPass를 추가해둡니다.

DAO와 Service를 작성합니다.

회원 탈퇴 페이지를 위한 메서드를 작성합니다.

// 회원 탈퇴 get
@RequestMapping(value = "/withdrawal", method = RequestMethod.GET)
public void getWithdrawal() throws Exception {
 logger.info("get withdrawal");
 
}

회원가입(register.jsp)를 복사하여 이름을 withdrawal.jsp로 변경한 후, 내용을 수정합니다.

회원정보 수정과 거의 같은데, 이번엔 닉네임(userName)가 아닌 아이디(userId)로 되어있습니다. 닉네임과 아이디중 무얼 써야하는지는 정해진게 없습니다. 특정 유저를 선택할 수 있는 모든 값으로 할 수 있으며, 저는 서로 다른 값으로 한 것 입니다.

이런 경우, 테이블의 기본키(primary key)로 되어있는 값으로하는것이 좋습니다. 기본키는 일반적으로 변경되지 않는 값이며, 그 테이블을 대표하는 값이기 때문입니다.

메인페이지에 회원탈퇴 페이지로 이동하기 위한 링크를 추가합니다.

<a href="member/withdrawal">회원탈퇴</a>

로그인하면 회원탈퇴 페이지로 이동은 할 수 있지만, 실제로 탈퇴는 되지 않습니다.

회원탈퇴 POST용 메서드를 작성합니다.

// 회원 탈퇴 post
@RequestMapping(value = "/withdrawal", method = RequestMethod.POST)
public String postWithdrawal(HttpSession session, MemberVO vo, RedirectAttributes rttr) throws Exception {
 logger.info("post withdrawal");
 
 MemberVO member = (MemberVO)session.getAttribute("member");
 
 String oldPass = member.getUserPass();
 String newPass = vo.getUserPass();
     
 if(!(oldPass.equals(newPass))) {
  rttr.addFlashAttribute("msg", false);
  return "redirect:/member/withdrawal";
 }
 
 service.withdrawal(vo);
 
 session.invalidate();
  
 return "redirect:/";
}

session.getAttribute("member"); 를 이용하여 로그인할 때 생성되는 세션에서 member값을 받아옵니다. 기본적으로 받아오는 데이터 타입은 오브젝트(Object) 타입이므로, 오브젝트 변수를 생성하여 받거나 형변환(캐스팅) 해주어야합니다. 이때, session.getAttribute("member"); 가 MemberVO의 형태라는것을 알고 있습니다.

그러므로 (MemberVO)session.getAttribute("member"); 형태로 형변환할 수 있으며, MemberVO형의 변수 member에 넣을 수 있습니다.

이렇게 받아온 member는 getter를 이용하여 내부에 접근할 수 있으며, 문자열(String) 변수 oldPass에 member에 있는 비밀번호(userPass)를 넣어줍니다. 다른 문자열(String) 변수 newPass에는 회원탈퇴 페이지에 입력했던 비밀번호를 넣습니다.

원래의 비밀번호인 oldPass와 확인용으로 입력한 비밀번호 newPass를 비교해야하는데, 문자열 변수를 비교할 땐 이퀄(=) 부호 2개를 사용하여 비교하면 의도와 다른 결과가 나옵니다.

더블 이퀄(==)로 비교할 땐 변수가 사용하는 주소를 비교하는것이며, 위의 코드처럼 .equals() 을 사용하면 변수가 가진 값 자체를 비교하게 됩니다.

즉, !(oldPass.equals(newPass)) 는 oldPass와 newPass의 값 자체를 비교하게 되며, 두 변수가 같다면 !(true), 다르면 !(false)라는 결과가 나옵니다. 이때 부정 부호(!)가 있어서 true는 false로 바뀌며 false는 true로 바뀌게 됩니다. 결과적으로 oldPass와 newPass가 같다면 조건문의 값이 false가 되어 넘어가고, 다르다면 true가 되어 조건문 내부의 코드가 작동합니다.

조건문 내부의 코드는 다시 회원탈퇴 페이지로 이동하게되며, 이 때 msg와 그 값인 false를 같이 넘겨주게됩니다.

<c:if test="${msg == false }">
 <p>
 입력한 비밀번호가 잘 못 되었습니다.
 </p>
</c:if>

이렇게 컨트롤러에서 받아온 msg를 이용하여, 비밀번호가 잘 못되었다는 메시지를 출력합니다.

확인을 해보기 위해, 로그인한 뒤 회원탈퇴 페이지에서 잘못된 비밀번호를 입력한 뒤 회원 탈퇴 버튼을 클릭해보면

이렇게 메시지가 출력됩니다.

이번엔 올바른 비밀번호를 입력하게되면

메인 페이지로 이동하게되며, 동시에 로그아웃까지 이루어집니다.

테이블을 확인해보면 회원탈퇴가 정상적으로 이루어진것을 확인할 수 있습니다.

여기까지 스프링 게시판 만들기에 대한 기본적인 기능의 구현이 완료되었습니다. 현재까지의 작업(1~15)은 깃허브 저장소에 저장되어있습니다. 깃허브에 저장된 코드를 사용할 경우 오라클 셋팅을 자신에게 맞는 설정으로 변경해야합니다.

게시물 수정
  1. MemberVO member= (MemberVO)session.getAttribute("member");

    String oldPass= member.getUserPass();

    oldPass 부분이 nullpointexception 이 뜨는데 어떻게 해봐야할까요
    session에 member이름으로 로그인된 vo 값 잘저장은했어용

    ++ if문에서 return "redirect:/member/delete // ==withdrwal
    빼면 if문은 통과하는지 삭제는됩니다 ;;ㅋ

    답글삭제
    답글
    1. 안녕하세요?

      oldPass가 Null이 되는 경우는 로그인이 되지 않았거나, 관련 변수 관련으로 잘못된 경우일 확률이 높습니다.
      로그인이 되었을 때, member 세션에 정상적으로 데이터가 들어있는지 확인해보셔야 할 것 같습니다.

      삭제
    2. 익명5/16/2019

      같은문제가있어서 문의드립니다

      세션에 다른값들은 불러오는데
      String aa = member.getUserId();
      String bb = member.getUserName(); 이렇게하고
      logger.info(aa+"IDIDIDID");
      logger.info(bb+"namename"); 해보면 값이 담겨서나옵니다

      근데 getUserPass();로받은건 값이안담겨오는데 어떻게해야할까요

      삭제
    3. 안녕하세요?

      위의 답변과 마찬가지로 로그인이 되지 않았거나, 관련 변수에 대한 문제입니다.
      다른 값은 불러와지는데 member.getUserPass만 불러올 수 없다면, 해당 값을 보내주는 get메서드에서 값을 제대로 적용하지 않았을 가능성이 높아보이네요.

      가장 먼저 매퍼에서 userPass를 가져오는지 확인해야하고, 다음은 DAO와 Service에서 값이 제대로 Controller로 전달되는지, 마지막으로 Controller에서 Model을 통해 제대로 값이 전달되는지 확인해봐야 할 것 같습니다.

      삭제
    4. 저도 이 문제 때문에 3일은 코드 뒤집어가면서 봤던 것 같습니다..

      일단 로그인을 할 때 생성되는 세션에서 userId, userName의 값은 넘어오지만 uwerPw의 값이 넘어오질 않았습니다.
      해결방법은 memberMapper에서 로그인 쿼리에서 SELECT문에 userPw를 추가해주시면 값이 넘어옵니다.

      이것 때문에 한참 고생했네요...

      블로그 주인님 덕분에 스프링 공부에 도움이 많이 되었습니다~!

      삭제
  2. 안녕하세요

    제가 게시판만들기를 만드는 도중

    ku님은 update insert delete 메소드에는 리턴타입을 void 를 쓰시고

    어떤분들은 int 타입을 쓰시더라구요 ,

    이런점은 개인의 취향차이일 뿐인가요??

    답글삭제
    답글
    1. 안녕하세요?

      update, insert, delete에 void 메서드를 사용하는 이유는, 단순히 리턴값이 없기 때문입니다.

      int 또는 다른 데이터형을 사용하는 경우, 프로시저(여러개의 쿼리가 실행되는 함수형 쿼리)등을 이용하여 update, insert, delete가 정상적으로 작업이 되었는지의 유무를 확인하기 위함으로 생각됩니다.
      물론 그렇게 사용되는 쿼리는 게시물에 있는 쿼리보다 더 복잡하겠지요?

      하지만 그런 프로시저가 없다면 아무의미없는 값을 리턴하고 있겠지요;

      일반적으로 update, insert, delete는 별다른 리턴값이 없이 진행해도 무관합니다. 단순히 에러의 유무만으로도 어느정도 구분할 수 있으니까요.

      하지만 어디까지나 제 생각이고, 개발자의 취향이나 애플리케이션의 용도에 따라서 달라질 수 있겠습니다.

      삭제
  3. 혹시 로그인이랑 로그아웃의 db쿼리문은 어떻게 짜나요?

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      로그인의 경우 select문을 통해 아이디(또는 패스워드)가 동일한 데이터를 가져와서 추가적인 로직(패스워드 매칭 확인 등)을 실행하면 됩니다

      로그아웃은 세션을 끊으면 되는거라, 마지막 로그아웃 기록을 남기는게 아니라면 별도의 쿼리문은 필요가 없습니다.

      삭제
  4. 익명1/14/2023

    nullpointer exception은 membermapper.xml에서 username이랑 userId만 select해서 생기는 문제네요. select * 하니까 해결됐습니다

    답글삭제
  5. 안녕하세요. 잘못 작성을 해서, JSP 페이지에서 userId가 아닌 userName을 입력하니 DB에서 삭제가 되지 않았네요.. 궁금한점이 있는데, 컨트롤러에서 Member memberVo 이런 방식으로 작성하면 폼형태에서 전송된 데이터를 기반으로 내부에서 알아서 userId 와 userPass를 구분해서 데이터를 처리하게 되는지 궁금해요.

    답글삭제