- Chapter 1 프로젝트 리팩토링
- User Entity의 Id 필드 제거
- User Entity에 @Builder 적용
- @Builder란?
- @Builder의 동작 원리
- @Builder 적용
- 리팩토링을 고민하고 있는 부분들
기존 User Entity에는 @Id, 즉 PK로 Long 타입 숫자가 사용됨
Auto_increment가 적용되었으며, 회원가입 시 CreatedBy에 반영하기 힘든 문제 발생
Id 필드를 제거하고 username을 @Id(PK)로 지정함
- 필요한 데이터만 설정할 수 있음
- 유연성이 뛰어남
- 가독성이 좋음
// Before:
@Builder
class Example<T> {
private T foo;
private final String bar;
}
// After:
class Example<T> {
private T foo;
private final String bar;
private Example(T foo, String bar) {
this.foo = foo;
this.bar = bar;
}
public static <T> ExampleBuilder<T> builder() {
return new ExampleBuilder<T>();
}
public static class ExampleBuilder<T> {
private T foo;
private String bar;
private ExampleBuilder() {}
public ExampleBuilder foo(T foo) {
this.foo = foo;
return this;
}
public ExampleBuilder bar(String bar) {
this.bar = bar;
return this;
}
@java.lang.Override public String toString() {
return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")";
}
public Example build() {
return new Example(foo, bar);
}
}
}
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "p_user")
public class User extends BaseEntity {
@Id
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false, unique = true)
private String phoneNumber;
@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserRoleEnum role;
private boolean publicProfile;
private String imageUrl;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "address_id")
private Address currentAddress;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Address> addresses = new ArrayList<>();
}
public UsernameResponseDto signup(@Valid SignupRequestDto requestDto, UserRoleEnum loggedInRole) {
String username = requestDto.getUsername();
String password = passwordEncoder.encode(requestDto.getPassword());
String email = requestDto.getEmail();
String phoneNumber = requestDto.getPhoneNumber();
UserRoleEnum role = requestDto.getRole();
checkUsername(username);
checkEmail(email);
checkPhoneNumber(phoneNumber);
if (loggedInRole != UserRoleEnum.MASTER) {
checkRole(role);
}
User user = User.builder()
.username(username)
.password(password)
.email(email)
.phoneNumber(phoneNumber)
.role(role)
.build();
// 로그인된 사용자가 있을 경우 그 사용자의 username을 CreatedBy로 설정, 없는 경우 회원가입 시 지정한 username이 됨
String createdBy = getCurrentUsername(username);
user.setCreatedBy(createdBy);
user.setLastModifiedBy(createdBy);
User savedUser = userRepository.save(user); // User 엔티티 저장
return new UsernameResponseDto(savedUser.getUsername()); //저장된 User Entity의 id값을 통해 SignupResponseDto를 생성하고 반환
}
현재 Entity에서 @Setter를 사용하고 있는 부분들이 있는데, setter 메서드의 사용은 의도를 드러내기 힘들고, 객체의 일관성을 유지하기 어렵다는 문제가 있다.
@Transactional
public UsernameResponseDto updateUser(@Valid UpdateUserRequestDto requestDto, String username) {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new NullPointerException(ExceptionMessage.USER_NOT_FOUND.getMessage()));
user.setPassword(passwordEncoder.encode(requestDto.getPassword()));
user.setEmail(requestDto.getEmail());
user.setPhoneNumber(requestDto.getPhoneNumber());
user.setImageUrl(requestDto.getImgUrl());
user.setPublicProfile(requestDto.isPublicProfile());
user.setRole(requestDto.getRole());
userRepository.save(user);
return new UsernameResponseDto(user.getUsername());
}