Layout.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. 'use client';
  2. import { useState, useCallback } from 'react';
  3. import Link from 'next/link';
  4. import Styles from '../styles/common.module.scss';
  5. import useAuth from '@/hooks/useAuth';
  6. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
  7. import { faBars, faXmark } from '@fortawesome/free-solid-svg-icons';
  8. type Props = {
  9. children: React.ReactNode;
  10. sidebarContent?: React.ReactNode;
  11. };
  12. export default function Layout({ children, sidebarContent }: Props) {
  13. const { isAuthenticated, isLoading, logout } = useAuth();
  14. const [sidebarOpen, setSidebarOpen] = useState(false);
  15. const toggleSidebar = useCallback(() => {
  16. setSidebarOpen((prev) => !prev);
  17. }, []);
  18. const closeSidebar = useCallback(() => {
  19. setSidebarOpen(false);
  20. }, []);
  21. if (isLoading) {
  22. return <></>;
  23. }
  24. return (
  25. <>
  26. <div id='container' className={`${Styles.container}${sidebarOpen ? ` ${Styles.sidebarOpen}` : ''}`}>
  27. {/* 상단 내용 */}
  28. <header id='header' className={`${Styles.header} flex items-center justify-between w-full px-4`}>
  29. <div className='flex items-center gap-2'>
  30. <button type='button' className={Styles.hamburger} onClick={toggleSidebar} aria-label='메뉴'>
  31. <FontAwesomeIcon icon={sidebarOpen ? faXmark : faBars} />
  32. </button>
  33. <Link href='/' className='font-bold text-xl'>
  34. <picture>
  35. <source src="image.webp" type="image/webp" />
  36. <img src="/resources/logo.svg" alt="bitforum logo" />
  37. </picture>
  38. </Link>
  39. </div>
  40. <nav className='flex grow items-center justify-between'>
  41. <ul className='flex gap-4'>
  42. <li>
  43. <Link href='/'>
  44. 코인
  45. </Link>
  46. </li>
  47. <li>
  48. <Link href='/latest'>
  49. 토론
  50. </Link>
  51. </li>
  52. <li>
  53. <Link href='/news'>
  54. 뉴스
  55. </Link>
  56. </li>
  57. <li>
  58. <Link href='/board/notice'>
  59. 고객지원
  60. </Link>
  61. </li>
  62. </ul>
  63. {!isAuthenticated ? (
  64. <ul className='flex gap-4'>
  65. <li>
  66. <a href='/login'>
  67. 로그인
  68. </a>
  69. </li>
  70. <li>
  71. <a href='/register'>
  72. 회원가입
  73. </a>
  74. </li>
  75. </ul>
  76. ) : (
  77. <ul className='flex gap-4'>
  78. <li>
  79. <Link href='/profile'>
  80. 내 정보
  81. </Link>
  82. </li>
  83. <li>
  84. <button type='button' onClick={logout}>
  85. 로그아웃
  86. </button>
  87. </li>
  88. </ul>
  89. )}
  90. </nav>
  91. </header>
  92. {/* 좌측 사이드바 */}
  93. <aside id='aside' className={Styles.aside}>
  94. {sidebarContent}
  95. </aside>
  96. {/* 모바일 오버레이 */}
  97. <div className={Styles.overlay} onClick={closeSidebar} />
  98. {/* 메인 내용 */}
  99. <main id='main' className={`${Styles.main} relative`}>
  100. {children}
  101. </main>
  102. {/* 우측 채팅 사이드바 */}
  103. <aside id='chatAside' className={Styles.chatAside}></aside>
  104. {/* 하단 내용 */}
  105. <footer id='footer' className={`${Styles.footer} px-4`}>
  106. <ol>
  107. {/* 최신글 표시 ▼ */}
  108. <li>111111</li>
  109. {/* 공지사항 표시 ▲ */}
  110. <li>222222</li>
  111. {/* 저작권 표시 */}
  112. <li>© 2025 PLAYR. All rights reserved.</li>
  113. </ol>
  114. </footer>
  115. </div>
  116. </>
  117. );
  118. }