본문 바로가기
STUDY/Project

게시글 수정 페이지 작성 및 삭제 추가 - EditpostPage, PostDetailPage

by Y.Choi 2025. 5. 7.
728x90
반응형

 

수정/삭제는 게시글을 작성한 사용자만 접근이 가능하게 한다. 그러기 위해서는 이전에 만든 AuthContext를 사용한다.

또한,  axios요청에 headers: { Authorization: `Bearer ${token}` }로 인증된 사용자라는 것을 증명하게 한다.

 

 Authorization 헤더에 대해 자세히 알기>

 

 

| 게시글 수정 페이지 작성

이전에 작성한 게시글을 불러와 수정이 가능하게 하기 위해 새로운 파일을 만들어 작성한다.

 

frontend/src/pages/EditPostPage.js

import React, { useState, useEffect, useContext } from 'react';
import axios from 'axios';
import { useParams, useNavigate } from 'react-router-dom';
import { AuthContext } from '../context/AuthContext';

const EditPostPage = () => {
  const { id } = useParams(); // 게시글 id 파라미터
  const navigate = useNavigate();
  const { user } = useContext(AuthContext);

  const [post, setPost] = useState({ title: '', content: '' });
  const [message, setMessage] = useState('');

  // 1️⃣ 기존 게시글 불러오기
  useEffect(() => {
    const fetchPost = async () => {
      try {
        const res = await axios.get(`http://localhost:5000/api/posts/${id}`);
        if (res.data.author._id !== user._id) {
          setMessage('자신의 게시글만 수정할 수 있습니다.');
        } else {
          setPost({ title: res.data.title, content: res.data.content });
        }
      } catch (err) {
        console.error(err);
        setMessage('게시글 정보를 불러오지 못했습니다.');
      }
    };
    fetchPost();
  }, [id, user]);

  // 2️⃣ 입력값 변경 처리
  const handleChange = (e) => {
    setPost({ ...post, [e.target.name]: e.target.value });
  };

  // 3️⃣ 게시글 수정 요청
  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const token = localStorage.getItem('token');
      await axios.put(
        `http://localhost:5000/api/posts/${id}`,
        post,
        { headers: { Authorization: `Bearer ${token}` } }
      );
      navigate(`/posts/${id}`);
    } catch (err) {
      console.error(err);
      setMessage('수정 실패');
    }
  };

  return (
    <div>
      <h2>게시글 수정 ✏️</h2>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          name="title"
          value={post.title}
          onChange={handleChange}
          placeholder="제목"
          required
        />
        <br />
        <textarea
          name="content"
          value={post.content}
          onChange={handleChange}
          placeholder="내용"
          required
        />
        <br />
        <button type="submit">수정 완료</button>
      </form>
      {message && <p>{message}</p>}
    </div>
  );
};

export default EditPostPage;

 

 

/ useParams() : URL에서 게시글 ID를 추출 ( /posts/edit/:id )

/ useEffect() : 마운트 시 게시글 내용 불러옴

/ axios.put() : 수정된 내용을 서버에 전송

/ user._id !== author._id : 다른 사용자가 해당 게시글을 수정하지 못하도록 제한

/ Authorization 헤더 : 로그인한 사용자임을 인증하기 위해 토큰 전송

 

 

| 수정 라우터 추가

 

frontend/src/routes/postRoutes.js

<Route
  path="/posts/edit/:id"
  element={
    <ProtectedRoute>
      <EditPostPage />
    </ProtectedRoute>
  }
/>

 

 

 

 

 

 

| 삭제 추가

기존의 PostDetailPage.js에 수정/삭제 버튼을 만들고 수정버튼을 누르면 위에서 만든 EditPostPage로 이동하게 하고 삭제를 누르면 해당 게시글이 삭제하게 한다.

 

frontend/src/pages/PostDetailPage.js

import React, { useEffect, useState, useContext } from 'react';
import { useParams, Link, useNavigate } from 'react-router-dom';
import axios from 'axios';
import { AuthContext } from '../context/AuthContext';

const PostDetailPage = () => {
  const { id } = useParams(); // URL 파라미터에서 post ID 추출
  const { user } = useContext(AuthContext);
  const [post, setPost] = useState(null);
  const [loading, setLoading] = useState(true);
  const [ message, setMessage ] = useState('');
  const navigate = useNavigate();

  // 게시글 불러오기
  useEffect(() => {
    const fetchPost = async () => {
      try {
        const res = await axios.get(`http://localhost:5000/api/posts/${id}`);
        setPost(res.data);
      } catch (err) {
        console.error('게시글 조회 실패:', err);
      } finally {
        setLoading(false);
      }
    };
    fetchPost();
  }, [id]);

   // 2️⃣ 삭제 요청
   const handleDelete = async () => {
    if (!window.confirm('정말 삭제하시겠습니까?')) return;

    try {
      const token = localStorage.getItem('token');
      await axios.delete(`http://localhost:5000/api/posts/${id}`, {
        headers: { Authorization: `Bearer ${token}` },
      });
      navigate('/posts');
    } catch (err) {
      console.error(err);
      setMessage('삭제 실패');
    }
  };

  if (loading) return <p>로딩 중...</p>;
  if (!post) return <p>게시글을 찾을 수 없습니다.</p>;

  const isAuthor = user && post.author._id === user._id;

  return (
    <div>
      <h2>{post.title}</h2>
      <p><strong>작성자:</strong> {post.author.username}</p>
      <p><strong>작성일:</strong> {new Date(post.createdAt).toLocaleDateString()}</p>
      <hr />
      <p>{post.content}</p>
      <br />

      {isAuthor && (
        <div>
          <Link to={`/posts/edit/${post._id}`}>
            <button>수정</button>
          </Link>
          <button onClick={handleDelete}>삭제</button>
        </div>
      )}

      {message && <p>{message}</p>}

      <br />
      <Link to="/posts"><button>목록</button></Link>
    </div>
  );
};

export default PostDetailPage;

 

글의 내용은 누구나 볼 수 있지만 수정/삭제 버튼은 글작성자만 확인 할 수 있다.

 

 

 

아직 레이아웃 구조가 완벽하지 않고 보기 좋게 꾸미기 전이라서 목록 버튼의 위치는 일단 넘어가겠다.

삭제를 누르면 확인 창이 뜨고 확인을 누르면 해당 글이 완전히 삭제된다.

 

 

 

 

 

728x90
반응형