엔지니어링 6분 소요

Astro 4.8에서 블로그 시스템 완벽 구현 - AI 번역 및 E2E 테스트를 포함한 포괄적인 접근 방식

Astro Content Collections을 기반으로 한 고성능 블로그 시스템 구현 기록. Google Gemini API를 이용한 AI 자동 번역, Playwright E2E 테스트, 속도 제한 기능 등 포괄적인 개발 과정을 설명

Terisuke
Share this article:

개요

최근 개발에서 Astro 4.8.7을 기반으로 포괄적인 블로그 시스템을 구현했습니다. 이 시스템은 단순한 Markdown 표시뿐만 아니라 AI 자동 번역, E2E 테스트, 본격적인 운영을 염두에 둔 기능군을 포함하는 현대적인 기술 블로그 플랫폼입니다.

더 이상 WordPress에서 위젯과 씨름하는 시대는 끝났습니다. 이제 AI로 번역시키고, 테스트도 자동화해서, 개발자는 잠만 자면 됩니다 (농담).

기술 스택

핵심 기술

  • Astro 4.8.7: 아일랜드 아키텍처를 통한 고속 SSG
  • TypeScript: 타입 안전한 개발 환경
  • Tailwind CSS: 유틸리티 우선 스타일링
  • Alpine.js 3.14.0: 경량 프론트엔드 JavaScript

블로그 전용 기능

  • Content Collections: Astro의 타입 안전한 콘텐츠 관리
  • Google Gemini API: AI 자동 번역 엔진
  • Playwright: E2E 테스트 자동화
  • remark-link-card-plus: 리치 링크 카드 생성

구현의 핵심 아키텍처

1. Content Collections 설계

기존의 Markdown 파일 관리에서 Astro의 Content Collections를 채택했습니다. 더 이상 “어라? 이 frontmatter 작성법이 맞았나?” 하고 고민할 필요가 없습니다.

// src/content/config.ts
const blogCollection = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    category: z.enum(['ai', 'engineering', 'founder', 'lab']),
    tags: z.array(z.string()).default([]),
    lang: z.enum(['ja', 'en']).default('ja'),
    featured: z.boolean().default(false),
  }),
});

장점:

  • 빌드 시 타입 검증으로 메타데이터 오류 방지
  • Hot reloading으로 실시간 업데이트
  • 자동 스키마 유효성 검사

2. AI 번역 시스템 설계

Google Gemini API를 활용한 자동 번역 시스템을 구축했습니다. 사람 번역가에게 의뢰하면 며칠이 걸릴 일을 AI는 몇 초 만에 끝냅니다. 게다가 불평도 하지 않습니다.

// scripts/translate-blog.js
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
const model = genAI.getGenerativeModel({ 
  model: "gemini-2.5-flash-lite-preview-06-17" 
});

async function translateBlogPost(japaneseContent) {
  const prompt = `Translate the following Japanese blog post to English. 
  Translate technical terms appropriately and make it readable English:

${japaneseContent}`;

  const result = await model.generateContent(prompt);
  return result.response.text().trim();
}

특징:

  • 속도 제한: 분당 15회 요청의 보수적인 제한
  • 오류 처리: 지수 백오프(Exponential backoff)를 통한 재시도 기능
  • YAML 안전성: frontmatter의 적절한 이스케이프 처리

3. 속도 제한 시스템

API의 안정적인 운영을 위한 속도 제한 기능을 구현했습니다. Google로부터 “너, 요청 너무 많이 보내!”라고 혼나지 않도록 말이죠.

// scripts/rate-limiter.js
class RateLimiter {
  constructor({ requestsPerMinute = 15, maxRetries = 3 }) {
    this.requestsPerMinute = requestsPerMinute;
    this.maxRetries = maxRetries;
    this.requestTimes = [];
  }

  async executeWithRetry(operation, description) {
    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        await this.checkRateLimit();
        return await operation();
      } catch (error) {
        if (attempt === this.maxRetries) throw error;
        await this.backoff(attempt);
      }
    }
  }
}

4. E2E 테스트 구현

Playwright를 사용한 포괄적인 블로그 기능 테스트를 구현했습니다. “잘 돌아가니 됐어!”라는 식으로는 안 됩니다.

// e2e/blog.spec.ts
test.describe('Blog functionality', () => {
  test('should navigate to Japanese blog', async ({ page }) => {
    await page.goto('/blog/');
    await expect(page.locator('h1')).toContainText('기술 블로그');
  });

  test('should display OGP image for blog posts', async ({ page }) => {
    await page.goto('/blog/');
    const firstPost = page.locator('[data-testid="blog-post"]').first();
    await firstPost.click();
    
    const ogImage = page.locator('meta[property="og:image"]');
    await expect(ogImage).toHaveAttribute('content', /\/og\/.*\.svg/);
  });
});

성능 최적화

압축 및 최적화 설정

// astro.config.mjs
integrations: [
  compress({
    CSS: true,
    HTML: {
      'remove-comments': true,
      'minify-js': true,
      'minify-css': true
    },
    JavaScript: true,
    SVG: true
  }),
  compressor({
    gzip: true,
    brotli: true
  })
]

이미지 최적화

  • AVIF 형식 이미지 제공
  • WebP 폴백(Fallback)
  • 반응형 이미지 크기 (480w, 800w)

