SBERT + ALS 하이브리드 알고리즘 기반의 영화 추천 서비스. 기업 고객이 B2B Console에서 API Key를 발급받아 자사 서비스에 연동하는 구조로, External API 분리 설계와 Rate Limiting 기반 플랜별 과금 체계를 구축했습니다. 카카오클라우드 2-Tier VPC 아키텍처 설계, 4계층 보안 방어 체계, CI/CD 파이프라인 자동화, Cron 기반 서버 운영 자동화를 담당했습니다.
핵심 성과
5,227 → 0
SSH 공격 차단
A+
SSL Labs 등급
4개 도메인
SSL 단일 서버 운영
인프라 아키텍처

KakaoCloud VPC10.0.0.0/16
| 서버 | Subnet | 서비스 (포트) | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
App Server Bastion Host | Public 10.0.0.0/20 | 보안그룹
| ||||||||||||||||||||
GPU Server Tesla T4 16GB | Private 10.0.32.0/20 | 보안그룹
|
문제 해결 1: SSH 브루트포스 공격 대응
01문제 발생
서버 운영 2주 만에 lastb로 1,107건 SSH 로그인 실패 기록 발견. 자동화된 봇의 브루트포스 공격.

02해결 과정
자동 차단
•fail2ban 도입 - 3회 실패 시 IP 자동 차단
수동 IP 차단은 비효율적. 로그 기반 자동 차단으로 운영 부담 최소화
포트 은닉
•SSH 포트 변경 (22 → 52222)
봇은 기본 포트 22만 스캔. 비표준 포트로 90% 이상 자동 공격 회피
다층 방어
•Defense in Depth - UFW + 보안그룹 이중 방화벽
단일 방화벽 설정 실수 시에도 2차 방어선 유지
네트워크 격리
•GPU Server Private Subnet 배치 + Public IP 제거
•Bastion Host (SSH ProxyJump) 구성
DB/AI 모델 보호. App Server 경유만 허용

03결과
| 서버 | 대응 전 | 대응 후 |
|---|---|---|
| App Server | 1,107건 | 침투 0건 (299 IP 차단) |
| GPU Server | 205건 | 공격 시도 0건 |

GPU Server - 신규 공격 0건

App Server - 포트 탐지됐으나 침투 차단
문제 해결 2: 서버 경량화 및 자동화
01문제 발생
App Server 스토리지 10GB SSD. Docker 이미지 1.2GB, 시스템 로그 427MB 누적.

02해결 과정
이미지 경량화
•Docker 멀티스테이지 빌드
빌드 환경(node_modules)과 실행 환경 분리. 최종 이미지에 빌드 도구 미포함
런타임 제거
•Nginx 정적 서빙으로 전환
SPA는 빌드 후 정적 파일만 필요. Node 런타임 제거로 리소스 절약
자동 정리
•Cron 자동화 스크립트 (매주 금요일)
수동 정리는 잊기 쉬움. 주간 스케줄로 디스크 풀 사전 예방
# 매주 금요일 자동 정리 0 17 * * 5 /home/ubuntu/scripts/weekly-cleanup.sh # journalctl 7일 이전 로그 삭제 + Docker prune
03결과
| 항목 | Before | After | 개선 |
|---|---|---|---|
| Docker 이미지 | 1.2GB | 95MB | 92% 감소 |
| 시스템 로그 | 427MB | 16MB | 96% 감소 |

freed 0B = 자동화 성공. 주간 정기 실행으로 쌓이기 전에 미리 정리됨.
문제 해결 3: CI/CD 파이프라인 구축
01문제 발생
수동 배포 시 휴먼 에러 발생. GPU 서버가 Private Subnet이라 GitHub Actions에서 직접 접근 불가.

02해결 과정
워크플로우 분리
•Path 기반 트리거 (frontend/backend/ai)
폴더별 독립 배포. 불필요한 빌드 방지
Private 서버 배포
•SSH ProxyJump 설정
App Server를 점프 호스트로 활용. GPU 서버 Public IP 없이 배포
시크릿 관리
•GitHub Secrets로 환경변수 분리
SSH 키, DB 비밀번호 등 민감 정보. 코드에 하드코딩 제거

