UUID 삽질 해결기

onyoo·2023년 2월 23일
0

Spring-Boot

목록 보기
3/6

개요

진짜 별거아니었는데 chat gpt씨랑 씨름을 한 끝에 해결법을 찾아냇다.
나중에 또 까먹을게 뻔하기 때문에 기록으로 남겨놓고자한다.

사건의 전말

내가 하고싶은 일은 다음과 같다.

1.PK가 아닌 UUID 값을 게시글의 식별자로 사용할 것이다
2.PK가 아닌 UUID의 auto generation 을 설정해주고 싶다.

일단,본격적으로 들어가기 전 대강 나의 세팅을 보자면 다음과 같다.

데이터를 저장할 테이블의 상태는 다음과 같다.

CREATE TABLE posts_table (
id int(11) NOT NULL AUTO_INCREMENT,
uuid uuid NOT NULL,
title varchar(255) NOT NULL,
contents varchar(5000) NOT NULL,
goods int(11) NOT NULL DEFAULT 0,
upload_date timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

사실, uuid 컬럼의 default 값을 uuid() 함수로 해두었는데 이게 mariadb 버전에 따라 차이가 있어서 빼버렷다.

어떤 삽질을 해보았는가

DTO에 넣으면 되지않는가 !!!

첫번째로 가장 생각이 든것은 DTO에서 데이터를 받으면 그때 생성해버리면 되지않는가 라는 생각이었다.
쉬웠다.

import com.example.everytime.domain.posts.Post;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.UUID;

@Getter
@NoArgsConstructor
public class PostCreateRequestDto {

    private UUID uuid;
    private String title;
    private String content;

    public PostCreateRequestDto(String title, String content){
        this.title = title;
        this.content = content;
        this.uuid = UUID.randomUUID();
    }
    public Post toEntity(){
        return Post.builder()
                .uuid(UUID.randomUUID())
                .title(title)
                .contents(content)
                .build();
    }
}

이렇게 하면 되겠다 라고 생각이 든 이유는 다음과 같다.
일단, 데이터를 create를 하는 과정에서만 uuid가 필요하니까.데이터 생성 dto에서 처리하면 되겠다 라는 생각이었다.

그러다 유효성 검증을 할때 문제가 발생했다.

사실, UUID 값은 데이터가 들어올때 같이 들어오는 값이 아니기 때문에 DTO에 들어가는 것은 적절하지 않고, 데이터 유효성 검증에서 UUID는 붕 떠버리게 된다.
내부에서 만들어주는 값인데 무슨 검증을 할 필요가 있겠는가.

그리하여 validator를 설정하는 과정에서 uuid를 생성하는 로직을 삭제했다.

그럼 그냥 데이터베이스에서 알아서 해주면 되지않을까?

데이터베이스에 default 값으로 uuid() 함수를 설정해주면 되지않을까?
일반키를 auto generation으로 하듯이 말이다.
그래서 default 값으로 uuid() 를 넣었다.

uuid 부분의 JPA 엔티티 코드를 보면 좀 많은 시도를 해보았다.

@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
private UUID uuid;

이런식으로 말이다, 다만 이 방법은 효과가 없었다. 왜냐하면 generatedValue의 경우 id로 사용되는 값에 한하여 사용하기 때문이다.

공식문서를 참고하자 -> 참고

그렇게 발생한 에러는

Column 'uuid' cannot be null

uuid 컬럼에 아무 데이터도 들어가지 않는 것이다.

JPA에서 제대로 uuid 를 생성하지 못했다.

더불어 uuid() 함수를 데이터베이스에 먹이는 것도 힘들었던 것이, uuid() 함수를 먹이는 방식이 mariadb 버전에 따라 불가능한 버전이 있기 때문에 이 방법을 포기했어야 했다.

해결책

chat gpt로 어떻게 코드를 수정하면 될까 씨름을 하던 중 다음과 같은 답변을 받아냈다.

왜..이런 키워드를 진작에 말해주지 않은거지..? 라는 생각이 잠시 지나갔지만.

해당 어노테이션을 사용한 예시코드를 참고하여 JPA 코드를 공개한다.

package com.example.everytime.domain.posts;

import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.UUID;

@Entity
@Table(name = "posts_table")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post {

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

    @Column(columnDefinition = "BINARY(16)",nullable = false, updatable = false)
    private UUID uuid;

    @Column(columnDefinition = "VARCHAR(255)", nullable = false)
    private String title;

    @Column(columnDefinition = "VARCHAR(5000)", nullable = false)
    private String contents;

    @Column(columnDefinition = "INT", nullable = false)
    private int goods;

    @CreationTimestamp
    @Column(nullable = false)
    private LocalDateTime upload_date;

    @Builder
    public Post(String title, String contents, int goods) {
        this.title = title;
        this.contents = contents;
        this.goods = goods;
    }

    public void update(String title, String contents) {
        this.title = title;
        this.contents = contents;
    }

    @PrePersist
    public void prePersist(){
        this.uuid = UUID.randomUUID();
    }
    
}

@PrePersist
    public void prePersist(){
        this.uuid = UUID.randomUUID();
    }
    

이 어노테이션을 사용하면 엔티티가 저장되기 전,데이터를 알아서 생성해주도록 하면 된다.

JPA @prePersist 내용은 다음과 같은 곳에서 참고하면 된다

참고

profile
반갑습니다 ! 백엔드 개발 공부를 하고있습니다.

0개의 댓글