DEV

Published on

LeetCode 문제풀이를 블로그에 자동 연동하는 시스템 구축기 #2

Authors
  • avatar
    Name
    Charles

들어가며

지난 7월 31일, "LeetCode 자동화 시스템 구축하기 with Claude Code" 포스트에서 계획했던 부분 중 LeetCode 문제를 풀고 Supabase에 저장하는 기능이 완성이 되었습니다. 당시 GitHub 저장소에서 내가 푼 문제를 찾아보는 과정이 점점 번거롭게 느껴졌다는 단순한 동기로 시작했던 프로젝트가, 불과 2일간의 집중적인 개발을 거쳐 실제로 동작하는 자동화 시스템으로 완성되었습니다.

이 포스트에서는 처음 계획했던 내용과 실제 구현 결과를 비교하며, 단기간에 완성할 수 있었던 비결과 Claude Code와의 협업 경험을 정리해보겠습니다.

📋 원래 계획 vs 실제 구현

초기 계획 (2025.07.31)

당시 계획했던 시스템 구성도는 다음과 같았습니다:

[LeetCode 문제 풀이 작성] [GitHub 저장소] ↓ (커밋: 풀이 및 문제 번역본 포함) [번역 및 가공 → Supabase에 데이터 저장] [블로그에서 Supabase 데이터 fetch 및 렌더링] [사용자: 블로그에서 풀이 확인]

계획했던 Supabase 테이블 설계:

필드명타입설명
idUUID고유 식별자
titleString문제 제목
slugStringURL용 슬러그
tagsString[]문제 유형 (예: Array, DFS 등)
difficultyEnum난이도 (Easy, Medium, Hard)
codeText풀이 코드
explanationMarkdown풀이 설명
problem_urlStringLeetCode 문제 링크
updated_atTimestamp최종 수정 시각

실제 구현된 Supabase 테이블 설계:

필드명타입설명
idSERIAL고유 식별자 (자동 증가)
problem_numberINTEGERLeetCode 문제 번호 (유니크)
titleVARCHAR영어 문제 제목
title_koreanVARCHAR한국어 문제 제목
difficultyVARCHAR난이도 (Easy, Medium, Hard)
tagsTEXT[]문제 유형 배열
description_englishTEXT영어 문제 설명
description_koreanTEXT한국어 문제 설명
constraints_englishTEXT[]영어 제약조건 배열
constraints_koreanTEXT[]한국어 제약조건 배열
examplesJSONB입출력 예제 (구조화된 데이터)
solution_codeTEXT풀이 코드
solution_languageVARCHAR코드 언어 (typescript/javascript)
explanation_koreanTEXT한국어 풀이 설명
approach_koreanTEXT한국어 접근 방법
time_complexityVARCHAR시간 복잡도 (Big O)
space_complexityVARCHAR공간 복잡도 (Big O)
slugVARCHARSEO용 URL 슬러그 (유니크)
leetcode_urlVARCHARLeetCode 문제 링크
github_urlVARCHARGitHub 솔루션 링크
is_premiumBOOLEAN프리미엄 문제 여부
acceptance_rateDECIMAL문제 통과율
submission_countINTEGER제출 횟수
created_atTIMESTAMP생성 시각
updated_atTIMESTAMP최종 수정 시각

테이블 설계 변경 이유

1. 다국어 지원 확장

  • 변경: titletitle + title_korean
  • 이유: 블로그에서 한국어/영어 제목을 모두 표시하기 위해
  • 장점: SEO 최적화와 사용자 경험 개선

2. 문제 데이터 세분화

  • 변경: 단일 explanationdescription, constraints, examples 분리
  • 이유: LeetCode 문제 구조를 정확히 반영하고 개별 번역 관리
  • 장점: 데이터 정규화와 번역 품질 향상

3. 복잡도 분석 추가

  • 추가: time_complexity, space_complexity 필드
  • 이유: 알고리즘 학습에 필수적인 복잡도 정보 제공
  • 장점: 교육적 가치 향상과 성능 분석 자동화

4. ID 타입 변경

  • 변경: UUIDSERIAL + problem_number
  • 이유: LeetCode 문제 번호를 기본 식별자로 사용하는 것이 자연스러움
  • 장점: 직관적인 데이터 관리와 중복 방지

