Skip to content
Merged
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
65 changes: 30 additions & 35 deletions src/pages/post/PostContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -93,7 +96,7 @@
};

fetchPostDetail();
}, [postId]);

Check warning on line 99 in src/pages/post/PostContent.tsx

View workflow job for this annotation

GitHub Actions / build-and-lint

React Hook useEffect has a missing dependency: 'fetchComments'. Either include it or remove the dependency array

const handleLikeToggle = async () => {
if (!postId || !postData || isLikeLoading) return;
Expand Down Expand Up @@ -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('μ„œλ²„μ™€ 톡신할 수 μ—†μŠ΅λ‹ˆλ‹€.');
}
Comment on lines 198 to 213
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

axios μΈν„°μ…‰ν„°μ—μ„œ 토큰 κ°±μ‹  μ‹€νŒ¨ μ‹œ TokenRefreshErrorλ₯Ό λ°œμƒμ‹œν‚€λ„λ‘ κ΅¬ν˜„λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. ν˜„μž¬ catch λΈ”λ‘μ—μ„œλŠ” 이 νŠΉμ • 였λ₯˜λ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•Šκ³  일반적인 μ„œλ²„ 톡신 였λ₯˜λ‘œ μ²˜λ¦¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 토큰 κ°±μ‹  μ‹€νŒ¨λŠ” μ‚¬μš©μžκ°€ λ‹€μ‹œ λ‘œκ·ΈμΈν•΄μ•Ό ν•˜λŠ” μ€‘μš”ν•œ μƒν™©μ΄λ―€λ‘œ, TokenRefreshErrorλ₯Ό λͺ…μ‹œμ μœΌλ‘œ ν™•μΈν•˜μ—¬ μ‚¬μš©μžμ—κ²Œ 재둜그인이 ν•„μš”ν•¨μ„ μ•Œλ¦¬κ³  둜그인 νŽ˜μ΄μ§€λ‘œ λ¦¬λ””λ ‰μ…˜ν•˜λŠ” λ“±μ˜ 처리λ₯Ό μΆ”κ°€ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

TokenRefreshErrorλ₯Ό μ‚¬μš©ν•˜λ €λ©΄ 파일 μƒλ‹¨μ—μ„œ importν•΄μ•Ό ν•©λ‹ˆλ‹€:

import { instance, TokenRefreshError } from '@/assets/shared/lib/axios';
      if (error instanceof TokenRefreshError) {
        toast.error('μ„Έμ…˜μ΄ λ§Œλ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ λ‘œκ·ΈμΈν•΄μ£Όμ„Έμš”.');
        // navigate('/login'); // ν•„μš”μ‹œ 둜그인 νŽ˜μ΄μ§€λ‘œ λ¦¬λ””λ ‰μ…˜
        return;
      }

      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('κ²Œμ‹œκΈ€ μ‚­μ œμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.');
        }
      } else {
        toast.error('μ„œλ²„μ™€ 톡신할 수 μ—†μŠ΅λ‹ˆλ‹€.');
      }
References
  1. API μΈν„°μ…‰ν„°λŠ” 토큰 κ°±μ‹  μ‹€νŒ¨ μ‹œ μ—λŸ¬λ₯Ό λ˜μ Έμ•Ό ν•˜λ©°, UI 계측은 이 μ—λŸ¬λ₯Ό μ²˜λ¦¬ν•˜μ—¬ 둜그인 νŽ˜μ΄μ§€λ‘œ λ¦¬λ””λ ‰μ…˜ν•˜λŠ” λ“±μ˜ μž‘μ—…μ„ μˆ˜ν–‰ν•΄μ•Ό ν•©λ‹ˆλ‹€.

} finally {
setIsDeleteModalOpen(false);
}
};

Expand Down Expand Up @@ -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 (
<>
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

κ΄€λ¦¬μž μ—¬λΆ€λ₯Ό ν™•μΈν•˜λŠ” 쑰건뢀 λ Œλ”λ§μ΄ μ œκ±°λ˜μ–΄ λͺ¨λ“  μ‚¬μš©μžμ—κ²Œ μ‚­μ œ λ²„νŠΌμ΄ ν‘œμ‹œλ©λ‹ˆλ‹€. μ΄λŠ” κ΄€λ¦¬μžκ°€ μ•„λ‹Œ μ‚¬μš©μžμ—κ²Œ ν˜Όλž€μ„ 쀄 수 있으며, μ‚­μ œ μ‹œλ„λ₯Ό ν•œ 후에야 κΆŒν•œ μ—†μŒ 였λ₯˜λ₯Ό 보게 λ˜μ–΄ μ‚¬μš©μž κ²½ν—˜μ„ μ €ν•΄ν•©λ‹ˆλ‹€.

μ΄μ „μ²˜λŸΌ κ΄€λ¦¬μžμ—κ²Œλ§Œ μ‚­μ œ λ²„νŠΌμ΄ 보이도둝 쑰건뢀 λ Œλ”λ§μ„ λ‹€μ‹œ μΆ”κ°€ν•˜λŠ” 것을 κ³ λ €ν•΄ λ³΄μ„Έμš”. μ‚¬μš©μž κΆŒν•œμ„ ν™•μΈν•˜λŠ” 둜직(예: μ‚¬μš©μž 정보λ₯Ό λ‹΄κ³  μžˆλŠ” contextλ‚˜ store μ‚¬μš©)을 μ‚¬μš©ν•˜μ—¬ λ²„νŠΌμ˜ λ…ΈμΆœ μ—¬λΆ€λ₯Ό κ²°μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.

</div>
</div>

Expand Down Expand Up @@ -413,6 +401,13 @@
onReport={() => setIsModalOpen(false)}
/>
)}

{isDeleteModalOpen && (
<DeleteModal
onClose={() => setIsDeleteModalOpen(false)}
onDelete={handleAdminDelete}
/>
)}
</>
);
}
Loading