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