o2o-negosium-original/lps
민헌 3c5286aeec feat(lps): IP 선제 로테이션 — 요청 예산·포트 쿨다운·ip_session 관측
쿠팡 크롤 IP 를 '막힐 때까지' 쓰던 방식을 '막히기 전에 교체'로 전환한다.

- 요청 예산(LPS_IP_REQUEST_BUDGET, 기본 3): IP당 요청 수가 예산에 닿으면
  차단 전에 선제 회전. 실측상 5회 부근 차단 이력이 있어 보수적으로 3회.
  선제 교체된 포트는 평판이 깨끗해 로테이션 복귀 시 재사용된다.
- 포트 쿨다운(LPS_PORT_COOLDOWN_SEC, 기본 max(sticky,30분)): 차단 감지·
  전송오류 포트는 격리하고 _port() 가 건너뛴다. 전 포트 쿨다운이면 만료
  임박 포트 사용(가용성 우선). 포트 수는 config 범위에서 동적 산출.
- 차단 재시도 소진 시에도 회전 예약 — 불탄 포트로 다음 검색을 하지 않음.
- ip_session 테이블 신설: 세션마다 요청 수·성공/차단·종료 사유(budget/
  block/proxy_error/window/idle/shutdown)를 기록. bot_detection 과 달리
  무사 종료도 남아 예산 상한 튜닝의 원천 데이터가 된다(쿼리 database.md).
  models.py·migrations·init.sql(lps_db 섹션) 동행 갱신, dev DB 적용 완료.
- 테스트 17건 추가(쿨다운·예산 판정·세션 기록·CRUD), 전체 126 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:35:44 +09:00
..
common feat(lps): IP 선제 로테이션 — 요청 예산·포트 쿨다운·ip_session 관측 2026-07-13 16:35:44 +09:00
config fix(lps): Docker 이미지 무시크릿화 + 배포 견고화 — 리뷰 발견사항 일괄 적용 2026-07-10 11:53:47 +09:00
crud feat(lps): IP 선제 로테이션 — 요청 예산·포트 쿨다운·ip_session 관측 2026-07-13 16:35:44 +09:00
docs feat(lps): IP 선제 로테이션 — 요청 예산·포트 쿨다운·ip_session 관측 2026-07-13 16:35:44 +09:00
loadtest test(lps): 부하테스트 상품셋 100종 카탈로그 분리 — 실제 입력 분포 재현 2026-07-10 10:50:28 +09:00
migrations feat(lps): IP 선제 로테이션 — 요청 예산·포트 쿨다운·ip_session 관측 2026-07-13 16:35:44 +09:00
router feat(lps): P4 관측·알림·워커 헬스 — readyz/ops + 하트비트/HEALTHCHECK + 임계 알림 2026-07-09 23:21:43 +09:00
services feat(lps): IP 선제 로테이션 — 요청 예산·포트 쿨다운·ip_session 관측 2026-07-13 16:35:44 +09:00
tests feat(lps): IP 선제 로테이션 — 요청 예산·포트 쿨다운·ip_session 관측 2026-07-13 16:35:44 +09:00
worker fix(lps): 워커 행(hang) 3중 방어 — 잡 데드라인·웜업 타임아웃·정리 루프 격리 2026-07-10 11:27:03 +09:00
.dockerignore fix(lps): Docker 이미지 무시크릿화 + 배포 견고화 — 리뷰 발견사항 일괄 적용 2026-07-10 11:53:47 +09:00
.gitignore feat(lps): 로컬 워커·부하테스트(GUI) 실행 스크립트 추가 (대화형) 2026-07-10 08:33:11 +09:00
conftest.py feat(lps): 인터넷 최저가 검색 솔루션 프레임워크 골격 추가 2026-07-08 15:18:27 +09:00
Dockerfile fix(lps): Docker 이미지 무시크릿화 + 배포 견고화 — 리뷰 발견사항 일괄 적용 2026-07-10 11:53:47 +09:00
Dockerfile.worker fix(lps): Docker 이미지 무시크릿화 + 배포 견고화 — 리뷰 발견사항 일괄 적용 2026-07-10 11:53:47 +09:00
loadtest.py test(lps): 부하테스트 상품셋 100종 카탈로그 분리 — 실제 입력 분포 재현 2026-07-10 10:50:28 +09:00
pytest.ini feat(lps): 인터넷 최저가 검색 솔루션 프레임워크 골격 추가 2026-07-08 15:18:27 +09:00
README.md feat(lps): 오픈마켓 폴백 기본 비활성(LPS_FALLBACKS 토글) — 협의 결정 B안 적용 2026-07-10 10:14:39 +09:00
requirements-api.txt fix(lps): Docker 이미지 무시크릿화 + 배포 견고화 — 리뷰 발견사항 일괄 적용 2026-07-10 11:53:47 +09:00
requirements.txt feat(lps): 실시간 관측 대시보드 추가 — 큐 추이·코어별 CPU·프로세스 그룹 사용률 2026-07-10 09:50:14 +09:00
run_loadtest_gui.sh feat(lps): 로컬 워커·부하테스트(GUI) 실행 스크립트 추가 (대화형) 2026-07-10 08:33:11 +09:00
run_local_server.sh feat(lps): 서버 실행 스크립트에 멀티코어 대화형 선택 추가 2026-07-10 09:09:55 +09:00
run_local_worker.sh feat(lps): 오픈마켓 폴백 기본 비활성(LPS_FALLBACKS 토글) — 협의 결정 B안 적용 2026-07-10 10:14:39 +09:00
run_monitor.sh feat(lps): 실시간 관측 대시보드 추가 — 큐 추이·코어별 CPU·프로세스 그룹 사용률 2026-07-10 09:50:14 +09:00
web_main.py feat(lps): process_count 기반 커넥션 풀 자동 산정 — 멀티코어 풀 오버서브스크립션 방지 2026-07-09 23:59:05 +09:00
worker_main.py feat(lps): IP 선제 로테이션 — 요청 예산·포트 쿨다운·ip_session 관측 2026-07-13 16:35:44 +09:00

