firestore에서 sub collection 생성해서 comment 관리하기

둘둘·2020년 5월 16일
3

firebase

목록 보기
1/2
post-thumbnail

firestore는 Google firebase에서 제공하는 NoSQL 문서중심의 데이터베이스 서비스이다.

백앤드쪽에서 데이터 만들어줄땐 table이나 row 같은 값들이 있는데
그런 것과는 달리 field-value 쌍의 document가 있고,
그러한 document들이 모여서 collection이 되는 구조라고 이해했다!

내가 하고 있는 어드민페이지는 대략 이러한 구조로 되어있다.

firestore를 쓰기 위해선 일단 node.js를 이용해 function을 구축해야 하는데

우리팀의 경우 역할을 분담해서 했기에 내가 node로 세팅을 하진 않았다!
이 글에서 그 부분은 생략쓰.. 다음번에 내가 사이드 프로젝트를 할때 도전해볼 영역!

다시 본론으로 돌아가서...


firestore database 구조

현재 사용하고 있는 어드민페이지를 캡쳐해서 모자이크 처리 하기 귀찮아서
figma를 이용해 비슷하게 모양만 만들어봤다!

  • collection : customer
  • document : 각각의 customer 이름과 날짜로 된 id (자동 document id로 선택도 가능쓰)
  • field : customer에 대한 상세한 정보들

원래는 comments를 field 안의 값으로, 배열형식으로 관리했다.
새로운 comment가 생길 때마다 string으로 받아서 comments 배열 안에 들어가도록 관리했는데
관리 차원에서 넘넘 비효율적이었기 때문에 comments라는 이름의 subcollection을 생성하기로 했다.
(field영역 상단에 보면 start collection 버튼이 있다!)

코드를 짜기 전에 먼저 sub-collection을 생성하는 과정을 살펴보자!

start collection 버튼을 누르면
맨 처음에 내가 customer라는 collection을 생성했던 것처럼
각각의 document마다 collection을 새롭게 생성할 수 있다!

내 구조를 다시 한번 살펴보자면..

👉 admin 기본 구조

customer(collection) > 2020-05-16 11:34:10 김치킨(doc)>clientName, date, user(field)

👉 admin에서 sub-collection 구조까지 생각했을 때

customer(collection) > 2020-05-16 11:34:10 김치킨(doc)> comments(sub-collection) > Sd5oNmU9JtNhwaym93gq(doc) > comment, date, user(field)

이제 이걸 코드로 녹여보자!

먼저 자주 쓰이는 함수들은 config/firebase.js에 담아놨다가 사용했다.🙌

user 목록 불러오는 함수

import firebase from "firebase";

const firebaseConfig = {
// 각자 설정값에 따라 자유롭게!
}

firebase.initializeApp(firebaseConfig);
const firestore = firebase.firestore();

// 내가 작업했던 어드민 페이지에서 정말 자주 쓰이는 함수
export const getUsersRef = () => {
  return firestore.collection("customer");
};

customer collection에 있는 데이터를 불러오는 함수를 짰으니 이제는 그걸 활용할 차례!
sub-collection을 만들기 위해 commentsRef라는 함수를 만들어주자.

sub collection 생성하는 함수

class Customer extends Component {
  constructor(props) {
    super(props);
    const element = this.props.location.state.el;
    this.commentsRef = getUsersRef()
      .doc(element.id)
      .collection("comments");
    this.state = {
    	commentList : [],
        newComments : "",
    // 생략쓰
    }
    this.getCommentsList();

  }
  // 중략

나의 경우, All이라는 전체 customer list를 보여주는 페이지에서
해당하는 고객이름을 클릭했을때 보여주는 페이지가 Customer.js여서
해당 고객의 doc id를 props의 state안에 담아왔지만, 담아오는 방법은 각자 편한대로 고고!
(최근에 리팩토링 과정을 거친 코드는 다른 방법을 이용했다!)

사실 처음에 sub collection을 만들라는 임무(?)를 받았을땐 엄청 헤매서 노트에 이렇게 끄적끄적..

코드가 잘 안 써질땐 이렇게 구조를 그리거나 아무말이나 쓰는게 도움이 된다 ㅋㅋㅋ

여튼... 서브 컬렉션은 만들었으니 이제 불러와볼까! 룰루~

sub collection에서 데이터 불러오는 함수

// firestore에서 comment 정보 불러오는 함수
  getCommentsList = () => {
    let commentList = [];

    this.commentsRef
      .get()
      .then(commentSnap => {
        commentSnap.forEach(doc => {
          commentList.push({
            ...doc.data(),
            id: doc.id
          });
        });
        this.setState({ commentList });
      });
  };

그렇다면 업데이트는 어떻게 해줄까!
일단 내가 입력한 정보가 들어가야 하기 때문에 state를 이용해서 관리해주고

댓글 state로 관리하기 및 받아온 데이터 맵 돌리기

// 요 아래 부분은 테이블 안의 댓글 입력창 부분임

// handleChange는 여러분이 생각하는 그 함수 맞습니다!

