챕터 23: 네비게이션 디자인과 UX
서론
훌륭한 네비게이션은 단순히 링크의 모음이 아닙니다. 사용자가 웹사이트를 탐색하는 여정을 안내하는 지도와 같습니다. 사용자가 현재 어디에 있는지, 어디로 갈 수 있는지, 어떻게 돌아갈 수 있는지를 명확하게 알려주어야 합니다.
이번 챕터에서는 네비게이션의 다양한 패턴과 UX 원칙을 적용하여 더 나은 사용자 경험을 제공하는 방법을 배워보겠습니다. 브레드크럼, 사이드바, 탭 네비게이션, 그리고 고급 인터랙션까지 실제 프로젝트에서 활용할 수 있는 실용적인 예제들을 만들어보겠습니다.
본론
브레드크럼 네비게이션
브레드크럼은 사용자가 웹사이트의 계층 구조에서 현재 위치를 파악할 수 있도록 도와줍니다:
jsx
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
export default function BreadcrumbNavigation() {
const pathname = usePathname()
// 경로를 분석하여 브레드크럼 생성
const generateBreadcrumbs = () => {
const paths = pathname.split('/').filter(Boolean)
const breadcrumbs = [{ name: '홈', href: '/' }]
paths.forEach((path, index) => {
const href = `/${paths.slice(0, index + 1).join('/')}`
// 경로명을 사용자 친화적으로 변환
const name = path
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
breadcrumbs.push({ name, href })
})
return breadcrumbs
}
const breadcrumbs = generateBreadcrumbs()
return (
{/* 브레드크럼 스타일 1: 기본 */}
{/* 브레드크럼 스타일 2: 배경색 있는 스타일 */}
<div className="container mx-auto px-4 py-8">
<nav className="bg-gray-100 rounded-lg px-4 py-2 mb-8">
<ol className="flex items-center space-x-1 text-sm">
{breadcrumbs.map((item, index) => (
<li key={item.href} className="flex items-center">
{index > 0 && <span className="mx-2 text-gray-400">/</span>}
{index === breadcrumbs.length - 1 ? (
<span className="text-gray-700 font-medium">{item.name}</span>
) : (
<Link href={item.href} className="text-gray-600 hover:text-gray-900 transition">
{item.name}
</Link>
)}
</li>
))}
</ol>
</nav>
{/* 브레드크럼 스타일 3: 아이콘 포함 */}
<nav className="mb-8">
<ol className="flex items-center space-x-2">
{breadcrumbs.map((item, index) => (
<li key={item.href} className="flex items-center">
{index === 0 && (
<Link href={item.href} className="text-gray-600 hover:text-blue-600 transition">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
</svg>
</Link>
)}
{index > 0 && (
<>
<svg className="w-5 h-5 mx-1 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" />
</svg>
{index === breadcrumbs.length - 1 ? (
<span className="text-gray-700 font-medium">{item.name}</span>
) : (
<Link href={item.href} className="text-gray-600 hover:text-blue-600 transition">
{item.name}
</Link>
)}
</>
)}
</li>
))}
</ol>
</nav>
</div>
</div>
)
}
사이드바 네비게이션
복잡한 콘텐츠 구조를 가진 웹사이트에 적합한 사이드바 네비게이션입니다:
jsx
'use client'
import { useState } from 'react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
export default function SidebarNavigation() {
const pathname = usePathname()
const [expandedSections, setExpandedSections] = useState(['getting-started'])
const navigation = [
{
id: 'getting-started',
title: '시작하기',
icon: '🚀',
items: [
{ name: '소개', href: '/docs/introduction' },
{ name: '설치', href: '/docs/installation' },
{ name: '빠른 시작', href: '/docs/quick-start' }
]
},
{
id: 'guides',
title: '가이드',
icon: '📚',
items: [
{ name: '라우팅', href: '/docs/routing' },
{ name: '데이터 페칭', href: '/docs/data-fetching' },
{ name: '스타일링', href: '/docs/styling' },
{ name: '배포', href: '/docs/deployment' }
]
},
{
id: 'api',
title: 'API 참조',
icon: '⚙️',
items: [
{ name: 'Components', href: '/docs/components' },
{ name: 'Hooks', href: '/docs/hooks' },
{ name: 'Utils', href: '/docs/utils' }
]
},
{
id: 'examples',
title: '예제',
icon: '💡',
items: [
{ name: '블로그', href: '/docs/examples/blog' },
{ name: '이커머스', href: '/docs/examples/ecommerce' },
{ name: '대시보드', href: '/docs/examples/dashboard' }
]
}
]
const toggleSection = (sectionId) => {
setExpandedSections(prev =>
prev.includes(sectionId)
? prev.filter(id => id !== sectionId)
: [...prev, sectionId]
)
}
return (
{/* 사이드바 */}