Commit Graph

16 Commits

Author SHA1 Message Date
민헌
7b1bcb6d24 docs(lps): README 에 크롤·매칭·IP 로테이션 실제 방식 추가 + 낡은 서술 정정
"어떻게 동작하나"가 워크플로우 그림 한 장뿐이라, 정작 이 시스템에서 어려운 세 가지
(안티봇 뚫는 법·같은 상품 고르는 법·IP 돌리는 법)를 README 만 봐서는 알 수 없었다.
기존 스타일(표·ASCII·비개발자 설명)을 유지해 '🔬 안을 열어보면' 절을 추가한다.

- 소스별 크롤: 네이버(모바일 msearch·WTM)와 쿠팡(Akamai)의 **정반대 전략**을 표로 대조.
  네이버는 리소스 차단을 끄고(요청 가로채기 자체가 탐지 신호) 한국 IP+ko-KR 로케일이 필수,
  쿠팡은 리소스를 막아 대역폭을 줄인다. 스크롤은 횟수가 아니라 '안 늘어남'이 종료 조건인 이유도.
- 같은 상품 판정: 필터 3단계 + AI 판정, '무시할 차이 / 불일치로 볼 차이' 기준표,
  후보를 10건씩 쪼개야 하는 이유(37건 일괄 → 전멸).
- IP 로테이션: 포트=sticky 세션, 게이트웨이 2개, DB 장부(SKIP LOCKED·LRU),
  포트 3상태(임대/휴식/쿨다운), 회전 계기 4종, **태우지 않는 경우**(구조적 차단·서킷브레이커·
  확신 없는 0건)와 예산을 IP 기준으로 세는 이유.

낡은 서술 정정:
- "네이버 쇼핑 API" → 오픈API 는 2026-07-31 종료, 지금은 둘 다 크롤
- 테이블 5종 → 6종(proxy_port), 알림 10룰 → 11룰(fatal_block)
- 요청 예산 "기본 3회" → 쿠팡 3·네이버 10
- config 설명 "배포는 env 주입" → 실제로는 config.local.toml 마운트, env 는 DB 접속점만
- 폴더 구조에 crud/port_lease·profile_slot 추가

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 17:23:23 +09:00
민헌
b7fc327779 refactor(lps): 사문화된 네이버 오픈API 어댑터 제거 + 낡은 테스트 계약 갱신
**낡은 테스트**: test_handler_skips_record_on_negative_cache_hit 는 '캐시 히트면 이력을
남기지 않는다'를 검증했는데, 그 동작은 실측 버그였다 — 잡은 DONE 인데 price_history 에
새 행이 없어 이를 폴링하는 소비자(negodata 최저가 모달)가 결과를 영영 못 받고 로딩만 돌았다.
코드는 이미 '캐시 히트도 이 잡의 결과이므로 기록한다'로 고쳐져 있었고 테스트만 남아 있었다.
→ 현재 계약(not_found 스냅샷 1건 기록, 가격은 null)을 검증하도록 다시 씀. 전체 220 passed·0 failed.

**오픈API 어댑터 제거**: shop.json 이 2026-07-31 종료돼 404 SE05 만 반환하고, 파이프라인은
naver_shop(크롤)로 옮겨 갔다. 되살릴 수 없는 코드를 남겨두면 다음 사람이 "키를 넣으면 되나"
하고 시간을 쓴다.
- services/search/naver/ (adapter·transform) 삭제
- NaverConfig 모델·로더·설정 섹션 3개 파일에서 제거(죽은 키)
- test_naver_transform 삭제, test_alerts 는 NaverAdapter 대신 스텁 사용
  (검증 대상인 recent_stats/_note_result 는 베이스 SearchAdapter 계약이라 무관)

**문서 정합화**: architecture(네이버 안티봇=WTM, 통과 3조건) · api(배송비가 이제 채워짐,
가격은 즉시판매가·쿠폰가 제외) · operations(kr_host·naver_ip_request_budget) · README 트리.

source 이름 "naver" 는 그대로다 — price_history·by_mall·프론트 계약은 구현 교체와 무관하다.
2026-08-05 13:26:10 +09:00
민헌
dc8c288f68 feat(lps): docker-compose 에 lps-admin 추가 + 환경설정 config.local.toml 단일화
- lps-admin 컨테이너화: Vite 정적 빌드 → nginx, /v1·/healthz·/readyz 를
  lps-api:9600 으로 동일출처 프록시(빌드 타임 API URL 주입 불필요)