5. 메타데이터 확장

  • 추가: is_premium, acceptance_rate, submission_count
  • 이유: 문제 난이도와 인기도를 정량적으로 측정
  • 장점: 블로그에서 추가 정보 제공 가능

6. 구조화된 데이터 타입 활용

  • 변경: 단순 배열 → TEXT[], JSONB 활용
  • 이유: PostgreSQL의 강력한 배열/JSON 기능 활용
  • 장점: 쿼리 성능 향상과 데이터 무결성

7. SEO 최적화

  • 강화: slug 필드에 한국어 기반 URL 생성
  • 이유: 검색 엔진 최적화와 사용자 친화적 URL
  • 장점: 블로그 트래픽 증가와 접근성 향상

예상 기술 스택:

  • GitHub Actions (기본 자동화)
  • Supabase (데이터베이스)
  • Next.js (블로그)
  • 간단한 파싱 스크립트
  • Claude Code와의 바이브 코딩

예상 소요 시간: 길어야 1주 정도

실제 구현 결과 (2025.08.04)

[LeetCode 문제 해결] [LeetHub 자동 커밋] [GitHub Actions 번역 워크플로우] → [HTML 코드블럭 정리] → [마크다운 최적화] [OpenAI API 번역] [Supabase 구조화 저장] → [복잡도 분석] → [태그 시스템] [jin-blog 연동 준비]

실제 기술 스택:

  • LeetHub v3 (자동 커밋)
  • GitHub Actions (2개 워크플로우)
  • OpenAI GPT-4o-mini (번역)
  • Supabase (PostgreSQL)
  • TypeScript (타입 안전성)

🛠️ 주요 구현 내용

1. 스마트 번역 시스템

구현된 기능

// 해시 기반 변경 감지 const fileHash = crypto.createHash('sha256').update(content).digest('hex'); // 메타데이터 추적 interface TranslationMeta { hash: string; translatedAt: string; version: string; } // AI 번역 최적화 const translation = await openai.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'system', content: 'LeetCode 문제를 자연스러운 한국어로 번역하되, 기술 용어는 영어 그대로 유지하세요.' } ], temperature: 0.3 // 일관성을 위한 낮은 temperature });

예상과의 차이점

  • 계획: 단순한 구글 번역 API 사용
  • 실제: OpenAI GPT-4o-mini를 활용한 맥락 기반 번역
  • 이유: 기술 문서 특성상 정확한 번역과 일관성이 중요했음

2. GitHub Actions 워크플로우

2-Stage 파이프라인 구현

# Stage 1: translate-readme.yml - LeetHub 커밋 감지 - README 번역 실행 - 번역 결과 커밋 - 다음 단계 트리거 # Stage 2: leetcode-auto-update.yml - Supabase 데이터 저장 - 통계 업데이트 - 에러 모니터링

예상과의 차이점

  • 계획: 단일 워크플로우로 모든 작업 처리
  • 실제: 2-stage 파이프라인으로 분리
  • 이유:
    • 번역과 데이터 처리의 실행 환경 차이
    • 에러 격리 및 디버깅 용이성
    • 재시도 로직의 개별 관리 필요

3. 데이터 구조 설계

Supabase 스키마

CREATE TABLE leetcode_problems ( id SERIAL PRIMARY KEY, problem_number INTEGER NOT NULL UNIQUE, title VARCHAR(255) NOT NULL, title_korean VARCHAR(255) NOT NULL, difficulty VARCHAR(20) NOT NULL, tags TEXT[], description_english TEXT NOT NULL, description_korean TEXT NOT NULL, constraints_english TEXT[], constraints_korean TEXT[], examples JSONB, solution_code TEXT NOT NULL, solution_language VARCHAR(50) NOT NULL, explanation_korean TEXT, approach_korean TEXT, time_complexity VARCHAR(100), space_complexity VARCHAR(100), -- SEO 최적화 slug VARCHAR(255) UNIQUE NOT NULL, leetcode_url VARCHAR(500), github_url VARCHAR(500), -- 메타데이터 is_premium BOOLEAN DEFAULT false, acceptance_rate DECIMAL(5,2), submission_count INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() );

