외부로 나가는 식별자는 auto-increment 대신 TSID를 쓴다
- 상태: 채택됨
- 작성일: 2026-07-11
- 관련 문서: —
맥락
User·Account·Session의 PK가 Long auto-increment였다. 그 값을 URL과 응답에 그대로 쓰면 레코드
수와 생성 순서가 밖으로 샌다. 나중에 바꾸면 계약 파괴가 된다. 식별자는 한 번 계약에 나가면 되돌리는
비용이 가장 큰 값이다. 그래서 다른 테이블이 늘기 전에 정해야 했다.
결정
- 외부로 나가는 식별자는 TSID로 한다. PK 타입을 nullable
String으로 바꾸고 Hypersistence Utils의@Tsid가 영속화 전에 값을 만든다. - 컬럼은
VARCHAR(13), 계약에서는 13자리 문자열로 다루고 숫자로 직렬화하지 않는다. API·application·persistence가 별도 인코딩 없이 같은 문자열을 쓴다. - 내부 전용 식별자까지 바꾸지는 않는다. 공개 여부가 애매하면 API 계약을 기준으로 판단한다(YAGNI).
대안
대안 1 — auto-increment Long 유지. 아무것도 안 해도 되고 인덱스가 가장 작다.
기각한 이유: 레코드 수와 생성 순서가 그대로 노출된다. 외부 계약이 내부 시퀀스를 따라가게 된다.
대안 2 — UUID. 표준이고 충돌 걱정이 없다. 기각한 이유: 36자에 시간 정렬이 안 된다. TSID는 같은 성질을 13자에 시간 정렬까지 붙여서 준다.
대안 3 — 내부는 숫자, 외부는 별도 인코딩(hashid류). 내부 성능을 그대로 두면서 노출만 막는다. 기각한 이유: 계층마다 변환 코드가 생긴다. 그 변환이 한 군데라도 빠지면 숫자가 그대로 새어 나간다.
결과
- 레코드 수와 생성 순서가 외부 계약에서 사라졌다. 내부 시퀀스와 외부 계약이 분리됐다.
- 계층 간 변환이 없다. API·application·persistence가 같은 문자열을 그대로 쓴다.
- 식별자 규칙이 코드베이스 전체로 굳었다.
@Tsid+@Column(length = 13)가 엔티티 관례가 됐다. harness v002의api-contract규칙도 "auto increment나 순차 내부 식별자를 외부 계약에 노출하지 않는다"로 규약화했다. 이후 모든 신규 테이블(sessions·workspace_invitations·notes)이 이 형태를 따른다. - 대가로 PK 타입이 두 종류로 공존한다. 새 테이블마다 "이 식별자가 밖으로 나가나"를 API 계약 기준으로 매번 판단해야 한다.
- PK가 숫자가 아니라 13자 문자열이라 인덱스와 조인 키가 커진다.