1. 개요

GUID(Globally Unique Identifier)라고도 하는 UUID (Universally Unique Identifier) 는 모든 실용적인 목적을 위해 고유한 128비트 길이 값을 나타냅니다 . UUID의 표준 표현은 16진수(옥텟)를 사용합니다.

123e4567-e89b-12d3-a456-556642440000

UUID는 16진수(각각 4자)와 4개의 "-" 기호로 구성되며 길이는 36자입니다.

Nil UUID는 모든 비트가 0으로 설정되는 특수한 형태의 UUID입니다.

이 예제에서는 Java UUID 클래스를 살펴보겠습니다 . 먼저 클래스 자체를 사용하는 방법을 살펴보겠습니다. 그런 다음 다양한 유형의 UUID와 Java에서 UUID를 생성하는 방법을 살펴보겠습니다.

2. UUID 클래스

UUID 클래스에는 단일 생성자가 있습니다.

UUID uuid = new UUID(long mostSignificant64Bits, long leastSignificant64Bits);

이 생성자를 사용하려면 두 개의 긴 값을 제공해야 합니다. 그러나 UUID에 대한 비트 패턴을 직접 구성해야 합니다.

편의상 UUID를 생성하는 세 가지 정적 메서드가 있습니다.

이 첫 번째 방법은 지정된 바이트 배열에서 버전 3 UUID를 만듭니다.

UUID uuid = UUID.nameUUIDFromBytes(byte[] bytes);

두 번째로 randomUUID() 메서드는 버전 4 UUID를 생성합니다. 이것은 UUID를 생성하는 가장 편리한 방법입니다 .

UUID uuid = UUID.randomUUID();

세 번째 정적 메서드는 주어진 UUID의 문자열 표현이 주어지면 UUID 객체를 반환합니다.

UUID uuid = UUID.fromString(String uuidHexDigitString);

이제 UUID가 어떻게 구성되는지 살펴보겠습니다.

3. 구조

UUID의 예를 살펴보겠습니다.

123e4567-e89b-42d3-a456-556642440000
xxxxxxxx-xxxx-Bxxx-Axxx-xxxxxxxxxxxx

3.1. UUID 변형

A 는 UUID의 레이아웃을 결정하는 변형을 나타냅니다. UUID의 다른 모든 비트는 변형 필드의 비트 설정에 따라 다릅니다. 변형은 A의 최상위 세 비트에 의해 결정됩니다.

  MSB1    MSB2    MSB3
   0       X       X     reserved (0)
   1       0       X     current variant (2)
   1       1       0     reserved for Microsoft (6)
   1       1       1     reserved for future (7)

언급된 UUID에서 A 의 값 은 "a"입니다. "a"(=10xx)에 해당하는 이진법은 변형을 2로 표시합니다.

3.2. UUID 버전

B 는 버전을 나타냅니다. 언급된 UUID( B 값)의 버전은 4입니다.

Java는 UUID의 변형 및 버전을 가져오는 방법을 제공합니다.

UUID uuid = UUID.randomUUID();
int variant = uuid.variant();
int version = uuid.version();

변형 2 UUID에는 시간 기반(UUIDv1), DCE Security(UUIDv2), 이름 기반(UUIDv3 및 UUIDv5), 랜덤(UUIDv4)의 5가지 버전이 있습니다.

Java는 v3 및 v4에 대한 구현을 제공하지만 모든 유형의 UUID를 생성하기 위한 생성자 도 제공합니다 .

UUID uuid = new UUID(long mostSigBits, long leastSigBits);

4. UUID 버전

4.1. 버전 1

UUID 버전 1은 UUID가 생성된 장치의 MAC 주소와 연결된 1582년 10월 15일부터 100나노초 단위로 측정된 현재 타임스탬프를 기반으로 합니다.

개인 정보가 우려되는 경우 MAC 주소 대신 랜덤의 48비트 숫자로 UUID 버전 1을 생성할 수 있습니다. 이 기사에서는 이 대안을 살펴보겠습니다.

먼저 64개의 최하위 및 최상위 비트를 긴 값으로 생성합니다.

private static long get64LeastSignificantBitsForVersion1() {
    Random random = new Random();
    long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL;
    long variant3BitFlag = 0x8000000000000000L;
    return random63BitLong + variant3BitFlag;
}

private static long get64MostSignificantBitsForVersion1() {
    LocalDateTime start = LocalDateTime.of(1582, 10, 15, 0, 0, 0);
    Duration duration = Duration.between(start, LocalDateTime.now());
    long seconds = duration.getSeconds();
    long nanos = duration.getNano();
    long timeForUuidIn100Nanos = seconds * 10000000 + nanos * 100;
    long least12SignificatBitOfTime = (timeForUuidIn100Nanos & 0x000000000000FFFFL) >> 4;
    long version = 1 << 12;
    return 
      (timeForUuidIn100Nanos & 0xFFFFFFFFFFFF0000L) + version + least12SignificatBitOfTime;
}

그런 다음 이 두 값을 UUID의 생성자에 전달할 수 있습니다.

public static UUID generateType1UUID() {

    long most64SigBits = get64MostSignificantBitsForVersion1();
    long least64SigBits = get64LeastSignificantBitsForVersion1();

    return new UUID(most64SigBits, least64SigBits);
}

4.2. 버전 2

Version 2 is based on a timestamp and the MAC address as well. However, RFC 4122 does not specify the exact generation details, so we won't look at an implementation in this article.

4.3. Versions 3 and 5

The UUIDs are generated using the hash of namespace and name. The namespace identifiers are UUIDs like Domain Name System (DNS), Object Identifiers (OIDs), URLs, etc.

UUID = hash(NAMESPACE_IDENTIFIER + NAME)

The only difference between UUIDv3 and UUIDv5 is the Hashing Algorithm — v3 uses MD5 (128 bits), while v5 uses SHA-1 (160 bits).

Simply put, we truncate the resulting hash to 128 bits and then replace 4 bit for the version and 2 bit for the variant.

Let’s generate type 3 UUID:

byte[] nameSpaceBytes = bytesFromUUID(namespace);
byte[] nameBytes = name.getBytes("UTF-8");
byte[] result = joinBytes(nameSpaceBytes, nameBytes);

UUID uuid = UUID.nameUUIDFromBytes(result);

Here, it's important to note that the hex string for the namespace first needs to be converted to a byte array.

Finally, Java doesn’t provide the implementation for type 5. Check our source code repository for the UUIDv5.

4.4. Version 4

The UUIDv4 implementation uses random numbers as the source. The Java implementation is SecureRandom, which uses an unpredictable value as the seed to generate random numbers to reduce the chance of collisions.

Let’s generate version 4 UUID:

UUID uuid = UUID.randomUUID();

Let's generate a unique key using “SHA-256” and a random UUID:

MessageDigest salt = MessageDigest.getInstance("SHA-256");
salt.update(UUID.randomUUID().toString().getBytes("UTF-8"));
String digest = bytesToHex(salt.digest());

5. Conclusion

In this article, we saw how a UUID is structured and which variants and versions there are.

We also learned for which versions Java provides an out-of-the-box implementation and looked at code examples to generate the other versions.

And as always, the source code of implementation is available over on GitHub.

Junit footer banner