안녕하세요! delay100입니다.
이번에는 매우 단순한 Update를 수행했던 마이페이지 정보 업데이트에 대해 다뤄볼까 합니다.
사실 많이 간단하기 때문에 글을 올릴까 말까 고민하다가, 단순한 CRUD여도 혼자 구현한 프로젝트의 일부이기 때문에 포스팅 하기로 했습니다!
1. 마이페이지 정보 업데이트 1 - (주소, 전화번호)
1-1. 동작 방식
로그인 시 프론트에 반환했던 JWT토큰을 넘겨받고 SpringSecurity의 AuthorizationFilter에서 인가 처리가 되어 API에 접근됩니다. 요청으로 들어온 회원 정보를 업데이트 합니다.
Service에서 Builder를 직접 사용하는 것은 코드가 길어질 수 있어 지양하고자 합니다. 이를 해결하기 위해, Member Entity에 from 메서드를 구현하여 코드를 간결하게 만드는 데 초점을 두었습니다. 앞으로 나오는 모든 Builder는 이 from 방식을 이용해서 구현했습니다.
1-2. 주소, 전화번호 업데이트 구현
Github 주소
- MemberController
- MemberService
- AES256Encoder -> 회원가입에서 다뤘었습니다. 정보 암/복호화
- MemberMyInfoResponseDto
// MemberController
/**
* PATCH
* 회원 정보 변경 1 - 주소, 전화번호
* @param userDetails security의 회원 정보
* @param memberMyInfoRequestDto 회원 정보 요청 객체 DTO
* @return 회원 정보 응답 객체 DTO
*/
@PatchMapping(BASE_MEMBER + "/myinfo")
public ApiResponse<MemberMyInfoResponseDto> updateMemberMyInfo(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestBody MemberMyInfoRequestDto memberMyInfoRequestDto
) {
return ApiResponse.createSuccess(memberService.updateMemberMyInfo(userDetails.getMember(), memberMyInfoRequestDto));
}
//MemberService
@Transactional
public MemberMyInfoResponseDto updateMemberMyInfo(Member member, MemberMyInfoRequestDto memberMyInfoRequestDto) {
member = memberRepository.findByMemberId(member.getMemberId())
.orElseThrow(() -> new IllegalArgumentException("잘못된 사용자 아이디 또는 비밀번호입니다."));
member.setAddress(aes256Encoder.encodeString(memberMyInfoRequestDto.getAddress()));
member.setPhone(aes256Encoder.encodeString(memberMyInfoRequestDto.getPhone()));
member.setZipCode(memberMyInfoRequestDto.getZipCode());
return MemberMyInfoResponseDto.from(member, aes256Encoder);
}
// MemberMyInfoResponseDto
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberMyInfoResponseDto {
private String memberId;
private String email;
private String memberName;
private String address;
private int zipCode;
private String phone;
public static MemberMyInfoResponseDto from(Member member, AES256Encoder aes256Encoder) {
return MemberMyInfoResponseDto.builder()
.memberId(member.getMemberId())
.email(aes256Encoder.decodeString(member.getEmail()))
.memberName(aes256Encoder.decodeString(member.getMemberName()))
.address(aes256Encoder.decodeString(member.getAddress()))
.zipCode(member.getZipCode())
.phone(aes256Encoder.decodeString(member.getPhone()))
.build();
}
}
2. 마이페이지 정보 업데이트 2 - (비밀번호)
2-1. 동작 방식
무신사(Musinsa)의 비밀번호 변경 페이지를 참고했습니다.
로그인 시 프론트에 반환했던 JWT토큰을 넘겨받고 SpringSecurity의 AuthorizationFilter에서 인가 처리가 되어 API에 접근됩니다. 기존 비밀번호, 새 비밀번호, 새 비밀번호 확인 문자열을 프론트 측에서 RequestBody로 요청받습니다.
비밀번호 일치하는지 확인하고 일치하면 비밀번호 변경을 하게 됩니다.
2-2. 비밀번호 업데이트 구현
Github 주소
// MemberController
/**
* PATCH
* 회원 정보 변경 - 비밀번호
* @param userDetails security의 회원 정보
* @param memberPasswordRequestDto 회원 비밀번호 요청 DTO
* @return 비밀번호 변경 성공 여부(T/F)
*/
@PatchMapping(BASE_MEMBER + "/modify/password")
public ApiResponse<Boolean> updateMemberPassword(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestBody MemberPasswordRequestDto memberPasswordRequestDto
) {
return ApiResponse.createSuccess(memberService.updateMemberPassword(userDetails.getMember(), memberPasswordRequestDto));
}
// MemberService
@Transactional
public boolean updateMemberPassword(Member member, MemberPasswordRequestDto memberPasswordRequestDto) {
member = memberRepository.findByMemberId(member.getMemberId())
.orElseThrow(() -> new IllegalArgumentException("잘못된 사용자 아이디 또는 비밀번호입니다."));
// 현재 비밀번호 검증
if (!passwordEncoder.matches(memberPasswordRequestDto.getPrePassword(), member.getPassword())) {
throw new IllegalArgumentException("현재 비밀번호가 일치하지 않습니다.");
}
// 새로운 비밀번호 확인
if (!memberPasswordRequestDto.getNewPassword().equals(memberPasswordRequestDto.getNewPasswordConfirm())) {
throw new IllegalArgumentException("새로운 비밀번호가 일치하지 않습니다.");
}
// 새로운 비밀번호 암호화 및 설정
member.setPassword(passwordEncoder.encode(memberPasswordRequestDto.getNewPassword()));
return true;
}
// Member
@Builder
@Getter
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Table(name= "member")
public class Member extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String memberId;
@Column(nullable = false)
private String password;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String memberName;
@Column(nullable = false)
private String address;
@Column(nullable = false)
private int zipCode;
@Column(nullable = false)
private String phone;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private MemberRoleEnum role;
public static Member from(SignupRequestDto member, PasswordEncoder passwordEncoder, AES256Encoder aesEncoder) {
return Member.builder()
.memberId(member.getMemberId())
.password(passwordEncoder.encode(member.getPassword()))
.address(aesEncoder.encodeString(member.getAddress()))
.zipCode(member.getZipCode())
.email(aesEncoder.encodeString(member.getEmail()))
.memberName(aesEncoder.encodeString(member.getMemberName()))
.phone(aesEncoder.encodeString(member.getPhone()))
.role(member.getRole())
.build();
}
public void setAddress(String address) {
this.address = address;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setZipCode(int zipCode) {
this.zipCode = zipCode;
}
public void setPassword(String password) {
this.password = password;
}
}
3. 회원 정보 확인
3-1. 동작 방식
로그인 시 프론트에 반환했던 JWT토큰을 넘겨받고 SpringSecurity의 AuthorizationFilter에서 인가 처리가 되어 API에 접근됩니다. 토큰 안에 있는 회원 정보를 꺼내온 후, DB에 접근해 현재 로그인한 사용자의 회원 정보를 가져옵니다.
3-2. 회원 정보 확인 구현
Github 주소
// MemberController
/**
* GET
* 회원 정보 업데이트
* @param userDetails security의 회원 정보
* @return 회원 정보 응답 객체 DTO
*/
@GetMapping(BASE_MEMBER + "/myinfo")
public ApiResponse<MemberMyInfoResponseDto> getMemberMyInfo(
@AuthenticationPrincipal UserDetailsImpl userDetails
) {
return ApiResponse.createSuccess(memberService.getMemberMyInfo(userDetails.getMember()));
}
// MemberService
public MemberMyInfoResponseDto getMemberMyInfo(Member member) {
member = memberRepository.findByMemberId(member.getMemberId())
.orElseThrow(() -> new IllegalArgumentException("잘못된 사용자 아이디 또는 비밀번호입니다."));
return MemberMyInfoResponseDto.from(member, aes256Encoder);
}
4. 결과 및 예제(API)
아래의 링크에서 더 자세히 보실 수 있습니다.
API(POSTMAN) : https://documenter.getpostman.com/view/23481846/2sA3kSo3ZJ
5. 이후 추가하고 싶은 기능
5-1. 주소, 전화번호 업데이트 - 도로명 주소 API 도입
도로명 주소 API(go.kr) : https://business.juso.go.kr/addrlink/openApi/apiExprn.do
위의 사이트에서 도로명 주소 API를 불러올 수 있습니다! 지금은 String으로 사용자에게 직접 모든 주소를 받아오도록 하고 있는데, 추후에 해당 API를 도입하여 사용자 편의성을 증대시키려 합니다.
5-2. 비밀번호 변경 - 이메일 인증으로 변경
비밀번호 변경 시 이메일 인증을 하도록 변경하면 좋을 듯 싶습니다. 이렇게 하면 보안이 강화되고 사용자의 계정을 보다 안전하게 보호할 수 있습니다. 사실 지금 구현할 때도 고민을 했지만, 비밀번호를 변경하기 위해 이메일을 인증하는건 유저 편의성이 떨어진다고 판단해서 그냥 만들었습니다..
구현은 이전에 회원가입 할 때 구현해둔 이메일 인증을 여기서 사용하면 될 것 같습니다.
긴 글 읽어주셔서 감사합니다! 코드나 동작 방식에 대해 궁금한 점이 있으시면 언제든지 질문해 주세요. 피드백도 환영합니다.
'Study > SpringBoot' 카테고리의 다른 글
[e-commerce 프로젝트] 6. 위시리스트, 장바구니 (0) | 2024.07.29 |
---|---|
[e-commerce 프로젝트] 5. 상품(AWS S3, DB Scheduling, Redis) (0) | 2024.07.26 |
[e-commerce 프로젝트] 3. 로그인(SpringSecurity + JWT 로그인 및 인가) (7) | 2024.07.24 |
[e-commerce 프로젝트] 2. 회원가입(네이버 이메일 인증, 개인정보·비밀번호 암호화) (6) | 2024.07.24 |
[e-commerce 프로젝트] 1. 프로젝트 간단 소개 및 ERD 설계 (1) | 2024.07.17 |