요즘 밤에 틈이 나면 수련을 위한 개인 프로젝트를 진행중인데, 구현하는 과정 중에 클라이언트를 식별하기 위해 세션ID를 생성하는 기능이 필요해졌다. 이 기능을 직접 구현하기가 너무 너무 귀찮아서 자바에서 기본으로 제공하는 UUID 클래스를 사용하기로 마음 먹었다.
UUID의 사용법은 간단한데, 조금 마음에 안 들었던 건 UUID를 문자열로 변환할 때 그 길이가 다소 길다는 것이었다. 아래 코드처럼 UUID를 문자열로 변환하면 32자 ('-' 포함하면 36자)이다.
UUID uuid = UUID.randomUUID();
System.out.println(uuid.toString()); // --> "bf6f75a4-a1e9-4c0b-9b17-e9b6b5c0e5ed"
세션ID로 쓰기에 너무 길어서 이걸 조금이라도 줄여보고 싶은 마음에 16진수가 아닌 알파벳 대소문자와 특수문자 몇 개를 더 보태 64진수로 표현하는 코드를 만들어 보았다. 사실 만들었다기 보다는 자바에 있는 변환 코드를 복사해서 약간 보탰다.
public static String toUnsignedString(long i, int shift) {
char[] buf = new char[64];
int charPos = 64;
int radix = 1 << shift;
long mask = radix - 1;
long number = i;
do {
buf[--charPos] = digits[(int) (number & mask)];
number >>>= shift;
} while (number != 0);
return new String(buf, charPos, (64 - charPos));
}
final static char[] digits = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '_', '*' // '.', '-'
};
toUnsignedString() 메서드에서 i는 변환할 문자열을, shift는 변환 대상 진수를 위한 값을 나타낸다. 예를 들어, shift가 1이면 2진수를, shift가 3이면 8진수를, shift가 6이면 64진수를 의미한다. 여기서 코드를 보면서 눈치를 챈 사람도 있겠지만, 숫자 1을 왼쪽으로 shfit 만큼 비트 이동했을 때 나오는 숫자가 변환 대상 진수가 된다.
실제 toUnsignedString()을 이용해서 UUID를 문자열로 변환하면 아래와 같이 10글자를 줄일 수 있는 것을 알 수 있다.
UUID uuid = UUID.randomUUID();
System.out.println(uuid.toString());
// eda788c4-2187-4e3c-9a53-affe69dd4fb5 : 32자 ('-' 포함 36자)
System.out.println(toUnsignedString(uuid.getMostSignificantBits(), 6) +
toUnsignedString(uuid.getLeastSignificantBits(), 6));
// eSDycgxxQUY9FjH*VFTk_R (22자)