UUID (Universally Unique IDentifier)
UUID 는 네트워크 상에서 고유성이 보장되는 ID 를 만들기 위한 표준 규약이다.
왜 UUID 를 ID 로 사용할까?
ID 를 넣을때 auto increment 를 사용해 자동으로 숫자가 올라가는 방식으로 사용하는 경우가 있다. ex) 1, 2, 3, 4
이렇게하더라도 노출이 안되는 부분들도 있겠지만, 예를들어 상세조회나 업데이트, 삭제하기 등을 사용할때 파라미터에 ID 를 넣어 주고받는다. 이때 ID 숫자가 노출되는데 이는 유추할수 있어 보안상의 위험을 초래한다.
UUID 에는 여러가지 버전이 있는데 그 중에서 많이 사용하는 1, 4 버전에 대해 알아본다.
UUID v1
UUID 는 공통적으로 128비트의 숫자이며, 32자리의 16진수로 표현된다.
8-4-4-4-12 글자마다 하이픈을 집어넣어 5개의 파트로 구분된다.
ex) 57a90ca0-fd7d-11ee-8517-31e52dfb29e3
이름 | 길이(바이트) | 길이(16진수) | 내용 |
time_low | 4 | 8 | 시간의 low 32비트를 부여하는 정수 |
time_mid | 2 | 4 | 시간의 middle 16비트를 부여하는 정수 |
time_hi_and_version | 2 | 4 | 최상위 비트에서 4비트 "version", 그리고 시간의 high 12 비트 |
clock_seq_hi_and_res clock_seq_low |
2 | 4 | 최상위 비트에서 1-3비트, 그리고 13-15비트 클럭 시퀀스 |
node | 6 | 12 | 48비트 노드 id |
UUID v1 은 호스트 컴퓨터의 MAC 주소와 현재 날짜 및 시간의 조합 등을 사용해 생성된다.
즉 동일한 컴퓨터에서 동시에 생성하지 않는 한 완전히 고유한 ID 를 얻을 수 있음이 보장된다.
하지만 이렇게 보장된 고유성은 익명성을 희생한다. UUID v1 은 시간과 MAC 주소를 가지고 있기 때문에 누군가가 생성된 시간과 장소를 식별할 수 있다.
UUID v4
UUID v4 도 마찬가지로 128비트의 숫자이며 32자리의 16진수로 표현된다.
ex) 4df9bf0a-3bdf-4cd2-95bc-f6b8be820939
UUID v4 는 훨씬 간단하다. UUID v4 를 구성하는 비트는 고유한 논리 없이 무작위로 생성된다. 이 때문에 UUID 를 보면 소스에 대한 정보를 식별할 수 있는 방법이 없다.
그러나 UUID 가 중복될 가능성이 있다. 하지만 이에대해 걱정할 필요는 없다.
가능한 조합의 수가 너무 많기 때문이다. (2^128)
수년동안 매초마다 수조개의 ID 를 생성하지 않는 한 복제본을 생성하는 것은 현재 불가능하다.
하지만 중요한 애플리케이션의 경우(은행, 의료) UUID v4 의 충돌을 방지하기 위해 고유성 제약 조건을 추가해야 한다.
비슷한 UUID v4 만들어보기
시간기반 필드와 노드 부분을 제외해 간단하게 uuid v4 를 만들어 본 코드이다.
class UUIDGenerator {
constructor() {}
// 자리수에 맞게 16진수 생성
createHex(digit) {
// 주어진 자릿수에 해당하는 최대 16진수 값을 계산 ex) digit 이 4라면 16^4 - 1 을 계산
const maxHex = 16 ** digit - 1;
// 0 부터 maxHex 까지의 랜덤 정수 생성, Math.random() 은 0 이상 1 미만의 랜덤수를 생성하고
// 이를 maxHex + 1 로 곱하고 Math.floor 를 통해 소수점 아래를 버린다.
const hex = Math.floor(Math.random() * (maxHex + 1));
// 생성된 정수를 16진수 문자열로 반환하고, 문자열의 길이가 지정된 자릿수에 미치지 못하면 왼쪽을 0으로 채워 길이를 맞춘다.
return hex.toString(16).padStart(digit, "0");
}
generate() {
const part1 = this.createHex(8);
const part2 = this.createHex(4);
// 4 는 uuid 의 버전을 나타냄
const part3 = "4" + this.createHex(3);
// 8, 9, a, b 중 하나를 반환받아 3자리 랜덤 16진수를 추가한다.
const part4 = this.createVariant() + this.createHex(3);
const part5 = this.createHex(12);
let uuid = `${part1}-${part2}-${part3}-${part4}-${part5}`;
return uuid;
}
createVariant() {
const variants = ["8", "9", "a", "b"];
const index = Math.floor(Math.random() * 4);
return variants[index];
}
}
const uuidGenerator = new UUIDGenerator();
console.log(uuidGenerator.generate());
마무리
무엇을 사용할지 모르겠다면 v4 를 일반적으로 선택하는 것이 좋다. 충분히 좋고 충돌가능성은 거의 없다.
하지만 실제로 UUID 가 생성된 날짜와 컴퓨터에 대한 표시를 제공하려면 UUID v1 이 적합하다.
마지막으로 일반 ID 와 UUID 에 대해서도 언급하려고 한다.
일반 ID 는 유추할 수 있다는 단점이 있고, UUID 는 이 단점을 보완한다.
그렇다고 UUID 는 완벽하지는 않다. 버전마다 특징이 있고 문제점이 있다.
또 일반 정수형 ID 의 경우는 메모리소비량이 적지만, UUID 는 32자리의 16진수로 표현되기에 메모리소비량이 더욱 많다.
일반적으로 보안과 성능은 반비례한다고 생각한다.
프로덕트의 요구사항을 고려해 적절히, 효율적으로 사용하고 개선하는 것이 개발자라고 생각한다.