Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ba6ad64
Feat: api/favorites/index 로직 생성
99minji Nov 7, 2024
2b8d6cd
Fix: axiosInstance withCredentials 삭제
99minji Nov 7, 2024
237a259
Feat: 외부 이미지 도메인 추가
99minji Nov 7, 2024
750aebd
Fix: next.config remotePatterns 옵션 추가
99minji Nov 8, 2024
b728084
Merge branch 'feature/login-api' of https://github.com/codeit9-tempor…
99minji Nov 8, 2024
ca6598e
Merge branch 'develop' of https://github.com/codeit9-temporary/linkbr…
99minji Nov 8, 2024
5087f9c
Feat: 임시 useEffect 로 데이터 호출
99minji Nov 8, 2024
8ace08e
Design: Container css 수정
99minji Nov 8, 2024
47e65e1
Design : Carditem css margin 제거
99minji Nov 8, 2024
115dcae
Design: CardsLayout css 수정
99minji Nov 8, 2024
410e092
Fix: CardItem 에 isFavoritePage props 추가
99minji Nov 8, 2024
7cd4007
Comments : 주석 삭제
99minji Nov 8, 2024
0bf37e0
Design: auth css 수정
99minji Nov 8, 2024
54cd458
Fix: SSR 로 API 호출 시 헤더에 쿠키 포함시킴
99minji Nov 8, 2024
527827f
Merge branch 'develop' of https://github.com/codeit9-temporary/linkbr…
99minji Nov 8, 2024
afd4862
Chore: 불필요 코드 제거
99minji Nov 8, 2024
41ae3a7
Fix: ssr에서 proxy로 접근
99minji Nov 8, 2024
78e97f5
Merge branch 'develop' of https://github.com/codeit9-temporary/linkbr…
99minji Nov 8, 2024
3ee4e21
Merge branch 'develop' of https://github.com/codeit9-temporary/linkbr…
99minji Nov 9, 2024
e574e6a
Chore : 오타 수정
99minji Nov 9, 2024
763cb56
Chore : 오타 수정
99minji Nov 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/Layout/CardsLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface CardsLayoutProps {

const CardsLayout = ({ children }: CardsLayoutProps) => {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 lg:gap-[10px] w-full">
<div className="grid place-items-center grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 lg:gap-[20px] w-full">
{children}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion components/Layout/Container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface ContainerProps {

const Container = ({ children }: ContainerProps) => {
return (
<div className="w-full max-w-[1125px] mx-auto p-[10px] md:p-10 px-[32.5px]">
<div className="w-full max-w-[1125px] mx-auto p-[10px] md:p-10 lg:p-10 px-[32.5px]">
{children}
</div>
);
Expand Down
81 changes: 45 additions & 36 deletions components/LinkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list.map 으로 가져오게 되니까 div대신 li 태그가 나을까요? 🙄

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

li 로 사용하게되면 cardslayout 도 ul태그로 바뀌어야겟네욤 : ) 리팩토링 할 때 얘기해볼게여

<section className="relative w-full h-[60%]">
<Image
src={info.imageSource || `/images/no-content.svg`}
objectFit="cover"
className="object-cover"
Copy link
Collaborator

Choose a reason for hiding this comment

The 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={() => setIsSubscribed(!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={() => setIsSubscribed(!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 || "설명"}
Expand Down
1 change: 1 addition & 0 deletions lib/api/axiosInstanceApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const axiosInstance = axios.create({
export const proxy = axios.create({
// 배포 이후에는 배포된 URL로 변경해야 함.
// baseURL: "http://localhost:3000", -> baseURL을 안쓰면 로컬에서는 로컬3000, 배포했을때는 배포된 도메인으로 감
baseURL: "http://localhost:3000",
timeout: 5000,
withCredentials: true,
});
Expand Down
10 changes: 10 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
reactStrictMode: true,
// 외부 이미지 도메인 추가
images: {
remotePatterns: [
{
protocol: "https",
hostname: "*",
pathname: "/**",
},
],
},
};

export default nextConfig;
41 changes: 41 additions & 0 deletions pages/api/favorites/index.ts
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: "즐겨찾기 폴더가 없습니다." });
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스웨거에서 즐겨찾기 폴더 없을때 404 코드인건 확인했는데 어떤 상황에 즐겨찾기 폴더가 없는건지 잘 모르겠네욥
즐겨찾기한 링크가 없다는 걸까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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;
79 changes: 70 additions & 9 deletions pages/favorite/index.tsx
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">
Expand All @@ -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>

{/* 즐겨찾기 항목이 없을 때 보여줄 메시지 (공통 컴포넌트로 사용할 건지 논의 필요) */}
Copy link
Collaborator

Choose a reason for hiding this comment

The 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;
Loading