Skip to main content

Command Palette

Search for a command to run...

[Next.js] 나만의 학습 블로그 만들기 #2 - 방문자 수 측정하기(feat. Firebase)

Updated
•3 min read•View as Markdown
[Next.js] 나만의 학습 블로그 만들기 #2 - 방문자 수 측정하기(feat. Firebase)

내용 기록 외에도 방문자 수랑 댓글 기능도 넣으려고 한다.!

이번 글에서는 방문자 수 측정 기능 구현을 적어보겠다🤭

나는 서버가 없으니 파이어베이스를 선택해줬다…!

  1. 파이어베이스 사이트에 들어가서 프로젝트를 생성해주고 Firestore Database를 클릭해주자

👉🏻 링크 클릭

  1. 클릭하면 이런 화면이 나옴

  1. 데이터베이스 만들기를 클릭

프로덕션 모드에서 시작

  1. 그러면 이런 화면이 뜬다 ! 여기서 규칙을 수정해주자

기본 파이어베이스에서 제공해주는 코드를 해석해보면

→ 모든 경로에 대해 읽기와 쓰기 전부 금지! 안전하긴 하지만 아무것도 하지 못하기 때문에 수정을 해줘야한다.

📬 조회수 측정은 가능할 수 있도록

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /views/{slug} {
      allow read: if true;

      allow update: if
        request.resource.data.keys().hasOnly(['count']) &&
        request.resource.data.count is int &&
        request.resource.data.count > resource.data.count;

      allow create: if
        request.resource.data.keys().hasOnly(['count']) &&
        request.resource.data.count == 1;
    }
    match /{document=**} {
      allow read, write: if false;
    }
  }
}

이렇게 수정하고 게시를 눌러줘서 반영해줬다 !

  1. Firebase 프로젝트 클릭하면 상단 가운데 아이콘 중 </> (웹 앱 추가) 클릭해주고 하라는대로 해주면
const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

이런 설정객체들을 알려주는데

firebase.js 폴더를 만들어줘서 넣어줬다.

import { initializeApp, getApps } from "firebase/app";
import { getAnalytics, isSupported } from "firebase/analytics";

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

const app =
  getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];

let analytics = null;
if (typeof window !== "undefined") {
  isSupported().then((yes) => {
    if (yes) {
      analytics = getAnalytics(app);
    }
  });
}

export { app };

이렇게 하면 firebase 준비는 끝났다 !!!!!

이제 vscode에서 설정해보자

incrementView.js

import { db } from "./firebase.js";
import {
  collection,
  getDocs,
  doc,
  getDoc,
  setDoc,
  updateDoc,
  increment,
} from "firebase/firestore";

export async function incrementViewCount(slug) {
  const ref = doc(db, "views", slug);
  const snap = await getDoc(ref);

  if (snap.exists()) {
    await updateDoc(ref, {
      count: increment(1),
    });
  } else {
    await setDoc(ref, {
      count: 1,
    });
  }
}

export async function getViewCount(slug) {
  const ref = doc(db, "views", slug);
  const snap = await getDoc(ref);
  return snap.exists() ? snap.data().count : 0;
}

export async function getTotalViews() {
  const snapshot = await getDocs(collection(db, "views"));
  let total = 0;
  snapshot.forEach((doc) => {
    total += doc.data().count || 0;
  });
  return total;
}

TotalView.js

import { useEffect, useState } from "react";
import { getTotalViews } from "../../lib/incrementView";

export default function TotalView() {
  const [total, setTotal] = useState(0);

  useEffect(() => {
    getTotalViews()
      .then(setTotal)
      .catch(() => setTotal(0));
  }, []);

  return (
    <div className="text-right sm:text-sm text-xs text-gray-600 dark:text-gray-400">
      조회수 | <span className="font-semibold">{total.toLocaleString()}회</span>
    </div>
  );
}

만든 컴포넌트를 _app.js에 넣어줬다

이렇게 틀은 만들었고 아직 만든 페이지가 없어서 slug를 홈에만 넣어주려고 한다.

index.js

import { useEffect } from "react";
import { incrementViewCount } from "../../lib/incrementView";
export default function Home() {
  useEffect(() => {
    incrementViewCount("home");
  }, []);

  return <div className="max-w-[940px] mx-auto"></div>;
}

설정 완료 ~~~ ! 🤭

아직은 배포를 안 해서 조회수는 증가가 안되지만 기능 자체는 잘 붙여서 뿌듯하다.

