- Published on
LeetCode 문제풀이를 블로그에 자동 연동하는 시스템 구축기 #2
- Authors
- Name
- Charles
들어가며
지난 7월 31일, "LeetCode 자동화 시스템 구축하기 with Claude Code" 포스트에서 계획했던 부분 중 LeetCode 문제를 풀고 Supabase에 저장하는 기능이 완성이 되었습니다.
당시 GitHub 저장소에서 내가 푼 문제를 찾아보는 과정이 점점 번거롭게 느껴졌다
는 단순한 동기로 시작했던 프로젝트가, 불과 2일간의 집중적인 개발을 거쳐 실제로 동작하는 자동화 시스템으로 완성되었습니다.
이 포스트에서는 처음 계획했던 내용과 실제 구현 결과를 비교하며, 단기간에 완성할 수 있었던 비결과 Claude Code와의 협업 경험을 정리해보겠습니다.
📋 원래 계획 vs 실제 구현
초기 계획 (2025.07.31)
당시 계획했던 시스템 구성도는 다음과 같았습니다:
[LeetCode 문제 풀이 작성] ↓ [GitHub 저장소] ↓ (커밋: 풀이 및 문제 번역본 포함) [번역 및 가공 → Supabase에 데이터 저장] ↓ [블로그에서 Supabase 데이터 fetch 및 렌더링] ↓ [사용자: 블로그에서 풀이 확인]
계획했던 Supabase 테이블 설계:
필드명 | 타입 | 설명 |
---|---|---|
id | UUID | 고유 식별자 |
title | String | 문제 제목 |
slug | String | URL용 슬러그 |
tags | String[] | 문제 유형 (예: Array, DFS 등) |
difficulty | Enum | 난이도 (Easy, Medium, Hard) |
code | Text | 풀이 코드 |
explanation | Markdown | 풀이 설명 |
problem_url | String | LeetCode 문제 링크 |
updated_at | Timestamp | 최종 수정 시각 |
실제 구현된 Supabase 테이블 설계:
필드명 | 타입 | 설명 |
---|---|---|
id | SERIAL | 고유 식별자 (자동 증가) |
problem_number | INTEGER | LeetCode 문제 번호 (유니크) |
title | VARCHAR | 영어 문제 제목 |
title_korean | VARCHAR | 한국어 문제 제목 |
difficulty | VARCHAR | 난이도 (Easy, Medium, Hard) |
tags | TEXT[] | 문제 유형 배열 |
description_english | TEXT | 영어 문제 설명 |
description_korean | TEXT | 한국어 문제 설명 |
constraints_english | TEXT[] | 영어 제약조건 배열 |
constraints_korean | TEXT[] | 한국어 제약조건 배열 |
examples | JSONB | 입출력 예제 (구조화된 데이터) |
solution_code | TEXT | 풀이 코드 |
solution_language | VARCHAR | 코드 언어 (typescript/javascript) |
explanation_korean | TEXT | 한국어 풀이 설명 |
approach_korean | TEXT | 한국어 접근 방법 |
time_complexity | VARCHAR | 시간 복잡도 (Big O) |
space_complexity | VARCHAR | 공간 복잡도 (Big O) |
slug | VARCHAR | SEO용 URL 슬러그 (유니크) |
leetcode_url | VARCHAR | LeetCode 문제 링크 |
github_url | VARCHAR | GitHub 솔루션 링크 |
is_premium | BOOLEAN | 프리미엄 문제 여부 |
acceptance_rate | DECIMAL | 문제 통과율 |
submission_count | INTEGER | 제출 횟수 |
created_at | TIMESTAMP | 생성 시각 |
updated_at | TIMESTAMP | 최종 수정 시각 |
테이블 설계 변경 이유
1. 다국어 지원 확장
- 변경:
title
→title
+title_korean
- 이유: 블로그에서 한국어/영어 제목을 모두 표시하기 위해
- 장점: SEO 최적화와 사용자 경험 개선
2. 문제 데이터 세분화
- 변경: 단일
explanation
→description
,constraints
,examples
분리 - 이유: LeetCode 문제 구조를 정확히 반영하고 개별 번역 관리
- 장점: 데이터 정규화와 번역 품질 향상
3. 복잡도 분석 추가
- 추가:
time_complexity
,space_complexity
필드 - 이유: 알고리즘 학습에 필수적인 복잡도 정보 제공
- 장점: 교육적 가치 향상과 성능 분석 자동화
4. ID 타입 변경
- 변경:
UUID
→SERIAL
+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>
해결 과정
- 원인 분석: OpenAI API가 HTML 형태로 응답하여 마크다운 렌더링 실패
- 1차 시도: 프롬프트 엔지니어링으로 마크다운 출력 요청
- 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
해결 과정
- package.json 설정 변경
{ "type": "module", "scripts": { "fix:html": "tsx scripts/fix-html-codeblocks.ts" } }
- Import 구문 통일
// 변경 전 const fs = require('fs'); // 변경 후 import { readFileSync, writeFileSync, existsSync } from 'fs';
- ES 모듈 시스템 적용
- CommonJS에서 ES 모듈로 전환
package.json
에"type": "module"
설정- 최신 JavaScript 표준 준수
🔄 계획 대비 주요 변경 사항
1. 아키텍처 복잡도 증가
원래 계획
GitHub → 간단한 파싱 → Supabase → 블로그
실제 구현
GitHub → 번역 시스템 → HTML 정리 → 구조화 파싱 →
복잡도 분석 → Supabase → 캐싱 → 블로그 API →
SEO 최적화 → 렌더링
변경 이유:
- 실제 사용자 경험을 고려한 품질 향상
- SEO 최적화의 중요성 인식
- 다국어 지원의 복잡성
2. 기술 스택 확장
구분 | 계획 | 실제 | 변경 이유 |
---|---|---|---|
번역 | Google Translate API | OpenAI GPT-4o-mini | 맥락 이해와 품질 |
실행 환경 | Node.js | TypeScript | 타입 안전성 |
데이터 처리 | 단순 파싱 | 구조화된 서비스 계층 | 확장성과 유지보수성 |
에러 처리 | 기본 try-catch | 체계적인 에러 핸들링 | 운영 안정성 |
3. 개발 기간 차이
- 예상: 길어야 1주 정도 (2025.07.31 계획)
- 실제: 2일 (8월 1일: 4-5시간, 8월 4일: 5시간)
- 단기간 완성 가능했던 이유:
- Claude Code와의 효율적인 바이브 코딩
- 기본 아키텍처 설계에 집중
- 핵심 기능 위주의 MVP 구현
- AI 번역 품질을 한 번에 높은 수준으로 구현
🚀 다음 단계: jin-blog 연동
현재 상태
- ✅ 데이터 수집 및 저장 시스템 완성
- ✅ 번역 및 정리 파이프라인 구축
- ✅ API 및 데이터 구조 준비 완료
다음 작업 계획
-
jin-blog 프로젝트 연동
- Next.js 14 App Router 기반 페이지 구축
- Supabase 클라이언트 연동
- 동적 라우팅 구현
-
UI/UX 개발
- 문제 목록 페이지 (필터링, 정렬)
- 개별 문제 상세 페이지
- 코드 하이라이팅 및 복잡도 시각화
-
SEO 최적화
- 메타데이터 자동 생성
- 구조화된 데이터 (JSON-LD)
- 사이트맵 자동 생성
예상 추가 개발 기간
- 현실적 목표: Claude Code와의 효율적인 협업 경험을 바탕으로 1-2일 내 완성
🎯 핵심 학습 내용
기술적 인사이트
-
AI 번역의 중요성
- 단순 번역 vs 맥락 기반 번역의 품질 차이 체감
- 프롬프트 엔지니어링의 중요성 인식
-
GitHub Actions 운영 노하우
- 워크플로우 분리의 중요성
- 동시성 제어와 에러 처리 패턴
-
TypeScript ESM 생태계
- 모듈 시스템의 복잡성과 해결책
프로젝트 관리 관점
-
점진적 개선의 가치
- 완벽한 계획보다 반복적 개선이 더 효과적
- 사용자 피드백(자신의 사용 경험)이 방향성 결정에 중요
-
품질 vs 속도의 균형
- 처음 목표했던 "빠른 구현"에서 "지속 가능한 시스템"으로 관점 전환
- 장기적 관점에서의 투자 가치
-
문서화의 중요성
- 복잡해진 시스템을 다른 프로젝트에서 활용하기 위한 가이드 문서 작성
- 미래의 자신을 위한 컨텍스트 보존
🤖 Claude Code와의 바이브 코딩 경험
당초 계획에서 강조했던 **"Claude Code와의 바이브 코딩"**을 실제로 경험해본 솔직한 후기입니다.
기대했던 것 vs 실제 경험
✅ 기대 이상이었던 부분
-
복잡한 정규식 처리
- HTML을 마크다운으로 변환하는 40+ 규칙 작성
- 인간이 하기 번거로운 반복적 패턴 매칭 작업을 Claude가 정확하게 처리
-
에러 디버깅 속도
- GitHub Actions 동시성 문제 해결 시 즉시 원인 파악과 해결책 제시
- TypeScript ESM 모듈 문제도 빠르게 해결
-
문서 작성
- 현재 작성 중인 이 블로그 포스트처럼 체계적인 문서 구조화
- 코드 주석과 README 작성에서 뛰어난 품질
-
컨텍스트 엔지니어링의 효과
CLAUDE.md
파일을 통한 프로젝트 컨텍스트 관리- 일관성 있는 코드 스타일과 아키텍처 유지
- 기능 구현 시 이전 결정사항들이 자연스럽게 반영됨
-
MCP (Model Context Protocol) 활용
- Supabase MCP를 통한 직접적인 데이터베이스 조작
- API 문서를 찾아보지 않고도 즉시 스키마 수정 및 쿼리 실행 가능
- 개발 속도 향상과 실수 방지에 큰 도움
⚠️ 한계가 있었던 부분
-
전체 아키텍처 설계
- 시스템 전반의 설계 결정은 여전히 개발자의 판단이 중요
- Claude는 제안을 잘 하지만, 최종 결정은 컨텍스트를 이해하는 개발자가 해야 함
-
비즈니스 로직의 미묘한 부분
- 언제 번역을 다시 실행할지, 어떤 우선순위로 처리할지 등은 도메인 지식 필요
- 데이터 일관성 보장을 위한 전략 수립
-
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 협업의 핵심:
- 프롬프트 엔지니어링을 넘어선 컨텍스트 엔지니어링의 중요성
- MCP 같은 도구를 통한 직접적이고 효율적인 개발 환경 구축
- Rate limit 제약을 고려한 개발 계획 수립의 필요성
- CLAUDE.md 파일을 통한 프로젝트 일관성 유지
앞으로는 이러한 AI 협업 패턴을 더 많은 프로젝트에 적용하여, 개발자로서의 생산성과 코드 품질을 동시에 향상시키는 방법을 계속 탐구해보겠습니다.
다음에는 jin-blog 프로젝트와의 연동 과정을 진행하며, 실제 사용자가 볼 수 있는 블로그 페이지까지 완성해보겠습니다.