728x90

🧐 문제 발생 상황

OAuth2 소셜로그인을 하면 생일, 성별, 연락처에 대한 정보는 가져올 수가 없어서 추가 정보 기입 폼을 만들어서 디비에 저장되도록 구현했다.

그래서 생각한게 어느 블로그에서 본건데 Setter보다 @Builder 패턴을 사용하는게 좋다해서 소셜로그인 후 추가 정보 입력에 적용해보았다. 그래서 아래와 같은 코드로 실행을 했더니

// entity
@Getter 
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Oauth2LoginDto {

    private String phoneNumber; // 연락처
    private LocalDate birthdate; // 생년월일
    private String gender; // 성별
}
    // service
    
    /**
     * Oauth2 로그인 시 추가 정보 입력
     */
    @Override
    public Optional<User> updateOauth2(String username, Oauth2LoginDto oauth2LoginDto) {
        
        User user = User.builder()
        			.phoneNumber(oauth2LoginDto.getPhoneNumber())
				.gender(oauth2LoginDto.getGender())
				.birthdate(oauth2LoginDto.getBirthdate())
				.build();
        userRepository.save(user);
        return Optional.of(user);
    }

아래와 같은 오류가 발생했다.

org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : com.example.omg_project.domain.user.entity.User.nam

저 오류에 대해서 검색하니

 DataIntegrityViolationException 예외는 JPA가 엔티티를 저장하는 도중 User 엔티티의 name 필드가 null로 설정되어 있는 것을 감지했기 때문에 발생한 것이다. name 필드는 not-null 제약 조건이 설정되어 있어서 이로 인해 Hibernate가 엔티티를 데이터베이스에 저장할 수 없었다.

라고 한다.

 DataIntegrityViolationException 예외는 JPA가 엔티티를 저장하는 도중 User 엔티티의 name 필드가 null로 설정되어 있는 것을 감지했기 때문에 발생한 것이다. name 필드는 not-null 제약 조건이 설정되어 있어서 이로 인해 Hibernate가 엔티티를 데이터베이스에 저장할 수 없었다.

 

빌더 패턴을 사용해서 구현할 거면 아래와 같이 기존 사용자가 notnull로 갖고 있는 필드들에 대해서 다 값을 복사해줘야 한다는 것이다.. 그래서 아니 세터보다 빌더를 사용하라면서 왜 저렇게 굳이 저래야 하나..? 했는데 열심히 구글링을 해서 빌더와 세터의 차이에 대해서 확실히 이해가 갔다.

/**
 * Oauth2 로그인 시 추가 정보 입력
 */
@Override
public Optional<User> updateOauth2(String username, Oauth2LoginDto oauth2LoginDto) {

    User user = User.builder()
				.name(user.getName()) // 기존에 있던 값 추가
				.phoneNumber(oauth2LoginDto.getPhoneNumber())
				.gender(oauth2LoginDto.getGender())
				.birthdate(oauth2LoginDto.getBirthdate())
				.build();
    userRepository.save(user);
    return Optional.of(user);
}

 

이러한 문제가 발생된 원인은 내가 빌더패턴을 정확하게 이해하지 못해서 발생한 대참사이다. 빌더는 객체를 수정하는 것이 아니라 생성자처럼 객체를 생성하는 기능을 수행한다고 한다.

 

 

정확하게 알아야 할 개념

  • 빌더패턴은 객체의 생성을 담당하는 패턴으로, 일반적으로 빌더를 사용하여 객체를 새로 생성한다. 빌더를 사용하여 객체를 생성할 때, 이전 객체의 값을 수정하는 것이 아니라 새로운 객체를 생성하는 것이다.
  • 이것이 빌더 패턴의 주요 특징 중 하나이며, 이를 이해하는 것이 중요하다.
  • 빌더 패턴은 객체 생성 시에 여러 속성을 가진 객체를 편리하게 생성하기 위해 사용된다. 따라서 객체의 속성을 일부 변경하거나 해당 속성을 지정하여 새로운 객체를 생성해야한다고 한다.
  • 따라서 내가 dto로 받아온 값을 User 엔티티의 빌더 패턴으로 엔티티 객체로 변환하려고 하는 행위(?)는 잘못되었다고 할 수 있다.
  • 하지만 빌더 패턴을 사용해서 객체의 데이터를 수정하는 것이 아예 불가능한 것은 아니라고 한다.

 

 

그렇다면 빌더 패턴으로 어떻게 값을 수정할 수 있는가?

// entity
@Getter  @Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true) // 기존 인스턴스를 기반으로 새로운 객체를 빌드할 수 있게 함
public class Oauth2LoginDto {

    private String phoneNumber; // 연락처
    private LocalDate birthdate; // 생년월일
    private String gender; // 성별
}

이처럼 toBuilder 속성을 true 로 설정해줄 경우 toBuilder() 메서드를 사용할 수 있게되는데

이 메서드를 사용하면 이미 생성되어있는 객체의 값을 toBuilder() 메서드를 활용하여 특정 필드의 값만을 변경해줄 수 있다고 한다.

 

 

결론

toBuilder 메서드를 활용하면 객체의 값을 일부 수정할 수는 있지만 내가 구현하려는 로직에서는 사용하기에 적합하지 않은 것 같다는 생각이 들었다.

어쨌든 빌더패턴이라는 것이 매개변수가 많아 생성자에 정의된 순서대로 데이터를 넣기 어려워 이를 편하게 하기 위한 디자인패턴인 건데 즉, 객체를 생성하는 것이 주된 목적인데 나는 객체의 값을 수정하는 로직을 만들어야 하므로 객체의 값을 부분적으로 수정하기 위해서는 굳이 엔티티 객체로 바꿀 필요는 없을 것 같고, dto에서 값을 꺼내서 entity에 setter 를 사용해서 update를 해주는 방식으로 구현하는 것이 적합한 것 같다.

 

 

✅ 정리

  • 세터 사용 방법: 기존 사용자 객체를 업데이트하는 방식이므로, 코드가 간결하고 오류 발생 가능성이 적다. 또한, 필수적으로 설정해야 하는 필드를 실수로 놓칠 가능성도 적다.
  • 빌더 사용 방법: 만약 객체의 다른 필드를 변경하거나 새로운 객체로 교체해야 하는 상황이 아니라면, 굳이 빌더 패턴을 사용할 필요는 없다. 이 방법은 필요 이상으로 복잡할 수 있으며, 잘못된 필드 값을 복사하거나 누락할 위험이 있다.
  • 즉, 빌더 패턴은 새로운 객체를 생성하거나 객체의 불변성을 유지해야 할 때 더 유용하다. 하지만 여기서는 기존 객체에 OAuth2 유저의 성별, 생일, 연락처의 정만 업데이트하는 것이므로 세터방식을 사용하는 것이 더 자연스럽고 안전하다.
728x90

+ Recent posts