LPS — 인터넷 최저가 검색 솔루션

상품 정보를 넣으면 네이버·쿠팡을 뒤져 "같은 상품"의 최저가를 찾아 돌려주고, 그 가격을 시간에 따라 기록해 그래프로 볼 수 있는 시스템입니다.


🧭 이게 뭔가요? (비개발자용 3줄 요약)

  1. "맥심 커피 (1박스, 160개입)" 같은 상품 정보를 보내면,
  2. 시스템이 네이버·쿠팡을 실제로 검색하고, AI가 "진짜 같은 상품"만 골라 최저가를 알려줍니다. (빨대·커버 같은 엉뚱한 액세서리는 걸러냅니다)
  3. 같은 상품을 여러 번 조회하면 가격 변화가 쌓여서, 네이버/쿠팡/최종 최저가를 그래프로 볼 수 있습니다.

왜 유용한가? 사람이 일일이 검색·비교하지 않아도, 필요한 상품만(조회할 때만) 자동으로 최저가를 찾고 가격 추이를 남깁니다.


⚙️ 어떻게 동작하나요? (워크플로우)

 [1] 검색 요청           [2] 대기줄(큐)         [3] 일꾼(워커)가 처리
  상품 정보 전송   ─────▶  순서대로 쌓임   ─────▶  네이버 + 쿠팡 동시 검색
 (POST /search)          (즉시 접수번호 반환)         │
                                                     ▼
                                        [4] 걸러내기 + AI 판정
                                        가격 이상치 제거 → "같은 상품"만 선별
                                                     │
                                                     ▼
                                        [4-1] 오픈마켓 폴백 (옵션 · 기본 꺼짐)
                                        네이버가 못 덮은 몰(G마켓·옥션·11번가)만 크롤
                                                     │
                                                     ▼
                                        [5] 최저가 확정 + 기록
                                        네이버/쿠팡/최종 + 몰별 최저가 저장 → 이력 적재
                                                     │
                                                     ▼
                                        [6] 완료(결과 + 검색 원가 조회 가능)

핵심 포인트

  • 즉시 응답 + 나중 처리: 요청하면 바로 "접수번호(job_id)"를 주고, 실제 검색은 뒤에서 진행됩니다. (검색은 몇 초~수십 초 걸림)
  • 못 찾으면 검색어를 바꿔 재시도: "맥심 커피"로 안 나오면 "맥심 모카골드 커피믹스"처럼 AI가 검색어를 다듬어 다시 시도하고, 그래도 없으면 "없음"으로 정리합니다. (무한 재시도 안 함)
  • 차단 대응: 쿠팡(Akamai)·G마켓(Cloudflare 사람확인) 등이 봇으로 감지하면 다른 IP로 바꿔 재시도하고, 시작 시 챌린지를 미리 풀어(웜업) 실 작업을 빠르게 합니다.
  • 원가 투명: 검색 1건이 쓴 AI 비용·프록시 대역폭·시간을 함께 기록합니다.

✨ 주요 기능

