[개발 기록] KAMIS API 연동 개선 및 대시보드 필터링 로직 최적화

2026. 2. 1. 21:57·4. [팀] 프로젝트 및 공모전/4-2 Meat-A-Eye

1. 프로젝트 개요

고기 가격 정보 대시보드에서 KAMIS API 연동을 개선하고, 카테고리별 필터링 로직을 최적화했습니다.


2. 주요 작업 내용

2-1 KAMIS API 액션 구분 및 파라미터 처리

[문제점]

  • 수입 소고기 데이터가 제대로 조회되지 않음
  • p_productrankcode=00 전송 시 API 에러 발생
  • 국내 소고기 등급 선택 시 모든 등급이 동일한 가격으로 표시됨

[해결 방법]

  • 수입 소고기는 periodRetailProductList, 국내 소고기/돼지는 periodProductList를 사용하도록 구분했습니다.

1) apis.py - _fetch_kamis_price_single 함수

# 수입 소고기는 periodRetailProductList 액션 사용
if is_import_beef:
    params = {
        "action": "periodRetailProductList",
        "p_startday": target_day,
        "p_endday": target_day,
        "p_itemcategorycode": codes.get("category", "500"),
        "p_itemcode": codes.get("itemcode", ""),
        "p_kindcode": codes.get("kindcode", ""),
        "p_productrankcode": product_rank_code,  # 원산지 코드 (81=미국산, 82=호주산)
        "p_countrycode": county_code,
        "p_convert_kg_yn": "N",
        "p_cert_key": key,
        "p_cert_id": cert_id,
        "p_returntype": "xml",
    }
else:
    # 국내 소고기/돼지: periodProductList 액션 사용
    params = {
        "action": "periodProductList",
        "p_productclscode": "01",  # 필수 파라미터
        "p_startday": target_day,
        "p_endday": target_day,
        # ... 나머지 파라미터
    }

2) apis.py - fetch_kamis_price 함수 (p_productrankcode 처리)

p_productrankcode=00은 에러를 발생시키므로, "전체" 선택 시 빈 문자열("")을 전송하거나 각 등급을 개별 조회하도록 변경했습니다.

# 전체 등급(00) 선택 시: 국내 소고기는 각 등급을 개별 조회
if grade_code == "00" and is_domestic_beef:
    grade_prices: list[dict[str, Any]] = []
    grade_codes_to_fetch = ["01", "02", "03"]
    
    for gc in grade_codes_to_fetch:
        try:
            price_data = await _fetch_kamis_price_single(
                part_name=part_name,
                region=region,
                grade_code=gc,  # 개별 등급 조회
                target_day=target_day,
                # ... 기타 파라미터
            )
            if price_data:
                grade_code_map = codes.get("grade_codes", {})
                grade_name = grade_code_map.get(gc, f"{gc}등급")
                grade_prices.append({
                    "grade": grade_name,
                    "price": price_data["price"],
                    "unit": "100g",
                    "priceDate": price_data["date"],
                    "trend": "flat",
                })
        except Exception as e:
            logger.warning(f"등급 {gc} 조회 실패: {e}")
            continue
    
    # 전체 평균 계산
    avg_price = sum(gp["price"] for gp in grade_prices) / len(grade_prices)
    return {
        "currentPrice": int(avg_price),
        "unit": "100g",
        "trend": "flat",
        "price_date": grade_prices[0]["priceDate"],
        "source": "api",
        "gradePrices": grade_prices,
        "selectedGrade": "전체",
    }
else:
    # 돼지나 수입 소고기는 등급이 없으므로 빈 문자열 사용
    if is_pork:
        product_rank_code = ""
    elif is_import_beef:
        if "_US" in part_name:
            product_rank_code = "81"  # 미국산
        elif "_AU" in part_name:
            product_rank_code = "82"  # 호주산
        else:
            product_rank_code = ""  # 전체 선택 시

3) apis.py - 날짜 파싱 로직 개선

API는 어제 날짜까지만 데이터가 제공되므로, 연도 누락을 방지하며 파싱하도록 개선했습니다.

# API는 어제 날짜까지만 데이터가 있으므로 어제 날짜를 사용
today = date.today()
yesterday = today - timedelta(days=1)
target_day = yesterday.strftime("%Y-%m-%d")

# 날짜 파싱 시 target_day의 연도를 기본값으로 사용
target_year = target_day[:4] if target_day and len(target_day) >= 4 else None

