본문 바로가기
국비학원/수업기록

국비 지원 개발자 과정_Day97

by 루팽 2023. 4. 18.

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - index.js>

import React from "react";
import ReactDOM from "react-dom/client";
import "bootstrap/dist/css/bootstrap.min.css";
import "react-datetime/css/react-datetime.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import ImageUploader from "./service/imageUploader";
import { Provider } from "react-redux";
import { legacy_createStore } from "redux";
import rootReducer from "./redux/rootReducer";
import AuthLogic from "./service/authLogic";
import firebaseApp from "./service/firebase";
import { setAuth } from "./redux/userAuth/action";

// 리덕스 적용하기
const store = legacy_createStore(rootReducer)
// AuthLogic객체 생성하기
const authLogic = new AuthLogic(firebaseApp)
// store에 있는 초기 상태정보 출력하기
store.dispatch(setAuth(authLogic.getUserAuth(), authLogic.getGoogleAuthProvider()))
console.log(store.getState());
// 이미지 업로더 객체 생성
const imageUploader = new ImageUploader();
const root = ReactDOM.createRoot(document.getElementById("root"));
// 리덕스 추가 - store 생성
// createStore 호출
root.render(
  <>
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
  </>
);

 

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - App.jsx>

import { Route, Routes } from "react-router";
import "./App.css";
import HomePage from "./components/page/HomePage";
import MemoPage from "./components/page/MemoPage";
import SchedulePage from "./components/page/SchedulePage";
import MemoDetail from "./components/memo/MemoDetail";

function App() {
  return (
    <>
      <Routes>
      <Route path='/' exact={true} element={<HomePage />}  />
      <Route path='/schedule' exact={true} element={<SchedulePage />}  />
      <Route path='/memo' exact={true} element={<MemoPage />}  />
      <Route path='/memo/detail/:m_no' element={<MemoDetail />}  />
      </Routes>
    </>
  );
}

export default App;

 

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - ScheduleHeader.jsx>

import React from 'react'
import { Container, Nav, Navbar } from 'react-bootstrap'
import { Link } from 'react-router-dom'

const ScheduleHeader = () => {

  return (
    <>
      <Navbar bg="light" variant="light">
        <Container fluid>
          <Link to="/" className='nav-link'>다이어리</Link>
          <Nav className="me-auto">
            <Link to="/" className='nav-link'>Home</Link>
            <Link to="/schedule" className='nav-link'>일정관리</Link>
            <Link to="/memo" className='nav-link'>메모관리</Link>
          </Nav>
        </Container>
      </Navbar>
    </>
  )
}

export default ScheduleHeader

 

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - HomePage.jsx>

import React from 'react'
import ScheduleHeader from '../include/ScheduleHeader'
import ScheduleFooter from '../include/ScheduleFooter'
import { ContainerDiv, FormDiv, HeaderDiv } from '../style/FormStyle'

const HomePage = () => {
  return (
    <>
    <ContainerDiv>
        <ScheduleHeader />
        <HeaderDiv>
          <h1 style={{marginLeft:"10px"}}>다이어리</h1>
        </HeaderDiv>
        <FormDiv>
          <div>이벤트존</div>
          <hr style={{height:"2px"}} />
          <div>추천 수업존</div>
          <hr style={{height:"2px"}} />
          <div>지도 화면</div>
          <div>카카오맵존</div>
          <hr style={{height:"2px"}} />
        </FormDiv>
      <ScheduleFooter />
      </ContainerDiv>
    </>
  )
}

export default HomePage

 

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - SchedulePage.jsx>

import React, { useEffect, useState } from 'react'
import FullCalendar from '@fullcalendar/react'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'
import ScheduleHeader from '../include/ScheduleHeader'
import axios from 'axios'
import Datetime from 'react-datetime'
import moment from 'moment/moment'
import { Button, Form, Modal } from 'react-bootstrap'