역시 파이어베이스는 프엔한테 최고다…🙌🏻

More from this blog

[React] 클라이언트에서 이미지를 압축해보자

우리는 서버 비용을 직접 부담하고 있기 때문에 항상 최적화와 비용 절감 방법을 고민하게 된다. 현재 자유게시판이 활성화가 많이 되지는 않았지만 사용자들이 업로드하는 이미지가 평균 5MB 이상일 경우를 대비해서 클라이언트에서 이미지 압축을 진행했다. 웹에서는 크게 두 가지 방법이 있었는데 Canvas API를 직접 사용하는 방법 browser-image-compression 라이브러리를 활용하는 방법 이 있다. 여러 장점을 고려해 brow...

Oct 8, 20252 min read
[React] 클라이언트에서 이미지를 압축해보자

[책 추천] 프론트엔드 개발자라면 반드시 알아야 할 '웹 접근성' 이야기

내가 생각하는 프론트엔드 개발자는 단순히 보이는 화면만 구현하는 것을 넘어서 누구나 접근할 수 있는 웹을 만드는 데 중요한 역할을 해야 한다고 생각한다. 실무에 바로 적용하는 웹 접근성 가이드북 이번에 이 책을 받고 프엔들이 읽기에 너무 좋다고 생각해서 블로그에 추천 글까지 적게 되었다 ! 목차 Chatper1. 쉽게 이해하는 접근성 Chapter2. 웹 접근성의 기초 Chapter3. HTML 태그, 의미 있게 사용하기 Chpater4...

Jul 27, 20255 min read
[책 추천] 프론트엔드 개발자라면 반드시 알아야 할 '웹 접근성' 이야기

[Next.js] 나만의 학습 블로그 만들기#3-다국어 지원 (Feat.next-i18next)

오늘은 다국어 지원 가능한 기능 구현 과정을 적어보려고 한다. 원래는 댓글기능보다 먼저 구현하려고 했는데 ,, 갑자기 댓글기능 알아보다가 재밌어서 먼저 끝내버렸다,,,🙄 시작하기 전에 정말 어려웠다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 어려움이 많아서 기능 적용하는 데 시간이 꽤 걸렸다 ,,, 다국어 지원 기능 나는 영어랑 일본어를 추가해줬다 ! (해외취업도 관심 있기 때문에 나중에 이력서 낼 때 도움이 되지 않을까? ..🤭) 다국어 구현 방법 구현 방...

Jul 15, 20254 min read
[Next.js] 나만의 학습 블로그 만들기#3-다국어 지원 (Feat.next-i18next)

[Next.js] 나만의 학습 블로그 만들기#4 - 댓글 기능(Feat. Giscus)

댓글기능이 있으면 무조건 재밌을 거 같아서 넣어보려고 한다🤭 아무래도 개발에 관련된 학습 블로그라서 개발자들이 많을 것으로 예상했다 ! 그래서 깃허브 이슈 기반 댓글 시스템을 기반으로 기능을 구현해보려고 한다. 👉🏻 도움이 많이 된 블로그 ! 방법 Giscus | (https://giscus.app) utterances | (https://utteranc.es) 알아봤을 때는 이렇게 두 개가 유명하다고 한다. 그럼 각각의 특징과 장...

Jul 14, 20253 min read
[Next.js] 나만의 학습 블로그 만들기#4 - 댓글 기능(Feat. Giscus)

[React] React + localStorage로 하루에 한 번만 보이는 팝업 만들기

이번 프로젝트에 설문조사 배너를 제작했는데… 흠… 뭔가 별로라는 의견이 많았다! (팀원들 의견) 🙄 디자인이 문제일까? 해서 래퍼런스를 많이 찾아봤는데 … ! 일반 웹사이트는 설문조사 배너 자체를 안 만들어요,,,🤭 충격 그 자체 ( 난 아직 멀었음 ) 그리고 알아보니깐.. 설문조사 배너는 사용자 경험을 해치기 쉬워 잘 쓰이지 않는다고 한다! 그래서 나는 모달 팝업 방식으로 전환하기로 결정했고 실무에서 자주 사용하는 패턴으로 구현했다. 방...

Jul 9, 20253 min read
[React] React + localStorage로 하루에 한 번만 보이는 팝업 만들기
S

subin-dev-blog

27 posts

끊임없이 배우기 위해 노력합니다. 🌱