Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
17 changes: 10 additions & 7 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"semi": false,
"printWidth": 120,
"tabWidth": 2,
"singleQuote": false,
"quoteProps": "consistent",
"singleQuote": true,
"trailingComma": "all",
"singleAttributePerLine": false
}
"tabWidth": 2,
"semi": true,
"useTabs": false,
"printWidth": 80,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "auto",
"bracketSameLine": false,
}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
"coverage": "vitest run --coverage"
},
"dependencies": {
"@tanstack/react-query": "^5.74.4",
"react": "^19.1.0",
"react-dom": "^19.1.0"
"react-dom": "^19.1.0",
"zustand": "^5.0.3"
},
"devDependencies": {
"@eslint/js": "^9.25.1",
"@radix-ui/react-dialog": "^1.1.11",
"@radix-ui/react-select": "^2.2.2",
"@tanstack/react-query-devtools": "^5.74.6",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
Expand Down
66 changes: 65 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions src/entities/comment/api/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { AddCommentResponse, Comment, CommentResponse } from '../model/type.ts';

export const fetchCommentsByPostId = async (postId: string) => {
const res = await fetch(`/api/comments/post/${postId}`);
if (!res.ok) throw new Error('댓글 불러오기 실패');
return res.json();
};

export const addComment = async (comment: {
body: string;
postId: string;
userId: number;
}): Promise<AddCommentResponse> => {
const res = await fetch(`/api/comments/add`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(comment),
});
if (!res.ok) throw new Error('댓글 추가 실패');
return res.json();
};

export const updateComment = async (
id: string,
body: string,
): Promise<Comment> => {
const res = await fetch(`/api/comments/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body }),
});
if (!res.ok) throw new Error('댓글 업데이트 실패');
return res.json();
};

export const deleteComment = async (id: string): Promise<Comment> => {
const res = await fetch(`/api/comments/${id}`, { method: 'DELETE' });
if (!res.ok) throw new Error('댓글 삭제 실패');
};

export const likeComment = async (id: string, likes: number) => {
const res = await fetch(`/api/comments/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ likes }),
});
if (!res.ok) throw new Error('댓글 좋아요 실패');
return res.json();
};
99 changes: 99 additions & 0 deletions src/entities/comment/model/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { create } from 'zustand';

interface Comment {
id: string;
body: string;
postId: string | null;
likes: number;
user: {
id: number;
username: string;
fullName: string;
};
}

interface CommentState {
comments: Record<string, Comment[]>;
selectedComment: Comment | null;
newComment: { body: string; postId: string | null; userId: number };
showAddCommentDialog: boolean;
showEditCommentDialog: boolean;

setComments: (postId: string, comments: Comment[]) => void;
addComment: (postId: string, comment: Comment) => void;
updateComment: (postId: string, comment: Comment) => void;
deleteComment: (postId: string, commentId: string) => void;

setSelectedComment: (comment: Comment | null) => void;
setNewComment: (newComment: {
body: string;
postId: string | null;
userId: number;
}) => void;

openAddCommentDialog: (postId: string) => void;
openEditCommentDialog: (comment: Comment) => void;
closeCommentDialog: () => void;
}
export const useCommentStore = create<CommentState>((set) => ({
comments: {},
selectedComment: null,
newComment: { body: '', postId: null, userId: 1 },
showAddCommentDialog: false,
showEditCommentDialog: false,

setComments: (postId, comments) =>
set((state) => ({
comments: { ...state.comments, [postId]: comments },
})),

addComment: (postId, comment) =>
set((state) => ({
comments: {
...state.comments,
[postId]: [...(state.comments[postId] || []), comment],
},
})),

updateComment: (postId, updatedComment) =>
set((state) => ({
comments: {
...state.comments,
[postId]: state.comments[postId].map((comment) =>
comment.id === updatedComment.id ? updatedComment : comment,
),
},
})),

deleteComment: (postId, commentId) =>
set((state) => ({
comments: {
...state.comments,
[postId]: state.comments[postId].filter(
(comment) => comment.id !== commentId,
),
},
})),

setSelectedComment: (comment) => set({ selectedComment: comment }),

setNewComment: (newComment) => set({ newComment }),

openAddCommentDialog: (postId) =>
set((state) => ({
showAddCommentDialog: true,
newComment: { ...state.newComment, postId },
})),

openEditCommentDialog: (comment) =>
set(() => ({
selectedComment: comment,
showEditCommentDialog: true,
})),

closeCommentDialog: () =>
set(() => ({
showAddCommentDialog: false,
showEditCommentDialog: false,
})),
}));
20 changes: 20 additions & 0 deletions src/entities/comment/model/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export interface CommentResponse {
comments: Comment[];
total: number;
skip: number;
limit: number;
}

export type AddCommentResponse = Omit<Comment, 'likes'>;

export interface Comment {
id: string;
body: string;
postId: string | null;
likes: number;
user: {
id: number;
username: string;
fullName: string;
};
}
60 changes: 60 additions & 0 deletions src/entities/post/api/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Post, PostResponse } from '../model/type.ts';

// 게시물 가져오기
export const fetchPosts = async (
limit: number,
skip: number,
): Promise<PostResponse> => {
const response = await fetch(`/api/posts?limit=${limit}&skip=${skip}`);
if (!response.ok) throw new Error('Fail to fetch posts');
return response.json();
};

// 태그별 게시물 가져오기
export const fetchPostsByTag = async (tag: string) => {
const response = await fetch(`/api/posts/tag/${tag}`);
if (!response.ok) throw new Error('Fail to fetchPostsByTag');
return response.json();
};

// 게시물 추가
export const addPost = async (
newPost: Omit<Post, 'id' | 'reactions' | 'views'>,
) => {
const response = await fetch(`/api/posts/add`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newPost),
});
if (!response.ok) throw new Error('Fail to addPost');
return response.json();
};

// 게시물 업데이트
export const updatePost = async (selectedPost: Post) => {
const response = await fetch(`/api/posts/${selectedPost.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(selectedPost),
});
if (!response.ok) throw new Error('Fail to updatePost');
return response.json();
};

// 게시물 삭제
export const deletePost = async (id: number) => {
try {
await fetch(`/api/posts/${id}`, {
method: 'DELETE',
});
} catch (error) {
console.error('게시물 삭제 오류:', error);
}
};

// 태그 가져오기
export const fetchTag = async () => {
const response = await fetch('/api/posts/tags');
if (!response.ok) throw new Error('태그 가져오기 오류');
return response.json();
};
Loading