dev zone/database

[Primary Key 생성 전략] #7. 기타 방식 (Hash ID, Nano ID 등)

voco choe 2025. 2. 12. 00:40

 

 

 


 

# 0. 들어가며

 

UUID나 Snowflake ID 외에도 고유한 식별자(Unique ID)를 생성하는 다양한 방법이 있습니다. 이번 글에서는 Hash 기반 ID, Nano ID에 대해 알아보았습니다.

 

 


 

# 1. Hash 기반 ID 사용 방법

 

Hash 기반 ID는 특정 데이터를 고유한 해시 값으로 변환하여 사용합니다. 이 방식은 기존 데이터(예: 이메일, 사용자명 등)를 기반으로 유니크한 ID를 생성할 때 유용합니다.

 

📌 해시 함수란?

해시(Hash) 함수는 입력 값을 일정한 길이의 고유한 문자열로 변환하는 함수입니다. 같은 입력 값 → 같은 해시 값이 나오며, 출력 값은 충돌 가능성이 매우 낮습니다. 비밀번호 저장, 데이터 무결성 검사, 유니크 ID 생성 등 다양한 용도로 사용됩니다. 

 

 

📌 예제 코드 ( SHA-256을 사용한 해시 기반 ID 생성 (Java) )

 

public static String generateSHA256Hash(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] hash = md.digest(input.getBytes(StandardCharsets.UTF_8));
            
            // 바이트 배열을 16진수 문자열로 변환
            BigInteger number = new BigInteger(1, hash);
            StringBuilder hexString = new StringBuilder(number.toString(16));
            
            // 64자리 길이를 맞추기 위해 앞에 0을 추가
            while (hexString.length() < 64) {
                hexString.insert(0, '0');
            }
            
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("해시 알고리즘 오류", e);
        }
}

 

 

 Hash 기반 ID의 장점

 

기존 데이터로부터 유니크한 ID 생성 가능합니다


별도의 ID 생성 시스템이 필요 없습니다


암호화 수준이 강한 해시(SHA-256 이상)는 보안성이 높습니다

 

Hash 기반 ID의 단점

 

  길이가 길어질 수 있습니다 (SHA-256: 64자리, SHA-512: 128자리)


  역추적 가능성 (Rainbow Table 공격)

  • 해시 값이 예측 가능하면 공격자가 원래 데이터를 찾을 수 있습니다.
  • 해결 방법: Salt(랜덤 값)를 추가하여 보안 강화

 

 


 

# 2. Nano ID와 같은 짧은 ID 사용 방식

 

Nano ID는 짧고 충돌 가능성이 낮은 유니크한 문자열을 생성하는 방법입니다. UUID보다 더 짧고, 읽기 쉽고, URL-safe한 형식으로 ID를 생성할 수 있습니다

 

📌 예제 코드 ( Nano ID 생성 코드 (Python) )

from nanoid import generate

nano_id = generate(size=21)  # 기본 21자
print(nano_id)  # 예: "V1StGXR8_Z5jdHi6B-myT"

 

 

Nano ID의 장점

 

UUID보다 짧고, 사람이 읽기 쉽습니다.


URL-safe (특수 문자 없이 안전한 ID 생성 가능)


속도가 매우 빠름니다 (UUID보다 60% 이상 빠름)

 

Nano ID의 단점

 

  분산 환경에서 충돌 가능성 있습니다

 

  특정 길이를 유지해야 합니다

 

  랜덤성이 높아 정렬이 어렵습니

 

 

 


 

# 3. 특정한 상황에서 유리한 이유

 

Hash 기반 ID가 유리한 경우

 

  • 기존 데이터(이메일, 사용자명 등)로부터 유니크한 ID를 생성할 때
  • 보안이 중요한 환경에서 데이터 무결성을 유지해야 할 때
  • 별도의 ID 생성 시스템 없이도 유니크한 값이 필요할 때

 

Nano ID가 유리한 경우

 

  • 짧고 사람이 읽기 쉬운 ID가 필요할 때
  • URL-safe한 ID가 필요할 때
  • 빠른 속도로 고유한 ID를 생성해야 할 때

 

UUID나 Snowflake 대신 사용하면 좋은 경우

 

  • UUID가 너무 길고 가독성이 나쁠 때
  • Snowflake 같은 정렬 가능한 ID가 필요 없을 때

 

 


 

# 4. 해결 방법 (Hash ID, Nano ID 등의 단점 보완 방법)

 

✅ 해결 방법 1: Salt 추가로 보안 강화 (Hash ID의 Rainbow Table 공격 방지)

 

해커가 미리 계산된 해시 값을 통해 원래 데이터를 역추적하는 것을 방지할 수 있습니다.

 

import hashlib
import os

def generate_salted_hash(input_value):
    salt = os.urandom(16).hex()  
    hash_value = hashlib.sha256((salt + input_value).encode()).hexdigest()
    return f"{salt}:{hash_value}"

print(generate_salted_hash("user@example.com"))

 

 

✅ 해결 방법 2: Nano ID의 충돌 문제 해결 (특정 패턴 적용)

 

특정 길이와 패턴을 유지하면서 충돌 가능성을 줄일 수 있습니다

 

from nanoid import generate

def generate_custom_nanoid():
    return generate(alphabet="0123456789ABCDEF", size=16)

print(generate_custom_nanoid())  # "A1B2C3D4E5F67890"

 

 

✅ 해결 방법 3: 짧은 Hash ID 사용 (Base58 인코딩 활용)

 

Base58을 사용하면 길이를 줄이면서도 유니크한 해시 값을 유지할 수 있습니다. 

 

import base58
import hashlib

def generate_base58_hash(input_value):
    hash_value = hashlib.sha256(input_value.encode()).digest()
    return base58.b58encode(hash_value).decode()[:16]  # 16자리로 축소

print(generate_base58_hash("user@example.com"))

 

 

 


 

# 5. 결론 

 

기존 데이터(이메일, 사용자명) 기반으로 유니크한 ID 만들기 → Hash ID 
짧고 URL-safe한 유니크 ID 만들기 → Nano ID 
UUID보다 가독성 좋은 ID가 필요 → Base58 변환 
정렬이 필요 없고, 빠른 랜덤 ID가 필요 → Nano ID

 

 

 

 

 

🔹 SHA-256 및 MD5 공식 문서 (NIST, RFC 1321)
📌 https://csrc.nist.gov/publications/detail/fips/180-4/final

🔹 Nano ID 공식 문서 (GitHub 프로젝트)
📌 https://github.com/ai/nanoid

🔹 Base58 인코딩 공식 문서 
📌 https://en.bitcoin.it/wiki/Base58Check_encoding