Firebase 및 React로 나만의 좋아요 및 댓글 시스템을 구축하는 방법

React 앱의 할일 항목 중 하나는 정적 웹사이트를 위한 성능 우선 동적 댓글 및 좋아요 시스템을 갖는 것이었습니다. 왜요? 콘텐츠를 넘어 사용자 참여를 유도하는 기능을 추가할 수 있는 기능을 제공하기 때문입니다.

Cusdis와 Disqus는 CLS(Cumulative Layout Shift)에 큰 영향을 미치기 때문에 성능 친화적이지 않습니다.



그래서 나는 Firebase , TailwindCSSReact 으로 시스템을 만들기 시작했습니다. TailwindCSS는 강제는 아니지만 도서관에 가는 것입니다. 시작하자.

Firebase 설정


  • 다음 명령을 사용하여 Firebase(클라이언트 측)를 설치합니다.

  • npm i firebase
    


  • 다음 구성으로 firebase.js를 생성합니다.

  • // File: @/lib/firebase.js
    
    import 'firebase/firestore'
    import firebase from 'firebase/app'
    
    // More about firebase config on https://firebase.google.com/docs/web/setup#config-object
    var firebaseConfig = {
      apiKey: '',
      authDomain: '',
      projectId: '',
      storageBucket: '',
      messagingSenderId: '',
      appId: '',
    }
    
    if (!firebase.apps.length) {
      firebase.initializeApp(firebaseConfig)
    } else {
      firebase.app()
    }
    
    export const firestore = firebase.firestore()
    export default firebase
    


    유사 구성 요소 생성


  • like.js 파일을 만듭니다.

  • // File: components/blog/like.js
    
    import { firestore } from '@/lib/firebase'
    


  • 블로그 페이지의 슬러그를 받는 getLikes 함수와 필요한 경우 콜백 함수를 추가합니다.

  • export const getLikes = (slug, callBackFunction) => {
      firestore
        .collection('likes')
        .doc(slug)
        .get()
        .then((doc) => {
          if (doc.exists) {
            callBackFunction(Object.keys(doc.data()).length)
          }
        })
        .catch((err) => {
          console.error(err)
        })
    }
    


  • 블로그 페이지의 슬러그를 받는 postLike 함수와 필요한 경우 콜백 함수를 추가합니다.

  • export const postLike = (slug, callBackFunction) => {
      fetch('https://api.ipify.org/?format=json', {
        method: 'GET',
      })
        .then((res) => res.json())
        .then((res) => {
          firestore
            .collection('likes')
            .doc(slug)
            .set(
              {
                [res['ip']]: null,
              },
              { merge: true }
            )
            .then(callBackFunction)
        })
        .catch((err) => {
          console.error(err)
        })
    }
    


    코멘트 컴포넌트 생성


  • comment.js 파일을 만듭니다.

  • // File: components/blog/comments.js
    
    import { useState } from 'react'
    import firebase, { firestore } from '@/lib/firebase'
    


  • 블로그 페이지의 슬러그를 받는 getComments 함수와 필요한 경우 콜백 함수를 추가합니다.

  • export const getComments = (slug, callBackFunction) => {
      firestore
        .collection('comments')
        .get()
        .then((snapshot) => {
          const posts = snapshot.docs
            .map((doc) => doc.data())
            .filter((doc) => doc.slug === slug)
            .map((doc) => {
              return { id: doc.id, ...doc }
            })
          callBackFunction(posts)
        })
        .catch((err) => {
          console.log(err)
        })
    }
    


  • 블로그 페이지의 슬러그를 받는 writeComment 함수와 필요한 경우 콜백 함수를 추가합니다.

  • export const writeComment = (name, slug, content, email, callBackFunction) => {
      let temp = {
        name,
        slug,
        content,
        time: firebase.firestore.Timestamp.fromDate(new Date()),
      }
      if (email.length > 0) temp['email'] = email
      firestore
        .collection('comments')
        .add(temp)
        .then(() => {
          callBackFunction()
        })
        .catch((err) => {
          console.error(err)
        })
    }
    


  • 표시할 주석 세트를 가져오는 LoadComments 함수 생성

  • export const LoadComments = ({ comments }) => {
      return comments
        .sort((a, b) =>
          a.time.toDate().getTime() > b.time.toDate().getTime() ? -1 : 1
        )
        .map((item) => (
          <div
            key={item.time.seconds}
            className="border dark:border-gray-500 rounded p-5 w-full mt-5 flex flex-col"
          >
            <span className="text-lg text-gray-500 dark:text-gray-300 font-medium">
              {item.name} &middot; {item.time.toDate().toDateString()}
            </span>
            <span className="mt-3 text-md text-gray-500 dark:text-gray-300">
              {item.content}
            </span>
          </div>
        ))
    }
    


  • 블로그 페이지의 슬러그를 가져오는 WriteComment 구성 요소와 표시할 새 댓글 집합을 설정하기 위한 setComments를 만듭니다.

  • const WriteComment = ({ slug, setComments }) => {
      const [name, setName] = useState('')
      const [email, setEmail] = useState('')
      const [comment, setComment] = useState('')
    
      return (
        <form
          onSubmit={(e) => {
            e.preventDefault()
            writeComment(name, slug, comment, email, () =>
              getComments(slug, setComments)
            )
            setName('')
            setEmail('')
            setComment('')
          }}
          className="mt-10 flex flex-col w-full"
        >
          <h1 className="font-semibold text-lg">Write a comment</h1>
          <div className="flex flex-col sm:flex-row sm:space-x-5 items-start">
            <input
              required
              value={name}
              placeholder="Name*"
              onChange={(e) => setName(e.target.value)}
              className="mt-5 w-full sm:w-1/2 appearance-none outline-none ring-0 px-5 py-2 border dark:hover:border-white hover:border-black rounded hover:shadow text-black dark:bg-black dark:text-gray-300 dark:border-gray-500"
            />
            <div className="mt-5 w-full sm:w-1/2 flex flex-col space-y-1">
              <input
                value={email}
                placeholder="Email (Optional)"
                onChange={(e) => setEmail(e.target.value)}
                className="w-full appearance-none outline-none ring-0 px-5 py-2 border dark:hover:border-white hover:border-black rounded hover:shadow text-black dark:bg-black dark:text-gray-300 dark:border-gray-500"
              />
              <span className="text-sm text-gray-400">
                Email will remain confidential.
              </span>
            </div>
          </div>
          <textarea
            required
            value={comment}
            onChange={(e) => setComment(e.target.value)}
            placeholder={'Comment*\nMaximum of 500 characters.'}
            className="mt-5 appearance-none outline-none ring-0 pt-5 px-5 pb-10 border dark:hover:border-white hover:border-black rounded hover:shadow text-black dark:bg-black dark:text-gray-300 dark:border-gray-500"
          />
          <button
            type="submit"
            className="w-[200px] appearance-none mt-5 py-2 px-5 text-center rounded border hover:bg-gray-100 dark:hover:bg-[#28282B] dark:border-gray-500"
          >
            Post a comment
          </button>
        </form>
      )
    }
    
    export default WriteComment
    


    다이내믹 블로그 컴포넌트 생성


  • 동적 블로그 [slug].js 파일에서 구성 요소를 로드합니다.

  • import WriteComment, {
      getComments,
      LoadComments,
    } from '@/components/blog/comments'
    
    export default function Post({ post }) {
      const [comments, setComments] = useState([])
      return <>
        <WriteComment setComments={setComments} slug={post.slug} />
        <div className="mt-10 pt-10 w-full border-t dark:border-gray-500">
          <button
            onClick={() => getComments(post.slug, setComments)}
            className="w-[200px] appearance-none py-2 px-5 text-center rounded border hover:bg-gray-100 dark:hover:bg-[#28282B]   dark:border-gray-500"
          >
            Load Comments
          </button>
        </div>
        <LoadComments comments={comments} />
      </>
    }
    


    예시



    blog page에서 예를 볼 수 있습니다! 소스 코드를 사용할 수 있습니다here.

    좋은 웹페이지 즐겨찾기