import { writeFileSync, existsSync, readFileSync } from "fs"; import * as cheerio from "cheerio"; const CACHE_FILE = "./temp/scheduleCache.json"; const URLS = { section1: "https://isangjeong.icems.kr/schdList.do?section=1&m=021101&s=isangjeong", section2: "https://isangjeong.icems.kr/schdList.do?section=2&m=021101&s=isangjeong", }; const MONTH_XPATH_MAP: Record< string, { section: keyof typeof URLS; divIndex: number } > = { "03": { section: "section1", divIndex: 2 }, "04": { section: "section1", divIndex: 3 }, "05": { section: "section1", divIndex: 4 }, "06": { section: "section1", divIndex: 5 }, "07": { section: "section1", divIndex: 6 }, "08": { section: "section1", divIndex: 7 }, "09": { section: "section2", divIndex: 2 }, "10": { section: "section2", divIndex: 3 }, "11": { section: "section2", divIndex: 4 }, "12": { section: "section2", divIndex: 5 }, "01": { section: "section2", divIndex: 6 }, "02": { section: "section2", divIndex: 7 }, }; async function fetchSectionSchedule( url: string, divIndexes: { month: string; index: number }[] ): Promise> { const res = await fetch(url, { headers: { "User-Agent": "Mozilla/5.0 (compatible; today.isangjeong/1.0)", }, }); const html = await res.text(); const $ = cheerio.load(html); const sectionSchedule: Record = {}; for (const { month, index } of divIndexes) { const selector = `#all-scroll > div > form > div > div:nth-of-type(2) > div:nth-of-type(${index}) > dl > dd > ul`; const items: { date: string; desc: string }[] = []; $(selector).find("li").each((_, li) => { const text = $(li).text().trim().replace(/\u00A0/g, " "); if (text) { const [date, desc] = text.split(" : "); if (date && desc) { items.push({ date: date.trim(), desc: desc.trim() }); } } }); sectionSchedule[month] = items; } return sectionSchedule; } export async function getAllSchedules( refresh = false ): Promise> { if (!refresh && existsSync(CACHE_FILE)) { const cached = readFileSync(CACHE_FILE, "utf-8"); return JSON.parse(cached); } const sectionMap: Record = { section1: [], section2: [], }; for (const [month, { section, divIndex }] of Object.entries(MONTH_XPATH_MAP)) { sectionMap[section].push({ month, index: divIndex }); } // 병렬로 section1, section2 요청 const promises = (Object.keys(sectionMap) as (keyof typeof URLS)[]).map( async (section) => { const url = URLS[section]; try { const sectionSchedule = await fetchSectionSchedule(url, sectionMap[section]); console.log(`✅ ${section} 완료`); return sectionSchedule; } catch (err) { console.error(`❌ ${section} 실패:`, err); return Object.fromEntries(sectionMap[section].map(({ month }) => [month, []])); } } ); const results = await Promise.all(promises); const schedule = Object.assign({}, ...results); writeFileSync(CACHE_FILE, JSON.stringify(schedule, null, 2), "utf-8"); console.log(`📦 캐시 저장됨 → ${CACHE_FILE}`); return schedule; }