과제와 해결책

1. YAML Frontmatter 이스케이프 문제

과제: 번역된 제목이나 설명에 특수 문자가 포함될 경우 YAML 파싱 오류

해결책:

function escapeYAMLString(str) {
  return str
    .replace(/\\/g, "\\\\") // 백슬래시를 먼저 이스케이프
    .replace(/"/g, '\\"');   // 큰따옴표 이스케이프
}

2. API 속도 제한 대응

과제: Gemini API 제한으로 인한 요청 실패

해결책: 지수 백오프와 지능적인 속도 제한

  • 1초 기반 지연
  • 최대 60초까지 단계적 지연
  • 요청 시간 추적을 통한 사전 제한

3. E2E 테스트 안정성

과제: 동적 콘텐츠로 인한 테스트 불안정성

해결책: data-testid 속성을 사용한 확실한 요소 식별

운영 흐름

새 게시글 작성 프로세스

  1. 일본어 기사 작성: /src/content/blog/ja/에 Markdown 파일 생성
  2. AI 번역: npm run translate src/content/blog/ja/article-name.md
  3. 테스트 실행: npm run test:e2e
  4. 빌드: npm run build
  5. 배포: Firebase Hosting으로 자동 배포

품질 보증

  • 타입 체크: TypeScript를 통한 컴파일 시 체크
  • 스키마 검증: Zod를 통한 콘텐츠 검증
  • E2E 테스트: Playwright를 통한 기능 테스트

배운 점

1. Content Collections의 위력

기존의 파일 기반 관리와 비교했을 때, 타입 안전성과 개발 경험이 극적으로 향상되었습니다. 특히 스키마 변경 시 영향 범위가 명확해져 안전한 리팩토링이 가능해졌습니다.

이제 TypeScript가 “타입이 다르잖아!”라고 쏘아주니, 런타임 오류에 덜덜 떨 필요도 없습니다. 최고입니다.

2. AI 번역의 실용성

Google Gemini API의 번역 품질은 기술 기사에 있어 매우 높았으며, 사람의 미세 조정을 최소화할 수 있었습니다. 다만, 전문 용어의 일관성에 대해서는 지속적인 개선이 필요합니다.

“npm”을 “엔피엠”이라고 번역했을 때는 웃음이 터졌습니다. 뭐, 틀린 건 아니지만요.

3. E2E 테스트의 중요성

정적 사이트 생성기에서도 라우팅이나 메타데이터 생성 등 복잡한 기능에 대해서는 E2E 테스트가 필수적임을 실감했습니다.

“정적 사이트니 테스트 필요 없겠지”라고 생각했던 과거의 나를 때리고 싶습니다.

성능 결과

  • First Contentful Paint: 0.8초
  • Largest Contentful Paint: 1.2초
  • Cumulative Layout Shift: 0.05
  • 총 번들 크기: 45KB (gzip 압축 후)

요약

이 블로그 시스템 구현을 통해 현대적인 JAMstack 아키텍처에서 다음과 같은 중요성을 재확인할 수 있었습니다.

  1. 타입 안전성: Content Collections를 통한 설계 시 오류 방지 (이제 런타임 오류로 울지 않겠습니다)
  2. 자동화: AI 번역과 E2E 테스트를 통한 운영 효율화 (인간의 일을 빼앗아 갑니다)
  3. 성능: 적극적인 최적화를 통한 고속화 (폭발적인 속도의 극치)
  4. 유지보수성: 명확한 아키텍처를 통한 지속 가능한 개발 (미래의 나에게 친절함)

기술 블로그 플랫폼으로서, 기사 작성부터 공개까지 완전 자동화를 실현하여 개발자가 본질적인 콘텐츠 제작에 집중할 수 있는 환경을 구축했습니다.

즉, 이제 블로그 업데이트를 게을리할 변명을 할 수 없게 되었다는 뜻입니다. 훌륭한… 건가요?

Support this article

If this article was helpful, please support us with a tip. You can choose any amount.

Secure payment via Stripe

관련 글

글 읽기
韓国語ブログ404ページ
엔지니어링

【완벽 5개 국어 지원】영일만 지원하던 블로그를 중한서까지 확장한 격렬했던 3시간 분투기

5개 국어 지원이라 생각했지만 사실은 2개 국어만 지원했던 충격적인 사실. Claude Code와 함께 중국어, 한국어, 스페인어 지원을 폭풍처럼 구현했던 모든 기록

#多言語化 #i18n +4
글 읽기
Google Cloud Platform ロゴ
엔지니어링

Google Cloud 프로젝트를 전체 조직 간 이전했더니, 하루 만에 끝나버린 이야기

GCS를 조금씩 이전하는 스크립트까지 만들었는데, 사실은 프로젝트 단위로 이동할 수 있다는 것을 알게 된 개발자의 눈물겨운 기록

#Google Cloud #DevOps +3
글 읽기
高性能Webサイトのイメージ
엔지니어링

아베 히로시의 홈페이지에 계속 도전했더니 굉장한 HP가 만들어진 이야기

"자칭 내리막길 최고 속도의 홈페이지를 가진 남자"로서, 아베 히로시의 홈페이지에 계속 도전한 궤적과 기술적 통찰

#Astro #Alpine.js +3