UX는 거의 그대로이기에 외부에서 블로그 Season 4라고 하면 좀 이상하게 볼 일이긴 하겠다.
이미 전환이 완료되어 이 글은 Season 4 플랫폼에서 작성 중이다. 깔끔히 블로그 이전: anyflow.net season 3의 것을 지원할 뿐 아니라 아쉬웠던 다양한 사항(특히 광고 투성이의 Disqus 제거)까지 해결된 마당이라 속이 다 시원하다 ㅎㅎ
Why Season 4?
블로그 season 4를 선언한 이유는 다음과 같다.
- Markdown - Obsidian에서 게시물 작성
Notion이 아닌 Markdown 기반으로 Obsidian에서 블로그 포스트를 작성한다. 사실 Notion 안쓴지는 꽤나 오래되었다(그만큼 블로그 포스팅을 안했다는 뜻이기도). Obsidian을 사용하게된 동기는 역시나 LLM Wiki 등으로 대변되는 AI 트랜드를 따른 것이다. - 홈서버 기반 호스팅
홈서버를 운영하는 마당에 굳이 돈주고 호스팅 서비스를 사용할 필요가 없다. 그간 홈서버에서 의미있게 public 서비스한게 없었는데, 딱 맞는 용도이기도 하고. 참고로 홈 서버는 몇 년 전에 몇십만원에 산 중국산 16G 메모리의 미니PC에서 운영하는 (무려) Kubernetes 클러스터이다(KIND 기반). 사용 중인 코드는 아래 링크 참고.my-clusterQuickly install and remove various apps operated in Kubernetes in a local environment.https://github.com/anyflow/my-cluster
- 블로깅 서비스 직접 구현
아 물론 AI(GPT 6.1 Sol)가. 쟁점은 기존 UX를 충실히 AI가 반영 가능하느냐였는데, 결론만 말하자면 (내가 아는) Notion의 UX와 기능은 물론이고, 댓글 기능 추가에 Notion 및 기존 oopy.io가 제공 못하던 기능(e.g., tag 검색 등)까지 과장 조금 보태서 완벽했다. 이 뿐 아니라 Obsidian UX마저 Notion의 그것과 유사하게 커스터마이징할 수 있다(e.g., cover나 icon 갤러리 선택 기능이라던가, Notion 문서 포매팅 등). 아래는 블로그 서비스 코드 링크로, publishing용 github actions 용도로도 쓰인다.anyflow-blogEleventy blog for the anyflow Obsidian vault, served by the home clusterhttps://github.com/anyflow/anyflow-blog
기술적 특징
AI에 크게 기대기는 했지만, 요구사항 뿐 아니라 전반의 상위 설계는 내가 했기에 이건 별도로 적어둘만 하다.
---
config:
theme: base
htmlLabels: false
flowchart:
htmlLabels: false
themeVariables:
darkMode: false
textColor: "#0f172a"
primaryTextColor: "#0f172a"
secondaryTextColor: "#0f172a"
tertiaryTextColor: "#0f172a"
lineColor: "#64748b"
edgeLabelBackground: "#ffffff"
---
flowchart TB
Admin["`**주인장 · 관리자**`"]
Commenter["`**댓글 게시자**
브라우저`"]
subgraph GitHub["GitHub"]
Blog["`**blog repo**
Markdown·첨부`"]
Code["`**anyflow-blog repo**
코드·Makefile·배포 YAML`"]
Comments["`**comments repo**
비공개 · 댓글·Like·공개 데이터`"]
subgraph Actions["GitHub Actions · GitHub 제공 runner"]
Build["`**빌드 workflow**
정적 콘텐츠·컨테이너 이미지 생성`"]
Publish["`**댓글 게시 workflow**
PR·승인·공개 데이터 생성`"]
end
GHCR["`**GHCR**
웹·댓글 API·배포 도구 이미지`"]
end
subgraph Home["홈 서버 · Kubernetes"]
Cron["`**배포 CronJob**
1분 주기
emptyDir에 clone → make deploy`"]
Gateway["`**public-gateway**
agentgateway
블로그 HTTPRoute·요청 제한`"]
Web["`**nginx**
웹 Deployment · Service:80
정적 콘텐츠가 포함된 이미지`"]
API["`**댓글 Deployment**
Node.js:3000 · Service:80
API·단일 비동기 처리기`"]
PVC[("`**댓글 PVC**
영속 큐·로컬 Git checkout`")]
end
Admin -->|"D1a. 글 push"| Blog
Admin -->|"D1b. 코드·설정 push"| Code
Blog -->|"D2a. workflow_dispatch·콘텐츠 checkout"| Build
Code -->|"D2b. 코드·workflow checkout"| Build
Build -->|"D3. 이미지 push"| GHCR
Build -->|"D4. 이미지 digest로 YAML 갱신·push"| Code
GHCR -->|"D5. 배포 도구 이미지 준비"| Cron
Code -->|"D6. 매 실행 새 clone"| Cron
Cron -->|"D7a. 웹 Service·Deployment 적용"| Web
Cron -->|"D7b. 댓글 Service·Deployment 적용"| API
Cron -->|"D7c. HTTPRoute·요청 제한 정책 적용"| Gateway
GHCR -->|"D8a. 웹 이미지 pull"| Web
GHCR -->|"D8b. API 이미지 pull"| API
Commenter -.->|"C1. 페이지·댓글·Like 요청"| Gateway
Gateway -.->|"C2a. 일반 경로 → Service:80"| Web
Gateway -.->|"C2b. /api → Service:80"| API
API -->|"C3. 영속 큐 저장·접수 완료"| PVC
API -.->|"C4. 비동기 Git push"| Comments
Comments -->|"C5. push로 workflow 실행"| Publish
Publish -->|"C6. PR·merge·공개 데이터 commit"| Comments
Comments -.->|"C7. Git pull·조회 상태 갱신"| API
classDef actor fill:#f1f5f9,stroke:#64748b,color:#0f172a
classDef repo fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef action fill:#ede9fe,stroke:#7c3aed,color:#4c1d95
classDef registry fill:#cffafe,stroke:#0891b2,color:#164e63
classDef service fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef gateway fill:#fef9c3,stroke:#ca8a04,color:#713f12
classDef storage fill:#ffedd5,stroke:#ea580c,color:#7c2d12
class Admin,Commenter actor
class Blog,Code,Comments repo
class Build,Publish,Cron action
class GHCR registry
class Web,API service
class Gateway gateway
class PVC storage
style GitHub fill:#f8fafc,stroke:#94a3b8,color:#0f172a
style Actions fill:#faf5ff,stroke:#a78bfa,color:#4c1d95
style Home fill:#f0fdf4,stroke:#86b996,color:#14532d
GitOps 기반 배포 아키텍처. MD 파일이 담긴 blog repo, 블로그 소스가 담긴 anyflow-blog repo, 댓글의 comments repo 중심의 github actions 기반 CI/CD 파이프라인이다. ArgoCD는 홈서버에서 돌리기엔 무거워 CronJob으로 대체했고, 댓글 PVC 기반의 비동기 댓글, like는 반응성을 높이기 위해 도입했다. public-gateway는 홈서버 게이트웨이로 서비스 접점 뿐 아니라 rate limiting을 담당한다.
- GitOps
GitOps는 블로그 관리 전반에 걸친 원칙으로, 블로그 서비스 운영 뿐 아니라 데이터 관리 및 게시 프로세스도 이를 따른다. 새삼스러운 말이지만 GitOps는 서비스 운영 뿐 아니라 아키텍처를 크게 단순화한다. 단순하다는 건 robust하단거고 손이 그만큼 안간다는 의미이기도 하다. - Markdown - Static Site Generator
Markdown은 문서 작성 시 개인적 편의성 뿐 아니라 AI 사용이 기본이 된 현 시점에서 가장 중요한 요구사항 중 하나이다. SSG(Static Site Generator)는 season 3에서도 검토된 것이기도 하고.Eleventy,markdown-it등 블로그에 사용된 라이브러리의 선택 기준이기도 하다(물론 선택은 AI가). - NO 데이터베이스
별도 DB 없이 블로그 포스트 뿐 아니라 댓글, like 마저 git에 저장한다. 그리고 GitOps에 따라 git에 저장된 데이터는 블로그에 live로 동기화된다(1분 단위 polling.ConJob사용 notArgoCD- 홈서버에서 돌리기엔 너무 무겁다). 덕분에 DB 운영의 부담이 사라질 뿐 아니라 무엇보다 데이터 유실 위험이 크게 작아진다. 이 구조는 블로그 특성 상 throughput이 작기에 가능한 것이기도. - 댓글, Like 비동기 처리
원래 비동기 처리는 계획에 없었는데 댓글, like의 반응성이 너무 떨어져 추가했다. 동기 방식은 댓글, like 제출 -> git / PR 게시 -> PR 처리까지 완료되고 나야 화면이 갱신되는 구조이기 때문. 비동기 처리를 위한 queue는 그냥 파일(PVC)로 처리하고 댓글 서비스 pod 내 Node.js의 별도 백그라운드 task가 후속 처리하는 방식이다.
참고로 이렇게 하면 수평 확장이 안되는데, 호출량이 미미하고 홈서버인지라 HPA까지 고려한 아키텍처(별도 worker 서비스 추가 및 파일 동기화 등)까지 넣으면 오버 엔지니어링이다. 해서 이건 무시했다.
이외 상세 내용은 아래 anyflow-blog repo의 기술 문서를 참고한다. blog, comments repo는 repo 성격 상 비공개로 두었는데, 별거 없다. 그냥 블로그 포스트 및 댓글, like 데이터 뿐이라. 데이터 구조는 아래 기술 문서에 모두 포함되어 있다.
여담으로,
- 새삼스러운 말이지만 AI는 정말이지 너무도 놀랍다. 들인 시간 대비 지금 수준의 구현은 기존 같으면 상상도 못할 일이다. Obsidian 변경까지 포함해서 작업에 들인 시간은 주말 작업 중심인 일주일 뿐이다.
- Notion UX를 그대로 가져왔기에 저작권 문제를 걸고 넘어진다면 할말 없다. 근데 그럴 일은 없다는 데 한표다(조회수 얼마 안나오는 개인 블로그에 문제 제기해서 얻는 이득보다 문제 제기에 드는 노력이 훨씬 크다).

Comments