- lps-api/worker: APP_ENV=local 고정 + DB_HOST override(도메인 backend·negodata·agent
  와 동일 패턴), config.<env>.toml 마운트 방식 폐기
- server_configs 에 DB 접속 env override(_apply_db_env_override) 복원
- config.dev/prod.toml.example 제거 — 환경 구분 없이 config.local.toml 하나
  (prod 서버도 그 서버의 config.local.toml + docker compose up -d)
- run_docker.sh 환경 선택 제거·lps-admin 포함, README·operations 문서 갱신

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 11:54:32 +09:00
민헌
8fb54be830 feat(lps): local/dev/prod 환경별 실행 체계 — 환경 toml 2종 + 대화형 run_docker.sh
다른 개발자가 어느 환경이든 같은 방식으로 띄울 수 있게 한다.
환경 = APP_ENV 가 고르는 config.<env>.toml 하나(구조 동일, 값만 다름).

- config.docker.toml → config.dev.toml 개명, config.prod.toml.example 신설
  (prod 성격 반영: api_keys 필수 표기·debug 로그 금지·웹훅 권장).
- compose 의 lps 서비스 APP_ENV/마운트를 ${APP_ENV:-dev} 로 파라미터화,
  API 포트를 ${LPS_API_BIND:-0.0.0.0} 바인드로 노출 제어.
- run_docker.sh(대화형) 신설: 환경 선택 → 설정 파일 검증(없으면 example
  복사 제안) → 기동/재시작/중지/로그. prod 는 guard 키 비면 경고 후
  확인받고, LPS_API_BIND=127.0.0.1 자동 설정(외부는 리버스프록시 경유).
- 운영 가이드에 환경 개요 표, README 빠른 시작에 Docker 실행 추가.
- 검증: 스크립트 문법·compose(dev/prod) 파싱·APP_ENV=dev 로딩·145 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 21:22:53 +09:00
민헌
c81df5bd88 refactor(lps): 설정을 TOML 단일 소스로 통합 — env/.env 이중 관리 제거
설정이 .env(compose 주입)·config.toml·코드 곳곳의 os.environ 직독 3계층에
흩어져 관리가 어려웠다. TOML 하나로 통합한다(협의 결정).

- 신설 [WorkerConfig](동시성·폴백·프로필·데드라인·유예·Chrome·하트비트),
  [AlertConfig](웹훅·쿨다운·임계 10종). [WebServerConfig].api_keys(guard),
  [DecodoConfig].ip_request_budget/port_cooldown_sec 추가 — 흩어져 있던
  LPS_* env 20여 개를 섹션으로 흡수.
- server_configs 의 env override 계층(DB_*·시크릿·NAVER_KEYS 등) 삭제.
  남는 env 는 APP_ENV(부트스트랩)·PROCESS_COUNT/WORKER_CONCURRENCY(실행
  스크립트 대화형 입력 전용)·LPS_LIVE(테스트 옵트인)뿐.
- Docker: env 주입 → config.docker.toml 마운트 + APP_ENV=docker.
  이미지 무시크릿 유지, 마운트 누락 시 FileNotFoundError 즉시 실패.
  .env.example 삭제, config.docker.toml.example 신설.
- negodata 호출부: guard 키를 env 직독에서 [WebServerConfig].lps_api_key
  (+기존 관례대로 env override)로 이동.
- 실행 스크립트: 프로필·폴백·예산 프롬프트 제거(toml 소스 안내),
  동시성/프로세스 수만 임시 override 로 유지.
- docs 7종·example toml 의 env 표기를 toml 키로 일괄 갱신.
- 전체 145 passed + APP_ENV=docker 로딩·API 기동 스모크 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 21:11:31 +09:00
민헌
993301be8b docs(lps): README·docs 최신화 — IP 선제 회전·알림 10룰·guard·ip_session 반영
이번 기능 3종+알림 확장 이후 문서와 코드의 어긋남을 정리한다.

- README: 주요 기능 표에 선제 회전(예산 3회)·임계 알림·API guard 추가,
  차단 대응 설명을 '막히기 전 교체' 순서로 재서술, 폴더 구조에
  alerts/ip_session 반영, 데이터베이스 문서 링크를 5종으로 수정.
- api.md: /v1/lps/ops 운영 스냅샷 섹션 신설(필드 주석 포함),
  /readyz 문서화, HTTP 401(guard) 상태 코드 추가.
- architecture.md: 구성요소 표에 관측·알림/API guard 행 추가.
- database.md: 제목 '테이블 5종'으로 수정.
- operations.md: 테스트 수 96→145, 로그 읽는 법에 예산 선제 회전·
  포트 쿨다운 로그 2행 추가.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 20:46:59 +09:00
