diff --git a/src/app/collection/page.tsx b/src/app/collection/page.tsx index ca36286d..ba68de40 100644 --- a/src/app/collection/page.tsx +++ b/src/app/collection/page.tsx @@ -69,11 +69,11 @@ export default function CollectionPage() {
{data && data.map((category) => ( -
+
{ - handleCategoryClick(category.nameValue); + handleCategoryClick(category.engName); }} > {`${category.korNameValue} -
{codeToFolderIcon(category.codeValue, language)}
+
{codeToFolderIcon(category.code, language)}
-

{language === 'ko' ? category.korNameValue : category.nameValue}

+

{language === 'ko' ? category.korName : category.engName}

))}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5e86f360..9b480225 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,7 @@ import { ReactNode } from 'react'; import type { Metadata, Viewport } from 'next'; +import { ReferrerEnum } from 'next/dist/lib/metadata/types/metadata-types'; +import { OpenGraphType } from 'next/dist/lib/metadata/types/opengraph-types'; import { ToastContainer } from 'react-toastify'; import { GoogleTagManager } from '@next/third-parties/google'; @@ -9,6 +11,7 @@ import '@/styles/GlobalStyles.css'; import * as styles from './layout.css'; import CommonProvider from './_context/CommonProvider'; +import METADATA from '@/lib/constants/metadata'; export const viewport: Viewport = { width: 'device-width', @@ -19,27 +22,24 @@ export const viewport: Viewport = { }; export const metadata: Metadata = { - // Template Object title: { - template: '%s | ListyWave', - default: 'ListyWave', // 대체 제목 (required), + template: METADATA.title.template, + default: METADATA.title.default, }, - description: - "What’s In Your List? 🌊 나의 취향을 리스트로 기록하고, 공유하고, 발견해요. 리스티웨이브에서 모든 기준은 '나의 취향'이에요. 내 취향 가득한 편안한 공간이 되면 좋겠습니다.", - authors: [{ name: '에잇🩷' }], - generator: 'Next.js', - applicationName: 'ListyWave', - referrer: 'origin-when-cross-origin', // Referrer-Policy - keywords: ['ListyWave', 'list', 'SNS'], - metadataBase: new URL('https://listywave.com'), + description: METADATA.description.default, + authors: [{ name: METADATA.authors.name, url: METADATA.authors.url }], + generator: METADATA.generator, + applicationName: METADATA.applicationName, + referrer: METADATA.referrer as ReferrerEnum, + keywords: METADATA.keywords, + metadataBase: new URL(METADATA.url), openGraph: { - title: 'ListyWave', - description: - "What’s In Your List? 🌊 나의 취향을 리스트로 기록하고, 공유하고, 발견해요. 리스티웨이브에서 모든 기준은 '나의 취향'이에요. 내 취향 가득한 편안한 공간이 되면 좋겠습니다.", - url: 'https://listywave.com', - type: 'website', - siteName: 'ListyWave', - locale: 'ko', + title: METADATA.title.openGraph, + description: METADATA.description.default, + url: METADATA.url, + type: METADATA.type as OpenGraphType, + siteName: METADATA.siteName, + locale: METADATA.locale, }, }; diff --git a/src/app/list/[listId]/_components/ListDetailInner/index.tsx b/src/app/list/[listId]/_components/ListDetailInner/index.tsx index 5e227037..12bc6c50 100644 --- a/src/app/list/[listId]/_components/ListDetailInner/index.tsx +++ b/src/app/list/[listId]/_components/ListDetailInner/index.tsx @@ -33,7 +33,7 @@ function ListDetailInner({ data, listId }: ListDetailInnerProps) { listId: listId, ownerId: data?.ownerId, items: listData, - category: data?.category, + category: data?.categoryKorName, title: data?.title, description: data?.description, collaborators: data?.collaborators, diff --git a/src/app/list/[listId]/_components/ListDetailOuter/ListInformation.css.ts b/src/app/list/[listId]/_components/ListDetailOuter/ListInformation.css.ts index 37993f87..2e073a1f 100644 --- a/src/app/list/[listId]/_components/ListDetailOuter/ListInformation.css.ts +++ b/src/app/list/[listId]/_components/ListDetailOuter/ListInformation.css.ts @@ -34,6 +34,7 @@ export const categoryWrapper = style({ export const labelWrapper = style({ marginRight: '8px', + cursor: 'pointer', }); export const listTitle = style([ diff --git a/src/app/list/[listId]/_components/ListDetailOuter/ListInformation.tsx b/src/app/list/[listId]/_components/ListDetailOuter/ListInformation.tsx index 227a01d7..ceb15c70 100644 --- a/src/app/list/[listId]/_components/ListDetailOuter/ListInformation.tsx +++ b/src/app/list/[listId]/_components/ListDetailOuter/ListInformation.tsx @@ -1,4 +1,5 @@ 'use client'; +import { MouseEvent } from 'react'; import Image from 'next/image'; import { useQuery } from '@tanstack/react-query'; import { useParams, useRouter } from 'next/navigation'; @@ -77,6 +78,18 @@ function ListInformation() { return null; } + const handleCategorySearch = (categoryEngName: string) => { + router.push(`/search?category=${categoryEngName}`); + }; + + const handleLabelSearch = (e: MouseEvent) => { + const labelElement = e.currentTarget.querySelector('div'); + const labelText = labelElement?.textContent; + if (labelText) { + router.push(`/search?keyword=${labelText}`); + } + }; + return ( <> {isOn && ( @@ -105,12 +118,12 @@ function ListInformation() { <>
-
- +
handleCategorySearch(list?.categoryEngName)}> +
{list?.labels.map((item: LabelType) => { return ( -
+
); diff --git a/src/app/list/[listId]/edit/page.tsx b/src/app/list/[listId]/edit/page.tsx index 5681994f..5eba0bd4 100644 --- a/src/app/list/[listId]/edit/page.tsx +++ b/src/app/list/[listId]/edit/page.tsx @@ -118,7 +118,7 @@ export default function EditPage() { useEffect(() => { if (listDetailData) { methods.reset({ - category: categories?.find((c) => c.korNameValue === listDetailData.category)?.nameValue || 'culture', + category: categories?.find((c) => c.korName === listDetailData.categoryKorName)?.engName || 'culture', labels: listDetailData.labels.map((obj) => obj.name), collaboratorIds: listDetailData.collaborators.filter((c) => c.id !== user.id).map((c) => c.id), title: listDetailData.title, diff --git a/src/app/list/[listId]/page.tsx b/src/app/list/[listId]/page.tsx index cb8f4bc1..e74f0fbf 100644 --- a/src/app/list/[listId]/page.tsx +++ b/src/app/list/[listId]/page.tsx @@ -1,6 +1,37 @@ +import { Metadata, ResolvingMetadata } from 'next'; import * as styles from './ListDetail.css'; import ListInformation from '@/app/list/[listId]/_components/ListDetailOuter/ListInformation'; +import axiosInstance from '@/lib/axios/axiosInstance'; +import { ListDetailType } from '@/lib/types/listType'; +import METADATA from '@/lib/constants/metadata'; + +interface ListDetailProps { + params: { listId: number }; +} + +export async function generateMetadata({ params }: ListDetailProps, parent: ResolvingMetadata): Promise { + const listId = params.listId; + const { data } = await axiosInstance.get(`/lists/${listId}`); + const { title, ownerNickname, collaborators, description, items } = data; + + const previousImages = (await parent).openGraph?.images || []; + const listType = collaborators.length === 0 ? 'Mylist' : 'Collabo-list'; + + return { + title: { + absolute: `${ownerNickname}'s ${listType} - ${data.title}`, + }, + description: `${description}`, + authors: [{ name: `${ownerNickname}` }], + openGraph: { + title: `${title} By.${ownerNickname}`, + description: `${description || `${ownerNickname}님의 취향을 기록한 리스트입니다.`}`, + url: `${METADATA.url}/list/${listId}`, + images: [`${items[0].imageUrl}`, ...previousImages], + }, + }; +} export default function ListDetailPage() { return ( diff --git a/src/app/list/create/_components/CreateList.tsx b/src/app/list/create/_components/CreateList.tsx index 8c0a03fd..14311fff 100644 --- a/src/app/list/create/_components/CreateList.tsx +++ b/src/app/list/create/_components/CreateList.tsx @@ -81,7 +81,7 @@ function CreateList({ onNextClick, type }: CreateListProps) { const handleQueryParams = () => { if (isTemplateCreation) { setValue('title', searchParams?.get('title')); - const c = categories?.find((c) => c.korNameValue === searchParams?.get('category'))?.nameValue; + const c = categories?.find((c) => c.korName === searchParams?.get('category'))?.engName; setValue('category', c); } }; @@ -143,9 +143,9 @@ function CreateList({ onNextClick, type }: CreateListProps) { {/* 카테고리 */}
category.codeValue !== '0') || []} + list={categories?.filter((category: CategoryType) => category.code !== '0') || []} onClick={(item: CategoryType) => { - setValue('category', item.nameValue); + setValue('category', item.engName); }} defaultValue={category} /> diff --git a/src/app/list/create/_components/list/ButtonSelector.tsx b/src/app/list/create/_components/list/ButtonSelector.tsx index 27e206ce..617fa7fb 100644 --- a/src/app/list/create/_components/list/ButtonSelector.tsx +++ b/src/app/list/create/_components/list/ButtonSelector.tsx @@ -28,14 +28,14 @@ function ButtonSelector({ list, onClick, defaultValue }: ButtonSelectorProps) {
{list.map((item) => ( ))}
diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 00000000..081320c1 --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,12 @@ +import DOMAIN_URL from '@/lib/constants/domain'; +import type { MetadataRoute } from 'next'; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: '*', + allow: '/', + }, + sitemap: `${DOMAIN_URL}/sitemap.xml`, + }; +} diff --git a/src/app/search/_components/CategoryArea.tsx b/src/app/search/_components/CategoryArea.tsx index f1a6a9bf..ce49b658 100644 --- a/src/app/search/_components/CategoryArea.tsx +++ b/src/app/search/_components/CategoryArea.tsx @@ -32,18 +32,16 @@ function CategoryArea({ onClick }: { onClick: MouseEventHandler }) { ? Array.from({ length: 6 }).map((_, index) => ) : data && data.map((category) => ( -
+
{category.korNameValue} -
- {language === 'ko' ? category.korNameValue : category.nameValue} -
- {categoryValue === category.nameValue && ( +
{language === 'ko' ? category.korName : category.engName}
+ {categoryValue === category.engName && (
diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 00000000..30e9df2f --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,25 @@ +import type { MetadataRoute } from 'next'; +import DOMAIN_URL from '@/lib/constants/domain'; + +export default async function sitemap(): Promise { + return [ + { + url: DOMAIN_URL, + lastModified: new Date(), + changeFrequency: 'weekly', + priority: 1, + }, + { + url: `${DOMAIN_URL}/intro`, + lastModified: new Date(), + changeFrequency: 'yearly', + priority: 0.8, + }, + { + url: `${DOMAIN_URL}/search`, + lastModified: new Date(), + changeFrequency: 'weekly', + priority: 0.8, + }, + ]; +} diff --git a/src/app/start-listy/_components/ChoiceCategory.tsx b/src/app/start-listy/_components/ChoiceCategory.tsx index 2c235d48..f443983c 100644 --- a/src/app/start-listy/_components/ChoiceCategory.tsx +++ b/src/app/start-listy/_components/ChoiceCategory.tsx @@ -11,7 +11,7 @@ import { startListyLocale } from '@/app/start-listy/locale'; import { useLanguage } from '@/store/useLanguage'; interface CategoryProps { - handleChangeCategory: (category: Omit) => void; + handleChangeCategory: (category: Omit) => void; } export default function ChoiceCategory({ handleChangeCategory }: CategoryProps) { @@ -23,7 +23,7 @@ export default function ChoiceCategory({ handleChangeCategory }: CategoryProps) queryFn: getCategories, }); - const categories = data ? data?.filter((category) => category.korNameValue !== '전체') : []; + const categories = data ? data?.filter((category) => category.korName !== '전체') : []; const onClickChangeCategory = (e: MouseEvent) => { if (e.target === e.currentTarget) { @@ -31,19 +31,19 @@ export default function ChoiceCategory({ handleChangeCategory }: CategoryProps) } const targetId = (e.target as HTMLButtonElement).id; - const category = data?.find((category) => category.nameValue === targetId); + const category = data?.find((category) => category.engName === targetId); if (category) { handleChangeCategory({ - nameValue: category.nameValue, - korNameValue: category.korNameValue, + engName: category.engName, + korName: category.korName, }); - setValue('category', category.nameValue); + setValue('category', category.engName); } else { console.log(startListyLocale[language].notFountCategory); handleChangeCategory({ - nameValue: '', - korNameValue: '', + engName: '', + korName: '', }); } }; @@ -54,16 +54,16 @@ export default function ChoiceCategory({ handleChangeCategory }: CategoryProps)
{categories.map((category) => ( ))}
diff --git a/src/app/start-listy/_components/CreateListStep.tsx b/src/app/start-listy/_components/CreateListStep.tsx index 8119ff77..08c6dd52 100644 --- a/src/app/start-listy/_components/CreateListStep.tsx +++ b/src/app/start-listy/_components/CreateListStep.tsx @@ -30,8 +30,8 @@ export default function CreateListStep({ nickname }: CreateListStepProps) { const router = useRouter(); const [stepIndex, setStepIndex] = useState(0); const [selectedCategory, setSelectedCategory] = useState({ - nameValue: '', - korNameValue: '', + engName: '', + korName: '', }); const [isLoading, setIsLoading] = useState(false); @@ -69,10 +69,10 @@ export default function CreateListStep({ nickname }: CreateListStepProps) { } }; - const handleChangeCategory = (category: Omit) => { + const handleChangeCategory = (category: Omit) => { setSelectedCategory({ - nameValue: category.nameValue, - korNameValue: category.korNameValue, + engName: category.engName, + korName: category.korName, }); }; @@ -112,7 +112,7 @@ export default function CreateListStep({ nickname }: CreateListStepProps) {
- +

step2

@@ -134,8 +134,8 @@ export default function CreateListStep({ nickname }: CreateListStepProps) { diff --git a/src/app/start-listy/_components/RegisterItems.tsx b/src/app/start-listy/_components/RegisterItems.tsx index 35510748..cd764680 100644 --- a/src/app/start-listy/_components/RegisterItems.tsx +++ b/src/app/start-listy/_components/RegisterItems.tsx @@ -10,7 +10,7 @@ import { ListCreateType } from '@/lib/types/listType'; import { useLanguage } from '@/store/useLanguage'; interface ItemsStepProps { - selectedCategory: Omit; + selectedCategory: Omit; } export default function RegisterItems({ selectedCategory }: ItemsStepProps) { @@ -29,8 +29,8 @@ export default function RegisterItems({ selectedCategory }: ItemsStepProps) { {language === 'ko' ? '아이템을 적어주세요.' : 'items to be included in the list'}
-

{getValues('title')}

diff --git a/src/app/start-listy/_components/RegisterListTitle.tsx b/src/app/start-listy/_components/RegisterListTitle.tsx index 9a57255c..28a38478 100644 --- a/src/app/start-listy/_components/RegisterListTitle.tsx +++ b/src/app/start-listy/_components/RegisterListTitle.tsx @@ -11,7 +11,7 @@ import { startListyLocale } from '@/app/start-listy/locale'; import { useLanguage } from '@/store/useLanguage'; interface ListTitleStepProps { - selectedCategory: Omit; + selectedCategory: Omit; } export default function RegisterListTitle({ selectedCategory }: ListTitleStepProps) { @@ -48,8 +48,8 @@ export default function RegisterListTitle({ selectedCategory }: ListTitleStepPro

{errors.title?.message}

-

{watchForm}

diff --git a/src/app/user/[userId]/_components/Categories.tsx b/src/app/user/[userId]/_components/Categories.tsx index aeb77fcb..94b4777f 100644 --- a/src/app/user/[userId]/_components/Categories.tsx +++ b/src/app/user/[userId]/_components/Categories.tsx @@ -34,11 +34,11 @@ export default function Categories({ handleFetchListsOnCategory, selectedCategor ) : ( data?.map((category) => ( )) )} diff --git a/src/app/user/[userId]/collabolist/page.tsx b/src/app/user/[userId]/collabolist/page.tsx index 9d109b4b..3f562446 100644 --- a/src/app/user/[userId]/collabolist/page.tsx +++ b/src/app/user/[userId]/collabolist/page.tsx @@ -1,4 +1,4 @@ -import { Metadata } from 'next'; +import { Metadata, ResolvingMetadata } from 'next'; import Profile from '../_components/Profile'; import Content from '../_components/Content'; @@ -6,16 +6,33 @@ import FloatingContainer from '@/components/floatingButton/FloatingContainer'; import PlusOptionFloatingButton from '@/components/floatingButton/PlusOptionFloatingButton'; import ArrowUpFloatingButton from '@/components/floatingButton/ArrowUpFloatingButton'; +import axiosInstance from '@/lib/axios/axiosInstance'; +import { UserType } from '@/lib/types/userProfileType'; +import METADATA from '@/lib/constants/metadata'; + interface CollaboListPageProps { - params: { - userId: number; - }; + params: { userId: number }; } -export const metadata: Metadata = { - title: 'Collabo List', - description: '콜라보레이터와 함께 기록한 리스트 입니다.', -}; +export async function generateMetadata({ params }: CollaboListPageProps, parent: ResolvingMetadata): Promise { + const userId = params.userId; + const { data } = await axiosInstance.get(`/users/${userId}`); + + const previousImages = (await parent).openGraph?.images || []; + + return { + title: { + absolute: `${data.nickname}'s Collabo-list`, + }, + authors: [{ name: `${data.nickname}` }], + description: METADATA.description.collabolist, + openGraph: { + description: `${data.description || METADATA.description.collabolist}`, + url: `${METADATA.url}/user/${userId}/collabolist`, + images: [`${data.profileImageUrl}`, ...previousImages], + }, + }; +} export default function CollaboListPage({ params }: CollaboListPageProps) { return ( diff --git a/src/app/user/[userId]/mylist/page.tsx b/src/app/user/[userId]/mylist/page.tsx index 17d56917..289041f8 100644 --- a/src/app/user/[userId]/mylist/page.tsx +++ b/src/app/user/[userId]/mylist/page.tsx @@ -1,4 +1,4 @@ -import { Metadata } from 'next'; +import { Metadata, ResolvingMetadata } from 'next'; import Profile from '../_components/Profile'; import Content from '../_components/Content'; @@ -6,16 +6,33 @@ import FloatingContainer from '@/components/floatingButton/FloatingContainer'; import PlusOptionFloatingButton from '@/components/floatingButton/PlusOptionFloatingButton'; import ArrowUpFloatingButton from '@/components/floatingButton/ArrowUpFloatingButton'; +import axiosInstance from '@/lib/axios/axiosInstance'; +import { UserType } from '@/lib/types/userProfileType'; +import METADATA from '@/lib/constants/metadata'; + interface MyListPageProps { - params: { - userId: number; - }; + params: { userId: number }; } -export const metadata: Metadata = { - title: 'My List', - description: '나의 취향을 기록한 나만의 리스트 입니다.', -}; +export async function generateMetadata({ params }: MyListPageProps, parent: ResolvingMetadata): Promise { + const userId = params.userId; + const { data } = await axiosInstance.get(`/users/${userId}`); + + const previousImages = (await parent).openGraph?.images || []; + + return { + title: { + absolute: `${data.nickname}'s Mylist`, + }, + authors: [{ name: `${data.nickname}` }], + description: METADATA.description.mylist, + openGraph: { + description: `${data.description || METADATA.description.mylist}`, + url: `${METADATA.url}/user/${userId}/mylist`, + images: [`${data.profileImageUrl}`, ...previousImages], + }, + }; +} export default function MyListPage({ params }: MyListPageProps) { return ( diff --git a/src/lib/constants/domain.ts b/src/lib/constants/domain.ts new file mode 100644 index 00000000..8bf61cab --- /dev/null +++ b/src/lib/constants/domain.ts @@ -0,0 +1,3 @@ +const DOMAIN_URL = 'https://listywave.com'; + +export default DOMAIN_URL; diff --git a/src/lib/constants/metadata.ts b/src/lib/constants/metadata.ts new file mode 100644 index 00000000..ec77ca5a --- /dev/null +++ b/src/lib/constants/metadata.ts @@ -0,0 +1,29 @@ +import DOMAIN_URL from './domain'; + +const METADATA = { + title: { + template: '%s | ListyWave', // Template Object + default: 'ListyWave | 리스티웨이브', + openGraph: 'ListyWave', + }, + description: { + default: + "나의 취향을 리스트로 기록하고, 공유하고, 발견해요. 리스티웨이브에서 모든 기준은 '나의 취향'이에요. 내 취향 가득한 편안한 공간이 되면 좋겠습니다.", + mylist: '나의 취향을 기록한 리스트 입니다.', + collabolist: '콜라보레이터와 함께 기록한 콜라보 리스트 입니다.', + }, + authors: { + name: '에잇🩷', + url: 'https://github.com/8-Sprinters', + }, + generator: 'Next.js', + applicationName: 'ListyWave', + referrer: 'origin-when-cross-origin', // Referrer-Policy + keywords: ['ListyWave', 'list', '리스티웨이브'], + url: DOMAIN_URL, + type: 'website', + siteName: 'ListyWave', + locale: 'ko', +}; + +export default METADATA; diff --git a/src/lib/types/categoriesType.ts b/src/lib/types/categoriesType.ts index ac15d366..5bcfbc5d 100644 --- a/src/lib/types/categoriesType.ts +++ b/src/lib/types/categoriesType.ts @@ -1,6 +1,6 @@ export interface CategoryType { - codeValue: string; - nameValue: string; - korNameValue: string; + code: string; // 기존 : codeValue + engName: string; // 기존 : nameValue + korName: string; // 기존 : korNameValue categoryImageUrl?: string; } diff --git a/src/lib/types/listType.ts b/src/lib/types/listType.ts index 6414cbb6..d5f3f713 100644 --- a/src/lib/types/listType.ts +++ b/src/lib/types/listType.ts @@ -95,7 +95,8 @@ export interface LabelType { export interface ListDetailType { id?: number; - category: string; + categoryKorName: string; // 기존 : category + categoryEngName: string; // 추가됨 labels: LabelType[]; title: string; description: string;