!
입사한지 5개월 째 되던 때엿다. 쿠폰에 대한 기획이 회사에서 나왔다.
대표님께서 비트연산을 할 줄 아셨냐고 물어보셨고,
솔직하게 모른다고 답변을 했다. 뭐 그런 연산자가 있는건 알고있지만
써먹을 데가 있어야지!
대표님께서 지속적으로 많은 도움을 주셔서 처음 해보는 거지만 잘 해낸 것 같고
지금도 여전히 쿠폰이 발생되어 쓰이고 있음에 큰 만족감을 느끼고 있다.
쿠폰이라 함은 사용자가 딱 한번만 쓸 수 있는 형태여야 한다. 즉 중복이 되면 안되는 것이다.
같은 쿠폰으로 여러번 등록을 하면 안되니까.
쿠폰이 만들어질때의 epoch time 과 쿠폰의 발행 index 값을 그 데이터 재료로 써보자
먼저 영 대문자로 이루어진 8자리 쿠폰의 경우의 수는 얼마 일까?
AAAA-AAAA ~ ZZZZ-ZZZZ 까지이다.
한 자리가 26가지를 가지므로 26의 8 승 == 208827064576
약 2천억가지수가 만들어진다. 많으면 많을수록 좋긴하다.
그럼 이 2천억 가지수를 표현할 수 있는 데이터의 용량은 얼마가 되어야 할까?
데이터는 비트로 이루어져있다.
저 가지수를 포함하는 비트는 38비트이다. 2의 38승은 십진수로 274877906944 약 2천 7백억에 해당한다.
바이트로 다시 나타내면 4바이트하고도 6비트를 더 써야 한다는 의미이다.
편의상 5바이트로 넉넉하게 잡아두자.
하나의 바이트는 0xff 로 표현할 수가 있다. (f는 0~f까지의 숫자를 16진수로 나타낸 것이다.)
그러므로 우리가 만들 데이터는 다음과 같이 표현할수도 있다 .
0xffffffffff
이 데이터를 요목조목 뜯어보자.
우리는 앞서 시간값을 넣는다고 했는데
초단위로 계산한 epoch 즉 unix time 은 10자리의 정수이다 최고값은 9999999999. 99억 9999만 9999 이다.
이 10자리 정수를 표현하려면 100억이라는 가짓수를 가져야 한다. 이를 바이트로 환산해보자.
4바이트는 32비트이고, 42억이다. 100억을 채우려면 2를 두번 곱해야 한다. 84억은 부족하고 168억까지는 만들어주어야
저 숫자를 데이터로 담아 낼 수 있을 것이다.
32비트에서 곱하기 2를 두번하는 것은 2비트를 더해주는 것으로 우리가 unix time을 담는데 필요한 비트는 34비트이다.
우리는 앞서 5바이트 데이터를 쓰기로 했는데 이는 40비트에 해당한다. (8비트 * 5)
그런데 unix time 데이터를 담는데 34비트를 이미 써버렸다.
나머지 6비트를 채우기 위해 의미 있는 숫자를 써보도록 하자.
사실 이미 34비트 숫자가 초단위로 고유한 숫자값을 가지고 있기때문에 초단위로만 생산한다면 중복없이 쿠폰을 생산할수가 있다.
이는 초당 하나씩 생산할 경우 260년이 넘게 쿠폰을 계속 찍어 낼 수 있다는 이야기 이다.
하지만 초당 하나씩 생산하는 것은 컴퓨터에겐 좀 느릴 수 있겠다 싶어
나머지 6비트 64가지의 고유번호를 입력해서 1/64 초만큼씩 생산해도 고유값이 보장되도록 하겠다.
이 6비트 값에는 쿠폰이 생성될때마다 주어지는 인덱스 값 1 부터 시작해서 1 씩 증가한다. 63이 되면 다시 0으로 계산한다.
이미 34 비트가 초단위로 고유값을 보장하고 있으므로
만약 위와 같이 데이터를 짠다면 동일한 초내에 63번까지는 고유값을 보장 할 수 있는 셈이다.
그래서 unix time 10자리 초단위가 아닌 13자리 마이크로 초 단위로 데이터를 표현하게 되면
굉장히 빠른 속도로 생성함에도 중복없이 계속 발행될수 있는 쿠폰을 발행할 수가 있게 되는 것이다.
이제 이 부분에서 비트연산이 활용되게 되는데
단순 10진수로 변환해서 계산해도 상관은 없지만 굉장히 보기 불편한 면이 없잖아 있다.
16진수로 표현하게 되면 비트 연산을 하는데 굉장히 유리해진다.
0xffffffffff - 5 byte
ff 는 255의 숫자값으로 한 바이트를 표현한다고 보면 된다.
0xff 앞부분을 를 고유번호
ffffffff 뒷부분을 시간값으로 계산하겠다.
유닉스타임의 경우 빅엔디안이다. 노드의 경우는 리틀 엔디안이다. 이에 엔디안 변환을 해주어야 한다. 엔디안에 대한
이야기는 추후 포스팅에서 다시한번 정리해야겠다. 왜냐하면 나도 기억이 잘 안난다.
자바스크립트에서 비트 연산은 << 와 >> 로 할 수 있다. 비트 연산 자 뒤에 오는 숫자 값은 비트값으로 좌우로 몇번을 움직이는지를
나타내는 것으로 << 는 *2 와 같고 >> 는 /2 와 같다.
예를 들어
왼쪽으로 ff가 5번 움직이려면 5바이트 만큼이다. 즉 40비트 만큼 움직여야 하므로
0xff << 40 와 같이 연산을 준다
이 연산을 거치게되면 ff을 5번 왼쪽으로 이동시키는 것과 같기 때문에 왼쪽에 00 데이터가 5번쌓인다.
0xff0000000000
비트 연산을 통해서 쉽게 데이터를 원하는 바이트에 위치시킬 수 있다는 점이 멋지다!
자 우리는 6비트 쿠폰 순서 값과 34비트 unix 타임 값을 데이터로 담아 낼 것이다 .
총 5바이트 짜리 데이터는 16진수로 되어있는데 이를 26진수로 나타내고 26가지의 영 대문자에 매핑하면
영 대문자 8자리 쿠폰을 만들어 내는 것이다.
2018년 7월 1일 어느 시간대의 유닉스 타임 값이다 . 1530448628
그리고 이 쿠폰은 현재 35번째로 만들어 지고 있다.
자 쿠폰으로 사용할 데이터는 준비되었다.
먼저 35라는 데이터를 한 바이트로 표현하면 ?
0x23 이다.
그런데 앞서 unix time이 34비트를 이용하기 때문에 바이트 단위가 맞질 않는다. 조금 번거롭지만 비트로 변환하자.
0b100011
6비트를 차지하고 있는 데이터이다.
나머지 34비트를 넣어줄 수 있게 다음과 같이 연산한다.
0b100011 << 34
0b100011 0000000000000000000000000000000000
꾸에엑 엄청 길지만 34비트 데이터가 들어갈 수 있는 공간이 마련되었다.
유닉스 타임의 2진수는 다음과 같다
0b000000 0001011011001110001100101011110100
데이터를 붙이는 방법은?
Or 연산을 해주면 된다.
Or 연산을 할때 반대쪽이 항상 0 이므로 기존의 데이터가 아무런 가감없이 붙기때문이다.
Or연산자는 | 이다.
0b 100011 0000000000000000000000000000000000
|
0b 000000 0001011011001110001100101011110100
=
0b 100011 0001011011001110001100101011110100
이게 바로 쿠폰데이터이다.
이 데이터를 26으로 나누는데 나누는 값의 나머지 마다 영문자를 하나 고른다.
26으로 나오게 되면 0~25의 숫자가 나오기때문이다.
26을 8번 나눠도 충분히 숫자가 남기때문에 계속해서 만들수 있다.
위 사항을 잘 숙지한다면 코드로도 충분히 나타 낼 수 있다.
해당 데이터에 남들이 예측할 수 없도록 한번 더 해시함수를 통하게 하는 등의 작업을 거쳐서
쿠폰데이터를 해석 할 수 없게끔 만들수도 있을 것이다.