MinhoJung 59 9
게시물 메뉴

아직 완료된 결정은 아니고 진행 상태 공유합니다.

참고하시거나 도움 주실 분들 댓글 부탁해요.


Ankk SNS API 인프라 구조 결정 정리

작성일: 2026-09-11

 

1. 전체 방향

 

Ankk의 SNS 연동 작업은 공급자(provider)별 Cloudflare Worker로 분리한다.

예: Meta Worker, YouTube Worker, TikTok Worker 등.

GitHub 모노레포 안에서 각 Worker를 별도 배포 단위로 운영하고, 공급자별 API 특성·웹훅·레이트 리밋 정책을 각각 독립적으로 관리한다.

 

기존 Trigger.dev가 VPS에서 담당하던 SNS API 호출·대기·재시도 중심 작업은 가능한 범위에서 Cloudflare Workers + Workflows로 이전한다.

CPU 집약적인 동영상 변환 작업은 당분간 VPS에 남긴다.

 

2. Workers와 Workflows 역할

 

Worker

- 외부 요청 수신

- SNS API 호출

- 웹훅 수신 및 검증

- Workflow 인스턴스 생성

- 짧은 실시간 로직 처리

 

Workflow

- 장시간 이어지는 비동기 작업

- 단계별 실행 상태 유지

- 쿼터 부족 시 reset 시점까지 sleep 후 재개

- 재시도 및 장기 작업 오케스트레이션

 

Workflow는 일반 함수와 비슷하지만, 별도 실행 인스턴스로 상태가 관리되는 durable한 비동기 함수로 이해한다.

Worker가 Workflow를 시작한 뒤 Worker 요청 자체는 종료될 수 있고, Workflow는 독립적으로 계속 실행된다.

 

예: YouTube 업로드 쿼터가 부족한 경우

1) Workflow 시작

2) 쿼터 확인

3) 부족하면 resetAt까지 sleep

4) 깨어난 뒤 다시 쿼터 확인

5) 가능하면 업로드 API 호출

 

Queue는 처음부터 필수로 사용하지 않는다.

현재 요구사항에서는 Workflows만으로 대기·재시도 처리가 가능하다.

대량 작업이 순간적으로 몰려 별도 버퍼링이 필요할 때 Queue 도입을 검토한다.

 

3. 병렬 실행과 확장

 

SNS API 호출처럼 I/O 중심 작업은 Workers/Workflows의 자동 확장을 활용한다.

VPS에서 Trigger.dev 작업이 동시에 몰려 CPU 사용률이 급증하는 문제를 줄일 수 있다.

 

대량 작업은 한 Worker invocation 안에서 외부 API 요청을 무리하게 대량 병렬 호출하기보다, 작업 단위로 Workflow 인스턴스를 분리하는 방향을 사용한다.

예: 게시 작업 100개 → 필요 시 Workflow 인스턴스 100개로 분리.

 

이 구조로 이동하면 주요 병목 관리 대상은 서버 CPU/로드밸런싱보다 다음 두 가지로 이동한다.

- PostgreSQL 연결 및 쿼리 부하

- SNS 공급자별 API rate limit / quota

 

추가로 모든 작업은 멱등성(idempotency)을 고려해 중복 실행에도 안전하도록 설계한다.

 

4. PostgreSQL / Hyperdrive

 

PostgreSQL은 기존 VPS를 계속 사용한다.

현재 주요 서버 사양: AMD 16코어 / RAM 64GB / NVMe 2TB.

 

Workers에서 PostgreSQL 접근은 Hyperdrive를 사용한다.

PostgreSQL의 max_connections를 무조건 높이기보다는 Hyperdrive connection pool을 이용해 실제 origin 연결 수를 제한한다.

 

운영 방향

- 최신 데이터/쓰기용 Hyperdrive: 캐시 없이 사용

- 분석/통계용 Hyperdrive: 별도 구성, 읽기 중심 캐시 사용

 

분석 데이터는 실시간성이 중요하지 않으므로 5~10분 정도 지연을 허용할 수 있다.

따라서 분석 전용 Hyperdrive에서는 캐시를 적극적으로 활용한다.

캐시 설정이 있는 Hyperdrive에서도 쓰기 자체는 가능하지만, 코드 레벨에서는 사실상 읽기 전용으로 사용하고 쓰기는 primary 경로로만 보내는 방향이 안전하다.

 

5. API Rate Limit 관리 원칙

 

모든 공급자의 rate limit을 동일한 방식으로 관리하지 않는다.

쿼터가 누구에게 귀속되는지(scope)에 따라 관리 강도를 다르게 한다.

 

A. 사용자/계정 단위 제한

예: Meta 계열처럼 개별 사용자 또는 계정별로 한도가 분리되는 경우.

 

이 경우 중앙에서 모든 사용량을 정밀하게 선차감하는 시스템을 처음부터 만들 필요는 없다.

우선 SNS API 응답과 응답 헤더를 기준으로 대응한다.

 

- 정상 호출 → 그대로 진행

- rate limit 응답 → Workflow에서 대기 후 재시도

- 필요 시 사용자에게 지연/실패 알림

- exponential backoff 및 최대 재시도 횟수 적용

 

한 사용자의 트래픽이 다른 사용자 쿼터를 소모하지 않으므로 앱 전체 관점의 중앙 조정 필요성이 상대적으로 낮다.

 

B. 앱/프로젝트 단위 제한

예: YouTube처럼 여러 Ankk 사용자가 하나의 앱/프로젝트 quota를 공유하는 경우.

 

이 경우 중앙 quota 관리가 필요하다.