예상과의 차이점

  • 계획: 간단한 JSON 형태 저장
  • 실제: 정규화된 관계형 데이터베이스 설계
  • 추가된 기능:
    • 복잡도 분석 자동화
    • SEO 최적화를 위한 slug 생성
    • 블로그 연동을 위한 메타데이터

🔧 주요 트러블슈팅 사례

1. HTML 코드블럭 문제

문제 상황

# 예상된 결과 ## [1. Two Sum](https://leetcode.com/problems/two-sum) ### Easy Given an integer array `nums`... # 실제 결과 ```html <h2><a href="https://leetcode.com/problems/two-sum">1. Two Sum</a></h2> <h3>Easy</h3> <p>Given an integer array <code>nums</code>...</p>

해결 과정

  1. 원인 분석: OpenAI API가 HTML 형태로 응답하여 마크다운 렌더링 실패
  2. 1차 시도: 프롬프트 엔지니어링으로 마크다운 출력 요청
  3. 2차 시도: 후처리 스크립트 개발
// HTML을 마크다운으로 변환하는 정규식 엔진 function convertHtmlToMarkdown(content: string): string { return content .replace(/<h2><a href="([^"]+)">([^<]+)<\/a><\/h2>/g, '## [$2]($1)') .replace(/<h3>([^<]+)<\/h3>/g, '### $1') .replace(/<code>([^<]+)<\/code>/g, '`$1`') .replace(/<strong>([^<]+)<\/strong>/g, '**$1**') // ... 40+ 변환 규칙 }

최종 해결책

  • 번역 시스템에 HTML 방지 로직 추가
  • 기존 파일 일괄 정리 스크립트 개발

2. GitHub Actions 동시성 문제

문제 상황

Error: Updates were rejected because the remote contains work
that you do not have locally. This is usually caused by another
repository pushing to the same ref.

해결 과정

# 문제: 두 워크플로우가 동시에 실행되어 git conflict 발생 concurrency: group: leetcode-processing-${{ github.ref }} cancel-in-progress: false # 해결: Pull-before-push 전략 - name: Safe git operations run: | git fetch origin main git merge origin/main || { echo "Merge conflict detected, resolving..." git checkout --theirs '*.md' || true git add . git commit --no-edit || true }

최종 결과

  • 동시성 제어를 통한 안정적인 파이프라인 구축
  • 자동 conflict resolution 구현

3. TypeScript ESM 모듈 문제

문제 상황

ReferenceError: require is not defined
TypeError: Cannot use import statement outside a module

해결 과정

  1. package.json 설정 변경
{ "type": "module", "scripts": { "fix:html": "tsx scripts/fix-html-codeblocks.ts" } }
  1. Import 구문 통일
// 변경 전 const fs = require('fs'); // 변경 후 import { readFileSync, writeFileSync, existsSync } from 'fs';
  1. ES 모듈 시스템 적용
  • CommonJS에서 ES 모듈로 전환
  • package.json"type": "module" 설정
  • 최신 JavaScript 표준 준수

🔄 계획 대비 주요 변경 사항

1. 아키텍처 복잡도 증가

원래 계획

GitHub → 간단한 파싱 → Supabase → 블로그

실제 구현

GitHub → 번역 시스템 → HTML 정리 → 구조화 파싱 → 
복잡도 분석 → Supabase → 캐싱 → 블로그 API → 
SEO 최적화 → 렌더링

변경 이유:

  • 실제 사용자 경험을 고려한 품질 향상
  • SEO 최적화의 중요성 인식
  • 다국어 지원의 복잡성

2. 기술 스택 확장

구분계획실제변경 이유
번역Google Translate APIOpenAI GPT-4o-mini맥락 이해와 품질
실행 환경Node.jsTypeScript타입 안전성
데이터 처리단순 파싱구조화된 서비스 계층확장성과 유지보수성
에러 처리기본 try-catch체계적인 에러 핸들링운영 안정성

