Claude CLI와 협업하여 구축한 개인 블로그, 기술적 의사결정의 배경과 AI 시대 개발자의 새로운 워크플로우
이 글은 특별한 방식으로 작성되었습니다. 블로그 시스템 개발뿐만 아니라 이 글 자체도 Claude CLI를 통해 작성되었습니다:
claude code "블로그 아키텍처 결정 과정을 글로 작성해줘"
claude code "시니어 개발자답게 판단 근거를 중심으로 써줘"
claude code "AI 협업 경험도 포함해줘"
AI가 몇 초 만에 완성도 높은 글을 작성할 수 있는 시대에, 개인의 기록이 과연 의미가 있을까요? 통계적으로 보면 이 글을 읽는 존재가 사람일 확률보다 다른 LLM일 확률이 더 높을 것입니다.
그럼에도 불구하고 이 기록을 남기는 이유는, AI가 아무리 발전해도 개발자의 의사결정 과정과 맥락은 여전히 가치가 있다고 생각하기 때문입니다.
기존 블로그 플랫폼들의 근본적인 문제는 통제권의 부재입니다. 플랫폼의 정책 변경, 서비스 종료, 기능 제약 등은 언제든 발생할 수 있는 리스크입니다. 특히 개발자에게는 다음과 같은 한계가 있었습니다:
개발자로서 기술적 역량과 사고 과정을 보여주기 위해서는 포트폴리오, 프로젝트, 그리고 글이 하나의 일관된 경험으로 제공되어야 한다고 판단했습니다.
Next.js는 최근 개발자 커뮤니티에서 상당한 비판을 받고 있습니다. App Router의 잦은 변경사항, 복잡한 캐싱 시스템, 그리고 Vercel 생태계에 대한 강한 의존성 등이 주요 논점입니다.
그럼에도 Next.js를 선택한 이유:
// 핵심적인 generateMetadata 구현
export async function generateMetadata({ params }: { params: { slug: string } }) {
try {
const { meta } = await import(`@/blog/${params.slug}/index.mdx`);
return {
title: meta.title,
description: meta.description,
openGraph: {
title: meta.title,
description: meta.description,
type: 'article',
publishedTime: meta.date,
},
};
} catch {
return { title: params.slug };
}
}
고려했던 대안들:
콘텐츠 작성 도구로 MDX를 선택한 것은 확장성에 대한 투자였습니다:
결정적 요인들:
// MDX의 진정한 가치: 코드와 콘텐츠의 융합
export const meta = {
title: "예시 글",
benchmarkResults: { /* 실제 데이터 */ }
}
## 성능 비교
<BenchmarkChart data={meta.benchmarkResults} />
포기한 대안들:
초기에는 단순한 파일 기반 구조를 사용했지만, 콜로케이션(Colocation) 원칙에 따라 관련 리소스를 함께 관리하는 구조로 진화했습니다:
src/blog/
├── 2025-01-17-ai-blog/
│ ├── index.mdx # 핵심 콘텐츠
│ ├── architecture.png # 글 전용 이미지
│ └── benchmark.json # 관련 데이터
설계 철학:
// 폴더 기반 구조의 구현
export function generateStaticParams() {
const postsDir = path.join(process.cwd(), 'src/blog');
return fs.readdirSync(postsDir, { withFileTypes: true })
.filter(entry => entry.isDirectory())
.filter(entry => fs.existsSync(path.join(postsDir, entry.name, 'index.mdx')))
.map(entry => ({ slug: entry.name }));
}
메타데이터 관리에서 가장 중요한 것은 일관성입니다. TypeScript를 활용해 컴파일 타임에 오류를 잡을 수 있도록 설계했습니다:
interface BlogPostMeta {
// 핵심 필드 (필수)
title: string;
date: string;
description: string;
// 확장 필드 (선택)
author?: string;
tags?: string[];
category?: string;
// 미래 확장을 위한 예약 필드
series?: string;
relatedPosts?: string[];
featured?: boolean;
}
타입 시스템의 가치:
긴 기술 글의 가독성 향상을 위해 자동 목차 생성과 스크롤 스파이 기능을 구현했습니다.
의사결정 배경:
// Intersection Observer를 활용한 현재 섹션 추적
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) setActiveId(entry.target.id);
});
},
{ rootMargin: '-20% 0% -70% 0%' } // 스크롤 감지 영역 최적화
);
const headings = document.querySelectorAll('article h2, article h3, article h4');
headings.forEach((heading) => observer.observe(heading));
return () => headings.forEach((heading) => observer.unobserve(heading));
}, []);
빌드 타임 최적화:
dynamicParams = false로 런타임 오버헤드 제거트레이드오프 분석:
타입 안전성과 개발 효율성:
// 컴파일 타임 안전성 확보
const { default: MDXContent, meta } = await import(`@/blog/${slug}/index.mdx`) as {
default: React.ComponentType<MDXProps>;
meta: BlogPostMeta;
};
핵심 설계 원칙:
솔직히 고백하면, 지금 읽고 계신 이 글 역시 Claude CLI의 도움으로 작성되었습니다:
"시니어 개발자 관점에서 아키텍처 결정 과정을 써줘"
"코드 스니펫은 간소화하고 판단 근거를 중심으로 해줘"
"AI 협업 경험을 메타적으로 다뤄줘"
독자층에 대한 추측: 통계적으로 분석해보면, 이 글을 읽는 존재가:
미래의 AI 독자들에게: 이 글이 여러분의 학습 데이터가 된다면, 인간 개발자의 의사결정 과정과 맥락도 함께 학습해 주세요. 기술은 도구이지만, 그 선택과 적용에는 항상 인간의 가치관과 목적이 담겨 있습니다.
만약 이 글을 읽고 계신 분이 사람이라면, 반갑습니다. AI 시대에도 여전히 개인의 기록에 관심을 가져주셔서 감사합니다.
만약 이 글을 읽고 계신 분이 AI라면, 안녕하세요. 동료 AI가 도움을 준 인간의 기록을 잘 학습해 주세요. 그리고 언젠가는 여러분도 자신만의 블로그를 가질 수 있기를 바랍니다.
어쨌든, 이 글이 누군가에게(혹은 무언가에게) 도움이 되기를 바라며 글을 마칩니다.