-
Notifications
You must be signed in to change notification settings - Fork 6
Feat : 즐겨찾기 페이지 API 구현 #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 20 commits
ba6ad64
2b8d6cd
237a259
750aebd
b728084
ca6598e
5087f9c
8ace08e
47e65e1
115dcae
410e092
7cd4007
0bf37e0
54cd458
527827f
afd4862
41ae3a7
78e97f5
3ee4e21
e574e6a
763cb56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,66 +6,75 @@ interface linkDataType { | |
| id: number; | ||
| title: string; | ||
| description: string; | ||
| favorite: boolean; | ||
| favorite?: boolean; | ||
| imageSource: string; | ||
| url: string; | ||
| createdAt: string; | ||
| } | ||
|
|
||
| const LinkCard = (info: linkDataType) => { | ||
| const [isSubscribed, seIsSubscribed] = useState(false); | ||
| interface CardItemProps extends linkDataType { | ||
| isFavoritePage?: boolean; // 즐겨찾기 페이지 여부를 판별하는 flag | ||
| } | ||
|
|
||
| const LinkCard = ({ isFavoritePage, ...info }: CardItemProps) => { | ||
| const [isSubscribed, setIsSubscribed] = useState(false); | ||
| const [isOpen, setIsOpen] = useState(false); | ||
|
|
||
| const formattedDate = info.createdAt?.slice(0, 10).replace(/-/g, "."); | ||
| const createdTime = timeAgo(info.createdAt); | ||
|
|
||
| return ( | ||
| <div className="w-[340px] h-[344px] rounded-[12px] shadow-lg mt-20 ml-20 overflow-hidden cursor-pointer hover:scale-105 hover:duration-300"> | ||
| <div className="w-[340px] h-[344px] rounded-[12px] shadow-lg overflow-hidden cursor-pointer hover:scale-105 hover:duration-300"> | ||
| <section className="relative w-full h-[60%]"> | ||
| <Image | ||
| src={info.imageSource || `/images/no-content.svg`} | ||
| objectFit="cover" | ||
| className="object-cover" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| alt="링크 미리보기" | ||
| fill | ||
| /> | ||
| {isSubscribed ? ( | ||
| <div | ||
| onClick={() => seIsSubscribed(!isSubscribed)} | ||
| className="absolute top-[15px] right-[15px] z-1" | ||
| > | ||
| <Image | ||
| src="/icons/star-fill.svg" | ||
| width={32} | ||
| height={32} | ||
| alt="subscripe button" | ||
| /> | ||
| </div> | ||
| ) : ( | ||
| <div | ||
| onClick={() => seIsSubscribed(!isSubscribed)} | ||
| className="absolute top-[15px] right-[15px] z-1" | ||
| > | ||
| <Image | ||
| src="/icons/star-empty.svg" | ||
| width={32} | ||
| height={32} | ||
| alt="subscripe button" | ||
| /> | ||
| </div> | ||
| )} | ||
| {/* isFavoritePage가 false일 때만 즐겨찾기 버튼 렌더링 */} | ||
| {!isFavoritePage && | ||
| (isSubscribed ? ( | ||
| <div | ||
| onClick={() => seIsSubscribed(!isSubscribed)} | ||
| className="absolute top-[15px] right-[15px] z-1" | ||
| > | ||
| <Image | ||
| src="/icons/star-fill.svg" | ||
| width={32} | ||
| height={32} | ||
| alt="subscripe button" | ||
| /> | ||
| </div> | ||
| ) : ( | ||
| <div | ||
| onClick={() => seIsSubscribed(!isSubscribed)} | ||
| className="absolute top-[15px] right-[15px] z-1" | ||
| > | ||
| <Image | ||
| src="/icons/star-empty.svg" | ||
| width={32} | ||
| height={32} | ||
| alt="subscripe button" | ||
| /> | ||
| </div> | ||
| ))} | ||
| </section> | ||
|
|
||
| <section className="w-full h-[40%] flex flex-col justify-between gap-[10px] pt-[15px] px-[20px] pb-[10px]"> | ||
| <div className="flex justify-between"> | ||
| <span className="text-sm text-gray-400"> | ||
| {createdTime || "1일 전"} | ||
| </span> | ||
| <div | ||
| className="relative w-[21px] h-[17px]" | ||
| onClick={(state) => setIsOpen(!state)} | ||
| > | ||
| <Image src="/icons/kebab.svg" alt="kebab button" fill /> | ||
| </div> | ||
| {/* isFavoritePage가 false일 때만 케밥 버튼 렌더링 */} | ||
| {!isFavoritePage && ( | ||
| <div | ||
| className="relative w-[21px] h-[17px]" | ||
| onClick={(state) => setIsOpen(!state)} | ||
| > | ||
| <Image src="/icons/kebab.svg" alt="kebab button" fill /> | ||
| </div> | ||
| )} | ||
| </div> | ||
| <div className="text-[black100] text-lg "> | ||
| {info.description || "설명"} | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 와일드카드로 이미지 경로를 하면 전부 받아올 수 있으니 괜찮을 줄 알았는데 이미지 최적화 등 기능이 지원이 안된다고 하네요 ㅠ_ㅠ
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵! ㅎㅎ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { NextApiRequest, NextApiResponse } from "next"; | ||
| import { parse } from "cookie"; | ||
| import axiosInstance from "@/lib/api/axiosInstanceApi"; | ||
|
|
||
| const handler = async (req: NextApiRequest, res: NextApiResponse) => { | ||
| const cookies = parse(req.headers.cookie || ""); | ||
| const accessToken = cookies.accessToken; | ||
|
|
||
| if (!accessToken) { | ||
| return res.status(401).json({ message: "인증 오류: 토큰이 없습니다." }); | ||
| } | ||
|
|
||
| switch (req.method) { | ||
| case "GET": | ||
| // 즐겨찾기 목록 조회 | ||
| try { | ||
| const response = await axiosInstance.get( | ||
| "/favorites?page=1&pageSize=10", | ||
| { | ||
| headers: { Authorization: `Bearer ${accessToken}` }, | ||
| } | ||
| ); | ||
| return res.status(200).json(response.data); | ||
| } catch (err: any) { | ||
| // 즐겨찾기 폴더가 없는 경우 (404 처리) | ||
| if (err.response?.status === 404) { | ||
| return res.status(404).json({ message: "즐겨찾기 폴더가 없습니다." }); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 스웨거에서 즐겨찾기 폴더 없을때 404 코드인건 확인했는데 어떤 상황에 즐겨찾기 폴더가 없는건지 잘 모르겠네욥
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 스웨거 문구에 즐겨 찾기 폴더가 없는 경우 라고 되어있긴한데, 조금 헷갈릴 수도 있을 것 같네요! |
||
|
|
||
| console.error(err); | ||
| return res | ||
| .status(500) | ||
| .json({ message: "서버 에러 : 즐겨찾기 목록 조회에 실패했습니다." }); | ||
| } | ||
| default: | ||
| // 지원하지 않는 메서드 | ||
| res.setHeader("Allow", ["GET"]); | ||
| return res.status(405).end(`메서드 ${req.method}는 허용되지 않습니다.`); | ||
| } | ||
| }; | ||
| export default handler; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,48 @@ | ||
| import { GetServerSideProps, GetServerSidePropsContext } from "next"; | ||
| import { proxy } from "@/lib/api/axiosInstanceApi"; | ||
| import LinkCard from "@/components/LinkCard"; | ||
| import CardsLayout from "@/components/Layout/CardsLayout"; | ||
| import Container from "@/components/Layout/Container"; | ||
|
|
||
| const Favorite = () => { | ||
| interface FavoriteDataType { | ||
| id: number; | ||
| favorite: boolean; | ||
| url: string; | ||
| title: string; | ||
| imageSource: string; | ||
| description: string; | ||
| createdAt: string; | ||
| } | ||
|
|
||
| interface FavoriteProps { | ||
| totalCount: number; | ||
| favoriteList: FavoriteDataType[]; | ||
| } | ||
|
|
||
| export const getServerSideProps: GetServerSideProps = async ( | ||
| context: GetServerSidePropsContext | ||
| ) => { | ||
| const { req } = context; | ||
|
|
||
| // 클라이언트의 쿠키 가져오기 | ||
| const cookies = req.headers.cookie || ""; | ||
|
|
||
| try { | ||
| const res = await proxy.get("/api/favorites", { | ||
| headers: { | ||
| Cookie: cookies, // 쿠키를 그대로 포함시킴 | ||
| }, | ||
| }); | ||
|
|
||
| const { list, totalCount } = res.data || { list: [], totalCount: 0 }; | ||
| return { props: { favoriteList: list, totalCount } }; | ||
| } catch (error) { | ||
| console.error("서버사이드에러", error); | ||
| return { props: { favoriteList: [], totalCount: 0 } }; | ||
| } | ||
| }; | ||
|
|
||
| const FavoritePage = ({ favoriteList, totalCount }: FavoriteProps) => { | ||
| return ( | ||
| <> | ||
| <div className="page-title pt-[10px] md:pt-5 pb-10 md:pb-[60px] bg-gray100 text-center"> | ||
|
|
@@ -11,17 +52,37 @@ const Favorite = () => { | |
| </div> | ||
| <Container> | ||
| <CardsLayout> | ||
| {/* 카드 공통 컴포넌트로 구현 예정 */} | ||
| <div className="border border-red-800">card</div> | ||
| <div className="border border-red-800">card</div> | ||
| <div className="border border-red-800">card</div> | ||
| <div className="border border-red-800">card</div> | ||
| <div className="border border-red-800">card</div> | ||
| <div className="border border-red-800">card</div> | ||
| {favoriteList.length > 0 | ||
| ? favoriteList.map((favorite) => ( | ||
| <LinkCard | ||
| key={favorite.id} | ||
| id={favorite.id} | ||
| url={favorite.url} | ||
| title={favorite.title} | ||
| imageSource={favorite.imageSource} | ||
| description={favorite.description} | ||
| createdAt={favorite.createdAt} | ||
| isFavoritePage={true} | ||
| /> | ||
| )) | ||
| : null} | ||
| </CardsLayout> | ||
|
|
||
| {/* 즐겨찾기 항목이 없을 때 보여줄 메시지 (공통 컴포넌트로 사용할 건지 논의 필요) */} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 별도의 컴포넌트로 뺀다면 해당 페이지 코드가 좀더 깔끔하긴 할것같습니다 |
||
| {favoriteList.length === 0 && ( | ||
| <div className="flex flex-col justify-center items-center h-full p-10 bg-gray100 text-center text-gray600"> | ||
| <div className="text-2xl md:text-3xl font-semibold text-gray600"> | ||
| <span className="block mb-4">⭐️</span> | ||
| 즐겨찾기 항목이 없습니다. | ||
| </div> | ||
| <div className="text-sm text-purple100 mt-2"> | ||
| 저장한 즐겨찾기 항목을 추가해보세요. | ||
| </div> | ||
| </div> | ||
| )} | ||
| </Container> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default Favorite; | ||
| export default FavoritePage; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
list.map 으로 가져오게 되니까 div대신 li 태그가 나을까요? 🙄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
li 로 사용하게되면 cardslayout 도 ul태그로 바뀌어야겟네욤 : ) 리팩토링 할 때 얘기해볼게여