알린홈마의 코드친구들
article thumbnail

2023.08.25 - [JS/React] - "나의 일지"프로젝트(5)_handsontable, 파이어스토어 데이터 베이스

 

"나의 일지"프로젝트(5)_handsontable, 파이어스토어 데이터 베이스

이제 첫 리포트를 생성해보자~!! 작업일지 리포트는 테이블형태(엑셀)로 작성하고 엑셀과 csv 파일로 내보내고 불러오게 가능하도록 구상을 했다. 마침 테이블 형태 관련해서 좋은 글과 좋은 라

aliencoding.tistory.com

리포트에 필요한 조건으로는 

글 제목, 글 작성시간, 글 내용, 수정여부(false로 시작)가 꼭 필요하다 

 

글 제목을 setDoc에 전달해줄려면

상태 저장을 위해 useState를 사용 기본 값은 "" 로 둔다

import React, { useState } from "react";
const [title, setTitle] = useState("");

그 다음, 제목 입력에 onChange 이벤트에 이벤트타겟의 값을 `setTitle()`에 전달해준다

const handleTitleChange = (e) => {
setTitle(e.target.value);
};
//.
//.
//.
<input
    type="text"
    placeholder="제목"
    onChange={handleTitleChange}
    required
    className=" max-w-[300px] "
/>

템플릿 넣어주기

더보기

제목 옆에 체크박스와 라벨을 넣어주고 useTemplete 값에 따라 체크박스 아이콘을 설정해준다.

 <input
    type="checkbox"
    id="useTemplete"
    className="hidden"
    onChange={hadleuseTemplete}
  />
  <label htmlFor="useTemplete" className="flex items-center gap-2">
    <p>템플릿 사용</p>
    {useTemplete ? (
      <AiFillCheckSquare className="text-blue-700" />
    ) : (
      <AiOutlineCheckSquare />
    )}
</label>

 

또한 체크박스에 onChange 이벤트에 함수를 전달해주어 표에 데이터를 넣어주자!

  const [data, setData] = useState();
  
  const hadleuseTemplete = () => {
    hot = hotRef.current.hotInstance;
    const data = hot.getData();
    if (!useTemplete) {
      if (window.confirm("템플릿을 사용하겠습니까?")) {
        const templete = [
          "날짜",
          "분류",
          "요청자",
          "내용",
          "작업자",
          "전달방식",
          "관련 파일명",
        ];
        console.log(Array(templete).concat(data));
        setData(Array(templete).concat(data));
        setUseTemplete(true);
      }
    } else {
      if (window.confirm("템플릿을 사용 취소 하겠습니까?")) {
        setData(data.slice(1));
        setUseTemplete(false);
      }
    }
  };

템블릿 체크가 안되어 있으면(false) 데이터를 수정하는데

hot.getData()로 불러온 데이터는 배열안에 배열들을 반환하니 templete를 Array 함수로 감싸고 concat 함수를 이용해서 붙여주어 getData()의 인자로 넣어주어 데이터를 수정한다.

 

만약 체크가 되어있으면(true: 템플릿 취소)

 

 

hot.getData()로 데이터로 불러와 템플릿영역만 slice() 함수를 사용해 제외하고 setData()의 인자로 전달해준다.

 

 

hottable에는 데이터(전달해주면 테이블에 표시)에 데이터를 전달하도록 해놓았다

    <HotTable
      id="hot"
      data={data && data}
      colHeaders={colHeaders}
      rowHeaders={true}
      manualColumnMove={true}
      fixedColumnsStart={window.innerWidth > 1024 ? 1 : 0}
      className="htCenter htMiddle z-0"
      colWidths={colWidths()}
      rowHeights={`${(window.innerHeight - 200) / 10}`}
      licenseKey="non-commercial-and-evaluation"
      readOnly={readOnly ? readOnly : false}
      ref={hotRef}
      contextMenu={true}
      manualColumnResize={true}
      dropdownMenu={true}
      columnSorting={true}
      width={"100%"}
      afterColumnSort={exclude}
      // for non-commercial use only
    />

 

 

