sooleeandtomas

댓글 사용하기, 댓글 알람주기 feat. firebase realtime DB 본문

코딩 공부 노트/React

댓글 사용하기, 댓글 알람주기 feat. firebase realtime DB

sooleeandtomas 2021. 5. 14. 21:07

firebase에서 복합쿼리 사용하는 법 

1. 색인 탭에서 -> 색인 만들기 클릭

 

3. 컬렉션 id, 필드 입력, 쿼리범위 : 컬렉션

 

4. 복합쿼리를 생성하는데 좀 시간이 오래 걸린다고 합니다. 1시간정도? 

 


 

댓글 불러오기 

 

댓글의 데이터 형태는 아래와 같습니다. 

각 post_id 가 [ { }, { }, { } ] post_id에 해당하는 댓글 리스트를 가져오는 것입니다.

[
  13kngsfOSNO:[{
    centents: "",
    id: "",
    insert_dt: "",
    post_id: "",
    user_id: "",
    user_name: "",
    user_profile: "",
  },{
    centents: "",
    id: "",
    insert_dt: "",
    post_id: "",
    user_id: "",
    user_name: "",
    user_profile: "", 	
  }]
]

 

redux에서 댓글 리스트 가져오기

const getCommentFB = (post_id = null) => {
  if(!post_id){return}
  return function(dispatch, getState, {history}){
    const commentDB = firestore.collection("comment"); // 파이어베이스에서 "comment" 이름을가진 db를 찾아옵니다.
    commentDB 
      .where("post_id", "==", post_id)  // commentDB에서 "post_id" 가 post_id 와 같은 값을 찾아옵니다.
      .orderBy("insert_dt", "desc") // 내림차순으로 정렬해줍니다. 
      .get() //데이터를 가져옵니다. 
      .then(docs => {
        let list =[]  //배열을 하나 만들어줍니다.
        
        docs.forEach(doc=>{
          list.push({...doc.data(), id: doc.id}) // id값을 같이 넣어서 배열에 {comment} 를 넣어줍니다. 
          // [ { comment1 }, { comment2 }, { comment3 } .... ] 이 됩니다.
        })

        dispatch(setComment(post_id, list))  // redux에 저장해줍니다. * post_id와 리스트를 보내줍니다. 
         // {  [postid] : [ { coment1 }, { comment2 }, { comment3 } ... ] } 형식으로 저장해야하기 때문입니다.
       }).catch(err=>{
        console.log('get comment err', err);
      });
  }
}

dispatch - setComment 로 post_id 와 list를 전달하면 

dispatch - setComment 에서 post_id : [  { } , { } ] 로 저장해줍니다.

export default handleActions(
  {
    [SET_COMMENT]: (state, action) => produce(state, (draft) => {
      draft.list[action.payload.post_id] = action.payload.comment_list;
      // 데이터 형식이 { post_id : [ {comment1}, {comment1}, {commnt3} ] } 으로 저장되어야 합니다. 
    })
  },
  initialState
);

  

 

댓글 쓰기 

const addCommentFB = (post_id, contents) => {
  return function(dispatch, getState, {history}){
    const commentDB = firestore.collection("comment");
    const user_info = getState().user.user;

    let comment = {
      post_id,
      user_id: user_info.uid,
      user_name: user_info.user_name,
      user_profile: user_info.user_profile,
      contents : contents,
      insert_dt: moment().format("YYYY-MM-DD HH:mm"),
    }  //comment 데이터 형식을 설정해줍니다.

    commentDB
      .add(comment) // commentDB에 데이터를 넣어줍니다.
      .then(doc => { // 
        const postDB = firestore.collection("post");  //postDB를 찾아줍니다.
        const post = getState().post.list.find( l => l.id === post_id) //postDB 데이터 중에서 post_id 와 id 가 같은 데이터를 찾아줍니다. 
        const increment = firebase.firestore.FieldValue.increment(1); // increment를 가져옵니다.
        comment = {...comment, id: doc.id} // comment에 comment.id를 저장해줍니다.

        postDB
          .doc(post_id) // post_id로 업데이트 할 포스트를 찾아줍니다.
          .update({ comment_cnt : increment } ) // comment_cnt를 업데이트해줍니다.
          .then(()=>{
            dispatch(addComment(post_id, comment)); // 커맨트를 리듀서로 저장하기위해 보냅니다. 
            if(post){
              dispatch(postActions.updatePost(post_id, { comment_cnt: parseInt(post.comment_cnt) + 1})) // 포스트 리듀서로 cnt값을 보냅니다.
            }
          }).catch(err => {
            console.log(err);
          })
      }).catch(err => {
        console.log(err)
      })
  }
}

 

 

