이 블로그를 만든 이유와 설계 결정
왜 만들었나
나는 배운 걸 정리하는 일을 정말 못 한다. 그래서 같은 걸 매번 새로 찾아보고 있었다. 요즘은 대부분 Claude Code와 대화하면서 개발하니까, 대화에서 배운 내용이 그대로 노트가 되면 정리를 안 해도 된다.
여기에 목표를 하나 더 붙였다. 실제 서버에 배포까지 해 보는 것. 회사에서는 만들어진 배포 환경만 써 봤기 때문에, 시스템이 실제로 어떻게 올라가는지와 그 과정에 쓰이는 도구를 직접 겪어 보고 싶었다.
Note
한 줄 요약: "Claude Code에 /blog라고만 하면 배운 게 블로그에 올라가고, 그 블로그를 직접 배포한다."
목표와 하지 않을 것
| 구분 | 내용 |
|---|---|
| 목표 | 대화 → 노트 자동 정리, 실제 도메인으로 배포, 배포 과정의 개념 학습 |
| 하지 않을 것 | 블로그에서 글 직접 쓰기가 주 용도가 되는 것(편집은 수정용), 서버에서 AI API 호출 |
| 실패 조건 | 비밀값이 공개 노트에 올라가는 것, 정리가 귀찮아서 결국 안 쓰게 되는 것 |
Important
노트는 검토 없이 바로 공개된다. 그래서 비밀값 차단을 두 겹(내 PC + 서버)으로 했다.
전체 구조
graph LR
A["나: /blog"] --> B["Claude Code + /blog 스킬"]
B -->|"blog.py: 로컬 비밀값 검사"| C["노트 API (토큰 인증)"]
C -->|"서버 비밀값 검사"| D[("PostgreSQL")]
D --> E["공개 블로그 화면"]
F["방문자"] --> E
- AI 분석은 내 PC의 Claude Code가 한다. 서버는 받은 마크다운을 저장하고 보여주기만 한다. 그래서 서버에 Claude API 키가 필요 없다.
- Claude Code는 기존 노트 목록과 본문을 읽은 다음, 새 노트를 만들지 기존 노트를 보강할지 직접 판단한다.
설계 결정과 이유
기술 스택: Spring Boot + Thymeleaf + PostgreSQL
블로그 하나에 Spring Boot는 과해 보일 수 있다. 하지만 내가 원래 자바·스프링 개발자라서 배포 학습에 집중하려면 익숙한 스택이 낫다고 봤다. 처음 보는 프레임워크와 처음 하는 배포를 동시에 하면 어디서 막혔는지 구분하기 어렵다.
| 선택 | 이유 |
|---|---|
| Thymeleaf (서버 렌더링) | 프런트엔드 빌드 도구가 따로 필요 없다 |
| PostgreSQL | 버전 이력, 댓글, 검색까지 한 DB로 해결된다 |
| Flyway | 스키마 변경을 코드로 남긴다 |
| commonmark-java | 표, 체크리스트, 강조 박스 같은 GFM 기능 지원 |
노트 구조: 날짜순 블로그가 아니라 주제별 위키
날짜순으로 쌓으면 같은 주제가 여러 글로 흩어진다. 나는 "다시 찾아보는" 용도로 쓰려는 거라서 주제별로 묶이고 계속 보강되는 위키형이 맞다. 왼쪽 주제 트리, 가운데 본문, 오른쪽 목차.
URL은 숫자 id가 아니라 slug
DB의 키는 여전히 숫자 id다. URL과 API에서만 flyway로-db-스키마-관리하기 같은 slug를 쓴다. Claude Code가 노트를 이름으로 다루기 쉽고, 주소만 봐도 내용을 알 수 있다. 너무 길어지지 않게 최대 60자로 자른다.
비밀값 치환
API 키, 비밀번호, 회사명, 사내 코드는 [API_KEY], [PASSWORD], [COMPANY], [INTERNAL_CODE] 같은 자리표시자로 바꿔서 올린다.
- 1차: Claude Code가 쓰면서 치환
- 2차:
blog.py가 보내기 직전에 정규식 + 내 PC에만 있는 회사 금지어 목록으로 검사 - 3차: 서버가 같은 정규식 파일로 다시 검사하고, 걸리면 422로 거절
Tip
정규식 파일 하나를 Java 서버와 Python 스크립트가 같이 읽는다. 규칙이 두 군데로 갈라지지 않는다.
로그인: Google 로그인
관리자 편집 화면과 댓글에 로그인이 필요하다. 비밀번호를 직접 저장하는 대신 Google 로그인(OAuth2)을 쓰기로 했다. 관리자는 이메일로 구분한다. OAuth 흐름을 직접 붙여 보는 것 자체도 학습 목표다.
호스팅과 비용: AWS Lightsail
| 항목 | 월 비용(대략) |
|---|---|
| Lightsail 1GB 인스턴스 | 약 7달러 |
| 도메인 | 연 단위 결제를 월로 환산 |
| 합계 | 약 1만 1천~1만 2천 원 |
이미지는 S3에 따로 두지 않고 Lightsail 디스크에 저장해서 추가 비용이 없게 했다. 백업만 S3로 보낸다.
화면: 기술 문서형
GitHub 문서나 Docusaurus 같은 단정한 스타일. 로고는 이모지 대신 saip. 텍스트 워드마크로 했다(이모지는 너무 AI가 만든 느낌이라서).
진행 단계
- 계획 1: 로컬 블로그 +
/blog스킬 - 계획 2: Google 로그인, 관리자 편집기, 댓글, 버전 복원, 이미지
- 계획 3: Docker, Lightsail, 도메인, HTTPS(Caddy), GitHub Actions 자동 배포, S3 백업
진행하면서 겪은 것
- 환경변수가 안 보임: 토큰을 사용자 환경변수로 설정했는데 새 Claude Code 세션이 인식하지 못했다. 프로그램은 켜질 때 환경변수를 복사해 가므로, Claude Code만이 아니라 터미널 앱 전체를 다시 켜야 했다.
- 한글 slug 요청 실패: HTTP 요청 줄은 ASCII만 허용해서 한글 slug를 그대로 넣으면 터졌다. 퍼센트 인코딩(
urllib.parse.quote)으로 해결. - 토큰 유출 경로: Python
urllib은 리다이렉트를 따라갈 때Authorization헤더를 다른 호스트로도 넘긴다. 리다이렉트를 거부하고,localhost가 아니면 https만 허용하도록 막았다.
댓글 0
로그인하고 댓글 달기