본문 바로가기
웹 프로그래밍

[프론트] 리액트로 게시판 만들기

by CSEGR 2024. 3. 28.
728x90

<게시판 기능>

  • pagination 기능 -> 한 페이지당 글의 개수 설정할 수 있는 기능 
  • 수정, 삭제 
//App.js

import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Board from "./components/Board";
import PostForm from "./components/PostForm";
import BoardDetail from "./components/BoardDetail";
import PostEditForm from "./components/PostEditForm";
function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Board />} />
        <Route path="/board/:id" element={<BoardDetail />} />
        <Route path="/postform" element={<PostForm />} />
        <Route path="/edit/:id" element={<PostEditForm />} />
      </Routes>
    </Router>
  );
}

export default App;

총 네 개의 페이지로 구성되어있습니다. 

 

  1. Board.js
  2. BoardDetail.js
  3. PostForm.js
  4. PostEditForm.js

 

Board.js 

-> 게시판 메인 화면

- 글의 목록을 보여줌 ➔ 글의 제목을 누르면  해당 글을 볼 수 있는 페이지로 랜더링

- 한 페이지당 노출할 글의 수를 설정할 수 있고 페이지를 바꿀 수 있는 기능이 있음. 

- 글을 쓸 수 있는 버튼

//Board.js 

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import "../css/Board_style.css";
const Board = () => {
  const navigate = useNavigate();
  const [boardList, setBoardList] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [postsPerPage, setPostsPerPage] = useState(10); // Number of posts per page, default: 5
  const [totalPages, setTotalPages] = useState(1); // Total number of pages

  useEffect(() => {
    getBoardList();
  }, [currentPage, postsPerPage]);

  const getBoardList = async () => {
    try {
      const response = await axios.get("http://localhost:8080/board/me");
      setBoardList(response.data);
      setTotalPages(Math.ceil(response.data.length / postsPerPage));
    } catch (error) {
      console.error("불러오지 못함", error);
    }
  };

  const indexOfLastPost = currentPage * postsPerPage;
  const indexOfFirstPost = indexOfLastPost - postsPerPage;
  const currentPosts = boardList.slice(indexOfFirstPost, indexOfLastPost);

  const paginate = (pageNumber) => setCurrentPage(pageNumber);

  const handlePostsPerPage = (e) => {
    setPostsPerPage(parseInt(e.target.value));
    setCurrentPage(1);
  };

  const Post = () => {
    navigate("/postform");
  };
  return (
    <div className="board-container">
      <h1 className="board-title">게시판</h1>
      <div className="board-button">
        <button onClick={Post}>글쓰기</button>
      </div>
      <br />

      <ul className="board-posts">
        {currentPosts.map((board) => (
          <li key={board.id} className="board-post-item">
            <Link to={`/board/${board.id}`}>{board.title}</Link>
            <span>작성자: {board.writer}</span>
            <span> | 작성 시간: {board.writingTime}</span>
          </li>
        ))}
      </ul>

      <div className="board-posts-per-page">
        <label>
          게시물 수:{" "}
          <select value={postsPerPage} onChange={handlePostsPerPage}>
            <option value={10}>10개</option>
            <option value={20}>20개</option>
            <option value={30}>30개</option>
          </select>
        </label>
      </div>

      <div className="board-pagination">
        {[...Array(totalPages).keys()].map((number) => (
          <button
            key={number + 1}
            className={currentPage === number + 1 ? "selected" : ""}
            onClick={() => paginate(number + 1)}
          >
            {number + 1}
          </button>
        ))}
      </div>
    </div>
  );
};

export default Board;

 

<글쓰기 버튼을 누를 시>

//PostForm.js

import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import "../css/PostForm_style.css";
const PostForm = ({ addPost }) => {
  const navigate = useNavigate();
  const [writer, setWriter] = useState("");
  const [title, setTitle] = useState("");
  const [body, setBody] = useState("");
  const savePost = async (e) => {
    try {
      const response = await axios.post(
        "http://localhost:8080/board",
        {
          writer: writer,
          title: title,
          body: body,
        },
        {
          headers: {
            "Content-Type": "application/json",
          },
        }
      );

      if (response.status === 200) {
        alert("게시물이 등록되었습니다.");
        navigate("/");
      } else {
        throw new Error("게시물 등록 실패");
      }
    } catch (error) {
      console.error("Error:", error);
      alert("게시물 등록에 실패했습니다.");
    }
  };

  const backToBoard = () => {
    navigate("/");
  };

  return (
    <div className="container">
      <div className="input-group">
        <h2 style={{ textAlign: "center" }}>글쓰기</h2>
        <span>제목</span>
        <input
          type="text"
          placeholder="제목"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
      </div>

      <div className="input-group">
        <span>작성자</span>
        <input
          type="text"
          placeholder="작성자"
          value={writer}
          onChange={(e) => setWriter(e.target.value)}
        />
      </div>

      <textarea
        placeholder="내용"
        cols="100"
        rows="10"
        value={body}
        onChange={(e) => setBody(e.target.value)}
      />

      <div className="button-group">
        <button onClick={savePost}>저장</button>
        <button onClick={backToBoard}>취소</button>
      </div>
    </div>
  );
};

