Skip to content

feat(pesticide): 농약 등록정보 동기화 활성화 및 실 적재 - #20

Draft
Kimseungin0529 wants to merge 15 commits into
devfrom
feat/pesticide-sync-activation
Draft

feat(pesticide): 농약 등록정보 동기화 활성화 및 실 적재#20
Kimseungin0529 wants to merge 15 commits into
devfrom
feat/pesticide-sync-activation

Conversation

@Kimseungin0529

Copy link
Copy Markdown
Collaborator

변경 내용

  • PSIS-RDA OpenAPI(농약등록정보) 전량 페이지네이션 수집 → 실 DB 적재하는 sync 서비스 활성화, 관리자 트리거 컨트롤러(AdminPesticideSyncController), 진행상태 추적(PesticideSyncJob)
  • totalCount 미달 종료 시 잡 FAILED 처리(완결성 가드), 응답 파서/로우매퍼/probe 정비
  • 관리자 토큰 없이 실행하는 수동 로더 테스트(PesticideSyncManualLoaderTest)로 적재+검증을 한 실행에 병합
  • backend/docs/pesticide-sync-runbook.md에 서버 반영 dump/restore 핸드오프 절차 추가

변경 이유

농약 검색/조회 기능에 필요한 실데이터를 확보하고, 부분 적재로 인한 데이터 불완전성을 방지(완결성 가드)하기 위함.

영향 범위

농약 sync/조회 도메인, 관리자 sync API, 신규 pesticide_sync_job 테이블 및 농약 카탈로그 데이터.

검증

  • 수동 로더 테스트 PesticideSyncManualLoaderTest: BUILD SUCCESSFUL, 1 test / 0 failures / 0 errors, fetched=143912=total, status=SUCCEEDED, 적재 pesticides=3583 / pests=990 / applications=121448, 조회 검증('가스가마이신' → 5건, nextCursor·병해충 조인 정상) 통과.
  • 코드리뷰(시니어 리뷰어) 통과: 완결성 가드 정확·단위테스트됨, 시크릿 유출 없음, admin 엔드포인트 ROLE_ADMIN 게이트, 페이지 단위 트랜잭션. 판정 "Ready".
  • Known issue / follow-up: 동시 sync job 방지 가드 없음 — 수동 1회 운영이고 unique 제약으로 데이터 손상은 방지되나, RUNNING 잡 존재 시 409 반환 가드를 후속으로 권장.

배포 및 호환성

  • DB: dev/prodddl-auto:none. 신규 pesticide_sync_job 테이블과 적재된 농약 카탈로그 데이터는 런북의 pg_dump/restore 절차(Part A/B, --clean --if-exists, 롤백 포함)로 서버에 반영합니다. dump 산출물에는 키/토큰이 포함되지 않음을 확인했습니다.
  • 시크릿: PSIS API 키는 설정에서 ${ENV:} 플레이스홀더만 사용하며, .psis.env는 gitignore 처리되어 있습니다.
  • API: 관리자 sync 엔드포인트는 ROLE_ADMIN 권한으로만 접근 가능합니다.

체크리스트

  • 관련 테스트를 추가하거나 수정했습니다.
  • 로컬 검증을 완료했습니다. (로더 실행 BUILD SUCCESSFUL·적재/조회 검증 통과)
  • API·설정·DB 호환성 영향을 확인했습니다.
  • 민감한 정보가 포함되지 않았습니다.

🤖 Generated with Claude Code

Kimseungin0529 and others added 15 commits July 13, 2026 15:23
data.go.kr류 API는 HTTP 200에 에러 resultCode를 담아 응답할 수 있어, 이후
동기화 로직이 에러를 감지하려면 item 목록뿐 아니라 응답 봉투(header/body)
정보가 필요하다. parseEnvelope를 추가하고 기존 parse는 이를 재사용하도록
정리한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
data.go.kr류 API는 HTTP 200 + 에러 resultCode + 빈 item으로 응답할 수 있는데,
기존 로직은 이를 "빈 페이지 = 동기화 완료"로 오인해 0건 동기화를 성공으로
보고했다. resultCode가 성공 코드("00"/"0")가 아니면
BusinessException(PESTICIDE_SYNC_FAILED)로 즉시 실패시켜 운영자가 키/URL
설정 문제를 바로 알 수 있게 한다. resultCode 태그 자체가 없는 기존 테스트
픽스처는 하위호환을 위해 그대로 통과한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
diagnoseRequired가 itemName/cropName/pestName 각각이 실제 raw row에서
해석됐는지 여부를 반환한다. 이후 프로브 기능이 실응답을 받았을 때 어떤
필드의 후보 태그명이 틀렸는지 바로 알려주는 데 재사용한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PesticideSyncService.probe가 1페이지(기본 10건)만 조회해 resultCode/
totalCount/실제 태그명/필수 필드 매핑 결과/샘플 매핑 결과를 반환한다.
전량 동기화 전에 필드 태그 매핑과 데이터 규모를 안전하게 검증할 수 있다.
sync와 동일한 업스트림 에러 처리 규칙을 적용하며 DB에는 쓰지 않는다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
POST /api/v1/admin/pesticide-sync/probe?rows=1..100 을 추가해 전량 동기화
전에 PesticideSyncService.probe 결과를 확인할 수 있게 한다. rows 범위
검증은 컨트롤러 경계에서 수행하고, 범위를 벗어나면 기존 INVALID_INPUT으로
400을 반환한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
API 키 발급 완료를 전제로 env 설정, 프로브→매핑 보정→규모 판단→전량 동기화
순서의 절차와 롤백 방법을 정리한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- parseEnvelope에서 getElementsByTagName("item")을 루프마다 재호출하던 것을
  itemNodes로 1회 hoist (페이지당 O(n^2) DOM 재스캔 회귀 복원)
