HomeBlogProjectsTagContact
GitHub
©-3 kd02109 All rights reserved.

Next js 블로그 조회수 기능 만들기 with Supabase

  • project
  • nextJs
May 14, 2024

블로그 조회수 기록하기

지금까지 블로그를 운영해서 아쉬운 점이 각 글의 조회수를 알 수 없었다는 점입니다. GA를 활용해서 지금까지 누적 방문자와 각 path 별 조회수를 확인할 수 있었지만 명시적이지 않았고 직관적으로 어떤 글이 방문자들의 관심을 끌었는지 알기 쉽지 않았습니다. 따라서 각 글마다 조회수를 기록하기로 했습니다.

조회수 기록을 위한 로직

  • DB를 어떻게 대체할 것인가?

그렇다면 해당 조회수를 기록하기 위해 무엇이 필요한지를 검토해야 했습니다. 모든 사용자들에게 동일한 조회수를 보여주어야 하기에 localStorage는 한계가 있었습니다. 따라서 별도의 DB를 연결해야 할 필요가 있었습니다. 제가 고민한 서비스는 쉽게 DB를 사용하게 해주는 서비스를 제공(Baas)하는 Firebase와 Supabase 중에 고민했고 이중 Supabase를 선택했습니다.

Supabase를 선택한 이유는 Supabase가 Postgres 확장 프로그램으로 관계형 db를 지원한다는 점이 좋았습니다. 또한 가격 정책에서도 매력적이었습니다. Firebase는 읽기, 쓰기, 삭제에 대해 요금을 부과하지만 Supabase는 저장된 데이터 양에 따라 요금이 부과되고 API 요청과 사용자 인증 횟수에 대한 제한이 없다는 것이 매력적이었습니다. 두 서비스 모두 쉽게 FE에서 적용할 수 있다는 점이 가장 큰 매력이었습니다. (Supabase시작 및 설정 방식은 공식 문서에서 쉽게 확인할 수 있습니다.)

https://supabase.com/docs

  • 조회수를 어떻게 기록할 것인가?

사용자가 해당 글을 조회하는 방법은 두 가지가 있습니다. 링크를 타고 접속하는 방법, 블로그에서 해당 버튼을 클릭하고 접속하는 방법입니다. 해당 두 가지 방법으로 블로그 글에 접속할 때 마다 조회 수를 1씩 증가시켜 달라는 요청을 보낼 수 있습니다.

이때 해당 방법을 사용하게 되면, 조회 수를 증가시킬 수 있지만, 악의적으로 조회수를 증가시키는 방법에 대해서 대응을 하지 못합니다. 네이버 블로그, 카카오 블로그, 티스토리 블로그에서 어떻게 조회수 관리를 하는지 찾아보았습니다.

네이버

조회수 이미지

네이버는 해당 방문자의 시간을 확인해서 측정을 하고 있었습니다.

카카오 티스토리

조회수 이미지

티스토리는 조회수와 방문자를 별도로 구분해서 허위 조회수를 구분하고 있었습니다.

저는 사용자가 해당 글을 읽기 위해 방문한 여부를 확인하고 싶었으며, 새로고침 등의 방법으로 허위 조회를 증가시키고 싶지 않았습니다. 블로그에서 별도의 로그인 기능을 만들지 않았기 때문에, 로그인 토큰을 통해 확인하기에는 어려움이 있었습니다. 따라서 최초 유저가 해당 글을 보기 위해 들어왔을 때, 조회 수를 증가시키면서 동시에 세션 쿠키를 발급하였습니다. 세션 쿠키는 브라우저 종료시와 함께 사라지므로, 브라우저 종료를 판단 기준으로 삼아 제가 작성한 글을 참고 하기 위해 방문했는지 여부를 파악할 수 있다고 보았습니다.

브라우저와 쿠키에 대해 아는 사용자라면 충분히 쿠키를 조작해서 조회수를 증가시킬 수 있지만 최소한의 대비책을 만들고자 했습니다.

const onClick = async () => {
  const slug = ids[ids.length - 1].trim();
  if (!getCookieClient(slug)) {
    await supabaseIncrement(slug);
    makeCookieClient(slug);
  }
};

다음과 같이 해당 블로그 글에 접속할 시에 현재 해당 post와 연관된 session cookie가 없다면, supabase에 조회수를 1 증가하는 요청과 세션 쿠키를 만드는 함수를 호출하도록 했습니다.

또한 버튼을 클릭하지 않고 바로 페이지로 접근하는 방식에 대해서도 useEffect를 활용해서 다음과 같이 대처를 했습니다.

// 클라이언트 컴포넌트
useEffect(() => {
  const slugs = post?.url.split('/');
  if (slugs) {
    const slug = slugs[slugs.length - 1];
    makeCookieClient(slug);
  }
}, [post?.url]);
// page
const isCookie = await getCookieServer(slug as string);
 
if (!isCookie) {
  await supabaseIncrement(slug as string);
}

Reference

집계 기준 : Creator Advisor 고객센터

Supabase Docs

Content Table
  • 블로그 조회수 기록하기
  •    - 조회수 기록을 위한 로직
  • Reference

Nextjs-blog

2024.05.14
6 / 7