Skip to content
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

[Feat/#60] 메인 페이지 뉴스 조회 API 연결 #62

Merged
merged 6 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
1 change: 0 additions & 1 deletion src/apis/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import axios from 'axios';

export const instance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
withCredentials: true,
Copy link
Member

Choose a reason for hiding this comment

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

디버깅의 흔적인가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

로그인 토큰 넣을 때 필요한데, 이번 합세에서는 헤더에 넣는 요청들이 없더라구요! 그래서 지웠습니다...ㅎ

});

export function get<T>(...args: Parameters<typeof instance.get>) {
Expand Down
6 changes: 4 additions & 2 deletions src/apis/news/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { get } from '@apis/index';
import { AxiosResponse } from 'axios';
import { getNewsResponse, newsList } from 'src/types/news';

export const getNews = async (): Promise<newsList[] | null> => {
export const getNews = async (cursor?: number): Promise<newsList[] | null> => {
try {
const response: AxiosResponse<getNewsResponse> = await get('/news');
const response: AxiosResponse<getNewsResponse> = await get(
`/news${cursor ? `?cursor=${cursor}` : ''}`,
Copy link
Member

Choose a reason for hiding this comment

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

👍

);
return response.data.news;
} catch (error) {
console.log('error', error);
Expand Down
12 changes: 6 additions & 6 deletions src/apis/news/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { newsList } from 'src/types/news';
import { getNews } from './api';
import { useQuery } from '@tanstack/react-query';

export const useNews = () =>
export const useNews = (cursor: number) =>
useQuery<newsList[] | null>({
queryKey: ['news'], // 쿼리 키
queryFn: getNews, // API 함수
staleTime: 1000 * 60 * 5, // 5분
gcTime: 1000 * 60 * 10, // 10분
retry: 3, // 실패 시 재시도 횟수
queryKey: ['news', cursor],
queryFn: () => getNews(cursor),
staleTime: 1000 * 60 * 60,
gcTime: 1000 * 60 * 60 * 12,
retry: 3,
Copy link
Member

Choose a reason for hiding this comment

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

이 쿼리 세팅 값들은 그냥 보편적으로 사용되는 세팅값인가요? 아니면 고려하고 설정한게 있나요?? 궁금해서 여쭤보는 것...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

개발자가 쿼리마다 적절하게 지정해주는 것으로 알고 있습니다!!
저는 이렇게 지정해준 이유는, 맥도날드 홍보용 카드뉴스(?)는 사실 올라오는 주기가 그렇게 많지도 않고 한번 올라왔을 때 수정이 발생하지 않는다고 생각했습니다.
그래서 1시간 정도를 staletime으로 주어도 괜찮을 것 같다고 판단했고, staleTime보다 gcTime이 길게 설정되어야 하고, 적절한 시간을 고민하다가 12시간으로 설정해두었습니다!

refetchOnWindowFocus: false,
});
4 changes: 3 additions & 1 deletion src/components/main/HomeCard.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const TitleStyle = (isThreeLines: boolean) => (theme: Theme) => css`
color: ${theme.colors.gray500};
${theme.fonts.title07};
overflow: auto;
word-break: keep-all;
Copy link
Member

Choose a reason for hiding this comment

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

미친거니

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

왜그러니~~~~!

}
`;

Expand All @@ -61,14 +62,15 @@ export const SkeletonText = (theme: Theme) => css`
display: block;
width: 13.1rem;
height: fit-content;
min-height: 1.8rem;

color: transparent;
${theme.fonts.title07};
overflow: auto;
}

p::after {
content: '';
content: 'dd';
Copy link
Collaborator

Choose a reason for hiding this comment

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

디버깅의 흔적인가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ㄷㄷ 야무지다

position: absolute;
top: 0;
left: 0;
Expand Down
30 changes: 17 additions & 13 deletions src/components/main/HomeCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import * as S from './HomeCard.style';
import { calculateLine } from '@utils/calculateLine';

export interface HomeCardProps {
img: React.ReactNode;
title: string;
img: React.ReactNode | null;
title: string | null;
}

const HomeCard = ({ img, title }: HomeCardProps) => {
const titleRef = useRef<HTMLParagraphElement>(null);
const [isThreeLines, setIsThreeLines] = useState(false);
const [isCalculated, setIsCalculated] = useState(false);
const isDataValid = img && title;

useEffect(() => {
if (titleRef.current) {
Expand All @@ -21,18 +20,23 @@ const HomeCard = ({ img, title }: HomeCardProps) => {
}
}, [title]);

if (!img || !title || !isCalculated) {
return (
<section css={S.CardStyle}>
<div css={S.ImgStyle}>
<div css={S.SkeletonImg} />
</div>
<div css={S.SkeletonText}>
<p ref={titleRef}></p>
</div>
</section>
);
}

return (
<section css={S.CardStyle}>
<div css={S.ImgStyle}>
{isCalculated && isDataValid ? img : <div css={S.SkeletonImg} />}
</div>
<div
css={
isCalculated && isDataValid
? S.TitleStyle(isThreeLines)
: S.SkeletonText
}
>
<div css={S.ImgStyle}>{img}</div>
<div css={S.TitleStyle(isThreeLines)}>
<p ref={titleRef}>{title}</p>
</div>
</section>
Expand Down
12 changes: 3 additions & 9 deletions src/constants/main/homeCardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,26 @@ export const HOMECARD_LIST = [
{
id: 1,
img: <HCimg1 />,
title: '버거의 맛과 크기를 더블 패티로 높였다!',
},
{
id: 2,
img: <HCimg2 />,
title: '달콤한 블루베리 맛을 담은 보라빛 쉐이크',
},
{
id: 6,
id: 3,
img: <HCimg6 />,
title: '맥런치 할인에 사이드 할인까지 맥런치 플러스 세트 출시!',
},
{
id: 3,
id: 4,
img: <HCimg3 />,
title: '맥도날드 대표 간식을 1000원부터!',
},

{
id: 5,
img: <HCimg5 />,
title: '맥도날드 버거의 비밀 시즌 3 OPEN',
},
{
id: 4,
id: 6,
img: <HCimg4 />,
title: '모두가 기다린 애플파이 컴백!',
},
];
61 changes: 45 additions & 16 deletions src/pages/main/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,51 @@ import * as S from './Main.style';
import Spacing from '@components/common/spacing/Spacing';
import { IcSeemore } from '@assets/svgs';
import Carousel from '@components/main/Carousel';
import { useNews } from '@apis/news/queries';
import { useEffect, useState } from 'react';
import { mergeNewsWithImages } from '@utils/mergeNewsWithImages';

const Main = () => (
<>
<Carousel />
<main css={S.MainLayout}>
<h1 css={S.Title}>McDonald’s LIVE</h1>
<Spacing size="2.2" />
<article css={S.CardLayout}>
{HOMECARD_LIST.map((card) => (
<HomeCard key={card.id} img={card.img} title={card.title} />
))}
</article>
<Spacing size="6" />
<IcSeemore width={70} height={70} />
</main>
</>
);
const Main = () => {
const [cursor, setCursor] = useState(0);
const { data, isLoading } = useNews(cursor);
const [articles, setArticles] = useState<
{ id: number; content: string; img: JSX.Element | null }[]
>([]);

useEffect(() => {
if (data) {
const merged = mergeNewsWithImages(data, HOMECARD_LIST);
setArticles((prev) => [...prev, ...merged]);
}
}, [data]);

const handleSeeMore = () => {
const lastId = articles[articles.length - 1]?.id;
if (lastId) {
setCursor(lastId);
}
};

return (
<>
<Carousel />
<main css={S.MainLayout}>
<h1 css={S.Title}>McDonald’s LIVE</h1>
<Spacing size="2.2" />
<article css={S.CardLayout}>
{isLoading
? Array.from({ length: 6 }).map((_, index) => (
<HomeCard key={`skeleton-${index}`} img={null} title={null} />
))
: articles.map((card) => (
<HomeCard key={card.id} img={card.img} title={card.content} />
))}
</article>
<Spacing size="6" />
<IcSeemore width={70} height={70} onClick={handleSeeMore} />
</main>
</>
);
};

export default Main;
14 changes: 14 additions & 0 deletions src/utils/mergeNewsWithImages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { HOMECARD_LIST } from '@constants/main/homeCardList';
import { newsList } from 'src/types/news';

export const mergeNewsWithImages = (
news: newsList[] | null | undefined,
images: typeof HOMECARD_LIST,
) =>
news?.map((item, index) => {
const matchedImage = images[index % images.length];
return {
...item,
img: matchedImage ? matchedImage.img : null,
};
}) || [];