비개발자/기획자/개발자 누구나 이해하도록 일목요연하게 정리. 길이 분산 위해 분할. - 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>
108 lines
4.0 KiB
Markdown
108 lines
4.0 KiB
Markdown
# 데이터베이스 구조
|
|
|
|
[← README로](../README.md)
|
|
|
|
- **DB 이름**: `lps_db` (PostgreSQL, negosium_db와 별개)
|
|
- **테이블 정의**: `common/database/model/models.py` (SQLAlchemy) — 이 파일이 스키마의 단일 출처
|
|
- **공통 규칙**: 외래키(FK) 안 씀(무결성은 앱에서) · 코드값은 정수(SMALLINT) · 시각은 전부 `TIMESTAMPTZ`(UTC)
|
|
|
|
## 테이블 4종 한눈에
|
|
|
|
| 테이블 | 용도 |
|
|
|--------|------|
|
|
| `job` | 작업 큐 — 검색 요청을 순서대로 보관·처리 |
|
|
| `price_history` | 최저가 이력 — 그래프용 시계열 스냅샷 |
|
|
| `search_negative` | 네거티브 캐시 — "없음"으로 확인된 상품을 일정 시간 기억 |
|
|
| `bot_detection` | 봇 감지 이력 — 쿠팡이 차단한 패턴 기록 |
|
|
|
|
---
|
|
|
|
## 1. `job` — 작업 큐
|
|
|
|
| 컬럼 | 뜻 |
|
|
|------|-----|
|
|
| `job_id` | 작업 고유 ID (요청 시 반환되는 접수번호) |
|
|
| `job_type` | 작업 종류 (1=검색, 2=외부전송) |
|
|
| `status` | 상태 (아래 코드표) |
|
|
| `priority` | 우선순위(낮을수록 먼저) |
|
|
| `payload` | 요청 내용(상품 정보) JSON |
|
|
| `result` | 처리 결과 JSON (최저가·단계·소스별 건수 등) |
|
|
| `attempts` / `max_attempts` | 시도 횟수 / 최대 |
|
|
| `run_after` | 이 시각 이후 실행(재시도 대기용) |
|
|
| `lease_until` / `worker_id` | 점유 만료 시각 / 처리 중인 워커 (죽으면 자동 회수) |
|
|
| `last_error` | 마지막 오류 메시지 |
|
|
| `created_at` / `updated_at` | 생성/수정 시각 |
|
|
|
|
**status 코드값** (`JobStatus`)
|
|
| 값 | 이름 | 뜻 |
|
|
|----|------|-----|
|
|
| 1 | PENDING | 대기 중 |
|
|
| 2 | RUNNING | 처리 중 |
|
|
| 3 | DONE | 완료 (found/not_found 모두 포함) |
|
|
| 4 | DEAD | 재시도 소진 실패 (사람 확인 필요) |
|
|
|
|
**job_type 코드값** (`JobType`): 1=SEARCH(검색), 2=OUTBOX(외부전송)
|
|
|
|
---
|
|
|
|
## 2. `price_history` — 최저가 이력 (그래프)
|
|
|
|
검색할 때마다 1행씩 쌓입니다. 특정 상품의 시계열을 뽑아 그래프로 그립니다.
|
|
|
|
| 컬럼 | 뜻 |
|
|
|------|-----|
|
|
| `product_code` | 상품 식별 키(요청의 product_code) |
|
|
| `triggered_at` | 검색 실행 시각 (**그래프 X축**) |
|
|
| `outcome` | found / not_found |
|
|
| `matched_count` | AI가 "같은 상품"으로 판정한 개수 |
|
|
| `naver_lowest` / `naver_name` / `naver_url` | 네이버 최저가 + 상품명/링크 |
|
|
| `coupang_lowest` / `coupang_name` / `coupang_url` | 쿠팡 최저가 + 상품명/링크 |
|
|
| `final_lowest` | 전체 최저가 (**그래프 Y축 핵심**) |
|
|
| `final_source` | 최종 최저가가 나온 소스(naver/coupang) |
|
|
| `job_id` / `created_at` | 검색 잡 연결 / 생성 시각 |
|
|
|
|
> 한쪽 소스에 그 상품이 없던 시점은 해당 컬럼이 `null`(그래프 선이 빈다 — 정상).
|
|
|
|
---
|
|
|
|
## 3. `search_negative` — 네거티브 캐시
|
|
|
|
"검색해도 없더라"를 일정 시간(기본 24h) 기억해 **재검색 낭비를 막습니다**.
|
|
|
|
| 컬럼 | 뜻 |
|
|
|------|-----|
|
|
| `key` | 상품 식별 키(보통 product_code) |
|
|
| `until` | 이 시각까지 "없음"으로 간주 (지나면 다시 검색 허용) |
|
|
| `reason` | 사유 메모 |
|
|
| `created_at` | 생성 시각 |
|
|
|
|
---
|
|
|
|
## 4. `bot_detection` — 봇 감지 이력
|
|
|
|
쿠팡이 차단(봇 감지)했을 때 기록. "**어떤 IP로 몇 번째 요청에서 걸리나**"를 분석합니다.
|
|
|
|
| 컬럼 | 뜻 |
|
|
|------|-----|
|
|
| `source` | 소스(coupang) |
|
|
| `query` | 감지 당시 검색어 |
|
|
| `ip_request_no` | 현재 IP(브라우저)로 몇 번째 요청이었나 |
|
|
| `proxy_port` | 사용 중이던 프록시 포트(=IP 세션) |
|
|
| `elapsed_sec` | 브라우저 실행 후 경과(초) |
|
|
| `marker` | 감지 근거(차단 페이지 마커) |
|
|
| `headless` / `html_len` | 헤드리스 여부 / 응답 크기 |
|
|
| `created_at` | 감지 시각 |
|
|
|
|
**분석 예시**
|
|
```sql
|
|
-- IP당 평균 몇 요청 만에 감지되는지
|
|
SELECT avg(ip_request_no), count(*) FROM bot_detection;
|
|
```
|
|
|
|
---
|
|
|
|
## 스키마 생성/관리
|
|
|
|
- 개발·테스트: SQLAlchemy 모델에서 `create_all`로 자동 생성.
|
|
- DB 접속(로컬): `psql -h 127.0.0.1 -U postgres -d lps_db` (자세한 쿼리는 [운영 가이드](operations.md)).
|