diff --git a/DEVELOPMENT_LOG.md b/DEVELOPMENT_LOG.md new file mode 100644 index 000000000..53fcacb15 --- /dev/null +++ b/DEVELOPMENT_LOG.md @@ -0,0 +1,631 @@ +# 개발 로그 + +## 화면 기능 동작 범위 +### origin src +헤더에 있는 [홈, 대시보드, 설정] 버튼 기능 없음 +게시물 추가 가능: 게시물 목록 최상단에 노출 +검색하면 목록에서 검색값이 있는 게시물만 노출하는 것이 아닌 현재 노출된 게시물의 제목에 검색값이 하이라이트 됨: 로링되는 화면없이 변화(하이라이트)되기 전 화면에서 멈춰있다가 검색값을 찾으면 하이라이트 됨 +태그 선택 옵션 가능: 선택한 태그가 있는 게시물만 노출 +정렬 기준 옵션 안됨: ID, 제목, 반응 옵션을 눌러도 반응 없음 +오름차순/내림차순 옵션 안됨: 반응 없음 + +게시물 테이블 기능 중... +반응: READ ONLY +작업 내, 댓글/수정/삭제 모두 가능 + +하단에 게시물 노출 개수와 페이지네이션 모두 가능 + +### 1차 수정된 src +| origin src 기능에서 안되는 것과 바뀐 기능만 작성 +게시물 추가 가능 안됨: + 게시물 추가가 된 후 다시 새로고침을 함(아래 두 api 실행) + Request URL + http://localhost:5174/api/posts?limit=10&skip=0 + Request Method + GET + Status Code + 304 Not Modified + Remote Address + [::1]:5174 + Referrer Policy + strict-origin-when-cross-origin + + Request URL + http://localhost:5174/api/users?limit=0&select=username,image + Request Method + GET + Status Code + 304 Not Modified + Remote Address + [::1]:5174 + Referrer Policy + strict-origin-when-cross-origin +검색 할 때, 게시물 목록에 노출되는 것은 동일. 하지만 검색값이 바뀔 때마다 로딩화면이 나옴. 이전에 입력했었던 검색값을 입력하면 그때는 로딩화면 없이 바로 반응함 + +게시물 테이블 기능 중... +작업 내, 댓글/수정/삭제 모두 안됨: 게시물 추가 기능의 이슈와 동일. 그래서 수정한 후 화면은 안바뀌었지만 다시 수정버튼을 누르면 수정된 내용으로 나오는 것을 확인할 수 있음 + +### 2차 수정된 src +| 1차 수정된 src 기능에서 안되는 것 수정 및 추가 기능 +게시물 추가 이슈 처리 + +게시물 테이블 기능 중... +작업 내, 댓글/수정/삭제 기능 이슈 처리 + +추가된 기능 +게시물 like/dislike 반응 기능 추가 + +## 2025-12-12: FSD 아키텍처 적용 및 TanStack Query, Jotai 도입 + +### 📝 변경 개요 +게시물 관리 Admin 코드를 FSD(Feature-Sliced Design) 아키텍처로 리팩토링하고, 전역 상태 관리(Jotai)와 서버 상태 관리(TanStack Query)를 도입하여 코드의 구조화와 유지보수성을 개선했습니다. + +### 🎯 수정한 이유 +1. **컴포넌트 복잡도**: 기존 PostsManagerPage가 700줄이 넘는 단일 파일로 되어 있어 유지보수가 어려움 +2. **타입 안정성**: TypeScript를 사용하지만 타입 처리가 부실하여 런타임 오류 가능성 존재 +3. **상태 관리 혼란**: 상태 관리 개념 없이 너무 많은 useState를 사용하여 상태 추적이 어려움 +4. **useEffect 남용**: 비동기 처리 로직이 복잡하게 구성되어 있고 useEffect 관리가 안 됨 +5. **관심사 미분리**: 비즈니스 로직, UI, API 호출이 한 곳에 섞여 있어 재사용성과 테스트 용이성 저하 + +### 🔧 수정 내용 + +#### 1. 프로젝트 구조 변경 (FSD 아키텍처 적용) + +**변경 전**: +``` +src/ +├── components/ +│ ├── Header.tsx +│ ├── Footer.tsx +│ └── index.tsx (모든 UI 컴포넌트) +└── pages/ + └── PostsManagerPage.tsx (700+ 줄) +``` + +**변경 후**: +``` +src/ +├── app/ +│ └── providers/ +│ └── QueryProvider.tsx +├── pages/ +│ └── posts-manager/ +│ └── ui/ +│ └── PostsManagerPage.tsx +├── widgets/ +│ ├── post-table/ui/PostTable.tsx +│ ├── post-detail/ui/PostDetail.tsx +│ └── user-modal/ui/UserModal.tsx +├── features/ +│ ├── post-add/ui/PostAddDialog.tsx +│ ├── post-edit/ui/PostEditDialog.tsx +│ ├── post-search/ +│ │ ├── model/store.ts +│ │ └── ui/PostSearchBar.tsx +│ ├── comment-add/ui/CommentAddDialog.tsx +│ └── comment-edit/ui/CommentEditDialog.tsx +├── entities/ +│ ├── post/ +│ │ ├── api/index.ts +│ │ ├── model/usePost.ts +│ │ └── ui/PostCard.tsx +│ ├── comment/ +│ │ ├── api/index.ts +│ │ ├── model/useComment.ts +│ │ └── ui/CommentItem.tsx +│ ├── user/ +│ │ ├── api/index.ts +│ │ └── model/useUser.ts +│ └── tag/ +│ ├── api/index.ts +│ └── model/useTag.ts +└── shared/ + ├── api/client.ts + ├── lib/highlight.tsx + ├── types/index.ts + └── ui/ + ├── index.tsx (공통 UI 컴포넌트) + ├── Header.tsx + └── Footer.tsx +``` + +#### 2. 라이브러리 추가 + +**설치한 패키지**: +```bash +pnpm add jotai @tanstack/react-query +pnpm add -D @types/node +``` + +- **jotai**: 전역 상태 관리 (검색어, 필터, 정렬, 페이지네이션) +- **@tanstack/react-query**: 서버 상태 관리 (API 호출, 캐싱, 동기화) +- **@types/node**: path 모듈 타입 정의 + +#### 3. shared 레이어 구현 + +**src/shared/types/index.ts** (신규): +```typescript +// 엔티티별 타입 정의 +export interface User { ... } +export interface Post { ... } +export interface Comment { ... } +export interface Tag { ... } + +// DTO 타입 정의 +export interface CreatePostDto { ... } +export interface UpdatePostDto { ... } +``` + +**src/shared/api/client.ts** (신규): +```typescript +export const apiClient = { + async get(url: string): Promise { ... }, + async post(url: string, data: unknown): Promise { ... }, + async put(url: string, data: unknown): Promise { ... }, + async patch(url: string, data: unknown): Promise { ... }, + async delete(url: string): Promise { ... }, +} +``` + +**src/shared/lib/highlight.tsx** (신규): +- 검색어 하이라이트 기능을 공통 유틸로 분리 + +**src/shared/ui/index.tsx**: +- 기존 components/index.tsx를 TypeScript 타입과 함께 개선 +- Button, Input, Card, Table, Dialog, Select 등 공통 UI 컴포넌트 + +#### 4. entities 레이어 구현 + +각 엔티티(Post, Comment, User, Tag)별로 api, model, ui 분리: + +**Post Entity 예시**: +```typescript +// entities/post/api/index.ts +export const postApi = { + async getPosts(params): Promise { ... }, + async searchPosts(query: string): Promise { ... }, + async getPostsByTag(tag: string): Promise { ... }, + async createPost(data: CreatePostDto): Promise { ... }, + async updatePost(id: number, data: UpdatePostDto): Promise { ... }, + async deletePost(id: number): Promise { ... }, +} + +// entities/post/model/usePost.ts +export const useQueryPosts = (params) => useQuery({ ... }) +export const useQueryPostsSearch = (query) => useQuery({ ... }) +export const useQueryPostsByTag = (tag) => useQuery({ ... }) +export const useMutationPostAdd = () => useMutation({ ... }) +export const useMutationPostUpdate = () => useMutation({ ... }) +export const useMutationPostDelete = () => useMutation({ ... }) +``` + +#### 5. features 레이어 구현 + +사용자 행동(이벤트 처리) 중심으로 분리: + +**features/post-search/model/store.ts** (신규): +```typescript +import { atom } from "jotai" + +export const searchQueryAtom = atom("") +export const selectedTagAtom = atom("") +export const sortByAtom = atom("") +export const sortOrderAtom = atom<"asc" | "desc">("asc") +export const paginationAtom = atom({ skip: 0, limit: 10 }) +``` + +- PostAddDialog: 게시물 추가 기능 +- PostEditDialog: 게시물 수정 기능 +- PostSearchBar: 검색 기능 +- CommentAddDialog: 댓글 추가 기능 +- CommentEditDialog: 댓글 수정 기능 + +#### 6. widgets 레이어 구현 + +재사용 가능한 복잡한 UI 블록: + +- **PostTable**: 게시물 목록 테이블 (게시물 카드, 편집/삭제 버튼 포함) +- **PostDetail**: 게시물 상세 및 댓글 목록 +- **UserModal**: 사용자 정보 모달 + +#### 7. pages 레이어 구현 + +**src/pages/posts-manager/ui/PostsManagerPage.tsx**: + +**변경 전**: 700+ 줄의 단일 파일 +**변경 후**: 230줄로 축소, 비즈니스 로직은 각 레이어로 분리 + +```typescript +const PostsManagerPage = () => { + // Jotai atoms 사용 + const [searchQuery, setSearchQuery] = useAtom(searchQueryAtom) + const [selectedTag, setSelectedTag] = useAtom(selectedTagAtom) + + // TanStack Query hooks 사용 + const { data: tagsData } = useQueryTags() + const { data: postsData, isLoading } = useQueryPosts({ ...pagination }) + + // 위젯 조합 + return ( + + + + + + + ) +} +``` + +#### 8. app 레이어 구현 + +**src/app/providers/QueryProvider.tsx** (신규): +```typescript +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 1, + refetchOnWindowFocus: false, + staleTime: 60 * 1000, // 1분 + }, + }, +}) + +export const QueryProvider = ({ children }) => ( + + {children} + +) +``` + +**src/App.tsx** 수정: +```typescript +const App = () => ( + + +
+ +