RealTime DataBase 사용하기

firebase console에서 이번엔 realtime database 설정을 해줍니다.

만약 설정 시 싱가포르, 미국 이런데를 선택했다면 databaseURL 이 바뀌었을 겁니다.

realtime database를 사용하려 하면 아래와 같은 에러가 뜨면서 작동하지 않습니다.

이때 다시 firebase console 나의 사용자 및 권한으로 들어가서 

apiKey, autodomain,

projectId,

storageBucket ... 

등등의  firebase config를 복사해서 다시 나의 firebaseConfig에 붙여넣어주면 됩니다.

 

import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
import "firebase/storage";
import "firebase/database"; // 실시간 데이터는 database입니다.

const firebaseConfig = {
  //생략
};

firebase.initializeApp(firebaseConfig);

const apiKey = firebaseConfig.apiKey;
const auth = firebase.auth();
const firestore = firebase.firestore();
const storage = firebase.storage();
const realtime = firebase.database();

export { auth, apiKey, firestore, storage, realtime };
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { Badge } from "@material-ui/core"; //material ui를 사용합니다.
import NotificationsIcon from '@material-ui/icons/Notifications';
import { realtime } from "../shared/firebase"; //fire베이스에서 등록해준 realtime 불러옵니다.

const NotiBadge = props => {
  const user_id = useSelector(state => state.user.user.uid); 
  const { _onClick } = props
  const [is_read, setIsRead] = useState(true);  // read: false일 경우에만 알람이 울립니다.

  const notiCheck = () => { // 알람 표시를 누른 경우에는 
    const notiDB = realtime.ref(`noti/${user_id}`);
    notiDB.update({ read: true }) // read: true로 바꿔주어 알람을 꺼줍니다. 
    _onClick();
  }
  
  useEffect(()=>{
    const notiDB = realtime.ref(`noti/${user_id}`); // 구독 DB 
    
    notiDB.on("value", snapshot => { //구독을 켜줍니다.
      setIsRead(snapshot.val().read); // firebase realtimeDB에 등록해 놓은 key값을 가져옵니다.
    }) 

    return () => notiDB.off(); // 랜더가 종료되면 구독을 종료해줍니다.
  },[])

  return(
    <>
      <Badge color="secondary" variant="dot" invisible={is_read} onClick={notiCheck}> 
      <!-- isvisible이 알람을 보여주는 것 -->
        <NotificationsIcon/>
      </Badge>
    </>
  )
};

NotiBadge.defaultProps = {
  _onClick : () => {}
}

export default NotiBadge

 

noti상태를 업데이트 하는 법

const notiDB = realtime.ref(`noti/${user_id}`); // 참조할 notiDB 가져오기
notiDB.update({ read: true }) // notiDB 업데이트 해줍니다.

 

noti의 정보가 모여있는 news 페이지를 realtime DB의 정보를 가져와 사용할 것입니다.

이를 위해 comment를 남기는 즉시 realtime DB에 comment 관련 정보를 저장해줍니다.

redux/module/comment.js 에서 addCommentFB 함수를 수정해줍니다.