if "/" in regday_str:
    parts = regday_str.split("/")
    if len(parts) == 2:
        # MM/DD 형식: target_day의 연도 사용
        if target_year:
            regday = f"{target_year}-{parts[0].zfill(2)}-{parts[1].zfill(2)}"

2-2 수입 소고기/돼지고기 데이터 정리

[변경 사항]

  • 수입 소고기: 척아이롤, 양지 삭제 → 갈비, 갈비살만 유지 (미국산/호주산)
  • 수입 돼지고기: 삼겹살만 유지

1) apis.py - PART_TO_CODES 딕셔너리

# 수입 소고기 - itemcode 4401 (갈비, 갈비살만 유지)
"Import_Beef_Rib_US": {
    "itemcode": "4401", "kindcode": "31", "category": "500",
    "food_nm": "수입 소고기/갈비", "grades": ["미국산"], "grade_codes": {"81": "미국산"},
},
"Import_Beef_Rib_AU": {
    "itemcode": "4401", "kindcode": "31", "category": "500",
    "food_nm": "수입 소고기/갈비", "grades": ["호주산"], "grade_codes": {"82": "호주산"},
},
"Import_Beef_Ribeye_US": {
    "itemcode": "4401", "kindcode": "37", "category": "500",
    "food_nm": "수입 소고기/갈비살", "grades": ["미국산"], "grade_codes": {"81": "미국산"},
},
"Import_Beef_Ribeye_AU": {
    "itemcode": "4401", "kindcode": "37", "category": "500",
    "food_nm": "수입 소고기/갈비살", "grades": ["호주산"], "grade_codes": {"82": "호주산"},
},
# 수입 돼지고기 - itemcode 4402
"Import_Pork_Belly": {
    "itemcode": "4402", "kindcode": "27", "category": "500",
    "food_nm": "수입 돼지고기/삼겹살", "grades": ["전체"], "grade_codes": {"00": "전체"},
},

2-3 프론트엔드 카테고리별 필터링 로직 개선

[문제점]

  • "전체" 선택 시 모든 카테고리 데이터가 섞여서 표시됨
  • 수입육 선택 시에도 국내산 데이터가 표시되는 현상

[해결 방법] 카테고리별 기본 부위 목록을 정의하고, "전체" 선택 시 해당 부위만 개별 조회하도록 변경했습니다.

1) dashboard-view.tsx - getDefaultPartsForCategory

const getDefaultPartsForCategory = () => {
  if (selectedCategory === "소") {
    return { beef: ["Beef_Ribeye", "Beef_Rib"], pork: [] };
  } else if (selectedCategory === "돼지") {
    return { beef: [], pork: ["Pork_Shoulder", "Pork_Belly", "Pork_Rib", "Pork_Loin"] };
  } else if (selectedCategory === "수입 소고기") {
    return { beef: ["Import_Beef_Rib_US", "Import_Beef_Rib_AU", "Import_Beef_Ribeye_US", "Import_Beef_Ribeye_AU"], pork: [] };
  } else if (selectedCategory === "수입 돼지고기") {
    return { beef: [], pork: ["Import_Pork_Belly"] };
  }
  return { beef: [], pork: [] };
};

 

2) dashboard-view.tsx - UI 개선 (원산지 카테고리 제어)

const shouldHideGradeCategory = () => {
  return selectedCategory === "수입 소고기" || selectedCategory === "수입 돼지고기";
};

// UI 렌더링 부분 (원산지가 품종에 포함된 경우 등급 선택 UI 숨김)
{!shouldHideGradeCategory() && (
  <Select value={selectedGrade} onValueChange={setSelectedGrade}>
    <SelectTrigger className="w-full">
      <SelectValue placeholder="등급/원산지 선택" />
    </SelectTrigger>
    <SelectContent>
      {gradeOptions[selectedCategory]?.map((option) => (
        <SelectItem key={option.value} value={option.value}>
          {option.label}
        </SelectItem>
      ))}
    </SelectContent>
  </Select>
)}

3. 핵심 학습 포인트

  1. API 파라미터 처리의 정밀함: p_productrankcode=00 에러 대응을 위해 빈 문자열 처리나 개별 루프 조회가 필요하다는 점을 배웠습니다.
  2. 프론트엔드 필터링 전략: 카테고리별 기본 부위(Default List)를 명확히 정의함으로써 데이터 혼선을 방지하고 로직을 단순화할 수 있었습니다.
  3. 날짜 데이터 가공: 공공 API의 데이터 갱신 시점(어제 날짜)을 고려한 타겟팅과 연도 보정 로직의 중요성을 확인했습니다.