3. 개발 기간 차이

  • 예상: 길어야 1주 정도 (2025.07.31 계획)
  • 실제: 2일 (8월 1일: 4-5시간, 8월 4일: 5시간)
  • 단기간 완성 가능했던 이유:
    • Claude Code와의 효율적인 바이브 코딩
    • 기본 아키텍처 설계에 집중
    • 핵심 기능 위주의 MVP 구현
    • AI 번역 품질을 한 번에 높은 수준으로 구현

🚀 다음 단계: jin-blog 연동

현재 상태

  • ✅ 데이터 수집 및 저장 시스템 완성
  • ✅ 번역 및 정리 파이프라인 구축
  • ✅ API 및 데이터 구조 준비 완료

다음 작업 계획

  1. jin-blog 프로젝트 연동

    • Next.js 14 App Router 기반 페이지 구축
    • Supabase 클라이언트 연동
    • 동적 라우팅 구현
  2. UI/UX 개발

    • 문제 목록 페이지 (필터링, 정렬)
    • 개별 문제 상세 페이지
    • 코드 하이라이팅 및 복잡도 시각화
  3. SEO 최적화

    • 메타데이터 자동 생성
    • 구조화된 데이터 (JSON-LD)
    • 사이트맵 자동 생성

예상 추가 개발 기간

  • 현실적 목표: Claude Code와의 효율적인 협업 경험을 바탕으로 1-2일 내 완성

🎯 핵심 학습 내용

기술적 인사이트

  1. AI 번역의 중요성

    • 단순 번역 vs 맥락 기반 번역의 품질 차이 체감
    • 프롬프트 엔지니어링의 중요성 인식
  2. GitHub Actions 운영 노하우

    • 워크플로우 분리의 중요성
    • 동시성 제어와 에러 처리 패턴
  3. TypeScript ESM 생태계

    • 모듈 시스템의 복잡성과 해결책

프로젝트 관리 관점

  1. 점진적 개선의 가치

    • 완벽한 계획보다 반복적 개선이 더 효과적
    • 사용자 피드백(자신의 사용 경험)이 방향성 결정에 중요
  2. 품질 vs 속도의 균형

    • 처음 목표했던 "빠른 구현"에서 "지속 가능한 시스템"으로 관점 전환
    • 장기적 관점에서의 투자 가치
  3. 문서화의 중요성

    • 복잡해진 시스템을 다른 프로젝트에서 활용하기 위한 가이드 문서 작성
    • 미래의 자신을 위한 컨텍스트 보존

🤖 Claude Code와의 바이브 코딩 경험

당초 계획에서 강조했던 **"Claude Code와의 바이브 코딩"**을 실제로 경험해본 솔직한 후기입니다.

기대했던 것 vs 실제 경험

✅ 기대 이상이었던 부분

  1. 복잡한 정규식 처리

    • HTML을 마크다운으로 변환하는 40+ 규칙 작성
    • 인간이 하기 번거로운 반복적 패턴 매칭 작업을 Claude가 정확하게 처리
  2. 에러 디버깅 속도

    • GitHub Actions 동시성 문제 해결 시 즉시 원인 파악과 해결책 제시
    • TypeScript ESM 모듈 문제도 빠르게 해결
  3. 문서 작성

    • 현재 작성 중인 이 블로그 포스트처럼 체계적인 문서 구조화
    • 코드 주석과 README 작성에서 뛰어난 품질
  4. 컨텍스트 엔지니어링의 효과

    • CLAUDE.md 파일을 통한 프로젝트 컨텍스트 관리
    • 일관성 있는 코드 스타일과 아키텍처 유지
    • 기능 구현 시 이전 결정사항들이 자연스럽게 반영됨
  5. MCP (Model Context Protocol) 활용

    • Supabase MCP를 통한 직접적인 데이터베이스 조작
    • API 문서를 찾아보지 않고도 즉시 스키마 수정 및 쿼리 실행 가능
    • 개발 속도 향상과 실수 방지에 큰 도움

⚠️ 한계가 있었던 부분

  1. 전체 아키텍처 설계

    • 시스템 전반의 설계 결정은 여전히 개발자의 판단이 중요
    • Claude는 제안을 잘 하지만, 최종 결정은 컨텍스트를 이해하는 개발자가 해야 함
  2. 비즈니스 로직의 미묘한 부분

    • 언제 번역을 다시 실행할지, 어떤 우선순위로 처리할지 등은 도메인 지식 필요
    • 데이터 일관성 보장을 위한 전략 수립
  3. Rate Limit 제약

    • $17 구독 플랜으로도 집중적인 바이브 코딩 시 limit에 자주 걸림
    • 초기화될 때까지 기다려야 하는 불편함
    • 긴 세션의 개발에서는 작업 흐름이 끊기는 단점

