본문 바로가기
DB/RDB

엔티티의 id를 AUTO_INCREMENT가 아닌 UUID를 사용하고자 할 때는

by devson 2022. 7. 26.

왜 사용해야하나

첫째로는 보안적인 이유이다.

AUTO_INCREMENT를 엔티티의 id로 쓰는 경우 id가 단순 숫자값이기 때문에 외부에서 데이터를 긁어가기가 좋다.

예를 들어 커머스 서비스인 경우 상품 데이터에 AUTO_INCREMENT id를 사용한다면 조회 API를 사용하여(단순 상품 조회 public API일 것이기 때문에) 모든 상품 데이터를 긁어갈 수 있을 것이다.

(https://some-commerce/products/1, https://some-commerce/products/2, ..., https://some-commerce/products/102341, ...)

 

더 크리티컬한 경우는 API를 통해 유저의 개인정보를 조회할 때, 만약 보안 기능이 제대로 작동하지 않는 경우라면 타인의 데이터를 쉽게 빼갈 수 있다.

 

둘째로는 외부에 서비스의 규모 추정을 막기 위함이다.

AUTO_INCREMENT id를 사용하면 새로운 데이터가 생성될 때 그 id가 그동안 쌓인 데이터의 양이 된다.

그러므로 새로 생성된 데이터의 id를 보면 쉽게 그 서비스의 규모가 추정된다.

출처: https://twitter.com/dylayed/status/1496020581610778625

 

UUID 컬럼은 Binary16 type으로 사용할 것

UUID는 36자로 VARCHAR 타입으로 저장하게 되면 BIGINT 타입보다 많은 저장 공간을 필요로 한다.

그렇기 때문에 UUID를 사용한 컬럼의 저장 공간을 최소화하기 위해 해당 컬럼을 BINARY(16) 타입으로 지정하도록 한다.

다만 BINARY 타입이기 때문에 직접 컬럼을 조회할 때는 BIN_TO_UUID를 통해 조회해야 human-readable한 값을 얻을 수 있다.

 

또는 VARCHAR 타입을 사용하고자 할 때 UUID 보다 적은 공간을 사용하도록 Nano ID를 사용할 수도 있다.

 

PK를 UUID 그대로 사용하지 말 것

PK기본적으로 저장공간(디스크)에 테이블의 row 데이터의 저장되는 순서를 지정하는 clustered index로 쓰인다.

일반적으로 UUID 사용 시 사용되는 UUID v4는 랜덤값이기 때문에 insert가 많아지면 저장된 데이터 sorting으로 인한 오버헤드가 발생하여 성능 저하가 발생할 수 있다.

출처: https://blog.programster.org/mysql-performance-when-using-uuid-for-primary-key


그렇기 때문에 Table의 PK는 AUTO_INCREMENT 등의 sequential한 값으로 지정하고,
UUID를 사용하는 엔티티 id 컬럼에 보조 index를 거는 방식으로 성능 저하를 완화한다.

CREATE TABLE my_entity(
  pk BIGINT AUTO_INCREMENT PRIMARY KEY, -- DB PK
  id BINARY(16) NON NULL -- Entity ID
);

CREATE UNIQUE INDEX idx_my_entity_id on my_entity (id);

 

이는 일종의 Surrogate Primary Key로 볼 수 있다.
(참고: Entity Identity vs Database Primary Key)

 

또한 이렇게 따로 Table PK엔티티의 ID를 같이 사용하게 되면 운영 편의 상으로도 좋다.

운영 시 문제가 생겼을 때, 운영팀에서 개발팀에게 특정 데이터의 id를 전달해줄때가 있다.

이때 엔티티의 ID를 UUID + PK로 사용한다면, id 값을 전달해주기가 조금 번거로워진다. (특히 유선상으로)

그런 점에서 운영팀과 얘기를 할 때는, Table PK로 서로 얘기하면 의사소통이 조금 더 편리해지는 경향이 있다.

 

하지만 DB 테이블에 유니크한 값인 Table PK엔티티의 ID가 동시에 존재하기 때문에 다른 테이블에서 참조 시 어떤 컬럼을 사용할지에 대한 명확한 기준이 필요하다.

그렇지 않다면 어떤 테이블에서는 Table PK를 참조할 것이며, 어떤 테이블에서는 엔티티의 ID를 참조할 것이다.

이는 개발의 복잡도를 올리는 방향이며, 차라리 UUID 사용을 안하느니만 못한 방식이다.

 

이처럼 Table PK엔티티의 ID를 사용하는 경우 애플리케이션에서 DB 모델과 엔티티 모델을 분리해주면 코딩을 할 때 헷갈림이 줄어든다.

 


 

하지만 단순히 '일어날 것 같은' 또는 '일어나지 않을' 미래를 위해 위와 같은 방법을 쓰는 것은 추천하지 않는다.

예를 들어

- 서비스가 잘되서 데이터가 엄청나게 많아지는 경우
- RDBMS에서 제공하는 AUTO_INCREMENT를 사용하다 이를 제공하지 않는 DB로 마이그레이션을 해야하는 경우
  (관련해서 Twitter의 Announcing Snowflake - The Problem 문단을 살펴봐도 좋다)

 

트래픽이 정말 많지 않고, 데이터가 아무리 생각해도 수백, 수천만 건이 생기지 않을 서비스가 아니라면
단순히 AUTO_INCREMENT PK를 엔티티의 ID로 사용하거나, UUID를 PK와 엔티티의 ID로써 사용하여도 큰 문제는 없을 것이다.

또는 Sequencial한 UUID를 사용하는 방법도 있을 것이고 (참고), 혹은 SnowflakeTicket Server와 같은 ID Generator를 사용할 수도 있다.

 

가장 어려운 일이지만 적절하게, 상황에 맞게, 필요한 상황이 오면, 늦지 않게 적정 아키텍처에 대해 생각하는 것이 중요하다.

 

 

 

추가 참고: https://dzone.com/articles/uuid-as-primary-keys-how-to-do-it-right

'DB > RDB' 카테고리의 다른 글

UUID for Primary Key  (4) 2021.07.23
Foreign Key에도 index가 걸릴까?  (0) 2021.01.02

댓글