Skip to main content
Glama
bizinfo-api.ts9.33 kB
// 기업마당 API 클라이언트 // API 문서: https://www.bizinfo.go.kr/web/lay1/program/S1T175C174/apiDetail.do import { fetchWithTimeout } from "../utils/fetch-with-timeout.js"; const BIZINFO_API_KEY = process.env.BIZINFO_API_KEY || ""; const BIZINFO_API_BASE = "https://www.bizinfo.go.kr/uss/rss/bizinfoApi.do"; // 분야 코드 export const BIZINFO_CATEGORIES = { 금융: "PLD0001", 기술: "PLD0002", 인력: "PLD0003", 수출: "PLD0004", 내수: "PLD0005", 창업: "PLD0006", 경영: "PLD0007", 기타: "PLD0008", } as const; // API 응답 타입 export interface BizinfoItem { pblancId: string; pblancNm: string; bsnsSumryCn: string; reqstBeginEndDe: string; jrsdInsttNm: string; excInsttNm: string; refrncNm: string; pblancUrl: string; hashtags: string; trgetNm: string; pldirSportRealmLclasCodeNm: string; pldirSportRealmMlsfcCodeNm: string; creatPnttm: string; totCnt: number; } interface BizinfoResponse { jsonArray: BizinfoItem[]; reqErr?: string; } export class BizinfoApi { private apiKey: string; constructor() { this.apiKey = BIZINFO_API_KEY; } private checkApiKey(): void { if (!this.apiKey) { throw new Error("BIZINFO_API_KEY가 설정되지 않았습니다."); } } // 지원사업 검색 async searchFunds(options?: { category?: keyof typeof BIZINFO_CATEGORIES; hashtags?: string; count?: number; }): Promise<BizinfoItem[]> { this.checkApiKey(); // 카테고리 없이 전체 검색 (카테고리 필터가 너무 제한적임) const params = new URLSearchParams({ crtfcKey: this.apiKey, dataType: "json", searchCnt: String(options?.count || 100), }); // hashtags로 검색 키워드 추가 if (options?.hashtags) { params.append("hashtags", options.hashtags); } const url = `${BIZINFO_API_BASE}?${params}`; const response = await fetchWithTimeout(url, { headers: { Accept: "application/json", }, }); if (!response.ok) { throw new Error(`기업마당 API 요청 실패: ${response.status}`); } const data = (await response.json()) as BizinfoResponse; if (data.reqErr) { throw new Error(`기업마당 API 오류: ${data.reqErr}`); } let results = data.jsonArray || []; // 클라이언트 측에서 카테고리 필터링 (창업 관련 키워드로) if (options?.category) { const categoryKeywords: Record<string, string[]> = { 창업: ["창업", "스타트업", "예비창업", "초기창업", "신규사업", "소상공인"], 금융: ["융자", "대출", "보증", "투자", "펀드"], 기술: ["기술", "R&D", "연구개발", "특허", "혁신"], 인력: ["인력", "채용", "고용", "인턴", "일자리"], 수출: ["수출", "해외", "글로벌", "무역"], 내수: ["내수", "국내", "판로", "마케팅"], 경영: ["경영", "컨설팅", "멘토링", "교육"], }; const keywords = categoryKeywords[options.category] || []; if (keywords.length > 0) { results = results.filter(item => { const text = `${item.pblancNm} ${item.bsnsSumryCn} ${item.hashtags} ${item.pldirSportRealmLclasCodeNm}`; return keywords.some(kw => text.includes(kw)); }); } } return results; } // 창업 관련 지원사업 검색 async searchStartupFunds(options?: { region?: string; founderType?: string; count?: number; }): Promise<BizinfoItem[]> { // 전체 검색 후 창업 관련 키워드로 필터링 let results = await this.searchFunds({ count: options?.count || 100, }); // 창업 관련 키워드로 필터링 const startupKeywords = ["창업", "스타트업", "예비창업", "초기창업", "소상공인", "중소기업", "신규사업", "사업화"]; results = results.filter(item => { const text = `${item.pblancNm} ${item.bsnsSumryCn} ${item.hashtags} ${item.trgetNm}`; return startupKeywords.some(kw => text.includes(kw)); }); // 관련 없는 분야 제외 const excludeKeywords = ["수출", "해외진출", "글로벌", "전시회", "박람회", "번역", "콘텐츠수출", "무역", "산업용"]; results = results.filter(item => { const text = `${item.pblancNm} ${item.pldirSportRealmLclasCodeNm}`; // 제외 키워드가 제목이나 분류에 있으면 제외 return !excludeKeywords.some(kw => text.includes(kw)); }); // 관련성 점수 계산 및 정렬 const scored = results.map(item => { let score = 0; const text = `${item.pblancNm} ${item.bsnsSumryCn} ${item.hashtags} ${item.trgetNm}`; // 창업 관련 점수 if (text.includes("창업")) score += 10; if (text.includes("소상공인")) score += 8; if (text.includes("자금")) score += 5; if (text.includes("융자")) score += 5; if (text.includes("보조금")) score += 5; // 창업자 유형 매칭 if (options?.founderType) { if (options.founderType === "청년" && text.includes("청년")) score += 15; if (options.founderType === "여성" && text.includes("여성")) score += 15; if (options.founderType === "중장년" && (text.includes("중장년") || text.includes("시니어") || text.includes("재도전"))) score += 15; } // 지역 매칭 if (options?.region) { const regionKeywords = ["서울", "부산", "대구", "인천", "광주", "대전", "울산", "세종", "경기", "강원", "충북", "충남", "전북", "전남", "경북", "경남", "제주"]; const userRegion = regionKeywords.find(r => options.region?.includes(r)); if (userRegion && text.includes(userRegion)) score += 10; } return { item, score }; }); // 점수순 정렬 scored.sort((a, b) => b.score - a.score); // 클라이언트 측 필터링 (너무 엄격하지 않게) let filtered = scored.map(s => s.item); // 지역 필터링 (해당 지역 또는 전국 사업) - 완화된 조건 if (options?.region) { const regionKeywords = ["서울", "부산", "대구", "인천", "광주", "대전", "울산", "세종", "경기", "강원", "충북", "충남", "전북", "전남", "경북", "경남", "제주"]; const userRegion = regionKeywords.find(r => options.region?.includes(r)); if (userRegion) { const regionFiltered = filtered.filter(item => { const text = `${item.pblancNm} ${item.bsnsSumryCn} ${item.hashtags} ${item.jrsdInsttNm}`; // 특정 다른 지역이 명시된 사업만 제외 (지역 특화 사업) const otherRegions = regionKeywords.filter(r => r !== userRegion); // 명확히 다른 지역만 대상으로 한 경우에만 제외 const isOtherRegionOnly = otherRegions.some(r => item.pblancNm.includes(r) || item.jrsdInsttNm.includes(r) ); const isMyRegion = text.includes(userRegion); // 내 지역이거나, 다른 지역 전용이 아니면 포함 return isMyRegion || !isOtherRegionOnly; }); // 필터링 후에도 결과가 있으면 적용 if (regionFiltered.length > 0) { filtered = regionFiltered; } } } // 창업자 유형 필터링 (완화된 조건 - 전용 사업만 제외) if (options?.founderType === "청년") { const typeFiltered = filtered.filter(item => { const name = item.pblancNm; // "중장년 전용", "시니어 전용" 같은 명확한 제한만 제외 return !name.includes("중장년") && !name.includes("시니어") && !name.includes("50+"); }); if (typeFiltered.length > 0) filtered = typeFiltered; } else if (options?.founderType === "여성") { // 여성 대상 사업 우선 정렬 (제외하지 않음) filtered.sort((a, b) => { const aWoman = `${a.pblancNm} ${a.hashtags}`.includes("여성") ? 1 : 0; const bWoman = `${b.pblancNm} ${b.hashtags}`.includes("여성") ? 1 : 0; return bWoman - aWoman; }); } else if (options?.founderType === "중장년") { const typeFiltered = filtered.filter(item => { const name = item.pblancNm; // "청년 전용" 같은 명확한 제한만 제외 return !name.includes("청년전용") && !name.includes("청년 전용"); }); if (typeFiltered.length > 0) filtered = typeFiltered; } return filtered; } // HTML 태그 제거 static stripHtml(html: string): string { return html .replace(/<[^>]*>/g, "") .replace(/&nbsp;/g, " ") .replace(/\s+/g, " ") .trim(); } // 신청기간 파싱 (YYYYMMDD ~ YYYYMMDD -> 읽기 좋은 형식) static formatDateRange(dateRange: string): string { if (!dateRange || dateRange === "추후 공지") return "추후 공지"; const parts = dateRange.split("~").map((d) => d.trim()); if (parts.length !== 2) return dateRange; const formatDate = (d: string) => { if (d.length !== 8) return d; return `${d.slice(0, 4)}.${d.slice(4, 6)}.${d.slice(6, 8)}`; }; return `${formatDate(parts[0])} ~ ${formatDate(parts[1])}`; } } export const bizinfoApi = new BizinfoApi();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/re171113-byte/startup-helper-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server