diff --git a/config/base.next.config.js b/config/base.next.config.js index 3c76e311..0f0a3239 100644 --- a/config/base.next.config.js +++ b/config/base.next.config.js @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-var-requires */ -const IS_PROD = process.env.NODE_ENV === "production"; +const isProd = process.env.NODE_ENV === "production"; +const cloudName = process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME || ""; // The folders containing files importing twin.macro const path = require("path"); @@ -62,16 +63,10 @@ module.exports = () => ({ return config; }, images: { - unoptimized: true, - remotePatterns: [ - { - protocol: "https", - hostname: "**", - }, - ], + unoptimized: !isProd || !cloudName, // Link: https://fe-developers.kakaoent.com/2022/220714-next-image/ - imageSizes: [64, 256], - deviceSizes: [512], + deviceSizes: [440], + imageSizes: [100, 200], contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;", }, sentry: { @@ -81,6 +76,6 @@ module.exports = () => ({ // https://webpack.js.org/configuration/devtool/ and // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#use-hidden-source-map // for more information. - hideSourceMaps: IS_PROD, + hideSourceMaps: isProd, }, }); diff --git a/config/cloudinary-loader.ts b/config/cloudinary-loader.ts new file mode 100644 index 00000000..99dfbb06 --- /dev/null +++ b/config/cloudinary-loader.ts @@ -0,0 +1,26 @@ +"use client"; + +import type { ImageLoaderProps } from "next/image"; + +const cloudName = process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME || ""; + +export function cloudinaryLoader({ src, width, quality }: ImageLoaderProps) { + const rawTransformations = ["f_auto", "c_limit", `w_${width}`, `q_${quality || "auto"}`]; + let isAbsolute: boolean; + let href: string; + + if (src.startsWith("/")) { + href = src; + isAbsolute = false; + } else { + const hrefParsed = new URL(src); + href = hrefParsed.toString(); + isAbsolute = true; + } + + const cldUrl = `https://res.cloudinary.com/${cloudName}/image/fetch/${rawTransformations.join( + ",", + )}/${href}`; + + return isAbsolute ? cldUrl : src; +} diff --git a/src/common/components/Navigation/SideBar/LoginSideBarContent.tsx b/src/common/components/Navigation/SideBar/LoginSideBarContent.tsx index a4a5a2eb..fb303639 100644 --- a/src/common/components/Navigation/SideBar/LoginSideBarContent.tsx +++ b/src/common/components/Navigation/SideBar/LoginSideBarContent.tsx @@ -1,3 +1,4 @@ +import { cloudinaryLoader } from "config/cloudinary-loader"; import Link from "next/link"; import { Icon } from "@/common/components/Icon"; @@ -17,7 +18,13 @@ export const LoginSideBarContent = (props: LoginSideBarContentProps) => { <>
- + {user?.name} diff --git a/src/common/components/Navigation/SideBar/LogoutSideBarContent.tsx b/src/common/components/Navigation/SideBar/LogoutSideBarContent.tsx index ca246dce..973d782a 100644 --- a/src/common/components/Navigation/SideBar/LogoutSideBarContent.tsx +++ b/src/common/components/Navigation/SideBar/LogoutSideBarContent.tsx @@ -15,7 +15,13 @@ export const LogoutSideBarContent = (props: LogoutSideBarContentProps) => { <>
); diff --git a/src/common/components/RandomImge/RandomImage.tsx b/src/common/components/RandomImge/RandomImage.tsx index acef46bc..c10bbea7 100644 --- a/src/common/components/RandomImge/RandomImage.tsx +++ b/src/common/components/RandomImge/RandomImage.tsx @@ -2,7 +2,7 @@ import type { ComponentProps } from "react"; import { Photo } from "../Photo"; -interface Props extends ComponentProps { +interface Props extends Omit, "alt" | "src" | "sizes"> { images?: { name: string; src: string }[]; } const randomImages = [ @@ -33,5 +33,13 @@ const randomImages = [ ]; export const RandomImage = ({ images = randomImages, className = "" }: Props) => { const randomImage = images[Math.floor(Math.random() * images.length)]; - return ; + return ( + + ); }; diff --git a/src/features/common/components/InfiniteMemeList.tsx b/src/features/common/components/InfiniteMemeList.tsx index 5462bc28..d2e80916 100644 --- a/src/features/common/components/InfiniteMemeList.tsx +++ b/src/features/common/components/InfiniteMemeList.tsx @@ -4,7 +4,7 @@ import { MemeItem } from "@/features/common"; import type { Meme } from "@/types"; interface InfiniteMemeListProps { - memeList: Meme[]; + memeList: (Meme & { priority?: boolean })[]; onRequestAppend: () => void; } diff --git a/src/features/common/components/MemeItem.tsx b/src/features/common/components/MemeItem.tsx index bcbb5997..339590de 100644 --- a/src/features/common/components/MemeItem.tsx +++ b/src/features/common/components/MemeItem.tsx @@ -1,3 +1,4 @@ +import { cloudinaryLoader } from "config/cloudinary-loader"; import { memo } from "react"; import { Icon } from "@/common/components/Icon"; @@ -8,7 +9,7 @@ import { MemeActionSheet, useMoveMemeDetail } from "@/features/common"; import type { Meme } from "@/types"; interface Props { - meme: Meme; + meme: Meme & { priority?: boolean }; onClick?: (id: number) => void; } @@ -32,7 +33,8 @@ export const MemeItem = memo(({ meme, onClick }: Props) => { className="rounded-16" draggable={false} height={image.images[0]?.imageHeight} - sizes="100px" + loader={cloudinaryLoader} + sizes="200px" src={image.images[0]?.imageUrl} unoptimized={isEncodingError(image.images[0]?.imageUrl)} width={image.images[0]?.imageWidth} diff --git a/src/features/common/components/TagCategory/CategoryContent.tsx b/src/features/common/components/TagCategory/CategoryContent.tsx index 6a9cb8ca..9a0dde39 100644 --- a/src/features/common/components/TagCategory/CategoryContent.tsx +++ b/src/features/common/components/TagCategory/CategoryContent.tsx @@ -64,7 +64,13 @@ export const CategoryContent = () => { gtmTrigger[category.name] } flex w-full items-center justify-between gap-8 rounded-full px-4 py-12 text-16-semibold-140 [&>span>#chevronDown]:data-[state=open]:rotate-180`} > - + {category.mainTags.length ? ( {
- + {FAVORITE_ID} { } return ( <> - + fetchNextPage({ cancelRefetch: false })} @@ -29,9 +34,10 @@ export const MemesByTagsContainer = ({ tag }: Props) => { interface ThumbnailProps { image: string; + tag: string; totalCount: number; } -const Thumbnail = React.memo(function Thumbnail({ image, totalCount }: ThumbnailProps) { +const Thumbnail = React.memo(function Thumbnail({ image, tag, totalCount }: ThumbnailProps) { const router = useRouter(); const clipboard = useClipboard(); const toast = useToast(); @@ -40,7 +46,14 @@ const Thumbnail = React.memo(function Thumbnail({ image, totalCount }: Thumbnail return (
- +
{totalCount}개 밈