민헌
5e485e9b6e feat(lps): 오픈마켓 폴백 기본 비활성(LPS_FALLBACKS 토글) — 협의 결정 B안 적용
2026-07-10 협의: 검색은 네이버+쿠팡만. G마켓·옥션·11번가 폴백은 최종 최저가
기여 0회에 검색당 최대 15s·프록시 비용 ~87%를 차지해 로직에서 제외.
주석처리 대신 env 토글로 코드·테스트는 살려둔다(부패 방지·env 한 줄 재가동).

- worker_main: LPS_FALLBACKS(기본 빈값=OFF)로만 폴백 어댑터 생성, 잘못된 값 경고,
  기동 로그에 폴백 상태 표기. 핸들러는 빈 폴백을 원래 정상 처리라 로직 변경 없음
- run_local_worker.sh: 폴백 여부 대화형 질문 추가(기본 비활성)
- compose: LPS_FALLBACKS 주석 env(재가동용)
- decision-openmarket-crawler.md: 결정(B)·근거·재가동 절차(라이브 스모크 선행) 확정 기록
- README·architecture·operations: 기본 비활성 반영

검증: 전체 테스트 106 passed(폴백 로직 테스트는 fake 주입이라 계속 유효),
워커 실기동 로그 '오픈마켓 폴백: OFF' 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 10:14:39 +09:00
민헌
0e30354ba5 feat(lps): 로컬 워커·부하테스트(GUI) 실행 스크립트 추가 (대화형)
기존 run_local_server.sh 스타일에 맞춘 대화형 .sh 두 개.

- run_local_worker.sh: 동시성(WORKER_CONCURRENCY)·Chrome 프로필(LPS_PROFILE_DIR)
  선택 후 워커 기동. 실행 중 워커 감지 시 교체 여부 확인. venv/config 자동 보장.
- run_loadtest_gui.sh: Locust 웹 UI(:8089) 기동. 워커 실행 중이면 경고(크롤 비용),
  API 미기동 시 PROCESS_COUNT 받아 백그라운드 기동(종료 시 함께 정리). 부하생성기 --processes.
- .gitignore(lps): .profiles/(Chrome 쿠키·cf_clearance 세션) 커밋 방지
- README·operations: 새 스크립트 안내로 갱신

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 08:33:11 +09:00
민헌
eba78dbbb9 docs(lps): README 최신화 — Dockerfile.worker·문서목록(배포/관측/크롤러논의)
폴더구조에 Dockerfile.worker, 문서 표에 Docker배포·관측·decision-openmarket-crawler 반영.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 23:32:52 +09:00
민헌
10eb67e8c6 docs(lps): 문서 최신화 — 오픈마켓 폴백·Turnstile·비용계측·동시성·프록시복원력
이번 라운드 변경을 README/아키텍처/운영 가이드에 반영.
- README: 기능표(오픈마켓 폴백·원가계측·다중병렬), 워크플로우 4-1 폴백 단계, 폴더구조
- architecture: 안티봇 대응(쿠팡 Akamai / ESM Cloudflare Turnstile 웜업·동적차단),
  검색 원가 계측(CDP 실측·컴포넌트별 비용), 다중 상품 병렬, 유휴 브라우저 정리
- operations: 로그 항목(프리플라이트·웜업·IP회전·데드라인·유휴정리), 문제해결(407·Turnstile·
  Chrome 정리·느림/비쌈), 배포유의, 원가/몰별 DB 쿼리

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 21:18:12 +09:00
민헌
61b9e6ae82 docs(lps): 오픈마켓 폴백 크롤 반영 — 아키텍처·README
파이프라인 ⑤-1 폴백 단계(네이버 미커버 몰만 크롤), 구성요소 표에 오픈마켓 폴백,
폴더 구조에 esm/st11 추가.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 14:54:00 +09:00
민헌
8bf34ea346 docs(lps): 프로젝트 문서화 — README + docs/(아키텍처·DB·API·운영)
비개발자/기획자/개발자 누구나 이해하도록 일목요연하게 정리. 길이 분산 위해 분할.

