상세 컨텐츠

본문 제목

[JPA] 사용자 비밀번호 확인과 정보 수정

JAVA/SPRING

by ranlan 2021. 4. 29. 03:29

본문

728x90

사용자 본인의 정보 수정을 하기 전 비밀번호를 입력하여 본인확인 절차를 거치도록 하였다.

 

 

[이전] 2021.04.20 - [project/spring] - [JPA] 회원가입과 유효성 체크

 

[JPA] 회원가입과 유효성 체크

Spring Boot JPA 회원가입 [도메인 설계] 회원 정보의 내용으로 회원 가입을 위한 필수 입력 항목들은 아래와 같다. - 이름 - 아이디 - 비밀번호 - 이메일주소 - 전화번호 위 항목들을 포함하여 회원가

juran-devblog.tistory.com


 

 

 

비밀번호 재확인

사용자가 본인확인을 위해 입력한 비밀번호와 저장된 비밀번호를 비교하기 위해 passwordEncoder 인터페이스 의 메서드를 이용하였다.

 

MemberService

private final PasswordEncoder passwordEncoder;

@Transactional
public boolean checkMemberPassword(String inputPassword, String memberId) {
    return passwordEncoder.matches(inputPassword, loadUserByUsername(memberId).getMemberPw());
}

@Override
public Member loadUserByUsername(String username) throws UsernameNotFoundException {
    return memberRepository.findByMemberId(username)
            .orElseThrow(()->new UsernameNotFoundException("Not Found by " + username));
}

loadUserByUsername() 은 해당 서비스가 상속받은 UserDetailsService 의 메서드를 오버라이딩한 것으로, 서비스나 컨트롤러 내에서 username으로 Member객체를 조회해야할 때 (DTO가 따로 필요없이 서비스 로직을 위해 이용하거나 DB에 직접적으로 영향을 미칠 때) 주로 사용하였다.

 

inputPassword는 입력받은 비밀번호이고

memberId는 현재 로그인한 사용자의 아이디이며 해당 아이디로 Member객체를 찾아 저장된 비밀번호를 조회한다.

 

이 둘을 비교하여 일치하면 true를 일치하지 않으면 false를 반환한다.

 

 

MemberApiRespository

@PostMapping("/password/check")
public ResponseEntity<?> checkPassword(Authentication authentication, @RequestBody Map<String, Object> params) {

    if(memberService.checkMemberPassword(params.get("password").toString(), authentication.getPrincipal().toString()) == false){
        throw new BadRequestException("잘못된 비밀번호 입니다.");
    } else {
        return ResponseEntity.ok(true);
    }
}

Authentication 객체를 이용해 현재 로그인한 사용자의 정보를 받아온다.

RequestBody에는 입력한 비밀번호값만 받으면 됨으로 따로 DTO를 만들지 않고 Map으로 받았다.

 

>> 비밀번호 불일치인 경우

checkMemberPassword()의 반환값이 false일때(비밀번호가 일치하지 않을 때) 사용자에게 보여줄 메시지를 담아 BadReqeustException을 반환한다.

 

BadRequestException

@ResponseStatus(value = HttpStatus.CONFLICT)
public class BadRequestException extends RuntimeException {

    public BadRequestException(String message) {
        super(message);
    }
}

 

>> 비밀번호 일치인 경우

반환값이 true일 때(비밀번호가 일치할 때) true를 반환한다.

 

원래는 ApiResponse를 만들어 성공여부, 메시지, 에러코드, 반환할 데이터 등을 담아 반환하였으나 성공했을 때 딱히 넘겨줄 데이터가 없어 ResponseEntity에 바로 true를 담아 반환하였다.

 

 

+ 쓰다보니 굳이 ApiResponse를 써야하나 의문이 든다. 대부분 성공했을 때 데이터를 담아 ApiResponse를 반환하기 때문에 에러코드나 에러 메시지를 담을 때가 거의 없다. 에러 발생 시 ApiResponse에 에러코드를 넘겨 반환하기보다는 때에 따라 Exception객체를 만들어 반환하고 있다. 또한 성공 시 넘겨줄 데이터가 간단하거나 없을 경우에는 위와 같은 식으로 ResponseEntity Body에 바로 담아 넘겨준다. 각 경우의 쓰임새와 방법에 대해 고민을 해봐야할 것 같다 🤔

 

 

