Skip to content

Commit

Permalink
Refactor/#523 회원 엔티티 속성 값객체분리 (#525)
Browse files Browse the repository at this point in the history
* refactor: Member에서 Nickname 값 객체로 분리

* refactor: Member에서 Email 값 객체로 분리

* refactor: Member에서 Password 값 객체로 분리

* refactor: 이미 암호화된 비밀번호는 다시 암호화되지 않도록 방어로직 추가
  • Loading branch information
This2sho authored Nov 13, 2023
1 parent 95f6cfb commit 70b333f
Show file tree
Hide file tree
Showing 21 changed files with 418 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import edonymyeon.backend.member.application.MemberService;
import edonymyeon.backend.member.application.dto.ActiveMemberId;
import edonymyeon.backend.member.application.dto.MemberId;
import edonymyeon.backend.member.domain.Email;
import edonymyeon.backend.member.domain.Member;
import edonymyeon.backend.member.domain.Nickname;
import edonymyeon.backend.member.domain.SocialInfo;
import edonymyeon.backend.member.domain.SocialInfo.SocialType;
import edonymyeon.backend.member.repository.MemberRepository;
Expand Down Expand Up @@ -62,7 +64,7 @@ private Member authenticateMember(final String email, final String password) {
}

private Member findByEmail(final String email) {
final Member member = memberRepository.findByEmail(email)
final Member member = memberRepository.findByEmail(Email.from(email))
.orElseThrow(() -> new EdonymyeonException(MEMBER_EMAIL_NOT_FOUND));
if (member.isDeleted()) {
throw new EdonymyeonException(MEMBER_IS_DELETED);
Expand Down Expand Up @@ -121,8 +123,7 @@ public Member joinSocialMember(final SocialInfo socialInfo, final String deviceT
}

private Member saveMember(Member member) {
final String encodedPassword = passwordEncoder.encode(member.getPassword());
member.encrypt(encodedPassword);
member.encrypt(passwordEncoder);
return memberRepository.save(member);
}

Expand Down Expand Up @@ -150,13 +151,13 @@ public Member joinMember(final JoinRequest joinRequest) {
}

private void validateDuplicateEmail(final String email) {
if (memberRepository.existsByEmail(email)) {
if (memberRepository.existsByEmail(Email.from(email))) {
throw new EdonymyeonException(MEMBER_EMAIL_DUPLICATE);
}
}

private void validateDuplicateNickname(final String nickname) {
if (memberRepository.existsByNickname(nickname)) {
if (memberRepository.existsByNickname(Nickname.from(nickname))) {
throw new EdonymyeonException(MEMBER_NICKNAME_INVALID);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public enum ExceptionInformation {
LOGOUT_FAILED(1525, "로그아웃에 실패하였습니다."),
NOT_SUPPORTED_VERSION(1526, "지원하지 않는 버전입니다."),
NOT_SUPPORTED_ALGORITHM(1527, "현재 지원하지 않는 알고리즘입니다."),
ENCODED_PASSWORD_INVALID(1528, "잘못 인코딩된 비밀번호입니다."),
ALREADY_ENCODED_PASSWORD(1528, "이미 암호화된 비밀번호입니다."),

// 2___: 게시글 관련
POST_ID_NOT_FOUND(2000, "존재하지 않는 게시글입니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import edonymyeon.backend.member.application.dto.response.MyPageResponseV1_1;
import edonymyeon.backend.member.application.event.ProfileImageDeletionEvent;
import edonymyeon.backend.member.domain.Device;
import edonymyeon.backend.member.domain.Email;
import edonymyeon.backend.member.domain.Member;
import edonymyeon.backend.member.domain.Nickname;
import edonymyeon.backend.member.repository.MemberRepository;
import java.util.Objects;
import java.util.Optional;
Expand Down Expand Up @@ -87,7 +89,7 @@ public MemberUpdateResponse updateMember(final MemberId memberId, final MemberUp

private void validateDuplicateNickname(final String currentNickname, final String updateNickname) {
if (!currentNickname.equals(updateNickname) &&
memberRepository.existsByNickname(updateNickname)) {
memberRepository.existsByNickname(Nickname.from(updateNickname))) {
throw new EdonymyeonException(MEMBER_NICKNAME_DUPLICATE);
}
}
Expand All @@ -103,10 +105,10 @@ public DuplicateCheckResponse checkDuplicate(final String target, final String v

private boolean existsByValidateType(final ValidateType validateType, final String value) {
if (validateType.equals(ValidateType.EMAIL)) {
return memberRepository.existsByEmail(value);
return memberRepository.existsByEmail(Email.from(value));
}

return memberRepository.existsByNickname(value);
return memberRepository.existsByNickname(Nickname.from(value));
}

@Transactional
Expand Down
47 changes: 47 additions & 0 deletions backend/src/main/java/edonymyeon/backend/member/domain/Email.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package edonymyeon.backend.member.domain;

import static edonymyeon.backend.global.exception.ExceptionInformation.MEMBER_EMAIL_INVALID;
import static edonymyeon.backend.member.domain.SocialInfo.*;

import edonymyeon.backend.global.exception.EdonymyeonException;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.util.UUID;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.apache.logging.log4j.util.Strings;

@EqualsAndHashCode
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Embeddable
public class Email {

private static final int MAX_EMAIL_LENGTH = 30;

@Column(name = "email", nullable = false, unique = true)
private String value;

private Email(String email) {
this.value = email;
}

public static Email from(String email) {
validate(email);
return new Email(email);
}

private static void validate(final String email) {
if (Strings.isBlank(email) || email.length() > MAX_EMAIL_LENGTH) {
throw new EdonymyeonException(MEMBER_EMAIL_INVALID);
}
}

public static Email from(final SocialType socialType) {
return new Email("#" + socialType.name() + UUID.randomUUID());
}

public String getValue() {
return value;
}
}
99 changes: 29 additions & 70 deletions backend/src/main/java/edonymyeon/backend/member/domain/Member.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
package edonymyeon.backend.member.domain;

import static edonymyeon.backend.global.exception.ExceptionInformation.ENCODED_PASSWORD_INVALID;
import static edonymyeon.backend.global.exception.ExceptionInformation.MEMBER_EMAIL_INVALID;
import static edonymyeon.backend.global.exception.ExceptionInformation.MEMBER_NICKNAME_INVALID;
import static edonymyeon.backend.global.exception.ExceptionInformation.MEMBER_PASSWORD_INVALID;

import edonymyeon.backend.auth.domain.PasswordEncoder;
import edonymyeon.backend.global.domain.TemporalRecord;
import edonymyeon.backend.global.exception.BusinessLogicException;
import edonymyeon.backend.global.exception.EdonymyeonException;
import edonymyeon.backend.image.profileimage.domain.ProfileImageInfo;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
Expand All @@ -23,7 +17,6 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
Expand All @@ -37,21 +30,19 @@
public class Member extends TemporalRecord {

private static final int MAX_EMAIL_LENGTH = 30;
private static final int MAX_NICKNAME_LENGTH = 20;
private static final String UNKNOWN = "Unknown";

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, unique = true)
private String email;
@Embedded
private Email email;

@Column(nullable = false)
private String password;
@Embedded
private Password password;

@Column(nullable = false, unique = true)
private String nickname;
@Embedded
private Nickname nickname;

private SocialInfo socialInfo;

Expand All @@ -72,61 +63,30 @@ public Member(
final ProfileImageInfo profileImageInfo,
final List<String> deviceTokens
) {
validate(email, password, nickname);
this.email = email;
this.password = password;
this.nickname = nickname;
this.email = Email.from(email);
this.password = Password.from(password);
this.nickname = Nickname.from(nickname);
this.profileImageInfo = profileImageInfo;
this.devices = deviceTokens.stream()
.map(token -> new Device(token, this))
.toList();
}

private Member(final String email, final String password, final String nickname, final SocialInfo socialInfo) {
private Member(final Email email, final Password password, final Nickname nickname, final SocialInfo socialInfo) {
this.email = email;
this.password = password;
this.nickname = nickname;
this.socialInfo = socialInfo;
}

public static Member from(final SocialInfo socialInfo) {
return new Member(UUID.randomUUID().toString(),
defaultSocialPassword(),
"#" + socialInfo.getSocialType().name() + UUID.randomUUID(),
return new Member(
Email.from(socialInfo.getSocialType()),
Password.from(socialInfo.getSocialType()),
Nickname.from(socialInfo.getSocialType()),
socialInfo);
}

private static String defaultSocialPassword() {
final String uuid = UUID.randomUUID().toString();
return uuid.replace("-", "").substring(0, 25) + "!";
}

private void validate(final String email, final String password, final String nickname) {
validateEmail(email);
validateNickName(nickname);
validatePassword(password);
}

private void validateEmail(final String email) {
if (Objects.isNull(email) || email.isBlank() || email.length() > MAX_EMAIL_LENGTH) {
throw new EdonymyeonException(MEMBER_EMAIL_INVALID);
}
}

private void validateNickName(final String nickname) {
if (Objects.isNull(nickname) || nickname.isBlank() || nickname.length() > MAX_NICKNAME_LENGTH
|| nickname.equalsIgnoreCase(UNKNOWN)) {
throw new EdonymyeonException(MEMBER_NICKNAME_INVALID);
}
}

private void validatePassword(final String password) {
if (PasswordValidator.isValidPassword(password)) {
return;
}
throw new EdonymyeonException(MEMBER_PASSWORD_INVALID);
}

public Optional<String> getActiveDeviceToken() {
return devices.stream().filter(Device::isActive)
.map(Device::getDeviceToken)
Expand All @@ -142,11 +102,19 @@ public boolean isDeleted() {
return deleted;
}

public String getEmail() {
return email.getValue();
}

public String getNickname() {
if (deleted) {
return UNKNOWN;
return Nickname.NONE;
}
return nickname;
return nickname.getValue();
}

public String getPassword() {
return password.getValue();
}

public boolean isActiveDevice(final String deviceToken) {
Expand Down Expand Up @@ -178,25 +146,16 @@ public boolean hasId(final Long memberId) {
return Objects.equals(this.id, memberId);
}

public void encrypt(final String encodedPassword) {
validateEncodedPassword(encodedPassword);
this.password = encodedPassword;
}

private void validateEncodedPassword(final String encodedPassword) {
if (PasswordValidator.isValidEncodedPassword(encodedPassword)) {
return;
}
throw new BusinessLogicException(ENCODED_PASSWORD_INVALID);
public void encrypt(final PasswordEncoder passwordEncoder) {
this.password = password.encrypt(passwordEncoder);
}

public void deleteProfileImage() {
this.profileImageInfo = null;
}

public void updateNickname(final String nickname) {
validateNickName(nickname);
this.nickname = nickname;
this.nickname = Nickname.from(nickname);
}

public void updateProfileImageInfo(final ProfileImageInfo profileImageInfo) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package edonymyeon.backend.member.domain;

import static edonymyeon.backend.global.exception.ExceptionInformation.MEMBER_NICKNAME_INVALID;
import static edonymyeon.backend.member.domain.SocialInfo.*;

import edonymyeon.backend.global.exception.EdonymyeonException;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.util.UUID;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.apache.logging.log4j.util.Strings;

@EqualsAndHashCode
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Embeddable
public final class Nickname {

public static final String NONE = "Unknown";
private static final int MAX_NICKNAME_LENGTH = 20;

@Column(name = "nickname", nullable = false, unique = true)
private String value;

private Nickname(final String nickname) {
this.value = nickname;
}

public static Nickname from(final String nickname) {
validate(nickname);
return new Nickname(nickname);
}

private static void validate(final String nickname) {
if (Strings.isBlank(nickname) || nickname.length() > MAX_NICKNAME_LENGTH
|| nickname.equalsIgnoreCase(NONE)) {
throw new EdonymyeonException(MEMBER_NICKNAME_INVALID);
}
}

public static Nickname from(final SocialType socialType) {
return new Nickname("#" + socialType.name() + UUID.randomUUID());
}

public String getValue() {
return value;
}
}
Loading

0 comments on commit 70b333f

Please sign in to comment.