23P by kunggom 2022-02-24 | favorite | 댓글 15개

특정한 리소스를 고유하게 지정하는 ID를 어떻게 만들 것인가 하면 보통은 크게 2가지 방법을 주로 쓰는 걸로 압니다. 하나는 DB 테이블의 Primary Key에 Auto Increment를 걸어서 나오는 순차적인 정수값을 그대로 써먹는 것이고, 다른 하나는 랜덤한 128비트 값인 GUID(UUID라고도 함)를 그때그때 생성하여 사용하는 방식입니다.

웹에서 볼 수 있는 수많은 서비스들의 데이터는 RDBMS가 상당수 책임지고 있고, 이런 DBMS의 Auto Increment는 내부적으로 최적화되어 있을 뿐더러 사용하는 개발자 입장에서 이해하기도 예측하기도 쉽고 데이터가 들어온 순서대로 정렬하는 것도 간단합니다. 그저 숫자를 1씩 더해갈 뿐이니까요. 하지만 이 방법은 특정한 경우 보안상 노출되지 않았으면 하는 정보를 외부에 노출할 수 있다거나 (예를 들면 경쟁사에서 우리 서비스의 사용자 숫자 등 주요 지표를 쉽게 눈치챌 수 있음) 또는 분산형 아키텍쳐에서 문제가 될 수 있는 등의 문제점이 있습니다.

GUID를 사용하는 방법은 위와는 특징이 정반대입니다. GUID는 다른 의존성 없이 충돌가능성이 0에 가까운 사실상 고유한 128비트 값을 그때그때 만들어 사용하기 때문에, 분산형 아키텍쳐에서도 아무런 문제가 없을 뿐더러 외부에 다른 유의미한 정보를 뜻하지 않게 흘릴 우려도 없습니다. 그러나 랜덤하게 생성된 값을 RDBMS에 쓰는 것은 성능 저하를 불러올 수 있으며, 또한 그 자체로는 데이터가 들어온 순서대로 정렬하는 것도 불가능합니다. 이러한 약점을 보완하고자 완전한 랜덤이 아니라 시간 정보가 가미되어 불완전한 순차성을 띄는 Timeflake와 같은 것을 사용하는 경우도 있습니다. 제가 직접 써본 적은 없지만, Laravel 같은 프레임워크에서도 이런 방식을 쓴다고 하더라고요.

개인적으로는 지금 다니는 회사에서 Microsoft의 Office 365나 Graph API 등 GUID를 적극적으로 사용하는 물건과 연동되는 제품을 개발하다 보니, GUID를 적극적으로 사용하는 방식도 꽤 괜찮지 않나 하는 생각을 갖게 되었습니다. 하지만 결국 이런 건 사용처와 목적에 따라 무엇이 더 좋은지 달라지는 것이니까, 각 방법의 장단점을 명확히 알아두는 것이 좋겠지요. 그래서 이와 관련된 가상의 서비스 개발자의 일지를 담은 트윗 타래를 소개합니다. (한국어)

최근 신한카드에서 부정사용 사고가 발생했는데, 이와 관련하여 해당 카드사가 신용카드 번호를 순차적으로 발급하는 바람에 해외로부터의 부정사용에 노출될 수 있는 위험성이 확인되었습니다.
번호만 살짝 바꿨는데 "결제"…도용에 노출된 신용카드
금융감독원, 최근 신한카드 부정사용 등에 대한 대책 강구

여러 분들께서 댓글을 달아주신 덕에 잘 모르던 것도 많이 알게 되었네요.
덕분에 Hashids라던가 Nano ID, 인스타그램이 사용하는 방식 같은 것도 처음 알게 되었습니다.

ulid와 비슷한 동기이겠지만 제안 중인 Internet Draft가 있어서 저는 이 스펙을 이전 프로젝트에서 썼었습니다.
https://github.com/uuid6/uuid6-ietf-draft

이런식으로 만들어지는 id 체계는 자주 보이는데, 이제 UUID-like한 녀석만이라도 하나로 통일되어야 하는 시점인 것 같습니다.

그치만 난립하는 표준을 하나로 통일하는 새로운 표준을 만들겠다는 시도는 대개 시장에 새로운 경쟁후보 하나를 내놓을 뿐이라고 하죠. ㅋㅋㅋ
https://xkcd.com/927/

그렇죠 ㅎㅎ 그래서 다들 새로운 id 제안을 내놓나봅니다.

얼마전에 규원님이 공유해주셨던데, 사실 심플한 문제 아닌가요?
https://byterot.blogspot.com/2013/02/…

저도 "심플한 문제"라는 이야기의 추가 설명을 원해요

어떤 면에서 심플한 문제라고 말씀하시는걸까요?

이 글에서는 'With storage nowadays very cheap, this normally is not a problem from the storage point of view.'이라고 얘기하고 있기는 한데, 네트워크 상에서 이 id가 돌아다녀야하는 경우라든가, 메모리 상에서도 key 역할을 해야한다든가, 대용량의 데이터로 여러 곳에서 쓰여야하는 키인 경우 등, 몇 바이트라도 줄이는 것이 중요할 때가 있어서 상황에 따라 UUID를 거절해야하는 상황도 있기는 한 것 같습니다.

이 글에서 말한 문제는 랜덤하게 생성된 값을 primary key 로 사용할 때 생기는 성능저하인데
(다른 문제가 언급되어 있는데 제가 캐치하지 못했다면 말씀해주세요)
그 문제는 이미 답이 있습니다. 시간 순으로 cursor based pagination 할 때랑 같은 문제라 이미 다들 해결해보셨을듯 합니다.

어떤 면에서 복잡한 문제인지 저도 궁금합니다.
말씀하신 상황은 거절해야하는 상황이라고 하셨으니 심플한 문제인 것 같은데...
복잡한 문제는 이도저도 결정을 못내릴 때가 복잡한 문제 아닌가요?

'심플한 문제'라는 것의 의미가 해석될 여지가 많아서 어떤 의미로 쓰신 것인지 궁금해서 여쭤봤습니다. 문제 자체가 어려운 문제가 아니라는 것인지, 글에서 제시하는 명쾌한 답(?)이 있기 때문에 고민의 여지가 적어서 그렇다는 것인지 등이요.
전자에 대해서는 위에서 말했듯이, 상황에 따라서 id가 데이터베이스 외부로도 통용되어야 하는 경우가 있기 때문에 고려할 요소가 많아지고 단순한 문제는 아니라고 생각합니다.

오, Hashids라는 방법도 있군요.
만약에 Salt가 유출된다면 위 본문에서 언급했던 정보 외부 노출이 되는 문제가 생길 수는 있겠지만, 그래도 좋은 방법이라고 생각합니다.

ulid도 있습니다. 128bit, 시간순으로 정렬가능.
https://github.com/ulid/spec

모양이 비슷비슷한 게 참 많은 걸 보면, 사람 생각이란 대개 다 거기서 거기인지도…?