- sync/probe에 중복된 pageNo/numOfRows/type 쿼리맵을 pageQuery 헬퍼로 추출
  (type 파라미터명 조정 시 단일 지점)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- serviceKey -> apiKey, PSIS_PESTICIDE_SERVICE_KEY -> PSIS_PESTICIDE_API_KEY
- pageNo/numOfRows -> serviceCode(SVC01)/serviceType(AA001)/displayCount/startPoint
  오프셋 페이지네이션으로 전환, displayCount는 최대 50으로 clamp
- resultCode/resultMsg -> errorCode/errorMsg (성공 응답엔 errorCode가 없음)
- PsisPesticideRowMapper가 실응답 태그(pestiKorName/cropName/diseaseWeedName 등)를
  직접 매핑하도록 후보 태그 목록 제거
- 관련 파서/매퍼/서비스/컨트롤러 테스트를 PSIS 실응답 XML 형태로 갱신

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- domain: PesticideSyncJob/PesticideSyncJobStatus/PesticideSyncJobRepository 추가
  (PolicySyncJob과 동일한 패턴, table pesticide_sync_job)
- application: PesticideSyncService.sync()를 createSyncJob(RUNNING 잡 저장)/
  runExistingJob(startPoint 오프셋 페이지네이션 실행 후 succeed/fail)/getJob으로 분리,
  PesticideSyncAsyncRunner(@async)로 실제 순회를 비동기 실행
- api: AdminPesticideSyncController가 POST로 잡을 생성해 비동기 실행을 트리거하고
  GET /{jobId}로 상태를 조회하도록 변경, probe 허용 범위를 1..50으로 조정
  (displayCount 최대치에 맞춤)
- 관련 도메인/서비스/컨트롤러 테스트 추가·갱신

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 실제 엔드포인트/파라미터(apiKey, serviceCode=SVC01, serviceType=AA001,
  displayCount<=50, startPoint)와 errorCode 기반 에러 규격으로 정정
- 동기 전량 실행 절차를 잡 생성(POST) + 상태 폴링(GET /{jobId}) 흐름으로 갱신
- dev/prod에 pesticide_sync_job 테이블 수동 준비가 필요함을 명시

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
크롤 도중 PSIS가 일시적으로 빈 페이지를 반환하면 수집량이 totalCount에
못 미친 채 순회가 종료될 수 있는데, 기존에는 SUCCEEDED로 표시돼 부분 적재가
성공으로 오인됐다. 미달이면 명확한 메시지와 함께 FAILED로 표시(재실행은
dedup되어 idempotent). 코드리뷰 지적사항 반영.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ROLE_ADMIN HTTP 엔드포인트 대신, PSIS_PESTICIDE_SYNC_RUN=true 플래그가 있을
때만 활성화되는 @SpringBootTest 로더로 실 Postgres에 전량 적재. 일반 test
실행에선 스킵. 런북에 사용법·전제(Postgres+Redis+env) 추가.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- PesticideCatalogVerificationTest: 적재 후 PesticideCatalogService(검색/병해충
  조회) 로직을 실 DB에 대해 직접 검증(PSIS_PESTICIDE_VERIFY=true 게이트, 토큰 불필요)
- .psis.env(로컬 PSIS 키 파일) gitignore 처리

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
local 프로필은 ddl-auto:create라 Spring 컨텍스트가 부팅될 때마다 스키마를
DROP/재생성한다. 적재 로더와 조회 검증을 별도 테스트(별도 부팅)로 두면 검증
테스트의 부팅이 방금 적재한 데이터를 통째로 지운다. 검증을 로더 테스트 안으로
옮겨 한 번의 부팅에서 적재→검색/병해충 조회까지 끝내도록 하고, 별도
PesticideCatalogVerificationTest를 제거한다. 런북에 함정을 명시한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dev/prod는 ddl-auto:none이라 농약 스키마+데이터를 서버 DB에 직접 넣어야 한다.
서버에서 PSIS를 재호출하는 대신 로컬 적재분을 pg_dump로 덤프해 서버에 붓는
방식을 문서화한다. pg_dump 파일이 CREATE TABLE(스키마)+데이터를 함께 담으므로
별도 DDL은 불필요. Part A(앱 담당자: 덤프 생성)와 Part B(서버 담당자: 처음
하는 사람도 따라할 수 있는 적용/검증/롤백 명령)로 분리해 단독 수행 가능하게 했다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant