
@@ -72,6 +69,4 @@ const PostItem = React.memo(({ post, onLike, onBookmark }) => {
);
});
-PostItem.displayName = 'PostItem';
-
export default PostItem;
diff --git a/frontend/src/components/Board/PostItem.module.css b/frontend/src/components/Board/PostItem.module.css
index 9ea3d85e..7a40075c 100644
--- a/frontend/src/components/Board/PostItem.module.css
+++ b/frontend/src/components/Board/PostItem.module.css
@@ -6,6 +6,7 @@
margin-bottom: 16px;
box-sizing: border-box;
position: relative;
+ cursor: pointer;
}
.mainContent {
diff --git a/frontend/src/pages/Board.jsx b/frontend/src/pages/Board.jsx
index d3c4e3e6..b113c7c6 100644
--- a/frontend/src/pages/Board.jsx
+++ b/frontend/src/pages/Board.jsx
@@ -4,8 +4,11 @@ import Modal from '../components/Board/Modal';
import SearchBar from '../components/Board/SearchBar';
import BoardActions from '../components/Board/BoardActions';
import styles from './Board.module.css';
+import { useParams } from 'react-router-dom';
const Board = () => {
+ const { team } = useParams();
+
const [posts, setPosts] = useState(() => {
const saved = localStorage.getItem('boardPosts');
if (saved) {
@@ -23,6 +26,7 @@ const Board = () => {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [sortOption, setSortOption] = useState('latest');
+ const [selectedFiles, setSelectedFiles] = useState([]);
useEffect(() => {
localStorage.setItem('boardPosts', JSON.stringify(posts));
@@ -38,6 +42,56 @@ const Board = () => {
setContent('');
};
+ // 파일 선택 핸들러
+ const handleFileChange = (e) => {
+ const newFiles = Array.from(e.target.files);
+
+ setSelectedFiles((prevFiles) => {
+ const updatedFiles = [...prevFiles];
+ const replacedFileNames = [];
+
+ newFiles.forEach((newFile) => {
+ const sameNameIndex = updatedFiles.findIndex(
+ (f) => f.name === newFile.name
+ );
+
+ if (sameNameIndex !== -1) {
+ // 같은 이름 발견
+ if (updatedFiles[sameNameIndex].size === newFile.size) {
+ // 같은 크기 = 완전 중복 (무시)
+ return;
+ } else {
+ // 다른 크기 = 교체
+ updatedFiles[sameNameIndex] = newFile;
+ replacedFileNames.push(newFile.name);
+ return;
+ }
+ }
+
+ // 새 파일 추가
+ updatedFiles.push(newFile);
+ });
+
+ // 교체된 파일이 있으면 알림
+ if (replacedFileNames.length > 0) {
+ alert(`교체됨: ${replacedFileNames.join(', ')}`);
+ }
+
+ return updatedFiles;
+ });
+
+ if (e.target.value !== undefined) {
+ e.target.value = '';
+ }
+ };
+
+ // 파일 삭제 핸들러
+ const handleRemoveFile = (indexToRemove) => {
+ setSelectedFiles((prevFiles) =>
+ prevFiles.filter((_, index) => index !== indexToRemove)
+ );
+ };
+
const handleSave = () => {
const newPost = {
title,
@@ -46,7 +100,13 @@ const Board = () => {
id: Date.now(),
likeCount: 0,
isLiked: false,
+ bookmarkCount: 0,
isBookmarked: false,
+ files: selectedFiles.map((file) => ({
+ name: file.name,
+ size: file.size,
+ type: file.type,
+ })),
};
setPosts([newPost, ...posts]);
handleCloseModal();
@@ -70,7 +130,13 @@ const Board = () => {
setPosts(
posts.map((post) =>
post.id === postId
- ? { ...post, isBookmarked: !post.isBookmarked }
+ ? {
+ ...post,
+ isBookmarked: !post.isBookmarked,
+ bookmarkCount: post.isBookmarked
+ ? (post.bookmarkCount || 1) - 1
+ : (post.bookmarkCount || 0) + 1,
+ }
: post
)
);
@@ -119,6 +185,7 @@ const Board = () => {
@@ -134,6 +201,9 @@ const Board = () => {
setTitle={setTitle}
content={content}
setContent={setContent}
+ selectedFiles={selectedFiles}
+ onFileChange={handleFileChange}
+ onRemoveFile={handleRemoveFile}
onSave={handleSave}
onClose={handleCloseModal}
/>
diff --git a/frontend/src/pages/PostDetail.jsx b/frontend/src/pages/PostDetail.jsx
index 0a82b7b3..80e9d5d6 100644
--- a/frontend/src/pages/PostDetail.jsx
+++ b/frontend/src/pages/PostDetail.jsx
@@ -9,15 +9,22 @@ import HeartIcon from '../assets/boardHeart.svg';
import HeartFilledIcon from '../assets/boardHeart.fill.svg';
import EditIcon from '../assets/boardPencil.svg';
import DeleteIcon from '../assets/boardCloseIcon.svg';
+import FolderIcon from '../assets/boardFolder.svg';
import { getTimeAgo } from '../utils/TimeUtils';
const PostDetail = () => {
- const { id } = useParams();
+ const { postId, team } = useParams();
const navigate = useNavigate();
const location = useLocation();
const [post, setPost] = useState(null);
const [loading, setLoading] = useState(true);
+ const [isEdit, setIsEdit] = useState(false);
+ const [editTitle, setEditTitle] = useState('');
+ const [editContent, setEditContent] = useState('');
+ const [editFiles, setEditFiles] = useState([]);
+ const [newFiles, setNewFiles] = useState([]);
+
const [comments, setComments] = useState([]);
const [commentText, setCommentText] = useState('');
const [showMenu, setShowMenu] = useState(false);
@@ -41,7 +48,6 @@ const PostDetail = () => {
} catch (error) {
if (error.name === 'QuotaExceededError') {
console.error('localStorage 용량이 부족합니다.');
- // 사용자에게 알림 표시 또는 오래된 데이터 정리
}
throw error;
}
@@ -70,17 +76,22 @@ const PostDetail = () => {
if (!currentPost) {
const allPosts = getPostsFromStorage();
- currentPost = allPosts.find((p) => p.id === parseInt(id, 10));
+ currentPost = allPosts.find((p) => p.id === parseInt(postId, 10));
}
setPost(currentPost);
setLoading(false);
+ if (currentPost) {
+ setEditTitle(currentPost.title);
+ setEditContent(currentPost.content);
+ }
+
if (currentPost) {
setComments(getCommentsFromStorage(currentPost.id));
} else {
setComments([]);
}
- }, [id, location.state]);
+ }, [postId, location.state]);
// 좋아요 토글
const handleLike = () => {
@@ -107,7 +118,13 @@ const PostDetail = () => {
let updatedPost = null;
const updatedPosts = allPosts.map((p) => {
if (p.id === post.id) {
- updatedPost = { ...p, isBookmarked: !p.isBookmarked };
+ updatedPost = {
+ ...p,
+ isBookmarked: !p.isBookmarked,
+ bookmarkCount: p.isBookmarked
+ ? (p.bookmarkCount || 1) - 1
+ : (p.bookmarkCount || 0) + 1,
+ };
return updatedPost;
}
return p;
@@ -116,35 +133,83 @@ const PostDetail = () => {
setPost(updatedPost);
};
- // 게시글 삭제
- const handleDelete = () => {
- if (window.confirm('게시글을 정말 삭제하시겠습니까?')) {
- const allPosts = getPostsFromStorage();
- const updatedPosts = allPosts.filter((p) => p.id !== post.id);
- savePostsToStorage(updatedPosts);
- navigate('/board');
- }
+ // 수정 모드 진입
+ const handleEdit = () => {
+ setIsEdit(true);
+ setShowMenu(false);
+ setEditFiles(post.files || []);
+ setNewFiles([]);
};
- // 게시글 수정
- const handleUpdate = () => {
- setShowMenu(false);
- const newTitle = prompt('수정할 제목을 입력하세요:', post.title);
- if (newTitle === null || newTitle.trim() === '') return;
- const newContent = prompt('수정할 내용을 입력하세요:', post.content);
- if (newContent === null) return;
+ // 수정 취소
+ const handleCancelEdit = () => {
+ setIsEdit(false);
+ setEditTitle(post.title);
+ setEditContent(post.content);
+ };
+
+ // 기존 파일 삭제
+ const handleRemoveExistingFile = (index) => {
+ setEditFiles(editFiles.filter((_, i) => i !== index));
+ };
+
+ // 새 파일 추가
+ const handleAddNewFile = (e) => {
+ const files = Array.from(e.target.files);
+ setNewFiles([...newFiles, ...files]);
+ e.target.value = '';
+ };
+
+ // 새 파일 삭제
+ const handleRemoveNewFile = (index) => {
+ setNewFiles(newFiles.filter((_, i) => i !== index));
+ };
+
+ // 수정 저장
+ const handleSaveEdit = () => {
+ if (!editTitle.trim() || !editContent.trim()) {
+ alert('제목과 내용을 입력해주세요.');
+ return;
+ }
+
+ // 기존 파일 + 새 파일 정보 합치기
+ const allFiles = [
+ ...editFiles,
+ ...newFiles.map((file) => ({
+ name: file.name,
+ size: file.size,
+ type: file.type,
+ })),
+ ];
const allPosts = getPostsFromStorage();
let updatedPost = null;
const updatedPosts = allPosts.map((p) => {
if (p.id === post.id) {
- updatedPost = { ...p, title: newTitle, content: newContent };
+ updatedPost = {
+ ...p,
+ title: editTitle,
+ content: editContent,
+ files: allFiles,
+ };
return updatedPost;
}
return p;
});
savePostsToStorage(updatedPosts);
setPost(updatedPost);
+ setIsEdit(false);
+ setNewFiles([]);
+ };
+
+ // 게시글 삭제
+ const handleDelete = () => {
+ if (window.confirm('게시글을 정말 삭제하시겠습니까?')) {
+ const allPosts = getPostsFromStorage();
+ const updatedPosts = allPosts.filter((p) => p.id !== post.id);
+ savePostsToStorage(updatedPosts);
+ navigate(`/board/${team || 'all'}`);
+ }
};
// 댓글 추가
@@ -210,36 +275,49 @@ const PostDetail = () => {
-
{post.title}
-
-
- {showMenu && (
-
-
-
-
- )}
-
+ {isEdit ? (
+
setEditTitle(e.target.value)}
+ placeholder="제목을 입력하세요"
+ />
+ ) : (
+
{post.title}
+ )}
+
+ {!isEdit && (
+
+
+ {showMenu && (
+
+
+
+
+ )}
+
+ )}
@@ -250,115 +328,249 @@ const PostDetail = () => {
{getTimeAgo(post.date)}