detail.gif

상세페이지에 Toast-UI 에디터를 사용해서 댓글 기능을 적용했다.

Toast UI Editor


Editor 설치

리액트에서 사용하기 때문에 toast ui editor의 React Wrapper 버전을 설치

npm install @toast-ui/react-editor

1. 댓글 작성하기


// Comment.js
import { Button, Paper } from '@mui/material';

import '@toast-ui/editor/dist/toastui-editor.css';
import { Editor } from '@toast-ui/react-editor';

import SkeletonComment from '../../components/Skeleton/SkeletonComment';

const onSubmit = async (e) => {
  e.preventDefault();

  // 값 가져오기
  const editorInstance = editorRef.current.getInstance();
  // 마크다운 텍스트를 추출해주는 내장 메서드 getMarkdown() 이용
  const getContent = editorInstance.getMarkdown();
  setDisplay(!display);

  setComment([
    ...comment,
    {
      replyContent: getContent,
      replyId: id,
      memberId: memberId,
      nickname: user,
      createdAt: `${date}`,
      modifiedAt: `${date}`,
      responseTo: 'root',
      exist: true,
    },
  ]);

  const addComment = {
    replyContent: getContent,
    replyId: id,
    memberId: memberId,
    nickname: user,
    createdAt: `${date}`,
    modifiedAt: `${date}`,
    responseTo: 'root',
    exist: true,
  };
  await axios.post(`${process.env.REACT_APP_API_URL}reply/` + id, addComment);

  await axios.get(`${process.env.REACT_APP_API_URL}reply/` + id).then((result) => {
    setComment(result.data.data);
  });
};

const onclickHandler = () => {
  if (memberId) {
    setDisplay(!display);
  } else if (!memberId) {
    alert('로그인이 필요합니다.');
    navigate('/login');
  }
};

return (
  <>
    {loading && <SkeletonComment />}
    {!loading && (
      <Paper sx={{ mt: 1, mb: 10, width: 690 }}>
        <Button
          onClick={() => onclickHandler()}
          sx={{
            width: '5.5rem',
            fontSize: 12,
          }}
        >
          댓글 작성
        </Button>
        {display && (
          <>
            {/* toast editor */}
            <Editor ref={editorRef} />
            <div>
              <Button sx={{ color: '#afafaf' }} onClick={onSubmit}>
                저장
              </Button>
            </div>
          </>
        )}
      </Paper>
    )}
  </>
);

로그인 했을 때 댓글 작성 버튼을 누르면 toast editor가 나타나지만,

detail.gif

회원 정보가 없다면, 로그인 페이지로 이동시킨다.


2. 댓글 수정 & 삭제


import '@toast-ui/editor/dist/toastui-editor.css';
import { Editor } from '@toast-ui/react-editor';

// 댓글 수정
const onEdit = async ({ replyId }) => {
  const editorInstance = editorRef.current.getInstance();
  const getContent = editorInstance.getMarkdown();

  setComment([
    {
      replyContent: getContent,
      replyId: id,
      memberId: memberId,
      nickname: user,
      createdAt: `${date}`,
      modifiedAt: `${date}`,
      responseTo: 'root',
      exist: false,
    },
  ]);

  const editComment = {
    replyContent: getContent,
    replyId: id,
    memberId: memberId,
    nickname: user,
    createdAt: `${date}`,
    modifiedAt: `${date}`,
    responseTo: 'root',
    exist: false,
  };
  // ui는 잘 됨,
  await axios.patch(`${process.env.REACT_APP_API_URL}reply/` + replyId, editComment).then(() => {
    setComment(comment);
  });

  await axios.get(`${process.env.REACT_APP_API_URL}reply/` + id).then((result) => {
    setComment(result.data.data);
  });
};

// 댓글 삭제
const onRemove = async ({ replyId }) => {
  await axios.delete(`${process.env.REACT_APP_API_URL}reply/` + replyId);

  await axios.get(`${process.env.REACT_APP_API_URL}reply/` + id).then((result) => {
    setComment(result.data.data);
  });
};

return (
  <>
    {/* comment 수정 */}
    {comment.memberId === memberId && (
      <>
        {openEditor === comment.replyId && <Editor value={comment.replyContent} ref={editorRef} />}
        <Button
          sx={{ color: '#afafaf', fontSize: 12 }}
          onClick={() => {
            if (comment.replyId === openEditor) {
              onEdit(comment);
              setOpenEditor('');
            } else {
              setOpenEditor(comment.replyId);
            }
          }}
        >
          수정
        </Button>

        {/* comment 삭제 */}
        <Button
          sx={{ color: '#afafaf', fontSize: 12 }}
          onClick={() => {
            onRemove(comment);
          }}
        >
          삭제
        </Button>
      </>
    )}
  </>
);


결과 화면


comment10-2.gif

댓글 작성, 수정, 삭제 권한은 오직 자신만 할 수 있도록 설정해놓았다.

추후 대댓글 기능 넣어보면 좋을 것 같다.