---
title: "티스토리를 떠나 블로그를 직접 만들었다"
description: "관리가 불편해서 옮겼고, 댓글과 조회수까지 전부 무료 한도 안에서 직접 구현한 기록."
author: "유니얼"
pubDate: 2026-09-07
category: "blog"
tags: ["회고", "Cloudflare"]
canonical: https://blog.unialgames.com/posts/hello-world/
---
첫 글입니다. 이 파일은 글쓰기 형식을 보여주는 견본이기도 하니, 구조를 확인한 뒤
내용을 지우고 쓰셔도 됩니다.

## 왜 옮겼나

티스토리는 글을 쓰는 것 자체보다 **글을 올리는 과정**이 번거로웠습니다. 에디터를
열고, 폼을 채우고, 이미지를 붙이고, 카테고리를 고르는 절차가 매번 반복됩니다.
쓰고 싶은 게 있어도 그 절차가 떠올라 미루게 되는 게 가장 큰 문제였습니다.

지금 구조에서는 마크다운 파일 하나를 만들고 `git push` 하면 끝입니다.

## 무엇으로 만들었나

| 기능 | 도구 |
| --- | --- |
| 사이트 | Astro (정적 생성) |
| 배포 | Cloudflare Workers + 정적 에셋 |
| 댓글 · 조회수 | Cloudflare D1 |
| 방문자 분석 | Cloudflare Web Analytics |

도메인 비용을 빼면 전부 무료 한도 안에서 돌아갑니다.

## 조회수를 세는 방법

조회할 때마다 행을 새로 넣으면 D1 무료 한도(하루 쓰기 10만 행)를 금방 씁니다.
그래서 글마다 한 행만 두고 값을 올리는 쪽을 택했습니다.

```sql
INSERT INTO post_views (slug, views, updated_at)
VALUES (?, 1, ?)
ON CONFLICT(slug) DO UPDATE SET
  views = post_views.views + 1,
  updated_at = excluded.updated_at
RETURNING views;
```

중복 카운트는 두 겹으로 막습니다. 브라우저에서 `localStorage` 로 24시간에 한 번만
요청을 보내고, 그걸 지우고 새로고침하는 경우를 대비해 서버에서도 IP 해시 기준으로
1분에 한 번만 인정합니다.

> 클라이언트 제한은 사용자가 언제든 우회할 수 있습니다. 비용이 걸린 제한은
> 반드시 서버에도 있어야 합니다.

## 앞으로

읽은 것, 만든 것, 막혔다가 푼 것을 여기에 남길 생각입니다.