사용자의 기존 정보 조회

먼저 비밀번호로 사용자 인증에 성공하면 기존의 저장된 사용자정보를 보여주었다.

 

MemberService

@Transactional
public MemberInfoDto findMemberInfoByName(String memberName) {
    Member member = loadUserByUsername(memberName);
    return new MemberInfoDto(member);
}

직접적으로 화면에 노출되기 때문에 MemberInfoDto로 변환하여 반환하였다.

*API 개발 시 엔티티를 바로 반환하는 것은 매우 좋지 않다

 

MemberInfoDto

@Getter
@Setter
@NoArgsConstructor
public class MemberInfoDto {

    private long memberNo;
    private String memberId;
    private String memberName;
    private String memberPw;
    private  String memberTell;
    private String memberEmail;

    @Builder
    public MemberInfoDto(Member member) {
        this.memberNo = member.getMemberNo();
        this.memberName = member.getMemberNm();
        this.memberId = member.getMemberId();
        this.memberPw = member.getMemberPw();
        this.memberTell = member.getMemberTell();
        this.memberEmail = member.getMemberEmail();
    }
}

Builder를 이용하여 Member에서 MemberInfoDto로 바꿈으로써 코드 반복을 줄이고 재사용성을 높혔다.

(생각보다 Member를 MemberInfoDto로 바꿔 화면에 뿌려줘야할 때가 많았듬)

 

MemberApiController

@GetMapping("/info/me")
public ResponseEntity<?> memberDetailInfo(Authentication authentication) {
    ApiResponse apiResponse = new ApiResponse(true, "로그인한 사용자 상세정보 조회");
    apiResponse.putData("memberInfo", memberService.findMemberInfoByName(authentication.getPrincipal().toString()));

    return ResponseEntity.ok(apiResponse);
}

마찬가지로 Authentication 객체로 현재 로그인한 사용자의 정보를 받는다.

 

사용자가 비밀번호를 입력하고 본인인증이 성공하면 위의 api를 호출해 기존에 DB에 저장되어 있던 사용자의 정보를 보여준다.

 

 

사용자 정보 수정

MemberUpdateRequest

@Getter
@NoArgsConstructor
public class MemberUpdateRequest {

    @NotBlank
    private long memberNo;

    @NotBlank
    private String memberId;

    @Email
    private String memberEmail;

    @NotBlank
    private String memberTell;
}

사용자가 변경할 수 있는 필드와 사용자를 식별할 수 있는 필드들이 포함되어 있다.

 

MemberRepository

 @Transactional
 public void editMemberInfo(MemberUpdateRequest memberUpdateRequest) {
    Member member = memberRepository.getOne(memberUpdateRequest.getMemberNo());
    member.updateMemberInfo(memberUpdateRequest.getMemberEmail(), memberUpdateRequest.getMemberTell());
 }

MemberUpdateRequest에 MemberNo정보가 포함되어 있어 JPA가 제공하는 getOne()메서드로 Member 객체를 조회한다.

 

 * 데이터가 어디서 변경되었는지 찾기 힘들기 때문에 setter를 남발하는 것은 좋지 않다.

    게시판 CRUD때와 마찬가지로 setter 대신 도메인에 서비스로직을 구현하였다.

 

Member

public void updateMemberInfo(String memberEmail, String memberTell) {
    this.memberEmail = memberEmail;
    this.memberTell = memberTell;
}

엔티티에 작성한 update 로직 (이메일과 전화번호만 바꿀 수 있다고 가정)

 

MemberApiController

@PutMapping("/edit")
public ResponseEntity<?> editMemberInfo(@RequestBody MemberUpdateRequest memberUpdateRequest) {
    ApiResponse apiResponse = new ApiResponse(true, "사용자 정보 수정");
    memberService.editMemberInfo(memberUpdateRequest);

    return ResponseEntity.ok(apiResponse);
}

정보 수정임으로 PutMapping으로 매핑하였다.

 

 

나머지 유효성 검사는 jQuery 처리가 전부..

 

사용자 정보 수정 끝 ~

 

728x90

관련글 더보기

댓글 영역