이 블로그는 Claude Code가 만들었다

블로그를 왜 직접 만드나
글을 쓸 곳은 많다. Medium, Tistory, velog, 어디든 가입하면 바로 쓸 수 있다. 그런데 개발자로서 블로그를 "직접 만든다"는 건 다른 의미가 있다. 내가 원하는 디자인, 내가 원하는 구조, 내가 원하는 기능. 남의 플랫폼에 맞추는 게 아니라, 내 글에 맞는 공간을 만드는 것이다.
문제는 시간이다. 블로그를 만드느라 정작 글을 못 쓰면 본말이 전도된다. 그래서 대부분은 플랫폼을 선택하고, 커스터마이징을 포기한다. 합리적인 선택이다.
하지만 Claude Code와 함께라면 이야기가 좀 다르다.
시작: Vercel 템플릿
출발점은 Vercel의 blog-starter-kit이었다. Next.js 기반의 깔끔한 블로그 템플릿이다. Markdown 파일을 _posts/ 디렉토리에 넣으면 자동으로 포스트가 된다. 기본 기능은 충분했지만, 디자인이 Vercel 브랜딩 그대로였고, 다크모드도 없었고, 태그 시스템도 없었다.
원하는 건 명확했다:
- shadcn/ui + Tailwind CSS 기반의 깔끔한 디자인
- 다크모드 지원
- 태그 시스템 (글 분류)
- 코드 하이라이팅 (기술 블로그니까)
- Vercel 템플릿 흔적 완전 제거
Claude Code에게 맡기다
계획을 세우고, Claude Code에게 실행을 맡겼다. 6단계로 나눠서 진행했다.
Phase 1 — 기반 설정. shadcn/ui의 CSS 변수 시스템을 도입하고, Tailwind 설정을 재작성하고, 글로벌 스타일을 새로 잡았다. cn() 유틸리티 함수 하나로 조건부 클래스 적용이 깔끔해졌다.
Phase 2 — 컴포넌트. Button, Card, Badge, Avatar, Separator. shadcn/ui의 컴포넌트를 하나씩 생성했다. Radix UI 기반이라 접근성이 기본으로 따라온다.
Phase 3 — 블로그 컴포넌트. 헤더, 푸터, 포스트 카드, 테마 토글. 블로그에 필요한 컴포넌트들을 새로 만들었다. 텍스트 중심의 포스트 리스트는 tailwind-nextjs-starter-blog의 스타일을 참고했다.
Phase 4 — 페이지. 홈, 포스트 상세, About, 태그 목록, 태그별 필터. 각 페이지를 새 컴포넌트에 맞게 재작성하고, 마크다운 파이프라인도 교체했다.
Phase 5 — 정리. 더 이상 필요 없는 파일 15개를 삭제했다. 샘플 이미지, 옛 컴포넌트, Vercel 브랜딩 요소들.
Phase 6 — 검증. 빌드 성공 확인, 모든 페이지 동작 확인.
이 전체 과정에서 내가 한 건 방향 설정과 의사결정이었다. "hero post 구분 없이 텍스트 리스트로", "네비게이션은 Blog, Tags, About 세 개", "단일 저자 블로그니까 카드에 저자 표시 불필요" 같은 판단들. 코드는 Claude Code가 작성했다.
재미있었던 버그: 한글과 볼드체
블로그를 만들고 첫 글을 올렸는데, 볼드체가 깨져 있었다. <strong>"텍스트"</strong>는 같은 패턴에서 **가 그대로 노출되는 문제였다.
원인을 찾아보니 CommonMark 스펙의 특성이었다. 마크다운에서 **로 볼드를 닫으려면 "right-flanking delimiter"로 인식되어야 하는데, 닫는 ** 바로 앞에 따옴표나 괄호 같은 구두점이 오고, 바로 뒤에 한글이 오면 이 조건을 만족하지 못한다.
영어 환경에서는 거의 발생하지 않는, CJK 문자 특유의 엣지 케이스였다. 해결은 마크다운 파싱 전에 문제 패턴을 <strong> HTML 태그로 전처리하는 방식으로 했다.
function fixCjkEmphasis(markdown: string): string {
return markdown.replace(
/\*\*(.+?)\*\*(?=[\u3131-\u318E\uAC00-\uD7A3])/g,
"<strong>$1</strong>",
);
}한국어 블로그를 마크다운으로 운영하는 사람이라면 한 번쯤 만날 수 있는 함정이다.
커버 이미지 자동 생성
기술 블로그에 커버 이미지가 필요한가? 솔직히 없어도 된다. 하지만 있으면 확실히 나아 보인다. 문제는 매번 직접 만들기 귀찮다는 것이다.
그래서 자동화했다. Replicate API로 Seedream 4.5 모델을 호출해서, 글의 제목과 요약을 기반으로 커버 이미지를 생성한다. 프롬프트는 블로그에 최적화했다.
Minimal, editorial illustration style. Muted tones, soft gradient background.
No text, no letters, no words, no watermark, no UI elements.
Clean composition with generous negative space.
Suitable as a blog hero image at 16:9 aspect ratio.
미니멀하고, 텍스트 없고, 여백이 충분한 이미지. 블로그 히어로 이미지로 딱이다.
여기서 한 발 더 나아가서, GitHub Actions로 파이프라인을 만들었다. _posts/ 디렉토리에 새 글을 push하면, 커버 이미지가 없는 포스트를 자동으로 감지해서 이미지를 생성하고, 커밋까지 해준다.
on:
push:
branches: [main]
paths:
- "_posts/**"글만 쓰면 이미지는 알아서 따라온다.
메타적인 감상
이 글도 Claude Code와 함께 쓰고 있다. 이 블로그를 만든 도구로, 이 블로그에 대한 글을 쓰고 있는 셈이다.
돌이켜보면, 전체 과정에서 가장 시간이 걸린 건 코딩이 아니었다. "어떤 블로그를 만들 것인가"를 결정하는 과정이었다. 디자인 방향, 기능 범위, 사용할 기술 스택. 이런 의사결정은 여전히 사람의 몫이다.
하지만 결정이 나면, 실행은 빠르다. shadcn/ui 컴포넌트를 세팅하고, 페이지를 재작성하고, 마크다운 파이프라인을 교체하고, 태그 시스템을 추가하고, 커버 이미지 자동 생성까지 — 이 모든 게 하나의 대화 안에서 이루어졌다.
예전이었으면 주말을 통째로 투자하고도 절반밖에 못 했을 작업이다. 지금은 아이디어가 떠오른 그 시점에 바로 실행할 수 있다. 그리고 그 실행의 결과물 위에 이렇게 글을 쓰고 있다.
블로그를 만드느라 글을 못 쓰는 문제. 해결됐다.