1. 서버 전체 구성도
인터넷 ↓ ┌─────────────────────────────────────────────────────────┐ │ Oracle Cloud 마이크로 인스턴스 (무료) │ │ • CPU: 1 vCPU (AMD EPYC 7551) │ │ • RAM: 1GB │ │ • Storage: 47GB │ │ • OS: Ubuntu 22.04 LTS │ │ • IP: 144.24.79.13 │ └─────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ Docker 컨테이너 환경 │ │ ┌─────────────┬─────────────┬────────────────────────────┐ │ │ │ - Backend │ - Redis │ - Nginx (Reverse Proxy) │ │ │ │ Node.js │ Cache │ Load Balancer │ │ │ │ Express │ In-Memory │ Static Files │ │ │ │ Port: 3001 │ Port: 6379 │ Port: 80/443 │ │ │ └─────────────┴─────────────┴────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘
2. Docker 컨테이너 구성
컨테이너 | 이미지 | 메모리 제한 | CPU 제한 | 역할 |
Backend | Node.js 18 Alpine | 200MB | 0.7 | API 서버 + AI 분석 |
Redis | Redis 7 Alpine | 80MB | 0.2 | 캐싱 시스템 |
Nginx | Nginx Alpine | 50MB | 0.1 | 리버스 프록시 |
Alpine(알파인)은 운영체제(OS) Alpine Linux(알파인 리눅스) 의미
Alpine Linux: 경량화와 보안에 초점을 맞춰 개발된 미니멀한 Linux 배포판.
일반적인 등산 장비가 아닌, 필수적인 것만 챙겨 가볍게 오르는 '알파인 스타일' 등반처럼,
이 운영체제도 꼭 필요한 핵심 기능만 담고 있음
Alpine 버전 장점
일반적인 Node.js 이미지(
node:18)는 Debian 같은 범용 OS를 기반으로 하여 기능이 많은 대신 용량이 큼. - 매우 작은 크기: 일반적인 Node.js 이미지의 크기가 수백 MB인 것에 비해, Alpine 기반 이미지는 수십 MB에 불과. 이로 인해 빌드 및 배포 속도가 빨라지고 저장 공간을 절약할 수 있음
- 보안 강화: 운영체제에 포함된 패키지와 라이브러리가 적기 때문에, 잠재적인 보안 취약점(공격 지점)이 그만큼 줄어듦
- 단순함: 꼭 필요한 요소만 포함되어 있어 환경이 깔끔하고 예측 가능성이 높음
3. 메모리/CPU 최적화 기법
메모리 최적화
1. Node.js 메모리 제한
# Dockerfile에서 CMD ["node", "--max-old-space-size=128", "dist/app.js"] # Docker Compose에서 environment: - NODE_OPTIONS=--max-old-space-size=128 mem_limit: 200m
2. Redis 경량화
# 64MB만 사용 + LRU 정책 redis-server --maxmemory 64mb --maxmemory-policy allkeys-lru
3. Alpine Linux 사용
- 기본 Ubuntu: ~72MB → Alpine: ~5MB
- 불필요한 패키지 모두 제거
4. Multi-stage 빌드
# 빌드 스테이지: 개발 도구 포함 FROM node:18-alpine AS builder # 런타임 스테이지: 필수 파일만 FROM node:18-alpine
CPU 최적화
1. 워커 프로세스 최적화
worker_processes 1; # CPU 1개에 맞춤 worker_connections 512;
2. 연결 재사용
keepalive 2; # 연결 재사용으로 CPU 절약
3. Gzip 압축
gzip on; # CPU 사용해서 대역폭 절약
4. 데이터베이스 (SQLite) 구조
저장 위치
/app/data/criti-ai.db (컨테이너 내부) ↓ Docker Volume: app_data (호스트에 영구 저장)
주요 테이블
analysis_cache -- 분석 결과 캐시 (24시간) users -- 사용자 정보 challenges -- 챌린지 데이터 challenge_results -- 사용자 답안 결과 user_badges -- 배지 시스템 analysis_stats -- 일일 통계
SQLite 선택 이유
- 메모리 효율성: PostgreSQL 대비 적은 메모리 사용
- 단순성: 별도 DB 서버 불필요
- 성능: 단일 사용자 환경에서 충분히 빠름
- 안정성: 파일 기반으로 백업 쉬움
5. 캐싱 시스템
요청 처리 흐름 1. 사용자 요청 ↓ 2. Redis 캐시 확인 (0.1초) ↓ cache miss 3. SQLite DB 캐시 확인 (0.3초) ↓ cache miss 4. 메모리 캐시 확인 (0.01초) ↓ cache miss 5. Gemini AI 분석 (3-10초) ↓ 6. 모든 캐시에 저장 ↓ 7. 사용자에게 응답
캐시 저장 우선순위
- Redis (1순위): 빠른 접근 + 만료 관리
- SQLite (2순위): 영구 보관 + 통계 활용
- Memory (3순위)
6. API 엔드포인트 전체 목록
분석 API (/api/analysis)
엔드포인트 | 메소드 | 설명 | 응답 시간 |
/analyze | POST | 뉴스 기사 AI 분석 | 0.1초 (캐시) ~ 30초 (AI) |
/quick-check | POST | 빠른 신뢰도 체크 | 0.1초 |
챌린지 API (/api/challenge)
엔드포인트 | 메소드 | 설명 | 기능 |
/challenges | GET | 모든 챌린지 조회 | 난이도별 필터 |
/challenges/:id | GET | 특정 챌린지 조회 | 상세 정보 |
/challenges/:id/submit | POST | 답안 제출 및 채점 | 배지 지급 |
/progress/:userId | GET | 사용자 진행도 | 레벨/배지 시스템 |
/generate | POST | AI 챌린지 생성 | Gemini AI 활용 |
/stats | GET | 전체 통계 | 대시보드용 |
시스템 API
엔드포인트 | 설명 | 모니터링 |
/health | 서버 상태 확인 | 1분마다 체크 |
7. 실행 과정
1. 서버 부팅
1. Ubuntu 시스템 부팅 2. Docker 서비스 시작 3. docker-compose.micro.yml 실행 4. 이미지 다운로드 (최초만) 5. 컨테이너 생성 및 시작
2. 컨테이너 초기화
Backend ├── Node.js 프로세스 시작 ├── Prisma DB 연결 ├── Redis 연결 대기 ├── Gemini API 키 검증 └── Express 서버 시작 (포트 3001) Redis ├── Redis 서버 시작 (포트 6379) ├── 64MB 메모리 할당 └── LRU 정책 활성화 Nginx ├── 설정 파일 로드 ├── 백엔드 연결 확인 └── 포트 80 리스닝 시작
3. 서비스 준비 완료
요청 처리 과정
사용자가 뉴스 분석 요청 시
1. 크롬 확장 → Background Script 2. Background Script → Nginx (포트 80) 3. Nginx → Backend (포트 3001) 4. Backend → Redis 캐시 확인 5. 캐시 없으면 → Gemini AI 호출 6. 결과를 3단계 캐시에 저장 7. 사용자에게 응답 반환
응답 속도 최적화
- 캐시 우선: 대부분요청은 캐시에서 처리 (0.1초)
- 연결 재사용: Keep-Alive로 연결 오버헤드 최소화
- Gzip 압축: 응답 크기 감소
메모리 최적화
- Node.js 힙 제한: 128MB로 고정
- Redis 메모리 제한: 64MB + LRU 정책
- Alpine Linux: 최소 OS 이미지
- Multi-stage Build: 런타임 이미지 최소화
CPU 최적화
- 워커 프로세스: CPU 코어 수에 맞춤
- 비동기 처리: Node.js 이벤트 루프 활용
- 캐싱 전략: CPU 집약적 AI 분석 최소화