글쓰기 초안 저장 상태 보정

This commit is contained in:
2026-07-03 11:23:04 +09:00
parent 955ae90697
commit 243dfbc5e2
9 changed files with 99 additions and 10 deletions

View File

@@ -744,7 +744,7 @@ components/content/
- 제목은 별도 라벨 영역이 아니라 에디터 상단의 큰 제목 입력으로 표시한다.
- 글 작성/수정 화면은 페이지 제목 헤더를 별도로 두지 않고, 폼 상단의 Ghost 스타일 도구막대에서 목록 이동, 상태 문구, Preview, 상태별 주요 액션(Publish / Update·Unpublish / Update·Unschedule), 설정 패널 토글을 제공한다.
- 도구막대 상태 문구는 영어로 표시한다. **초안**: 편집 중 `Draft`, 저장(수동·서버 자동 저장) 진행 중 `Saving...`, 서버 기준과 동일할 때 `Draft - Saved`(신규 작성에서도 첫 `POST` 저장 후에는 `Draft - Saved`를 사용할 수 있다). **즉시 발행**: 공개 URL이 있으면 `Published ↗`를 링크로, 없으면 동일 문구만 표시한다. **예약 발행**: `Scheduled`를 `#2BBA3C`·보통 굵기로 표시하고 마우스 오버 시 영문 한 줄로 예약 시각을 `title` 툴팁에 보여 준다. **멤버십**은 `Members`, **비공개**는 `Private`로 표시한다.
- **초안**(예약 발행 제외)은 입력 변경 후 약 1.2초 디바운스로 서버 자동 저장을 호출한다(슬러그가 유효할 때만). 제목이 비어 있으면 DB/API 저장 시에만 `(제목 없음)` 플레이스홀더를 쓰고, 관리자 폼·목록 API 응답의 `title`은 빈 문자열로 내려 준다. 임시 슬러그(`d`+24자리 hex)는 제목을 직접 수정하기 전까지 제목 입력에 따라 슬러그가 따라가며, 사용자가 슬러그를 직접 고친 뒤에는 자동 연동하지 않는다. 신규 작성 화면 마운트 시 슬러그가 비어 있으면 임시 슬러그를 채운다. 기존 글은 `PUT /admin/api/posts/:id`, **신규 작성**은 첫 저장 시 `POST /admin/api/posts`로 행을 만든 뒤 `replace`로 `/admin/posts/:id` 편집 화면으로 옮긴다. 저장 요청 중 사용자가 추가 입력한 본문·태그는 성공 응답 시 저장 완료로 오판하지 않고, 저장 완료 후 남은 변경으로 다시 자동 저장한다. **이미 발행·예약으로 서버에 반영된 글**은 사이드바 등으로 폼만 초안처럼 바뀌어도 자동 저장하지 않으며, `Update`를 눌렀을 때만 `PUT`으로 반영한다(툴바의 `Publish`/`Update`/`Unpublish`/`Unschedule` 분기도 서버에 반영된 게시 형태를 기준으로 한다). 다른 화면으로 나가기 직전에는 디바운스 대기 중인 초안 변경이 있으면 타이머를 취소하고 한 번에 `POST` 또는 `PUT`으로 플러시한다.
- **초안**(예약 발행 제외)은 입력 변경 후 약 1.2초 디바운스로 서버 자동 저장을 호출한다(슬러그가 유효할 때만). 제목이 비어 있으면 DB/API 저장 시에만 `(제목 없음)` 플레이스홀더를 쓰고, 관리자 폼·목록 API 응답의 `title`은 빈 문자열로 내려 준다. 임시 슬러그(`d`+24자리 hex)는 제목을 직접 수정하기 전까지 제목 입력에 따라 슬러그가 따라가며, 사용자가 슬러그를 직접 고친 뒤에는 자동 연동하지 않는다. 신규 작성 화면 마운트 시 슬러그가 비어 있으면 임시 슬러그를 채운다. 기존 글은 `PUT /admin/api/posts/:id`, **신규 작성**은 첫 저장 시 `POST /admin/api/posts`로 행을 만든 뒤 `replace`로 `/admin/posts/:id` 편집 화면으로 옮긴다. 신규 글 첫 자동 저장 중에는 초안 저장 준비 안내 오버레이를 표시하고, 첫 `POST` 이후 이동 직전 현재 폼의 최신 제목·본문·태그·옵션이 첫 요청 payload와 다르면 생성된 글에 `PUT`으로 한 번 더 반영한다. 저장 요청 중 사용자가 추가 입력한 본문·태그는 성공 응답 시 저장 완료로 오판하지 않고, 저장 완료 후 남은 변경으로 다시 자동 저장한다. 미저장 변경 비교는 태그 표시명 보조값(`tagNames`)처럼 비동기 태그 API 로딩으로만 바뀌는 값을 제외하고 실제 저장 필드 기준으로 판단한다. **이미 발행·예약으로 서버에 반영된 글**은 사이드바 등으로 폼만 초안처럼 바뀌어도 자동 저장하지 않으며, `Update`를 눌렀을 때만 `PUT`으로 반영한다(툴바의 `Publish`/`Update`/`Unpublish`/`Unschedule` 분기도 서버에 반영된 게시 형태를 기준으로 한다). 다른 화면으로 나가기 직전에는 디바운스 대기 중인 초안 변경이 있으면 타이머를 취소하고 한 번에 `POST` 또는 `PUT`으로 플러시한다.
- `Publish`는 **서버에 아직 초안으로만 저장된 글**에만 표시되며 클릭 시 전체 화면 발행 모달을 연다. 초안에서 연 모달의 기본 선택은 **발행·지금 바로**이다. 모달 본문은 뷰포트 세로 중앙에 가깝게 배치하고, 상단에는 제목·닫기만 둔다(도구막대의 `Preview` 버튼은 두지 않는다). 모달에서는 상태(발행/초안)와 발행 시점(즉시/예약)을 최종 선택한 뒤 확정하며, 예약 시각은 날짜·시간(KST 표기) 입력을 분리한다. 모달에서는 행마다 접힌 상태로 현재 선택 요약만 보이고, 행을 눌러 펼친 뒤 버튼으로 값을 고른다. 게시 상태 행에는 Ghost와 같은 종이비행기 아이콘·펼침 화살표 SVG를 쓰고, 발행 시점 행에는 시계 아이콘·동일 화살표를 쓴다. 발행이 아닌 상태에서는 발행 시점 행 자체를 숨긴다. 발행 저장 요청이 진행되는 동안 다른 화면으로 이동하려 하면 미저장 이탈 확인을 표시한다.
- `Update`는 발행·예약·멤버십·비공개 글에 표시되며, 마지막 저장 이후 변경이 있을 때만 활성화된다(활성 텍스트 `#394047`, 비활성 `#8E9CAC`). `Publish`·활성화된 `Update`·`Unpublish`·`Unschedule`에는 호버 시 배경 `#f1f3f4`를 적용한다.
- `Unpublish`·`Unschedule` 클릭 시 Ghost형 전체 화면 확인 화면을 연다. 발행·예약 시각 요약과 **발행 취소하고 초안으로 되돌리기 →**(또는 예약 취소 문구) 링크를 눌렀을 때만 `status`를 초안으로 되돌리고 `published_at`을 비운 뒤 `PUT`으로 저장한다.