4. 결과

  • ✅ 수입 소고기/돼지고기 데이터 정리 완료
  • ✅ 카테고리별 필터링 로직 개선으로 데이터 혼선 해결
  • ✅ 대시보드 로딩 속도 최적화
  • ✅ UI 개선 (원산지 카테고리 스마트 숨김, 텍스트 잘림 해결)

마치며: 이번 작업을 통해 KAMIS API의 특성을 깊이 이해하고, 사용자에게 정확한 데이터를 전달하기 위한 백엔드와 프론트엔드의 협업 로직 설계 과정을 경험할 수 있었습니다.

'4. [팀] 프로젝트 및 공모전 > 4-2 Meat-A-Eye' 카테고리의 다른 글

[프로젝트 회고] Meat-A-Eye: AI 기반 축산물 부위 인식 및 관리 플랫폼 개발기  (0) 2026.02.24
[Meat-A-Eye] 성능 개선 프로세스 상세 메모 블로그  (0) 2026.02.06
[Meat_A_Eye] 소고기 부위 분류 모델 성능 개량  (0) 2026.02.03
[개발 기록] 대시보드 가격 API 응답 시간 줄이기 - 병렬 호출과 캐시 사용  (0) 2026.02.02
[Meat-A-Eye] 데이터 수집 과정 정리  (0) 2026.01.24
'4. [팀] 프로젝트 및 공모전/4-2 Meat-A-Eye' 카테고리의 다른 글
  • [Meat-A-Eye] 성능 개선 프로세스 상세 메모 블로그
  • [Meat_A_Eye] 소고기 부위 분류 모델 성능 개량
  • [개발 기록] 대시보드 가격 API 응답 시간 줄이기 - 병렬 호출과 캐시 사용
  • [Meat-A-Eye] 데이터 수집 과정 정리
고니3000원
고니3000원
공부 내용 정리, 자기발전 블로그 입니다. 기존 네이버 블로그에서 티스토리로 이전했습니다. https://blog.naver.com/pak1010pak
  • 고니3000원
    곤이의 공부 블로그
    고니3000원
  • 전체
    오늘
    어제
    • 분류 전체보기 (176)
      • 1. AI 논문 + 모델 분석 (19)
        • AI 논문 분석 (13)
        • AI 모델 분석 (6)
      • 2. 자료구조와 알고리즘 (16)
        • 2-1 자료구조와 알고리즘 (13)
        • 2-2 강화학습 알고리즘 (3)
      • 3. 자습 & 메모(실전, 실습, 프로젝트) (25)
        • 3-1 문제 해석 (4)
        • 3-2 메모(실전, 프로젝트) (14)
        • 3-3 배포 실전 공부 (7)
      • 4. [팀] 프로젝트 및 공모전 (14)
        • 4-1 팀 프로젝트(메모, 공부) (1)
        • 4-2 Meat-A-Eye (6)
        • 4-3 RL-Tycoon-Agent (3)
        • 4-4 구조물 안정성 물리 추론 AI 경진대회(D.. (4)
      • 5. [개인] 프로젝트 및 공모전 (0)
        • 4-1 귀멸의칼날디펜스(자바스크립트 활용) (5)
        • 4-2 바탕화면 AI 펫 프로그램 (4)
        • 4-3 개인 프로젝트(기타) (3)
      • 개념 정리 step1 (32)
        • Python 기초 (7)
        • DBMS (1)
        • HTML | CSS (3)
        • Git | GitHub (1)
        • JavaScript (5)
        • Node.js (5)
        • React (1)
        • 데이터 분석 (6)
        • Python Engineering (3)
      • 개념 정리 step2 (56)
        • Machine | Deep Learning (15)
        • 멀티모달(Multi-modal) (23)
        • 강화 학습 (10)
        • AI Agent (8)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 네이버 곤이의 블로그(Naver->Tistory)
    • Github
  • 공지사항

  • 인기 글

  • 태그

    html
    EfficientNet
    프로젝트
    Ai
    구현
    bottleneck
    파이썬
    파인튜닝
    pandas
    Attention Is All You Need
    OCR
    Grad-CAM
    강화 학습
    github
    OCR학습
    귀칼
    javascript
    Vision
    논문 리뷰
    알고리즘
    Python
    강화학습
    학습
    자바스크립트
    ViT
    transformer
    자료구조
    paddleocr
    데이터분석
    공모전
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
고니3000원
[개발 기록] KAMIS API 연동 개선 및 대시보드 필터링 로직 최적화
상단으로

티스토리툴바