Java/이론 및 문법

[Java] UUID 이해 및 사용방법

adjh54 2023. 3. 9. 23:25
728x170
해당 글에서는 UUID에 대해서 이해하며 구조를 파악하여 이를 활용할 수 있는 방법에 대해서 이해를 돕기 위해 작성한 글입니다.


 

 

1) UUID(Universally Unique Identifier) 정의 및 구조


 

1. UUID 정의


💡 UUID(Universally Unique Identifier)란?

- 범용 고유 식별자를 의미하며 중복이 되지 않는 유일한 값을 구성하고자 할때 주로 사용이 되는
고유 식별자를 의미합니다.
- 주로 세션 식별자, 쿠키 값, 무작위 데이터베이스 키 등에 사용이 됩니다.

 
 

2. UUID 구조


💡 UUID는 16바이트(128비트) 형태의 구조를 가지며 하나의 UUID 길이는 36자리이며 “4개의 하이픈(-)”과 “32개의 16진수 문자열”로 구성이 되어있습니다.

출처: https://www.techtarget.com/searchapparchitecture/definition/UUID-Universal-Unique-Identifier

 

구조 길이 (바이트 /비트) 내용
Low Time 4 / 8 (8자리) 시간의 low 32비트를 부여하는 정수
Mid time 2 / 4 (4자리) 시간의 middle 16비트를 부여하는 정수
Mid time + version 2 / 4 (4자리) 최상위 비트에서 4비트 "version", 그리고 시간의 high 12비트
Clock sequence and variant 2 / 4 (4자리) 최상위 비트에서 1-3비트는 UUID의 레이아웃형식, 그리고 13-15비트 클럭 시퀀스
Node 6 / 12 (12자리) 48비트 노드 id

 

[ 더 알아보기 ]

💡 UUID를 구성하였을 경우 중복이 발생하는 경우는?

- 340,282,366,920,938,463,463,374,607,431,768,211,456개의 다양한 UUID를 사용하기에 한 개의 시스템의 고유 식별자로 중복이 발생하는 경우는 이와 같습니다. 이는 100년 동안 생성했을 때 최소 1개가 중복 및 충돌될 확률이라고 합니다. 🥶
 

범용 고유 식별자 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 범용 고유 식별자(汎用固有識別子, 영어: universally unique identifier, UUID)는 소프트웨어 구축에 쓰이는 식별자 표준으로, 개방 소프트웨어 재단(OSF)이 분산 컴퓨팅

ko.wikipedia.org

 
 
 
 

2) UUID의 버전


💡 UUID는 각각의 버전을 가지고 있고 랜덤한 문자열을 출력할 때 어떠한 기준으로 구성이 되었습니다.

 

1. 요약


💡 UUID를 이용한 버전은 총 5개가 존재합니다. 각각 출시년도에 따라서 버전이 존재합니다.
버전 설명 특징
UUID Version1 해당 버전은 “현재 시간”과 “랜덤한 MAC 주소”를 기반으로 생성됩니다. 유일성이 보장되지만 보안에 취약함.
UUID Version2 해당 버전은 이전 버전 1과 유사하지만 시퀀스 번호 대신 POSIX UID(사용자 ID)를 사용하여 생성됩니다. 현재는 거의 사용하지 않음.
UUID Version3 해당 버전은 해시 함수인 “MD5 해시”를 기반으로 이름과 네임스페이스에 대한 조합으로 생성됩니다. 암호화 해시 함수를 사용하여 생성하므로 보안성이 높음.
이름과 네임스페이스가 같으면 같은 UUID가 생성됩니다.
UUID Version4 해당 버전은 “랜덤한 값”을 기반으로 생성됩니다. 보안성이 높고 생성속도가 빠릅니다.
UUID Version5 해당 버전은 이전 버전 3과 유사하지만 “SHA-1 해시”를 사용하여 생성됩니다.  보안에 취약하다는 단점이 있습니다.

 
 

2. import java.util.UUID 메서드 종류


메서드 설명
static UUID randomUUID() 무작위 UUID를 생성합니다.
static UUID fromString(String uuid) 주어진 UUID 문자열로부터 UUID를 생성합니다.
long getLeastSignificantBits() 이 UUID의 가장 낮은 64비트를 반환합니다.
long getMostSignificantBits() UUID의 가장 높은 64비트를 반환합니다.
int compareTo(UUID val) UUID와 주어진 UUID를 비교합니다.
boolean equals(Object obj) UUID와 주어진 객체가 같은지 여부를 반환합니다.
String toString() UUID를 문자열로 반환합니다.

 
 

