본문 바로가기
React

React 게시판 만들기 : 게시글 작성, 조회 (6)

by Fastlane 2020. 10. 5.
728x90
반응형

앞에서 만든 UploadFiles 컴포넌트와, Editor 컴포넌트를 사용하여, 작성 컴포넌트를 만들어보자. 

NoticeWriteComponent.js

import React, { useState, useRef, useEffect } from 'react';
import ajax from '../../utils/ajax';
import Editor from '../EditorComponent';
import UploadFiles from '../UploadFiles';
import LFSelect from '../common/LFSelect';
import { Link } from "react-router-dom";


const NoticeWriteComponent = () => {

    const tabs = [
        { value: '공지사항', text: '공지사항' },
        { value: '업데이트', text: '업데이트' }
     ]

    const [id, setId] = useState(0);
    const [title, setTitle] = useState('');
    const [desc, setDesc] = useState('');
    const [type, setType] = useState('공지사항');
    const uploadReferenece = React.createRef();

    async function onClickSearch() {

        if (title.trim() == '') {
            alert('제목을 입력해주세요'); return;
        }

        if (desc.trim() == '') {
            alert('내용을 입력해주세요'); return;
        }

        await uploadReferenece.current.upload().then(function (result) {

            const files = result;


            ajax('/api/notice/saveNotice', { title: title, desc: desc, type: type, files: files }, (res) => {
                if (res.data && res.data.ok === 1) {
                    alert('저장 완료');
                    setId(res.data.insertedId);
                    var linkToClick = document.getElementById('notice_Detail_Link');
                    linkToClick.click();
                } else {
                    alert('공지사항을 저장하는 도중 오류가 발생하였습니다.')
                }
            }, null, true); 

        }).catch(function (err) {

        });

    }

    function onEditorChange(value) {
        setDesc(value)
    }

    return (
        <div className="container" style={{ fontFamily: 'Noto Sans Korean,Malgun Gothic,sans-serif' }}>
            <div className="lf-menu-nav"><span>설정</span><span>공지사항</span></div>
            <div className="lf-contents pd12">
                <div className="top-controls">
                    <a href="/"><button className="lf-button primary float-right">목록으로</button></a>
                </div>

                <div style={{ padding: "12px" }}>
                  
                    <div className="form-group">
                    <LFSelect options={tabs} onChange={(event) => setType(event.target.value)}/>
                    </div>
                    <div className="form-group">
                    <input type="text" placeholder="제목" className="form-control" onChange={(event) => setTitle(event.target.value)} />
                    </div>
                    <UploadFiles ref={uploadReferenece} />
                    <Editor value={desc} onChange={onEditorChange} />
                    <div className="text-center pd12">
                        <button className="lf-button primary" onClick={onClickSearch}>저장</button>
                    </div>
                    <Link id="notice_Detail_Link" to={{ pathname: '/noticeDetail', state: { _id: id } }}></Link>
                </div>
            </div>
        </div>
    )
};

export default NoticeWriteComponent;

 

 

저장 후, 자동으로 넘어가는 상세조회 컴포넌트도 작성해보자 

NoticeDetailComponent.js

문자열을 HTML태그로 렌더링하기 위해서는 'dangerouslySetInnerHTML'을 사용하면 된다. 

본인이 작성한 글인 경우에, 상세화면 하단에 '수정'과 '삭제' 버튼이 보이도록 처리한다. 

 

import React, { useState, useEffect } from 'react';
import { Link } from "react-router-dom";
import ajax from '../../utils/ajax'
import moment from 'moment';
import Editor from '../EditorComponent';

import 'react-quill/dist/quill.snow.css';

const NoticeDetailComponent = (props) => {

   const { location: { state: { _id } } } = props;
   const [post, setPost] = useState([]);
   const [fileList, setFileList] = useState([]);
   const [isWriter, setIsWriter] = useState(false);


   useEffect(() => {
      const fetchPost = async () => {
         let res = await ajax('/api/notice/noticeDetail', { _id });

         if (res.data && res.data.length == 0) {
            alert('조회된 결과가 없습니다');
         } else {
            setPost(res.data.result);
            setFileList(res.data.result.fileList);
            setIsWriter(res.data.result.isWriter)
            await ajax('/api/notice/saveNoticeView', { _id });
         }
      }

      fetchPost();

   }, []);


   function onClickDeleteNotice() {
      if (confirm('삭제 하시겠습니까?')) {
         ajax('/api/notice/deleteNotice', { _id: _id }, (res) => {
            if (res.data && res.data.ok === 1) {
               alert('삭제 완료');
               location.href = '/';
            }
         })
      }
   }

   return (
      <div className="container" style={{ overflow: 'auto' }}>
         <div className="lf-menu-nav"><span>공지사항</span></div>
         <div className="lf-contents pd12">
            {/* align-right */}
            <div className="top-controls">
               <a href="/"><button className="lf-button primary float-right">목록으로</button></a>
            </div>
            <div style={{ padding: "12px" }}>
               <table className="notice-table">
                  <colgroup>
                     <col width="10%" />
                     <col width="40%" />
                     <col width="10%" />
                     <col width="40%" />
                  </colgroup>
                  <thead>
                     <tr>
                        <th>구분</th>
                        <td colSpan="3">{post.type}</td>
                     </tr>
                     <tr>
                        <th>제목</th>
                        <td colSpan="3">{post.title}</td>
                     </tr>
                     <tr>
                        <th>작성자</th>
                        <td>{post.userName}</td>
                        <th>작성일시</th>
                        <td>{moment(post.date).format('YYYY-MM-DD')}</td>
                     </tr>
                     <tr>
                        <th>첨부파일</th>
                        <td colSpan="3">
                           {fileList.map((name, index) => (
                              <span key={index}> <a href={'/uploads/' + name} target="_blank"
                              >{name}
                              </a> |</span>
                           ))}
                        </td>
                     </tr>
                  </thead>
                  <tbody>
                     <tr>
                        <td className="notice-contents" colSpan="4" dangerouslySetInnerHTML={{
                           __html: post.desc
                        }}></td>
                     </tr>
                  </tbody>
               </table>
            </div>
            {
               isWriter &&
               <div className="text-center mb8">
                  <button className="lf-button dark-gray" onClick={onClickDeleteNotice}>삭제</button>
                  <Link to={{ pathname: '/noticeModify', state: { _id: post._id } }}><button className="lf-button primary ml8">수정</button></Link>
               </div>
            }
         </div>
      </div>
   )
};

export default NoticeDetailComponent;

 

728x90
반응형

댓글