diff --git a/src/app/error.tsx b/src/app/error.tsx
index 3eb337a..2c03c1b 100644
--- a/src/app/error.tsx
+++ b/src/app/error.tsx
@@ -2,16 +2,28 @@
import { useMounted } from '@hooks/useMounted'
import Image from 'next/image'
+import { useRouter } from 'next/navigation'
import { useTheme } from 'next-themes'
export default function ErrorPage({ reset }: { reset?: () => void }) {
const { theme, systemTheme } = useTheme()
+ const router = useRouter()
const mounted = useMounted()
if (!mounted) return null
const currentTheme = theme === 'system' ? systemTheme : theme
const isDark = currentTheme === 'dark'
+ const handleClick = () => {
+ if (reset) {
+ reset()
+ } else {
+ router.back()
+ }
+ }
+
+ const buttonText = reset ? '다시 시도하기' : '이전 페이지로'
+
return (
void }) {
? 'mt-8 rounded-lg bg-blue-700 px-8 py-3 text-lg font-semibold text-white shadow transition-colors hover:bg-blue-800'
: 'mt-8 rounded-lg bg-blue-600 px-8 py-3 text-lg font-semibold text-white shadow transition-colors hover:bg-blue-700'
}
- onClick={() => (reset ? reset() : window.history.back())}
+ onClick={handleClick}
>
- 이전 페이지로
+ {buttonText}
)
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
index a2815fb..7e0f064 100644
--- a/src/app/not-found.tsx
+++ b/src/app/not-found.tsx
@@ -2,16 +2,31 @@
import { useMounted } from '@hooks/useMounted'
import Image from 'next/image'
+import { useRouter } from 'next/navigation'
import { useTheme } from 'next-themes'
+import { useAuthStore } from '@/app/features/auth/store/useAuthStore'
+
export default function NotFound() {
const { theme, systemTheme } = useTheme()
const mounted = useMounted()
+ const router = useRouter()
+ const { isLoggedIn } = useAuthStore()
+
if (!mounted) return null
const currentTheme = theme === 'system' ? systemTheme : theme
const isDark = currentTheme === 'dark'
+ const buttonText = isLoggedIn ? '대시보드로 이동' : '메인으로 이동'
+ const buttonClass = isDark
+ ? isLoggedIn
+ ? 'bg-blue-700 hover:bg-blue-800'
+ : 'bg-blue-500 hover:bg-blue-600'
+ : isLoggedIn
+ ? 'bg-blue-600 hover:bg-blue-700'
+ : 'bg-blue-400 hover:bg-blue-500'
+
return (
)
diff --git a/src/app/shared/components/Redirect.tsx b/src/app/shared/components/Redirect.tsx
index 983246f..6beb8f1 100644
--- a/src/app/shared/components/Redirect.tsx
+++ b/src/app/shared/components/Redirect.tsx
@@ -10,6 +10,14 @@ import { useAuthStore } from '@/app/features/auth/store/useAuthStore'
// 로그인 없이 접근 가능한 경로
const PUBLIC_ROUTES = ['/login', '/signup']
+// 보호 경로: 로그인 필요, 정규식 기반
+const PROTECTED_ROUTE_PATTERNS = [
+ /^\/dashboard\/[^/]+$/, // /dashboard/:id
+ /^\/dashboard\/[^/]+\/edit$/, // /dashboard/:id/edit
+ /^\/mypage$/, // /mypage
+ /^\/mydashboard$/, // /mydashboard
+]
+
export default function Redirect({ children }: { children: React.ReactNode }) {
const router = useRouter()
const pathname = usePathname()
@@ -20,11 +28,12 @@ export default function Redirect({ children }: { children: React.ReactNode }) {
const { data: firstDashboardId, isSuccess } = useFirstDashboardIdQuery()
- // ✅ 경로 파생값 선언 (중복 제거)
const isRoot = pathname === '/'
const isPublic = PUBLIC_ROUTES.includes(pathname)
+ const isProtectedRoute = PROTECTED_ROUTE_PATTERNS.some((pattern) =>
+ pattern.test(pathname),
+ )
- // 경로 변경 시 redirecting 상태 초기화
useEffect(() => {
if (prevPath.current !== pathname) {
setRedirecting(false)
@@ -32,21 +41,17 @@ export default function Redirect({ children }: { children: React.ReactNode }) {
}
}, [pathname])
- // 로그인 상태와 경로에 따른 리다이렉트 처리
useEffect(() => {
if (!mounted || redirecting) return
- // 1. 비로그인 + 루트(/): 랜딩 페이지 접근 허용
if (!isLoggedIn && isRoot) return
- // 2. 비로그인 + 보호 경로: 로그인 페이지로 이동
- if (!isLoggedIn && !isPublic && !isRoot) {
+ if (!isLoggedIn && isProtectedRoute) {
setRedirecting(true)
router.replace('/login')
return
}
- // 3. 로그인 + 루트(/): 대시보드 또는 마이대시보드로 이동
if (isLoggedIn && isRoot) {
if (!isSuccess) return
setRedirecting(true)
@@ -56,14 +61,11 @@ export default function Redirect({ children }: { children: React.ReactNode }) {
return
}
- // 4. 로그인 + 퍼블릭 경로: 마이대시보드로 이동
if (isLoggedIn && isPublic) {
setRedirecting(true)
router.replace('/mydashboard')
return
}
-
- // 5. 나머지는 접근 허용
}, [
pathname,
isLoggedIn,
@@ -74,11 +76,10 @@ export default function Redirect({ children }: { children: React.ReactNode }) {
firstDashboardId,
isRoot,
isPublic,
+ isProtectedRoute,
])
- // 🔒 깜빡임 방지: 루트 경로만 예외로 즉시 렌더링 허용
if (!mounted && !isRoot) return null
- // ✅ 최종 렌더링
return <>{children}>
}