[UUID] 사용자에게 보여주는 URL 관련 보드의 고유키 값 구성하기

다나·2023년 10월 17일
1

다담다 프로젝트

목록 보기
27/28

0️⃣ 서론

저희 서비스에서 이번에 새로운 기능으로 스크랩들을 정리하는 보드 기능을 추가하기로 하였습니다.

해당 보드에서 원하는 스크랩들을 추가하여, 주제에 맞는 스크랩들을 정리할 수 있습니다.

저희는 어떤 보드인지 식별해야 하므로, 유저가 직접 보는 보드의 URL에 보드의 고유 식별키를 넣는 방식을 선택하였습니다.

이때, 보드의 고유 식별키를 UUID로 할지 아니면, auto increment인 board_id로 할지를 선택해야 했습니다.

저희가 위의 사진처럼 boardUUID를 사용했는데, 왜 UUID를 선택하였는지 board_id로 하면 어떠한 문제가 발생할지 작성해보겠습니다.


1️⃣ 본론

1. Auto Increment인 board_id를 선택하면 발생하는 문제점

board_id는 아래의 코드로 생성된 id로 GeneratedValue입니다.

public class Board extends BaseTimeEntity {

    @Id
    @GeneratedValue
    @Column(name = "board_id")
    private Long id;
}

✍️ 사용자가 임의로 ID 값을 변경하여 오류 발생

이전의 스크랩 관련 API에서는 해당 스크랩을 식별하는 값으로 scrap의 id와 같이 자동으로 증가되는 키 값을 사용했습니다.
이때에는 사용자가 직접 보는 URL이 아닌 API 자체에서 사용하고 있었습니다.
따라서 사용자가 직접적으로 id의 값을 볼 수 없습니다.

사용자에게 직접 공유되는 URL 자체에 id를 넣게 되면, 사용자가 손쉽게 임의로 ID 값을 변경하여 남의 보드를 보게 되는 경우가 발생할 수 있다고 생각했습니다.

물론 남의 보드를 보게 되는 경우, 해당 보드의 유저가 아니므로 에러가 발생하는 로직이 추가는 되어 있습니다.

하지만, 이러한 경우 API에서 발생하는 오류가 많아지고, 악의적인 유저에 의해 DB에서 해당 유저의 보드인지 확인하는 로직 등 API 서버에 요청을 많이 보내어 부담을 주게 될 수도 있습니다.

✍️ DB의 데이터 예측 가능

Auto Increment를 사용하면, 저장되는 보드의 개수가 많아질 수록 숫자가 커지기 때문에 DB에 보드 데이터가 얼마나 존재하는지를 예측할 수 있습니다. 하지만, 올바르게 API 호출을 통해서 얻은 데이터 정보가 아닌 board_id 값으로 알 필요는 없다고 생각했습니다.

✍️ 단축 URL

저희 서비스에서는 보드를 다른 사람들에게 공유할 수 있는 기능도 제공할 예정입니다.
그러나, 자동으로 증가하는 키를 사용하면 URL을 단축할 때 이때에도 key의 길이가 일정하지 않고 일정한 길이의 URL로 단축할 수 없다는 판단을 하였습니다.

즉, 6자리로 줄이려고 했는데, 이미 board_id가 1~2자리이면 동일한 단축 URL을 사용할 수 없습니다.


2. UUID이란?

UUID는 네트워크 상에서 고유성이 보장되는 id를 만들기 위한 표준 규약입니다.

즉, Universally Unique IDentifier의 약어입니다.

32자리의 16진수로 표현되며, 8자리-4자리-4자리-4자리-12자리 패턴으로 되어 있습니다.

UUID는 사용자가 최대한 예측할 수 없는 값을 사용하고, DB 데이터 개수와 연관이 없습니다. 그리고 길이가 일정하기 때문에 단축 URL도 동일하게 모든 URL에 적용할 수 있습니다.

  • UUID 종류
    • 버전은 1,3,4,5가 있습니다.
    • 이중 가장 많이 사용하는 버전은 1,4 버전입니다.
    • 1버전 : 타임스탬프를 기준으로 생성합니다. -> 호스트 ID, 시퀀스 번호 및 현재 시각으로 UUID를 발급합니다.
    • 4버전 : 랜덤 생성 (무작위 UUID 생성)
    • 주로 1버전보다는 4버전을 많이 사용합니다.
    • 이때, 애플에서도 UUID를 생성할 때 4 버전을 사용하는데, 보안성이 높은 랜덤 생성 UUID인 4버전을 사용합니다.

