본문 바로가기
개발/게시판 만들기

[React] 리액트 게시판 만들기 #5 - 게시글 등록, 수정, 삭제

by onethejay 2023. 4. 24.
728x90

안녕하세요. 원더제이입니다.
지난 포스팅까지 게시판 목록을 조회해서 화면에 출력하는 부분까지 개발했습니다.

이번 포스팅에서는 게시글을 등록하고 수정, 삭제하는 부분을 개발하도록 하겠습니다.

게시글 등록

우선 게시글 목록에서 글쓰기 화면으로 이동할 수 있게 버튼을 추가합니다.

/* BoardList.js */
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Link, useNavigate } from 'react-router-dom';

const BoardList = () => {
  const navigate = useNavigate();
  const [boardList, setBoardList] = useState([]);

  const getBoardList = async () => {
    const resp = await (await axios.get('//localhost:8080/board')).data; // 2) 게시글 목록 데이터에 할당
    setBoardList(resp.data); // 3) boardList 변수에 할당

    const pngn = resp.pagination;
    console.log(pngn);
  };

  const moveToWrite = () => {
    navigate('/write');
  };

  useEffect(() => {
    getBoardList(); // 1) 게시글 목록 조회 함수 호출
  }, []);

  return (
    <div>
      <ul>
        {boardList.map((board) => (
          // 4) map 함수로 데이터 출력
          <li key={board.idx}>
            <Link to={`/board/${board.idx}`}>{board.title}</Link>
          </li>
        ))}
      </ul>
      <div>
        <button onClick={moveToWrite}>글쓰기</button>
      </div>
    </div>
  );
};

export default BoardList;

다음으로 App.js에서 글쓰기 화면을 나타낼 Route를 추가합니다.

/* App.js */
import { Route, Routes } from 'react-router-dom';
import BoardList from './routes/BoardList';
import Home from './routes/Home';
import React from 'react';
import BoardDetail from './routes/BoardDetail';
import BoardWrite from './routes/BoardWrite';

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/board" element={<BoardList />} />
      <Route path="/board/:idx" element={<BoardDetail />} />
      <Route path="/write" element={<BoardWrite />} />
    </Routes>
  );
}

export default App;

이어서 routes에 BoardWrite.js 파일을 생성합니다.

/* BoardWrite.js */
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';

const BoardWrite = () => {
  const navigate = useNavigate();

  const [board, setBoard] = useState({
    title: '',
    createdBy: '',
    contents: '',
  });

  const { title, createdBy, contents } = board; //비구조화 할당

  const onChange = (event) => {
    const { value, name } = event.target; //event.target에서 name과 value만 가져오기
    setBoard({
      ...board,
      [name]: value,
    });
  };

  const saveBoard = async () => {
    await axios.post(`//localhost:8080/board`, board).then((res) => {
      alert('등록되었습니다.');
      navigate('/board');
    });
  };

  const backToList = () => {
    navigate('/board');
  };

  return (
    <div>
      <div>
        <span>제목</span>
        <input type="text" name="title" value={title} onChange={onChange} />
      </div>
      <br />
      <div>
        <span>작성자</span>
        <input
          type="text"
          name="createdBy"
          value={createdBy}
          onChange={onChange}
        />
      </div>
      <br />
      <div>
        <span>내용</span>
        <textarea
          name="contents"
          cols="30"
          rows="10"
          value={contents}
          onChange={onChange}
        ></textarea>
      </div>
      <br />
      <div>
        <button onClick={saveBoard}>저장</button>
        <button onClick={backToList}>취소</button>
      </div>
    </div>
  );
};

export default BoardWrite;

제목, 작성자, 내용에 데이터를 입력하고 저장을 누르면 리스트로 이동합니다.
등록한 글이 리스트에 잘 추가되었는지 확인합니다.

게시글 수정

게시글 수정으로 이동하기 위해 게시글 상세에 버튼을 추가하겠습니다.

게시글 상세에서는 Board 컴포넌트를 사용해 화면에 출력하고 있습니다.

Board.js 컴포넌트의 내용을 수정해 수정 버튼과 삭제 버튼, 목록으로 돌아갈 목록 버튼을 추가합니다.

각 버튼별 함수도 생성해줍니다.

/* Board.js */
import React from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';

const Board = ({ idx, title, contents, createdBy }) => {
  const navigate = useNavigate();

  const moveToUpdate = () => {
    navigate('/update/' + idx);
  };

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

  const moveToList = () => {
    navigate('/board');
  };

  return (
    <div>
      <div>
        <h2>{title}</h2>
        <h5>{createdBy}</h5>
        <hr />
        <p>{contents}</p>
      </div>
      <div>
        <button onClick={moveToUpdate}>수정</button>
        <button onClick={deleteBoard}>삭제</button>
        <button onClick={moveToList}>목록</button>
      </div>
    </div>
  );
};