03결과
deploy-frontend.yml
frontend/** 변경 → npm build → rsync → /var/www/
deploy-backend.yml
backend/** 변경 → SSH → docker compose up -d --build
deploy-gpu.yml
ai/** 변경 → SSH (ProxyJump) → GPU 서버 배포
dev/main push만으로 자동 배포. 수동 배포 대비 배포 시간 단축, 휴먼 에러 제거.

Before - 10m 49s

After - 1m 43s (83% 단축)
문제 해결 4: Nginx SSL/HTTPS 보안 설정
01문제 발생
4개 도메인(Landing, Demo, Console, API)을 단일 서버에서 HTTPS로 서빙해야 하는 상황. HTTP 평문 통신으로 인한 보안 취약점과, HTTPS 전환 시 SPA 라우팅 404, CORS 중복 헤더 등 복합적인 문제 발생.
02해결 과정
SSL 인증서
•Let's Encrypt 와일드카드 인증서로 4개 도메인 통합 관리
도메인별 개별 인증서 대신 와일드카드로 갱신 포인트 단일화. systemd timer 자동 갱신
TLS 강화
•TLS 1.2/1.3만 허용, 구버전 프로토콜 차단
HSTS max-age 2년 설정으로 브라우저 레벨에서 HTTPS 강제
보안 헤더
•X-Content-Type-Options, X-Frame-Options, Referrer-Policy 적용
MIME 스니핑, 클릭재킹, 정보 유출 방지. Nginx add_header 상속 이슈 해결
리버스 프록시
•도메인별 라우팅 분리 (정적 서빙 + API 프록시)
SPA try_files 폴백, 정적 파일 1년 캐시 + index.html no-cache 전략
03결과

SSL Labs A+ 등급

TLS 1.3, HSTS 적용 확인

moviesir.cloud - Landing Page

demo.moviesir.cloud - Demo App

console.moviesir.cloud - B2B Console

api.moviesir.cloud - External API
| 항목 | 설정 |
|---|---|
| SSL Labs 등급 | A+ |
| TLS 프로토콜 | 1.2 / 1.3 |
| HSTS | max-age=63072000 (2년) |
| 도메인 | 4개 서브도메인 단일 서버 운영 |
| 인증서 갱신 | 자동 (systemd timer) |
회고
배운 점
서버 2대라는 제한된 자원으로 VPC 설계부터 CI/CD 자동화까지 프로덕션 수준의 인프라를 직접 구축했다. 제약 조건 안에서 최적의 아키텍처를 설계하는 과정에서, 클라우드 인프라는 "완성"이 아니라 "운영하며 개선하는 것"이라는 걸 체감했다.
SSH 브루트포스 5,227건을 탐지하고 단계적으로 대응하면서, 보안은 한 번의 설정이 아니라 계층별로 쌓아야 한다는 걸 배웠다. fail2ban → 포트 변경 → 이중 방화벽 → Private Subnet 격리까지 Defense in Depth 전략을 실전에서 경험했다.
Cron 자동화와 SSL 설정을 직접 하면서, 수동 작업을 반복하기보다 자동화 스크립트로 만드는 습관이 운영 안정성에 직결된다는 걸 느꼈다.
개선하고 싶은 점
현재 서버 상태를 수동으로 확인하고 있는데, Prometheus + Grafana 기반 모니터링을 도입하여 CPU, 메모리, 디스크 사용량을 시각화하고 임계치 알림을 자동화하고 싶다.
Docker Compose 기반 배포를 Kubernetes로 전환하여 오토스케일링과 무중단 배포를 경험해보고 싶다.
Terraform으로 현재 수동 구성한 카카오클라우드 인프라를 코드로 관리(IaC)하여, 환경 재현성과 변경 이력 추적을 개선하고 싶다.