diff --git a/docs/frontend-improvement.md b/docs/frontend-improvement.md
index 11c07d7..e4965a6 100644
--- a/docs/frontend-improvement.md
+++ b/docs/frontend-improvement.md
@@ -44,7 +44,7 @@ components/
### 1.4 대형 파일
| 파일 | 원래 라인 수 | 현재 라인 수 | 상태 |
|------|-------------|-------------|------|
-| AlbumPhotos.jsx | 1536 | 1536 | 미분리 |
+| AlbumPhotos.jsx | 1536 | 1033 | ✅ 분리 완료 |
| Schedules.jsx | 1465 | 1159 | ✅ 분리 완료 |
| ScheduleForm.jsx | 1047 | 765 | ✅ 분리 완료 |
| ScheduleDict.jsx | 714 | 572 | ✅ 분리 완료 |
@@ -99,7 +99,11 @@ components/
│ │ ├── ImageUploader.jsx # 이미지 업로드
│ │ └── WordItem.jsx # 사전 단어 아이템
│ └── album/ # ✅ 추가된 컴포넌트들:
-│ └── TrackItem.jsx # 트랙 입력 폼
+│ ├── TrackItem.jsx # 트랙 입력 폼
+│ ├── PendingFileItem.jsx # 업로드 대기 파일
+│ ├── BulkEditPanel.jsx # 일괄 편집 도구
+│ ├── PhotoGrid.jsx # 사진/티저 그리드
+│ └── PhotoPreviewModal.jsx # 미리보기 모달
```
### 2.3 중복 코드 제거
@@ -184,7 +188,11 @@ pages/pc/admin/schedules/
4. [x] AlbumForm.jsx 분리 (631줄 → 443줄, 188줄 감소)
- `CustomSelect.jsx` 추출 (공통 드롭다운 → common/)
- `TrackItem.jsx` 추출 (트랙 입력 폼 → album/)
-5. [ ] AlbumPhotos.jsx 분리 (1536줄, 구조 복잡)
+5. [x] AlbumPhotos.jsx 분리 (1536줄 → 1033줄, 503줄 감소)
+ - `PendingFileItem.jsx` 추출 (업로드 대기 파일 아이템)
+ - `BulkEditPanel.jsx` 추출 (일괄 편집 도구)
+ - `PhotoGrid.jsx` 추출 (사진/티저 그리드)
+ - `PhotoPreviewModal.jsx` 추출 (미리보기 모달)
### Phase 3: 추가 개선
1. [x] 관리자 페이지용 에러 페이지 추가 (404)
diff --git a/frontend-temp/src/components/pc/admin/album/BulkEditPanel.jsx b/frontend-temp/src/components/pc/admin/album/BulkEditPanel.jsx
new file mode 100644
index 0000000..6f1b4ba
--- /dev/null
+++ b/frontend-temp/src/components/pc/admin/album/BulkEditPanel.jsx
@@ -0,0 +1,211 @@
+/**
+ * 일괄 편집 패널 컴포넌트
+ */
+import { memo } from 'react';
+import { Tag, Users, User, Users2, Check } from 'lucide-react';
+
+/**
+ * 범위 문자열 파싱
+ */
+export const parseRange = (rangeStr, baseNumber = 1) => {
+ if (!rangeStr.trim()) return [];
+ const indices = new Set();
+ const parts = rangeStr.split(',').map((s) => s.trim());
+
+ for (const part of parts) {
+ if (part.includes('-')) {
+ const [start, end] = part.split('-').map((n) => parseInt(n.trim()));
+ if (!isNaN(start) && !isNaN(end)) {
+ for (let i = Math.min(start, end); i <= Math.max(start, end); i++) {
+ const idx = i - baseNumber;
+ if (idx >= 0) indices.add(idx);
+ }
+ }
+ } else {
+ const num = parseInt(part);
+ if (!isNaN(num)) {
+ const idx = num - baseNumber;
+ if (idx >= 0) indices.add(idx);
+ }
+ }
+ }
+ return Array.from(indices).sort((a, b) => a - b);
+};
+
+/**
+ * @param {Object} props
+ * @param {Object} props.bulkEdit - 일괄 편집 상태
+ * @param {Function} props.setBulkEdit - 일괄 편집 상태 설정
+ * @param {number} props.startNumber - 시작 번호
+ * @param {number} props.pendingFilesCount - 대기 파일 수
+ * @param {Array} props.members - 멤버 목록
+ * @param {Function} props.onApply - 적용 핸들러
+ */
+const BulkEditPanel = memo(function BulkEditPanel({
+ bulkEdit,
+ setBulkEdit,
+ startNumber,
+ pendingFilesCount,
+ members,
+ onApply,
+}) {
+ const groupTypes = [
+ { value: 'group', icon: Users, label: '단체' },
+ { value: 'solo', icon: User, label: '개인' },
+ { value: 'unit', icon: Users2, label: '유닛' },
+ ];
+
+ const toggleBulkMember = (memberId) => {
+ setBulkEdit((prev) => ({
+ ...prev,
+ members: prev.members.includes(memberId)
+ ? prev.members.filter((m) => m !== memberId)
+ : [...prev.members, memberId],
+ }));
+ };
+
+ return (
+
+
+
+
+ 일괄 편집
+
+
+ {/* 번호 범위 */}
+
+
+
setBulkEdit((prev) => ({ ...prev, range: e.target.value }))}
+ placeholder={`예: ${startNumber}-${startNumber + 4}, ${startNumber + 7}`}
+ className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary"
+ />
+
+ {startNumber}~{startNumber + pendingFilesCount - 1}번 중{' '}
+ {parseRange(bulkEdit.range, startNumber).filter((i) => i < pendingFilesCount).length}개
+ 선택
+
+
+
+ {/* 타입 선택 */}
+
+
+
+ {groupTypes.map(({ value, icon: Icon, label }) => (
+
+ ))}
+
+
+
+ {/* 멤버 선택 */}
+ {bulkEdit.groupType !== 'group' && (
+
+
+
+ {members
+ .filter((m) => !m.is_former)
+ .map((member) => (
+
+ ))}
+ {members.filter((m) => m.is_former).length > 0 && (
+ |
+ )}
+ {members
+ .filter((m) => m.is_former)
+ .map((member) => (
+
+ ))}
+
+
+ )}
+
+ {/* 컨셉명 */}
+
+
+ setBulkEdit((prev) => ({ ...prev, conceptName: e.target.value }))}
+ placeholder="컨셉명 입력"
+ className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary"
+ />
+
+
+ {/* 적용 버튼 */}
+
+
+
+ );
+});
+
+export default BulkEditPanel;
diff --git a/frontend-temp/src/components/pc/admin/album/PendingFileItem.jsx b/frontend-temp/src/components/pc/admin/album/PendingFileItem.jsx
new file mode 100644
index 0000000..c59200a
--- /dev/null
+++ b/frontend-temp/src/components/pc/admin/album/PendingFileItem.jsx
@@ -0,0 +1,214 @@
+/**
+ * 업로드 대기 파일 아이템 컴포넌트
+ */
+import { memo } from 'react';
+import { Reorder } from 'framer-motion';
+import { GripVertical, Trash2, Users, User, Users2 } from 'lucide-react';
+
+/**
+ * @param {Object} props
+ * @param {Object} props.file - 파일 데이터
+ * @param {number} props.index - 인덱스
+ * @param {number} props.startNumber - 시작 번호
+ * @param {string} props.photoType - 사진 타입 (concept/teaser)
+ * @param {Array} props.members - 멤버 목록
+ * @param {Function} props.onPreview - 미리보기 핸들러
+ * @param {Function} props.onDelete - 삭제 핸들러
+ * @param {Function} props.onUpdateFile - 파일 업데이트 핸들러
+ * @param {Function} props.onToggleMember - 멤버 토글 핸들러
+ * @param {Function} props.onChangeGroupType - 그룹 타입 변경 핸들러
+ * @param {Function} props.onMoveToPosition - 위치 이동 핸들러
+ * @param {Array} props.pendingFiles - 전체 대기 파일 목록 (위치 계산용)
+ */
+const PendingFileItem = memo(function PendingFileItem({
+ file,
+ index,
+ startNumber,
+ photoType,
+ members,
+ onPreview,
+ onDelete,
+ onUpdateFile,
+ onToggleMember,
+ onChangeGroupType,
+ onMoveToPosition,
+ pendingFiles,
+}) {
+ const groupTypes = [
+ { value: 'group', icon: Users, label: '단체' },
+ { value: 'solo', icon: User, label: '개인' },
+ { value: 'unit', icon: Users2, label: '유닛' },
+ ];
+
+ return (
+
+
+ {/* 드래그 핸들 + 순서 번호 */}
+
+
+ {
+ const val = e.target.value.trim();
+ const scrollY = window.scrollY;
+ const currentIndex = pendingFiles.findIndex((f) => f.id === file.id);
+ const currentOrder = startNumber + currentIndex;
+
+ if (val && !isNaN(val) && parseInt(val) !== currentOrder) {
+ onMoveToPosition(file.id, val);
+ }
+
+ const newIndex = pendingFiles.findIndex((f) => f.id === file.id);
+ e.target.value = String(startNumber + newIndex).padStart(2, '0');
+
+ requestAnimationFrame(() => {
+ window.scrollTo(0, scrollY);
+ });
+ }}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ e.target.blur();
+ }
+ }}
+ className="w-10 h-8 bg-primary/10 text-primary rounded-lg text-center text-sm font-bold border-0 focus:outline-none focus:ring-2 focus:ring-primary [appearance:textfield]"
+ />
+
+
+ {/* 썸네일 */}
+ {file.isVideo ? (
+
+
+ ) : (
+

onPreview(file)}
+ />
+ )}
+
+ {/* 메타 정보 */}
+
+
{file.filename}
+
+ {photoType === 'concept' && (
+ <>
+ {/* 단체/솔로/유닛 선택 */}
+
+
타입:
+
+ {groupTypes.map(({ value, icon: Icon, label }) => (
+
+ ))}
+
+
+
+ {/* 멤버 태깅 */}
+
+
+ 멤버:
+ {file.groupType === 'group' ? (
+ 단체 사진은 멤버 태깅이 필요 없습니다
+ ) : (
+ <>
+ {members
+ .filter((m) => !m.is_former)
+ .map((member) => (
+
+ ))}
+ >
+ )}
+
+ {file.groupType !== 'group' && members.filter((m) => m.is_former).length > 0 && (
+
+
+ {members
+ .filter((m) => m.is_former)
+ .map((member) => (
+
+ ))}
+
+ )}
+
+
+ {/* 컨셉명 */}
+
+ 컨셉명:
+ onUpdateFile(file.id, 'conceptName', e.target.value)}
+ className="flex-1 px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary"
+ placeholder="컨셉명을 입력하세요"
+ />
+
+ >
+ )}
+
+
+ {/* 삭제 버튼 */}
+
+
+
+ );
+});
+
+export default PendingFileItem;
diff --git a/frontend-temp/src/components/pc/admin/album/PhotoGrid.jsx b/frontend-temp/src/components/pc/admin/album/PhotoGrid.jsx
new file mode 100644
index 0000000..3103e70
--- /dev/null
+++ b/frontend-temp/src/components/pc/admin/album/PhotoGrid.jsx
@@ -0,0 +1,142 @@
+/**
+ * 사진/티저 그리드 컴포넌트
+ */
+import { memo } from 'react';
+import { motion } from 'framer-motion';
+import { Image, Check } from 'lucide-react';
+
+/**
+ * @param {Object} props
+ * @param {Array} props.items - 사진/티저 목록
+ * @param {Array} props.selectedItems - 선택된 아이템 ID 목록
+ * @param {Function} props.onToggleSelect - 선택 토글 핸들러
+ * @param {'concept'|'teaser'} props.type - 그리드 타입
+ */
+const PhotoGrid = memo(function PhotoGrid({ items, selectedItems, onToggleSelect, type }) {
+ if (items.length === 0) {
+ return (
+
+
+
+ 등록된 {type === 'concept' ? '컨셉 포토' : '티저 이미지'}가 없습니다
+
+
+ 업로드 탭에서 {type === 'concept' ? '사진' : '티저'}을 추가하세요
+
+
+ );
+ }
+
+ if (type === 'concept') {
+ return (
+
+ {items.map((photo, index) => (
+
onToggleSelect(photo.id)}
+ >
+
+
+
+
+ {selectedItems.includes(photo.id) && }
+
+
+
+ {String(photo.sort_order).padStart(2, '0')}
+
+
+
+ {photo.concept_name && (
+
+ {photo.concept_name}
+
+ )}
+
+
+ ))}
+
+ );
+ }
+
+ // Teaser grid
+ return (
+
+ {items.map((teaser, index) => {
+ const teaserId = `teaser-${teaser.id}`;
+ return (
+
onToggleSelect(teaserId)}
+ >
+ {teaser.media_type === 'video' ? (
+
+ );
+ })}
+
+ );
+});
+
+export default PhotoGrid;
diff --git a/frontend-temp/src/components/pc/admin/album/PhotoPreviewModal.jsx b/frontend-temp/src/components/pc/admin/album/PhotoPreviewModal.jsx
new file mode 100644
index 0000000..90a1e31
--- /dev/null
+++ b/frontend-temp/src/components/pc/admin/album/PhotoPreviewModal.jsx
@@ -0,0 +1,58 @@
+/**
+ * 사진/비디오 미리보기 모달 컴포넌트
+ */
+import { memo } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import { X } from 'lucide-react';
+
+/**
+ * @param {Object} props
+ * @param {Object|null} props.photo - 미리보기할 사진/비디오 객체
+ * @param {Function} props.onClose - 닫기 핸들러
+ */
+const PhotoPreviewModal = memo(function PhotoPreviewModal({ photo, onClose }) {
+ return (
+
+ {photo && (
+
+
+ {photo.isVideo ? (
+ e.stopPropagation()}
+ controls
+ autoPlay
+ />
+ ) : (
+ e.stopPropagation()}
+ />
+ )}
+
+ )}
+
+ );
+});
+
+export default PhotoPreviewModal;
diff --git a/frontend-temp/src/components/pc/admin/album/index.js b/frontend-temp/src/components/pc/admin/album/index.js
index 2d27312..f90e0bd 100644
--- a/frontend-temp/src/components/pc/admin/album/index.js
+++ b/frontend-temp/src/components/pc/admin/album/index.js
@@ -1 +1,5 @@
export { default as TrackItem } from './TrackItem';
+export { default as PendingFileItem } from './PendingFileItem';
+export { default as BulkEditPanel, parseRange } from './BulkEditPanel';
+export { default as PhotoGrid } from './PhotoGrid';
+export { default as PhotoPreviewModal } from './PhotoPreviewModal';
diff --git a/frontend-temp/src/pages/pc/admin/albums/AlbumPhotos.jsx b/frontend-temp/src/pages/pc/admin/albums/AlbumPhotos.jsx
index a83c8c8..67c6154 100644
--- a/frontend-temp/src/pages/pc/admin/albums/AlbumPhotos.jsx
+++ b/frontend-temp/src/pages/pc/admin/albums/AlbumPhotos.jsx
@@ -2,28 +2,20 @@
* 관리자 앨범 사진 관리 페이지
*/
import { useState, useEffect, useRef } from 'react';
-import { useNavigate, Link, useParams } from 'react-router-dom';
+import { Link, useParams } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { motion, AnimatePresence, Reorder } from 'framer-motion';
-import {
- Upload,
- Trash2,
- Image,
- X,
- Check,
- Plus,
- Home,
- ChevronRight,
- GripVertical,
- Users,
- User,
- Users2,
- Tag,
- FolderOpen,
- Save,
-} from 'lucide-react';
+import { Upload, Trash2, Image, Plus, Home, ChevronRight, FolderOpen, Save } from 'lucide-react';
import { Toast } from '@/components/common';
-import { AdminLayout, ConfirmDialog } from '@/components/pc/admin';
+import {
+ AdminLayout,
+ ConfirmDialog,
+ PendingFileItem,
+ BulkEditPanel,
+ PhotoGrid,
+ PhotoPreviewModal,
+ parseRange,
+} from '@/components/pc/admin';
import { useAdminAuth } from '@/hooks/pc/admin';
import { useToast } from '@/hooks/common';
import { adminAlbumApi, adminMemberApi } from '@/api/admin';
@@ -65,32 +57,6 @@ function AdminAlbumPhotos() {
conceptName: '',
});
- // 범위 문자열 파싱
- const parseRange = (rangeStr, baseNumber = 1) => {
- if (!rangeStr.trim()) return [];
- const indices = new Set();
- const parts = rangeStr.split(',').map((s) => s.trim());
-
- for (const part of parts) {
- if (part.includes('-')) {
- const [start, end] = part.split('-').map((n) => parseInt(n.trim()));
- if (!isNaN(start) && !isNaN(end)) {
- for (let i = Math.min(start, end); i <= Math.max(start, end); i++) {
- const idx = i - baseNumber;
- if (idx >= 0) indices.add(idx);
- }
- }
- } else {
- const num = parseInt(part);
- if (!isNaN(num)) {
- const idx = num - baseNumber;
- if (idx >= 0) indices.add(idx);
- }
- }
- }
- return Array.from(indices).sort((a, b) => a - b);
- };
-
// 일괄 편집 적용
const applyBulkEdit = () => {
const indices = parseRange(bulkEdit.range, startNumber);
@@ -134,16 +100,6 @@ function AdminAlbumPhotos() {
setBulkEdit({ range: '', groupType: '', members: [], conceptName: '' });
};
- // 일괄 편집 멤버 토글
- const toggleBulkMember = (memberId) => {
- setBulkEdit((prev) => ({
- ...prev,
- members: prev.members.includes(memberId)
- ? prev.members.filter((m) => m !== memberId)
- : [...prev.members, memberId],
- }));
- };
-
// 앨범 정보 로드
const {
data: album,
@@ -527,46 +483,7 @@ function AdminAlbumPhotos() {
/>
{/* 이미지 미리보기 */}
-
- {previewPhoto && (
- setPreviewPhoto(null)}
- >
-
- {previewPhoto.isVideo ? (
- e.stopPropagation()}
- controls
- autoPlay
- />
- ) : (
- e.stopPropagation()}
- />
- )}
-
- )}
-
+ setPreviewPhoto(null)} />
{/* 브레드크럼 */}
@@ -838,185 +755,21 @@ function AdminAlbumPhotos() {
className="space-y-3"
>
{pendingFiles.map((file, index) => (
-
-
- {/* 드래그 핸들 + 순서 번호 */}
-
-
- {
- const val = e.target.value.trim();
- const scrollY = window.scrollY;
- const currentIndex = pendingFiles.findIndex((f) => f.id === file.id);
- const currentOrder = startNumber + currentIndex;
-
- if (val && !isNaN(val) && parseInt(val) !== currentOrder) {
- moveToPosition(file.id, val);
- }
-
- const newIndex = pendingFiles.findIndex((f) => f.id === file.id);
- e.target.value = String(startNumber + newIndex).padStart(2, '0');
-
- requestAnimationFrame(() => {
- window.scrollTo(0, scrollY);
- });
- }}
- onKeyDown={(e) => {
- if (e.key === 'Enter') {
- e.target.blur();
- }
- }}
- className="w-10 h-8 bg-primary/10 text-primary rounded-lg text-center text-sm font-bold border-0 focus:outline-none focus:ring-2 focus:ring-primary [appearance:textfield]"
- />
-
-
- {/* 썸네일 */}
- {file.isVideo ? (
-
-
- ) : (
-

setPreviewPhoto(file)}
- />
- )}
-
- {/* 메타 정보 */}
-
-
- {file.filename}
-
-
- {photoType === 'concept' && (
- <>
- {/* 단체/솔로/유닛 선택 */}
-
-
타입:
-
- {[
- { value: 'group', icon: Users, label: '단체' },
- { value: 'solo', icon: User, label: '개인' },
- { value: 'unit', icon: Users2, label: '유닛' },
- ].map(({ value, icon: Icon, label }) => (
-
- ))}
-
-
-
- {/* 멤버 태깅 */}
-
-
- 멤버:
- {file.groupType === 'group' ? (
-
- 단체 사진은 멤버 태깅이 필요 없습니다
-
- ) : (
- <>
- {members
- .filter((m) => !m.is_former)
- .map((member) => (
-
- ))}
- >
- )}
-
- {file.groupType !== 'group' &&
- members.filter((m) => m.is_former).length > 0 && (
-
-
- {members
- .filter((m) => m.is_former)
- .map((member) => (
-
- ))}
-
- )}
-
-
- {/* 컨셉명 */}
-
- 컨셉명:
-
- updatePendingFile(file.id, 'conceptName', e.target.value)
- }
- className="flex-1 px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary"
- placeholder="컨셉명을 입력하세요"
- />
-
- >
- )}
-
-
- {/* 삭제 버튼 */}
-
-
-
+ file={file}
+ index={index}
+ startNumber={startNumber}
+ photoType={photoType}
+ members={members}
+ pendingFiles={pendingFiles}
+ onPreview={setPreviewPhoto}
+ onDelete={setPendingDeleteId}
+ onUpdateFile={updatePendingFile}
+ onToggleMember={toggleMember}
+ onChangeGroupType={changeGroupType}
+ onMoveToPosition={moveToPosition}
+ />
))}
@@ -1025,154 +778,14 @@ function AdminAlbumPhotos() {
{/* 일괄 편집 도구 */}
{pendingFiles.length > 0 && photoType === 'concept' && (
-
-
-
-
- 일괄 편집
-
-
- {/* 번호 범위 */}
-
-
-
setBulkEdit((prev) => ({ ...prev, range: e.target.value }))}
- placeholder={`예: ${startNumber}-${startNumber + 4}, ${startNumber + 7}`}
- className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary"
- />
-
- {startNumber}~{startNumber + pendingFiles.length - 1}번 중{' '}
- {parseRange(bulkEdit.range, startNumber).filter((i) => i < pendingFiles.length).length}
- 개 선택
-
-
-
- {/* 타입 선택 */}
-
-
-
- {[
- { value: 'group', icon: Users, label: '단체' },
- { value: 'solo', icon: User, label: '개인' },
- { value: 'unit', icon: Users2, label: '유닛' },
- ].map(({ value, icon: Icon, label }) => (
-
- ))}
-
-
-
- {/* 멤버 선택 */}
- {bulkEdit.groupType !== 'group' && (
-
-
-
- {members
- .filter((m) => !m.is_former)
- .map((member) => (
-
- ))}
- {members.filter((m) => m.is_former).length > 0 && (
- |
- )}
- {members
- .filter((m) => m.is_former)
- .map((member) => (
-
- ))}
-
-
- )}
-
- {/* 컨셉명 */}
-
-
-
- setBulkEdit((prev) => ({ ...prev, conceptName: e.target.value }))
- }
- placeholder="컨셉명 입력"
- className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary"
- />
-
-
- {/* 적용 버튼 */}
-
-
-
+
)}
>
@@ -1269,146 +882,30 @@ function AdminAlbumPhotos() {
{/* 컨셉 포토 그리드 */}
{manageSubTab === 'concept' && (
- <>
- {photos.length === 0 ? (
-
-
-
등록된 컨셉 포토가 없습니다
-
업로드 탭에서 사진을 추가하세요
-
- ) : (
-
- {photos.map((photo, index) => (
-
{
- setSelectedPhotos((prev) =>
- prev.includes(photo.id)
- ? prev.filter((id) => id !== photo.id)
- : [...prev, photo.id]
- );
- }}
- >
-
-
-
-
- {selectedPhotos.includes(photo.id) && (
-
- )}
-
-
-
- {String(photo.sort_order).padStart(2, '0')}
-
-
-
- {photo.concept_name && (
-
- {photo.concept_name}
-
- )}
-
-
- ))}
-
- )}
- >
+ {
+ setSelectedPhotos((prev) =>
+ prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id]
+ );
+ }}
+ type="concept"
+ />
)}
{/* 티저 이미지 그리드 */}
{manageSubTab === 'teaser' && (
- <>
- {teasers.length === 0 ? (
-
-
-
등록된 티저 이미지가 없습니다
-
업로드 탭에서 티저를 추가하세요
-
- ) : (
-
- {teasers.map((teaser, index) => (
-
{
- const teaserId = `teaser-${teaser.id}`;
- setSelectedPhotos((prev) =>
- prev.includes(teaserId)
- ? prev.filter((id) => id !== teaserId)
- : [...prev, teaserId]
- );
- }}
- >
- {teaser.media_type === 'video' ? (
-
- ))}
-
- )}
- >
+ {
+ setSelectedPhotos((prev) =>
+ prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id]
+ );
+ }}
+ type="teaser"
+ />
)}
)}