 <tr>
            <td className="td-comment">
              {editable && <CommentArea
                onChange={this.handleChange}
                name="newComments"
                placeholder="메모 입력"
              />}
              {commentList.map(el => (
                <FlexLine key={el.id}>
                  <div>
                    <CommentSpan>{el.user}</CommentSpan>
                    <CommentSpan small>
                      {`(${formatDateForComment(el.date)})`}
                    </CommentSpan>
                    <CommentP>{el.comment}</CommentP>
                  </div>
                  {editable && (
                    <I
                      className="fas fa-times-circle"
                      onClick={() => this.deleteComment(el.id)}
                    />
                  )}
                </FlexLine>
              ))}
            </td>
          </tr>
  • editable은 편집모드일 때의 상태
  • 새롭게 들어오는 코멘트 값들은 newComments state로 관리
  • 기존 firestore에서 가져오는 댓글정보는 commentList 배열에 넣고 맵 돌리기
  • formatDateForComment는 moment.js를 이용해 timestamp형식을 내가 원하는 날짜형식으로 바꾼 것
// util/moment.js 폴더 안에 있는 formatDateForComment 함수

import moment from "moment-timezone";

export const formatDateForComment = value => {
  if (!value || !value.seconds) return null;
  return moment(value.seconds * 1000)
    .tz("Asia/Seoul")
    .format("YYYY년 M월 D일 hh시 m분");
};

여기까지 하면 state로는 값이 잘 들어오지만, 우리는 firestore에 저장해야 하기 때문에 하나의 과정을 더 거쳐야 한다.
나의 경우 저장 버튼을 누르면 실행되는 handleSaveMode 함수를 만들었다.

state에 넣은 데이터 firestore로 내보내는 함수

  handleSaveMode = e => {
    e.preventDefault();

    const {
	// 엄청 많은 state 값들이 있지만 주인공이 아니므로 다 생략 
      newComments
    } = this.state;

    getUsersRef()
      .doc(users.id)
      .set(
        {
          // state 안에 있는 무수한 값들 넣어주기
        },
        { merge: true }
      )
      .then(() => console.log("저장쓰"))
      // 최근 리팩토링된 코드를 보면 여기에도 this.getCommentsList()가 불려오는데
      // 이렇게 해줘야 새로 들어온 값이 바로 업데이트 된다!
      .catch(error => console.error(error));

    // newComments && 조건을 안 넣어주면 댓글 안쓰고 다른 정보 수정하는데 빈 댓글이 생겨벌임!
    newComments &&
      this.commentsRef.doc().set(
        {
          comment: newComments, // state에 새로 들어오는 값
          date: this.today, // 변수 따로 만들어주세염
          user: localStorage.getItem("userName") // 로그인 해서 댓글쓰는 adminUser의 이름
        },
        { merge: true }
      );

    this.setState({ editable: false });
  };

이렇게 하면 서브 컬렉션으로 댓글 생성하기 성공!
글이 많으니까 너무 노잼이 된 것 같아서 속상쓰... 😂

아!! 한 가지 빠뜨릴 뻔!!
댓글 삭제기능 함수도 있다!

sub collection의 document 삭제 함수(댓글 삭제기능)

  deleteComment = id => {
    this.commentsRef.doc(id).delete();
    
    // 원래 여기까지 작성하였으나... 리팩토링 된 코드를 보니
    // getCommentsList를 다시 불러오는데 
    // 생각해보니 이렇게 다시 함수를 불러줘야 바로 삭제된게 반영된다!
    alert("삭제 되었습니다.");
    this.getCommentsList();
  };

어드민 페이지 작업 하면서 느낀 점

😂디자인과 기획의 일부를 맡아서 하는건 정말 어렵다

  • 하지만 현실은 zeplin으로 디자인이 주어져도 제대로 못함ㅋㅋㅋㅋ ㅠㅠ
  • 디자이너 님덜.. 기획자 님덜.. 존경합니다

😂마감기한 맞추기 정말 어렵다

  • 사수님 뎨동합니다 ㅠㅠㅠㅠㅠ

😂코드 깔끔하게 쓰기 정말 어렵다

  • 코드 많이 치고 경력이 쌓이다 보면 잘 해낼수 있겠지..?!

😂firebase 정보 구글링 하기 은근 어렵다

  • 제 포스팅이 많은 분들에게 도움이 되었으면...

어려운 과정이었고, 현재 대부분의 작업들이 사수님께 넘어갔지만..(뎨동합니다 ㅠㅠ)
작업 하는 동안 많이 배운 것 같다!!!

지금은 쭈구리 신입 개발자지만.. 언젠가 어깨 펴는 그날까지.. 💪💪

(다음 탄은 아마 firestore 필터기능 구현하기가 될듯!?)

profile
Dooreplay! 안 되면 될 때까지,

1개의 댓글

comment-user-thumbnail
2022년 5월 28일

이렇게 하면 내가 쓴 댓글 목록은 어떻게 가져와요..?>

답글 달기