3. UUID Version 1


💡 해당 버전은 “현재 시간”과 “랜덤한 MAC 주소”를 기반으로 생성됩니다.
💡 유일성이 보장되지만 보안에 취약하다는 단점이 있습니다.
import java.util.UUID;

/**
 * [공통함수] UUID v1을 생성하여 반환합니다.(MAC Address, TimeStamp 조합)
 *
 * @return
 */
public static UUID generateType1UUID() {
    long most64SigBits = get64MostSignificantBitsForVersion1();
    long least64SigBits = get64LeastSignificantBitsForVersion1();
    return new UUID(most64SigBits, least64SigBits); // 62dd98f0-bd8e-11ed-93ab-325096b39f47
}

/**
 * [MAC Address] MAC 주소 대신에 임의의 48비트 숫자를 생성합니다.(보안 우려로 이를 대체합니다)
 *
 * @return
 */
private static long get64LeastSignificantBitsForVersion1() {
    Random random = new Random();
    long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL;
    long variant3BitFlag = 0x8000000000000000L;
    return random63BitLong | variant3BitFlag;
}

/*
 * [TimeStamp] 타임스템프를 이용하여 64개의 최상위 비트를 생성합니다.
 */
private static long get64MostSignificantBitsForVersion1() {
    final long currentTimeMillis = System.currentTimeMillis();
    final long time_low = (currentTimeMillis & 0x0000_0000_FFFF_FFFFL) << 32;
    final long time_mid = ((currentTimeMillis >> 32) & 0xFFFF) << 16;
    final long version = 1 << 12;
    final long time_hi = ((currentTimeMillis >> 48) & 0x0FFF);
    return time_low | time_mid | version | time_hi;
}
[ 더 알아보기 ]

💡 MAC Address(Media Access Control)

- 네트워크 인터페이스를 식별하기 위한 고유한 주소입니다. 이는 전 세계에서 유일하며, 일반적으로 12자리 16진수로 나타납니다.

 
 
 

4. UUID Version 2


💡 해당 버전은 이전 버전 1과 유사하지만 시퀀스 번호 대신 POSIX UID(사용자 식별자)를 사용하여 생성됩니다.
💡 현재는 거의 사용하지 않는 버전입니다.

 
 
 
 

5. UUID Version 3


💡 해당 버전은 해시 함수인 “MD5 해시”를 기반으로 이름과 네임스페이스에 대한 조합으로 생성됩니다.
💡 암호화 해시 함수를 사용하여 생성하므로 보안성이 높습니다.
💡 단, 이름과 네임스페이스가 같으면 동일한 UUID가 생성됩니다.
import java.util.UUID;

/**
 * [공통함수] UUID v3를 생성합니다.
 *
 * @return
 */
public static UUID generateType3UUID() {
    String name = "example name";
    UUID uuid3 = UUID.nameUUIDFromBytes(name.getBytes());
    System.out.println("Version 3 UUID: " + uuid3);  // Version 3 UUID: 4dfc6b14-7213-3363-8009-b2
    return uuid3;
}
[참고] MD5 해시
 

MD5 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. MD5(Message-Digest algorithm 5)는 128비트 암호화 해시 함수이다. RFC 1321로 지정되어 있으며, 주로 프로그램이나 파일이 원본 그대로인지를 확인하는 무결성 검사 등에

ko.wikipedia.org

 
 
 

6. UUID Version 4


💡 해당 버전은 “랜덤한 값”을 기반으로 생성됩니다.
💡 보안성이 높고 생성속도가 빠르다는 장점을 가지고 있으며 대중적으로 많이 사용되는 UUID 버전입니다.
import java.util.UUID;
/**
 * [공통함수] UUID v4를 생성합니다.
 *
 * @return
 */
public static UUID generateType4UUID() {
    // 버전 4 UUID 생성하기
    UUID uuid4 = UUID.randomUUID();
    System.out.println("Version 4 UUID: " + uuid4); // Version 4 UUID: c48b2aef-9d79-44fe-bd97-46fd31361069
    return uuid4;
}

 
 

7. UUID Version 5