동시에 많은 Workflow가 같은 quota를 소비할 수 있기 때문이다.

이 용도에는 Durable Objects를 우선 검토한다.

 

6. Durable Objects의 역할

 

Durable Object는 특정 키에 대해 Cloudflare가 상태와 실행 순서를 관리하는 작은 상태 관리자 역할로 사용한다.

 

Ankk에서는 모든 SNS에 일괄 적용하지 않고, 공유 quota처럼 동시성 제어가 실제로 필요한 경우에만 사용한다.

 

특히 YouTube 앱 단위 quota 관리에 적합하다.

여러 Workflow가 동시에 quota를 확인하더라도 동일한 Durable Object를 통과하게 하면 quota 확인과 예약/차감을 원자적으로 처리할 수 있다.

 

Meta처럼 사용자별 quota가 완전히 분리돼 있는 공급자는 Durable Object를 처음부터 사용자별로 생성하지 않고, API 응답 기반 대응으로 단순하게 시작한다.

 

7. YouTube 계층형 Quota 관리

 

YouTube 쿼터는 다음과 같은 계층 구조로 본다.

 

- 통합(Global) quota

 - Bucket A

 - Bucket B

 - Bucket C

 - 필요 시 추가 bucket

 

API 호출 전에 두 조건을 모두 확인한다.

 

1) 통합 quota가 남아 있는가?

2) 해당 API가 사용하는 bucket quota가 남아 있는가?

 

둘 다 가능할 때만 호출한다.

통합 quota가 소진됐다면 개별 bucket에 잔여량이 있어도 해당 작업은 중단/대기한다.

 

예: Bucket A를 사용하는 API

Global quota 확인 → A quota 확인 → 둘 다 가능하면 예약/차감 → API 호출

 

8. Quota 차감 시점

 

Quota는 API 호출 완료 후 차감하지 않는다.

API 호출 직전에 ‘확인 + 예약(선차감)’을 원자적으로 수행한다.

 

이유:

동시에 여러 Workflow가 같은 잔여량을 확인한 뒤 모두 API를 호출하면 실제 quota를 초과할 수 있기 때문이다.

 

권장 패턴

1) Durable Object에서 global + bucket 잔여량 확인

2) 둘을 한 번에 예약/차감

3) 성공한 작업만 실제 API 호출

4) API 응답에서 실제 소비량이나 최신 quota 정보가 확인되면 내부 상태 보정

 

즉, 선예약 → API 호출 → 후보정 패턴을 사용한다.

 

9. Webhook 우선 전략

 

SNS 공급자가 webhook을 제공하는 데이터는 polling보다 webhook을 우선한다.

 

예:

- 게시 상태 변경

- 댓글 이벤트

- 기타 공급자가 push 가능한 이벤트

 

Webhook을 사용할 수 없는 데이터만 cron/polling으로 수집한다.

이렇게 하면 API 사용량과 불필요한 주기적 호출을 크게 줄일 수 있다.

 

10. 동영상 포맷 변환

 

SNS별 동영상 규격에 맞추기 위한 FFmpeg 변환 작업은 당분간 VPS에 남긴다.

 

이유

- 이미 보유한 VPS 리소스가 충분히 남아 있음

- FFmpeg는 CPU 집약적 작업

- 고정비로 확보한 서버 자원을 활용하는 것이 현재는 경제적

 

구조

Workflow → 변환 필요 판단 → VPS 변환 서비스 호출 → FFmpeg 작업 → 결과를 R2에 저장 → 완료 후 Workflow 재개 → SNS 업로드

 

VPS 변환기는 SNS API orchestration과 분리해서 단순한 미디어 변환 서비스로 축소하는 방향이 좋다.

동시 FFmpeg 실행 수는 VPS CPU/메모리에 맞춰 제한한다.

 

향후 변환량이 증가하고 자동 확장이 중요해지면 Cloudflare Containers + FFmpeg를 검토한다.

R2를 소스/결과 저장소로 사용하면 R2 egress 부담도 낮출 수 있다.

 

11. 최종 결정 요약

 

현재 결정된 방향은 다음과 같다.

 

- SNS 공급자별 Cloudflare Worker 분리

- GitHub 모노레포에서 각각 독립 배포

- SNS API orchestration은 Workers + Workflows 중심으로 이전

- Webhook 적극 활용, polling 최소화

- Queue는 당장 도입하지 않고 필요 시 추가

- PostgreSQL은 VPS 유지, Workers에서는 Hyperdrive 사용

- 최신/쓰기용 Hyperdrive와 분석 캐시용 Hyperdrive 분리

- 계정 단위 rate limit은 API 응답 기반 재시도 중심

- 앱 단위 공유 quota는 중앙 관리

- YouTube 공유 quota는 Durable Object 사용을 우선 검토

- YouTube global quota + bucket quota를 계층적으로 동시에 관리

- quota는 API 호출 전에 원자적으로 예약/차감

- 동영상 FFmpeg 변환은 현재 VPS 유지

- 향후 필요 시 Cloudflare Containers로 확장 검토

 

12. 구현 우선순위

 

1) 공급자별 Worker 프로젝트/모노레포 구조 정리

2) 기존 Trigger.dev SNS API 작업을 Workflow 단위로 이전

3) YouTube quota manager(Durable Object) 구현

4) Workflow의 sleep / retry / idempotency 정책 구현

5) Meta 등 계정 단위 공급자는 응답 기반 rate-limit 처리

6) Hyperdrive를 primary용 / analytics-cache용으로 분리

7) VPS FFmpeg 변환 API를 Workflow와 연결

8) 운영 지표를 본 뒤 Queue 또는 Containers 필요 여부 재검토