20241111 TIL : Lombok @Builder, Chapter 1 프로젝트 리팩토링

MCS·2024년 11월 11일

TIL

목록 보기
1/45

오늘 진행한 학습

  • Chapter 1 프로젝트 리팩토링
    • User Entity의 Id 필드 제거
    • User Entity에 @Builder 적용
      • @Builder란?
      • @Builder의 동작 원리
      • @Builder 적용
    • 리팩토링을 고민하고 있는 부분들

Chapter 1 프로젝트 리팩토링

User Entity의 Id 필드 제거

기존 User Entity에는 @Id, 즉 PK로 Long 타입 숫자가 사용됨
Auto_increment가 적용되었으며, 회원가입 시 CreatedBy에 반영하기 힘든 문제 발생
Id 필드를 제거하고 username을 @Id(PK)로 지정함

User Entity에 @Builder 적용

  • @Builder란?
    @Builder는 생성자를 직접 사용하지 않고 객체를 생성하기 위한 방법 중 하나이다.
    Builder를 사용할 경우 다음과 같은 장점이 있다.
    1. 필요한 데이터만 설정할 수 있음
    2. 유연성이 뛰어남
    3. 가독성이 좋음
  • @Builder의 동작 원리
    빌더 패턴을 사용할 경우 빌더를 만들고 그 안에서 멤버 필드별로 값을 설정하고 빌더를 반환하는 함수를 직접 구현해야 한다. @Builder는 이 과정을 자동으로 처리해 준다. 다음 코드는 @Builder의 javadoc에서 가져온 코드이며, 멤버 변수에 대한 빌더를 만들어주는 것을 확인할 수 있다.
// 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);
   		}
   	}
   }
  • @Builder 적용
@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());
    }
  • 해결할 수 있는 방법은?
    • Entity에 update 메서드 추가 -> dto와 passwordEncoder를 매개변수로 넘기고 값을 수정하도록 함
    • dto에서 toEntity 메서드 구현
profile
우리는 AI 시대에 살고 있다.

0개의 댓글