💡 해당 버전은 이전 버전 3과 유사하지만 “SHA-1 해시”를 사용하여 생성됩니다.
💡 SHA-1 해시에 대해서 보안 취약점이 발견이 되었다는 단점이 있습니다.
import java.util.UUID;

String name = "example_name";
UUID namespace = UUID.fromString("00000000-0000-0000-0000-000000000000");
UUID uuid = createUUIDv5(name, namespace); // 함수를 호출합니다

/**
 * [공통함수] UUID v5를 사용하여 UUID를 생성합니다.
 *
 * @param name
 * @param namespace
 * @return
 */
public static UUID createUUIDv5(String name, UUID namespace) {

    UUID uuid = createUUIDv5(name, namespace); // 함수를 호출합니다

    byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
    byte[] namespaceBytes = namespace.toString().getBytes(StandardCharsets.UTF_8);
    byte[] bytesToHash = new byte[nameBytes.length + namespaceBytes.length];

    System.arraycopy(nameBytes, 0, bytesToHash, 0, nameBytes.length);
    System.arraycopy(namespaceBytes, 0, bytesToHash, nameBytes.length, namespaceBytes.length);

    try {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] hash = md.digest(bytesToHash);
        hash[6] &= 0x0f;
        hash[6] |= 0x50;
        hash[8] &= 0x3f;
        hash[8] |= 0x80;
        return UUID.nameUUIDFromBytes(hash);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("Error creating UUID v5", e);
    }
}

 
 
 

[참고] SHA-1 보안 취약점 관련 기사
 

구형 암호화 기술(SHA-1)로 암호화 통신시 보안에 취약

구글은 https를 적용하더라도 구형 암호화 기술(SHA-1)을 이용하면 보안에 문제가 발생한다고 발표하였다.https의 핵심 기술 가운데 하나인 SHA(Secure Hash Algorithm, 안전한 해시 알고리즘)의 보안 취약

www.cisp.or.kr

 
 
 
 
 
 

2) UUID 활용


💡 일반적으로 보안성이 높고 생성속도가 빠른 “UUID Version 4”를 가장 많이 사용하기에 이를 기반으로 구성하는 방법 및 응용방법에 대해 공유합니다.

 
 

1. UUID Version 4 기본적인 타입


💡 일반적으로 UUID v4를 문자열로 사용하는 방법입니다.
import java.util.UUID;

String uuid = UUID.randomUUID().toString();

 
 
 

2. UUID Version 4 하이픈(-)을 제거한 구성


💡 UUID 총 길이는 36개이며, 하이픈을 제거하면 총 4개의 하이픈이 제거되고 32개의 랜덤 한 문자열로 구성이 됩니다.
💡단, 이 하이픈을 제거하면 국제 기준의 표준이 되는 UUID에 대해 유일성을 보장받을 수 없는 부분이 있습니다. 그래도 상당히 낮은 확률입니다.
import java.util.UUID;

/**
 * [공통함수] UUID 생성 함수
 *
 * @MethodName : makeUuid
 * @param isHyphen : 하이픈 포함 여부
 * @return String
 */
public static String makeUuid(boolean isHyphen) {

    String result = "";

    if (isHyphen) {
        result = UUID.randomUUID().toString();
    } else {
        result = UUID.randomUUID().toString().replace("-", "");
    }

    return result;
}

 

[참고] 랜덤하게 번호를 구성하는 방법에 대해 궁금하시면 이전에 작성하신 글을 참고하시면 됩니다
 

[Java/Short] 랜덤 숫자 사용방법 : Math.random()

해당 페이지에서는 자주 사용하는 개발 패턴으로 “특정 범위 안에서의 랜덤 한 숫자 출력 방법”하는 방법에 대해서 공유합니다. 1) 최대값만 지정된 랜덤 숫자 💡 기본 최소값은 0이며 랜덤

adjh54.tistory.com

 
 

[참고] 기타 블로그를 작성하면서 참고한 사이트입니다.

https://www.baeldung.com/java-uuid
 

 

 

💡 [참고] 해당 소스코드는 아래 Repository의 UuidUtils.java 내에서 확인이 가능합니다.
 

blog-codes/spring-boot-common/src/main/java/com/adjh/springbootcommon/commons/utils/UuidUtils.java at main · adjh54ir/blog-code

Contributor9 티스토리 블로그 내에서 활용한 내용들을 담은 레포지토리입니다. Contribute to adjh54ir/blog-codes development by creating an account on GitHub.

github.com

그리드형