Spring

Java에서 영숫자 UUID 문자열 생성

기록만이살길 2022. 11. 28. 13:34
반응형

1. 개요

GUID(Globally Unique Identifier)라고도 하는 UUID (Universally Unique Identifier)는 실용적인 용도로 고유한 128비트 값입니다. 고유성은 대부분의 다른 번호 매기기 체계와 달리 중앙 등록 기관이나 이를 생성하는 당사자 간의 조정에 의존하지 않습니다 .

이 사용방법(예제)에서는 Java에서 UUID 식별자를 생성하는 두 가지 구현 방법을 살펴봅니다.

2. 구조

UUID의 표준 표현이 뒤따르는 예제 UUID를 살펴보겠습니다.

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

표준 표현은 32개의 16진수(base-16) 숫자로 구성되며 하이픈으로 구분된 5개의 그룹으로 표시되며 8-4-4-4-12 형식으로 총 36자(16진수 32개 및 하이픈 4개)로 표시됩니다. .

Nil UUID는 모든 비트가 0인 특수 형태의 UUID입니다.

2.1. 변형

위의 표준 표현에서 A 는 UUID 의 레이아웃을 결정하는 UUID 변형을 나타냅니다 . UUID의 다른 모든 비트는 변형 필드의 비트 설정에 따라 다릅니다.

변형은 A 의 최상위 3개 비트에 의해 결정됩니다 .

  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로 표시합니다.

2.1. 버전

표준 표현을 다시 보면 B 는 버전 을 나타냅니다 . 버전 필드 는 주어진 UUID의 유형을 설명하는 값을 보유합니다 . 위 예제 UUID 의 버전( B 값)은 4입니다.

UUID 에는 5가지 기본 유형이 있습니다 .

  1. 버전 1(시간 기반): 1582년 10월 15일부터 100나노초 단위로 측정된 현재 타임스탬프를 기반으로 하며 UUID가 생성된 장치의 MAC 주소와 연결됩니다.
  2. 버전 2(DCE – 분산 컴퓨팅 환경): 로컬 시스템의 네트워크 인터페이스에 대한 MAC 주소(또는 노드)와 함께 현재 시간을 사용합니다. 또한 버전 2 UUID는 시간 필드의 낮은 부분을 UUID를 생성한 로컬 계정의 사용자 ID 또는 그룹 ID와 같은 로컬 식별자로 바꿉니다.
  3. 버전 3(이름 기반): UUID는 네임스페이스와 이름의 해시를 사용하여 생성됩니다. 네임스페이스 식별자는 DNS(도메인 이름 시스템), OID(개체 식별자) 및 URL과 같은 UUID입니다.
  4. 버전 4(무작위로 생성됨): 이 버전에서 UUID 식별자는 무작위로 생성되며 생성된 시간이나 이를 생성한 시스템에 대한 정보는 포함하지 않습니다.
  5. 버전 5(SHA-1을 사용하는 이름 기반): 버전 3과 동일한 접근 방식을 사용하여 생성되지만 해싱 알고리즘이 다릅니다. 이 버전은 네임스페이스 식별자 및 이름의 SHA-1(160비트) 해싱을 사용합니다.

3. UUID 클래스

Java에는 UUID를 임의로 생성하거나 생성자를 사용하여 생성하려는 경우 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 클래스를 사용하지 않고 UUID를 생성하기 위한 몇 가지 구현을 살펴보겠습니다.

4. 구현

요구 사항에 따라 구현을 두 가지 범주로 구분할 것입니다. 첫 번째 범주는 고유해야 하는 식별자에 대한 것이며 이를 위해 UUIDv1UUIDv4 가 최상의 옵션입니다. 두 번째 범주에서 주어진 이름에서 항상 동일한 UUID를 생성해야 하는 경우 UUIDv3 또는 UUIDv5 가 필요합니다 .

RFC 4122는 정확한 생성 세부 정보를 지정하지 않으므로 이 기사에서는 UUIDv2 구현을 살펴보지 않습니다.

이제 우리가 언급한 범주에 대한 구현을 살펴보겠습니다.

4.1. 버전 1 및 4

우선 프라이버시가 우려 되는 경우 MAC 주소 대신 랜덤의 48비트 숫자로 UUIDv1 을 생성할 수 있습니다. 이 기사에서는 이 대안을 살펴보겠습니다.

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