const SchedulePage = () => {
  const[memoList, setMemoList] = useState([])
  // 오늘 이전 날짜 비활성화 처리하기
  const yesterday = moment().subtract(1, 'day')
  const valid = (current) => {
    return current.isAfter(yesterday)
  }
  const [show, setShow] = useState(false) // 모달창 초기값
  const handleClose = () => setShow(false) // 모달창 닫기
  const handleShow = () => setShow(true) // 모달창 보여주기

  useEffect(() => {
    axios.get('/schedule.json').then(res => {
      console.log(res.data)
      setMemoList(res.data)
    })
  }, [])

  const handleDateClick = (arg) => {
    console.log(arg.dateStr)
    handleShow()
  }
  const handleChangeForm = () => {
  }
  const memoAdd = () => {
  }
  const handleStart = (date) => {
    console.log('handleStart=> ' + date)
    const start = moment(date._d).format('YYYY-MM-DD, a h:mm')
    console.log(start)
  }
  const handleEnd = (date) => {
    console.log('handleEnd=> ' + date)
    const end = moment(date._d).format('YYYY-MM-DD, a h:mm')
    console.log(end)
  }
  return (
    <>
      <ScheduleHeader />
      <FullCalendar
        plugins={[ dayGridPlugin, interactionPlugin ]}
        initialView="dayGridMonth"
        weekends={true}
        events={memoList}
        height={"100vh"}
        dateClick={handleDateClick}
      />
      {/* ========================== [[  일정등록 Modal ]] ========================== */}
      <Modal show={show} onHide={handleClose} animation={true}>
        <Modal.Header closeButton>
          <Modal.Title>새로운 일정</Modal.Title>
        </Modal.Header>
        <Modal.Body>
        <Form id="f_memo">         
          <Form.Group className="mb-3 row" controlId="mTitle">
            <Form.Label className="col-sm-2 col-form-label">일정명</Form.Label>
            <div className='col-sm-10'>
            <Form.Control className='form-control form-control-sm' type="text" name="m_title" onChange={handleChangeForm} placeholder="Enter 일정명" />
            </div>
          </Form.Group>
          <Form.Group className="mb-3 row" controlId="boardWriter">
            <Form.Label className="col-sm-2 col-form-label">등록자</Form.Label>
            <div className='col-sm-10'>
            <Form.Control type="text" name="m_writer" onChange={handleChangeForm} className='form-control form-control-sm' placeholder="Enter 작성자" />
            </div>
          </Form.Group>
          <Form.Group className="mb-3 row" controlId="edit-start">
            <Form.Label className="col-sm-2 col-form-label">시작</Form.Label>
            <div className='col-sm-10'>
            <Datetime dateFormat='YYYY-MM-DD' isValidDate={valid} name="m_start" onChange={handleStart} />
            </div>
          </Form.Group>
          <Form.Group className="mb-3 row" controlId="edit-end">
            <Form.Label className="col-sm-2 col-form-label">끝</Form.Label>
            <div className='col-sm-10'>
            <Datetime dateFormat='YYYY-MM-DD' isValidDate={valid} name="m_end" onChange={handleEnd}/>
            </div>
          </Form.Group>
          <Form.Group className="mb-3 row" controlId="boardContent">
            <Form.Label className="col-sm-2 col-form-label">내용</Form.Label>
            <div className='col-sm-10'>
            <textarea className="form-control" name='m_content' onChange={handleChangeForm} rows="3"></textarea>
            </div>
          </Form.Group>
        </Form>
        </Modal.Body>
        <Modal.Footer>
          <Button variant="secondary" onClick={handleClose}>
            닫기
          </Button>
          <Button variant="primary" onClick={memoAdd}>
            저장
          </Button>
        </Modal.Footer>
      </Modal>     
    {/* ========================== [[ 글등록 Modal ]] ========================== */}
    </>
  )
}

export default SchedulePage

 

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - MemoPage.jsx>

import React from 'react'
import ScheduleHeader from '../include/ScheduleHeader'
import ScheduleFooter from '../include/ScheduleFooter'
import MemoList from '../memo/MemoList'