export default Board;

삭제 버튼과 목록 버튼은 Board.js 안에서 완료되지만, 수정 버튼은 수정 화면 컴포넌트를 작업해주어야 합니다.

App.js에 BoardUpdate를 추가합니다.

import { Route, Routes } from 'react-router-dom';
import BoardList from './routes/BoardList';
import Home from './routes/Home';
import React from 'react';
import BoardDetail from './routes/BoardDetail';
import BoardWrite from './routes/BoardWrite';
import BoardUpdate from "./routes/BoardUpdate";

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/board" element={<BoardList />} />
      <Route path="/board/:idx" element={<BoardDetail />} />
      <Route path="/write" element={<BoardWrite />} />
      <Route path="/update/:idx" element={<BoardUpdate />} />
    </Routes>
  );
}

export default App;

BoardWrite와 마찬가지로 BoardUpdate.js를 routes 디렉토리 안에 생성합니다.
BoardUpdate는 BoardDetail과 BoardWrite가 섞여있는 형태가 됩니다.

화면 이동 시 useParams를 통해 비구조화 할당으로 idx 변수를 꺼내 데이터를 조회한 다음,
각 input의 value를 지정해 세팅해줍니다.

수정이 완료되면 수정 API를 호출해 변경된 내용을 저장하고 다시 상세보기 화면으로 넘어가는 방식으로 작업하겠습니다.

/* BoardUpdate.js */
import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import axios from 'axios';

const BoardUpdate = () => {
  const navigate = useNavigate();
  const { idx } = useParams(); // /update/:idx와 동일한 변수명으로 데이터를 꺼낼 수 있습니다.
  const [board, setBoard] = useState({
    idx: 0,
    title: '',
    createdBy: '',
    contents: '',
  });

  const { title, createdBy, contents } = board; //비구조화 할당

  const onChange = (event) => {
    const { value, name } = event.target; //event.target에서 name과 value만 가져오기
    setBoard({
      ...board,
      [name]: value,
    });
  };

  const getBoard = async () => {
    const resp = await (await axios.get(`//localhost:8080/board/${idx}`)).data;
    setBoard(resp.data);
  };

  const updateBoard = async () => {
    await axios.patch(`//localhost:8080/board`, board).then((res) => {
      alert('수정되었습니다.');
      navigate('/board/' + idx);
    });
  };

  const backToDetail = () => {
    navigate('/board/' + idx);
  };

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

  return (
    <div>
      <div>
        <span>제목</span>
        <input type="text" name="title" value={title} onChange={onChange} />
      </div>
      <br />
      <div>
        <span>작성자</span>
        <input type="text" name="createdBy" value={createdBy} readOnly={true} />
      </div>
      <br />
      <div>
        <span>내용</span>
        <textarea
          name="contents"
          cols="30"
          rows="10"
          value={contents}
          onChange={onChange}
        ></textarea>
      </div>
      <br />
      <div>
        <button onClick={updateBoard}>수정</button>
        <button onClick={backToDetail}>취소</button>
      </div>
    </div>
  );
};

export default BoardUpdate;

게시글 상세에서 수정 버튼 클릭시 아래처럼 데이터를 조회하여 input에 세팅하게 됩니다.
작성자는 readonly 상태로, 수정은 불가하게 되어있습니다.

내용을 변경하고 수정 버튼을 클릭합니다.

게시글이 수정된 것을 확인할 수 있습니다.

게시글 삭제

게시글 삭제는 이미 상세보기에 추가되어있습니다.
삭제 버튼을 클릭하면 window.confirm을 통해 삭제 여부를 물어보고
삭제를 원할 경우 삭제 API를 호출하여 삭제 후 목록 화면으로 돌아가게 됩니다.

게시글이 삭제되어 목록에 나타나지 않게 됩니다.

이번까지 해서 간단하게 게시글 CRUD 화면과 API 연동을 구현해봤습니다.
다음 포스팅에서는 페이징과 검색 기능을 만들어보도록 하겠습니다.
감사합니다.

해당 챕터의 소스는 https://github.com/onethejay/react-board/tree/chap5 에 업로드 되어있으며 챕터 마무리시 master에 마지막 챕터의 소스를 merge 하고 있습니다.
참고가 필요하신분은 해당 깃허브를 참고해주시기 바랍니다 :)

728x90

댓글