기존엔 네이버가 쿠팡 기준 예산(3회)을 그대로 썼다. 실측하니 체급이 다르다: 같은 KR IP 로 **12회 연속 검색까지 무차단**(IP 4개 전부 한계 미도달). 3회로 돌리면 불필요하게 4배 자주 회전해 KR 풀만 빨리 소모하고 회전마다 브라우저 재기동(~20s)이 붙는다. → [DecodoConfig].naver_ip_request_budget = 10 (실측 12 에 여유). 쿠팡은 3 유지. 그리고 선제 회전에 빠져 있던 조각을 채웠다 — **휴식(rest)**: 예산 도달로 놓은 포트를 곧바로 다른 워커가 집으면 그 IP 의 요청률이 도로 올라가 예산의 의미가 사라진다. release(rest_sec=...) 로 sticky 수명만큼 쉬게 한다. 차단으로 태우는 burn(30분)과는 별개 상태다: 휴식 탄 게 아님 · 짧음 · 소진 시 가장 먼저 회수 쿨다운 차단당함 · 김 · 휴식보다 나중에 회수 회전 종류(kind)를 browser_base → DecodoProxy.rotate(kind) 로 전달해 budget 일 때만 휴식을 건다. 라이브 검증(예산 3으로 낮춰 관찰): 6회 검색 = IP 2개만 사용, 3회마다 선제 회전, 놓은 포트는 휴식 1 · 쿨다운 0 · 차단 0. 즉 IP 를 태우지 않고 로테이션만으로 돌아간다. 테스트 6건 추가(휴식 재사용 금지·만료 복귀·burn 우선·회수 우선순위·budget vs block), 전체 202 passed. _MockProxy.rotate 가 kind 를 받도록 갱신.
120 lines
6.9 KiB
Plaintext
120 lines
6.9 KiB
Plaintext
# 복사해서 사용: cp config.local.toml.example config.local.toml
|
||
# 실제 config.local.toml 은 시크릿 포함이라 커밋하지 않는다(.gitignore: *.toml).
|
||
#
|
||
# ── 설정 소스는 TOML 하나다(2026-07-13 협의 — env/.env 이중 관리 제거) ──
|
||
# 호스트 실행: APP_ENV=local(기본) → 이 파일
|
||
# Docker : APP_ENV=dev|prod → config.dev.toml / config.prod.toml 을 컨테이너에 마운트
|
||
# (각 example 참고, 실행은 ../run_docker.sh 대화형 권장)
|
||
# env 는 APP_ENV·PROCESS_COUNT/WORKER_CONCURRENCY(실행 스크립트 대화형 입력)·LPS_LIVE(테스트)만 남는다.
|
||
[WebServerConfig]
|
||
server_name = "LpsServer"
|
||
port = 9600
|
||
process_count = 1
|
||
is_ssl = false
|
||
is_test = true
|
||
# CORS 허용 오리진(프론트). 비우면 [] (CORS 미적용). 5173=vite dev.
|
||
cors_origins = ["http://localhost:5173", "http://127.0.0.1:5173"]
|
||
# API guard 키 — 비우면 개방 모드(개발). prod 는 채운다(복수 등록 = 무중단 키 교체).
|
||
# 생성 예: openssl rand -hex 32. 호출자(negodata)도 같은 키를 설정해야 한다.
|
||
api_keys = []
|
||
|
||
[LogConfig]
|
||
print_console = true
|
||
log_level = "debug"
|
||
|
||
# DB Read/Write 분리. 관리형 DB(RDS/Aurora/Azure)는 host 에 엔드포인트, sslmode="require".
|
||
# LPS 도메인 로직/테이블이 생기기 전까지는 접속하지 않으므로(엔진 lazy) placeholder 여도 부팅된다.
|
||
[MainDBConfig]
|
||
db_type = "postgresql"
|
||
name = "lps_db"
|
||
write_host = "127.0.0.1"
|
||
write_port = 5432
|
||
write_id = "<DB_USER>"
|
||
write_pw = "<DB_PASSWORD>"
|
||
read_host = "127.0.0.1"
|
||
read_port = 5432
|
||
read_id = "<DB_USER>"
|
||
read_pw = "<DB_PASSWORD>"
|
||
show_log = false
|
||
pool_size = 10 # connection_budget>0 이면 무시(자동 산정). budget=0 일 때만 이 값 사용.
|
||
max_overflow = 20 # 〃
|
||
# 커넥션 예산(자동 산정). process_count 에 맞춰 pool_size/max_overflow 를 자동 계산:
|
||
# (pool+overflow) × 2엔진 × process_count ≤ connection_budget.
|
||
# 'lps API 가 쓸 총 커넥션 상한' — 공유 PG(max_connections)·동거 서비스(worker 등)를 고려한 값.
|
||
# 예) 전용 PG(max_connections=100)면 90 근처, 공유 PG면 40 권장. 0 이면 자동 끔(위 pool 값 사용).
|
||
connection_budget = 40
|
||
sslmode = "" # 로컬: "" / 관리형 DB: "require"|"verify-ca"|"verify-full"
|
||
|
||
# 워커 런타임 (worker_main.py). 동시성만 실행 시 WORKER_CONCURRENCY env 로 임시 override 가능.
|
||
[WorkerConfig]
|
||
concurrency = 1 # 상품 동시 검색 수(워커별 브라우저 세트, Chrome 최대 4×N). 로컬 권장 2~3
|
||
fallbacks = [] # 오픈마켓 폴백(기본 OFF). 예: ["gmarket", "auction", "st11"] — 켜기 전 라이브 스모크
|
||
# Chrome 프로필 베이스. 영속 경로면 재시작에도 쿠키 유지(재웜업 회피).
|
||
# ⚠️ 컨테이너로 띄우면 반드시 "/profiles" — compose 가 lps-profiles 볼륨을 그 경로에 마운트한다.
|
||
# ".profiles" 로 두면 /app/.profiles(컨테이너 레이어)에 쌓여 재생성 때마다 쿠키가 전부 날아가고,
|
||
# 볼륨은 붙어만 있고 아무 일도 하지 않는다(2026-07-28 로컬·배포서버 양쪽에서 실측).
|
||
# 호스트 직접 실행(run_local_worker.sh)일 때만 ".profiles" 를 쓴다.
|
||
profile_dir = ".profiles"
|
||
job_deadline_sec = 300 # 잡 1건 처리 상한(크롤 행 방어). 0=무제한(테스트용)
|
||
shutdown_grace_sec = 60 # graceful 종료 유예 — docker stop_grace_period 를 이보다 길게
|
||
chrome_channel = "chrome" # 로컬: 실제 Chrome
|
||
chrome_executable = "" # 컨테이너: "/opt/google/chrome/chrome" (설정 시 channel 무시)
|
||
heartbeat_file = "/tmp/lps_worker_heartbeat" # Docker HEALTHCHECK 가 신선도 확인
|
||
|
||
# 임계 알림 (AlertManager — 룰 의미는 docs/operations.md 표)
|
||
[AlertConfig]
|
||
webhook = "" # Slack 호환 웹훅 URL. 비우면 로그로만 알림
|
||
cooldown_min = 30 # 같은 룰 재발송 억제(분). 해소 알림은 즉시
|
||
dead_1h = 20 # 최근 1h DEAD 잡 수
|
||
blocks_1h = 80 # 최근 1h 봇 감지 수
|
||
queue_lag_sec = 300 # 가장 오래된 PENDING 대기 초
|
||
pool_pct = 90 # DB 커넥션 풀 포화율(%)
|
||
source_fail_30m = 5 # 소스별 30분 내 시도 N회 이상 & 성공 0건
|
||
deadline_1h = 5 # 최근 1h 잡 데드라인 강제종료 수
|
||
cost_1h_usd = 1.0 # 최근 1h 검색원가 합($)
|
||
ports_low_pct = 30 # 가용 프록시 포트 비율(%)
|
||
block_sessions_6h = 1 # 최근 6h '예산 회전에도 차단된' IP 세션 수
|
||
|
||
# ── 시크릿(API 키 등)도 이 파일에서 통합 관리 (미커밋). ──
|
||
|
||
# 네이버 쇼핑 오픈API (https://developers.naver.com/apps). 여러 개면 429/403 로테이션 자동 포함.
|
||
# ⚠️ 2026-07-31 네이버가 쇼핑·책·전문자료 검색을 종료(유예·대체 없음) → shop.json 은 404 SE05.
|
||
# 이 섹션은 사문화 상태로 남겨둔다(어댑터 제거 결정 전까지).
|
||
[NaverConfig]
|
||
[[NaverConfig.keys]]
|
||
id = "<NAVER_CLIENT_ID>"
|
||
secret = "<NAVER_CLIENT_SECRET>"
|
||
# 추가 키는 아래처럼 블록을 더 넣으면 됨:
|
||
# [[NaverConfig.keys]]
|
||
# id = "..."
|
||
# secret = "..."
|
||
|
||
# NCP NAVER API HUB (https://www.ncloud.com → Application Services > NAVER API HUB).
|
||
# 검색(블로그·뉴스·지식iN·이미지·지역·웹문서·백과·카페·오타변환·성인판별)·검색어 트렌드·쇼핑 인사이트.
|
||
# 쇼핑 '검색'(상품/가격)은 허브에도 없다 — 최저가 소스로는 쓸 수 없음.
|
||
# 키는 NCP 콘솔 > NAVER API HUB > Application > API 관리 > [인증 정보] 에서 발급.
|
||
[NaverApiHubConfig]
|
||
base_url = "https://naverapihub.apigw.ntruss.com"
|
||
client_id = "<NCP_CLIENT_ID>"
|
||
client_secret = "<NCP_CLIENT_SECRET>"
|
||
timeout_sec = 10.0
|
||
|
||
# AI 유사도 판정/검색어 생성 (OpenAI)
|
||
[OpenAIConfig]
|
||
api_key = "<OPENAI_API_KEY>"
|
||
model = "gpt-4o-mini"
|
||
|
||
# DECODO residential 프록시 (쿠팡 전용, 포트기반 sticky). 값 다 채우면 활성(비면 프록시 미사용).
|
||
[DecodoConfig]
|
||
host = "" # 예: gate.decodo.com
|
||
kr_host = "kr.decodo.com" # 한국 타깃 게이트웨이(네이버용). 비우면 host 사용
|
||
username = "" # 대시보드 USERNAME (예: sppd6a3ze3)
|
||
password = "" # 대시보드 PASSWORD
|
||
port_start = 0 # 예: 10001
|
||
port_end = 0 # 예: 10010 — 포트를 늘리면(계약 변경) 이 범위만 넓히면 됨(코드 무변경)
|
||
session_minutes = 10 # 대시보드 Sticky 지속시간(분)과 일치
|
||
cost_per_gb = 0.0 # DECODO 요금($/GB) — 검색 원가의 대역폭 비용 산정용(플랜에 맞게, 예 3.0)
|
||
ip_request_budget = 3 # IP당 요청 예산 — 도달 시 차단 전 선제 회전(0=비활성). 튜닝은 docs/database.md
|
||
naver_ip_request_budget = 10 # 네이버는 IP 당 12회까지 무차단 실측 → 여유 둔 10
|
||
port_cooldown_sec = 0 # 차단 감지 포트 격리 초. 0=자동 max(sticky, 30분)
|