기능 설명
멀티 소스 검색 네이버 쇼핑 API + 쿠팡(Akamai 우회) 동시 검색·병합
오픈마켓 폴백 크롤 네이버가 못 덮은 몰만 G마켓·옥션(Cloudflare Turnstile 우회)·11번가 크롤 → 몰별 가격. 기본 비활성(LPS_FALLBACKS, 배경)
AI 같은 상품 판정 "진짜 그 상품"만 선별 (액세서리·다른 규격 제외)
검색어 자동 정제 0건이면 정밀/광역 검색어로 재시도
최저가 이력 그래프 조회 시점마다 네이버/쿠팡/최종 + 몰별(by_mall) 최저가를 시계열로 기록
검색 원가 계측 검색 1건의 AI 토큰·비용 + DECODO 대역폭(실측 CDP) + 시간을 집계
다중 상품 병렬 워커별 브라우저 세트로 여러 상품 동시 검색(WORKER_CONCURRENCY)
안정적 큐 처리 작업 유실 없이 순서대로, 실패 시 자동 재시도
프록시 IP 회전 봇 감지·전송오류 시 IP 자동 순환 + 시작 웜업(DECODO)

🚀 빠른 시작

cd lps

# 1) 설정 파일 준비 (DB·API 키 등)
cp config/config.local.toml.example config/config.local.toml   # 값 채우기

# 2) API 서버 실행 (요청 접수)
./run_local_server.sh              # → http://localhost:9600/docs

# 3) 워커 실행 (실제 검색 수행) — 별도 터미널
./run_local_worker.sh              # 대화형: 동시성·프로필 선택 (또는 python worker_main.py)

# (선택) 부하 테스트 GUI — Locust 웹 UI(:8089)
./run_loadtest_gui.sh              # 브라우저에서 users/spawn 조절하며 RPS/지연 관측

간단 테스트:

curl -X POST localhost:9600/v1/lps/search -H 'Content-Type: application/json' \
  -d '{"data":[{"product_code":"T1","product_name":"맥심 커피","specification":"1박스, 160개입"}]}'

자세한 실행/설정은 운영 가이드 참고.


📚 문서

문서 대상 내용
아키텍처 개발자/기획자 구성요소·파이프라인·안티봇(Akamai/Turnstile)·비용계측·동시성
데이터베이스 개발자/기획자 테이블 4종 구조와 코드값(+by_mall)
API 사용법 연동 개발자 엔드포인트·요청/응답·metrics 예시
운영 가이드 운영자/개발자 실행·병렬·관측(readyz/ops/알림)·Docker 배포·문제 해결
크롤러 논의 팀 오픈마켓 크롤러 유지 여부(ROI) 의사결정 메모

📁 폴더 구조

lps/
├── web_main.py            # API 서버 진입점 (요청 접수)
├── worker_main.py         # 워커 진입점 (검색+웜업+유휴정리+ops모니터)
├── Dockerfile             # API 이미지(lean) · Dockerfile.worker  # 워커(Chromium+Xvfb)
├── run_local_server.sh    # 로컬 API 실행 (대화형)
├── run_local_worker.sh    # 로컬 워커 실행 (대화형: 동시성·프로필)
├── run_loadtest_gui.sh    # 부하 테스트 Locust 웹 UI(:8089) 실행 (대화형)
├── config/                # 설정(config.local.toml — 포트/DB/API키, 미커밋; 배포는 env 주입)
├── common/                # 공통(enums, DB 세션, 모델, 로거)
│   └── database/model/models.py   # DB 테이블 정의
├── loadtest.py            # 부하 테스트 (N개 상품 → 처리량·지연·비용 집계)
├── crud/                  # DB 접근 (job_crud, price_history, negative_cache, bot_detection)
├── services/
│   ├── search/            # 소스 어댑터 (coupang, naver, esm=G마켓·옥션, st11=11번가)
│   │   ├── browser_base.py    # patchright 공통(수명·프록시회전·차단감지·CDP 바이트계측)
│   │   ├── proxy.py           # DECODO(IP 회전·프리플라이트)
│   │   └── card_parser.py     # 오픈마켓 공용 카드 파서
│   ├── pipeline/          # 필터·이상치·최저가 정렬(+몰별 분해)
│   ├── ai/                # AI 유사도 판정·검색어 생성 (OpenAI)
│   └── metrics.py         # 검색 원가 계측(AI/DECODO 비용·시간)
├── worker/                # 워커 루프·핸들러(폴백·데드라인)·알림(NOTIFY)
├── router/v1/lps/         # API 라우터
└── tests/                 # 테스트

포트

backend 9300 / negodata 9400 / agent 9500 과 겹치지 않게 LPS는 9600.