한 땀 한 땀 블로그 만들기 - 조회수 세기

2022. 07. 31.

조회수를 세어보자

문득 블로그에 글을 하나 둘 씩 쓰고 있지만, 나 외에 다른 사람들이 보는지 알 수 없었다. 내 블로그가 마치 맥북 기본 메모 앱이 되어버릴 것 같았다. 최초의 내가 블로그를 만든 이유는 Jekyll과 같은 정적 사이트 생성기로는 성에 안찼기도 하고, 한번쯤은 구현해보고 싶었던 뉴모피즘 디자인을 구현해보고 싶었던 것이다. 원하던 디자인의 블로그를 만들고 난 후 생각해보니 블로그는 결국 글을 쓰고 조회하는 것이 목적일텐데 내가 쓴 글을 사람들이 얼마나 볼 지 문득 궁금해졌다. 그래서 조회수를 세기로 했다.

인스턴스를 찾아서

조회수를 세기 위해 어떤 것이 필요할 지 생각해봤다. 현재 블로그는 정적 사이트로, 개발할 때 작성했던 마크다운 파일을 파일 시스템으로 읽어 GitHub Page에 빌드 후 올리는 방식이다. 즉 레포지토리에 저장된 파일은 매번 복사되어 GitHub Page 호스팅에 올라가기 때문에 포스트와 같은 방식으로 조회수를 저장할 순 없었다. s3 같은 스토리지를 사용할까 했지만 추후에 다른 기능이 추가된다면 데이터베이스가 유리해보였다. 이제 서버와 데이터베이스가 필요해졌다. 하지만 나는 트래픽이 적은(적다고 생각하는) 블로그를 위해서 24시간 켜져있는 별도의 PC를 구매하고 싶지 않았기 때문에 클라우드로 눈을 돌렸다.

클라우드 중 가장 먼저 생각난 것은 대중적인 AWS EC2 인스턴스였다. AWS에 계정을 새로 만들면 1년간 월 750시간을 무료로 사용할 수 있는 EC2를 제공하지만 여러 가지 변수로 과금될 수 있고 1년 후에는 무료가 아니게 된다는 것이 걸렸다. 그 다음 눈을 돌린 것은 Heroku 였다. 한번도 사용해보지 않았지만 블로그에 적당한 스펙의 Dynos를 무료로 지원한다. 게다가 Postgresql 데이터베이스도 지원한다. 그래서 Heroku를 이용해 서버와 데이터베이스를 구현하기로 정했다.

Dynos

Dynos(Heroku에서의 isolated, virtualized linux container)

서버 어플리케이션 만들기

가용한 서버가 준비됐으니 이제 서버 어플리케이션을 준비할 차례다. 최근에 NestJS에 관심이 생겼는데, 조회수를 세는 것이 NestJS를 사용해야 할만큼 통일성이 필요한 프로젝트는 아니었지만 시험 삼아 사용하기로 했다. NestJS의 셋업 경험은 굉장했다. cli로 nest project를 만들고(nest new project-name) CRUD Generator(nest g resource posts)로 보일러 플레이트를 뚝딱 만들었다.

이제 데이터베이스와 연결해야 하는데, 우선 로컬에서 postgresql을 설치하고(brew install postgresql), typeorm으로 DB와 동기화된 데이터를 관리한다.(yarn install @nestjs/typeorm typeorm pg) 이후의 일은 간단히 post를 id로 조회하는 API(findAll, findOne)를 구현하고, post의 views를 increament하는 API를 만들면 끝난다. 처음에는 post를 통째로 update하는 API로 만들었는데, 그러면 views를 임의로 0으로 만들거나 말도 안되는 숫자로 만들 수 있을 것 같아서 increment로 제한해서 구현했다. 코드는 Server Repo에서 확인할 수 있다.

근 2년만에 서버를 만들어봐서 SQL문에 세미콜론을 안붙이고 확인하다가 왜 데이터가 안쌓이는지 한참 헤메기도 하고, 로컬에 database 연결하는 법도 한참 헤맸던 것 같다. TypeORM의 repository.create는 단순히 entity를 만드는 함수인데, save를 호출하지 않고 왜 db에서 row가 create가 안되지 하고 헤매기도 했다. 그렇지만 안하던 걸 해보니까 즐거웠다. 새로운 경험은 언제나 즐겁다.

서버 어플리케이션을 무사히 준비한 후 Heroku Dynos에 띄웠다. Heroku는 배포도 굉장히 간단했는데, Heroku 대시보드에서 GitHub 레포와 연결한 후 Procfile만 정의해주면 알아서 clone한 후 Procfile에 정의된 명령어(yarn prod)를 실행해줬다. 이제는 클라이언트에서 views를 조회하고 업데이트만 해주면 된다.

클라이언트 기능 추가하기

우선 Views를 직관적으로 표현해줄 수 있는 png 파일을 하나 받아오고 count를 받아서 렌더하는 ViewCount.tsx 컴포넌트를 만들었다. 클라이언트에서 기능을 구현하는 것은 익숙하기 때문에 금방 만들었다. react-query를 이용해서 포스트 페이지에 접속하면 모든 포스트에 대한 views를 받아오고, 포스트 페이지에 접속하면 increment 요청을 보내도록 했다. 한가지 염려되는 점은 세션ID를 체크하지 않아서 한 페이지에서 새로고침을 마구마구 하면 views가 마구마구 올라간다. 지금은 큰 문제가 될 것 같지 않아서 추후에 추가할 계획이다.

ViewCount 컴포넌트

우측 하단의 컴포넌트

이대로 괜찮을까?

고민이 있었다. 현재 클라이언트에 저장된 posts 파일에서 가져온 데이터만으로 정적 생성을 하고 있고, 이제는 서버에서도 post 데이터가 받아온다. 서버의 post 데이터는 views 값만 있다. 데이터의 관리 포인트가 2곳이 되었다. 그리고 id 값을 md 파일의 metadata로 손수 정의해주지만 새로운 포스팅을 쓸 때 다른 파일의 id와 중복되게 작성하는 등 실수할 여지가 큰 구조다. 궁극적으로는 포스트와 관련된 데이터를 모조리 서버로 옮기고 정적 생성할 때 서버에서 모든 데이터를 받아오는 것이 좋아보인다. 하지만 당장 필요하지 않으니 id 값을 조심스럽게 적어주기로 결정했다.

후기

주말을 이용해서 간단한 기능을 구현했다. 서버 구현할 때 조금 헤맸지만 앞으로 서버가 필요한 기능이 추가된다면 금방 추가할 수 있을 것 같다. 그리고 NestJS의 온보딩 경험이 꽤 괜찮아서 조금 더 관심을 갖고 사용하려고 한다. 이제 포스트의 views를 측정할 수 있게 되었으니 다음에는 코멘트 기능을 추가해야겠다. 블로그와 함께 나도 성장해서 더 풍성한 블로그가 되었으면 좋겠다.

참고한 포스트