private static long get64LeastSignificantBitsForVersion1() {
    long random63BitLong = new 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 least12SignificantBitOfTime = (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);
}

이제 UUIDv4를 생성하는 방법을 살펴보겠습니다. 구현은 난수를 소스로 사용합니다. Java 구현은 충돌 가능성을 줄이기 위해 예측할 수 없는 값을 시드로 사용하여 난수를 생성하는 SecureRandom 입니다.

버전 4 UUID 를 생성해 보겠습니다 .

UUID uuid = UUID.randomUUID();

그런 다음 "SHA-256"과 랜덤의 UUID 를 사용하여 고유 키를 생성해 보겠습니다 .

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

4.2. 버전 3 및 5

UUID는 네임스페이스와 이름의 해시를 사용하여 생성됩니다. 네임스페이스 식별자는 DNS(도메인 이름 시스템), OID(개체 식별자) 및 URL과 같은 UUID입니다. 알고리즘의 의사 코드를 살펴보겠습니다.

UUID = hash(NAMESPACE_IDENTIFIER + NAME)

UUIDv3UUIDv5 의 유일한 차이점 은 해싱 알고리즘입니다. v3은 MD5(128비트)를 사용하고 v5는 SHA-1(160비트)을 사용합니다.

UUIDv3 의 경우 바이트 배열을 사용하고 MD5 해시를 적용하는 UUID 클래스 nameUUIDFromBytes (String namespace, String name) 메서드를 사용합니다 .

따라서 먼저 네임스페이스와 특정 이름에서 바이트 표현을 추출하고 단일 배열로 결합하여 UUID api로 보냅니다.

byte[] nameSpaceBytes = bytesFromUUID(namespace);
byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
byte[] result = joinBytes(nameSpaceBytes, nameBytes);

마지막 단계는 이전 프로세스에서 얻은 결과를 nameUUIDFromBytes() 메서드에 전달하는 것입니다. 이 메서드는 변형 및 버전 필드도 설정합니다.

UUID uuid = UUID.nameUUIDFromBytes(result);

이제 UUIDv5 구현을 살펴보겠습니다 . Java는 버전 5를 생성하기 위한 내장 구현을 제공하지 않는다는 점에 유의해야 합니다.

다시 긴으로 최하위 비트와 최상위 비트를 생성하는 코드를 확인해 보겠습니다 .

private static long getLeastAndMostSignificantBitsVersion5(final byte[] src, final int offset) {
    long ans = 0;
    for (int i = offset + 7; i >= offset; i -= 1) {
        ans <<= 8;
        ans |= src[i] & 0xffL;
    }
    return ans;
}

이제 이름을 사용하여 UUID를 생성하는 메서드를 정의해야 합니다. 이 메서드는 UUID 클래스에 정의된 기본 생성자를 사용합니다.

public static UUID generateType5UUID(String name) {

    try {

        byte[] bytes = name.getBytes(StandardCharsets.UTF_8);
        MessageDigest md = MessageDigest.getInstance("SHA-1");

        byte[] hash = md.digest(bytes);

        long msb = getLeastAndMostSignificantBitsVersion5(hash, 0);
        long lsb = getLeastAndMostSignificantBitsVersion5(hash, 8);
         // Set the version field
        msb &= ~(0xfL << 12);
        msb |= 5L << 12;
        // Set the variant field to 2
        lsb &= ~(0x3L << 62);
        lsb |= 2L << 62;
        return new UUID(msb, lsb);

    } catch (NoSuchAlgorithmException e) {
        throw new AssertionError(e);
    }
}

5. 결론

이 기사에서는 UUID 식별자에 대한 주요 개념과 내장 클래스를 사용하여 식별자를 생성하는 방법을 살펴보았습니다. 그런 다음 다양한 버전의 UUID와 해당 응용 프로그램 범위에 대한 몇 가지 효율적인 구현을 확인했습니다.

항상 그렇듯이 이 기사의 전체 코드는 GitHub에서 사용할 수 있습니다 .

Generic footer banner
참고
  • https://docs.spring.io/spring-framework/docs/current/reference/html
  • https://www.baeldung.com/java-generate-alphanumeric-uuid
반응형