Jekyll 블로그 AI 자동화 구현 가이드

개요

Ruby와 Jekyll을 사용하는 GitHub 블로그에 AI를 활용한 자동 콘텐츠 생성 시스템을 구축하는 방법에 대한 종합 가이드입니다. 완전 자동화의 한계를 인식하고, 실용적인 Draft 기반 반자동화 방식을 중심으로 다양한 구현 방법을 제시합니다.

1. AI 블로그 자동화 기본 접근법

1.1 주요 구현 방식

GitHub Actions + AI API 조합 (권장)

  • GitHub Actions를 사용한 주기적 실행
  • OpenAI API, Claude API 등을 활용한 콘텐츠 생성
  • Jekyll 마크다운 포맷으로 자동 변환
  • 자동 커밋 및 푸시를 통한 블로그 업데이트

외부 서버 + Webhook 방식

  • Heroku, Vercel, AWS Lambda 등 외부 서버 활용
  • GitHub API를 통한 직접 파일 업로드
  • 더 복잡한 로직 구현 가능

로컬 스크립트 + 자동화

  • Ruby 스크립트를 통한 로컬 자동화
  • AI API 호출, Jekyll 포스트 생성, Git 작업 통합

1.2 실제 구현 사례

검증된 오픈소스 프로젝트

  • Ecrivai: LangChain과 GPT를 활용한 완전 자동화 블로그 작성기
  • AI-Writer: SEO 최적화, 웹 리서치 기반 콘텐츠 생성 플랫폼
  • Jekyll GitHub Actions: 공식 문서에서 제공하는 자동화 워크플로우

2. 완전 자동화의 한계와 문제점

2.1 커밋 히스토리 문제

완전 자동화 시스템의 가장 큰 문제점은 커밋 주체입니다:

  • GitHub Actions에서 자동 커밋 시 봇 계정 또는 시스템 계정으로 커밋됨
  • 개인 블로그의 Git 히스토리가 자동화 커밋으로 오염됨
  • 실제 작성자와 커밋 작성자의 불일치 발생

2.2 품질 관리의 어려움

  • AI 생성 콘텐츠의 품질 편차
  • 브랜드 톤앤매너 일관성 유지 어려움
  • 팩트 체크 및 정확성 검증 필요

3. Draft 기반 반자동화 솔루션

3.1 Draft 모드 구현 방식

방법 1: _drafts 폴더 활용

your-blog/
├── _posts/           # 발행된 글들
├── _drafts/          # AI가 생성한 초안들
│   ├── draft-title-1.md
│   ├── draft-title-2.md
│   └── 2025-06-06-future-post.md
└── ...

특징:

  • 기본적으로 빌드에서 제외됨
  • 파일명에 날짜 불필요
  • jekyll serve --drafts로 로컬 미리보기 가능

방법 2: published: false 활용

---
layout: post
title: "AI 생성 초안"
date: 2025-06-06
published: false    # Draft 상태 표시
categories: [tech]
tags: [ai, automation]
---

AI가 생성한 콘텐츠...

방법 3: 미래 날짜 활용

---
layout: post
title: "예약 발행 글"
date: 2025-12-31 10:00:00 +0900
---

3.2 Ruby 기반 구현 방식

기존 Ruby 스크립트 구조 분석

Jekyll 블로그에서 일반적으로 사용하는 포스트 생성 Ruby 스크립트:

#!/usr/bin/env ruby
require 'date'

# 포스트 제목을 인자로 받아옵니다.
title = ARGV[0] || "new-post"
date = DateTime.now

# 파일 이름을 'YYYY-MM-DD-title.md' 형식으로 만듭니다.
filename = "#{date.strftime('%Y-%m-%d')}-#{title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')}.md"

# 파일 경로를 설정합니다.
filepath = "_posts/#{filename}"

# 포스트 파일을 생성하고 기본 내용을 작성합니다.
File.open(filepath, "w") do |file|
  file.puts "---"
  file.puts "layout: post"
  file.puts "title: \"#{title.split('-').map(&:capitalize).join(' ')}\""
  file.puts "date: #{date.strftime('%Y-%m-%d %H:%M:%S %z')}"
  file.puts "categories: "
  file.puts "tags: "
  file.puts "---"
  file.puts
  file.puts "Content goes here"
