공개 명함 공유 구현과 배포 계약
기준일: 2026-07-20
실제 요청·응답과 상태 전이 전체는 공개 명함 접근·공유 통합 구현 명세를 기준으로 한다.
HTTP 계약
| 경로 | 역할 | 캐시 |
|---|---|---|
GET /card/{slug} | 상태별 OG 태그와 React 부트스트랩이 함께 있는 HTML | no-store |
GET /v1/internal/business-cards/{slug}/share-preview | 서버 간 최소 projection | no-store |
GET /v1/public/business-cards/{slug} | React 공개 화면 전체 데이터 | no-store |
GET /v1/public/business-cards/{slug}/og-image?v={revision} | 현재 공개 상태 확인 후 1200×630 PNG | 5분 |
GET /v1/public/og/default.png | 개인정보 없는 공통 PNG | 30일 |
공개 전체 API는 내부 businessCardId 대신 revision과 상대 shareUrl을 반환한다. 브라우저는
현재 주소를 복사하지 않고 shareUrl을 사이트 origin과 결합한다.
안전한 캐시와 fallback
개인화 이미지는 (slug, revision)으로 Caffeine에 최대 2,000개, 마지막 접근 후 6시간 보관한다.
모든 이미지 요청은 캐시 조회 전에 DB에서 현재 PUBLIC 상태를 다시 확인한다.
- 렌더러만 실패: 현재 공개 상태가 확인된 같은 slug의 직전 이미지 사용
- 직전 이미지 없음: 개인정보 없는 TAPLE 기본 이미지 사용
- DB timeout/5xx: 개인화 stale 금지, 기본 이미지와 HTTP 503 사용
- PRIVATE/NOT_FOUND: 개인화 캐시를 읽지 않고 기본 이미지와 HTTP 404 사용
따라서 비공개 전환 뒤 프로세스 메모리에 이미지가 남아 있어도 외부 응답으로 다시 활성화되지 않는다.
reverse proxy
한 도메인에서 아래 소유권을 지켜야 한다.
/card/* -> taple-be
/v1/* -> taple-be
/assets/* -> taple-fe dist
그 외 -> taple-fe 정적 SPA프론트 빌드는 /assets/app.js, /assets/app.css를 안정된 엔트리로 만든다. 두 파일은 배포 때
반드시 교체하고 CDN에서 재검증해야 한다. 해시가 붙은 lazy chunk와 font asset은 장기 캐시할 수 있다.
필수 환경 변수
PUBLIC_SITE_ORIGIN: canonical과 공유 URL의 사이트 originPUBLIC_API_ORIGIN: OG 이미지의 외부 접근 originPUBLIC_ASSET_ORIGIN: React 정적 asset originSHARE_PREVIEW_SERVICE_TOKEN,SHARE_PREVIEW_PREVIOUS_SERVICE_TOKENSHARE_PREVIEW_LOG_HMAC_KEYSLUG_RESERVATION_HMAC_KEY(최소 32자),SLUG_RESERVATION_HMAC_KEY_VERSION
Docker 이미지는 한글 OG 렌더링을 위해 font-noto-cjk를 설치한다.
slug 정책
사용자 slug는 trim, Unicode NFC, 소문자 정규화 후 3~32자의 영문 소문자·숫자·하이픈만 허용한다.
PATCH /v1/me/business-card/slug는 낙관적 version을 확인하고, 기존 slug와 새 slug를 모두 예약한다.
slug_reservations에는 원문, 회원 ID, 시각을 저장하지 않고 64자 HMAC과 키 버전만 저장한다.
키를 회전할 때는 과거 예약을 계속 대조할 수 있도록 이전 키를 previous-keys key ring에 영구 유지한다.
분석 계약 v2
POST /v1/analytics/events는 clientEventId, cardSlug, eventType을 받고 HTTP 202를 반환한다.
businessCardId, 도시, referrer 같은 내부·원문 필드는 공개 요청에서 받지 않는다. SHARE source와
shareRevision을 지원하며, event key unique index로 재시도를 중복 제거한다. NFC resolve는 같은
visitId를 nfc:{visitId} key로 사용해 NFC_TAP을 한 번만 기록한다.