From 7dc925bcd4e3b15b7a43eab27bfb7075fb17e499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AF=BC=ED=97=8C?= Date: Fri, 7 Aug 2026 15:02:25 +0900 Subject: [PATCH] =?UTF-8?q?docs(lps):=202026-08-07=20=EC=84=B8=EC=85=98=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=E2=80=94=20IP=20=EB=A1=9C=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EA=B2=80=EC=88=98=C2=B7=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20UA=20=EA=B7=9C=EB=AA=85=C2=B7=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 다음 세션이 이어받을 수 있게 남긴다. - docs/2026-08-07-session-notes.md 신규: 세 덩어리(IP 로테이션 결함 6개, 네이버 컨테이너 차단이 실은 UA 플랫폼 토큰이었다는 규명, '못 봤다'를 '없다'고 말하던 문제의 4단계 해결)와 **되돌리기 전에 볼 근거**(예산을 IP 에 묶는 이유, 태우지 않는 판정, 임대 없이 크롤 금지, UA 는 플랫폼만 바꾸고 버전은 실값, 상태는 하나로 저장하고 표시에서 접기). - 미해결: 쿠팡 간헐 차단 — IP 에 따라 갈리므로 환경이 아니라 평판. 쿠팡만 gate(해외 IP)를 쓰는 게 유력한 원인이고, 네이버처럼 kr_host 로 바꾸는 검증이 중단된 상태. - 다음 할 일 5가지(운영 마이그레이션 → 쿠팡 KR 검증 → 예산 튜닝 → AI 매칭 정확도 → admin 잡목록). - 주의사항 5가지: **마이그레이션→코드 순서 강제**(역순이면 최저가 조회가 통째로 실패, 재현 확인), 워커 켜고 pytest 하면 test_job_queue 3개 깨짐, JS 지문 위장은 patchright 가 막아 불가, 로컬 negodata-front 는 node_modules 가 named volume 이라 재빌드로 의존성이 안 늘어남. - README 문서 표에 링크 추가. Co-Authored-By: Claude Opus 5 --- lps/README.md | 1 + lps/docs/2026-08-07-session-notes.md | 87 ++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 lps/docs/2026-08-07-session-notes.md diff --git a/lps/README.md b/lps/README.md index fa14a86..da707e7 100644 --- a/lps/README.md +++ b/lps/README.md @@ -198,6 +198,7 @@ docker compose up -d # negosium 스택 + lps-api·lps-worker·lps | **[데이터베이스](docs/database.md)** | 개발자/기획자 | 테이블 6종 구조와 코드값(+by_mall·ip_session·proxy_port 장부) | | **[API 사용법](docs/api.md)** | 연동 개발자 | 엔드포인트·요청/응답·metrics 예시 | | **[운영 가이드](docs/operations.md)** | 운영자/개발자 | 실행·병렬·관측(readyz/ops/알림)·**Docker 배포**·문제 해결 | +| **[2026-08-07 세션 기록](docs/2026-08-07-session-notes.md)** | 팀 | 직전 작업 요약 — 결정 근거·미해결(쿠팡 차단)·다음 할 일·주의사항 | | **[크롤러 논의](docs/decision-openmarket-crawler.md)** | 팀 | 오픈마켓 크롤러 유지 여부(ROI) 의사결정 메모 | --- diff --git a/lps/docs/2026-08-07-session-notes.md b/lps/docs/2026-08-07-session-notes.md new file mode 100644 index 0000000..4a4a629 --- /dev/null +++ b/lps/docs/2026-08-07-session-notes.md @@ -0,0 +1,87 @@ +# 2026-08-07 작업 기록 — IP 로테이션 검수 · 네이버 컨테이너 차단 규명 · 결과 상태 정의 + +다음 세션이 이어받을 수 있도록 **무엇이 왜 바뀌었고, 무엇이 남았는지**만 적는다. +상태 정의는 `result-states.md`, 운영 절차는 `operations.md` 가 소스다. + +## 1. 무슨 일이 있었나 + +세 덩어리다. 앞의 둘은 "고장 났다"에서 출발했고, 셋째는 그 과정에서 드러난 설계 문제다. + +**① IP 로테이션이 설계대로 안 돌고 있었다.** 검수해 보니 결함이 6개였고, 그중 하나는 +`bot_detection.ip_request_no` 를 항상 `1` 로 만들어 **과거 진단 결론까지 오염**시켰다. + +**② 컨테이너에서 네이버만 막혔다.** 원인은 컨테이너가 아니라 **UA 의 플랫폼 토큰**이었다. +네이버는 리눅스 데스크톱 Chrome 을 HTTP 405 로 거부한다. + +**③ '못 봤다'를 '없다'고 말하고 있었다.** 차단당해 확인 못 한 몰이 화면에서 '없음(–)'과 +구분되지 않았다. 상태를 정의하고 4단계로 화면까지 연결했다. + +## 2. 지금 상태 + +| 항목 | 상태 | +|---|---| +| 네이버 | 컨테이너에서 정상(UA 보정). 검색당 40건 | +| 쿠팡 | **간헐적 차단** — 해외 IP(`gate`) 사용이 원인으로 추정(아래 4번) | +| IP 로테이션 | 예산·회전·소각이 모두 의도대로. 서킷브레이커가 풀 고갈 차단 | +| 결과 상태 | 몰별 7상태 → 운영자 화면은 그대로, 사용자 화면은 3가지로 접음 | +| 잡 | 한 소스가 막혀도 정상 종료(DEAD 아님) | +| 테스트 | lps 292 · negodata 107 passed | +| 배포 | 로컬 전체 재빌드·기동 완료. **운영은 마이그레이션 미적용**(아래 5번) | + +## 3. 이번에 정한 것 (되돌리려면 근거부터 볼 것) + +- **예산은 브라우저가 아니라 IP 에 묶는다.** 유휴 정리(120s)는 브라우저만 닫고 같은 IP 로 + 돌아오는데, 브라우저 기준으로 세면 카운터가 매번 초기화돼 **예산이 영영 발화하지 않는다** + (실측: 6회 검색이 전부 같은 포트·`ip_req#1`). 시계도 하나(`_session_started_at`)로 통일했다. +- **회전해도 소용없는 차단은 IP 를 태우지 않는다.** 서로 다른 IP 3개가 연속으로 **첫 요청부터** + 막히면 IP 문제가 아니다(평판이면 몇 개는 통과하고, 과사용이면 뒤쪽에서 막힌다). 임계 없이 + 태우면 잡 16건에 100포트가 30분 쿨다운으로 묶인다. 성공 1회로 자동 해제된다. +- **임대 없이는 크롤하지 않는다.** 풀이 마르면 예전엔 계산식으로 포트를 골라 **남이 쥔 IP 를 + 같이 썼다**(실측 재현). 장부가 막으려던 바로 그 문제라, 못 잡으면 실패하는 게 맞다. +- **UA 는 플랫폼 토큰만 바꾸고 버전은 실제 값을 쓴다.** 통째로 하드코딩하면 컨테이너 Chrome 이 + 업데이트될 때 UA 와 엔진이 어긋나 그 불일치가 새 봇 신호가 된다. 쿠팡은 잘 통과하므로 안 건드린다. +- **상태는 하나로 정의·저장하고 표시 단계에서 접는다.** 저장을 단순화하면 운영자가 원인을 못 보고, + 표시를 상세화하면 사용자가 못 읽는다. `partial` 을 컬럼으로 둔 것도 같은 이유 — 소비자가 + '어떤 상태가 확인된 것인가'라는 판단 규칙까지 알면 상태 정의가 두 곳으로 흩어진다. +- **부분 실패의 `not_found` 는 네거티브 캐시에 넣지 않는다.** 못 본 몰에 있었을 수 있는데 + '없음'으로 굳히면 TTL 동안 재검색이 막힌다(사용자가 '다시 검색'을 눌러도 캐시 히트). + +## 4. 미해결 — 쿠팡 간헐적 차단 + +`사용권한이 제한된`(3.4KB)·`errors.edgesuite.net`(0.4KB) 마커로 막히는데 **IP 에 따라 갈린다** +(일부는 통과 → 서킷브레이커가 자동 해제). 즉 환경이 아니라 **IP 평판** 쪽이다. + +유력한 원인: **쿠팡만 `gate`(국가 무지정 = 해외 IP)를 쓴다.** 네이버는 `kr_host`(한국 IP)로 +바꾼 뒤 안정됐다. 같은 처방이 통할 가능성이 높다. + +> 검증하려던 스크립트가 도구 시간 제한으로 중단됐다. 컨테이너에서 `gate` vs `kr` 통과율을 +> IP 3개씩 비교하면 된다(네이버 UA 검증과 같은 방식). + +⚠️ 맥 컨테이너는 amd64 를 **Rosetta 로 에뮬레이션**한다. 여기서 쿠팡이 막히는 건 실서버와 +다를 수 있다 — 실서버에선 쿠팡이 60건 정상이었다. 판단은 실서버 로그로 한다. + +## 5. 다음에 할 일 (우선순위) + +1. **운영 DB 마이그레이션 적용** — 아직 안 됐다. 순서가 중요하다(아래 6번). + `6_lps_2026-08_dbeaver.sql`(lps_db) · `2026-08-07-iilp-source-state.sql`(negosium_db) +2. **쿠팡 KR 게이트웨이 검증** — 4번. 지금 최저가 커버리지의 최대 구멍이다. +3. **예산 튜닝** — F1 수정으로 `ip_request_no` 가 **처음으로 실제 사용량을 반영**한다. + `ip_req#1` 위주면 IP 평판, `2` 이상이면 예산 하향. **2026-08-06 이전 데이터는 쓰지 말 것.** +4. **AI 매칭 정확도 측정** — 세션 초반에 접근법만 논의하고 미착수. 정답 세트 20~30개로 + "몇 % 맞나"를 재야 한다. 지금도 최저가 품질의 최대 병목이다. +5. lps-admin **잡 목록**에 몰별 상태 노출(상품 화면엔 이미 있음, 우선순위 낮음). + +## 6. 작업 시 주의 + +- **마이그레이션 → 코드 순서를 지킬 것.** negodata 가 ORM 전체 엔티티를 조회하므로 + (`lps_sync_crud.py:96`), 컬럼이 없는 DB 에 새 코드가 붙으면 **최저가 조회가 통째로 실패**한다 + (재현 확인). 역순(마이그레이션 후 옛 코드)은 안전하다 — 옛 컨테이너로 실증했다. +- **워커를 켜 둔 채 pytest 하면 `test_job_queue` 3개가 깨진다.** 워커가 테스트 잡을 집어가서다. + 코드 문제가 아니다 — `docker compose stop lps-worker` 후 돌리면 292 passed. +- **JS 로 브라우저 지문을 덮는 방법은 이 스택에서 통하지 않는다.** patchright 가 + `add_init_script`(CDP 주입)를 무력화하고, MV3 확장도 값이 안 바뀐다. 다시 시도하기 전에 + `services/search/fingerprint.py` docstring 을 볼 것. +- **로컬 dev 의 `negodata-front` 는 `node_modules` 가 named volume 이다.** 의존성이 늘면 + 이미지를 다시 빌드해도 반영되지 않는다 — 컨테이너 안에서 `npm install` 해야 한다(prod 은 + `Dockerfile.prod` 라 해당 없음). +- 테스트가 실DB를 TRUNCATE 하는 문제는 그대로다(`price_history`·`job`·`ip_session`·`bot_detection`).