snowflake는 트위터에서 만든 알고리즘인데, MSA(분산 시스템)에서 오름차순 ID를 만들기 위한 알고리즘이다.
다만 라이브러리가 아니라 그냥 알고리즘이라서 우리가 코드를 작성해야 한다.
public class Snowflake {
private static final int UNUSED_BITS = 1;
private static final int EPOCH_BITS = 41;
private static final int NODE_ID_BITS = 10;
private static final int SEQUENCE_BITS = 12;
private static final long maxNodeId = (1L << NODE_ID_BITS) - 1;
private static final long maxSequence = (1L << SEQUENCE_BITS) - 1;
private final long nodeId = RandomGenerator.getDefault().nextLong(maxNodeId + 1);
// UTC = 2024-01-01T00:00:00Z
private final long startTimeMillis = 1704067200000L;
private long lastTimeMillis = startTimeMillis;
private long sequence = 0L;
public synchronized long nextId() {
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis < lastTimeMillis) {
throw new IllegalStateException("Invalid Time");
}
if (currentTimeMillis == lastTimeMillis) {
sequence = (sequence + 1) & maxSequence;
if (sequence == 0) {
currentTimeMillis = waitNextMillis(currentTimeMillis);
}
} else {
sequence = 0;
}
lastTimeMillis = currentTimeMillis;
return ((currentTimeMillis - startTimeMillis) << (NODE_ID_BITS + SEQUENCE_BITS))
| (nodeId << SEQUENCE_BITS)
| sequence;
}
private long waitNextMillis(long currentTimestamp) {
while (currentTimestamp <= lastTimeMillis) {
currentTimestamp = System.currentTimeMillis();
}
return currentTimestamp;
}
}
노드 ID와 시퀀스 번호, 타임스탬프를 조합해서 64비트 ID를 만들어내는 코드이다.
이 클래스를 모듈로 만들어서 사용할 것이다.

외부에서 이 모듈을 사용하려면 이렇게 라이브러리처럼 넣어주면 된다.
그러고보니 쿠팡 해킹 사태가 터지고 나서 유저 ID를 그냥 AUTO_INCREMENT로 했다느니 하는 소문이 있었는데, 사실 AUTO_INCREMENT를 쓰더라도 외부에 노출하지 않고 내부적으로만 사용하면 괜찮다. UUID를 쓴다면 외부에 노출해도 그냥 난수이기 때문에 큰 정보 노출은 없을 것이다. 하지만 정렬이 안되기 때문에 성능 문제가 있다. UUIDv7은 정렬이 되지만 용량이 너무 크다(128bit).
Snowflake나 TSID와 같은 유니크 정렬 숫자를 쓰면 성능 문제와 보안 문제를 둘 다 해결할 수 있다.

나는 IdGenerator 포트를 통해서 Snowflake를 사용하는 빈을 주입받도록 만들었는데(클린코드 방식)
그냥 바로 서비스단에 넣어서 써도 상관없다.

public Article publishArticle(String username, PublishArticleFormRequest request){
Member member = memberRepository.findByEmail(new Email(username))
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
Article article = Article.of(idGenerator.nextId(), request, member);
articleRepository.save(article);
return article;
}
서비스단의 publishArticle 함수에서 id를 만들어서 Article 객체의 생성자에 넣는다.
원래는 Article 객체에서 id가 null인 채로 DB에 들어가면 @GeneratedValue로 생성하는 방식이었는데 이제 해당 어노테이션은 지워줬다.
'스프링 부트로 블로그 서비스 개발하기' 카테고리의 다른 글
| 포스트 CRUD 기능 - 인덱스 생성 및 커버링 인덱스 (1) | 2026.01.11 |
|---|---|
| 포스트 CRUD 기능 - 쓰레드풀을 써서 게시글 대량 삽입 테스트 (0) | 2026.01.10 |
| 대규모 시스템 설계 인강) MSA화 진행하기: 모듈 추가하고 build.gradle 분리 (0) | 2026.01.07 |
| 포스트 CRUD 기능 - 컨트롤러단 + API 테스트 (0) | 2025.12.07 |
| 포스트 CRUD 기능 - 테스트 코드 (1) | 2025.12.06 |