# API 사용법 [← README로](../README.md) - **베이스 URL**(로컬): `http://localhost:9600` - **Swagger 문서**: `http://localhost:9600/docs` (브라우저에서 바로 테스트 가능) - 모든 응답에는 공통 **결과 봉투** `result`가 붙습니다: ```json "result": { "success": true, "code": 0, "desc": "SUCCESS" } ``` (실패 시 `success:false`, `code`/`desc`에 오류 코드) --- ## 1. 검색 요청 — `POST /v1/lps/search` 상품 리스트를 보내면 상품마다 검색 작업을 큐에 넣고 **접수번호(job_id)**를 즉시 반환합니다. (실제 검색은 뒤에서 진행) **요청** ```json { "data": [ { "product_code": "T1", // (필수) 상품 식별 코드 — 이력·중복방지 키 "product_name": "맥심 커피", // (필수) 상품명 "specification": "1박스, 160개입", // (선택) 규격 — 자유 서술, 형식 무관 "model": "모카골드", // (선택) 모델명 "company": "동서식품", // (선택) 제조사/브랜드 "price": "25000", // (선택) 현재가 — 있으면 가격 범위 필터 기준 "job_type": "manual" // (선택) 요청 유형 → 우선순위 } ] } ``` > **specification은 나눌 필요 없이** "1박스, 160개입"처럼 통째로 넣으면 AI가 해석합니다. **`job_type` → 우선순위** (낮을수록 먼저): | 값 | 우선순위 | 의미 | |----|:---:|------| | `new_product` | 1 (최우선) | 신규 상품 등록 시 | | `manual` | 2 (기본) | 담당자 수동 요청 | | `partner` | 3 | 외부 시스템/파트너 연동 | | `batch` | 4 (최하위) | 정기 배치 | (알 수 없는 값은 batch와 동급으로 처리) **응답** ```json { "result": { "success": true, "code": 0, "desc": "SUCCESS" }, "accepted": 1, "items": [ { "product_code": "T1", "job_id": "c885...", "duplicated": false } ] } ``` - `duplicated: true` → 같은 상품이 이미 처리 대기/진행 중이라 중복 접수 생략(그 경우 `job_id` 없음). **curl** ```bash curl -X POST localhost:9600/v1/lps/search -H 'Content-Type: application/json' \ -d '{"data":[{"product_code":"T1","product_name":"맥심 커피","specification":"1박스, 160개입"}]}' ``` --- ## 2. 작업 상태·결과 — `GET /v1/lps/jobs/{job_id}` 접수번호로 진행 상태와 결과를 조회합니다. (`PENDING` → `RUNNING` → `DONE`) **응답 (완료 시)** ```json { "result": { "success": true, "code": 0, "desc": "SUCCESS" }, "job_id": "c885...", "status": "DONE", // PENDING / RUNNING / DONE / DEAD "attempts": 1, "output": { "outcome": "found", // found / not_found "query": "맥심 커피", "lowest": { "price": 25200, "source": "coupang", "name": "맥심모카골드 ...", "detail_url": "...", "shipping_fee": 0, "shipping_type": "rocket" }, "top": [ /* 최저가 상위 N개 */ ], "sources": { "naver": {"count": 40}, "coupang": {"count": 40} }, "stages": [ {"stage":"outlier","in":80,"out":76}, {"stage":"ai_match","in":76,"out":1}, {"stage":"top_n","in":1,"out":1} ], "metrics": { // 이 검색 1건이 쓴 리소스/비용/시간 "duration_ms": 21500, "ai": { "calls": 2, "prompt_tokens": 5200, "completion_tokens": 180, "est_cost_usd": 0.000888 }, "crawl": { "fetches": 3, "html_bytes": 1560000, "malls_crawled": ["gmarket"] }, "source_ms": { "naver": 480, "coupang": 12300, "gmarket": 8700 } } } } ``` - `output.stages` = 각 단계에서 몇 건이 걸러졌는지(디버깅·품질 확인용). - `output.metrics` = 검색 1건의 원가. `ai`(호출·토큰·추정 비용$), `crawl`(fetch 수·처리 HTML 바이트·크롤한 몰), `source_ms`(소스별 소요), `duration_ms`(전체). ※ `html_bytes`는 처리한 응답 본문 기준(대역폭 근사) — 오픈마켓은 리소스 미차단이라 실제 대역폭보다 작다(하한). - 없는 job_id/잘못된 형식 → `result.desc = "LPS_JOB_NOT_FOUND"`. > **⚠️ 가격의 의미 (배송비)** > - `price` 는 **상품가**입니다. 배송비 포함 여부는 `shipping_fee`/`shipping_type` 으로 판단합니다. > - **쿠팡**: 검색 화면의 배송 신호를 파싱해 채웁니다 — > `shipping_type`: `rocket`(로켓배송, 와우 무료/일반 19,800원↑ 무료) · `rocket_merchant`(판매자로켓) · `free`(명시 무료) · `paid`(유료, `shipping_fee`에 금액) · `null`(미확인) > - **네이버**: 오픈API `lprice` 는 **배송비 제외** 상품가라 둘 다 항상 `null` 입니다. > 가격비교(카탈로그) 화면의 기본 표시는 "**배송비포함** 최저가"라서 **API 값과 다르게 보이는 것이 정상**입니다 > (예: API 5,880원 vs 화면 7,520원). 카탈로그 페이지 자동 수집은 네이버 캡차로 차단되어 미지원. ```bash curl localhost:9600/v1/lps/jobs/c885... ``` --- ## 3. 최저가 이력(그래프) — `GET /v1/lps/products/{product_code}/history` 같은 상품을 여러 번 검색하면 쌓인 스냅샷을 **시각 오름차순**으로 반환합니다. 프론트에서 그래프로 그립니다. **쿼리 파라미터**: `limit` (기본 100, 최대 1000) **응답** ```json { "result": { "success": true, "code": 0, "desc": "SUCCESS" }, "product_code": "T1", "points": [ { "triggered_at": "2026-07-09T10:48:47", // X축 "outcome": "found", "matched_count": 1, "naver": 25200, // 네이버 최저가 "coupang": 24800, // 쿠팡 최저가 "final": 24800, // 최종 최저가 (Y축) "final_source": "coupang", "naver_name": "...", "naver_url": "...", "coupang_name": "...", "coupang_url": "..." } ] } ``` - `naver`/`coupang`가 `null`인 지점 = 그 시점에 해당 소스엔 그 상품이 없었음(그래프 선 공백). ```bash curl "localhost:9600/v1/lps/products/T1/history?limit=100" ``` --- ## 4. 큐 상태 — `GET /v1/lps/queue/stats` ```json { "result": {...}, "counts": { "PENDING": 0, "RUNNING": 1, "DONE": 12, "DEAD": 0 } } ``` ## 5. 헬스체크 — `GET /healthz` 서버 기동 시각을 반환(살아있는지 확인용). --- ## 상태 코드 요약 - **작업 상태**: `PENDING`(대기) · `RUNNING`(처리중) · `DONE`(완료) · `DEAD`(실패-확인필요) - **결과 outcome**: `found`(찾음) · `not_found`(검색했으나 같은 상품 없음)