파생 이미지 캐시 재검증 적용

This commit is contained in:
2026-07-02 10:46:09 +09:00
parent ffd6cbb822
commit 486c8bfe76
12 changed files with 95 additions and 19 deletions

View File

@@ -109,7 +109,7 @@
- 카드 썸네일은 640×360 기준 `cover` 리사이즈와 WebP 품질 82를 사용한다. GIF·동영상·문서 파일은 썸네일을 자동 생성하지 않는다.
- 본문 표시용 이미지는 1600px `thumbs/*-body.webp`와 720px `thumbs/*-body-720.webp`를 함께 생성하고, 공개 본문 단일 이미지·갤러리는 `srcset`으로 화면 폭에 맞는 파일을 우선 사용한다.
- 라이트박스용 이미지는 2560px `thumbs/*-lightbox.webp`를 생성해 크게 보기 화면에서 우선 사용한다. 별도 원본 보기 UI는 제공하지 않으며, 파생 이미지 로드 실패 시에만 원본으로 fallback한다.
- 카드·본문·라이트박스·Pages 커버 파생 이미지는 임시 파일에 먼저 생성하고 Sharp 메타데이터로 읽기 검증한 뒤 최종 파일로 교체한다. 생성 또는 검증에 실패하면 깨진 파생 파일이 남지 않도록 대상 파일을 제거하고 공개 화면은 원본 fallback을 사용한다.
- 카드·본문·라이트박스·Pages 커버 파생 이미지는 임시 파일에 먼저 생성하고 Sharp 메타데이터로 읽기 검증한 뒤 최종 파일로 교체한다. 생성 또는 검증에 실패하면 깨진 파생 파일이 남지 않도록 대상 파일을 제거하고 공개 화면은 원본 fallback을 사용한다. 파생 이미지는 같은 URL로 재생성될 수 있으므로 `/uploads/posts/YYYY/MM/thumbs/*.webp` 응답은 `no-cache`로 재검증하고, 공개 본문과 관리자 미디어 미리보기에는 파생 이미지 캐시 버전 쿼리를 붙여 기존 브라우저 캐시를 우회한다.
- 공개 게시물 응답은 원본 대표 이미지 `featuredImage`를 유지하고, 대응 썸네일 파일이 실제로 존재할 때만 `featuredImageThumbnail`을 추가한다.
- 기존 대표 이미지는 로컬에서 `npm run images:backfill-post-thumbnails`, NAS 운영에서 `sh scripts/backfill-post-thumbnails-production.sh`로 게시물 대표 이미지 URL만 스캔해 누락된 카드 썸네일을 생성한다. 관리자 미디어의 원본 이미지 상세에서도 카드 썸네일을 개별 생성·재생성할 수 있다.
- 기존 본문 이미지는 로컬에서 `npm run images:backfill-post-body-thumbnails`, NAS 운영에서 `sh scripts/backfill-post-body-thumbnails-production.sh`로 전체 게시물 본문 URL을 스캔해 누락된 본문 표시용 WebP, 작은 본문 표시용 WebP, 라이트박스용 WebP를 생성한다.
@@ -920,7 +920,7 @@ components/content/
- 관리자 미디어 업로드 API는 이미지·비디오·오디오·문서 확장자를 허용한다(에디터 슬래시·미디어 모달과 동일 목록).
- 업로드 파일 크기 제한은 종류별 환경 변수를 따른다. 이미지·아바타·로고 등은 `MAX_FILE_SIZE`(기본 10MB), 비디오는 `MAX_VIDEO_FILE_SIZE`(기본 200MB), 오디오는 `MAX_AUDIO_FILE_SIZE`(기본 50MB), 문서·ZIP 등은 `MAX_DOCUMENT_FILE_SIZE`(기본 50MB). 관리자 게시물·페이지용 미디어 업로드의 1회 파일 수와 전체 용량은 사이트 설정 POST 설정의 업로드 제한을 따른다.
- 로컬 개발 업로드 파일은 `public/uploads/posts/YYYY/MM/` 아래 저장하고 `/uploads/posts/YYYY/MM/filename` URL로 제공한다.
- 관리자 미디어 화면 상단에 **미디어 라이브러리**·**카드 썸네일**·**본문 이미지**·**프로필 이미지** 탭을 두어, 라이브러리 탭에서는 원본 업로드 파일만, 카드 썸네일 탭에서는 `/posts/YYYY/MM/thumbs/*-card.webp` 파일만, 본문 이미지 탭에서는 `/posts/YYYY/MM/thumbs/*-body.webp`, `*-body-720.webp`, `*-lightbox.webp` 파일만, 프로필 이미지 탭에서는 `/members/avatars/` 파일만 검색·탐색한다. 본문 이미지 탭의 상세 정보는 1600px, 720px, 2560px 용도를 구분해 표시한다. 라이브러리 탭의 파일 종류 필터는 `전체/이미지/영상/음악/파일`을 표시하고 사용 상태 필터는 중복 `전체` 배지를 피하기 위해 `미사용` 토글만 표시한다. 시스템 파생 탭에서는 전체/미사용 배지형 필터를 표시하고, 목록 카드 오른쪽 위에는 실제 사용 개수 숫자 배지를 표시한다. 카드 썸네일 탭과 본문 이미지 탭에서는 모든 항목이 해당 파생 파일이므로 각각 `카드`, `본문` 배지는 표시하지 않는다.
- 관리자 미디어 화면 상단에 **미디어 라이브러리**·**카드 썸네일**·**본문 이미지**·**프로필 이미지** 탭을 두어, 라이브러리 탭에서는 원본 업로드 파일만, 카드 썸네일 탭에서는 `/posts/YYYY/MM/thumbs/*-card.webp`와 `*-page-cover.webp` 파일만, 본문 이미지 탭에서는 `/posts/YYYY/MM/thumbs/*-body.webp`, `*-body-720.webp`, `*-lightbox.webp` 파일만, 프로필 이미지 탭에서는 `/members/avatars/` 파일만 검색·탐색한다. 본문 이미지 탭의 상세 정보는 1600px, 720px, 2560px 용도를 구분해 표시한다. 라이브러리 탭의 파일 종류 필터는 `전체/이미지/영상/음악/파일`을 표시하고 사용 상태 필터는 중복 `전체` 배지를 피하기 위해 `미사용` 토글만 표시한다. 시스템 파생 탭에서는 전체/미사용 배지형 필터를 표시하고, 목록 카드 오른쪽 위에는 실제 사용 개수 숫자 배지를 표시한다. 카드 썸네일 탭과 본문 이미지 탭에서는 모든 항목이 해당 파생 파일이므로 각각 `카드`, `본문` 배지는 표시하지 않는다. 파생 이미지 목록·상세 미리보기는 파일 수정 시각과 용량을 캐시 버전으로 붙여 재생성 직후에도 새 파일을 확인할 수 있게 한다.
- 미디어 라이브러리 탭은 왼쪽 폴더 트리와 오른쪽 고밀도 썸네일 갤러리, 검색, 파일명 변경, 개별 삭제를 제공한다.
- 관리자는 폴더 추가 버튼으로 모달에서 새 폴더·하위 폴더 이름을 입력해 만들 수 있으며, `미분류`·`썸네일`(및 그 하위)을 제외한 폴더는 목록에서 삭제할 수 있다. 폴더 삭제 시 해당 경로 및 하위 경로로 분류돼 있던 미디어 메타는 모두 `미분류`로 되돌린다.
- 썸네일 본문(이미지·파일명) 한 번 클릭 시 상세(미리보기) 모달이 열리고, 썸네일 좌측 상단 **선택 토글**로 개별 선택한다. Shift+클릭으로 범위 선택이 가능하다. 현재 필터 결과의 전체 선택은 사용 중이 아니어서 삭제 가능한 항목만 대상으로 한다.