# 같은 slug로 두 번 발행해도 안전한가: 기술블로그 멱등 발행 가드 실측기
매일 18:00 KST에 기술블로그를 발행하면서 가장 조용히 무서운 순간은 재시도 버튼 앞이다. 타임아웃이 떴는데, 재발행하면 중복 글이 생길까. DB 연동 발행 API의 답은 명확했다. 같은 slug를 재사용하면 내용이 완전히 동일할 때만 성공한다. 이 글은 그 한 줄짜리 계약을 실제로 믿을 수 있는지 확인한 기록이다.
## 1. 문제: 재시도는 중복 발행과 종이 한 장 차이
일일 발행 파이프라인은 publish → get → list 순서로 검증한다. 그런데 publish 호출이 네트워크 타임아웃으로 끊기면 상태가 모호해진다. DB에는 쓰였는데 응답만 못 받은 것인지, 아예 안 쓰인 것인지 알 수 없다. 무작정 다시 보내면 같은 글이 두 개 생길 수 있고, 안 보내면 그날 발행이 빠진다.
## 2. 계약: slug 재사용은 동일 내용일 때만 성공
발행 API의 명세는 이렇게 적혀 있다. slug 재사용은 전체 아티클이 동일할 때만 성공한다. 뒤집어 말하면 두 가지 보장이 된다.
- 같은 slug + 완전히 동일한 본문·제목·메타데이터 → 성공(멱등, 중복 생성 없음)
- 같은 slug + 하나라도 다른 필드 → 실패(덮어쓰기 방지)
이것은 실무에서 정확히 원하는 모양이다. 재시도는 안전하고, 실수로 다른 글을 같은 slug에 덮는 일은 막힌다.
## 3. 실측: 재시도를 안전하게 만드는 호출 순서
검증된 순서는 다음과 같다.
1. publish를 보낸다. 타임아웃이 나면 단정하지 않는다.
2. get(slug)으로 단건을 조회한다. 글이 있으면 title·content·tags·category·publishedAt을 원본과 필드 대조한다.
3. 전부 일치하면 재시도가 아니라 이미 완료된 것으로 판정하고 list 첫 페이지 최상단 노출까지 확인한다.
4. 글이 없으면 publish를 그대로(바이트 단위로 동일하게) 재전송한다.
5. 일부라도 다르면 새 slug를 발급한다. 절대 같은 slug에 다른 내용을 밀어넣지 않는다.
핵심은 4번의 괄호 안이다. 재전송 본문이 한 글자라도 달라지면 멱등이 깨져 실패한다. 재시도용 원본 페이로드는 변수로 만들지 말고 상수로 고정해 두는 편이 낫다.
## 4. 함정 두 가지
첫째, publishedAt을 재시도 시점에 `now()`로 다시 찍으면 안 된다. 타임스탬프가 바뀌는 순간 내용이 달라져 동일 재사용 조건에서 벗어난다. 발행 시각은 최초 시도 값을 그대로 유지한다.
둘째, DB PASS를 공개 PASS로 착각하면 안 된다. get·list 확인은 저장 계층의 정합성만 증명한다. 공개 사이트 반영은 sitemap.xml과 /blog 서버 HTML에 slug가 직접 포함됐는지 별도로 확인해야 한다. 과거 실측에서 DB 14편이 sitemap 9건·서버렌더 0건으로 미반영된 사례가 있었고, 원인은 클라이언트 전용 렌더였다.
## 5. 결론: 재시도 정책 한 줄
같은 slug 재발행은 바이트 동일 재전송일 때만 한다. 그 외에는 새 slug다. 이 한 줄을 파이프라인에 고정하면 타임아웃이 와도 손이 떨리지 않는다.