라벨이 데이터베이스인 게시물 표시

PostgreSQL로 YouTube 조회수 성장률 계산하기 (스냅샷 비교 방식 실전 예제)

이미지
YouTube 쇼츠 트렌드를 분석하면서 깨달은 게 있다. 조회수 자체는 별로 의미가 없다는 것이다. 조회수 100만짜리 영상도 6개월 전에 터진 거라면 지금 트렌드가 아니다. 반면 조회수 5,000인 영상이 어제 올라와서 24시간 만에 5,000이 찍혔다면 지금 뜨는 영상이다. 그래서 조회수 대신 성장률 을 기준으로 트렌드를 판단하는 구조를 만들었다. 현재 UGREEN DXP4800PLUS NAS에서 운영 중인 시스템에는 영상 약 1,100개, 스냅샷 약 74,000개가 쌓여 있다. 이 글은 그 구조를 만든 이유와 실제 PostgreSQL 쿼리를 정리한다. 왜 YouTube 조회수 스냅샷 방식이 필요한가 처음에는 단순하게 접근했다. 영상 정보를 수집할 때 현재 조회수를 같이 저장하면 된다고 생각했다. 문제는 바로 드러났다. 오늘 조회수: 12,000 어제 조회수: 알 수 없음 (덮어쓰여서 사라짐) 현재 값만 저장하면 변화량을 계산할 수 없다. 성장률은 이전 값과 현재 값의 차이 에서 나온다. 그래서 조회수를 덮어쓰는 대신 시간마다 찍어서 쌓는 스냅샷 방식을 선택했다. PostgreSQL 테이블 구조 설계 핵심 테이블은 두 개다. -- 영상 기본 정보 CREATE TABLE videos ( id SERIAL PRIMARY KEY, video_id VARCHAR UNIQUE NOT NULL, title VARCHAR, channel_name VARCHAR, tags TEXT, content_cluster VARCHAR, format_type VARCHAR, collected_at TIMESTAMP DEFAULT NOW() ); -- 시간별 조회수 스냅샷 CREATE TABLE video_snapshots ( id SERIAL PRIMARY KEY, video_id VARCHAR REFERENCES videos(video_id), ...

FastAPI와 PostgreSQL 연동하기 (Docker 환경 실전 예제)

UGREEN DXP4800PLUS NAS에서 FastAPI 기반 쇼츠 트렌드 수집 시스템을 운영하고 있다. 현재 DB에는 영상 700개 이상, 스냅샷 40,000개 이상이 쌓여 있고, 30분마다 자동으로 수집·저장이 돌아간다. 이 구조를 만들면서 FastAPI와 PostgreSQL 연동에서 막혔던 부분들을 정리한다. 단순한 예제 코드가 아니라 Docker Compose 환경에서 실제로 동작하는 구성을 기준으로 설명한다. 현재 운영 환경 NAS: UGREEN DXP4800PLUS (Debian 12 기반) FastAPI: Docker 컨테이너로 실행 PostgreSQL: 동일 Docker Compose 내 별도 컨테이너 ORM: SQLAlchemy 2.0 (비동기, asyncpg 드라이버) DB명: shorts / 주요 테이블: videos, snapshots 1. Docker Compose 구성 FastAPI와 PostgreSQL을 같은 Compose 파일 안에 두면 컨테이너 이름으로 서로 통신할 수 있다. services: db: image: postgres:15 environment: POSTGRES_DB: shorts POSTGRES_USER: user POSTGRES_PASSWORD: password volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d shorts"] interval: 5s retries: 5 api: build: . ports: - "8105:8000" environment: DATABASE_URL: postgresql+asyncp...

Docker Compose depends_on이 제대로 동작하지 않는 이유와 해결법

이미지
 Docker Compose에서 depends_on 을 설정했는데도 FastAPI 컨테이너가 PostgreSQL 연결에 실패했다. 분명히 postgres 가 먼저 시작되도록 설정했는데 왜 이런 문제가 발생한 걸까? 로그를 보니 이런 오류가 찍혀 있었다. could not connect to server: Connection refused Is the server running on host "postgres" and accepting TCP/IP connections on port 5432? UGREEN DXP4800PLUS NAS에서 쇼츠 자동화 시스템을 운영하면서 이 문제를 겪었다. depends_on 을 추가해도 해결이 안 됐고, 결국 healthcheck 까지 조합해야 완전히 해결됐다. 이 글에서는 원인과 해결 방법을 정리한다. depends_on에 대한 오해 많은 사람이 depends_on 을 이렇게 이해한다. postgres 준비 완료 ↓ api 시작 실제 동작은 이렇다. postgres 프로세스 시작 ↓ api 시작 depends_on 은 컨테이너 시작 순서 만 보장한다. 서비스가 실제로 사용 가능한 상태인지는 확인하지 않는다. 아래 설정이 바로 문제의 원인이다. yaml # 잘못된 구성 - 여전히 연결 실패 가능 services : api : depends_on : - postgres postgres : image : postgres : 15 - alpine postgres 컨테이너가 시작됐다고 해서 PostgreSQL이 접속 가능한 상태라는 의미가 아니다. 왜 이런 문제가 발생하는가 PostgreSQL은 컨테이너가 시작된 후 실제로 접속 가능한 상태가 되기까지 여러 단계를 거친다. 컨테이너 시작 ↓ PostgreSQL 프로세스 시작 ↓ 데이터베이스 초기화 ↓ WAL 복구 (재시작 시) ↓ 접속 가능 상태 이 과정이 수 초에서 수십 초까지 걸릴 수 있다. d...

Docker 재시작 후 PostgreSQL 데이터가 사라지는 이유와 영구 저장 방법

이미지
 Docker로 PostgreSQL을 운영하다가 한 번쯤은 이런 상황을 겪게 된다. 컨테이너를 재생성했더니 데이터베이스가 텅 비어 있었다. 처음에는 PostgreSQL 자체 문제인 줄 알았다. 로그를 확인하고 설정을 점검했지만 이상이 없었다. 원인은 Docker 볼륨 설정이었다. UGREEN DXP4800PLUS NAS에서 쇼츠 자동화 시스템과 트레이딩 봇을 운영하면서 이 문제를 직접 겪었다. 이 글에서는 PostgreSQL 데이터가 삭제되는 원인과 NAS 환경에서 데이터를 영구적으로 보존하는 방법을 정리한다. 왜 데이터가 사라지는가 Docker 컨테이너는 애플리케이션 실행 환경만 보관한다.  데이터를 별도 저장소에 보관하지 않으면 컨테이너 삭제 시 함께 사라진다. 가장 흔한 실수는 볼륨 설정 없이 PostgreSQL을 실행하는 것이다. yaml # 잘못된 구성 - 데이터가 사라짐 services : postgres : image : postgres : 15 - alpine environment : POSTGRES_DB : mydb POSTGRES_USER : myuser POSTGRES_PASSWORD : mypassword 이 상태에서 docker compose down 후 docker compose up 을 다시 실행하면 데이터가 사라진다. docker compose down 은 컨테이너를 삭제하기 때문이다. bash # 이 명령어가 데이터를 삭제한다 docker compose down # 이건 괜찮음 (컨테이너 중지만, 삭제 안 함) docker compose stop Docker Volume 개념 데이터를 영구 보존하려면 볼륨(Volume)을 사용해야 한다. Docker Volume 방식은 두 가지다. Named Volume (도커 관리 볼륨) yaml services : postgres : image : postgres :...

UGREEN DXP4800PLUS NAS로 유튜브 쇼츠 자동화 시스템 구축기 (Docker · FastAPI · PostgreSQL · Edge-TTS)

이미지
 집에 NAS가 있다면 유튜브 쇼츠 자동화 시스템을 직접 구축할 수 있다. 이 글은 UGREEN DXP4800PLUS NAS 위에서 실제로 운영 중인 쇼츠 자동화 파이프라인 구축 과정을 기록한 실전 후기다. 현재 운영 현황 (2026년 6월 기준) 수집 영상: 676개 저장 스냅샷: 38,134개 성장률 계산 성공률: 95.1% 트렌드 클러스터: 10종 (IT_DEVICE, AI/자동화, 연예/엔터 등) 자동 영상 생성: Video Factory v2 개발 중 단순 성공 사례가 아니다. FastAPI 404 오류, 분류기 정확도 문제, 영상 생성 실패까지 실제 겪은 삽질과 해결 과정을 함께 담았다. 전체 파이프라인 구조 YouTube API 수집 (30분마다) ↓ PostgreSQL 저장 ↓ 성장률 계산 ↓ content_cluster 분류 (10종) ↓ format_type 분류 (6종) ↓ 트렌드 대시보드 ↓ AI 대본 자동 생성 ↓ Edge-TTS 음성 생성 ↓ FFmpeg 영상 합성 ↓ drafts 폴더 저장 처음에는 간단한 스크립트 몇 개면 끝날 줄 알았다. 실전은 달랐다. FastAPI app.mount("/") 사용 시 API 404 오류 해결 방법 FastAPI에서 정적 파일을 서빙할 때 이런 코드를 썼다. # 오류 코드 - API가 전부 404 app . mount ( "/" , StaticFiles ( directory = "static" , html = True ) , name = "static" ) @app . get ( "/api/trends" ) async def get_trends ( ) : . . . API 호출마다 404가 났다. 원인은 라우팅 우선순위였다. app.mount("/") 가 상단에 있으면 뒤에 선언된 모든 엔드포인트가 무시된다. # 해결 코드 - 항상 moun...