이제 필요한 내용들을 담아서 파이어베이스 파이어스토어에 전달해주자!

제목과 테이블을 form 태그안에 담아서 form의 `onSubmit 이벤트`에 저장하는 역활을 하는 `saveClickCallback`함수를 전달해준다.


데이터 전달 방법

writePage.jsx

  const saveClickCallback = async (e) => {
    e.preventDefault();
    hot = hotRef.current.hotInstance;
    console.log(hot.getData());
    let data = JSON.stringify(hot.getData());
    await addReport(data, user, title).then((reportId) => {
      setTitle("");
      navigate(`/reports/${reportId}`);
    });
  };

getData() 받아온 데이터는 Array of Array 형태이기 때문에 `JSON.stringify()`로 json 문자열로 변환해주어서 전달해준다.

addReport 에는 데이터, 작성한 유저의 정보, 제목을 넘겨준다. 

 

firestore.js

export async function addReport(data, user, title) {
  const reportId = uuidv1();
  await setDoc(doc(db, "reports", reportId), {
    data: data,
    userId: user.uid,
    createdAt: serverTimestamp(),
    title: title,
    reportId: reportId,
    fix: false,
    writer: user.displayName,
  });

  return reportId;
}

 


serverTimestamp란?

`serverTimestamp()` 는 구글에서 설명하길 "문서의 필드를 서버 업데이트 수신 시점을 추적하는 서버 타임스탬프로 설정할 수 있습니다."

라고 한다. 작성시점을 `new Date()`를 통해서 전달하게 되면 네트워크 지연시 문제가 발생한다. new Date()는 요청때 값이 생성이 되어서 서버에 데이터가 저장된 시점과 달라질 수 있다. 즉 시스템 시계는  클라언트 서버 간에 다를 수 있기에 동기화하고 , 정확한 시간 정보가 필요하기 때문에 서버의 현재 시간을 나타내는 serverTimestamp()를 이용해주었다.

 

사용방법

  • 문서 생성시
import { setDoc, serverTimestamp } from "firebase/firestore";

export async function addReport() {
  const reportId = uuidv1();
  await setDoc(doc(db, "reports", reportId), {
    createdAt: serverTimestamp()
  });
}
  • 문서 업데이트
import { updateDoc, serverTimestamp } from "firebase/firestore";

export async function updateReport(data, title, reportId) {
  const reportRef = doc(db, "reports", reportId);

  await updateDoc(reportRef, {
    createdAt: serverTimestamp()
  });
}

글 작성 페이지 전체 코드

writePage.jsx

  return (
    <div className="w-full">
      <form onSubmit={saveClickCallback}>
        <div className="flex flex-col gap-2 grow p-4 xl:w-full w-screen">
          <div className="flex gap-2 w-full flex-col xl:flex-row items-start xl:items-end">
            <input
              type="text"
              placeholder="제목"
              onChange={handleTitleChange}
              required
              className=" max-w-[300px] "
            />
            <div>
              <input
                type="checkbox"
                id="useTemplete"
                className="hidden"
                onChange={hadleuseTemplete}
              />
              <label htmlFor="useTemplete" className="flex items-center gap-2">
                <p>템플릿 사용</p>
                {useTemplete ? (
                  <AiFillCheckSquare className="text-blue-700" />
                ) : (
                  <AiOutlineCheckSquare />
                )}
              </label>
            </div>
          </div>
          <div className="min-h-[120vw] xl:min-h-[30vw] w-full  overflow-hidden reportTable relative">
            <HotTableOption colHeaders={true} data={data} hotRef={hotRef} />
          </div>
          <div className="flex gap-4">
            <button type="submit" className="btn_default">
              save
            </button>
          </div>
        </div>
      </form>
    </div>
  );

 

결과

전달된 데이터

 

 

profile

알린홈마의 코드친구들

@알린팬클럽홈마

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...