diff --git a/frontend/public/logo/og-logo.png b/frontend/public/logo/og-logo.png new file mode 100644 index 000000000..4cbf5a094 Binary files /dev/null and b/frontend/public/logo/og-logo.png differ diff --git a/frontend/src/app/(with-header)/(home)/nearby/page.tsx b/frontend/src/app/(with-header)/(home)/nearby/page.tsx index be84ec8a8..cb9245552 100644 --- a/frontend/src/app/(with-header)/(home)/nearby/page.tsx +++ b/frontend/src/app/(with-header)/(home)/nearby/page.tsx @@ -3,8 +3,22 @@ import MapComponent from '@/app/_components/naver-map/MapComponent'; import ViewTravelMap from '@/app/_components/near/ViewTravelMap'; import { getMySpotIds } from '@/app/_components/near/hooks/nearActions'; import { myInfoSchema } from '@/types/response'; +import { Metadata } from 'next'; import React from 'react'; +export const metadata: Metadata = { + title: '장소/코스 검색', + description: `원하는 장소/코스를 검색해보세요.`, + openGraph: { + title: '장소/코스 검색', + description: `원하는 장소/코스를 검색해보세요.`, + }, + twitter: { + title: '장소/코스 검색', + description: `원하는 장소/코스를 검색해보세요.`, + }, +}; + export default async function NearbyPage() { const res = await authenticateUser(); const user = myInfoSchema.safeParse(res); diff --git a/frontend/src/app/(with-header)/(home)/places/popular/page.tsx b/frontend/src/app/(with-header)/(home)/places/popular/page.tsx index aae731189..eccde86f4 100644 --- a/frontend/src/app/(with-header)/(home)/places/popular/page.tsx +++ b/frontend/src/app/(with-header)/(home)/places/popular/page.tsx @@ -5,6 +5,20 @@ import { myInfoSchema } from '@/types/response'; import { Place } from '@/app/_components/place/places'; import { getPlaces } from '@/app/_components/place/places/action'; +import { Metadata } from 'next'; + +export const metadata: Metadata = { + title: '인기 장소/코스', + description: `현재 인기있는 장소/코스를 만나보세요.`, + openGraph: { + title: '인기 장소/코스', + description: `현재 인기있는 장소/코스를 만나보세요.`, + }, + twitter: { + title: '인기 장소/코스', + description: `현재 인기있는 장소/코스를 만나보세요.`, + }, +}; export default async function PopularPlacesPage() { const memberJson = await authenticateUser(); diff --git a/frontend/src/app/(with-header)/(home)/places/recommended/page.tsx b/frontend/src/app/(with-header)/(home)/places/recommended/page.tsx index 8b2b021d3..bd93f2a9e 100644 --- a/frontend/src/app/(with-header)/(home)/places/recommended/page.tsx +++ b/frontend/src/app/(with-header)/(home)/places/recommended/page.tsx @@ -3,6 +3,20 @@ import { myInfoSchema } from '@/types/response'; import { Place } from '@/app/_components/place/places'; import { getPlaces } from '@/app/_components/place/places/action'; +import { Metadata } from 'next'; + +export const metadata: Metadata = { + title: '맞춤 추천', + description: `개인별 맞춤 추천 지역의 장소/코스를 만나보세요.`, + openGraph: { + title: '맞춤 추천', + description: `개인별 맞춤 추천 지역의 장소/코스를 만나보세요.`, + }, + twitter: { + title: '맞춤 추천', + description: `개인별 맞춤 추천 지역의 장소/코스를 만나보세요.`, + }, +}; export default async function RecommendedPlacesPage() { const memberJson = await authenticateUser(); diff --git a/frontend/src/app/(with-header)/(home)/places/regions/[id]/page.tsx b/frontend/src/app/(with-header)/(home)/places/regions/[id]/page.tsx index 9ae4220ad..09d5e5de2 100644 --- a/frontend/src/app/(with-header)/(home)/places/regions/[id]/page.tsx +++ b/frontend/src/app/(with-header)/(home)/places/regions/[id]/page.tsx @@ -7,6 +7,20 @@ import { getInterestedRegions, getRegionPlaces } from '../../../action'; import DummyPlace from '@/app/_components/place/dummy/DummyPlace'; import { RegionPlaces } from '@/app/_components/place/places'; +import { Metadata } from 'next'; + +export const metadata: Metadata = { + title: '관심 지역', + description: `관심 지역을 설정하고 관심 지역의 장소/코스를 만나보세요.`, + openGraph: { + title: '관심 지역', + description: `관심 지역을 설정하고 관심 지역의 장소/코스를 만나보세요.`, + }, + twitter: { + title: '관심 지역', + description: `관심 지역을 설정하고 관심 지역의 장소/코스를 만나보세요.`, + }, +}; export default async function RegionsPlacePage({ params, diff --git a/frontend/src/app/(with-header)/(home)/search/page.tsx b/frontend/src/app/(with-header)/(home)/search/page.tsx index 5e3192985..40c7aef9f 100644 --- a/frontend/src/app/(with-header)/(home)/search/page.tsx +++ b/frontend/src/app/(with-header)/(home)/search/page.tsx @@ -6,6 +6,12 @@ import { myInfoSchema } from '@/types/response'; import type { TMemberStatus } from '@/context/MemberContext'; import BackendSearchBar from '@/app/_components/place/BackendSearchBar'; import TravelSearchResult from '@/app/_components/search/TravelSearchResult'; +import { Metadata } from 'next'; + +export const metadata: Metadata = { + title: '이길로그 검색 페이지', + description: '원하는 장소/코스를 검색해보세요.', +}; export default async function SearchPage({ searchParams, diff --git a/frontend/src/app/(with-header)/detail/[travel]/[id]/page.tsx b/frontend/src/app/(with-header)/detail/[travel]/[id]/page.tsx index 907425fcd..1039795cc 100644 --- a/frontend/src/app/(with-header)/detail/[travel]/[id]/page.tsx +++ b/frontend/src/app/(with-header)/detail/[travel]/[id]/page.tsx @@ -9,6 +9,70 @@ import { import SpotDetail from '@/app/_components/mypage/spot/SpotDetail'; import { myInfoSchema } from '@/types/response'; import React from 'react'; +import { Metadata } from 'next'; +import { createMetadata } from '@/utils'; + +export async function generateMetadata({ + params, +}: { + params: { id: number; travel: string }; +}): Promise { + if (params.travel === 'spot') { + const spotDetail = await getSpotDetail(params.id); + if (spotDetail.status === 'succeed') { + const data = { + title: spotDetail.data.place_name, + description: spotDetail.data.description, + image: spotDetail.data.image_urls[0], + url: `https://yigil.co.kr/detail/spot/${params.id}`, + }; + return createMetadata(data); + } else + return { + title: '장소 상세 페이지', + description: '장소 상세 설명', + openGraph: { + images: { + url: '/logo/og-logo.png', + alt: '이길로그 로고', + }, + }, + twitter: { + images: { + url: '/logo/og-logo.png', + alt: '이길로그 로고', + }, + }, + }; + } else { + const courseDetail = await getCourseDetail(params.id); + if (courseDetail.status === 'succeed') { + const data = { + title: courseDetail.data.title, + description: courseDetail.data.description, + image: courseDetail.data.map_static_image_url, + url: `https://yigil.co.kr/detail/course/${params.id}`, + }; + return createMetadata(data); + } else + return { + title: '코스 상세 페이지', + description: '코스 상세 설명', + openGraph: { + images: { + url: '/logo/og-logo.png', + alt: '이길로그 로고', + }, + }, + twitter: { + images: { + url: '/logo/og-logo.png', + alt: '이길로그 로고', + }, + }, + }; + } +} export default async function SpotDetailPage({ params, @@ -22,7 +86,7 @@ export default async function SpotDetailPage({ if (params.travel === 'spot') { const spotDetail = await getSpotDetail(params.id); - if (!spotDetail.success) + if (spotDetail.status === 'failed') return (
장소 상세 정보를 불러오는데 실패했습니다.
다시 시도해주세요. diff --git a/frontend/src/app/(with-header)/place/[id]/page.tsx b/frontend/src/app/(with-header)/place/[id]/page.tsx index 90ea90335..acb980470 100644 --- a/frontend/src/app/(with-header)/place/[id]/page.tsx +++ b/frontend/src/app/(with-header)/place/[id]/page.tsx @@ -1,4 +1,20 @@ import PlaceDetailWithMySpot from '@/app/_components/place/detail/PlaceDetailWithMySpot'; +import { getPlaceDetail } from '@/app/_components/place/detail/action'; +import { createMetadata } from '@/utils'; + +export async function generateMetadata({ params }: { params: { id: number } }) { + const detailResult = await getPlaceDetail(params.id); + if (detailResult.success) { + const data = { + title: detailResult.data.place_name, + description: detailResult.data.address, + image: detailResult.data.thumbnail_image_url, + url: `https://yigil.co.kr/place/${params.id}`, + }; + + return createMetadata(data); + } +} export default async function PlaceDetailPage({ params, diff --git a/frontend/src/app/(without-header)/login/page.tsx b/frontend/src/app/(without-header)/login/page.tsx index 7656acbf4..b58494e68 100644 --- a/frontend/src/app/(without-header)/login/page.tsx +++ b/frontend/src/app/(without-header)/login/page.tsx @@ -10,6 +10,20 @@ import { naverOAuthEndPoint } from '@/app/endpoints/api/auth/callback/naver/cons import GoogleLoginButton from '@/app/_components/ui/button/GoogleLoginButton'; import NaverLoginButton from '@/app/_components/ui/button/NaverLoginButton'; import { googleOAuthEndPoint } from '@/app/endpoints/api/auth/callback/google/constants'; +import { Metadata } from 'next'; + +export const metadata: Metadata = { + title: '로그인', + description: `로그인 후 이길로그의 여러 기능을 만나보세요`, + openGraph: { + title: '로그인', + description: `로그인 후 이길로그의 여러 기능을 만나보세요`, + }, + twitter: { + title: '로그인', + description: `로그인 후 이길로그의 여러 기능을 만나보세요`, + }, +}; export default async function LoginPage() { const { KAKAO_ID, GOOGLE_CLIENT_ID, NAVER_SEARCH_ID } = process.env; diff --git a/frontend/src/app/_components/mypage/hooks/myPageActions.ts b/frontend/src/app/_components/mypage/hooks/myPageActions.ts index cdac82a91..e724216b9 100644 --- a/frontend/src/app/_components/mypage/hooks/myPageActions.ts +++ b/frontend/src/app/_components/mypage/hooks/myPageActions.ts @@ -139,7 +139,7 @@ export const getSpotDetail = async (spotId: number) => { }, }); const result = await res.json(); - const parsedSpotDetail = mypageSpotDetailSchema.safeParse(result); + const parsedSpotDetail = parseResult(mypageSpotDetailSchema, result); return parsedSpotDetail; }; diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 5403545c9..e613e0534 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -14,13 +14,25 @@ const Pretendard = localFont({ }); export const metadata: Metadata = { + metadataBase: new URL('https://yigil.co.kr'), title: '이길로그', - icons: { icon: [{ url: '/logo/favicon.svg', href: '/logo/favicon.svg' }], }, - description: '지도 기반 장소 기록·공유 서비스', + openGraph: { + title: '이길로그 홈페이지', + description: '지도 기반 장소 기록·공유 서비스', + images: { url: '/logo/og-logo.png', alt: '이길로그 로고 이미지' }, + type: 'website', + siteName: '이길로그', + locale: 'ko-KR', + }, + twitter: { + title: '이길로그 홈페이지', + description: '지도 기반 장소 기록·공유 서비스', + images: { url: '/logo/og-logo.png', alt: '이길로그 로고 이미지' }, + }, }; export default function RootLayout({ children }: { children: ReactNode }) { diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts index 0fc6d9098..88dfd6f29 100644 --- a/frontend/src/utils.ts +++ b/frontend/src/utils.ts @@ -97,3 +97,30 @@ export function parseResult( return { status: 'succeed', data: result.data }; } +export async function createMetadata(data: { + title: string; + description: string; + image: string; + url: string; +}) { + const { title, description, image, url } = data; + return { + title: title, + description: description, + openGraph: { + url: url, + images: { + url: image, + alt: `${title}-이미지`, + }, + }, + twitter: { + title: title, + description: description, + images: { + url: image, + alt: `${title}-이미지`, + }, + }, + }; +}