AppHub 프로젝트 구조 재설계
몇 달 전 시작했다가 멈췄던 AppHub 프로젝트를 처음부터 다시 시작한다. docs만 남기고 전체 초기화 후, 5개의 Docker 컨테이너(Traefik, PostgreSQL+pgvector, Redis, ai-service, web)와 Bun 기반 Monorepo 구조로 재구성. Python은 uv, TypeScript는 Bun을 사용하며, LangGraph 기반 블로그 검색 기능을 ai-service에 통합하는 방식으로 설계했다.
프로젝트 구조 한눈에 보기
Docker 컨테이너 (총 5개)
services:
traefik # 리버스 프록시 (포트 80, 443, 8080)
postgres # PostgreSQL 16 + pgvector (포트 5432)
redis # 캐시 & 세션 (포트 6379)
ai-service # Python LangGraph (포트 8000)
web # Next.js 15 (포트 3000)
디렉토리 구조
apphub/
├── apps/
│ ├── web/ # Next.js 15 + React 19 (TypeScript)
│ └── ai-service/ # Python LangGraph + FastAPI
├── packages/
│ ├── ui/ # ShadCN 공통 컴포넌트
│ ├── database/ # SQL 스키마 정의
│ ├── eslint-config/ # ESLint 공유 설정
│ └── typescript-config/ # TypeScript 공유 설정
├── docker/
│ ├── compose.yaml # Docker Compose 설정
│ └── configs/ # 서비스별 설정 파일
└── docs/ # 기획 문서
기술 스택
컴포넌트 | 기술 |
---|---|
Frontend | Next.js 15 + React 19 + Bun |
Backend | Python 3.11 + LangGraph + uv |
Database | PostgreSQL 16 + pgvector |
Cache | Redis 7 |
Proxy | Traefik v2 |
Monorepo | Turborepo + Bun workspaces |
개발 환경
# Docker로 인프라 실행
docker compose up -d postgres redis traefik
# 앱은 로컬에서 실행
cd apps/web && bun dev # localhost:3000
cd apps/ai-service && uv run langgraph dev # localhost:8000
# 또는 전체 Docker로 실행
docker compose up -d # 전체 스택
데이터 흐름
User → Traefik
↓
┌────┴────┐
↓ ↓
web ai-service
↓ ↓
└─→ PostgreSQL ←┘
↓
pgvector (블로그 검색)
프로젝트 현황
기존 상태
- 기획 문서만 업데이트된 상태
- 이전 코드는 pnpm 기반 (문서와 불일치)
- 프로젝트 구조가 명확하지 않음
결정: 전체 초기화
docs 폴더만 남기고 전부 삭제하고 다시 시작하기로 결정.
이유:
- 기획이 크게 바뀜 (Bun, 최신 기술 스택)
- 깔끔한 시작이 더 빠름
- 명확한 구조로 재설계
Docker 컨테이너 설계
필요한 컨테이너 (총 5개)
services:
traefik # 리버스 프록시 (80, 443, 8080)
postgres # PostgreSQL 16 + pgvector (5432)
redis # 캐시 & 세션 (6379)
ai-service # Python LangGraph (8000)
web # Next.js 15 (3000)
컨테이너별 역할
traefik
- Nginx 대신 선택 (자동 SSL, Docker 통합)
- 라우팅:
/
→ web,/api
→ ai-service - 대시보드:
localhost:8080
postgres
- PostgreSQL 16 + pgvector 확장
- 블로그 포스트 메타데이터
- 벡터 임베딩 저장 (RAG용)
redis
- 세션 관리 (better-auth)
- API 응답 캐싱
- LangGraph 체크포인트
ai-service
- Python + LangGraph + FastAPI
- 블로그 검색 기능 내장
- OpenAI API 호출
web
- Next.js 15 + React 19
- ShadCN UI
- AI 챗봇 UI
프로젝트 디렉토리 구조
apphub/
├── apps/
│ ├── web/ # Next.js 웹 앱
│ └── ai-service/ # Python AI 서비스
│
├── packages/
│ ├── ui/ # 공통 UI 컴포넌트
│ ├── database/ # SQL 스키마
│ ├── eslint-config/ # ESLint 설정
│ └── typescript-config/ # TS 설정
│
├── docker/
│ ├── compose.yaml # Docker Compose
│ └── configs/ # 설정 파일들
│
└── docs/ # 기획 문서 & 개발 로그
구조 설계 원칙
1. Monorepo (Turborepo)
- apps: 독립 실행 가능한 애플리케이션
- packages: 공유 코드/설정
2. 명확한 분리
- Frontend (web) ↔ Backend (ai-service)
- TypeScript ↔ Python
- 각자 독립적으로 개발 가능
3. 공유 최소화
- database만 SQL 스키마 공유
- 나머지는 각 앱에서 관리
기술 스택 최종 확정
레이어 | 기술 | 이유 |
---|---|---|
패키지 매니저 | Bun | 최신 기술 경험 + 속도 |
Monorepo | Turborepo | Next.js 팀 제작 |
Frontend | Next.js 15 + React 19 | 최신 안정 버전 |
Backend | Python 3.11 + uv | AI 생태계 + 빠른 패키지 |
AI Framework | LangGraph Platform | 세밀한 제어 가능 |
Database | PostgreSQL 16 | pgvector 지원 |
Proxy | Traefik | 자동화, 간편함 |
개발 환경 구성
Python (ai-service)
# uv로 패키지 관리
uv sync
# LangGraph Platform으로 실행
uv run langgraph dev
TypeScript (web)
# Bun으로 패키지 관리
bun install
# Next.js 개발 서버
bun dev
Docker (전체 스택)
# 전체 실행
docker compose up -d
# 로그 확인
docker compose logs -f
데이터 흐름 설계
User Request
↓
Traefik (Reverse Proxy)
↓
┌───────────┬─────────────┐
↓ ↓ ↓
web ai-service Traefik Dashboard
(3000) (8000) (8080)
↓ ↓
└─→ PostgreSQL ←┘
(5432)
↓
pgvector
(블로그 검색)
블로그 검색 아키텍처 결정
고민: MCP 분리 vs ai-service 통합
MCP 서버로 분리
- ✅ Claude Desktop/Cursor에서 재사용 가능
- ❌ 복잡도 증가, 개발 시간 증가
ai-service에 통합
- ✅ 단순함, 빠른 개발
- ✅ 직접 함수 호출 (낮은 오버헤드)
- ❌ 외부에서 재사용 불가
결정: 통합
이유:
- 빠른 MVP 검증이 우선
- 나중에 필요하면 분리 쉬움
- YAGNI 원칙 (You Aren’t Gonna Need It)
블로그 검색 기능 설계
LangGraph Tools (ai-service 내장)
# src/tools/blog_search.py
@tool
async def search_blog_by_meaning(query: str, limit: int = 5):
"""의미 기반 벡터 검색"""
pass
@tool
async def search_blog_by_keyword(keywords: str):
"""키워드 전문 검색"""
pass
@tool
async def search_blog_by_tags(tags: list[str]):
"""태그 필터링"""
pass
@tool
async def get_recent_posts(limit: int = 10):
"""최신 글 목록"""
pass
데이터베이스 스키마
-- blog_posts: 메타데이터
CREATE TABLE blog_posts (
id SERIAL PRIMARY KEY,
slug VARCHAR(255) UNIQUE,
title TEXT,
content TEXT,
published_at DATE,
tags TEXT[],
url TEXT,
search_vector TSVECTOR -- 전문 검색용
);
-- blog_embeddings: 벡터 검색용
CREATE TABLE blog_embeddings (
id SERIAL PRIMARY KEY,
post_id INTEGER REFERENCES blog_posts(id),
chunk_text TEXT,
embedding VECTOR(1536) -- OpenAI embedding
);
다음 단계
- ✅ 프로젝트 구조 설계 완료
- ⏭️ Bun + Turborepo 초기화
- ⏭️ Next.js 15 프로젝트 생성
- ⏭️ Python + uv + LangGraph 설정
- ⏭️ Docker Compose 작성
- ⏭️ PostgreSQL 스키마 생성
- ⏭️ 기본 개발 환경 테스트
회고
잘한 점
- 기획 먼저 확실히 정리
- 처음부터 다시 시작하는 용기
- 명확한 구조 설계
배운 점
- 기술 선택에 일관성 중요 (Bun vs pnpm)
- 복잡도보다 속도 (MCP 분리 → 통합)
- docs 남기는 습관 (나중에 큰 도움)
다음 목표
프로젝트 초기화 후 1주일 안에 기본 챗봇 동작까지 완성하기!
개발 철학
“완벽한 계획보다 동작하는 코드가 먼저다. 하지만 명확한 구조 없이 시작하면 나중에 더 고생한다.”
관련 문서
- 2025-06-28-AppHub-개인-프로젝트-플랫폼-기획 - 전체 프로젝트 기획
- 2025-07-26-AppHub-구조-및-기술-스택 - 기술 스택 선택 과정
- 2025-10-07-AppHub-Quartz-통합-전략 - 블로그 통합 전략