From 8ab0c61e0b03b7f9df1bc5eac82c0e40657ebac5 Mon Sep 17 00:00:00 2001 From: yeeun426 Date: Sat, 28 Feb 2026 23:59:58 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat=20:=20=EB=B8=94=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=ED=9B=84,=20=EC=9E=90=EB=A3=8C=EC=A7=91=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9E=AC=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(user)/library/[id]/[title]/page.tsx | 217 +++++++++++++++++++ src/common/card/ContentCard.tsx | 88 ++++++++ src/domain/blog/BlogListContent.tsx | 108 +++++---- src/domain/blog/card/BlogCard.tsx | 54 ----- src/domain/library/LibraryListContent.tsx | 58 +++-- src/domain/library/card/LibraryCard.tsx | 92 -------- src/domain/library/data/mockLibraryData.ts | 95 ++++++++ src/domain/library/ui/LibraryArticle.tsx | 65 ++++++ src/utils/url.ts | 15 ++ 9 files changed, 569 insertions(+), 223 deletions(-) create mode 100644 src/app/(user)/library/[id]/[title]/page.tsx create mode 100644 src/common/card/ContentCard.tsx delete mode 100644 src/domain/blog/card/BlogCard.tsx delete mode 100644 src/domain/library/card/LibraryCard.tsx create mode 100644 src/domain/library/data/mockLibraryData.ts create mode 100644 src/domain/library/ui/LibraryArticle.tsx diff --git a/src/app/(user)/library/[id]/[title]/page.tsx b/src/app/(user)/library/[id]/[title]/page.tsx new file mode 100644 index 000000000..f2b866c84 --- /dev/null +++ b/src/app/(user)/library/[id]/[title]/page.tsx @@ -0,0 +1,217 @@ +import { ProgramRecommendItem } from '@/api/blog/blogSchema'; +import { fetchProgramRecommend } from '@/api/program'; +import ContentCard from '@/common/card/ContentCard'; +import HorizontalRule from '@/common/HorizontalRule'; +import MoreHeader from '@/common/header/MoreHeader'; +import BlogKakaoShareBtn from '@/domain/blog/button/BlogKakaoShareBtn'; +import BlogLinkShareBtn from '@/domain/blog/button/BlogLilnkShareBtn'; +import ProgramRecommendCard from '@/domain/blog/card/ProgramRecommendCard'; +import Heading2 from '@/domain/blog/ui/BlogHeading2'; +import { + findMockLibrary, + MOCK_LIBRARY_RECOMMENDS, +} from '@/domain/library/data/mockLibraryData'; +import LibraryArticle from '@/domain/library/ui/LibraryArticle'; +import { twMerge } from '@/lib/twMerge'; +import { ProgramStatusEnum, ProgramTypeEnum } from '@/schema'; +import { + getBaseUrlFromServer, + getLibraryPathname, + getLibraryTitle, +} from '@/utils/url'; +import { CircleChevronRight } from 'lucide-react'; +import { Metadata } from 'next'; +import Link from 'next/link'; +import { notFound } from 'next/navigation'; +import { ReactNode } from 'react'; + +const { CHALLENGE } = ProgramTypeEnum.enum; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ id: string }>; +}): Promise { + const { id } = await params; + const library = findMockLibrary(id); + + if (!library) return {}; + + return { + title: getLibraryTitle(library), + description: library.description, + openGraph: { + title: library.title, + description: library.description, + url: getBaseUrlFromServer() + getLibraryPathname(library), + images: library.thumbnail ? [{ url: library.thumbnail }] : [], + }, + alternates: { + canonical: getBaseUrlFromServer() + getLibraryPathname(library), + }, + }; +} + +export default async function LibraryDetailPage({ + params, +}: { + params: Promise<{ id: string; title: string }>; +}) { + const { id } = await params; + + const libraryInfo = findMockLibrary(id); + if (!libraryInfo) notFound(); + + const programRecommendList = await getProgramRecommendList(); + const recommendLibraries = MOCK_LIBRARY_RECOMMENDS.filter( + (item) => item.id !== libraryInfo.id, + ).slice(0, 4); + + async function getProgramRecommendList() { + const data = await fetchProgramRecommend(); + const list: ProgramRecommendItem[] = []; + const ctaTitles: Record = { + CAREER_START: '경험 정리부터 이력서 완성까지', + PERSONAL_STATEMENT: '합격을 만드는 자소서 작성법', + PORTFOLIO: '나를 돋보이게 하는 포트폴리오', + PERSONAL_STATEMENT_LARGE_CORP: '합격을 만드는 자소서 작성법', + }; + + if (data.challengeList.length > 0) { + const targets = data.challengeList.slice(0, 3).map((item) => ({ + id: `${CHALLENGE}-${item.id}`, + ctaLink: `/program/${CHALLENGE.toLowerCase()}/${item.id}`, + ctaTitle: ctaTitles[item.challengeType ?? 'CAREER_START'], + })); + list.push(...targets); + } + + return list; + } + + return ( +
+
+ {/* 본문 */} +
+ + +
+ {/* 공유하기 */} +
+ + 나만 보기 아깝다면 공유하기 + + + + + 공유하기 + +
+
+ + + +

+ + 무료 자료집 홈 + {' '} + 바로가기 +

+ + + +
+ + {/* 프로그램 추천 */} + {programRecommendList.length > 0 && ( + + )} +
+ + + + {/* 다른 자료집 추천 */} + {recommendLibraries.length > 0 && ( +
+ + 다른 취준생들이 함께 찾은 콘텐츠 + +
+ {recommendLibraries.map((lib) => ( + + ))} +
+ + 더 많은 자료집 보기 + +
+ )} +
+ ); +} + +function MoreLink({ + href, + children, + className, +}: { + href: string; + children?: ReactNode; + className?: string; +}) { + return ( + + {children} + + ); +} diff --git a/src/common/card/ContentCard.tsx b/src/common/card/ContentCard.tsx new file mode 100644 index 000000000..8253e0ee3 --- /dev/null +++ b/src/common/card/ContentCard.tsx @@ -0,0 +1,88 @@ +import { twMerge } from '@/lib/twMerge'; +import Link from 'next/link'; +import { ReactNode } from 'react'; + +interface ContentCardProps { + href: string; + target?: string; + thumbnail?: ReactNode; + category: string; + title: string; + date?: string; + dateClassName?: string; + actionButton?: ReactNode; + variant?: 'blog' | 'library'; + className?: string; + containerProps?: Record; +} + +export default function ContentCard({ + href, + target, + thumbnail, + category, + title, + date, + dateClassName, + actionButton, + variant = 'blog', + className, + containerProps, +}: ContentCardProps) { + return ( +
+
+ {thumbnail} +
+ +
+ + {category} + + +
+

+ + {title} + + +

+ + {(date || actionButton) && ( +
+ {date && ( + + {date} + + )} + {actionButton} +
+ )} +
+
+
+ ); +} diff --git a/src/domain/blog/BlogListContent.tsx b/src/domain/blog/BlogListContent.tsx index 674a5ecaf..35797c9b3 100644 --- a/src/domain/blog/BlogListContent.tsx +++ b/src/domain/blog/BlogListContent.tsx @@ -8,6 +8,7 @@ import { } from '@/api/blog/blog'; import BellIcon from '@/assets/icons/Bell.svg'; import LockKeyHoleIcon from '@/assets/icons/lock-keyhole.svg'; +import ContentCard from '@/common/card/ContentCard'; import { YYYY_MM_DD } from '@/data/dayjsFormat'; import dayjs from '@/lib/dayjs'; import { twMerge } from '@/lib/twMerge'; @@ -22,7 +23,6 @@ import EmptyContainer from '../../common/container/EmptyContainer'; import FilterDropdown from '../../common/dropdown/FilterDropdown'; import LoadingContainer from '../../common/loading/LoadingContainer'; import MuiPagination from '../program/pagination/MuiPagination'; -import BlogCard from './card/BlogCard'; const filterList = Object.entries(blogCategory).map(([key, value]) => ({ caption: value, @@ -158,19 +158,17 @@ function BlogList({ ((isMobile && index === 3) || (!isMobile && index === 3)) ) { blogBannerCard = ( - { - e.preventDefault(); - router.push(blogBanners[0].link ?? ''); - }} + { - e.preventDefault(); - router.push(link); - }} + {blogBannerCard} - {blogThumbnailInfo.title - {/* 공계 예정인 썸네일에만 적용 */} - {willBePublished(blogThumbnailInfo.displayDate ?? '') && ( + {/* 공개 예정인 썸네일에만 적용 */} + {isUpcoming && (
@@ -258,14 +253,14 @@ function BlogList({ )} } - displayDateItem={`${dayjs(blogThumbnailInfo.displayDate).format( + date={`${dayjs(blogThumbnailInfo.displayDate).format( YYYY_MM_DD, - )} ${willBePublished(blogThumbnailInfo.displayDate ?? '') ? '예정' : '작성'}`} - buttonItem={ - willBePublished(blogThumbnailInfo.displayDate ?? '') ? ( + )} ${isUpcoming ? '예정' : '작성'}`} + actionButton={ + isUpcoming ? ( 알림신청 @@ -296,7 +291,6 @@ function BlogRecommendList() { const { data, isLoading } = useBlogListQuery({ pageable: { page: 1, size: 10 }, }); - const router = useRouter(); // 공개된 블로그 중 최신 게시글 4개 추천 const displayedBlogblogInfos = useMemo( () => @@ -315,30 +309,28 @@ function BlogRecommendList() { return (
{displayedBlogblogInfos?.map(({ blogThumbnailInfo }) => ( - { - e.preventDefault(); - router.push(`/blog/${blogThumbnailInfo.id}`); - }} - data-url={`/blog/${blogThumbnailInfo.id}`} - data-text={blogThumbnailInfo.title} - className="blog_empty_recommended cursor-pointer" - thumbnailItem={ + title={blogThumbnailInfo.title ?? ''} + thumbnail={ {blogThumbnailInfo.title } - displayDateItem={ + date={ blogThumbnailInfo.displayDate ? dayjs(blogThumbnailInfo.displayDate).format(YYYY_MM_DD) + ' 작성' diff --git a/src/domain/blog/card/BlogCard.tsx b/src/domain/blog/card/BlogCard.tsx deleted file mode 100644 index c79b4bd2d..000000000 --- a/src/domain/blog/card/BlogCard.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { twMerge } from '@/lib/twMerge'; -import Link, { LinkProps } from 'next/link'; -import { ReactNode } from 'react'; - -interface Props extends LinkProps { - title: string; - displayDateItem?: string; - superTitle: string; - buttonItem?: ReactNode; - thumbnailItem: ReactNode; - className?: string; - target?: string; -} - -const BlogCard = ({ - title, - displayDateItem, - superTitle, - buttonItem, - thumbnailItem, - className, - target, - ...restProps -}: Props) => { - return ( - -
- {thumbnailItem} -
-
- - {superTitle} - -

- {title} -

-
- {displayDateItem && ( - - {displayDateItem} - - )} - {buttonItem} -
-
- - ); -}; - -export default BlogCard; diff --git a/src/domain/library/LibraryListContent.tsx b/src/domain/library/LibraryListContent.tsx index ebdc0d7c8..a613d8b3f 100644 --- a/src/domain/library/LibraryListContent.tsx +++ b/src/domain/library/LibraryListContent.tsx @@ -1,10 +1,11 @@ 'use client'; +import BellIcon from '@/assets/icons/Bell.svg'; +import ContentCard from '@/common/card/ContentCard'; import FilterDropdown from '@/common/dropdown/FilterDropdown'; import MuiPagination from '@/domain/program/pagination/MuiPagination'; import { useSearchParams } from 'next/navigation'; import { Suspense, useState } from 'react'; -import LibraryCard from './card/LibraryCard'; import LibraryTabNav from './ui/LibraryTabNav'; const TABS = [ @@ -77,35 +78,54 @@ function Content() { function LibraryGrid() { return (
- {}} + > + + 알림 설정 + + } /> - {}} + > + + 알림 설정 완료 + + } /> - -
); diff --git a/src/domain/library/card/LibraryCard.tsx b/src/domain/library/card/LibraryCard.tsx deleted file mode 100644 index 84a1b0993..000000000 --- a/src/domain/library/card/LibraryCard.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import BellIcon from '@/assets/icons/Bell.svg'; -import { twMerge } from '@/lib/twMerge'; -import Link from 'next/link'; - -type LibraryCardStatus = 'published' | 'upcoming' | 'notified'; - -interface LibraryCardProps { - href: string; - thumbnail?: string | null; - category: string; - title: string; - date?: string; - status?: LibraryCardStatus; - className?: string; -} - -export default function LibraryCard({ - href, - thumbnail, - category, - title, - date, - status = 'published', - className, -}: LibraryCardProps) { - return ( -
-
- {thumbnail && ( - {title} - )} -
- -
- - {category} - - -
-

- - {title} - - -

- - {date && ( -
- - {date}{' '} - {status === 'published' ? '작성' : '공개 예정'} - - - {status === 'upcoming' && ( - - )} - - {status === 'notified' && ( - - )} -
- )} -
-
-
- ); -} diff --git a/src/domain/library/data/mockLibraryData.ts b/src/domain/library/data/mockLibraryData.ts new file mode 100644 index 000000000..9c32b0a94 --- /dev/null +++ b/src/domain/library/data/mockLibraryData.ts @@ -0,0 +1,95 @@ +export interface LibraryDetailInfo { + id: number; + title: string; + category: string; + description: string; + thumbnail: string | null; + displayDate: string; + content: string; +} + +export const MOCK_LIBRARY_DETAILS: LibraryDetailInfo[] = [ + { + id: 1, + title: '제목이 한줄 일때', + category: '카테고리 분류', + description: + '취업 준비에 꼭 필요한 자료를 모았습니다. 지금 바로 확인하세요.', + thumbnail: null, + displayDate: '2025-01-30', + content: `

취업 준비, 어디서부터 시작해야 할지 막막하신가요?

+

렛츠커리어가 준비한 무료 자료집으로 취업 준비의 첫걸음을 내딛어 보세요. 이력서 작성부터 면접 준비까지, 취준생이 꼭 알아야 할 핵심 정보를 담았습니다.

+

자료집 주요 내용

+
    +
  • 이력서 작성 가이드
  • +
  • 자기소개서 핵심 포인트
  • +
  • 면접 준비 체크리스트
  • +
+

지금 바로 다운로드하여 취업 준비에 활용해 보세요!

`, + }, + { + id: 2, + title: + '제목이 들어갑니다 제목이 들어갑니다 제목이 들어갑니다 제목이 들어갑니다 제목', + category: '카테고리 분류', + description: '취업 시장 트렌드와 합격 전략을 한눈에 정리했습니다.', + thumbnail: null, + displayDate: '2025-01-30', + content: `

2025년 취업 시장은 어떻게 변화하고 있을까요?

+

최신 채용 트렌드와 기업별 선호 역량을 분석하여, 여러분의 취업 전략 수립에 도움이 될 자료를 준비했습니다.

+

주요 트렌드

+
    +
  1. AI 역량의 중요성 증가
  2. +
  3. 프로젝트 경험 중심 평가
  4. +
  5. 소프트스킬 강조
  6. +
+

변화하는 채용 시장에서 경쟁력을 갖추세요.

`, + }, + { + id: 3, + title: '나의 경험을 200% 활용하여 제작하는 자기소개서 2주 완성 챌린지', + category: '카테고리 분류', + description: + '자기소개서 작성에 어려움을 겪고 있다면, 이 자료집이 도움이 됩니다.', + thumbnail: null, + displayDate: '2025-02-15', + content: `

자기소개서, 매번 쓸 때마다 막막하셨나요?

+

나의 경험을 체계적으로 정리하고, 기업이 원하는 인재상에 맞춰 자기소개서를 작성하는 방법을 알려드립니다.

+

2주 완성 플랜

+
    +
  • 1주차: 경험 정리 및 핵심 역량 도출
  • +
  • 2주차: 기업별 맞춤 자기소개서 작성
  • +
+

이런 분께 추천해요

+
    +
  • 자기소개서를 처음 쓰는 취준생
  • +
  • 기존 자기소개서를 업그레이드하고 싶은 분
  • +
  • 다양한 경험을 효과적으로 어필하고 싶은 분
  • +
`, + }, + { + id: 4, + title: + '제목이 들어갑니다 제목이 들어갑니다 제목이 들어갑니다 제목이 들어갑니다 제목', + category: '카테고리 분류', + description: '포트폴리오 제작에 필요한 모든 것을 담았습니다.', + thumbnail: null, + displayDate: '2025-01-30', + content: `

포트폴리오, 어떻게 만들어야 할지 고민이신가요?

+

직무별 포트폴리오 작성 가이드와 실제 합격 사례를 참고하여 나만의 포트폴리오를 완성해 보세요.

+

포트폴리오 구성 요소

+
    +
  • 프로젝트 소개 및 역할
  • +
  • 문제 해결 과정
  • +
  • 성과 및 결과물
  • +
`, + }, +]; + +export const MOCK_LIBRARY_RECOMMENDS = MOCK_LIBRARY_DETAILS.slice(0, 4); + +export function findMockLibrary( + id: string | number, +): LibraryDetailInfo | undefined { + return MOCK_LIBRARY_DETAILS.find((item) => item.id === Number(id)); +} diff --git a/src/domain/library/ui/LibraryArticle.tsx b/src/domain/library/ui/LibraryArticle.tsx new file mode 100644 index 000000000..36fbae36f --- /dev/null +++ b/src/domain/library/ui/LibraryArticle.tsx @@ -0,0 +1,65 @@ +import { YYYY_MM_DD } from '@/data/dayjsFormat'; +import dayjs from '@/lib/dayjs'; +import Image from 'next/image'; +import { LibraryDetailInfo } from '../data/mockLibraryData'; + +interface Props { + libraryInfo: LibraryDetailInfo; +} + +export default function LibraryArticle({ libraryInfo }: Props) { + return ( +
+ {/* 썸네일 */} +
+ {libraryInfo.thumbnail && ( + 자료집 썸네일 + )} +
+ + {/* 헤더 */} +
+
+

+ {libraryInfo.category} +

+

+ {libraryInfo.title} +

+
+ +
+
+
+ 렛츠커리어 프로필 사진 +
+ + 렛츠커리어 + +
+

+ {dayjs(libraryInfo.displayDate).format(YYYY_MM_DD)} 작성 +

+
+
+ + {/* 본문 */} +
+
+ ); +} diff --git a/src/utils/url.ts b/src/utils/url.ts index 8778f680d..512deabd4 100644 --- a/src/utils/url.ts +++ b/src/utils/url.ts @@ -94,6 +94,21 @@ export function getBaseUrlFromServer(): string { return process.env.BASE_URL || 'http://localhost:3000'; } +export function getLibraryPathname({ + id, + title, +}: { + id?: string | number | null; + title?: string | null; +}): string { + const slug = (title?.replace(/[ /]/g, '-') || '').toLowerCase(); + return `/library/${id}/${encodeURIComponent(slug)}`; +} + +export function getLibraryTitle({ title }: { title?: string | null }) { + return `${title} | 무료 자료집 - 렛츠커리어`; +} + export function getUniversalBaseUrl(): string { if (typeof window !== 'undefined') { return window.location.origin; From e4485f6b9e9d7133b499c973bee60411a210c80d Mon Sep 17 00:00:00 2001 From: yeeun426 Date: Sun, 1 Mar 2026 01:35:26 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat=20:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(user)/library/[id]/[title]/page.tsx | 17 +++-- src/common/button/LikeButton.tsx | 69 ++++++++++++++++++++ src/domain/blog/button/BlogLikeBtn.tsx | 66 +++---------------- src/domain/blog/button/BlogLilnkShareBtn.tsx | 2 +- src/domain/library/ui/LibraryArticle.tsx | 33 ++++++---- 5 files changed, 108 insertions(+), 79 deletions(-) create mode 100644 src/common/button/LikeButton.tsx diff --git a/src/app/(user)/library/[id]/[title]/page.tsx b/src/app/(user)/library/[id]/[title]/page.tsx index f2b866c84..cbcab3482 100644 --- a/src/app/(user)/library/[id]/[title]/page.tsx +++ b/src/app/(user)/library/[id]/[title]/page.tsx @@ -1,8 +1,9 @@ import { ProgramRecommendItem } from '@/api/blog/blogSchema'; import { fetchProgramRecommend } from '@/api/program'; +import LikeButton from '@/common/button/LikeButton'; import ContentCard from '@/common/card/ContentCard'; -import HorizontalRule from '@/common/HorizontalRule'; import MoreHeader from '@/common/header/MoreHeader'; +import HorizontalRule from '@/common/HorizontalRule'; import BlogKakaoShareBtn from '@/domain/blog/button/BlogKakaoShareBtn'; import BlogLinkShareBtn from '@/domain/blog/button/BlogLilnkShareBtn'; import ProgramRecommendCard from '@/domain/blog/card/ProgramRecommendCard'; @@ -90,13 +91,19 @@ export default async function LibraryDetailPage({ } return ( -
+
{/* 본문 */}
-
+
+ {/* 좋아요 */} + {/* 공유하기 */}
@@ -127,9 +134,7 @@ export default async function LibraryDetailPage({ className="flex w-full items-center justify-center gap-2 py-5 md:rounded-xs md:bg-neutral-95" >

- - 무료 자료집 홈 - {' '} + 자료집 홈{' '} 바로가기

void; + onDislike?: (id: string) => void; + className?: string; +} + +export default function LikeButton({ + id, + likeCount, + storageKey = 'like', + onLike, + onDislike, + className, +}: LikeButtonProps) { + const likedIds = useRef([]); + const [alreadyLike, setAlreadyLike] = useState(false); + const [countOne, setCountOne] = useState(0); + + useEffect(() => { + const value = localStorage.getItem(storageKey); + if (value && id) { + const list = value.split(','); + likedIds.current = list; + setAlreadyLike(list.includes(id)); + } + }, [id, storageKey]); + + const handleClick = () => { + if (alreadyLike) { + onDislike?.(id); + setCountOne((prev) => prev - 1); + setAlreadyLike(false); + likedIds.current = likedIds.current.filter((i) => i !== id); + } else { + onLike?.(id); + setCountOne((prev) => prev + 1); + setAlreadyLike(true); + likedIds.current.push(id); + } + + localStorage.setItem(storageKey, likedIds.current.toString()); + }; + + return ( + + ); +} diff --git a/src/domain/blog/button/BlogLikeBtn.tsx b/src/domain/blog/button/BlogLikeBtn.tsx index cc6535879..65894d534 100644 --- a/src/domain/blog/button/BlogLikeBtn.tsx +++ b/src/domain/blog/button/BlogLikeBtn.tsx @@ -1,11 +1,8 @@ 'use client'; import { usePatchBlogDislike, usePatchBlogLike } from '@/api/blog/blog'; -import { Heart } from 'lucide-react'; +import LikeButton from '@/common/button/LikeButton'; import { useParams } from 'next/navigation'; -import { useEffect, useRef, useState } from 'react'; - -const LIKE = 'like'; interface Props { likeCount: number; @@ -13,65 +10,18 @@ interface Props { function BlogLikeBtn({ likeCount }: Props) { const { id } = useParams<{ id: string }>(); - const likedBlogs = useRef([]); // 이미 좋아요한 블로그 id 리스트 const patchLikeMutation = usePatchBlogLike(); const patchDislikeMutation = usePatchBlogDislike(); - const [alreadyLike, setAlreadyLike] = useState(false); - // 좋아요 클릭 시 1씩 카운트하는 state - const [countOne, setCountOne] = useState(0); - - /* 로컬 스토리지에서 좋아요 여부 확인 */ - useEffect(() => { - const checkLikedStatus = () => { - const value = localStorage.getItem(LIKE); - if (value && id) { - const likedBlogList = value.split(','); - likedBlogs.current = likedBlogList; - setAlreadyLike(likedBlogList.includes(id)); - } - }; - - checkLikedStatus(); - }, [id]); - return ( - + onLike={(id) => patchLikeMutation.mutate(id)} + onDislike={(id) => patchDislikeMutation.mutate(id)} + /> ); } diff --git a/src/domain/blog/button/BlogLilnkShareBtn.tsx b/src/domain/blog/button/BlogLilnkShareBtn.tsx index 4b13343c5..f05ee2f36 100644 --- a/src/domain/blog/button/BlogLilnkShareBtn.tsx +++ b/src/domain/blog/button/BlogLilnkShareBtn.tsx @@ -31,7 +31,7 @@ function BlogLinkShareBtn({ onClick={async () => { try { await navigator.clipboard.writeText( - window.location.origin + location.pathname, + window.location.origin + decodeURIComponent(location.pathname), ); snackbar('클립보드에 복사되었습니다.'); } catch (err) { diff --git a/src/domain/library/ui/LibraryArticle.tsx b/src/domain/library/ui/LibraryArticle.tsx index 36fbae36f..8caa24a31 100644 --- a/src/domain/library/ui/LibraryArticle.tsx +++ b/src/domain/library/ui/LibraryArticle.tsx @@ -1,4 +1,5 @@ import { YYYY_MM_DD } from '@/data/dayjsFormat'; +import BlogLinkShareBtn from '@/domain/blog/button/BlogLilnkShareBtn'; import dayjs from '@/lib/dayjs'; import Image from 'next/image'; import { LibraryDetailInfo } from '../data/mockLibraryData'; @@ -36,22 +37,26 @@ export default function LibraryArticle({ libraryInfo }: Props) {
-
-
-
- 렛츠커리어 프로필 사진 +
+
+
+
+ 렛츠커리어 프로필 사진 +
+ + 렛츠커리어 +
- - 렛츠커리어 - +

+ {dayjs(libraryInfo.displayDate).format(YYYY_MM_DD)} 작성 +

-

- {dayjs(libraryInfo.displayDate).format(YYYY_MM_DD)} 작성 -

+ {/* 공유 버튼 */} +