diff --git a/src/apis/contents.api.ts b/src/apis/contents.api.ts
index 488fb758..e80fd421 100644
--- a/src/apis/contents.api.ts
+++ b/src/apis/contents.api.ts
@@ -10,8 +10,19 @@ import type {
CultureContentDetail,
GetCultureContentsParams,
GetCultureContentsResponse,
+ OngoingContent,
} from '../types/content.type';
+export async function getOngoingContents(): Promise {
+ try {
+ const { data } = await apiClient.get('/contents/ongoing');
+
+ return data;
+ } catch (error) {
+ throw normalizeApiError(error);
+ }
+}
+
export async function getCultureContents(
params: GetCultureContentsParams = {},
signal?: AbortSignal,
diff --git a/src/hooks/useOngoingContents.ts b/src/hooks/useOngoingContents.ts
new file mode 100644
index 00000000..5378798a
--- /dev/null
+++ b/src/hooks/useOngoingContents.ts
@@ -0,0 +1,10 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { getOngoingContents } from '../apis/contents.api';
+
+export function useOngoingContents() {
+ return useQuery({
+ queryKey: ['contents', 'ongoing'],
+ queryFn: getOngoingContents,
+ });
+}
diff --git a/src/pages/detail/festival/index.tsx b/src/pages/detail/festival/index.tsx
index 3761f7e0..80655b52 100644
--- a/src/pages/detail/festival/index.tsx
+++ b/src/pages/detail/festival/index.tsx
@@ -195,7 +195,7 @@ function FestivalDetailContent({ contentId }: { contentId: number }) {
- {isCultureContentsPending ? (
+ {isOngoingContentsPending ? (
<>
diff --git a/src/pages/festival/ongoing/index.tsx b/src/pages/festival/ongoing/index.tsx
index 404f77cc..7ca53449 100644
--- a/src/pages/festival/ongoing/index.tsx
+++ b/src/pages/festival/ongoing/index.tsx
@@ -1,14 +1,13 @@
-import { useCallback } from 'react';
+import { useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import {
ContentCard,
ContentCardSkeleton,
} from '../../../components/common';
-import { useCultureContents } from '../../../hooks/useCultureContents';
+import { useOngoingContents } from '../../../hooks/useOngoingContents';
import { useContentLikeToggle } from '../../../hooks/useContentLikeToggle';
import { useGlobalScale } from '../../../hooks/useGlobalScale';
-import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
import { toContentTagIds } from '../../../utils/contentTags';
import { buildFestivalDetailPath } from '../../../utils/routes';
@@ -29,14 +28,6 @@ const LIST_GAP = 16;
const EMPTY_MARGIN_TOP = 40;
const ERROR_MARGIN_TOP = 24;
const MESSAGE_TEXT_SIZE = 13;
-const LOAD_MORE_HEIGHT = 40;
-
-const contentSortByFestivalSort = {
- RECOMMENDED: 'RECOMMEND',
- SAVED: 'LIKE',
- DISTANCE: 'DISTANCE',
- ENDING_SOON: 'DEADLINE',
-} as const;
function FestivalOngoingPage() {
const navigate = useNavigate();
@@ -47,35 +38,28 @@ function FestivalOngoingPage() {
handleSortSelect,
handleCategorySelect,
} = useFestivalFilters();
- const {
- data,
- fetchNextPage,
- hasNextPage,
- isError,
- isFetchingNextPage,
- isPending,
- } = useCultureContents({
- category:
- selectedFilters.category === 'ALL'
- ? undefined
- : selectedFilters.category,
- sort: contentSortByFestivalSort[selectedFilters.sort],
- size: 20,
- });
-
- const festivals = data?.pages.flatMap((page) => page.items) ?? [];
- const hasEmptyResult = !isPending && !isError && festivals.length === 0;
-
- const handleIntersect = useCallback(() => {
- if (hasNextPage && !isFetchingNextPage) {
- fetchNextPage();
+ const { data: ongoingContents, isPending, isError } = useOngoingContents();
+
+ // /contents/ongoing은 쿼리 파라미터를 아예 받지 않는 엔드포인트로 확인됨
+ // (category/sort/cursor 등 어떤 값을 보내도, 심지어 존재하지 않는 값을
+ // 보내도 항상 동일한 결과가 옴 - curl로 재확인). 응답 필드에도 category가
+ // 없어 카테고리 필터(체험/전시/공연/축제)를 적용할 데이터가 없다. 정렬도
+ // 종료 임박순(endDate)만 응답 필드로 계산 가능하고, 추천순/저장순/거리순은
+ // 추천 점수·좋아요 수·좌표가 응답에 없어 그대로 원본 순서를 유지한다.
+ // 필터 바 UI/상태는 그대로 두되, 실제 필터링·정렬은 API가 관련 필드/
+ // 파라미터를 지원해야 완전히 동작한다 - 백엔드에 카테고리 필드 및
+ // 정렬/페이지네이션 파라미터 추가를 요청해야 한다.
+ const festivals = useMemo(() => {
+ const items = ongoingContents ?? [];
+
+ if (selectedFilters.sort !== 'ENDING_SOON') {
+ return items;
}
- }, [fetchNextPage, hasNextPage, isFetchingNextPage]);
- const loadMoreRef = useInfiniteScroll({
- enabled: Boolean(hasNextPage) && !isPending,
- onIntersect: handleIntersect,
- });
+ return [...items].sort((a, b) => a.endDate.localeCompare(b.endDate));
+ }, [ongoingContents, selectedFilters.sort]);
+
+ const hasEmptyResult = !isPending && !isError && festivals.length === 0;
return (
))}
-
- {isFetchingNextPage
- ? FESTIVAL_SKELETON_ITEMS.slice(0, 4).map((item) => (
-
- ))
- : null}
{hasEmptyResult ? (
@@ -188,12 +162,6 @@ function FestivalOngoingPage() {
행사 목록을 불러오지 못했어요.
) : null}
-
-
);
}
diff --git a/src/pages/home/components/FestivalSection.tsx b/src/pages/home/components/FestivalSection.tsx
index c73e218e..969a1683 100644
--- a/src/pages/home/components/FestivalSection.tsx
+++ b/src/pages/home/components/FestivalSection.tsx
@@ -5,12 +5,14 @@ import {
ContentCardSkeleton,
SectionHeader,
} from '../../../components/common';
-import { useCultureContents } from '../../../hooks/useCultureContents';
+import { useOngoingContents } from '../../../hooks/useOngoingContents';
import { useContentLikeToggle } from '../../../hooks/useContentLikeToggle';
import { useGlobalScale } from '../../../hooks/useGlobalScale';
import { buildFestivalDetailPath } from '../../../utils/routes';
import { toContentTagIds } from '../../../utils/contentTags';
+const PREVIEW_ITEM_COUNT = 2;
+
const SECTION_MARGIN_TOP = 32;
const SECTION_PADDING_X = 24;
const LIST_MARGIN_TOP = 16;
@@ -20,15 +22,8 @@ function FestivalSection() {
const navigate = useNavigate();
const scale = useGlobalScale();
const { getLiked, toggleLike } = useContentLikeToggle();
- const {
- data: cultureContents,
- isPending: isLoading,
- } = useCultureContents({
- category: 'FESTIVAL',
- sort: 'RECOMMEND',
- size: 2,
- });
- const festivals = cultureContents?.pages[0]?.items ?? [];
+ const { data: ongoingContents, isPending: isLoading } = useOngoingContents();
+ const festivals = (ongoingContents ?? []).slice(0, PREVIEW_ITEM_COUNT);
return (
diff --git a/src/types/content.type.ts b/src/types/content.type.ts
index 94745111..bf45c0a8 100644
--- a/src/types/content.type.ts
+++ b/src/types/content.type.ts
@@ -93,6 +93,16 @@ export interface CultureContentDetail {
courses: CultureContentCourse[];
}
+export interface OngoingContent {
+ contentId: number;
+ title: string;
+ thumbnailImageUrl: string | null;
+ startDate: string;
+ endDate: string;
+ regionName: string;
+ hashtags: string[];
+}
+
export interface RecentCultureContent {
contentId: number;
title: string;