fix(admin-panel): useSearchParams 제거 + deploy 강건성 개선
Deploy momo-erp / deploy (push) Failing after 43s
Deploy momo-erp / deploy (push) Failing after 43s
- useSearchParams 가 Next.js 15 prerender 단계에서 Suspense 경계를 강제해 /admin-panel 빌드 자체가 실패 → docker image 재빌드 안 됨 → 컨테이너 swap 누락(2시간째 옛 이미지). window.location.search 직접 읽기로 대체 - deploy.yml: set +e 제거 (빌드 실패가 워크플로우 success 로 묻히는 문제 차단) - docker compose 에 --force-recreate 추가 (이미지가 같아도 컨테이너 강제 재생성) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,14 +16,14 @@ jobs:
|
||||
|
||||
- name: Deploy via SSH (password auth)
|
||||
run: |
|
||||
set +e # 배포 단계 실패해도 워크플로우 성공 처리 (실제 결과는 헬스체크가 판단)
|
||||
set -e # 배포 단계 실패하면 즉시 워크플로우 fail (헬스체크에 의존하지 않음)
|
||||
export SSHPASS='qlalfqjsgh11'
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan -H 183.99.177.40 >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||
|
||||
sshpass -e ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
chpark@183.99.177.40 'bash -s' <<'REMOTE_SCRIPT'
|
||||
set +e
|
||||
set -e # 원격 명령도 fail 즉시 중단
|
||||
DEPLOY_DIR="$HOME/momo-erp/source"
|
||||
mkdir -p "$HOME/momo-erp"
|
||||
|
||||
@@ -62,7 +62,8 @@ jobs:
|
||||
DEPLOY_WEBHOOK_TOKEN=momo-deploy-2026-secure
|
||||
ENVEOF
|
||||
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
# --force-recreate: docker compose 가 변화 감지 못해 컨테이너 swap 안 하는 케이스 방지
|
||||
docker compose -f docker-compose.prod.yml up -d --build --force-recreate momo-erp
|
||||
|
||||
# 마이그레이션 (idempotent) — 컨테이너 안에 db/migrations + scripts/migrate-momo.mjs 가
|
||||
# standalone 번들에 포함되어 있어야 동작 (next.config.ts outputFileTracingIncludes).
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useCallback, useEffect, useMemo } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { DataGrid, type GridColumn } from "@/components/grid/data-grid";
|
||||
import { SearchForm, SearchField } from "@/components/layout/search-form";
|
||||
@@ -106,22 +105,25 @@ const VALID_TABS: AdminTab[] = [
|
||||
"ref-product-group","ref-product","spec-data-category","car-option",
|
||||
];
|
||||
|
||||
function readTabFromUrl(): AdminTab {
|
||||
if (typeof window === "undefined") return "user";
|
||||
const t = new URLSearchParams(window.location.search).get("tab");
|
||||
return t && (VALID_TABS as string[]).includes(t) ? (t as AdminTab) : "user";
|
||||
}
|
||||
|
||||
export default function AdminPanelPage() {
|
||||
const searchParams = useSearchParams();
|
||||
const tabParam = searchParams.get("tab");
|
||||
const initialTab: AdminTab = tabParam && (VALID_TABS as string[]).includes(tabParam)
|
||||
? (tabParam as AdminTab)
|
||||
: "user";
|
||||
const [activeTab, setActiveTab] = useState<AdminTab>(initialTab);
|
||||
const [activeTab, setActiveTab] = useState<AdminTab>("user");
|
||||
const [groups, setGroups] = useState<SidebarGroup[]>([]);
|
||||
const [openSections, setOpenSections] = useState<Set<string>>(new Set(["권한 및 사용자 관리"]));
|
||||
|
||||
// 사이드바에서 ?tab= 으로 다른 탭 클릭 시 동기화
|
||||
// 마운트 + popstate(뒤로가기) 시 ?tab= 으로 activeTab 동기화.
|
||||
// useSearchParams 는 Next.js 15 의 prerender 단계에서 Suspense 경계를 강제하므로 사용하지 않음.
|
||||
useEffect(() => {
|
||||
if (tabParam && (VALID_TABS as string[]).includes(tabParam)) {
|
||||
setActiveTab(tabParam as AdminTab);
|
||||
}
|
||||
}, [tabParam]);
|
||||
setActiveTab(readTabFromUrl());
|
||||
const sync = () => setActiveTab(readTabFromUrl());
|
||||
window.addEventListener("popstate", sync);
|
||||
return () => window.removeEventListener("popstate", sync);
|
||||
}, []);
|
||||
|
||||
const toggleSection = (label: string) => {
|
||||
setOpenSections((prev) => {
|
||||
|
||||
Reference in New Issue
Block a user