const MemoPage = () => {
  return (
    <div>
      <ScheduleHeader />
      <MemoList />
      <ScheduleFooter />
    </div>
  )
}

export default MemoPage

 

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - MemoList.jsx>

import React, { useEffect, useState } from 'react'
import { Button, Form, Modal, Table } from 'react-bootstrap'
import Datetime from 'react-datetime'
import moment from 'moment/moment'
import '../css/style.css'
import { off, onValue, ref, set } from 'firebase/database'
import { database } from '../../service/firebase'
import MemoRow from './MemoRow'

const MemoList = () => {
  // 오늘 이전 날짜 비활성화 처리하기
  const yesterday = moment().subtract(1, 'day')
  const valid = (current) => {
    return current.isAfter(yesterday)
  }
  const [show, setShow] = useState(false) // 모달창 초기값
  const handleClose = () => setShow(false) // 모달창 닫기
  const handleShow = () => setShow(true) // 모달창 보여주기

  // 사용자로부터 입력받은 값 - 상태훅으로 관리하기
  const [m_no, setM_no] = useState(0) // 식별자
  const [m_title, setM_title] = useState('') // 일정명
  const [m_writer, setM_writer] = useState('') // 작성자
  const [m_content, setM_content] = useState('') // 일정내용
  const [m_start, setM_start] = useState('') // 시작날짜
  const [m_end, setM_end] = useState('') // 끝날짜

  const [memo, setMemo] = useState({
    m_no: 0,
    m_title: '',
    m_writer: '',
    m_content: '',
    m_start: '',
    m_end: '',
  })
  const handleStart = (date) => {
    console.log('handleStart=> ' + date)
    const m_start = moment(date._d).format('YYYY-MM-DD, a h:mm')
    console.log(m_start)
    setM_start(m_start)
  }
  const handleEnd = (date) => {
    console.log('handleEnd=> ' + date)
    const m_end = moment(date._d).format('YYYY-MM-DD, a h:mm')
    console.log(m_end)
    setM_end(m_end)
  }
  // 화면에 입력받은 정보 담기
  const handleChangeForm = (event) => {
    if(event.currentTarget == null) {
      return
    }
    // console.log('폼내용 변경 발생 name: ' + event.target.name)
    // console.log('폼내용 변경 발생 value: ' + event.target.value)
    console.log(memo)
    setMemo({
      ...memo,
      m_no: Date.now(), // 십진수 날짜 정보
      [event.target.name]: event.target.value,
    })
    console.log(memo)
  }
  const memoAdd = (event) => {
    // 이벤트 버블링 사전처리
    event.preventDefault()
    console.log(memo)
    const pmemo = {
      m_no: memo.m_no,
      m_title: memo.m_title,
      m_writer: memo.m_writer,
      m_content: memo.m_content,
      m_start: m_start,
      m_end: m_end,
    }
    console.log(pmemo)
    set(ref(database, 'memo/' + memo.m_no), pmemo)
    handleClose()
  }
  // 메모 정보 가져오기
  const [memos, setMemos] = useState({})
  useEffect (() => {
    const startCountRef = ref(database, 'memo')
    onValue(startCountRef, (snapshot) => {
      const data = snapshot.val()
      setMemos(data)
      console.log(memos)
      return () => off(startCountRef)
    })
  }, [])
  const memoSearch = () => {

  }
  const getMemoList = () => {

  }

  return (
    <>
      <div className="container">
      <div className="page-header">
        <h2>
          일정관리 <small>일정목록</small>
        </h2>
        <hr />
      </div>

      <div className="row">
        <div className="col-3">
          <select id="gubun" className="form-select" aria-label="분류선택">
            <option defaultValue>분류선택</option>
            <option value="m_title">일정명</option>
            <option value="m_writer">작성자</option>
            <option value="m_content">내용</option>
          </select>
        </div>
        <div className="col-6">
          <input
            type="text"
            id="keyword"
            className="form-control"
            placeholder="검색어를 입력하세요"
            aria-label="검색어를 입력하세요"
            aria-describedby="btn_search"
          />
        </div>
        <div className="col-3">
          <Button variant="danger" id="btn_search" onClick={memoSearch}>
            검색
          </Button>
        </div>
      </div>

      <div className="book-list">
        <Table striped bordered hover>
          <thead>
            <tr>
              <th>#</th>
              <th>일정명</th>
              <th>작성자</th>
              <th>일정시간</th>
            </tr>
          </thead>
          <tbody>
            {memos && Object.keys(memos).map((key) => (
              <MemoRow key={key} memo={memos[key]} />
            ))}
          </tbody>
        </Table>
        <hr />
        <div className="booklist-footer">
          <Button variant="warning" onClick={getMemoList}>
            전체조회
          </Button>
          &nbsp;
          <Button variant="success" onClick={handleShow}>
            글쓰기
          </Button>
        </div>
      </div>
    </div>
    {/* ========================== [[  일정등록 Modal ]] ========================== */}
    <Modal show={show} onHide={handleClose} animation={true}>
        <Modal.Header closeButton>
          <Modal.Title>새로운 일정</Modal.Title>
        </Modal.Header>
        <Modal.Body>
        <Form id="f_memo">         
          <Form.Group className="mb-3 row" controlId="mTitle">
            <Form.Label className="col-sm-2 col-form-label">일정명</Form.Label>
            <div className='col-sm-10'>
            <Form.Control className='form-control form-control-sm' type="text" name="m_title" onChange={handleChangeForm} placeholder="Enter 일정명" />
            </div>
          </Form.Group>
          <Form.Group className="mb-3 row" controlId="boardWriter">
            <Form.Label className="col-sm-2 col-form-label">등록자</Form.Label>
            <div className='col-sm-10'>
            <Form.Control type="text" name="m_writer" onChange={handleChangeForm} className='form-control form-control-sm' placeholder="Enter 작성자" />
            </div>
          </Form.Group>
          <Form.Group className="mb-3 row" controlId="edit-start">
            <Form.Label className="col-sm-2 col-form-label">시작</Form.Label>
            <div className='col-sm-10'>
            <Datetime dateFormat='YYYY-MM-DD' isValidDate={valid} name="m_start" onChange={handleStart} />
            </div>
          </Form.Group>
          <Form.Group className="mb-3 row" controlId="edit-end">
            <Form.Label className="col-sm-2 col-form-label">끝</Form.Label>
            <div className='col-sm-10'>
            <Datetime dateFormat='YYYY-MM-DD' isValidDate={valid} name="m_end" onChange={handleEnd}/>
            </div>
          </Form.Group>
          <Form.Group className="mb-3 row" controlId="boardContent">
            <Form.Label className="col-sm-2 col-form-label">내용</Form.Label>
            <div className='col-sm-10'>
            <textarea className="form-control" name='m_content' onChange={handleChangeForm} rows="3"></textarea>
            </div>
          </Form.Group>
        </Form>
        </Modal.Body>
        <Modal.Footer>
          <Button variant="secondary" onClick={handleClose}>
            닫기
          </Button>
          <Button variant="primary" onClick={memoAdd}>
            저장
          </Button>
        </Modal.Footer>
      </Modal>     
    {/* ========================== [[ 글등록 Modal ]] ========================== */}    
    </>
  )
}

