Files
web_6ea_educo/busan/utils/dataHelper.ts
T
2026-10-20 08:54:01 +09:00

214 lines
7.9 KiB
TypeScript

import regionsData from '../data/regions.json'
import keywordsData from '../data/keywords.json'
import curriculumsData from '../data/curriculums.json'
export interface RegionInfo {
fullName: string
sido: string
sigungu: string
dong: string
}
export interface ParsedRouteParams {
region: RegionInfo
sido: string
shortSido: string
sigungu: string
dong: string
shortRegionName: string
grade: string
subjectKey: string
subjectName: string
targetTerm: string
fullKeyword: string
}
// 5대 핵심 과목 정의
export const SUBJECTS_MAP: Record<string, { key: string, name: string, badge: string, aliases: string[] }> = {
korean: { key: 'korean', name: '국어', badge: '문해력·서술형', aliases: ['국어', '문학', '독서', '비문학', '화법'] },
english: { key: 'english', name: '영어', badge: '구문독해·내신', aliases: ['영어', '영문법', '영어독해', '공통영어'] },
math: { key: 'math', name: '수학', badge: '개념정립·킬러문항', aliases: ['수학', '수1', '수2', '미적분', '확통', '기하'] },
science: { key: 'science', name: '과학', badge: '통합과학·물화생지', aliases: ['과학', '통합과학', '물리', '화학', '생명과학', '지구과학', '과탐'] },
social: { key: 'social', name: '사회', badge: '통합사회·한국사', aliases: ['사회', '통합사회', '한국사', '사탐', '역사', '사회문화', '생활과윤리'] }
}
// 학년 정의
export const GRADES_LIST = [
{ key: 'middle', name: '중학생', aliases: ['중학생', '중1', '중2', '중3', '중등'] },
{ key: 'high', name: '고등학생', aliases: ['고등학생', '고1', '고2', '고3', '고등', '예비고1'] },
{ key: 'elem', name: '초등학생', aliases: ['초등학생', '초등', '초5', '초6'] }
]
// 동 이름으로 지역 검색
export function getRegionByDong(dongQuery: string): RegionInfo | null {
if (!dongQuery) return null
const clean = dongQuery.trim().replace(/동$/, '') // '황금' 또는 '황금동' 모두 매칭
const map = regionsData.dongMap as Record<string, RegionInfo[]>
// 1. 정확 매칭 (황금동)
if (map[dongQuery] && map[dongQuery].length > 0) {
return map[dongQuery][0]
}
// 2. '동' 붙여서 매칭
if (map[dongQuery + '동'] && map[dongQuery + '동'].length > 0) {
return map[dongQuery + '동'][0]
}
// 3. 리스트 전체 순회 매칭
const found = (regionsData.list as RegionInfo[]).find(r =>
r.dong === dongQuery ||
r.dong === (dongQuery + '동') ||
r.dong.startsWith(clean)
)
return found || null
}
// 17개 시도 축약 매핑 및 헬퍼 함수 (예: 대구광역시 -> 대구, 서울특별시 -> 서울)
export function getShortSido(sido: string): string {
if (!sido) return ''
const trimmed = sido.trim()
if (trimmed.includes('서울')) return '서울'
if (trimmed.includes('대구')) return '대구'
if (trimmed.includes('부산')) return '부산'
if (trimmed.includes('인천')) return '인천'
if (trimmed.includes('광주')) return '광주'
if (trimmed.includes('대전')) return '대전'
if (trimmed.includes('울산')) return '울산'
if (trimmed.includes('세종')) return '세종'
if (trimmed.includes('경기')) return '경기'
if (trimmed.includes('강원')) return '강원'
if (trimmed.includes('충북') || trimmed.includes('충청북')) return '충북'
if (trimmed.includes('충남') || trimmed.includes('충청남')) return '충남'
if (trimmed.includes('전북') || trimmed.includes('전라북')) return '전북'
if (trimmed.includes('전남') || trimmed.includes('전라남')) return '전남'
if (trimmed.includes('경북') || trimmed.includes('경상북')) return '경북'
if (trimmed.includes('경남') || trimmed.includes('경상남')) return '경남'
if (trimmed.includes('제주')) return '제주'
return trimmed.slice(0, 2)
}
// 축약 지역명 생성 (예: '대구 황금동', '서울 대치동', '경기 정발산동')
export function getShortRegionName(sido: string, dong: string, sigungu?: string): string {
const meta = regionsData as any
const prefix = meta.keywordPrefix !== undefined ? meta.keywordPrefix : `${getShortSido(sido)} `
if (prefix === '') {
// 서울 (예: 강남구 개포동)
return `${sigungu ? sigungu + ' ' : ''}${dong || ''}`.trim()
}
// 경기, 인천, 광주, 부산, 대구, 대전 (예: 경기 수원 영통동, 대구 수성구 범어동)
return `${prefix}${sigungu ? sigungu + ' ' : ''}${dong || ''}`.trim()
}
// 지능형 URL 슬러그 파서
// 지원 예시:
// - ['대구광역시', '수성구', '황금동']
// - ['대구광역시', '황금동', '중학생', '영어']
// - ['황금동', '중학생', '영어']
// - ['황금동-중학생-영어']
// - '대구광역시-황금동-중학생-영어'
export function parseRouteSlug(slugInput: string | string[]): ParsedRouteParams {
let tokens: string[] = []
if (Array.isArray(slugInput)) {
tokens = slugInput.flatMap(s => decodeURIComponent(s).split(/[-_\s/]+/))
} else if (typeof slugInput === 'string' && slugInput.trim()) {
tokens = decodeURIComponent(slugInput).split(/[-_\s/]+/)
}
tokens = tokens.map(t => t.trim()).filter(Boolean)
let matchedRegion: RegionInfo | null = null
let detectedGrade = '중학생'
let detectedSubjectKey = 'english'
let detectedSubjectName = '영어'
let detectedTerm = '과외'
// 1. 토큰들 중 동 이름 매칭
for (const token of tokens) {
const reg = getRegionByDong(token)
if (reg) {
matchedRegion = reg
break
}
}
// 2. 동 이름이 토큰에 없으면 전체 리스트에서 부분 일치 검색
if (!matchedRegion && tokens.length > 0) {
for (const token of tokens) {
const found = (regionsData.list as RegionInfo[]).find(r => token.includes(r.dong) || (token.length >= 2 && r.dong.includes(token)))
if (found) {
matchedRegion = found
break
}
}
}
// 기본값 (568개 타겟 지역 중 첫 번째 기준)
if (!matchedRegion) {
matchedRegion = (regionsData.list as RegionInfo[])[0] || {
fullName: '경기도 고양시 덕양구 고양동',
sido: '경기도',
sigungu: '고양시 덕양구',
dong: '고양동'
}
}
// 3. 학년 추출
const fullText = tokens.join(' ')
for (const g of GRADES_LIST) {
if (g.aliases.some(a => fullText.includes(a))) {
detectedGrade = g.name
break
}
}
// 4. 과목 추출
for (const [key, sub] of Object.entries(SUBJECTS_MAP)) {
if (sub.aliases.some(a => fullText.includes(a))) {
detectedSubjectKey = key
detectedSubjectName = sub.name
break
}
}
// 5. 목적/행동 추출
if (fullText.includes('내신')) detectedTerm = '내신대비'
else if (fullText.includes('학원')) detectedTerm = '학원추천'
else if (fullText.includes('수능')) detectedTerm = '수능대비'
else if (fullText.includes('기출')) detectedTerm = '기출문제분석'
else detectedTerm = '1:1 맞춤과외'
const shortSido = getShortSido(matchedRegion.sido)
const shortRegionName = getShortRegionName(matchedRegion.sido, matchedRegion.dong, matchedRegion.sigungu)
const fullKeyword = `${shortRegionName} ${detectedGrade} ${detectedSubjectName} ${detectedTerm}`
return {
region: matchedRegion,
sido: matchedRegion.sido,
shortSido,
sigungu: matchedRegion.sigungu,
dong: matchedRegion.dong,
shortRegionName,
grade: detectedGrade,
subjectKey: detectedSubjectKey,
subjectName: detectedSubjectName,
targetTerm: detectedTerm,
fullKeyword
}
}
export function getAllSubjectsList() {
return Object.values(SUBJECTS_MAP)
}
export function getCurriculumBySubject(subjectKey: string) {
const data = curriculumsData as any
return data[subjectKey] || data['english']
}
export function getPopularRegions(count = 20) {
return (regionsData.list as RegionInfo[]).slice(0, count)
}
export function getTotalRegionsCount() {
return regionsData.total
}