2024. 5. 12. 00:07ㆍProject/Naver Cafe
이번에는 네이버 회원의 일부 정보를 수정하는 기능을 구현하고자 한다. 현재 네이버 회원이 가지는 정보들 중 수정이 가능한 정보는 계정 비밀번호, 이메일, 휴대전화번호, 별명이다. 특히 계정 비밀번호의 경우 다른 정보들과 다르게 변경시, 로그아웃이 되도록 구현해야 하므로 계정 비밀번호 수정 기능과 {이메일, 휴대전화번호, 별명} 수정 기능 2가지를 구현하였다.
Member
package CloneCoding.NaverCafe.domain.member;
@Entity
@Table(name = "MEMBER")
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Member {
public void updateInfo(RequestUpdateMember request) {
if (request.getEmail() != null) {
this.email = request.getEmail();
}
if (request.getPhoneNumber() != null) {
this.phoneNumber = request.getPhoneNumber();
}
if (request.getNickname() != null) {
this.nickname = request.getNickname();
}
}
public void updateAccountPassword(String changeAccountPassword) {
this.accountPassword = changeAccountPassword;
}
}
Member 객체의 정보를 수정할 수 있도록 updateInfo(), updateAccountPassword() 를 추가하였다.
- updateInfo(RequestUpdateMember) : Member 객체의 정보를 요청 변경 정보(null 값이 아닌 정보)로 수정한다.
- updateAccountPassword(String changeAccountPassword) : Member 객체의 accountPassword 를 변경 정보(changeAccountPassword)로 수정한다.
RequestUpdateMember, RequestUpdateAccountPassword
// RequestUpdateMember
package CloneCoding.NaverCafe.domain.member.dto;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RequestUpdateMember {
@Pattern(regexp = "^[a-zA-Z0-9_]+@[a-zA-Z]+\\.[a-zA-Z]+(\\.[a-zA-Z]+)?",
message = "올바른 이메일 형식이 아닙니다.")
private String email;
@Size(min = 11, max = 11, message = "휴대전화번호는 11자 입니다.")
@Pattern(regexp = "^[0-9]+", message = "휴대전화번호는 숫자만 입력 가능합니다.")
private String phoneNumber;
@Size(min = 2, max = 20, message = "별명은 2 ~ 20자로 제한됩니다.")
@Pattern(regexp = "^[a-zA-Z0-9]+", message = "별명은 영소문자, 영대문자, 숫자만 입력 가능합니다.")
private String nickname;
}
// RequestUpdateAccountPassword
package CloneCoding.NaverCafe.domain.member.dto;
@Getter
public class RequestUpdateAccountPassword {
@NotNull
private String nowAccountPassword;
@NotNull
@Size(min = 8, max = 16,
message = "계정 비밀번호는 8 ~ 16자로 제한됩니다.")
@Pattern(regexp = "^[a-zA-Z0-9~!@#$%]+",
message = "계정 비밀번호는 영소문자, 영대문자, 숫자, 특수문자(~, !, @, #, $, %)만 사용 가능합니다.")
private String changeAccountPassword;
@NotNull
private String checkChangeAccountPassword;
}
회원 정보 수정 요청 정보를 전달하는 DTO 클래스들 이다. 요청 정보에 따라 검증이 필요하도록 작성하였다.
- RequestUpdateMember : 이메일, 휴대전화번호, 별명에 대한 수정 정보를 전달한다. 모든 정보를 수정할 수도 있고 세 가지 중 하나를 수정할 수 있으므로 Null 값을 허용하였다.
- RequestUpdateAccountPassword : 계정 비밀번호에 대한 수정 정보를 전달한다. 비밀번호 수정시 현재 비밀번호, 바꿀 비밀번호가 필요하며, 바꿀 비밀번호는 한 번더 입력해 제대로 입력했는지 확인을 하도록 하였다. 모든 정보는 반드시 입력되야 하므로 Null 값을 허용하지 않았고 nowAccountPassword와 checkChangeAccountPassword는 검증된 정보와 비교만하면 되므로 추가적으로 제약조건을 지정하지 않았다.
MemberController
package CloneCoding.NaverCafe.domain.member.controller;
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/member")
public class MemberController {
private final MemberService memberService;
@PutMapping("/update/{token}")
public ResponseMemberInfo updateMemberInfo(@RequestBody @Valid RequestUpdateMember request,
@PathVariable("token") String token) {
log.info("회원 정보 수정 요청");
return memberService.updateMemberInfo(request, token);
}
@PutMapping("/update/accountPassword/{token}")
public String updateAccountPassword(@RequestBody @Valid RequestUpdateAccountPassword request,
@PathVariable("token") String token) {
log.info("회원 계정 비밀번호 수정 요청");
return memberService.updateAccountPassword(request, token);
}
}
컨트롤러에는 회원 정보 수정과, 계정 비밀번호 변경에 대한 메서드들을 추가 하였다. 각 메서드들은 요청 정보에 대한 검증을 수행한 후 MemberService의 메서드를 호출한다.
MemberServiceImpl
package CloneCoding.NaverCafe.domain.member.service;
import static CloneCoding.NaverCafe.message.SystemMessage.*;
import static CloneCoding.NaverCafe.security.LoginStatus.STATUS_LOGIN;
import static CloneCoding.NaverCafe.security.LoginStatus.STATUS_LOGOUT;
@Service
@Transactional
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
private final AesUtil aesUtil;
@Override
public ResponseMemberInfo updateMemberInfo(RequestUpdateMember request, String token) {
String accountId = aesUtil.aesDecode(token);
Member findMember = memberRepository.findByAccountId(accountId);
if (memberRepository.checkLogin(findMember))
throw new RuntimeException(PLEASE_CHECK_LOGIN.getMessage());
Member updateMember = memberRepository.updateMemberInfo(findMember, request);
memberRepository.save(updateMember);
return memberRepository.makeResponseMemberInfo(updateMember);
}
@Override
public String updateAccountPassword(RequestUpdateAccountPassword request, String token) {
String accountId = aesUtil.aesDecode(token);
Member findMember = memberRepository.findByAccountId(accountId);
if (memberRepository.checkLogin(findMember))
throw new RuntimeException(PLEASE_CHECK_LOGIN.getMessage());
if (memberRepository.checkAccountPassword(findMember, request)) {
Member updateMember = memberRepository.updateAccountPassword(findMember, request.getChangeAccountPassword());
updateMember.setLoginStatus(STATUS_LOGOUT.getStatus());
memberRepository.save(updateMember);
return CHANGE_ACCOUNT_PASSWORD_COMPLETE.getMessage();
}
return CHANGE_ACCOUNT_PASSWORD_FAILED.getMessage();
}
}
MemberService 인터페이스의 updateMemberInfo(), updateAccountPassword() 추상 메서드들을 구현하였다. 각 메서드는 우선 토큰 정보를 복호화해 사용자 계정 ID를 식별하고 해당 정보로 회원 정보를 조회, 로그인한 회원인지 확인한다. 이후 아래의 기능을 수행한다.
- updateMemberInfo() : 조회한 회원 정보와 요청 정보를 통해 수정된 회원 정보를 DB에 반영한 후 수정된 회원 정보를 응답 DTO에 담아 반환한다.
- updateAccountPassword() : 조회한 회원 정보와 변경할 비밀번호 정보를 통해 수정된 회원 정보를 DB에 반영하며, DB 반영 전에 로그인 상태를 "logout"으로 수정한다. 이렇게 구현한 이유는 계정 비밀번호 변경시 재로그인을 유도하기 위해 로그아웃 시키기 위함이다. 최종적으로 비밀번호 변경에 대한 메시지를 반환한다.
QueryMemberRepositoryImpl
package CloneCoding.NaverCafe.domain.member.repository;
import static CloneCoding.NaverCafe.domain.member.QMember.*;
import static CloneCoding.NaverCafe.security.LoginStatus.*;
@Repository
@RequiredArgsConstructor
public class QueryMemberRepositoryImpl implements QueryMemberRepository {
private final JPAQueryFactory query;
@Override
public Member findByAccountId(String accountId) {
return Optional.ofNullable(query
.selectFrom(member)
.where(member.accountId.eq(accountId))
.fetchOne())
.orElseThrow(() -> new NoSuchElementException("유효하지 않은 접근입니다."));
}
@Override
public boolean checkLogin(Member member) {
return !member.getStatus().equals(STATUS_LOGIN.getStatus());
}
@Override
public Member updateMemberInfo(Member member, RequestUpdateMember request) {
member.updateInfo(request);
return member;
}
@Override
public boolean checkAccountPassword(Member member, RequestUpdateAccountPassword request) {
if (!member.getAccountPassword().equals(request.getNowAccountPassword())) return false;
return request.getChangeAccountPassword().equals(request.getCheckChangeAccountPassword());
}
@Override
public Member updateAccountPassword(Member member, String changeAccountPassword) {
member.updateAccountPassword(changeAccountPassword);
return member;
}
}
QueryMemberRepository 인터페이스의 checkLogin(), updateMemberInfo(), checkAccountPassword(), updateAccountPassword() 를 구현하였다. 각 메서드의 기능은 아래와 같다.
- checkLogin() : 회원의 로그인 상태를 확인하며 "login"이라면 false, "logout"이라면 true를 반환한다.
- updateMemberInfo() : 요청된 수정 정보를 회원 정보를 수정하고 수정된 회원 정보를 반환한다.
- checkAccountPassword() : 요청 정보의 현재 비밀번호 정보가 유효한지 확인한 후, 요청 정보의 변경 비밀번호 정보와 확인용 비밀번호 정보가 일치하는지 확인하고 모두 만족한다면 true를 반환, 하나라도 만족하지 않는다면 false를 반환한다.
- updateAccountPassword() : 바꿀 비밀번호 정보로 회원 정보를 수정하고, 수정된 회원 정보를 반환한다.
SystemMessage
package CloneCoding.NaverCafe.message;
@Getter
@RequiredArgsConstructor
public enum SystemMessage {
CHANGE_ACCOUNT_PASSWORD_FAILED("현재 비밀번호 또는 바꿀 비밀번호를 잘못 입력하였습니다."),
CHANGE_ACCOUNT_PASSWORD_COMPLETE("비밀번호 변경이 완료되었습니다!" +
System.lineSeparator() +
"변경된 비밀번호로 다시 로그인 해주세요.")
;
private final String message;
}
메세지를 상수로 가지는 Enum 클래스에 비밀번호 변경에 따른 메시지를 추가하였다.
API TEST
수정 가능한 정보들 중 이메일과 별명을 수정한 테스트 결과이다. 회원 정보들 중 이메일과 별명이 잘 수정된 것을 볼 수 있다.
비밀번호 수정에 대한 테스트 결과이다. DB를 통해서 비밀번호가 잘 변경된 것을 확인 가능하며, 로그인 상태가 "logout"으로 변경된 것을 확인 할 수 있다.
'Project > Naver Cafe' 카테고리의 다른 글
[클론 코딩] 네이버 카페 - 메모 (0) | 2024.05.12 |
---|---|
[클론 코딩] 네이버 카페 - 네이버 회원 탈퇴 (0) | 2024.05.12 |
[클론 코딩] 네이버 카페 - 네이버 로그아웃 (0) | 2024.05.11 |
[클론 코딩] 네이버 카페 - 네이버 로그인 (0) | 2024.05.10 |
[클론 코딩] 네이버 카페 - 네이버 회원 정보 조회(읽기) (0) | 2024.05.03 |