실제 협업 패턴

개발자: "HTML 코드블럭을 마크다운으로 변환하는 스크립트가 필요해"
Claude: [40+ 변환 규칙이 포함된 완전한 스크립트 제공]
개발자: "이 에러는 뭐야?" [에러 로그 제공]
Claude: [즉시 원인 분석 + 해결책 + 개선 제안]
개발자: "이제 이 결과를 문서로 정리해줘"
Claude: [체계적인 마크다운 문서 생성]

컨텍스트 엔지니어링의 중요성

프롬프트 엔지니어링도 중요하지만, 컨텍스트 엔지니어링이 더욱 중요하다는 것을 깨달았습니다:

  • CLAUDE.md 파일: 프로젝트의 핵심 컨텍스트를 체계적으로 관리
  • 일관성 유지: 이전 결정사항들이 새로운 기능 구현에 자연스럽게 반영
  • 효율성 향상: 매번 전체 맥락을 설명할 필요 없이 빠른 개발 가능

MCP를 통한 개발 경험

Supabase MCP를 활용한 개발이 특히 인상적이었습니다:

  • 데이터베이스 스키마를 직접 확인하고 수정 가능
  • 쿼리 실행 결과를 즉시 확인하며 개발
  • API 문서나 웹 인터페이스를 오가지 않아도 되는 편의성

2일간 사용 후 평가

  • 개발 속도: 약 10배 빨라짐 (예상 1주 → 실제 2일)
  • 코드 품질: 오히려 더 좋아짐 (Claude가 베스트 프랙티스 제안)
  • 학습 효과: 매우 높음 (설명을 들으며 개발하는 효과)
  • 재사용성: 높음 (Claude가 구조화된 코드 작성)
  • 제약사항: Rate limit으로 인한 작업 흐름 중단

결론: 7월에 기대했던 "AI와 함께 사고하며 구현하는 협업"이 실제로 가능했고, 예상보다 훨씬 더 효과적이었습니다. 특히 컨텍스트 엔지니어링과 MCP 활용을 통해 단기간에 고품질 시스템을 완성할 수 있다는 것이 가장 큰 수확이었습니다. 다만 rate limit 제약은 지속적인 바이브 코딩에서 고려해야 할 요소입니다.

마무리

처음 "GitHub 저장소에서 문제 찾기가 번거로워서"라는 단순한 동기로 시작된 프로젝트가 불과 2일 만에 체계적인 자동화 시스템으로 완성되었습니다. 예상보다 훨씬 빠르게 완성할 수 있었고, 그 과정에서 얻은 Claude Code와의 협업 경험은 예상보다 훨씬 값진 자산이 되었습니다.

특히 AI 번역, GitHub Actions 운영, TypeScript 모듈 시스템 등 복잡한 기술들을 Claude Code와 함께 빠르게 구현할 수 있었던 것이 가장 큰 수확이었습니다. 혼자서는 며칠이 걸렸을 작업을 단 2일 만에 완성할 수 있었습니다.

이번 프로젝트를 통해 깨달은 AI 협업의 핵심:

  1. 프롬프트 엔지니어링을 넘어선 컨텍스트 엔지니어링의 중요성
  2. MCP 같은 도구를 통한 직접적이고 효율적인 개발 환경 구축
  3. Rate limit 제약을 고려한 개발 계획 수립의 필요성
  4. CLAUDE.md 파일을 통한 프로젝트 일관성 유지

앞으로는 이러한 AI 협업 패턴을 더 많은 프로젝트에 적용하여, 개발자로서의 생산성과 코드 품질을 동시에 향상시키는 방법을 계속 탐구해보겠습니다.

다음에는 jin-blog 프로젝트와의 연동 과정을 진행하며, 실제 사용자가 볼 수 있는 블로그 페이지까지 완성해보겠습니다.