본문 바로가기
STUDY/Project

게시판 페이지 작성 - PostListPage, PostDetailPage, CreatePostPage

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

 

이미 backend 구현을 마치고 회원가입, 로그인까지 구현을 마쳤으니 기본적인 게시판을 만들어보려한다.

 

| 게시판 목록 페이지 작성

 

- 백엔드의 /api/posts에 요청해 게시글 목록을 가져온다.

- 제목, 작성자, 작성일을 보여준다.

- 제목을 클릭하면 해당 게시글 상세 페이지로 이동한다.

 

 

frontend/pages/PostListPage.js

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';

const PostListPage = () => {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchPosts = async () => {
      try {
        const res = await axios.get('http://localhost:5000/api/posts');
        setPosts(res.data);
      } catch (err) {
        console.error('게시글 목록 로드 실패:', err);
      } finally {
        setLoading(false);
      }
    };

    fetchPosts();
  }, []);

  if (loading) return <p>로딩 중...</p>;

  return (
    <div>
      <h2>📚 게시판</h2>
      {posts.length === 0 ? (
        <p>게시글이 없습니다.</p>
      ) : (
        <ul>
          {posts.map((post) => (
            <li key={post._id}>
              <Link to={`/posts/${post._id}`}>
                <strong>{post.title}</strong>
              </Link>{' '}
              <span>- {post.author.username}</span>{' '}
              <span>({new Date(post.createdAt).toLocaleDateString()})</span>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default PostListPage;

 

 

| 게시글 상세페이지 작성

 

- 게시글 ID를 기반으로 /api/posts/:id에서 데이터를 요청한다.

- 제목, 내용, 작성자, 작성일을 보여준다.

 

 

frontend/src/postes/PostDetailPage.js

import React, { useEffect, useState } from 'react';
import { useParams, Link } from 'react-router-dom';
import axios from 'axios';

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

  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]);

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

  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 />
      <Link to="/posts">← 목록으로 돌아가기</Link>
    </div>
  );
};

export default PostDetailPage;

 

 

 

| 게시글 작성 페이지

게시글은 로그인한 사용자만 입력할 수 있게 context를 사용한다.

 

 

frontend/src/pages/CreatePostPage.js

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

const CreatePostPage = () => {
  const { user } = useContext(AuthContext);
  const navigate = useNavigate();
  const [formData, setFormData] = useState({ title: '', content: '' });
  const [message, setMessage] = useState('');

  const handleChange = (e) => {
    setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const token = localStorage.getItem('token');
      const res = await axios.post(
        'http://localhost:5000/api/posts',
        formData,
        {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        }
      );

      setMessage('글이 성공적으로 작성되었습니다.');
      console.log('작성된 글: ', res.data)
      navigate('/posts');
    } catch (err) {
      console.error(err);
      setMessage('글 작성 실패 ❌');
    }
  };

  if (!user) {
    return <p>로그인이 필요한 기능입니다. 🔒</p>;
  }

  return (
    <div>
      <h2>게시글 작성 ✍️</h2>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          name="title"
          placeholder="제목"
          onChange={handleChange}
          required
        />
        <br />
        <textarea
          name="content"
          placeholder="내용"
          onChange={handleChange}
          rows="10"
          cols="50"
          required
        />
        <br />
        <button type="submit">등록</button>
      </form>
      <p>{message}</p>
    </div>
  );
};

export default CreatePostPage;

 

 

 

| 라우트 등록

 

frontend/src/App.js

<Route path="/posts" element={<PostListPage />} />
<Route path="/posts/:id" element={<PostDetailPage />} />
<Route path="/posts/create"
         element={
            <ProtectedRoute>
              <CreatePostPage />
            </ProtectedRoute>
          }
 />

 

 

 

http://localhost:3000/posts

아직은 글이 없어서 이와 같은 화면을 볼 수 있다.

 

 

 

 

http://localhost:3000/posts/create

게시글 작성을 확인하면 로그인을 한 사용자만 접근이 가능하게 했기 때문에 로그인이 필요하다.

 

 

등록을 하면 목록으로 이동한다.

 

 

 

목록을 클릭하면 이와 같은 상세 페이지를 볼 수 있다.

 

 

 

 

이제 글을 수정하고 삭제하는 기능이 필요하다.

또한, App.js에서 관리하고 있는 라우터들을 분리해야 할 필요가 있어보인다. 

728x90
반응형