diff --git a/components/meta/MetaHead.tsx b/components/meta/MetaHead.tsx index fd160dad..df2f4c92 100644 --- a/components/meta/MetaHead.tsx +++ b/components/meta/MetaHead.tsx @@ -4,36 +4,59 @@ import 뎁구리_og from '@public/image/뎁구리/뎁구리_og.png'; import { META, SITE_URL } from '@/constants/metaData'; -const MetaHead = ({ - title, - description, - url, - keyword, - image, -}: { +export type MetaHeadProps = { title?: string; description?: string; url?: string; keyword?: string[]; image?: string; -}) => { +}; + +const toAbsoluteUrl = (src: string) => { + if (/^https?:\/\//.test(src)) { + return src; + } + + try { + return new URL(src, SITE_URL).toString(); + } catch { + return `${SITE_URL}${src.startsWith('/') ? src : `/${src}`}`; + } +}; + +const MetaHead = ({ title, description, url, keyword, image }: MetaHeadProps) => { + const metaTitle = title ?? META.MAIN.title; + const metaDescription = description ?? META.MAIN.description; + const metaKeywords = (keyword ?? META.MAIN.keyword).join(','); + const metaUrl = url ?? SITE_URL; + const defaultImage = toAbsoluteUrl(뎁구리_og.src); + const metaImage = image ? toAbsoluteUrl(image) : defaultImage; + return ( - {title || META.MAIN.title} - + {metaTitle} + - - + + + {/* Open Graph */} + - {/* TODO: 최종 url은 변경 필요 */} - - - - {/* 트위터용 */} - - - + + + + + + {/* Twitter */} + + + + + + + ); }; + export default MetaHead; diff --git a/pages/_app.page.tsx b/pages/_app.page.tsx index 31291867..23c4a3a1 100644 --- a/pages/_app.page.tsx +++ b/pages/_app.page.tsx @@ -8,7 +8,7 @@ import { QueryClient, QueryClientProvider, HydrationBoundary } from '@tanstack/r import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import Layout from '@components/common/layout'; -import MetaHead from '@components/meta/MetaHead'; +import MetaHead, { type MetaHeadProps } from '@components/meta/MetaHead'; import useSetAxiosConfig from '@/api/useSetAxiosConfig'; import { DAY, HALF_DAY } from '@/constants/TimeConstants'; @@ -18,6 +18,8 @@ import '@/styles/globals.css'; import * as gtag from '../lib/gtag'; +type ComponentWithMeta = AppProps['Component'] & { meta?: MetaHeadProps }; + export default function MyApp({ Component, pageProps }: AppProps) { useSetAxiosConfig(); console.log('process.env.NODE_ENV', process.env.NODE_ENV); @@ -56,7 +58,9 @@ export default function MyApp({ Component, pageProps }: AppProps) { }; }, [router.events]); - const meta = pageProps.meta || META.MAIN; + const componentMeta = (Component as ComponentWithMeta)?.meta; + const meta: MetaHeadProps = + (pageProps.meta as MetaHeadProps | undefined) ?? componentMeta ?? META.MAIN; return ( <> diff --git a/pages/pickpickpick/[id]/index.page.tsx b/pages/pickpickpick/[id]/index.page.tsx index 95e0f51a..59e4725d 100644 --- a/pages/pickpickpick/[id]/index.page.tsx +++ b/pages/pickpickpick/[id]/index.page.tsx @@ -1,4 +1,5 @@ import Link from 'next/link'; +import type { NextPage } from 'next'; import { useRouter } from 'next/router'; import DevLoadingComponent from '@pages/loading/index.page'; @@ -16,7 +17,9 @@ import { PICK_VOTE_MODIFIED_MODAL, } from '@components/common/modals/modalConfig/pickVote'; import MoreButton from '@components/common/moreButton'; +import type { MetaHeadProps } from '@components/meta/MetaHead'; +import { META } from '@/constants/metaData'; import { ROUTES } from '@/constants/routes'; import { useMediaQueryContext } from '@/contexts/MediaQueryContext'; @@ -27,7 +30,9 @@ import SimilarPick from './components/SimilarPick'; import VoteCard from './components/VoteCard'; import usePickDetailHandlers from './handlers/usePickDetailHandlers'; -export default function Index() { +type NextPageWithMeta = NextPage & { meta?: MetaHeadProps }; + +const PickDetailPage: NextPageWithMeta = () => { const router = useRouter(); const { id } = router.query; @@ -148,4 +153,8 @@ export default function Index() { ); -} +}; + +PickDetailPage.meta = META.PICK; + +export default PickDetailPage; diff --git a/pages/techblog/[id]/index.page.tsx b/pages/techblog/[id]/index.page.tsx index b367cd6b..cbb22eb3 100644 --- a/pages/techblog/[id]/index.page.tsx +++ b/pages/techblog/[id]/index.page.tsx @@ -1,5 +1,6 @@ import React from 'react'; +import type { NextPage } from 'next'; import { useRouter } from 'next/router'; import DevLoadingComponent from '@pages/loading/index.page'; @@ -13,7 +14,9 @@ import WritableComment from '@components/common/comment/WritableComment'; import DevGuriHorizontalError from '@components/common/error/DevGuriHorizontalError'; import MobileToListButton from '@components/common/mobile/mobileToListButton'; import { LoginModal } from '@components/common/modals/modal'; +import type { MetaHeadProps } from '@components/meta/MetaHead'; +import { META } from '@/constants/metaData'; import { ROUTES } from '@/constants/routes'; import { useMediaQueryContext } from '@/contexts/MediaQueryContext'; @@ -24,7 +27,9 @@ import CompanyInfoCard from '../components/CompanyInfoCard'; import TechDetailCard from '../components/TechDetailCard'; import { TechCardProps } from '../types/techBlogType'; -export default function Page() { +type NextPageWithMeta = NextPage & { meta?: MetaHeadProps }; + +const TechBlogDetailPage: NextPageWithMeta = () => { const router = useRouter(); const techArticleId = router.query.id as string | undefined; @@ -116,4 +121,8 @@ export default function Page() { {isLoginModalOpen && loginStatus !== 'login' && } ); -} +}; + +TechBlogDetailPage.meta = META.TECH; + +export default TechBlogDetailPage;