end

puts "New post created: #{filepath}"

AI 통합 Ruby 스크립트 (ai_draft.rb)

#!/usr/bin/env ruby
require 'date'
require 'net/http'
require 'json'
require 'uri'

class AIDraftGenerator
  def initialize(api_key)
    @api_key = api_key
  end

  def generate_content(topic, content_type = "blog_post")
    uri = URI('https://api.openai.com/v1/chat/completions')
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true

    request = Net::HTTP::Post.new(uri)
    request['Authorization'] = "Bearer #{@api_key}"
    request['Content-Type'] = 'application/json'

    prompt = build_prompt(topic, content_type)
    
    body = {
      model: "gpt-3.5-turbo",
      messages: [
        {
          role: "user",
          content: prompt
        }
      ],
      max_tokens: 1500,
      temperature: 0.7
    }

    request.body = body.to_json
    response = http.request(request)
    
    if response.code == '200'
      result = JSON.parse(response.body)
      result.dig('choices', 0, 'message', 'content')
    else
      "AI 콘텐츠 생성 실패: #{response.code}"
    end
  end

  def create_draft(title, content_type = "tutorial")
    date = DateTime.now
    
    # AI로 콘텐츠 생성
    puts "AI가 '#{title}' 주제로 콘텐츠를 생성 중..."
    ai_content = generate_content(title, content_type)
    
    # 파일명 생성 (기존 로직 재사용)
    filename = "#{date.strftime('%Y-%m-%d')}-#{title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')}.md"
    
    # Draft 폴더에 저장
    filepath = "_drafts/#{filename}"
    
    File.open(filepath, "w") do |file|
      file.puts "---"
      file.puts "layout: post"
      file.puts "title: \"#{title.split('-').map(&:capitalize).join(' ')}\""
      file.puts "date: #{date.strftime('%Y-%m-%d %H:%M:%S %z')}"
      file.puts "published: false"
      file.puts "ai_generated: true"
      file.puts "content_type: #{content_type}"
      file.puts "categories: "
      file.puts "tags: "
      file.puts "---"
      file.puts
      file.puts ai_content
    end

    puts "AI Draft 생성 완료: #{filepath}"
    filepath
  end

  private

  def build_prompt(topic, content_type)
    case content_type
    when "tutorial"
      "#{topic}에 대한 초보자를 위한 튜토리얼을 작성해주세요. 단계별로 설명하고, 코드 예시가 있다면 포함해주세요."
    when "review"
      "#{topic}에 대한 객관적인 리뷰를 작성해주세요. 장단점을 균형있게 다뤄주세요."
    when "news"
      "#{topic}과 관련된 최근 동향을 요약해주세요."
    else
      "#{topic}에 대한 블로그 포스트를 작성해주세요."
    end
  end
end

# 스크립트 실행 부분
if __FILE__ == $0
  api_key = ENV['OPENAI_API_KEY']
  
  if api_key.nil? || api_key.empty?
    puts "OPENAI_API_KEY 환경변수를 설정해주세요."
    exit 1
  end

  title = ARGV[0] || "new-ai-post"
  content_type = ARGV[1] || "blog_post"
  
  generator = AIDraftGenerator.new(api_key)
  generator.create_draft(title, content_type)
end

사용법

# 환경변수 설정
export OPENAI_API_KEY="your-api-key"

# AI Draft 생성
ruby ai_draft.rb "Ruby 성능 최적화" tutorial
ruby ai_draft.rb "VS Code 확장" review
ruby ai_draft.rb "웹 개발 트렌드" news

3.3 GitHub Actions + Ruby 워크플로우

name: Generate AI Blog Draft
on:
  schedule:
    - cron: '0 9 * * 1'  # 매주 월요일 오전 9시
  workflow_dispatch:
    inputs:
      topic:
        description: '블로그 주제'
        required: true
        default: 'Weekly Tech Update'
      content_type:
        description: '콘텐츠 타입'
        required: true
        default: 'news'
        type: choice
        options:
        - tutorial
        - review
        - news
        - blog_post