const addCommentFB = (post_id, contents) => {
  return function(dispatch, getState, {history}){
    const commentDB = firestore.collection("comment");
    const user_info = getState().user.user;

    let comment = {
      post_id,
      user_id: user_info.uid,
      user_name: user_info.user_name,
      user_profile: user_info.user_profile,
      contents,
      insert_dt: moment().format("YYYY-MM-DD HH:mm"),
    }

    commentDB
      .add(comment)
      .then(doc => {
        const postDB = firestore.collection("post");
        const post = getState().post.list.find( l => l.id === post_id)
        const increment = firebase.firestore.FieldValue.increment(1);
        comment = {...comment, id: doc.id}

        postDB
          .doc(post_id)
          .update({ comment_cnt : increment } )
          .then(()=>{

            dispatch(addComment(post_id, comment));
            if(post){
              dispatch(postActions.updatePost(post_id, { comment_cnt: parseInt(post.comment_cnt) + 1}))
            }
            
            //😜 noti 여기서부터 봅니다.
            const _noti_item = realtime.ref(`noti/${post.user_info.user_id}/list`).push(); 
            // realtimeDB에 새로운 리스트를 저장할 자리를 마련해줍니다.
            // 이 db는 userid로 구분을 해줍니다.
            // db는 { {userid : read: false , list: [{ comment1 }, { comment2 }, { comment3 }] } }의 형태로 저장할겁니다.
            _noti_item.set({ // set함수를 통해 새로운 list 아이템을 저장해줍니다.
              post_id,
              user_name: comment.user_name,
              image_url: post.image_url,
              insert_dt: comment.insert_dt,
            }, err => {
              if(err){
                console.log("noti save error", err)
              }else{
                const notiDB = realtime.ref(`noti/${post.user_info.user_id}`); 
                notiDB.update({read: false})// 저장에 성공했다면 read : false를 통해 알람을 울려줍니다.
              }
            })

          }).catch(err => {
            console.log(err);
          })
      }).catch(err => {
        console.log(err)
      })
  }
};

 

데이터베이스에 저장했다면 이제 New.js 페이지에서 정보를 가져와서 사용합니다.

// PostList.js
import React, { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { Grid, Image, Text } from "../elements";
import { realtime } from "../shared/firebase";
import { history } from "../redux/configureStore";

const NewsList = (props) => { 
  const user = useSelector(state => state.user.user);
  const [noti, setNoti] = useState([]);

  useEffect(()=>{
    if(!user){ return };
    
    const notiDB = realtime.ref(`noti/${user.uid}/list`); // userid에 맞는 realtimeD를가져옵니다.
    
    const _noti = notiDB.orderByChild("insert_dt"); // 정보를 정렬해서 가져옵니다. r.db는 오름차순으로밖에 정렬이 안됩니다.

    _noti.once("value", snapshot => { //한번만 가져올 것이기 떄문에 noti.on 이 아닌 noti.once로 가져옵니다.
      if(snapshot.exists()){ // 정보가 있다면 : 정보.exists()의 함수로 체크합니다.
        let _data = snapshot.val();  // 정보의 data를 가져옵니다.

        let _noti_list = Object.keys(_data).reverse().map(s => { // 내림차순으로 재정렬해줍니다.
          return _data[s]; 
        });
        setNoti(_noti_list); 
      }
    })
    
  },[user])
  return(
    <>
      {noti.map((data, idx)=>(
        <News key={`NEWS_${idx}`}{...data}/>
      ))}
    </>
  )
}

const News = props => {
  const { image_url, user_name, post_id } = props;

  return(
    <Grid 
      _onClick={()=>{ history.push(`/postdetail/${post_id}`)}}
      is_flex
      justify="flex-start"
      padding="30px"
    >
      <Image src={image_url} shape="rectangle" size="100px"/>
      <Grid padding="20px">
        <Text align="start"><strong>{user_name}</strong>님이 게시글에 댓글을 남겼습니다 :) !</Text>
      </Grid>
    </Grid>
  )
}

NewsList.initialProsp = {
  user_name:"",
  post_id:"",
  image_url:"",
}

export default NewsList
Comments