export default MemoList

 

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - MemoRow.jsx>

import React from 'react'
import { Link } from 'react-router-dom'

const MemoRow = ({memo}) => {
  return (
    <>
    <tr>
      <td>{memo.m_no}</td>
      <td>
        <Link to={'/memo/detail/'+memo.m_no} className='btn btn-primary'>
          {memo.m_title}
        </Link>
      </td>
      <td>{memo.m_writer}</td>
      <td>{`${memo.m_start} ~ ${memo.m_end}`}</td>
    </tr>
      
    </>
  )
}

export default MemoRow

 

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - MemoDetail.jsx>

import React, { useEffect, useState } from 'react'
import { Button, Card, Form, ListGroup, ListGroupItem, Modal } from 'react-bootstrap';
import { useNavigate, useParams } from 'react-router';
import { Link } from 'react-router-dom';
import Datetime from 'react-datetime'
import moment from 'moment/moment';
import { off, onValue, ref, remove, set } from 'firebase/database';
import { database } from '../../service/firebase';

const MemoDetail = () => {
  const navigate = useNavigate()
  // 사용자가 선택한 로우 m_no
  const {m_no} = useParams()
  console.log("m_no => " + m_no);
  const [memo, setMemo] = useState({})

  // 오늘 이전 날짜 비활성화 처리하기
  const yesterday = moment().subtract(1, 'day')
  const valid = (current) => {
    return current.isAfter(yesterday)
  }
  const [show, setShow] = useState(false) // 모달창 초기값
  const handleClose = () => setShow(false) // 모달창 닫기
  const handleShow = () => setShow(true) // 모달창 보여주기

  // 사용자로부터 입력받은 값 - 상태훅으로 관리하기
  const [m_title, setM_title] = useState('') // 일정명
  const [m_writer, setM_writer] = useState('') // 작성자
  const [m_content, setM_content] = useState('') // 일정내용
  const [m_start, setM_start] = useState('') // 시작날짜
  const [m_end, setM_end] = useState('') // 끝날짜

  useEffect(() => {
    const startCountRef = ref(database, 'memo/'+m_no)
    onValue(startCountRef, (snapshot) => {
      const data = snapshot.val()
      setMemo(data)
      console.log(memo)
      return () => off(startCountRef)
    })
  }, [])

  const memoUpdate = (event) => {
    event.preventDefault()
    const pmemo = {
      m_no: m_no,
      m_title: memo.m_title,
      m_writer: memo.m_writer,
      m_content: memo.m_content,
      m_start: m_start  ?  m_start : memo.m_start,
      m_end: m_end ? m_end : memo.m_end,
    }
    console.log(pmemo)
    set(ref(database, 'memo/'+m_no), pmemo)
    handleClose()
    navigate('/memo')
  }
  const memoDelete = (event) => {
    event.preventDefault()
    remove(ref(database, 'memo/'+m_no))
    navigate('/memo')
  }
  const handleStart = (date) => {
    console.log('handleStart=> ' + date)
    const m_start = moment(date._d).format('YYYY-MM-DD, a h:mm')
    console.log(m_start)
    setM_start(m_start)
  }
  const handleEnd = (date) => {
    console.log('handleEnd=> ' + date)
    const m_end = moment(date._d).format('YYYY-MM-DD, a h:mm')
    console.log(m_end)
    setM_end(m_end)
  }
  // 화면에 입력받은 정보 담기
  const handleChangeForm = (event) => {
    if(event.currentTarget == null) {
      return
    }
    // console.log('폼내용 변경 발생 name: ' + event.target.name)
    // console.log('폼내용 변경 발생 value: ' + event.target.value)
    console.log(memo)
    setMemo({
      ...memo,
      m_no: Date.now(), // 십진수 날짜 정보
      [event.target.name]: event.target.value,
    })
    console.log(memo)
  }

  return (
    <>
      <div className="container">
        <div className="page-header">
          <h2>
            일정관리 <small>일정보기</small>
          </h2>
          <hr />
        </div>
        <Card style={{ width: "58rem" }}>
          <Card.Header>{memo.m_title}</Card.Header>
          <ListGroup className="list-group-flush">
            <ListGroupItem>{memo.m_writer}</ListGroupItem>
            <ListGroupItem>{`${memo.m_start} ~ ${memo.m_end}`}</ListGroupItem>
            <ListGroupItem>{memo.m_content}</ListGroupItem>
          </ListGroup>
          <div className="detail-link">
            <Button variant="primary" onClick={handleShow}>
              수정
            </Button>
            &nbsp;
            <Button variant="primary" onClick={memoDelete}>
              삭제
            </Button>
            <Link to="/memo" className="nav-link">
              일정목록
            </Link>
          </div>
        </Card>
        <hr />
        {/* ========================== [[  일정등록 Modal ]] ========================== */}
        <Modal show={show} onHide={handleClose} animation={true}>
          <Modal.Header closeButton>
            <Modal.Title>일정 수정</Modal.Title>
          </Modal.Header>
          <Modal.Body>
            <Form id="f_memo">
              <Form.Group className="mb-3 row" controlId="mTitle">
                <Form.Label className="col-sm-2 col-form-label">
                  일정명
                </Form.Label>
                <div className="col-sm-10">
                  <Form.Control
                    className="form-control form-control-sm"
                    type="text"
                    name="m_title"
                    value={memo.m_title}
                    onChange={handleChangeForm}
                    placeholder="Enter 일정명"
                  />
                </div>
              </Form.Group>
              <Form.Group className="mb-3 row" controlId="boardWriter">
                <Form.Label className="col-sm-2 col-form-label">
                  등록자
                </Form.Label>
                <div className="col-sm-10">
                  <Form.Control
                    type="text"
                    name="m_writer"
                    value={memo.m_writer}
                    onChange={handleChangeForm}
                    className="form-control form-control-sm"
                    placeholder="Enter 작성자"
                  />
                </div>
              </Form.Group>
              <Form.Group className="mb-3 row" controlId="edit-start">
                <Form.Label className="col-sm-2 col-form-label">
                  시작
                </Form.Label>
                <div className="col-sm-10">
                  <Datetime
                    dateFormat="YYYY-MM-DD"
                    isValidDate={valid}
                    name="m_start"
                    value={memo.m_start}
                    onChange={handleStart}
                  />
                </div>
              </Form.Group>
              <Form.Group className="mb-3 row" controlId="edit-end">
                <Form.Label className="col-sm-2 col-form-label">끝</Form.Label>
                <div className="col-sm-10">
                  <Datetime
                    dateFormat="YYYY-MM-DD"
                    isValidDate={valid}
                    name="m_end"
                    value={memo.m_end}
                    onChange={handleEnd}
                  />
                </div>
              </Form.Group>
              <Form.Group className="mb-3 row" controlId="boardContent">
                <Form.Label className="col-sm-2 col-form-label">
                  내용
                </Form.Label>
                <div className="col-sm-10">
                  <textarea
                    className="form-control"
                    name="m_content"
                    value={memo.m_content}
                    onChange={handleChangeForm}
                    rows="3"
                  ></textarea>
                </div>
              </Form.Group>
            </Form>
          </Modal.Body>
          <Modal.Footer>
            <Button variant="secondary" onClick={handleClose}>
              닫기
            </Button>
            <Button variant="primary" onClick={memoUpdate}>
              저장
            </Button>
          </Modal.Footer>
        </Modal>
        {/* ========================== [[ 글등록 Modal ]] ========================== */}
      </div>
    </>
  )
}

export default MemoDetail

 

<firebase 실시간 데이터베이스와 fullcalendar활용한 일정관리 - firebase.js>

import { initializeApp } from "firebase/app";
import { getDatabase } from "firebase/database";

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FS_APIKEY,
  authDomain: process.env.REACT_APP_FS_AUTHDOMAIN,
  databaseURL: process.env.REACT_APP_FS_DATABASEURL,
  projectId: process.env.REACT_APP_FS_PROJECTID,
  storageBucket: process.env.REACT_APP_FS_STORAGEBUCKET,
  messagingSenderId: process.env.REACT_APP_FS_MESSAGINGSENDERID,
  appId: process.env.REACT_APP_FS_APPID,
};

const firebaseApp = initializeApp(firebaseConfig);
export default firebaseApp;
export const database = getDatabase(firebaseApp);

댓글