명함 뷰어 개선 — 백엔드/프론트 분리 및 실행 계획
요청 항목 13개를 백엔드/프론트로 분리하고, 확정된 정책과 실행(커밋) 계획을 정리한다.
확정된 정책 (사용자 결정)
- 이미지 제한: 명함 전체 이미지 블록 합계 6장
- 블록 제한: 섹션(링크/콘텐츠/이미지)당 5개
- 리오더: 타입 내부만 (블록 순서 + 이미지 블록 내부 이미지 순서)
- 상한 도달 시: 추가 버튼 비활성 + 안내 토스트
- 블록 삭제: 즉시 삭제 + “삭제됨” 토스트 (확인 다이얼로그·Undo 없음)
- 저장 토스트: “저장됨”만, 자동저장이 잠잠해지면 debounce로 1회
- ⑬ 명함 비활성화: 마이페이지 분실 모드 (이미 구현 완료)
- ⑧ 애니메이션: flip 인터랙션 다듬기
- ⑨ 이미지 확대 뷰어: 공개 갤러리 탭 → 풀스크린 + 좌우 스와이프 +
n/총순번 (줌 없음) - ⑫ 저장 버튼: 앞뒷면 합본 PNG 다운로드
항목별 분리
범례: 🔵 BE 신규 · 🟢 BE 이미 있음(프론트가 연결) · 🟠 FE 전담 · ✅ 완료
| # | 요청 | 담당 | 상태 |
|---|---|---|---|
| 1 | 명함 전체 이미지 6장 제한 | 🔵 서버 검증 · 🟠 추가버튼 가드+토스트 | ✅ |
| 2 | 섹션당 블록 5개 제한 | 🔵 서버 검증 · 🟠 추가버튼 가드+토스트 | 예정 |
| 3 | 리오더(블록·이미지 내부) | 🟢 order 엔드포인트 존재 · 🟠 드래그 UI | 예정 |
| 4 | 블록 삭제 토스트 | 🟠 | 예정 |
| 5 | 테마 적용 토스트 | 🟠 | ✅ |
| 6 | 로고 삭제 토스트 | 🟠 | ✅ |
| 7 | 뒷면보기(공개 페이지) | 🟠 | ✅ |
| 8 | flip 애니메이션 다듬기 | 🟠 | 예정 |
| 9 | 이미지 확대 뷰어 | 🟠 | 예정 |
| 10 | 스와이프 순번 + 블록당 다중 이미지 | 🟢 images[] CRUD 존재 · 🟠 캐러셀·모델 | 예정 |
| 11 | 저장됨 토스트 | 🟠 | 예정 |
| 12 | 앞뒷면 합본 PNG 다운로드 | 🟢 CARD_DOWNLOAD 존재 · 🟠 이미지 생성 | ✅ |
| 13 | 명함 비활성화(분실 모드) | 🟢🟠 | ✅ 이미 구현 |
백엔드가 해야 할 일
리오더/이미지 CRUD/분실 모드/공개·비공개/분석과 개수 제한 검증까지 구현 완료됐다. 아래는 현재 계약을 유지하기 위한 기준이다.
1. 개수 제한 서버 검증 (구현 완료)
프론트가 UX 가드를 하더라도 서버가 최종 방어선이 되어야 한다.
- 명함 전체 이미지 6장 초과 거부
POST /v1/me/business-card/image-blocks/{blockId}/images에서 모든 이미지 블록의 이미지 합계가 이미 6장이면 거부.
- 섹션당 블록 5개 초과 거부
POST /me/business-card/links/.../text-blocks/.../image-blocks에서 해당 섹션 블록이 이미 5개면 거부.
에러 계약(프론트가 문구 분기에 사용):
HTTP 409 Conflict
{
"code": "LIMIT_EXCEEDED",
"message": "Business card item limit exceeded",
"data": {
"limit": 6,
"scope": "IMAGES_PER_CARD"
}
}
IMAGES_PER_BLOCK은 서버 제한 판단에는 사용하지 않고, 프론트에서 구버전 응답 호환 목적으로만 인식한다.
2. (확인) 리오더 검증
PATCH .../order계열은orderedIds가 해당 스코프의 현재 ID 집합과 정확히 일치하는지 검증하고, 누락/중복/외부 ID는 거부. 부분 순서만 와도 안전하게 처리(또는 400).
3. 신규 작업 없음(확인만)
- ⑬ 분실 모드:
report-lost/recover/publish/unpublish이미 존재 → 그대로 사용. - ③ 리오더: links/text-blocks/image-blocks
order+ image-blocks/{id}/images/order 이미 존재. - ⑩ 다중 이미지:
imagesadd/delete/order 이미 존재 → 프론트가 명함 전체 합계 최대 6장으로 연결. - ⑫ PNG: 파일 생성은 FE, 분석은
POST /analytics/events(CARD_DOWNLOAD)를 사용한다.
프론트 실행 계획 (커밋 단위)
-
feat(public-card): 공개 명함 NameCard 교체(프로필 반영 + 뒷면보기) — ⑦ -
docs: 본 문서 + API 명세서(현 openapi.yaml + ../api/business-card.md) -
feat(card-customize): 테마 적용·로고 삭제 토스트 — ⑤⑥ -
feat(public-card): 앞뒷면 합본 PNG 다운로드 + CARD_DOWNLOAD/SHARE_CLICK 분석 — ⑫ -
feat(public-card): 이미지 풀스크린 뷰어 +n/총순번 — ⑨⑩(뷰어) -
feat(live-studio): 저장/삭제 토스트 인프라 + 삭제·저장 토스트 — ④⑪ -
feat(live-studio): 섹션당 블록 5개 제한 가드 + 초과 토스트 — ② -
style(name-card): flip 인터랙션 다듬기 — ⑧ -
feat(live-studio): 블록·이미지 드래그 리오더(@dnd-kit) — ③ ← 다음 배치 -
feat(live-studio): 이미지 블록 다중 이미지 편집 + 인라인 캐러셀 + 명함 전체 6장 제한 — ①⑩
이미지 블록 다중 이미지와 명함 전체 6장 제한은 구현 완료했으며, 서버가 최종 한도를 검증한다.
API 개선 제안
- 에러 body 파싱 완료:
src/features/liveStudio/domain/api.ts의parseJson이code와data.limit·data.scope를 읽어LiveStudioApiError로 전달한다. - 버전 충돌(409) 구분:
expectedVersion불일치 시 서버가 구분 가능한 code를 주면, FE가 “다른 곳에서 수정됨 → 새로고침” 안내로 복구할 수 있다. /me/business-card응답 형태 정리: liveStudiogetMyBusinessCard(전체 스냅샷)와 myPagegetMyBusinessCardPublication이 같은 경로를 서로 다른 타입으로 소비 중 — 문서/타입 정합성 점검 권장.
예외 처리 정책 (FE)
- 개수 상한: UI에서 추가 버튼 비활성화가 1차 방어. 서버가 거부(LIMIT_EXCEEDED)하면 낙관적 추가를 롤백하고 “최대 N개까지 가능해요” 토스트.
- 삭제 실패: 낙관적 제거 롤백 + “삭제하지 못했어요” 토스트(negative).
- 리오더 실패: 낙관적 순서 롤백 + 실패 토스트. 서버 version으로 재동기화.
- 이미지 업로드 실패(presigned/complete/PUT): 미리보기 blob 정리 + “이미지를 올리지 못했어요” 토스트.
- 저장 실패: 사용자 결정상 실패 토스트는 노출하지 않기로 했으나, 데이터 유실 방지를 위해 콘솔/재시도는 유지. (표시 정책은 추후 조정 가능)
- PNG 저장/공유: 실패 시 2초 오류 토스트를 표시하며 방문자 흐름은 유지한다.