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 }}
-