따라서 저희는 사용자가 DB를 예측하면 안되고, 보안상으로 뛰어나며 일정한 길이를 가지고 있는 4버전의 UUID를 선택하여 보드의 키를 선택하겠습니다.

참고 자료 1 : https://mattmk.tistory.com/31

참고 자료 2 : https://americanopeople.tistory.com/378


3. UUID 적용하기

Java에서의 UUID를 사용해보겠습니다.

자바에서는 java.util.UUID의 randomUUID로 4버전의 UUID를 생성할 수 있습니다.

import java.util.UUID;

public class UUIDService {
    public static UUID generateUUID() {
        return java.util.UUID.randomUUID();
    }
}

그러면 위에서 본 UUID처럼 f1c0e2e0-b2af-43e1-b4c9-3d017215c657가 생성됩니다.

이때, 위의 UUID에서 43e1에서 맨 앞의 4는 UUID의 버전을 나타냅니다.

따라서 4버전의 UUID가 정상적으로 생성되었음을 알 수 있습니다.

4. UUID 컬럼 생성하기

UUID가 겹칠 확률은 매우 매우 낮지만, unique = true를 설정하여 unique 값이 아닌 경우에는 보드를 생성할 수 없도록 설정을 하였습니다.

그리고 unique로 설정하면, 이전의 게시글에서도 볼 수 있듯이 index도 생성해주기 때문에 uuid로 보드를 찾을 때에도 조회 성능이 향상될 것입니다.

https://velog.io/@da_na/실행계획-MySQL-데이터-베이스의-실행계획Explain을-통한-유저-테이블-인덱스-설계수정-2

이때, UUID 컬럼은 columnDefinition = "BINARY(16)"으로 생성하였습니다.

UUID가 32자리의 숫자로 이루어져 있어서, CHAR(32)라고 정의할 수도 있지만, BIGINT(8바이트)보다 4배가 크기 때문에 Binary형태로 변환하면 크기를 절반 줄일 수 있고 Binary(16)으로 설정하였습니다.

참고 자료 : https://chanos.tistory.com/entry/MySQL-UUID를-효율적으로-활용하기-위한-노력과-한계

아래의 코드는 Board 테이블에 대한 코드입니다.

public class Board extends BaseTimeEntity {

    @Id
    @GeneratedValue
    @Column(name = "board_id")
    private Long id;

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;

    @Column(length = 100, nullable = false)
    private String title;

    @Column(columnDefinition = "boolean", nullable = false)
    private boolean isPublic;

    private LocalDateTime fixedDate;

    @Column(nullable = false)
    private TAG tag;

    @Column(length = 1000)
    private String description;

    @Column(columnDefinition = "TEXT")
    private String contents;

    @ColumnDefault("0")
    private Long heartCnt;

    @ColumnDefault("0")
    private Long viewCnt;

    @ColumnDefault("0")
    private Long shareCnt;
}


2️⃣ 결론

  • 이전까지는 당연히 key값은 Auto Increment한 키일거야!라는 생각을 하고 있었고, UUID를 사용하면 길이가 길어지기 때문에 성능을 고려하면 board_id로 해야 되지 않을까하는 우려가 있었습니다. 그러나, 실제로 기능을 개발하면서 여러 가지의 상황을 비교하고 문제점을 파악하였을 때, 성능도 중요하지만 보안과 사용자의 측면에서 바라볼 때 UUID가 더 적합함을 파악하고 도입하게 되었습니다. 이처럼 이론으로 배울 때보다 실제 서비스를 구축해나가면서 운영에 대한 생각을 하게 되는 시각을 배우게 되었습니다.
profile
컴퓨터공학과 학생이며, 백엔드 개발자입니다🐰

0개의 댓글