From b3575d59a6947d8e47ef0bd2cd52e4d5b5592025 Mon Sep 17 00:00:00 2001 From: zenn Date: Thu, 2 Apr 2026 21:14:43 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A6=B4=EB=A6=AC=EC=8A=A4:=20v1.4.28=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=ED=85=9C=ED=94=8C=EB=A6=BF=20?= =?UTF-8?q?=EB=82=B4=EB=B6=80=20=EC=9D=B4=EB=A6=84=EC=B8=B5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/db.js | 4 +- backend/src/routes/admin.js | 8 +- docs/history.md | 4 + docs/todo.md | 3 + docs/update.md | 5 + frontend/src/App.vue | 2 +- .../components/admin/AdminGamesSection.vue | 24 ++-- .../src/composables/useAdminGameManager.js | 47 ++++--- frontend/src/views/AdminView.vue | 115 +++++++++--------- 9 files changed, 112 insertions(+), 100 deletions(-) diff --git a/backend/src/db.js b/backend/src/db.js index c96fd59..28cc82a 100644 --- a/backend/src/db.js +++ b/backend/src/db.js @@ -788,7 +788,7 @@ async function getTopicDetail(topicId) { const topic = await findTopicById(topicId) if (!topic) return null const items = await listTopicItems(topicId) - return { topic, game: topic, items } + return { topic, template: topic, items } } async function createTopic({ id, name, isPublic = true }) { @@ -1193,7 +1193,7 @@ async function cleanupMissingUploadReferences() { for (const row of gameItemRows) { if (await fileExistsForUploadSrc(row.src)) continue - await deleteGameItem(row.id) + await deleteTopicItem(row.id) stats.deletedGameItems += 1 } diff --git a/backend/src/routes/admin.js b/backend/src/routes/admin.js index c1edb43..692248f 100644 --- a/backend/src/routes/admin.js +++ b/backend/src/routes/admin.js @@ -187,7 +187,7 @@ router.post('/templates/:templateId/thumbnail', requireAdmin, upload.single('thu const optimized = await writeOptimizedImage({ file: req.file, - directory: 'games', + directory: 'topics', width: 1280, height: 1280, fit: 'inside', @@ -214,7 +214,7 @@ router.post('/templates/:templateId/images', requireAdmin, upload.array('images' files.map(async (file, index) => { const optimized = await writeOptimizedImage({ file, - directory: 'games', + directory: 'topics', width: 512, height: 512, fit: 'inside', @@ -593,7 +593,7 @@ async function createTemplateFromTierList({ tierList, templateId, templateName } ) } - return { game: await findTopicById(templateId), items: createdItems } + return { template: await findTopicById(templateId), items: createdItems } } async function createTemplateFromRequest({ templateRequest, templateId, templateName }) { @@ -609,7 +609,7 @@ async function createTemplateFromRequest({ templateRequest, templateId, template templateId, }) - return { game: await findTopicById(templateId), items } + return { template: await findTopicById(templateId), items } } router.delete('/custom-items/:itemId', requireAdmin, async (req, res) => { diff --git a/docs/history.md b/docs/history.md index 97e3801..466e6f9 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,5 +1,9 @@ # 의사결정 이력 +## 2026-04-02 v1.4.28 +- 이 시점 이후 코드 검색에 남는 `game`는 대부분 레거시 데이터 마이그레이션, 옛 주소 redirect, 저장 데이터의 `origin` 호환처럼 의도된 층이므로, 무리하게 전부 0으로 만들기보다 기능을 깨뜨리지 않는 선에서 의미 있는 이름층만 더 줄이는 편이 맞다고 판단했다. +- 관리자 화면 내부 상태명(`selectedTemplate.game`, `isGameLoading`, `gameVisibilitySaving`)은 실제 기능 의미와 어긋나므로, QA 전에 한 번 더 `template` 기준으로 옮겨두는 편이 이후 유지보수에 더 유리하다고 정리했다. + ## 2026-04-02 v1.4.27 - 공개/관리자 API 표면까지 `topic/template`로 정리된 뒤에는, 관리자 내부 상태 이름과 DB export alias에 남은 `game` 흔적도 계속 유지할 이유가 작아졌으므로 이 단계에서 함께 걷어내는 편이 맞다고 판단했다. - 다만 외부에서 직접 참조할 수 있는 공개 북마크와 달리, `adminGames`, `game-admin`, `favoriteGame` 같은 이름은 내부 구현 용어라서 이번 단계에서 정리해도 위험이 낮다고 정리했다. diff --git a/docs/todo.md b/docs/todo.md index 2277d8f..3535ab9 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -1,6 +1,9 @@ # 할 일 및 이슈 ## 단기 확인 +- `v1.4.28`에서 관리자 템플릿 상세 상태와 기본 아이템 정렬 상태 이름을 `template` 기준으로 더 정리했으므로, 관리자 템플릿 선택/공개 전환/기본 아이템 정렬 저장이 그대로 정상인지 한 번 더 확인한다. +- 새 템플릿 썸네일/기본 아이템 업로드는 이제 `topics` 디렉터리로 저장되므로, 실제 업로드 후 최적화 작업 분류와 관리자 최근 작업 표시가 자연스럽게 보이는지 확인한다. +- 현재 코드 검색에 남는 `game`는 레거시 redirect, DB 마이그레이션, `origin: 'game'` 호환이 중심이므로, 이 층까지 실제로 없앨지 여부는 `v1.4` QA 후 안정성 기준으로 다시 판단한다. - `v1.4.27`에서 관리자 내부 탭/라우트 이름과 DB alias export까지 더 정리했으므로, 관리자 템플릿 탭 이동, 커스텀 아이템에서 템플릿 관리로 점프, 템플릿 요청 확인하기 이동이 모두 정상인지 한 번 더 확인한다. - `v1.4.26`에서 관리자 기본 경로를 `/admin/templates`로 바꾸고 `/api/admin/templates`만 남겼으므로, 관리자 진입/새로고침/뒤로가기와 템플릿 생성·썸네일 업로드·아이템 추가가 모두 정상인지 확인한다. - `v1.4.26`에서 공개 API `/api/games`를 제거했으므로, 실제 서버 재시작 후 홈/주제 상세/티어표 편집기에서 `/api/topics`만으로 모두 정상 동작하는지 확인한다. diff --git a/docs/update.md b/docs/update.md index f254519..fd2e74b 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,10 @@ # 업데이트 로그 +## 2026-04-02 v1.4.28 +- 관리자 템플릿 상세 상태(`selectedTemplate.game`)와 관련 응답 키를 `template` 기준으로 정리해, 내부 코드 검색에서 남던 `game` 흔적을 더 줄였다. +- 관리자 기본 아이템 정렬/로딩 상태 이름도 `templateItem*`, `isTemplateLoading`, `templateVisibilitySaving` 기준으로 바꾸고, 새 템플릿 자산 업로드는 `topics` 디렉터리로 저장되게 맞췄다. +- 현재 코드 검색에서 남는 `game`는 주로 레거시 주소 redirect(`/games/:gameId`), DB 마이그레이션용 legacy 테이블/컬럼명, 기존 저장 데이터와 맞춘 `origin: 'game'` 값처럼 의도적으로 남겨둔 호환층만 남도록 정리했다. + ## 2026-04-02 v1.4.27 - 관리자 내부 탭/라우트 이름도 `template-admin`, `adminTemplates`, `/admin/templates` 기준으로 더 정리해, 화면 상태값과 라우트 이름에 남아 있던 `game-admin`, `adminGames` 흔적을 줄였다. - 더 이상 참조되지 않는 DB alias export(`listGames`, `createGame`, `favoriteGame` 등)와 `updateTemplateRequestTargetGame` 별칭도 제거해, 백엔드 모듈 표면에서 남아 있던 레거시 `game` 이름층을 더 걷어냈다. diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 0599234..c1274db 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -76,7 +76,7 @@ const showRightRailAction = computed(() => false) const showSettingsGuideButton = computed(() => route.name === 'profile') const guideSteps = [ { - id: 'select-game', + id: 'select-topic', title: '주제 또는 양식 선택', summary: '주제 템플릿을 고르거나 커스텀 티어표 만들기로 바로 시작합니다.', description: diff --git a/frontend/src/components/admin/AdminGamesSection.vue b/frontend/src/components/admin/AdminGamesSection.vue index 4e95331..19fe363 100644 --- a/frontend/src/components/admin/AdminGamesSection.vue +++ b/frontend/src/components/admin/AdminGamesSection.vue @@ -9,12 +9,12 @@ const props = defineProps({ stagedRequestDraftCount: { type: Number, required: true }, appliedRequestItemCount: { type: Number, required: true }, openTemplateCreateModal: { type: Function, required: true }, - isGameLoading: { type: Boolean, required: true }, + isTemplateLoading: { type: Boolean, required: true }, hasSelectedTemplate: { type: Boolean, required: true }, selectedTemplate: { type: Object, default: null }, displayThumbnailUrl: { type: String, default: '' }, canApplyThumbnail: { type: Boolean, required: true }, - gameVisibilitySaving: { type: Boolean, required: true }, + templateVisibilitySaving: { type: Boolean, required: true }, thumbFileInputRef: { type: Function, required: true }, openThumbFilePicker: { type: Function, required: true }, onThumb: { type: Function, required: true }, @@ -41,14 +41,14 @@ const props = defineProps({ removeUploadDraft: { type: Function, required: true }, hasTemplateItemOrderChanges: { type: Boolean, required: true }, saveTemplateItemOrder: { type: Function, required: true }, - gameItemListRef: { type: Function, required: true }, + templateItemListRef: { type: Function, required: true }, saveTemplateItemLabel: { type: Function, required: true }, removeTemplateItem: { type: Function, required: true }, selectedTemplateId: { type: String, default: '' }, }) function setGameItemListElement(el) { - props.gameItemListRef(el) + props.templateItemListRef(el) } function setThumbFileElement(el) { @@ -102,7 +102,7 @@ function setThumbFileElement(el) { -
+
템플릿 정보를 불러오는 중이에요.
선택한 템플릿의 썸네일과 기본 아이템을 곧 표시합니다.
@@ -122,7 +122,7 @@ function setThumbFileElement(el) { @dragleave="props.onThumbDragLeave" @drop="props.onThumbDrop" > - +
대표 썸네일
@@ -134,10 +134,10 @@ function setThumbFileElement(el) {
템플릿 설정
-
{{ props.selectedTemplate.game.name }} · {{ props.selectedTemplate.game.id }}
-