From 9b0a6d8f15e5af24c56d758d817f4c2adac1e9c5 Mon Sep 17 00:00:00 2001 From: zenn Date: Thu, 2 Apr 2026 18:57:12 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A6=B4=EB=A6=AC=EC=8A=A4:=20v1.4.10=20topicH?= =?UTF-8?q?ub=20=EB=9D=BC=EC=9A=B0=ED=8A=B8=20=EB=AA=85=EC=B9=AD=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/history.md | 3 +++ docs/todo.md | 2 ++ docs/update.md | 4 ++++ frontend/src/App.vue | 13 +++++++------ frontend/src/router/index.js | 6 +++--- frontend/src/views/GameHubView.vue | 2 +- frontend/src/views/TierEditorView.vue | 2 +- 7 files changed, 21 insertions(+), 11 deletions(-) diff --git a/docs/history.md b/docs/history.md index 9c64999..3c5ff83 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,5 +1,8 @@ # 의사결정 이력 +## 2026-04-02 v1.4.10 +- 사용자 주소는 이미 `/topics`로 옮기기 시작했으므로, 라우트 이름과 기본 파라미터도 `topicHub / topicId` 기준으로 맞추고 기존 `gameId`는 호환 fallback으로만 남기는 편이 더 자연스럽다고 판단했다. + ## 2026-04-02 v1.4.9 - 경로 전환은 화면마다 문자열을 직접 고치는 방식보다, 공용 경로 헬퍼를 먼저 세워 주제·에디터·로그인 리다이렉트 흐름을 한 기준으로 묶는 편이 이후 리네이밍 비용을 훨씬 줄인다고 판단했다. diff --git a/docs/todo.md b/docs/todo.md index f853e28..86a995f 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -1,6 +1,8 @@ # 할 일 및 이슈 ## 단기 확인 +- `topicHub / topicId`를 기본 라우트 기준으로 세웠으므로, 기존 `/games/...` 북마크와 새 `/topics/...` 주소 양쪽에서 주제 상세와 에디터 진입이 모두 자연스럽게 이어지는지 한 번 더 QA한다. +- 다음 단계에서는 `api.getGame`, `listGames`, `favoriteGame`처럼 남아 있는 프런트 API 이름을 어느 수준까지 `topic/template` 의미로 감쌀지 정리한다. - 경로 헬퍼를 사용자 주요 화면에 연결했으므로, 로그인 리다이렉트·공유 프리뷰·복사본 이동·주제 복귀 흐름이 실제 브라우저에서 모두 같은 주소 체계로 자연스럽게 이어지는지 한 번 더 QA한다. - 다음 단계에서는 `router/index.js`의 `gameHub`, `route.params.gameId`, `api.getGame`처럼 남아 있는 프런트 내부 이름도 어디까지 `topic/template` 의미로 감쌀지 범위를 더 좁힌다. - 주제 상세 화면 제목은 ID fallback 대신 로딩 문구를 쓰도록 바꿨으므로, 직접 진입·뒤로가기·다른 주제로 연속 이동할 때 헤더가 깜빡이거나 이전 제목을 잠깐 유지하지 않는지 한 번 더 QA한다. diff --git a/docs/update.md b/docs/update.md index 3f92402..749324a 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,9 @@ # 업데이트 로그 +## 2026-04-02 v1.4.10 +- 주제 상세 라우트 이름을 `topicHub`로, 기본 경로 파라미터를 `topicId`로 바꾸고 기존 `gameId` 주소는 alias로 유지했다. +- 앱 셸, 주제 상세, 티어표 편집기는 이제 내부에서 `topicId`를 우선 읽고, 레거시 주소로 들어온 경우에만 `gameId` fallback을 쓰도록 정리했다. + ## 2026-04-02 v1.4.9 - `frontend/src/lib/paths.js`를 추가해 주제 진입, 에디터 이동, 로그인 리다이렉트, 공유 프리뷰 주소 같은 사용자 표면 경로를 공용 함수로 모았다. - 홈, 주제 상세, 나의 티어표, 즐겨찾기, 검색 결과, 로그인, 설정, 관리자 미리보기, 티어표 편집기까지 이 경로 헬퍼를 쓰도록 바꿔 이후 `topics` 전환을 더 안전하게 이어갈 수 있는 기반을 만들었다. diff --git a/frontend/src/App.vue b/frontend/src/App.vue index a2887fa..b3ef536 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -23,6 +23,7 @@ const router = useRouter() const auth = useAuthStore() const { toasts, dismissToast } = useToast() const RIGHT_RAIL_COPYRIGHT_URL = 'https://zenn.town/@murabito' +const currentTopicId = computed(() => route.params.topicId || route.params.gameId || '') const leftRailCollapsed = ref(false) const rightRailOpen = ref(true) @@ -135,15 +136,15 @@ const isGuideNextDisabled = computed(() => guideStepIndex.value >= guideSteps.le const isLightTheme = computed(() => themeMode.value === 'light') const themeToggleLabel = computed(() => (isLightTheme.value ? '다크 모드' : '라이트 모드')) const showSettingsThemePanel = computed(() => false && route.name === 'profile') -const showTopicViewToggle = computed(() => route.name === 'gameHub') +const showTopicViewToggle = computed(() => route.name === 'topicHub') const topicViewMode = computed(() => (route.query.view === 'list' ? 'list' : 'grid')) const leftBottomPrimaryAction = computed(() => { if (!authReady.value) return null if (route.name === 'home' && auth.user) { return { label: '커스텀 티어표 만들기', to: editorNewPath('freeform'), iconSrc: iconDashboardCustomize } } - if (route.name === 'gameHub') { - const target = editorNewPath(route.params.gameId) + if (route.name === 'topicHub') { + const target = editorNewPath(currentTopicId.value) return { label: '새 티어표 만들기', to: auth.user ? target : loginPath(target), iconSrc: iconAddNotes } } return null @@ -162,7 +163,7 @@ const routeMeta = computed(() => { }, } } - if (route.name === 'gameHub') { + if (route.name === 'topicHub') { return { title: '주제 티어표', subtitle: '주제별 공개 티어표 탐색', @@ -170,7 +171,7 @@ const routeMeta = computed(() => { contextText: auth.user ? '이 주제의 새 티어표를 만들거나 기존 공개 티어표를 확인할 수 있어요.' : '로그인 후 새 티어표를 만들 수 있어요.', actionLabel: auth.user ? '새 티어표 만들기' : '로그인하러 가기', action: () => { - const target = editorNewPath(route.params.gameId) + const target = editorNewPath(currentTopicId.value) router.push(auth.user ? target : loginPath(target)) }, } @@ -346,7 +347,7 @@ function toggleRightRail() { } function setTopicViewMode(mode) { - if (route.name !== 'gameHub') return + if (route.name !== 'topicHub') return const nextQuery = { ...route.query } if (mode === 'list') nextQuery.view = 'list' else delete nextQuery.view diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 24a363d..bbc2693 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -16,9 +16,9 @@ export function createRouter() { history: createWebHistory(), routes: [ { path: '/', name: 'home', component: HomeView }, - { path: '/topics/:gameId', alias: ['/games/:gameId'], name: 'gameHub', component: GameHubView }, - { path: '/editor/:gameId/new', name: 'newEditor', component: TierEditorView }, - { path: '/editor/:gameId/:tierListId', name: 'editEditor', component: TierEditorView }, + { path: '/topics/:topicId', alias: ['/games/:gameId'], name: 'topicHub', component: GameHubView }, + { path: '/editor/:topicId/new', alias: ['/editor/:gameId/new'], name: 'newEditor', component: TierEditorView }, + { path: '/editor/:topicId/:tierListId', alias: ['/editor/:gameId/:tierListId'], name: 'editEditor', component: TierEditorView }, { path: '/login', name: 'login', component: LoginView }, { path: '/me', name: 'me', component: MyTierListsView }, { path: '/favorites', name: 'favorites', component: FavoriteTierListsView }, diff --git a/frontend/src/views/GameHubView.vue b/frontend/src/views/GameHubView.vue index 4793e74..6878f5b 100644 --- a/frontend/src/views/GameHubView.vue +++ b/frontend/src/views/GameHubView.vue @@ -10,7 +10,7 @@ const route = useRoute() const router = useRouter() const auth = useAuthStore() -const topicId = computed(() => route.params.gameId) +const topicId = computed(() => route.params.topicId || route.params.gameId) const topicName = ref('') const tierLists = ref([]) const error = ref('') diff --git a/frontend/src/views/TierEditorView.vue b/frontend/src/views/TierEditorView.vue index d79b0e8..d461e63 100644 --- a/frontend/src/views/TierEditorView.vue +++ b/frontend/src/views/TierEditorView.vue @@ -20,7 +20,7 @@ const auth = useAuthStore() const toast = useToast() const globalRightRailOpen = inject('rightRailOpen', ref(true)) const localRightRailTarget = inject('localRightRailTarget', '#local-right-rail-root') -const templateId = computed(() => route.params.gameId) +const templateId = computed(() => route.params.topicId || route.params.gameId) const tierListId = computed(() => route.params.tierListId) const previewMode = computed(() => route.query.preview === '1') const templateName = ref('')