diff --git a/src/app/(routes)/login/page.tsx b/src/app/(routes)/login/page.tsx index d4aa356f..98231396 100644 --- a/src/app/(routes)/login/page.tsx +++ b/src/app/(routes)/login/page.tsx @@ -1,4 +1,9 @@ import Login from '@/components/Login/Login' +import { Metadata } from 'next' + +export const metadata: Metadata = { + title: '로그인', +} const LoginPage = () => { return ( diff --git a/src/app/(routes)/notification/layout.tsx b/src/app/(routes)/notification/layout.tsx index 2d312b22..777340cd 100644 --- a/src/app/(routes)/notification/layout.tsx +++ b/src/app/(routes)/notification/layout.tsx @@ -1,27 +1,12 @@ -'use client' +import { NotificationController } from '@/components' +import { Metadata } from 'next' -import Tab from '@/components/common/Tab/Tab' -import TabItem from '@/components/common/Tab/TabItem' -import useTab from '@/components/common/Tab/hooks/useTab' +export const metadata: Metadata = { + title: '알림', +} const NotificationLayout = ({ children }: { children: React.ReactNode }) => { - const { currentTab, tabList } = useTab({ type: 'notification' }) - - return ( - <> - - {tabList.map((tabItem) => ( - - ))} - -
{children}
- - ) + return {children} } export default NotificationLayout diff --git a/src/app/(routes)/register/page.tsx b/src/app/(routes)/register/page.tsx index f3ac0308..02c6d36e 100644 --- a/src/app/(routes)/register/page.tsx +++ b/src/app/(routes)/register/page.tsx @@ -1,4 +1,9 @@ import UserInfoForm from '@/components/UserInfoForm/UserInfoForm' +import { Metadata } from 'next' + +export const metadata: Metadata = { + title: '회원가입', +} const RegisterPage = () => { return ( diff --git a/src/app/(routes)/search/page.tsx b/src/app/(routes)/search/page.tsx index 28b8336e..b3637c71 100644 --- a/src/app/(routes)/search/page.tsx +++ b/src/app/(routes)/search/page.tsx @@ -1,72 +1,24 @@ -'use client' +import { SearchController } from '@/components' +import { Metadata } from 'next' -import { CategoryList, Dropdown, SpaceList } from '@/components' -import UserList from '@/components/UserList/UserList' -import { useCategoryParam, useSortParam } from '@/hooks' -import { fetchSearchSpaces } from '@/services/space/spaces' -import { fetchSearchUsers } from '@/services/user/search/search' -import { cls } from '@/utils' -import { useSearchParams } from 'next/navigation' +type SearchPageProps = { + searchParams: { [key: string]: string | string[] | undefined } +} -const SearchPage = () => { - const searchParams = useSearchParams() - const keyword = searchParams.get('keyword') - const target = searchParams.get('target') - const { sort, sortIndex, handleSortChange } = useSortParam('space') - const { category, categoryIndex, handleCategoryChange } = - useCategoryParam('all') +export async function generateMetadata({ + searchParams, +}: SearchPageProps): Promise { + const { target, keyword } = searchParams - return ( - <> -
-
-

- '{keyword}' 에 대한{' '} - {target === 'space' ? '스페이스' : target === 'user' ? '유저' : ''}{' '} - 검색 결과 -

- {target === 'space' && ( -
- -
- )} -
- {target === 'space' && ( - - )} -
-
- {target === 'space' && ( - - )} - {target === 'user' && ( - - )} -
- - ) + return { + title: `'${keyword ?? ''}' ${ + target === 'space' ? '스페이스' : target === 'user' ? '유저' : '' + } 검색 결과`, + } +} + +const SearchPage = () => { + return } export default SearchPage diff --git a/src/app/(routes)/space/[spaceId]/layout.tsx b/src/app/(routes)/space/[spaceId]/layout.tsx new file mode 100644 index 00000000..d3a2d748 --- /dev/null +++ b/src/app/(routes)/space/[spaceId]/layout.tsx @@ -0,0 +1,29 @@ +import { fetchGetSpace } from '@/services/space/space' +import { Metadata } from 'next' + +type SpaceLayoutProps = { + params: { spaceId: number } +} + +export async function generateMetadata({ + params, +}: SpaceLayoutProps): Promise { + const spaceId = params.spaceId + const space = await fetchGetSpace({ spaceId }) + + return { + title: space.spaceName, + description: space.description, + openGraph: { + title: `${space.spaceName} • LinkHub`, + description: space.description, + images: space.spaceImagePath, + }, + } +} + +const layout = ({ children }: { children: React.ReactNode }) => { + return children +} + +export default layout diff --git a/src/app/(routes)/space/create/page.tsx b/src/app/(routes)/space/create/page.tsx index 79cc4dda..6b8c41e3 100644 --- a/src/app/(routes)/space/create/page.tsx +++ b/src/app/(routes)/space/create/page.tsx @@ -1,4 +1,9 @@ import SpaceForm from '@/components/Space/SpaceForm' +import { Metadata } from 'next' + +export const metadata: Metadata = { + title: '스페이스 생성', +} const SpaceCreatePage = () => { return ( diff --git a/src/app/(routes)/user/[userId]/layout.tsx b/src/app/(routes)/user/[userId]/layout.tsx index 7228c3a1..d2857576 100644 --- a/src/app/(routes)/user/[userId]/layout.tsx +++ b/src/app/(routes)/user/[userId]/layout.tsx @@ -1,37 +1,25 @@ -'use client' +import { UserController } from '@/components' +import { fetchGetUserProfile } from '@/services/user/profile/profile' +import { Metadata } from 'next' -import React from 'react' -import { Spinner } from '@/components' -import Tab from '@/components/common/Tab/Tab' -import TabItem from '@/components/common/Tab/TabItem' -import useTab from '@/components/common/Tab/hooks/useTab' -import useGetProfile from '@/hooks/useGetProfile' +type Props = { + params: { userId: number } +} -const UserLayout = ({ children }: { children: React.ReactNode }) => { - const { user, myId, isProfileLoading } = useGetProfile() - const { currentTab, tabList } = useTab({ - type: 'user', - userId: user?.memberId, - myId, - }) +export async function generateMetadata({ params }: Props): Promise { + const userId = params.userId + const user = await fetchGetUserProfile({ memberId: userId }) - return ( - <> - {!isProfileLoading && ( - - {tabList.map((tabItem) => ( - - ))} - - )} - {children} - - ) + return { + title: user.nickname, + openGraph: { + title: `${user.nickname} • LinkHub`, + }, + } +} + +const UserLayout = ({ children }: { children: React.ReactNode }) => { + return {children} } export default UserLayout diff --git a/src/app/(routes)/user/setting/page.tsx b/src/app/(routes)/user/setting/page.tsx index b4d02cd9..79b7d383 100644 --- a/src/app/(routes)/user/setting/page.tsx +++ b/src/app/(routes)/user/setting/page.tsx @@ -1,21 +1,12 @@ -'use client' +import { SettingController } from '@/components' +import { Metadata } from 'next' -import UserInfoForm from '@/components/UserInfoForm/UserInfoForm' -import { useCurrentUser } from '@/hooks/useCurrentUser' +export const metadata: Metadata = { + title: '프로필 수정', +} const UserSettingPage = () => { - const { currentUser } = useCurrentUser() - - return ( -
- {currentUser && ( - - )} -
- ) + return } export default UserSettingPage diff --git a/src/app/favicon.ico b/src/app/favicon.ico index 718d6fea..700688c9 100644 Binary files a/src/app/favicon.ico and b/src/app/favicon.ico differ diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0fcfd667..7b7e3572 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,8 +7,19 @@ import type { Metadata } from 'next' import './globals.css' export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', + title: { + template: '%s • LinkHub', + default: 'LinkHub', + }, + description: '링크 아카이빙 및 공유 플랫폼', + openGraph: { + title: 'LinkHub', + description: '링크 아카이빙 및 공유 플랫폼', + url: 'https://link-hub.site', + siteName: 'LinkHub', + locale: 'ko_KR', + type: 'website', + }, } export default function RootLayout({ @@ -21,7 +32,7 @@ export default function RootLayout({ diff --git a/src/app/opengraph-image.png b/src/app/opengraph-image.png new file mode 100644 index 00000000..29860e7e Binary files /dev/null and b/src/app/opengraph-image.png differ diff --git a/src/app/twitter-image.png b/src/app/twitter-image.png new file mode 100644 index 00000000..29860e7e Binary files /dev/null and b/src/app/twitter-image.png differ diff --git a/src/components/NotificationController/NotificationController.tsx b/src/components/NotificationController/NotificationController.tsx new file mode 100644 index 00000000..2d312b22 --- /dev/null +++ b/src/components/NotificationController/NotificationController.tsx @@ -0,0 +1,27 @@ +'use client' + +import Tab from '@/components/common/Tab/Tab' +import TabItem from '@/components/common/Tab/TabItem' +import useTab from '@/components/common/Tab/hooks/useTab' + +const NotificationLayout = ({ children }: { children: React.ReactNode }) => { + const { currentTab, tabList } = useTab({ type: 'notification' }) + + return ( + <> + + {tabList.map((tabItem) => ( + + ))} + +
{children}
+ + ) +} + +export default NotificationLayout diff --git a/src/components/SearchController/SearchController.tsx b/src/components/SearchController/SearchController.tsx new file mode 100644 index 00000000..0f451bb9 --- /dev/null +++ b/src/components/SearchController/SearchController.tsx @@ -0,0 +1,72 @@ +'use client' + +import { CategoryList, Dropdown, SpaceList } from '@/components' +import UserList from '@/components/UserList/UserList' +import { useCategoryParam, useSortParam } from '@/hooks' +import { fetchSearchSpaces } from '@/services/space/spaces' +import { fetchSearchUsers } from '@/services/user/search/search' +import { cls } from '@/utils' +import { useSearchParams } from 'next/navigation' + +const SearchController = () => { + const searchParams = useSearchParams() + const keyword = searchParams.get('keyword') + const target = searchParams.get('target') + const { sort, sortIndex, handleSortChange } = useSortParam('space') + const { category, categoryIndex, handleCategoryChange } = + useCategoryParam('all') + + return ( + <> +
+
+

+ '{keyword}' 에 대한{' '} + {target === 'space' ? '스페이스' : target === 'user' ? '유저' : ''}{' '} + 검색 결과 +

+ {target === 'space' && ( +
+ +
+ )} +
+ {target === 'space' && ( + + )} +
+
+ {target === 'space' && ( + + )} + {target === 'user' && ( + + )} +
+ + ) +} + +export default SearchController diff --git a/src/components/SettingController/SettingController.tsx b/src/components/SettingController/SettingController.tsx new file mode 100644 index 00000000..c6f17566 --- /dev/null +++ b/src/components/SettingController/SettingController.tsx @@ -0,0 +1,21 @@ +'use client' + +import UserInfoForm from '@/components/UserInfoForm/UserInfoForm' +import { useCurrentUser } from '@/hooks/useCurrentUser' + +const SettingController = () => { + const { currentUser } = useCurrentUser() + + return ( +
+ {currentUser && ( + + )} +
+ ) +} + +export default SettingController diff --git a/src/components/UserController/UserController.tsx b/src/components/UserController/UserController.tsx new file mode 100644 index 00000000..deea6e5b --- /dev/null +++ b/src/components/UserController/UserController.tsx @@ -0,0 +1,36 @@ +'use client' + +import React from 'react' +import Tab from '@/components/common/Tab/Tab' +import TabItem from '@/components/common/Tab/TabItem' +import useTab from '@/components/common/Tab/hooks/useTab' +import useGetProfile from '@/hooks/useGetProfile' + +const UserController = ({ children }: { children: React.ReactNode }) => { + const { user, myId, isProfileLoading } = useGetProfile() + const { currentTab, tabList } = useTab({ + type: 'user', + userId: user?.memberId, + myId, + }) + + return ( + <> + {!isProfileLoading && ( + + {tabList.map((tabItem) => ( + + ))} + + )} + {children} + + ) +} + +export default UserController diff --git a/src/components/index.ts b/src/components/index.ts index cf19a483..7664fcb1 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -19,3 +19,7 @@ export { default as Comment } from './common/Comment/Comment' export { default as Notification } from './common/Notification/Notification' export { default as SpaceList } from './SpaceList/SpaceList' export { default as Spinner } from './common/Spinner/Spinner' +export { default as SearchController } from './SearchController/SearchController' +export { default as NotificationController } from './NotificationController/NotificationController' +export { default as UserController } from './UserController/UserController' +export { default as SettingController } from './SettingController/SettingController'