- README.md: 3줄 요약 + 워크플로우 다이어그램 + 주요기능 + 빠른시작 + 문서 목차
- docs/architecture.md: 구성요소·처리 파이프라인·재시도/프록시/봇감지/이력 원리
- docs/database.md: 테이블 4종(job/price_history/search_negative/bot_detection) + 코드값
- docs/api.md: 엔드포인트 요청/응답 예시(curl/Postman), 상태·코드 요약
- docs/operations.md: 실행·로그·DB조회·테스트·문제해결·배포유의

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 11:27:39 +09:00
민헌
7911ba1745 refactor(lps): 환경설정 TOML 단일화 — .env 제거, 시크릿도 config.local.toml 로 통합
설정이 .env(시크릿)+toml(설정)로 갈려 있던 것을 config.local.toml 하나로 통합.
DB 비번이 이미 toml 에 있어 분리 기준이 임의적이었고, backend(하우스 패턴)도
toml 단일이라 일관성 확보. 환경별로 바뀌는 값(DB_HOST 등)만 env override 유지.

- config_models: NaverConfig(keys 로테이션)·OpenAIConfig·DecodoConfig 추가
- server_configs: 3개 로드, load_dotenv 제거(python-dotenv 의존성도 제거)
- proxy/naver/similarity/keyword/worker_main: os.environ → config 객체 참조
- config.local.toml.example: [NaverConfig]/[OpenAIConfig]/[DecodoConfig] 섹션
- .env/.env.example 삭제, README/주석 갱신 (배포는 toml 마운트 or env override)
- tests: DecodoConfig 기반으로 갱신 → 전체 44/44

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 10:06:00 +09:00
민헌
c4b8692f3a chore(lps): 시크릿용 .env 도입 — API 키(NAVER/OpenAI/Azure) 주입 경로
키는 코드/커밋이 아닌 환경변수로 주입한다(감사: 시크릿 하드코딩 금지).
config.local.toml=비-시크릿 설정, .env=시크릿으로 분리.

- .env.example(템플릿, 커밋) + .env(로컬, 미추적)로 키 슬롯 제공
  · NAVER_CLIENT_ID/SECRET(+로테이션 _2..), OPENAI_API_KEY, AZURE_OPENAI_*
- server_configs 가 기동 시 lps/.env 를 load_dotenv 로 환경변수 주입
- requirements: python-dotenv 추가
- README: .env 셋업 단계 안내

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 17:14:34 +09:00
민헌
da9bd7750c feat(lps): 작업 큐 엔진 — PostgreSQL 원자적 claim + lease + dead-letter
레퍼런스의 DB-큐 반면교사를 전부 뒤집는 큐 엔진을 구축한다.
 - 원자적 claim: FOR UPDATE SKIP LOCKED 서브쿼리 + 같은 UPDATE + RETURNING
   → 워커 다수여도 이중 할당 원천 불가(fetch/claim 미분리)
 - 모든 전이는 조건부 CAS(status/worker_id 가드) + RETURNING
 - 복구는 timeout 추측이 아닌 lease 만료 소유권 기반(reaper 회수)
 - 재시도 지수백오프 + max_attempts + dead-letter 를 큐에 내장(스크립트 난립 제거)
 - dedupe_key 부분 유니크로 활성 중복 차단, counts()로 관측(수동 psql 대체)

- enums: JobStatus(PENDING/RUNNING/DONE/DEAD)·JobType(SEARCH/OUTBOX)
- models: job 테이블(무FK·SMALLINT코드·TIMESTAMPTZ, claim/lease/dedupe 인덱스)
- crud/job_crud: JobQueue(enqueue/claim/complete/fail/renew_lease/reap/counts)
- tests: 동시 8워커 이중할당0·lease회수·재시도→dead·소유권가드 등 8건(실 lps_db)

부수: 포트 9400→9600 (negodata 9400 충돌 회피)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 16:37:32 +09:00
민헌
11a7be7154 feat(lps): 인터넷 최저가 검색 솔루션 프레임워크 골격 추가
backend 와 동일한 프레임워크로 lps/ 폴더를 신설한다. 구체적인 도메인
로직(크롤링/오픈API/최저가 산정)은 미정이라 골격만 구성한다.

- FastAPI 부트스트랩(web_main/router) + lifespan·CORS·gzip·로그 미들웨어 + /healthz
- TOML 설정 로더(APP_ENV) + pydantic config + DB env override
- DB 세션 매니저(논리 DB × R/W, service→람다 위임) — 엔진 lazy 라 DB 없이도 부팅
- 공통 응답 규약(gmodel) + ErrorType/DBType/DBWRType
- router→services→crud 계층 컨벤션(services/crud 는 빈 폴더로 자리만)
- 포트 9400(backend 9300·agent 9500 회피), Dockerfile/run_local_server.sh/README
- tests/test_health.py 스모크(healthz, DB 없이 통과)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 15:18:27 +09:00