피드관리 - Part 3 피드 수정 및 삭제
ㆍProject Diary/React + MariaDB (PicShare WebApp)
사용자가 작성한 피드를 수정하고 삭제할 수 있는 기능을 구현합니다. 이 글에서는 피드 수정 및 삭제를 위한 프론트엔드와 백엔드 로직을 상세히 설명합니다.
프론트엔드
1. 피드 수정 컴포넌트( FeedUpdateSection )
1-1 초기 상태 및 데이터 가져오기
import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import axios from "axios";
import { useSelector } from "react-redux";
const serverUrl = import.meta.env.VITE_API_URL;
const FeedUpdateSection = () => {
const user = useSelector((state) => state.members.user);
const navigate = useNavigate();
const { postId } = useParams();
const [content, setContent] = useState("");
const [hashtags, setHashtags] = useState([]);
const [photo, setPhoto] = useState([]);
useEffect(() => {
const fetchPostData = async () => {
try {
const response = await axios.get(`${serverUrl}/feed/postbypostid`, {
params: { postId },
});
const post = response.data;
setContent(post.content);
setHashtags(post.hashtags);
setPhoto(post.images);
} catch (err) {
console.error(err);
}
};
fetchPostData();
}, [postId]);
- useEffect : 컴포넌트가 마운트될 때 postId에 해당하는 피드 데이터를 서버에서 가져와 상태에 저장합니다.
- axios.get : 서버에 GET 요청을 보내 피드 데이터를 가져옵니다.
1-2 수정된 내용을 서버에 저장 요청
const handleSave = async () => {
try {
await axios.put(`${serverUrl}/feed/update/${postId}`, {
content,
});
navigate("/feed");
} catch (err) {
console.error(err);
}
};
- handleSave: 사용자가 입력한 내용을 서버에 PUT 요청으로 전송하여 피드를 수정합니다.
- axios.put: 서버에 PUT 요청을 보내 수정된 피드 데이터를 저장합니다.
1-3 뒤로 가기 버튼
const handleBack = () => {
handleSave();
navigate(-1);
};
- 뒤로 가기 버튼을 눌렀을 때 수정된 내용을 저장하고 이전 페이지로 돌아갑니다.
2. 피드 상세보기 및 수정/삭제 버튼( PersonalFeedDetailSection )
본이이 작성판 피드만 수정/삭제할 수 있어야 하기때문에 갱니 피드 상세보기에 수정/삭제 기능을 구현합니다.
2-1 초기 상태 설정
상세보기 페이지로 이동할 때 필요한 상태를 설정합니다.
import React, { useState } from "react";
import { useParams, useLocation, useNavigate } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { Link } from "react-router-dom";
import axios from "axios";
import { fetchAllFeed } from "@/store/feed";
const serverUrl = import.meta.env.VITE_API_URL;
const PersonalFeedDetailSection = () => {
const { postId } = useParams();
const location = useLocation();
const dispatch = useDispatch();
const navigate = useNavigate();
const { filteredFeeds } = location.state || { filteredFeeds: [] };
const [showEditDelete, setShowEditDelete] = useState({});
const user = useSelector((state) => state.members.user);
const postIndex = filteredFeeds.findIndex(
(feed) => feed.postId === parseInt(postId)
);
const postsToDisplay = filteredFeeds.slice(postIndex);
- useParams, useLocation, useNavigate: React Router의 훅을 사용하여 라우팅과 관련된 정보를 가져옵니다.
- useSelector : Redux 상태에서 사용자 정보를 가져옵니다.
2-2 수정/삭제 버튼 토글 핸들러
const toggleEditDelete = (postId) => {
setShowEditDelete((prev) => ({
...prev,
[postId]: !prev[postId],
}));
};
- toggleEditDelete : 해당 포스트 ID에 대한 수정/삭제 버튼의 표시 상태를 토글합니다.
2-3 피드 삭제 핸들러
const handleDelete = (postId) => {
const confirmDelete = window.confirm("정말 삭제하시겠습니까?");
if (confirmDelete) {
axios
.delete(`${serverUrl}/feed/delete`, { params: { postId } })
.then((res) => {
if (res.data === "포스트 및 관련 데이터 삭제 완료") {
dispatch(fetchAllFeed());
navigate(-1);
} else {
alert("삭제하지 못했습니다.");
}
})
.catch((err) => console.log(err));
}
};
- handleDelete : 삭제 확인 후 서버에 DELETE 요청을 보내 포스트를 삭제합니다.
- 삭제가 성공하면 모든 피드를 다시 가져와 갱신하고, 이전 페이지로 돌아갑니다.
3. Redux Slice: 피드 상태 관리
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const serverUrl = import.meta.env.VITE_API_URL;
const feedSlice = createSlice({
name: "feed",
initialState: {
feed: [],
feeds: [],
loading: false,
error: null,
},
reducers: {
initFeedStart(state) {
state.loading = true;
state.error = null;
},
initFeedSuccess(state, action) {
state.feeds = action.payload;
state.loading = false;
},
initFeedFail(state, action) {
state.error = action.payload;
state.loading = false;
},
initPostByPostid(state, action) {
const post = action.payload;
const index = state.feeds.findIndex(
(feed) => feed.postId === post.postId
);
if (index === -1) {
state.feeds.push(post);
} else {
state.feeds[index] = post;
}
state.loading = false;
},
initUpdateFeed(state, action) {
const updatedPost = action.payload;
const index = state.feeds.findIndex(
(post) => post.postId === updatedPost.postId
);
if (index !== -1) {
state.feeds[index] = updatedPost;
}
},
},
});
export const {
initFeedStart,
initFeedSuccess,
initFeedFail,
initUpdateFeed,
initPostByPostid,
} = feedSlice.actions;
export default feedSlice.reducer;
- initFeedStart, initFeedSuccess, initFeedFai : 피드 데이터를 가져오는 상태를 관리합니다.
- initPostByPostid : 특정 포스트 데이터를 상태에 추가하거나 업데이트합니다.
- initUpdateFeed : 수정된 포스트 데이터를 상태에 업데이트합니다.
> 피드 데이터를 가져오고 업데이트하는 Thunk 함수
// 모든 피드를 가져오는 Thunk 함수
export const fetchAllFeed = (filter) => async (dispatch) => {
dispatch(initFeedStart());
try {
let url = `${serverUrl}/feed/all`;
if (filter && filter.type === "following") {
url += `?userNos=${filter.userNos.join(",")}`;
}
const response = await axios.get(url);
dispatch(initFeedSuccess(response.data));
} catch (error) {
dispatch(initFeedFail(error.toString()));
}
};
// 특정 포스트를 가져오는 Thunk 함수
export const fetchPostByPostid = (postId) => async (dispatch) => {
dispatch(initFeedStart());
try {
const response = await axios.get(`${serverUrl}/feed/postbypostid?postId=${postId}`);
dispatch(initPostByPostid(response.data));
} catch (error) {
dispatch(initFeedFail(error.toString()));
}
};
// 피드를 업데이트하는 Thunk 함수
export const updateFeed = (postId, updatedData) => async (dispatch) => {
try {
const response = await axios.put(`${serverUrl}/feed/update/${postId}`, updatedData);
dispatch(initUpdateFeed(response.data));
} catch (error) {
console.error("Error updating post:", error);
}
};
- fetchAllFeed : 필터 조건에 따라 모든 피드를 가져옵니다.
- fetchPostByPostid : 특정 포스트를 가져옵니다.
- updateFeed : 포스트를 업데이트합니다.
백엔드
1. 피드 수정 API엔드포인트
import express from "express";
import { db } from "../db.js";
const feedRouter = express.Router();
feedRouter.put("/update/:postId", (req, res) => {
const { postId } = req.params;
const { content } = req.body;
db.query(
`UPDATE posts SET content = ?, updated_at = ? WHERE postId = ?`,
[content, new Date(), postId],
(err, result) => {
if (err) {
console.error("Error updating post:", err);
return res.status(500).send("실패");
}
res.send({ message: "피드가 성공적으로 수정되었습니다." });
}
);
});
- PUT /update/:postId : 요청된 포스트 ID에 대한 데이터를 수정합니다.
2. 피드 삭제 API엔드포인트
feedRouter.delete("/delete", (req, res) => {
const { postId } = req.query;
// 피드 삭제
db.query("DELETE FROM posts WHERE postId=?", [postId], (err, postResult) => {
if (err) {
console.error("포스트 삭제 중 에러:", err);
res.status(500).send("포스트 삭제 실패");
return;
}
// 이미지 삭제
db.query(
"DELETE FROM images WHERE postId=?",
[postId],
(err, imageResult) => {
if (err) {
console.error("이미지 삭제 중 에러:", err);
res.status(500).send("이미지 삭제 실패");
return;
}
// 해시태그 관계 삭제
db.query(
"DELETE FROM post_hashtags WHERE postId=?",
[postId],
(err, hashtagResult) => {
if (err) {
console.error("해시태그 관계 삭제 중 에러:", err);
res.status(500).send("해시태그 관계 삭제 실패");
return;
}
// 좋아요 정보 삭제
db.query(
"DELETE FROM postlike WHERE postId=?",
[postId],
(err, likeResult) => {
if (err) {
console.error("좋아요 정보 삭제 중 에러:", err);
res.status(500).send("좋아요 정보 삭제 실패");
return;
}
console.log("포스트 및 관련 데이터 삭제 완료");
res.send("포스트 및 관련 데이터 삭제 완료");
}
);
}
);
}
);
});
});
export default feedRouter;