-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 게시글 삭제 기능 추가 및 삭제 모달 구현 #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
|
|
||
| import ReactMarkdown from 'react-markdown'; | ||
| import remarkGfm from 'remark-gfm'; | ||
| import DeleteModal from '@/assets/components/modal/DeleteModal'; | ||
|
|
||
| interface PostDetailType { | ||
| id: number; | ||
|
|
@@ -61,6 +62,8 @@ | |
| const [comments, setComments] = useState<CommentType[]>([]); | ||
| const [isCommentLoading, setIsCommentLoading] = useState(false); | ||
|
|
||
| const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); | ||
|
|
||
| const calculateTimeAgo = (createdAt: string) => { | ||
| const now = new Date(); | ||
| const created = new Date(createdAt); | ||
|
|
@@ -93,7 +96,7 @@ | |
| }; | ||
|
|
||
| fetchPostDetail(); | ||
| }, [postId]); | ||
|
|
||
| const handleLikeToggle = async () => { | ||
| if (!postId || !postData || isLikeLoading) return; | ||
|
|
@@ -186,23 +189,30 @@ | |
| const handleAdminDelete = async () => { | ||
| if (!postId) return; | ||
|
|
||
| const confirmDelete = window.confirm('게시글을 삭제하시겠습니까?'); | ||
| if (!confirmDelete) return; | ||
|
|
||
| try { | ||
| await instance.delete(`/api/admin/post/${postId}`); | ||
|
|
||
| toast.success('게시글이 삭제되었습니다.'); | ||
| navigate(-1); | ||
| } catch (error: unknown) { | ||
| if (typeof error === 'object' && error !== null && 'response' in error) { | ||
| const status = (error as { response?: { status?: number } }).response | ||
| ?.status; | ||
|
|
||
| if (status === 401) toast.error('로그인이 필요합니다.'); | ||
| else if (status === 403) toast.error('관리자 권한이 없습니다.'); | ||
| else if (status === 404) toast.error('게시글을 찾을 수 없습니다.'); | ||
| else toast.error('게시글 삭제에 실패했습니다.'); | ||
| if (status === 401) { | ||
| toast.error('로그인이 필요합니다.'); | ||
| } else if (status === 403) { | ||
| toast.error('관리자 권한이 없습니다.'); | ||
| } else if (status === 404) { | ||
| toast.error('게시글을 찾을 수 없습니다.'); | ||
| } else { | ||
| toast.error('게시글 삭제에 실패했습니다.'); | ||
| } | ||
| } else { | ||
| toast.error('서버와 통신할 수 없습니다.'); | ||
| } | ||
| } finally { | ||
| setIsDeleteModalOpen(false); | ||
| } | ||
| }; | ||
|
|
||
|
|
@@ -231,29 +241,6 @@ | |
| } | ||
| }; | ||
|
|
||
| const isAdmin = () => { | ||
| try { | ||
| const token = localStorage.getItem('accessToken'); | ||
| if (!token) return false; | ||
|
|
||
| const payload = JSON.parse(atob(token.split('.')[1])); | ||
|
|
||
| console.log('JWT payload:', payload); | ||
|
|
||
| return ( | ||
| payload.role === 'ADMIN' || | ||
| payload.role === 'ROLE_ADMIN' || | ||
| payload.auth === 'ROLE_ADMIN' || | ||
| payload.authority === 'ADMIN' || | ||
| payload.roles?.includes('ADMIN') || | ||
| payload.roles?.includes('ROLE_ADMIN') | ||
| ); | ||
| } catch (e) { | ||
| console.error('isAdmin error', e); | ||
| return false; | ||
| } | ||
| }; | ||
|
|
||
| if (isLoading || !postData) { | ||
| return ( | ||
| <> | ||
|
|
@@ -363,11 +350,12 @@ | |
| <Report /> | ||
| </button> | ||
|
|
||
| {isAdmin() && ( | ||
| <button onClick={handleAdminDelete} className="cursor-pointer"> | ||
| <Delete /> | ||
| </button> | ||
| )} | ||
| <button | ||
| onClick={() => setIsDeleteModalOpen(true)} | ||
| className="cursor-pointer" | ||
| > | ||
| <Delete /> | ||
| </button> | ||
|
Comment on lines
+353
to
+358
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 삭제 버튼이 이제 모든 사용자에게 표시됩니다. 권한이 없는 사용자가 삭제 버튼을 클릭하고 모달에서 확인을 누르면 '관리자 권한이 없습니다'라는 오류 메시지를 받게 됩니다. 이는 사용자 경험에 좋지 않습니다. 이전의 사용자 정보를 관리하는 전역 상태(Context API, Redux 등)나 커스텀 훅(예: |
||
| </div> | ||
| </div> | ||
|
|
||
|
|
@@ -413,6 +401,13 @@ | |
| onReport={() => setIsModalOpen(false)} | ||
| /> | ||
| )} | ||
|
|
||
| {isDeleteModalOpen && ( | ||
| <DeleteModal | ||
| onClose={() => setIsDeleteModalOpen(false)} | ||
| onDelete={handleAdminDelete} | ||
| /> | ||
| )} | ||
| </> | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Axios 에러를 처리할 때
axios.isAxiosError타입 가드를 사용하는 것이 더 안정적이고 간결합니다. 현재의typeof error === 'object' && error !== null && 'response' in error방식 대신axios.isAxiosError(error)를 사용하면 코드가 더 명확해지고, Axios가 제공하는 타입 안전성을 활용할 수 있습니다. 이를 위해 파일 상단에import axios from 'axios';를 추가해야 합니다.