jobs:
  generate-draft:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.1'
          
      - name: Generate AI Draft
        env:
          OPENAI_API_KEY: $
        run: |
          if [ "$" = "workflow_dispatch" ]; then
            ruby scripts/ai_draft.rb "$" "$"
          else
            # 스케줄 실행시 기본 주제들
            ruby scripts/ai_draft.rb "Weekly Tech News" news
          fi
          
      - name: Commit draft
        run: |
          git config --local user.email "ai-assistant@blog.com"
          git config --local user.name "AI Blog Assistant"
          git add _drafts/
          if git diff --staged --quiet; then
            echo "No changes to commit"
          else
            git commit -m "AI: Add new draft [$(date +'%Y-%m-%d %H:%M')]"
            git push
          fi

3.4 Draft 관리 Ruby 스크립트

Draft 상태 확인 및 관리 (draft_manager.rb)

#!/usr/bin/env ruby
require 'date'
require 'fileutils'

class DraftManager
  def initialize
    @drafts_dir = '_drafts'
    @posts_dir = '_posts'
  end

  def list_drafts
    return [] unless Dir.exist?(@drafts_dir)
    
    drafts = Dir.glob("#{@drafts_dir}/*.md").map do |file|
      {
        file: file,
        title: extract_title(file),
        date: extract_date(file),
        age_days: (Date.today - extract_date(file)).to_i
      }
    end
    
    drafts.sort_by { |d| d[:date] }.reverse
  end

  def publish_draft(draft_file)
    return false unless File.exist?(draft_file)
    
    # 파일명을 posts 형식으로 변경
    basename = File.basename(draft_file)
    new_filename = ensure_date_prefix(basename)
    new_path = "#{@posts_dir}/#{new_filename}"
    
    # published: false 제거
    content = File.read(draft_file)
    content.gsub!(/^published:\s*false\s*$/, '')
    content.gsub!(/^ai_generated:\s*true\s*$/, '')
    
    # 새 위치에 저장
    File.write(new_path, content)
    
    # 원본 draft 삭제
    File.delete(draft_file)
    
    puts "Draft published: #{new_path}"
    true
  end

  def cleanup_old_drafts(days_old = 30)
    old_drafts = list_drafts.select { |d| d[:age_days] > days_old }
    
    old_drafts.each do |draft|
      puts "Deleting old draft: #{draft[:title]} (#{draft[:age_days]} days old)"
      File.delete(draft[:file])
    end
    
    puts "Cleaned up #{old_drafts.length} old drafts"
  end

  def show_draft_summary
    drafts = list_drafts
    
    puts "\n=== Draft Summary ==="
    puts "Total drafts: #{drafts.length}"
    
    if drafts.empty?
      puts "No drafts found."
      return
    end
    
    drafts.each_with_index do |draft, index|
      puts "#{index + 1}. #{draft[:title]}"
      puts "   Date: #{draft[:date]}"
      puts "   Age: #{draft[:age_days]} days"
      puts "   File: #{File.basename(draft[:file])}"
      puts
    end
  end

  private

  def extract_title(file)
    content = File.read(file)
    title_match = content.match(/^title:\s*["']?(.*?)["']?\s*$/)
    title_match ? title_match[1] : File.basename(file, '.md')
  end

  def extract_date(file)
    content = File.read(file)
    date_match = content.match(/^date:\s*(\d{4}-\d{2}-\d{2})/)
    
    if date_match
      Date.parse(date_match[1])
    else
      Date.parse(File.basename(file)[0..9]) rescue File.mtime(file).to_date
    end
  end

  def ensure_date_prefix(filename)
    return filename if filename.match(/^\d{4}-\d{2}-\d{2}-/)
    
    date_prefix = Date.today.strftime('%Y-%m-%d')
    "#{date_prefix}-#{filename}"
  end
end

# 명령행 인터페이스
if __FILE__ == $0
  manager = DraftManager.new
  
  case ARGV[0]
  when 'list', 'ls'
    manager.show_draft_summary
  when 'publish', 'pub'
    if ARGV[1]
      draft_file = ARGV[1].start_with?('_drafts/') ? ARGV[1] : "_drafts/#{ARGV[1]}"
      manager.publish_draft(draft_file)
    else
      puts "Usage: ruby draft_manager.rb publish <draft_filename>"
    end
  when 'cleanup'
    days = ARGV[1] ? ARGV[1].to_i : 30
    manager.cleanup_old_drafts(days)
  else
    puts "Usage:"
    puts "  ruby draft_manager.rb list        # Show all drafts"
    puts "  ruby draft_manager.rb publish <file>  # Publish a draft"
    puts "  ruby draft_manager.rb cleanup [days]  # Delete old drafts"
  end
end

사용법

# Draft 목록 확인
ruby draft_manager.rb list

# 특정 Draft 발행
ruby draft_manager.rb publish 2025-06-06-ruby-optimization.md

# 30일 이상 된 Draft 정리
ruby draft_manager.rb cleanup 30

4. AI 활용 고급 전략

4.1 스마트 콘텐츠 분류 시스템

def classify_ai_content(content):
    """AI 생성 콘텐츠 품질 평가 및 분류"""
    quality_score = evaluate_content_quality(content)
    
    if quality_score > 0.8:
        return "high_priority"
    elif quality_score > 0.6:
        return "medium_priority"
    else:
        return "needs_review"

4.2 다양한 콘텐츠 타입 자동 생성

주기별 콘텐츠 전략

  • 일간: 기술 뉴스 요약, 개발 일지
  • 주간: GitHub 활동 요약, 학습 정리
  • 월간: 트렌드 분석, 프로젝트 회고

콘텐츠 타입별 접근

  • 튜토리얼: 단계별 가이드 자동 생성
  • 리뷰: 도구/라이브러리 분석 보고서
  • 뉴스: 큐레이션된 기술 소식 정리

4.3 개인화된 AI 어시스턴트 구축

def create_personalized_ai_writer():
    """기존 블로그 글을 학습하여 개인 스타일 모방"""
    
    # 기존 포스트 분석
    writing_style = analyze_existing_posts()
    
    # 개인화된 프롬프트 생성
    personalized_prompt = create_style_prompt(writing_style)
    
    return personalized_prompt

4.4 인터랙티브 Draft 관리

자동 정리 시스템

  • 일정 기간 지난 Draft 자동 아카이브
  • 비슷한 주제 Draft 병합 제안
  • 트렌드 기반 발행 우선순위 추천

알림 시스템 통합

  • Slack/Discord를 통한 새 Draft 알림
  • 주간 Draft 요약 리포트 자동 전송
  • 발행 추천 알고리즘 기반 알림

5. 실용적 구현 로드맵

5.1 단계별 구현 계획

Phase 1: 기본 Draft 시스템

  1. _drafts 폴더 설정
  2. 간단한 AI API 호출 스크립트 작성
  3. GitHub Actions 기본 워크플로우 구성

Phase 2: 자동화 확장

  1. 주기적 콘텐츠 생성 스케줄링
  2. 품질 평가 시스템 도입
  3. 다양한 콘텐츠 타입 지원

Phase 3: 고도화

  1. 개인화된 AI 라이터 구축
  2. 인터랙티브 관리 시스템
  3. 분석 및 최적화 도구

5.2 현실적 시작 방안

초보자용 간단 시작

# 일일 GitHub 활동 요약 봇
def daily_github_summary():
    activities = fetch_github_activities()
    summary = ai_summarize(activities)
    save_to_drafts(summary, f"github-summary-{today()}.md")

중급자용 확장

# 북마크 기반 주간 정리
def weekly_bookmark_digest():
    bookmarks = fetch_saved_links()
    categorized = ai_categorize_links(bookmarks)
    digest = ai_create_digest(categorized)
    save_to_drafts(digest, f"weekly-digest-{week_number()}.md")

6. AI API 토큰 비용 분석

6.1 주요 AI 서비스 토큰 비용 (2025년 6월 기준)

OpenAI GPT 모델

  • GPT-3.5 Turbo: $0.0015/1K input tokens, $0.002/1K output tokens
  • GPT-4: $0.03/1K input tokens, $0.06/1K output tokens
  • GPT-4 Turbo: $0.01/1K input tokens, $0.03/1K output tokens

Claude API (Anthropic)

  • Claude 3 Haiku: $0.00025/1K input tokens, $0.00125/1K output tokens
  • Claude 3 Sonnet: $0.003/1K input tokens, $0.015/1K output tokens
  • Claude 3 Opus: $0.015/1K input tokens, $0.075/1K output tokens

6.2 블로그 포스트별 예상 토큰 사용량

일반적인 블로그 포스트 (1000-1500단어)

  • Input tokens: 200-500 (프롬프트 + 컨텍스트)
  • Output tokens: 2000-3000 (생성된 콘텐츠)

기술 튜토리얼 (2000-3000단어)

  • Input tokens: 300-800 (더 상세한 프롬프트)
  • Output tokens: 4000-6000 (코드 예시 포함)

간단한 뉴스 요약 (500-800단어)

  • Input tokens: 150-300
  • Output tokens: 1000-1500

6.3 월간 비용 시나리오 분석

시나리오 1: 주 1회 포스팅 (GPT-3.5 Turbo 사용)

주간 사용량:
- Input: 400 tokens × 1회 = 400 tokens
- Output: 2500 tokens × 1회 = 2500 tokens

월간 비용:
- Input: 400 × 4 × $0.0015/1K = $0.0024
- Output: 2500 × 4 × $0.002/1K = $0.02
- 총 월간 비용: 약 $0.02 (약 30원)

시나리오 2: 일 1회 포스팅 (GPT-3.5 Turbo 사용)

일간 사용량:
- Input: 400 tokens
- Output: 2500 tokens

월간 비용:
- Input: 400 × 30 × $0.0015/1K = $0.018
- Output: 2500 × 30 × $0.002/1K = $0.15
- 총 월간 비용: 약 $0.17 (약 230원)

시나리오 3: 고품질 주 2회 포스팅 (GPT-4 사용)

주간 사용량:
- Input: 600 tokens × 2회 = 1200 tokens
- Output: 4000 tokens × 2회 = 8000 tokens

월간 비용:
- Input: 1200 × 4 × $0.03/1K = $0.144
- Output: 8000 × 4 × $0.06/1K = $1.92
- 총 월간 비용: 약 $2.06 (약 2,800원)

시나리오 4: 경제적 대량 생성 (Claude 3 Haiku 사용)

일간 사용량:
- Input: 300 tokens
- Output: 2000 tokens

월간 비용 (매일 포스팅):
- Input: 300 × 30 × $0.00025/1K = $0.00225
- Output: 2000 × 30 × $0.00125/1K = $0.075
- 총 월간 비용: 약 $0.08 (약 110원)

6.4 비용 최적화 전략

비용 최적화 Ruby 코드
class CostOptimizer
  # 토큰 수 추정
  def estimate_tokens(text)
    # 대략적 추정: 1토큰 ≈ 4글자 (영어 기준)
    # 한국어는 약간 더 효율적
    (text.length / 3.5).ceil
  end

  # 프롬프트 최적화
  def optimize_prompt(topic, content_type)
    base_prompts = {
      "tutorial" => "#{topic} 튜토리얼 작성. 단계별 설명, 코드 예시 포함.",
      "review" => "#{topic} 리뷰. 장단점 균형있게 서술.",
      "news" => "#{topic} 최신 동향 요약.",
      "blog" => "#{topic} 블로그 포스트 작성."
    }
    
    base_prompts[content_type] || base_prompts["blog"]
  end

  # 배치 처리로 비용 절약
  def batch_generate(topics, max_tokens_per_request = 4000)
    batches = []
    current_batch = []
    current_tokens = 0
    
    topics.each do |topic|
      estimated_tokens = estimate_tokens(topic) + 2000 # 출력 예상
      
      if current_tokens + estimated_tokens > max_tokens_per_request
        batches << current_batch unless current_batch.empty?
        current_batch = [topic]
        current_tokens = estimated_tokens
      else
        current_batch << topic
        current_tokens += estimated_tokens
      end
    end
    
    batches << current_batch unless current_batch.empty?
    batches
  end

  # 비용 추정
  def estimate_cost(input_tokens, output_tokens, model = "gpt-3.5-turbo")
    pricing = {
      "gpt-3.5-turbo" => { input: 0.0015, output: 0.002 },
      "gpt-4" => { input: 0.03, output: 0.06 },
      "claude-haiku" => { input: 0.00025, output: 0.00125 }
    }
    
    rates = pricing[model]
    return 0 unless rates
    
    input_cost = (input_tokens / 1000.0) * rates[:input]
    output_cost = (output_tokens / 1000.0) * rates[:output]
    
    {
      input_cost: input_cost,
      output_cost: output_cost,
      total_cost: input_cost + output_cost,
      currency: "USD"
    }
  end
end

스마트 토큰 관리 시스템

class TokenBudgetManager
  def initialize(monthly_budget_usd = 5.0)
    @monthly_budget = monthly_budget_usd
    @usage_file = '.ai_usage.json'
    load_usage_data
  end

  def can_generate?(estimated_cost)
    current_month_usage = get_current_month_usage
    (current_month_usage + estimated_cost) <= @monthly_budget
  end

  def record_usage(cost, tokens_used)
    @usage_data[current_month] ||= { cost: 0, tokens: 0 }
    @usage_data[current_month][:cost] += cost
    @usage_data[current_month][:tokens] += tokens_used
    save_usage_data
  end

  def get_remaining_budget
    current_usage = get_current_month_usage
    [@monthly_budget - current_usage, 0].max
  end

  private

  def current_month
    Date.today.strftime('%Y-%m')
  end

  def load_usage_data
    @usage_data = File.exist?(@usage_file) ? 
      JSON.parse(File.read(@usage_file)) : {}
  end

  def save_usage_data
    File.write(@usage_file, @usage_data.to_json)
  end

  def get_current_month_usage
    @usage_data.dig(current_month, :cost) || 0
  end
end

권장 비용 절약 방법

  1. 모델 선택 최적화
    • 일반 포스트: Claude 3 Haiku (가장 경제적)
    • 기술 문서: GPT-3.5 Turbo (균형잡힌 선택)
    • 고품질 콘텐츠: GPT-4 (주 1-2회만)
  2. 프롬프트 최적화
    • 간결하고 명확한 지시사항
    • 불필요한 컨텍스트 제거
    • 템플릿화된 프롬프트 사용
  3. 배치 처리 활용
    • 여러 주제를 한 번에 요청
    • API 호출 횟수 최소화
  4. 예산 관리 시스템
    • 월간 예산 설정 및 모니터링
    • 사용량 초과 시 자동 중단

6.5 실제 운영 비용 예시

대부분의 개인 블로그 운영자에게 권장하는 현실적인 비용 구간:

  • 미니멀: 월 $0.5 이하 (약 700원) - 주 1회 포스팅
  • 일반적: 월 $2-5 (약 2,700-6,800원) - 주 2-3회 포스팅
  • 활발한: 월 $10-20 (약 1.4-2.7만원) - 일 1회 포스팅

7. 주의사항 및 고려사항

7.1 기술적 고려사항

  • GitHub 토큰 보안: GitHub Secrets 활용 필수
  • Jekyll 호환성: Front Matter 형식 준수
  • 에러 핸들링: API 실패 시 대응 방안 마련
  • 토큰 사용량 모니터링: 예상치 못한 비용 발생 방지

6.2 콘텐츠 품질 관리

  • 팩트 체크: AI 생성 정보 검증 프로세스
  • 표절 방지: 원본성 확인 시스템
  • 브랜드 일관성: 톤앤매너 가이드라인 적용
  • SEO 최적화: 키워드 밀도 및 구조 최적화

6.3 법적/윤리적 고려사항

  • 저작권 준수: AI 생성 콘텐츠의 법적 지위
  • 투명성: AI 생성 콘텐츠 명시 여부
  • 책임성: 잘못된 정보에 대한 책임 소재

7. 결론

Jekyll 블로그의 AI 자동화는 완전 자동화보다는 Draft 기반 반자동화가 현실적이고 효과적입니다. 이 방식은 AI의 생산성과 인간의 품질 관리를 균형있게 결합하여, 블로그 운영의 효율성을 높이면서도 콘텐츠 품질을 유지할 수 있게 해줍니다.

핵심은 AI를 완전한 대체재가 아닌 강력한 어시스턴트로 활용하는 것이며, 지속적인 모니터링과 개선을 통해 개인화된 최적의 시스템을 구축하는 것입니다.


이 가이드는 Claude Sonnet 4를 이용해 작성했습니다. 2025년 6월 기준으로 작성되었으며, AI 기술과 Jekyll 생태계의 발전에 따라 지속적인 업데이트가 필요합니다.