내 명함 편집 API 맥락 (Live Studio)

계약(엔드포인트·스키마·enum)의 원본은 frontmatter spec:의 OpenAPI (2026-07-24 시드 완료). 이 문서는 스펙이 못 담는 행동 계약과 맥락만. 과거의 live-studio.md 포크와 FE business-card-api-spec.md는 해체됐다(원문은 git 이력).

한눈에

엔드포인트역할 한 줄
GET /v1/me/business-card편집 진입 시 전체 스냅샷 + 현재 version
PATCH /v1/me/business-card/profile · /style · /slug섹션 단위 저장
/v1/me/business-card/links* · /text-blocks* · /image-blocks*블록 CRUD + 정렬
POST /v1/media-assets/presigned-url → S3 PUT → /complete이미지 3단계 업로드
POST /v1/me/business-card/publish · /unpublish공개·비공개 전환

왜 이렇게 생겼나

  • 단일 version 낙관적 동시성. 모든 쓰기는 expectedVersion을 싣고(PATCH/POST는 body, DELETE는 query ?expectedVersion={n}), 성공 응답은 항상 새 version을 반환 — FE가 다음 요청에 이어 쓴다. FE가 쓰기를 큐로 직렬화하므로 한 사용자의 쓰기는 사실상 순차적. 서버는 매 요청 최신 version만 정확히 돌려주면 된다.
  • 저장 전략이 필드별로 다른 이유: 프로필은 필수값(이름·직함·회사)이 있어 입력 중간 상태를 공개 데이터에 넣으면 안 됨 → 저장 버튼. 링크·텍스트는 타이핑 중 요청 폭주 방지 → debounce. 테마·추가·삭제·정렬은 의도가 명확 → 즉시 저장.
  • 이미지는 BE가 바이너리를 받지 않는다. presigned URL 3단계(발급 → S3 직접 PUT → complete 확정). complete 전에 mediaAssetId를 연결하면 422 MEDIA_ASSET_NOT_UPLOADED.

불변조건

  • 개수 제한: 섹션당 블록 5개, 명함 전체 이미지 6장(imageBlocks[*].images만 합산 — 아바타·로고 제외). FE 버튼 비활성은 1차 방어, 서버가 최종 방어선. 이미 초과된 기존 데이터는 조회·삭제 허용, 추가만 거부.
  • 오류는 status + code로만 분기(영문 message 비의존). LIMIT_EXCEEDEDdata.limit·data.scope 동봉. IMAGES_PER_BLOCK scope는 구버전 호환용으로 FE만 인식 — BE는 더 이상 안 씀.
  • 정렬(PATCH .../order): orderedIds는 해당 스코프의 전체 ID 집합과 정확히 일치해야 함(누락·중복·외부 ID → 422). 서버가 0..n-1 재부여.
  • 이미지 추가의 sortOrder는 요청에서 받지 않는다 — 서버가 마지막 다음 순서 부여.
  • 동시 이미지 추가는 명함 루트 쓰기 잠금 + version 검증으로 직렬화 — 최종 합계 6장 초과 불가.

엣지케이스 · gotcha

  • 로고 삭제는 별도 엔드포인트가 아니라 PATCH /stylelogoMediaAssetId: null.
  • card.status(NFC 연결 상태 UNCONNECTED|CONNECTED)와 공개 API의 visibility(PUBLIC|PRIVATE)는 다른 축 — 두 live-studio 초안이 이 지점에서 서로 다르게 갈라져 있었음(중복 문서의 실제 피해 사례).
  • mutation 실패 시 FE는 로컬 입력값 유지 + 토스트 — 서버 상태로 롤백하지 않는다.
  • 업로드 중 사용자가 삭제하면: 이미지 추가 POST 전이면 요청 중단, POST 성공 후면 삭제 API로 서버까지 정리.
  • 명함 저장용 PNG는 FE가 생성·다운로드 — BE 엔드포인트 없음, CARD_DOWNLOAD 이벤트만 전송.

관련 문서