diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 473bade..4102775 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,22 +1,19 @@ +import { Button } from '@workspace/ui/components/button'; import Link from 'next/link'; -import { getInfiniteScrollData } from '@/features/post/post-list/api/post-infinite-scroll'; -import { InfiniteScroll } from '@/features/post/post-list/ui/infinite-scroll'; -import { Button } from '@/shared/ui/button'; +import { getInfiniteScrollData } from '@/features/post/post-list'; +import { PostInfiniteScroll } from '@/features/post/post-list'; export default async function Home() { const { data, nextCursor, hasMore } = await getInfiniteScrollData('', 10); return ( <> - - ({ + ...post, + localeCreatedAt: formatToLocaleDate(post.createdAt), +}); diff --git a/apps/web/features/post/post-list/api/post-infinite-scroll.ts b/apps/web/features/post/post-list/api/post-infinite-scroll.ts index 00ef53c..8fcd764 100644 --- a/apps/web/features/post/post-list/api/post-infinite-scroll.ts +++ b/apps/web/features/post/post-list/api/post-infinite-scroll.ts @@ -1,26 +1,15 @@ -import { BASE_URL } from '@/shared/api/http-client'; -import { GetPostsCursor } from '@/shared/types/post-types'; - -const commonHeaders = new Headers(); -commonHeaders.append('Content-Type', 'application/json'); +import { GetPostsCursor } from '@/entities/post'; +import { httpClient } from '@/shared/api'; export async function getInfiniteScrollData( cursor?: string, limit?: number ): Promise { - const followingURL = `/posts/infinite?${new URLSearchParams({ - ...(cursor && { cursor }), // cursor가 있으면 추가, undefined or null이면 추가 안 함 - ...(limit && { limit: limit.toString() }), // limit가 있으면 추가 - })}`; - - const response = await fetch(`${BASE_URL}${followingURL}`, { + return httpClient('/posts/infinite', { method: 'GET', - headers: commonHeaders, + queryParams: { + cursor, + limit, + }, }); - - if (!response.ok) { - throw new Error(`${response.status} ${response.statusText}`); - } - - return response.json(); } diff --git a/apps/web/features/post/post-list/index.ts b/apps/web/features/post/post-list/index.ts new file mode 100644 index 0000000..84011c2 --- /dev/null +++ b/apps/web/features/post/post-list/index.ts @@ -0,0 +1,3 @@ +export { getInfiniteScrollData } from './api/post-infinite-scroll'; +export { PostInfiniteScroll } from './ui/post-infinite-scroll'; +export { PostItem } from './ui/post-item'; diff --git a/apps/web/features/post/post-list/ui/infinite-scroll.tsx b/apps/web/features/post/post-list/ui/post-infinite-scroll.tsx similarity index 64% rename from apps/web/features/post/post-list/ui/infinite-scroll.tsx rename to apps/web/features/post/post-list/ui/post-infinite-scroll.tsx index 768d82b..ab9acb4 100644 --- a/apps/web/features/post/post-list/ui/infinite-scroll.tsx +++ b/apps/web/features/post/post-list/ui/post-infinite-scroll.tsx @@ -1,23 +1,23 @@ 'use client'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { getInfiniteScrollData } from '@/features/post/post-list/api/post-infinite-scroll'; -import { PostItem } from '@/features/post/post-list/ui/post-item'; -import { formatToLocaleDate } from '@/shared/lib/format-date'; -import { Post } from '@/shared/types/post-types'; +import { mapPostToViewModel } from '@/entities/post'; +import { Post } from '@/entities/post'; +import { getInfiniteScrollData } from '@/features/post/post-list'; +import { PostItem } from '@/features/post/post-list'; -type InfiniteScrollProps = { +type PostInfiniteScrollProps = { postList: Post[]; lastPostId: string | null; hasMore: boolean; }; -export function InfiniteScroll({ +export function PostInfiniteScroll({ postList, lastPostId, hasMore, -}: InfiniteScrollProps) { +}: PostInfiniteScrollProps) { const [loading, setLoading] = useState(false); const [posts, setPosts] = useState(postList); const [cursor, setCursor] = useState(lastPostId ? lastPostId : null); @@ -55,24 +55,26 @@ export function InfiniteScroll({ }; }, [loadMorePosts]); + const postViewModels = useMemo( + () => posts.map((post) => mapPostToViewModel(post)), + [posts] + ); + return (
- {posts.map(({ id, title, content, author, createdAt }) => { - const localeCreatedAt = formatToLocaleDate(createdAt); - return ( - - ); - })} + {postViewModels.map((postViewModel) => ( + + ))}

{posts.at(-1)?.id === cursor ? '*************더 많은 게시글 로딩 중****************' diff --git a/apps/web/features/post/post-list/ui/post-item.tsx b/apps/web/features/post/post-list/ui/post-item.tsx index b499695..d0cd749 100644 --- a/apps/web/features/post/post-list/ui/post-item.tsx +++ b/apps/web/features/post/post-list/ui/post-item.tsx @@ -13,7 +13,7 @@ export function PostItem({

{title}


{content}

-

+
{author} diff --git a/apps/web/shared/api/http-client.ts b/apps/web/shared/api/http-client.ts index ad9962d..224c94e 100644 --- a/apps/web/shared/api/http-client.ts +++ b/apps/web/shared/api/http-client.ts @@ -1,10 +1,25 @@ export const BASE_URL = 'http://localhost:4000/api'; +interface HttpClientOptions extends RequestInit { + queryParams?: Record; +} + export const httpClient = async ( endpoint: string, - options?: RequestInit + options?: HttpClientOptions ): Promise => { - const response = await fetch(`${BASE_URL}${endpoint}`, { + const url = new URL(`${BASE_URL}${endpoint}`); + + // queryParams가 있을 경우 URL에 추가 + if (options?.queryParams) { + Object.entries(options.queryParams).forEach(([key, value]) => { + if (value !== undefined) { + url.searchParams.append(key, String(value)); + } + }); + } + + const response = await fetch(url.toString(), { headers: { 'Content-Type': 'application/json', }, diff --git a/apps/web/shared/ui/button.tsx b/apps/web/shared/ui/button.tsx deleted file mode 100644 index 60d410e..0000000 --- a/apps/web/shared/ui/button.tsx +++ /dev/null @@ -1,27 +0,0 @@ -'use client'; - -import { MouseEventHandler } from 'react'; - -interface ButtonProps { - divClassName?: string; - buttonClassName?: string; - onClick?: MouseEventHandler; - children: React.ReactNode; -} - -const Button = ({ - divClassName = '', - buttonClassName = '', - onClick, - children, -}: ButtonProps) => { - return ( -
- -
- ); -}; - -export { Button };