export default PostForm;

  • 저장 버튼 누를 시 게시판 메인화면(Board.js)로 다시 렌더링
  • 취소 버튼 누를 시 게시판 메인화면(Board.js)로 다시 렌더링
BoardDetail.js

-> 글의 세부사항을 볼 수 있는 페이지

- 수정버튼-> 새로운 페이지(PostEditForm.js)로 렌더링

- 삭제할 수 있는 기능이 있음

- 게시판 목록으로 다시 돌아 갈 수 있는 기능이 있음 (Board.js 페이지 렌더링)

//BoardDetail.js

import React, { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import axios from "axios";
import "../css/BoardDetail_style.css";
const BoardDetail = () => {
  const { id } = useParams();
  const navigate = useNavigate();
  const [board, setBoard] = useState({});

  const getBoard = async () => {
    try {
      const response = await axios.get(
        `http://localhost:8080/board?writingId=${id}`
      );

      setBoard(response.data);
    } catch (error) {
      console.error("불러오지 못함", error);
    }
  };

  useEffect(() => {
    getBoard();
  }, [id]);

  const moveToEdit = () => {
    navigate("/edit/" + id);
  };

  const deletePost = async () => {
    if (window.confirm("게시글을 삭제하시겠습니까?")) {
      await axios.delete(`http://localhost:8080/board/${id}`);
      alert("삭제되었습니다.");
      navigate("/");
    }
  };

  const moveToBoard = () => {
    navigate("/");
  };

  return (
    <div className="board-detail-container">
      <div className="board-detail-content">
        <h2 className="board-detail-title">{board.title}</h2>
        <div className="board-detail-info">
          <h5>작성자 : {board.writer}</h5>
          <p style={{ fontSize: "12px", color: "gray" }}>{board.writingTime}</p>
        </div>
        <hr />
        <p className="board-detail-body">{board.body}</p>
        <hr />
        <div className="board-detail-button-group">
          <button onClick={moveToEdit}>수정</button>
          <button onClick={deletePost}>삭제</button>
          <button onClick={moveToBoard}>목록</button>
        </div>
      </div>
    </div>
  );
};

export default BoardDetail;

<삭제 버튼 클릭시>

 

PostEditForm.js

-> 글을 수정할 수 있는 페이지 

//PostEditForm.js

import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import axios from "axios";
import "../css/PostForm_style.css";
const PostEditForm = () => {
  const navigate = useNavigate();
  const { id } = useParams();
  const [post, setPost] = useState({});

  const { writer, title, body } = post;

  const onChange = (event) => {
    const { value, name } = event.target;
    setPost((prevPost) => ({
      ...prevPost,
      [name]: value,
    }));
  };

  const getPost = async () => {
    try {
      const response = await axios.get(
        `http://localhost:8080/board?writingId=${id}`
      );
      setPost(response.data);
    } catch (error) {
      console.error("불러오지 못함", error);
    }
  };
  const backToPost = () => {
    navigate("/board/" + id);
  };
  const updatePost = async () => {
    try {
      const response = await axios.put(
        `http://localhost:8080/board/${id}`,
        post
      );
      console.log(post);
      if (response.status === 200) {
        alert("수정되었습니다.");
        navigate("/board/" + id);
      } else {
        throw new Error("게시물 수정 실패");
      }
    } catch (error) {
      console.error("Error updating board:", error);
      alert("게시물 수정에 실패했습니다.");
    }
  };

  useEffect(() => {
    getPost();
  }, []);
  return (
    <div>
      <h2 class style={{ textAlign: "center" }}>
        글 수정하기
      </h2>
      <div className="container">
        <div className="input-group">
          <span>제목</span>
          <input
            type="text"
            name="title"
            placeholder="제목"
            value={title}
            onChange={onChange}
          />
          <span> 작성자</span>
          <input
            type="text"
            name="writer"
            placeholder="작성자"
            value={writer}
            readOnly={true}
          />
          <br />

          <br />
          <textarea
            placeholder="내용"
            name="body"
            cols="100"
            rows="30"
            value={body}
            onChange={onChange}
          />
          <br />
          <div className="button-group">
            <button onClick={updatePost}>수정완료</button>
            <button onClick={backToPost}>수정취소</button>
          </div>
        </div>
      </div>
    </div>
  );
};

export default PostEditForm;

수정이 가능하게 하려면 <textarea>에 name 필드를 무조건 작성해야 한다...!!

수정완료 및 수정취소 버튼을 누르면 글의 상세정보를 보여주는 페이지(PostDetail.js)로 렌더링 됨.

 

게시판 프로젝트를 하면서 API 참조하는 법을 확실하게 배웠다. 

API 참조 방법을 배우고자 하는 분들에게 아주 간단하고 배우기 좋은 프로젝트 주제라고 생각한다 !

 

 

참고 : https://onethejay.tistory.com/191

 

728x90