Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/app/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div
className={
Expand Down Expand Up @@ -53,9 +65,9 @@ export default function ErrorPage({ reset }: { reset?: () => 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}
</button>
</div>
)
Expand Down
25 changes: 18 additions & 7 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div
className={
Expand Down Expand Up @@ -48,14 +63,10 @@ export default function NotFound() {
μš”μ²­ν•˜μ‹  νŽ˜μ΄μ§€λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.
</div>
<button
className={
isDark
? '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={() => window.history.back()}
className={`mt-8 rounded-lg px-8 py-3 text-lg font-semibold text-white shadow transition-colors ${buttonClass}`}
onClick={() => router.replace('/')}
>
이전 νŽ˜μ΄μ§€λ‘œ
{buttonText}
</button>
</div>
)
Expand Down
25 changes: 13 additions & 12 deletions src/app/shared/components/Redirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -20,33 +28,30 @@ 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)
prevPath.current = pathname
}
}, [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)
Expand All @@ -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,
Expand All @@ -74,11 +76,10 @@ export default function Redirect({ children }: { children: React.ReactNode }) {
firstDashboardId,
isRoot,
isPublic,
isProtectedRoute,
])

// πŸ”’ κΉœλΉ‘μž„ λ°©μ§€: 루트 경둜만 μ˜ˆμ™Έλ‘œ μ¦‰μ‹œ λ Œλ”λ§ ν—ˆμš©
if (!mounted && !isRoot) return null

// βœ… μ΅œμ’… λ Œλ”λ§
return <>{children}</>
}
Loading