From 236041c6069708dd53835c2bc00c712f2394674e Mon Sep 17 00:00:00 2001 From: rmkim7 Date: Fri, 7 Mar 2025 23:40:38 +0900 Subject: [PATCH 1/6] =?UTF-8?q?chore:=20tailwind=20CSS=20config=EC=97=90?= =?UTF-8?q?=20content=20source=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ui/tailwind.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui/tailwind.config.ts b/packages/ui/tailwind.config.ts index 8096a01..f392344 100644 --- a/packages/ui/tailwind.config.ts +++ b/packages/ui/tailwind.config.ts @@ -7,6 +7,7 @@ const config = { 'app/**/*.{ts,tsx}', 'components/**/*.{ts,tsx}', '../../packages/ui/src/components/**/*.{ts,tsx}', + 'features/**/*.{ts,tsx}', ], theme: { extend: { From 488b7840b4eee8fd14dc15cb112ec3a320017a0d Mon Sep 17 00:00:00 2001 From: rmkim7 Date: Sun, 23 Mar 2025 03:00:42 +0900 Subject: [PATCH 2/6] =?UTF-8?q?refactor:=20=EC=9E=AC=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=84=B1=EC=9D=84=20=EA=B3=A0=EB=A0=A4=ED=95=9C=20http-client?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20API=EC=97=90=EC=84=9C=EC=9D=98=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post-list/api/post-infinite-scroll.ts | 23 +++++-------------- apps/web/shared/api/http-client.ts | 19 +++++++++++++-- 2 files changed, 23 insertions(+), 19 deletions(-) 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..eb960ec 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 { httpClient } from '@/shared/api/http-client'; import { GetPostsCursor } from '@/shared/types/post-types'; -const commonHeaders = new Headers(); -commonHeaders.append('Content-Type', 'application/json'); - 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/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', }, From c1455f2e1e7e08696561fb275b92ba1d48f308f1 Mon Sep 17 00:00:00 2001 From: rmkim7 Date: Sun, 23 Mar 2025 03:03:53 +0900 Subject: [PATCH 3/6] =?UTF-8?q?refactor:=20=ED=8C=8C=EC=9D=BC=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=ED=8F=B4=EB=8D=94=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/entities/post/{model/api.ts => api/post-api.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apps/web/entities/post/{model/api.ts => api/post-api.ts} (100%) diff --git a/apps/web/entities/post/model/api.ts b/apps/web/entities/post/api/post-api.ts similarity index 100% rename from apps/web/entities/post/model/api.ts rename to apps/web/entities/post/api/post-api.ts From f5fa9dabdebf9a8384e19aa7e4a17cd02f1a0c06 Mon Sep 17 00:00:00 2001 From: rmkim7 Date: Sun, 23 Mar 2025 03:06:18 +0900 Subject: [PATCH 4/6] =?UTF-8?q?refactor:=20ViewModel=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=EB=B0=8F=20post-infinite-scroll=EC=97=90=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entities/post/model/post-view-model.ts | 7 ++++ ...te-scroll.tsx => post-infinite-scroll.tsx} | 40 ++++++++++--------- 2 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 apps/web/entities/post/model/post-view-model.ts rename apps/web/features/post/post-list/ui/{infinite-scroll.tsx => post-infinite-scroll.tsx} (68%) diff --git a/apps/web/entities/post/model/post-view-model.ts b/apps/web/entities/post/model/post-view-model.ts new file mode 100644 index 0000000..06e3c07 --- /dev/null +++ b/apps/web/entities/post/model/post-view-model.ts @@ -0,0 +1,7 @@ +import { formatToLocaleDate } from '@/shared/lib/format-date'; +import { Post } from '@/shared/types/post-types'; + +export const mapPostToViewModel = (post: Post) => ({ + ...post, + localeCreatedAt: formatToLocaleDate(post.createdAt), +}); 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 68% 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..333672d 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 { mapPostToViewModel } from '@/entities/post/model/post-view-model'; 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'; -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 ? '*************더 많은 게시글 로딩 중****************' From ddcd2cf3557dd4757fb1ed07bac5641e1fb6a55f Mon Sep 17 00:00:00 2001 From: rmkim7 Date: Sun, 23 Mar 2025 03:07:48 +0900 Subject: [PATCH 5/6] =?UTF-8?q?refactor:=20UI=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20=EB=A7=88?= =?UTF-8?q?=ED=81=AC=EC=97=85=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/page.tsx | 11 ++++------- apps/web/features/post/post-list/ui/post-item.tsx | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 473bade..fd6aef0 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 { PostInfiniteScroll } from '@/features/post/post-list/ui/post-infinite-scroll'; export default async function Home() { const { data, nextCursor, hasMore } = await getInfiniteScrollData('', 10); return ( <> - - {title}


{content}

-

+
{author} From e1050480bf701797fb0bb6534fcab79f7cbf5eb0 Mon Sep 17 00:00:00 2001 From: rmkim7 Date: Sun, 23 Mar 2025 22:22:52 +0900 Subject: [PATCH 6/6] =?UTF-8?q?refactor:=20Public=20API=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/page.tsx | 4 +-- apps/web/entities/post/index.ts | 8 +++++- .../entities/post/model/post-view-model.ts | 4 +-- .../post-list/api/post-infinite-scroll.ts | 4 +-- apps/web/features/post/post-list/index.ts | 3 +++ .../post-list/ui/post-infinite-scroll.tsx | 8 +++--- apps/web/shared/ui/button.tsx | 27 ------------------- 7 files changed, 20 insertions(+), 38 deletions(-) create mode 100644 apps/web/features/post/post-list/index.ts delete mode 100644 apps/web/shared/ui/button.tsx diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index fd6aef0..4102775 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,8 +1,8 @@ import { Button } from '@workspace/ui/components/button'; import Link from 'next/link'; -import { getInfiniteScrollData } from '@/features/post/post-list/api/post-infinite-scroll'; -import { PostInfiniteScroll } from '@/features/post/post-list/ui/post-infinite-scroll'; +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); diff --git a/apps/web/entities/post/index.ts b/apps/web/entities/post/index.ts index e2d601d..1127c67 100644 --- a/apps/web/entities/post/index.ts +++ b/apps/web/entities/post/index.ts @@ -1,4 +1,10 @@ export { createPost, getPostById, getPosts } from './api/post-api'; export { ERROR_MESSAGES } from './lib/error-messages'; export { validateFormField } from './lib/form-validation'; -export type { CreatePostDTO, Post, UpdatePostDTO } from './model/post-types'; +export type { + CreatePostDTO, + GetPostsCursor, + Post, + UpdatePostDTO, +} from './model/post-types'; +export { mapPostToViewModel } from './model/post-view-model'; diff --git a/apps/web/entities/post/model/post-view-model.ts b/apps/web/entities/post/model/post-view-model.ts index 06e3c07..6501625 100644 --- a/apps/web/entities/post/model/post-view-model.ts +++ b/apps/web/entities/post/model/post-view-model.ts @@ -1,5 +1,5 @@ -import { formatToLocaleDate } from '@/shared/lib/format-date'; -import { Post } from '@/shared/types/post-types'; +import { Post } from '@/entities/post'; +import { formatToLocaleDate } from '@/shared/lib'; export const mapPostToViewModel = (post: Post) => ({ ...post, 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 eb960ec..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,5 +1,5 @@ -import { httpClient } from '@/shared/api/http-client'; -import { GetPostsCursor } from '@/shared/types/post-types'; +import { GetPostsCursor } from '@/entities/post'; +import { httpClient } from '@/shared/api'; export async function getInfiniteScrollData( cursor?: string, 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/post-infinite-scroll.tsx b/apps/web/features/post/post-list/ui/post-infinite-scroll.tsx index 333672d..ab9acb4 100644 --- a/apps/web/features/post/post-list/ui/post-infinite-scroll.tsx +++ b/apps/web/features/post/post-list/ui/post-infinite-scroll.tsx @@ -2,10 +2,10 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { mapPostToViewModel } from '@/entities/post/model/post-view-model'; -import { getInfiniteScrollData } from '@/features/post/post-list/api/post-infinite-scroll'; -import { PostItem } from '@/features/post/post-list/ui/post-item'; -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 PostInfiniteScrollProps = { postList: Post[]; 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 };