1// íì§ ì ë³´(Local Info) â íêµê´ê´ ë°ì´í°ë©/TourAPI ì°ë + 목ì í´ë°± 2// TourAPI ê¸°ë° ì¹´í ê³ ë¦¬ë ì¤API(/api/tour) ì°ì , ì¤í¨ ì 목ì ì¼ë¡ í´ë°±. 3// êµíµÂ·ìì ì íë ì´ì ì½í ì¸ (ë°ì´í°ë© API ìë) â data-i18nì¼ë¡ ë¤êµì´. 4 5const TOURAPI_TYPE = { stay: 32, food: 39, heritage: 14, activity: 28 }; 6// TourAPI ê³ì´ ìë¹ì¤ ì¬íì©(ë°ë ¤ë물 ëë°ì¬í) â KorService2ì ëì¼ íë¼ë¯¸í°(area/sigungu via region) 7const TOURAPI_SVC = { pet: "KorPetTourService2" }; 8// ì¤ì¬ê´ê´ì§(LocgoHubTarService1)ë ë°ì´í°ë© ê³ì´: íì íì¤ì½ë signguCd íì â ì ë¨ ìêµ° 매í 9const HUB_SIGNGU = { mokpo: "46110", muan: "46840", sinan: "46910", jindo: "46900", wando: "46890", boseong: "46780", haenam: "46820", 10 yeosu: "46130", suncheon: "46150", goheung: "46770", jangheung: "46800", gangjin: "46810", yeongam: "46830", hampyeong: "46860", yeonggwang: "46870" }; 11 12// êµíµÂ·ìì â íë ì´ì (ë°ì´í°ë© ìë). data-i18n í¤ë¡ ë¤êµì´. 13const CURATED = { 14 transport: [ 15 { icon: "âï¸", t: "local.tr.air.t", d: "local.tr.air.d", tags: ["무ìêµì ê³µí (MWX)", "ëª©í¬ ê´ë¬¸", "ì¤ì¬ì¹´Â·ìíì´Â·íì´ë² ì´"] }, 16 { icon: "ð", t: "local.tr.rail.t", d: "local.tr.rail.d", tags: ["목í¬ì · ê´ì£¼ì¡ì ì", "KTX·SRT", "ì©ì° 2h30m"] }, 17 { icon: "â´", t: "local.tr.ship.t", d: "local.tr.ship.d", tags: ["목í¬Â·ìë ì¬ê°ì í°ë¯¸ë", "íêµí´ì´ì¡°í© ëì ", "ê³ í¥ ëë¡ëí", "ì§ë í½ëª©í"] }, 18 { icon: "ð", t: "local.tr.bus.t", d: "local.tr.bus.d", tags: ["ì ë¨ ê³µìë²ì¤", "ì¬ì¦ ìµì¤íë ì¤"] }, 19 { icon: "ð", t: "local.tr.car.t", d: "local.tr.car.d", tags: ["ë í°ì¹´", "ì²ì¬ëêµÂ·ìí´ìê³ ì"] }, 20 { icon: "ð²", t: "local.tr.bike.t", d: "local.tr.bike.d", tags: ["ì¦ë·ì²ì°ë ì¬ë¡ìí°", "ìì ê±° ëì¬"] }, 21 // ë¤ì¤ê´ë¬¸ì²´ê³(Multi-Gateway) â 무ìê³µí ë¨ì¼ìì¡´ ëì A~D 4ë¨ê³ë¡ ì¤ê³(ì¤ê³ ì ì). 22 { icon: "ð", t: "local.tr.gw.t", d: "local.tr.gw.d", tags: ["A ì 주공í(íì¬)", "B ì¸ì²ê³µí+KTX(íì¬)", "C 무ìê³µí(ì¬ê°í í)", "D 목í¬í·ìëí(íì¬)"] } 23 ], 24 safety: [ 25 { icon: "ð¡", t: "local.sf.comm.t", d: "local.sf.comm.d", tags: ["ì¤íë§í¬ ìì± ìì ë§", "ì¤ì§ K-Eco Hotspot"] },
26 { icon: "ð", t: "local.sf.sos.t", d: "local.sf.sos.d", tags: ["ìì± SOS", "112 · ê´ê´ê²½ì°°"] }, 27 { icon: "ð¥", t: "local.sf.med.t", d: "local.sf.med.d", tags: ["ìê¸ìë£ ìë´", "ë§ì ì격 ìë£ë¶ì¤"] }, 28 { icon: "ð", t: "local.sf.sea.t", d: "local.sf.sea.d", tags: ["물ë·íë ì림", "ëì ì´í ìí"] } 29 ], 30 // ê³ ë ¤ì¸ì ìë¨í´ (F-07 ãê·í¥ã) â ì¤ì¬ ì¬ë£Â·íì¥ ê¸°ë°. ì¶ì²: docs/09_ê³ ë ¤ì¸_ê·í¥_ì¤ê³ì §10 31 koryoin: [ 32 { icon: "ð", t: "local.ko.center.t", d: "local.ko.center.d", tags: ["ê´ì£¼ ê´ì°êµ¬ ì곡ë", "2021 ê°ê´", "ì 물 1.2ë§ì "] }, 33 { icon: "ðï¸", t: "local.ko.park.t", d: "local.ko.park.d", tags: ["ë¤ëª¨ìì´ë¦°ì´ê³µì", "2021 ì í´ë´í 기ë "] }, 34 { icon: "ð¨", t: "local.ko.art.t", d: "local.ko.art.d", tags: ["2024 ê°ê´", "ê³ ë ¤ì¸ íê° ì주"] }, 35 { icon: "ð¤", t: "local.ko.support.t", d: "local.ko.support.d", tags: ["ì¢ í©ì§ìì¼í°", "ë¬ììì´ ìë´Â·ë¿ë¦¬ì°¾ê¸° ì°ê³"] } 36 ], 37 // 2026 ì¬ ë°©ë¬¸ì í´ â ì ë¨ê´ì£¼ ãì¬ ë°ê°ì¬íã(ê³µì ë°í) 8ê° ìêµ° 16ê° ì¬ + ãì¬ì ë (8.8)ã ì¶ì² ì¬ ì¬êµ¬ì±. 38 // ì ì§ ìì¹: íê¸(50%)ì´ ìëë¼ ì£¼ë¯¼íì·ì¸ë°ì´ë ìë¶ì¬ì¼ë¡ ë©ìì§ ì¬êµ¬ì±, ì ë¶ì¦ ìê±´ ë± íê³ ëª ì. 39 islands2026: [ 40 { icon: "ð", t: "local.is.policy.t", d: "local.is.policy.d", tags: ["ì ë¨ê´ì£¼íµí©í¹ë³ì ê³µì ë°í", "2026.8.29~11.4", "8ê° ìêµ° 16ê° ì¬"] }, 41 { icon: "â", t: "local.is.mokpo.t", d: "local.is.mokpo.d", tags: ["목í¬ì°ìì¬ê°ì í°ë¯¸ë ì¶í", "ë¬ë¦¬ë·ì¸ë¬ë"] }, 42 { icon: "ð¥¾", t: "local.is.yeosu.t", d: "local.is.yeosu.d", tags: ["ì¬ì íì§ ì¬ê°ì ê²½ì ", "ííë·ì¬ë·거문ë·ê°ë·ê¸ì¤ë"] }, 43 { icon: "ð¿", t: "local.is.goheung.t", d: "local.is.goheung.d", tags: ["ê³ í¥ ëë¡ëí ì¶í", "ì¥ì¬Â·ì ë·ì°íë"] }, 44 { icon: "ð¦ª", t: "local.is.boseong.t", d: "local.is.boseong.d", tags: ["ë³´ì± ë²êµ ì¸ê·¼ ì ì°©ì¥", "ì¥ë(ëì¬ë¥´ ìµì§)"] }, 45 { icon: "ð", t: "local.is.gangjin.t", d: "local.is.gangjin.d", tags: ["ê°ì§ ëë³´ ì°ëêµ(ì¶ë ë¤ë¦¬)", "ê°ì°ë"] }, 46 { icon: "ð¦", t: "local.is.yeonggwang.t", d: "local.is.yeonggwang.d", tags: ["ìê´ íì§ ì ì°©ì¥ ê²½ì ", "ì¡ì´ë(ì²ì°ê¸°ë 물 ììì§)"] }, 47 { icon: "ð", t: "local.is.jindo.t", d: "local.is.jindo.d", tags: ["ì§ë í½ëª©í ì¶í", "ê´ë§¤ë(ë¤ëí´í´ìêµë¦½ê³µì)"] }, 48 { icon: "ð", t: "local.is.sinan.t", d: "local.is.sinan.d", tags: ["ì ì íì§ ì¬ê°ì ê²½ì ", "íì°ë
48·ì¦ë·ë°ìë°ì§ë"] }, 49 // ì°ë¦¬ì ì ì â ãì¬ì ë (8.8)ã·ãì¬ ë°ê°ì¬íã ì ì ì ì´ì´ë°ì ì¸ë°ì´ë ì ì© ì¤ë¦¬ì§ë íë¡ê·¸ë¨(ì¤ê³ ì ì, B2G íì íì) 50 { icon: "ð", t: "local.is.challenge.t", d: "local.is.challenge.d", tags: ["ì¤ê³ ì ì · B2G íì íì", "ëì§í¸ ì¬ ì¤í¬í", "L-ìí©í¸ ë±ì§ ì°ê³"] } 51 ], 52 // ìë¨í´ ì¬í¸ë ì¼ â ì°í°ìê³ ìë¡ê¸¸Â·êµë´ ëìí¸ë ì¼(849km·ë¨ê³ì ê°íµ) 벤ì¹ë§í¹. 53 // ì¬ì ëë³´ë¡ ì ì´ì´ì§ë¯ë¡ 'ìì¼ëë í¸í í¸ë ì¼'ë¡ ì¤ê³. 거리ë ì ë¶ ì¤ì íì¸ë ê°ë§ í기(ì ì§ ìì¹). 54 // ê±°ì (ê´ë¬¸) 9ê³³ ì¤ì¬ êµ¬ì± â êµíµÂ·ìë°Â·ê´ê´ì§Â·ë¬¸íì ì° 4ìì를 ê° ê²ì´í¸ì¨ì´ ì¹´ëì í¬í¨(ì ë¨ê´ì£¼ 27ê° ì§ìì²´ ì¤ ì¬ ë³´ì 주ì ê´ë¬¸). 55 islandtrail: [ 56 { icon: "ð", t: "local.it.intro.t", d: "local.it.intro.d", tags: ["ì°í°ìê³ Â·ëìí¸ë ì¼ ë²¤ì¹ë§í¹", "ê±°ì 9곳·13ê°+ ì¬", "ë¨ê³ì íì¥"] }, 57 { icon: "â", t: "local.it.gw.mokpo.t", d: "local.it.gw.mokpo.d", tags: ["무ìê³µí·KTX·ì¬ê°ì í°ë¯¸ë", "ê·¼ëìì¬ê´(문íì ì°)", "ì¸ë¬ë·ë¬ë¦¬ë"] }, 58 { icon: "ð", t: "local.it.gw.sinan.t", d: "local.it.gw.sinan.d", tags: ["1004ëêµÂ·íì§ ì¬ê°ì ", "ííì¼ì (êµê°ë±ë¡ë¬¸íì ì°)", "íì°ë·ì¦ë·ë°ìë°ì§ë"] }, 59 { icon: "ð¶", t: "local.it.gw.wando.t", d: "local.it.gw.wando.d", tags: ["ìë ì¬ê°ì í°ë¯¸ë", "ì¥ë³´ê³ ì²í´ì§ ì ì (문íì ì°)", "ì²ì°ë ì¬ë¡ê¸¸ 42.195km"] }, 60 { icon: "ð¥¾", t: "local.it.gw.yeosu.t", d: "local.it.gw.yeosu.d", tags: ["ì¬ìì¬ê°ì í°ë¯¸ë·KTX", "거문ë ë±ë·ë±ë ¸ë«ê¸¸", "ê¸ì¤ë 18.5km·ííë 5.7km"] }, 61 { icon: "ð", t: "local.it.gw.goheung.t", d: "local.it.gw.goheung.d", tags: ["ëë¡ëí", "ëë¡ì°ì£¼ì¼í°", "ì°íë ëë 길 4km"] }, 62 { icon: "ð", t: "local.it.gw.jindo.t", d: "local.it.gw.jindo.d", tags: ["í½ëª©í", "ì§ëì리ë·ê°ê°ì ë(무íì ì°)", "ê´ë§¤ë"] }, 63 { icon: "ð", t: "local.it.gw.gangjin.t", d: "local.it.gw.gangjin.d", tags: ["ì¡ë¡ ì°ëêµ(ë°° ë¶íì)", "ë¤ì°ì´ë¹(êµê°ì§ì 문íì ì°)", "ê°ì°ë"] }, 64 { icon: "ð¦", t: "local.it.gw.yeonggwang.t", d: "local.it.gw.yeonggwang.d", tags: ["íì§ ì ì°©ì¥", "ë¶ê°ì¬(문íì ì°)", "ì¡ì´ë 몽ëí´ë³ 4km"] }, 65 { icon: "ð¦ª", t: "local.it.gw.boseong.t", d: "local.it.gw.boseong.d", tags: ["ë²êµ ì¸ê·¼ ì ì°©ì¥", "ë»ë°°(êµê°ì´ì ì ì°)", "ì¥ë(ëì¬ë¥´ ìµì§)"] }, 66 { icon: "ð¢", t: "local.it.tier.t", d: "local.it.tier.d", tags: ["â 1ê° ì¬ ì 문í", "â¡ 2ê° ì¬ ëì í", "⢠3ê° ì¬+ ìë¡ìí", "ìì½Â·ê²°ì í¥í ì°ê³"] }, 67 { icon: "ð±", t: "local.it.esg.t", d: "local.it.esg.d", tags: ["ëì§í¸ ì¤í¬í·ì주ì¦", "í´ë¦° í¸ë ì¼(í´ìì°ë 기 ì ê³ )", "ë°ì´í°ì°ê¸ ì°ê³"] }, 68 // ì¶ë° ì ìì ì²´í¬ë¦¬ì¤í¸ â ì¤ì¬ ê³µê³µê¸°ê´ 4ê³³ë§ ì¸ì©(íëì¨ì´Â·ê¸°ê¸° ëì¬ ìì, ì ë³´ ì ê³µë§). ë§í¬ë ê° ê¸°ê´ ëí ííì´ì§. 69 { icon: "ð", t: "local.it.safety.t", d: "local.it.safety.d", tags: ["ë°ë¤ë리(khoa.go.kr) 물ë", "KOMSA(komsa.or.kr) ì´íì ë³´", "기ìì²(weather.go.kr) íëí¹ë³´", "Emergency Ready App(ë¤êµì´)"] }, 70 // 벤ì¹ë§í¹ â ì¤ì¡´ ë¨ì²´(ê°ì ëíê° ì´ëë ë¹ì리+ë²¤ì² ê²°í© ëª¨ë¸). 'í´ë¦° í¸ë ì¼' 기ë¥ì ì¤ì ì ë¡ë¡ ì¸ì©, íì ì ì ì ë¨ê³. 71 { icon: "ð§¹", t: "local.it.bench.t", d: "local.it.bench.d", tags: ["ì¬ë¨ë²ì¸ ì¬ì¦ì (ì1í ì 기방문)", "무ì¸ëì¬í ë§ì°êµ¬ì(í´ìê´ê´ë²¤ì²)", "íì ì ì·ì¤ê³ ì°¸ê³ "] } 72 ], 73 // ëíë¯¼êµ ê²½ê³ì ì¬ â ìí´ê¸°ì ESG ì±ë¦°ì§. ì¬ì¦ì ·무ì¸ëì¬í ë§ì°êµ¬ì 모ë¸ì ê³ì¹í´ 74 // â 방문 ìë¹ì¤ â¡ESG ê°ì¹ì¤í â¢ìë¨í´ ì½ë¦¬ë íì° ë¡ëë§µì¼ë¡ ì¤ê³. 75 // ì ì§ ìì¹: ì¤ì ìì¹(23=ë´ë¥3+ì ì¸7+무ì¸ëì13)ë¡ ì ì , 무ì¸ë ì¸ë¶ëª©ë¡ì í¥í ê²ì¦ ë¨ê³ë¡ ëª ì. 76 borderislands: [ 77 { icon: "ð§", t: "local.bi.intro.t", d: "local.bi.intro.d", tags: ["23ê° ìí´ê¸°ì (ì êµ)", "ì ë¨ê¶ ì ì¸ ê¸°ì ì¬ 5ê³³", "ì¬ì¦ì ëª¨ë¸ ê³ì¹"] }, 78 { icon: "ð", t: "local.bi.gageo.t", d: "local.bi.gageo.d", tags: ["ì ì", "ìµìë¨ë¨", "ìí´ê¸°ì "] }, 79 { icon: "ð´", t: "local.bi.hongdo.t", d: "local.bi.hongdo.d", tags: ["ì ì", "ë¤ëí´í´ìêµë¦½ê³µì", "ìí´ê¸°ì "] }, 80 { icon: "ð", t: "local.bi.yeoseo.t", d: "local.bi.yeoseo.d", tags: ["ìë", "2025 ì¬í´ì ì¬", "ìí´ê¸°ì "] }, 81 { icon: "ð¢", t: "local.bi.geomun.t", d: "local.bi.geomun.d", tags: ["ì¬ì", "2026 ì¬í´ì ì¬", "ìí´ê¸°ì "] }, 82 { icon: "âµ", t: "local.bi.hoengdo.t", d: "local.bi.hoengdo.d", tags: ["ìê´", "ìí´ê¸°ì "] }, 83 { icon: "ð±", t: "local.bi.esg.t", d: "local.bi.esg.d", tags: ["í´ìì°ë 기 ì í ì¸ì¦", "êµí ê²½ê³ ì§í´ì´ ë°°ì§", "ë°ì´í°ì°ê¸Â·ESG 리í¬í¸ ì°ê³"] }, 84 { icon: "ðº", t: "local.bi.roadmap.t", d: "local.bi.roadmap.d", tags: ["1ë¨ê³: ì ì¸ê¸°ì 5ê³³", "2ë¨ê³: 무ì¸ëì ê²ì¦Â·ì¶ê°", "3ë¨ê³: ìë¨í´ ì½ë¦¬ë ì ì²´"] } 85 ], 86 // ìë¨í´ ì°Â·ì¬ 10ê° ì½ì¤ ìì ê²ì¦í â ìë군·ì¬ìì ê³µì ê´ê´ì ë³´ + KOMSA + 기ìì² êµì°¨ê²ì¦(기ì¤ìì 202
866-08-22). 87 // ìì¹: ê³µììë£ë¡ íì¸ ì ë íì¥ì¤Â·ëí¼ë¡Â·119ì ê·¼ì±ì ììë¡ ì±ì°ì§ ìê³ "ë¶íì¤"ë¡ íì(ì ì§ ìì¹). 88 coursesafety: [ 89 { icon: "ð", t: "local.cs.intro.t", d: "local.cs.intro.d", tags: ["ëí¼Â·íì¶ë¡: 10ê° ì½ì¤ ì ë¶ ê³µì 미íì¸", "119 ì ê·¼ì±: ì ë¶ ë¶íì¤Â·íì¥íì¸ íì", "ì ë°: KOMSAâì ì¬â기ìì²âíì§ìë´ ì íì¸", "êµë¦½ê³µì(íìì°Â·ìì¶ì°)ì ë³ë 기ì¤, ì´ í ì ì¸"] }, 90 { icon: "ð", t: "local.cs.table.t", d: "local.cs.table.d", wide: true, tags: [] }, 91 { icon: "â ", t: "local.cs.c1.t", d: "local.cs.c1.d", tags: ["ìë ì²ì°ë", "ê³µì 5.71km·90ë¶", "ìì íì : ì°ì ê²í "] }, 92 { icon: "â¡", t: "local.cs.c2.t", d: "local.cs.c2.d", tags: ["ìë ì²ì°ë", "ë²ë°ì길~ì©ê¸¸", "ìì íì : ì¶ê° íì¥ê²ì¦ íì"] }, 93 { icon: "â¢", t: "local.cs.c3.t", d: "local.cs.c3.d", tags: ["ì¬ì ê¸ì¤ë", "5km·2h·í(ì 문ì ì í©)", "ìì íì : ë¨, ì ë²½ 주ì"] }, 94 { icon: "â£", t: "local.cs.c4.t", d: "local.cs.c4.d", tags: ["ì¬ì ê¸ì¤ë", "3.5km·1h30m·í", "ìì íì : ì´ë³´ì íë³´"] }, 95 { icon: "â¤", t: "local.cs.c5.t", d: "local.cs.c5.d", tags: ["ì¬ì ê¸ì¤ë", "3.5km·2h·ì", "ìì íì : ì´ë³´ì ìí ì ì¸ ê¶ì¥"] }, 96 { icon: "â¥", t: "local.cs.c6.t", d: "local.cs.c6.d", tags: ["ì¬ì ííë", "5.7~6km·3h·í", "ìì íì : ì¡°ê±´ë¶ ì í©Â·1ìì ì¶ì²"] }, 97 { icon: "â¦", t: "local.cs.c7.t", d: "local.cs.c7.d", tags: ["ì¬ì ê°ë", "4.5km·1h30m·í", "ìì íì : ì¡°ê±´ë¶"] }, 98 { icon: "â§", t: "local.cs.c8.t", d: "local.cs.c8.d", tags: ["ì¬ì ê°ë", "3.4km·2h30m·ì", "ìì íì : ì´ë³´ì ì ì¸ ê¶ì¥"] }, 99 { icon: "â¨", t: "local.cs.c9.t", d: "local.cs.c9.d", tags: ["ì¬ì ëë", "40ë¶Â·í", "ìì íì : ì¡°ê±´ë¶Â·3ìì ì¶ì²"] }, 100 { icon: "â©", t: "local.cs.c10.t", d: "local.cs.c10.d", tags: ["ì¬ì 거문ë", "1.2km·1h", "ìì íì : ì¡°ê±´ë¶"] }, 101 { icon: "ð¥", t: "local.cs.rank.t", d: "local.cs.rank.d", tags: ["1ìì: ííë ê½ì¬ê¸¸", "2ìì: ê¸ì¤ë 1ì½ì¤", "3ìì: ëë 1ì½ì¤", "4ìì: ì²ì°ë 1ì½ì¤"] }, 102 { icon: "ð¿", t: "local.cs.esg1.t", d: "local.cs.esg1.d", tags: ["ì²ì°ë ì¬ë¡ê¸¸", "ë린ì¬í·주민공존", "ìë"] }, 103 { icon: "ð", t: "local.cs.esg2.t", d: "local.cs.esg2.d", tags: ["ê¸ì¤ë ë¹ë 길", "í´ììí·ë¡ì»¬í¸ë", "ì¬ì"] }, 104 { icon: "ð¼", t: "local.cs.esg3.t", d: "local.cs.esg3.d", tags: ["ííë ê½ì¬ê¸¸", "ì¼ìí·ì§ìì문í", "ì¬ì"] }, 105 { icon: "ð", t: "local.cs.esg4.t", d: "local.cs.esg4.d", tags: ["ííë ì¬íë§ë°¥ì", "ìí+ì§ìê²½ì ì°ê²°", "ì¬ì"] }, 106 { icon: "ð¥", t: "local.cs.esg5.t", d: "local.cs.esg5.d", tags: ["ê¸ì¤ë ë°©íë물", "ì§ìí¹ì° ì문íì²´í", "ì¬ì"] }, 107 { icon: "ð", t: "local.cs.score.t", d: "local.cs.score.d", tags: ["ìì·공ì ì¸ì¦ ìë", "1ì ì²ì°ë(89)", "2ì ííë+ì¼ìí(86)", "3ì ê¸ì¤ë+ë°©íë물(85)"] } 108 ], 109 // K-ì½í ì¸ ì´¬ìì§ â ì ë¨ê´ì£¼íµí©í¹ë³ì, 1990ë ~íì¬. ì¹ê²ì êµì°¨ê²ì¦(2026-08-28 기ì¤) + ì ë¨ììììí ãì ë¨íë¦ã ê³µì DB ì¡´ì¬ íì¸. 110 // ì ì§ ìì¹: ê²ìì¼ë¡ íì¸ ì ë ì§ìì²´ë ìµì§ë¡ ì°ê²°íì§ ìê³ ë³ë í목ì¼ë¡ ëª ì. í¥í ìë¨í´ì½ë¦¬ë íì¥ ì ì§ì ì½ëë§ ì¶ê°íë©´ ë¨(ì¤ê³ë docs ì°¸ê³ ). 111 filmlocation: [ 112 { icon: "ð¬", t: "local.fl.intro.t", d: "local.fl.intro.d", tags: ["1990ë ~íì¬", "ì¹ê²ì êµì°¨ê²ì¦(2026-08-28)", "ì ë¨ììììí ãì ë¨íë¦ã ê³µìDB ì¡´ì¬"] }, 113 { icon: "âï¸", t: "local.fl.wando1.t", d: "local.fl.wando1.d", tags: ["ìë ì²í´í¬êµ¬ ì´¬ìì¥", "í´ì (2004)·ì¶ë ¸(2010)Â·ëª ë(2014)·í´ì (2014)", "ì¬ê·¹Â·í´ì물 50ì¬ í¸"] }, 114 { icon: "ð", t: "local.fl.wando2.t", d: "local.fl.wando2.d", tags: ["ìë ì²ì°ë", "ìí¸ì (1993, ìµì´ 100ë§ ê´ê° ìí)", "ë´ì ìì¸ (KBS, 2006)"] }, 115 { icon: "ð", t: "local.fl.mokpo.t", d: "local.fl.mokpo.d", tags: ["ëª©í¬ ê·¼ëìì¬ê´", "í¸í ë¸ë£¨ë(tvN, 2019)", "1987(2017)·모ëìê³(1995)·ì¼ì¸ìë(2002)"] }, 116 { icon: "ðµ", t: "local.fl.boseong.t", d: "local.fl.boseong.d", tags: ["ë³´ì± ëíë¤ì ë ¹ì°¨ë°", "ì¬ë¦í¥ê¸°(2003)·í¸ë¥¸ ë°ë¤ì ì ì¤(2016)·ìì (2017)"] }, 117 { icon: "ð²", t: "local.fl.damyang.t", d: "local.fl.damyang.d", tags: ["ë´ì ì£½ë ¹ì·ë©íì¸ì¿¼ì´ì길", "미ì¤í° ì ì¤ì¸(2018)·군주(2017)", "ëª ë¹Â·ì°½ê¶Â·ì린(ìí)·ì¼ì§ë§¤(2008)"] },
118 { icon: "ð", t: "local.fl.suncheon.t", d: "local.fl.suncheon.d", tags: ["ìì² ëë¼ë§ ì´¬ìì¥", "ì¬ëê³¼ ì¼ë§(2006)·ìë´ì ë쪽(2008)", "ìì´ì¸í¸(2010)·ê°ìí(2012)·íì¹ ìììë¤(2025)"] }, 119 { icon: "ð", t: "local.fl.yeosu.t", d: "local.fl.yeosu.d", tags: ["ì¬ì ì¤ëë·ì´ìì ê´ì¥", "ë물(2010)·í¼ì ë¨ë (2016)·í¸êµ¬ì ì¬ë(2015)"] }, 120 { icon: "ð", t: "local.fl.sinan.t", d: "local.fl.sinan.d", tags: ["ì ì ë³íë 'ì¬ëì í´ë³'", "ë´ì ìì¸ (2006)"] }, 121 { icon: "ð¾", t: "local.fl.haenam.t", d: "local.fl.haenam.d", tags: ["í´ë¨ ë¶í면·ëí¥ì¬ ì ì ê´", "í¸í(2026)·ì¥êµ°ì ìë¤(1990)·ë¼ì¼ìë ë¨(2021)"] }, 122 { icon: "ð", t: "local.fl.gwangju.t", d: "local.fl.gwangju.d", tags: ["ê´ì£¼ êµë¦½ììì문íì ë¹Â·ì¡°ì ë·ê´ì£¼ê·¹ì¥", "íë¬(2024)·ìì¸ì ë´(2023)Â·ì¢ ì´ì ì§(2022)·ìì´(2015)"] }, 123 { icon: "â", t: "local.fl.unconfirmed.t", d: "local.fl.unconfirmed.d", tags: ["ê°ì§Â·ìì·ì¥í¥Â·ê³ í¥Â·í¨í·ìê´Â·ë¬´ì·ì§ë", "ì´¬ì ì´ë ¥ ê²ì 미íì¸", "í¥í ãì ë¨íë¦ã ì°ë ì ê°±ì "] } 124 ], 125 // ì°ë¦¬ì ì리(Our Sound) â ìí ì ì¥ë©´ì´ ìëë¼ ê·¸ ì¥ë©´ì ë§ë ì¤ì ì리·ì¬ë·ì¥ì를 ìëë¤. 126 // ì¹ê²ì êµì°¨ê²ì¦(2026-08-30 기ì¤): ê°ì°ì (íêµë¯¼ì¡±ë¬¸íë백과ì¬ì ·ìí¤ë°±ê³¼), ì§ëì리ë(기존 filmlocation·islandtrail í목과 ëì¼ ì¶ì² ì¬íì¸). 127 // ì ì§ ìì¹: íì¸ ëª»í ë±ë ¸ë·민ìë ì§ì´ë´ì§ ìê³ 'ì°ê²° ë기'ë¡ ëª ì. 128 oursound: [ 129 { icon: "ð¶", t: "local.os.intro.t", d: "local.os.intro.d", tags: ["ìí·ëë¼ë§ë ì 구", "ì¤ì ì리·ì¬ë·ì¥ìê° ë³¸ì²´", "ì¹ê²ì êµì°¨ê²ì¦(2026-08-30)"] }, 130 { icon: "ð¼", t: "local.os.gangsanje.t", d: "local.os.gangsanje.d", tags: ["ë³´ì± ì ì¹ë©´ ê°ì°ë¦¬", "íì리 ì í ê°ì°ì ", "ë°ì ì "] }, 131 { icon: "ð", t: "local.os.jindoarirang.t", d: "local.os.jindoarirang.d", tags: ["ìë ì²ì°ë ì´¬ì ì¥ë©´", "ì§ë ëí 민ì", "무íì ì°"] }, 132 { icon: "ð", t: "local.os.pending.t", d: "local.os.pending.d", tags: ["ë±ë ¸ë·ì´ì ìë ì§ìë³ íì¥ ì±ë¡ ì ", "ì§ì´ë´ì§ ìê³ ë기 íì", "íì¸ëë ëë¡ ê°±ì "] } 133 ] 134}; 135const CURATED_SRC = { koryoin: "local.src.koryoin", islands2026: "local.src.islands2026", islandtrail: "local.src.islandtrail", borderislands: "local.src.borderislands", coursesafety: "local.src.coursesafety", filmlocation: "local.src.filmlocation", oursound: "local.src.oursound" }; 136 137let localCat = "transport"; 138let localRegion = ""; 139 140function t(key, fallback){ 141 const all = window.I18N || {}; 142 const dict = all[window.__lang] || {}; 143 // íì¬ ì¸ì´ â ìì¼ë©´ íêµì´ â ìì¼ë©´ fallback â í¤ 144 return dict[key] || (all.ko && all.ko[key]) || fallback || key; 145} 146 147function selectLocalCat(el, cat){ 148 document.querySelectorAll('.local-cat').forEach(c => c.classList.remove('active')); 149 el.classList.add('active'); 150 localCat = cat; 151 const regionBar = document.getElementById('localRegionBar'); 152 // êµíµÂ·ìì ì ì ë¨ ì ì ê³µíµ â ì§ì íí° ì¨ê¹ 153 regionBar.style.display = (cat === 'transport' || cat === 'safety') ? 'none' : 'flex'; 154 renderLocal(); 155} 156 157function selectLocalRegion(el, region){ 158 document.querySelectorAll('.local-region').forEach(r => r.classList.remove('active')); 159 el.classList.add('active'); 160 localRegion = region; 161 renderLocal(); 162} 163 164function renderCurated(list, srcKey){ 165 const grid = document.getElementById('localGrid'); 166 grid.innerHTML = list.map(item => ` 167 <div class="loc-info-card${item.wide ? ' wide' : ''}"> 168 <div class="loc-info-icon">${item.icon}</div> 169 <div class="loc-info-title" data-i18n="${item.t}">${t(item.t)}</div> 170 <div class="loc-info-desc" data-i18n="${item.d}">${t(item.d)}</div> 171 <div class="loc-info-tags">${(item.tags||[]).map(tag => `<span class="loc-info-tag">${tag}</span>`).join('')}</div> 172 </div>`).join(''); 173 document.getElementById('localSource').innerHTML = 174 '<span class="mock">â ' + t(srcKey || 'local.src.curated', 'ìë¨í´ ê·¸ëë íë ì´ì ì ë³´') + '</span>'; 175} 176 177// XSS ë°©ì§: ì¸ë¶(TourAPI) ë°ì´í°ë¥¼ innerHTMLì ë£ê¸° ì HTML ì´ì¤ì¼ì´í 178// esc()ë utils.jsìì ì ì ì ê³µ (index.html ìµìë¨ ë¡ë) 179// ì´ë¯¸ì§ URL íì´í¸ë¦¬ì¤í¸: http(s) + ë°ì´í/ê´í¸/공백/꺾ì ìë ê²½ì°ë§ íì© (CSS url() íì¶ ì°¨ë¨) 180function safeImg(u){ 181 u = String(u || ''); 182 return /^https?:\/\/[^\s'"()<>
182]+$/i.test(u) ? u : ''; 183} 184 185// ë루ëë¹ í¸ë í¹ â ìë¨í´ ìêµ° íí°ì© (sigun/ì½ì¤ëª ì í¬í¨ëë íê¸ëª ) 186const TRAIL_REGION_KO = { gwangju:"ê´ì£¼", mokpo:"목í¬", muan:"무ì", sinan:"ì ì", jindo:"ì§ë", wando:"ìë", boseong:"ë³´ì±", haenam:"í´ë¨", 187 yeosu:"ì¬ì", suncheon:"ìì²", goheung:"ê³ í¥", jangheung:"ì¥í¥", gangjin:"ê°ì§", yeongam:"ìì", hampyeong:"í¨í", yeonggwang:"ìê´" }; 188const SEONAMHAE_KO = ["ì ì","ìë","ì§ë","í´ë¨","목í¬","무ì","ë³´ì±"]; 189 190function trailCard(item){ 191 const isBike = item.brdDiv === "DNBW"; 192 const typeLabel = isBike ? t("local.trail.bike", "ìì 거길") : t("local.trail.walk", "걷기길"); 193 const mins = parseInt(item.crsTotlRqrmHour, 10) || 0; 194 const h = Math.floor(mins / 60), m = mins % 60; 195 const timeStr = h ? `${h}h${m ? m + "m" : ""}` : (m ? `${m}m` : "-"); 196 const meta = `ð ${esc(item.crsDstnc)}km · â± ${timeStr}${item.crsLevel ? ` · â° Lv.${esc(item.crsLevel)}` : ""}`; 197 return `<div class="loc-card"> 198 <div class="loc-card-body"> 199 <span class="loc-card-region">${isBike ? "ð²" : "ð¥¾"} ${esc(item.sigun)}</span> 200 <div class="loc-card-title">${esc(item.crsKorNm)}</div> 201 <div class="loc-card-meta" style="margin-top:6px;"> 202 <div><span>${meta}</span></div> 203 <div style="margin-top:6px;"><span class="loc-info-tag">${esc(typeLabel)}</span></div> 204 </div> 205 </div> 206 </div>`; 207} 208 209// 0ê±´ì¼ ë íìí ìë´ â í¹ì ì§ìì ê³¨ë¼ ì¡°ííë¤ë©´ ì¸ê·¼ ì§ìë íì¸í´ë³´ë¼ë íí¸ë¥¼ ë§ë¶ì(ì ì²´ ì¡°í ìì ìëµ) 210function emptyStateHTML(){ 211 const base = `<div class="local-empty">${t('local.none', 'ì´ ì§ì·ë¶ë¥ì ë±ë¡ë ê´ê´ê³µì¬ ë°ì´í°ê° ììµëë¤.')}</div>`; 212 const hint = localRegion 213 ? `<div class="local-empty-hint">${t('local.none.hint', 'ð¡ ì¸ê·¼ ì§ìë í¨ê» íì¸í´ë³´ì¸ì â ì ì§ì íìì ë¤ë¥¸ ì·군ì ì ííê±°ë ì ì²´ë³´ê¸°ë¡ ëìê°ë©´ ë©ëë¤.')}</div>` 214 : ''; 215 return base + hint; 216} 217 218async function renderTrail(){ 219 const grid = document.getElementById("localGrid"); 220 grid.innerHTML = `<div class="local-empty">${t("local.loading", "ë¶ë¬ì¤ë ì¤â¦")}</div>`; 221 try { 222 const res = await fetch("/api/trail?op=courseList&numOfRows=300", { signal: AbortSignal.timeout(8000) }); 223 if(!res.ok) throw new Error("proxy " + res.status); 224 const data = await res.json(); 225 if(data.error) throw new Error(data.error); 226 let raw = data?.response?.body?.items?.item; 227 raw = Array.isArray(raw) ? raw : (raw ? [raw] : []); 228 const names = localRegion ? [TRAIL_REGION_KO[localRegion]].filter(Boolean) : SEONAMHAE_KO; 229 const items = raw.filter(i => names.some(n => (String(i.sigun || "") + String(i.crsKorNm || "")).includes(n))); 230 document.getElementById("localSource").innerHTML = 231 '<span class="live">â ' + t("local.src.trail", "ì¤ìê° â íêµê´ê´ê³µì¬ ë루ëë¹ (ì¬Â·ì¤ì§ ëë³´/ìì 거길)") + "</span>"; 232 grid.innerHTML = items.length 233 ? items.map(trailCard).join("") 234 : emptyStateHTML(); 235 } catch (e) { 236 document.getElementById("localSource").innerHTML = 237 '<span class="mock">â ' + t("local.trail.err", "ë루ëë¹ ì°ê²° ë기 â API í¤/íë¡ì íì¸ íì") + "</span>"; 238 grid.innerHTML = emptyStateHTML(); 239 } 240} 241 242// ì¸ê¸° ê´ê´ì§(ì¤ì¬ê´ê´ì§) â ìêµ°ë³ ìê° ëí¹ (LocgoHubTarService1) 243function hubCard(item){ 244 const cat = [item.hubCtgryLclsNm, item.hubCtgryMclsNm].filter(Boolean).map(esc).join(' · '); 245 return `<div class="loc-card"> 246 <div class="loc-card-body"> 247 <span class="loc-card-region">â TOP ${esc(item.hubRank)} · ${esc(item.signguNm)}</span> 248 <div class="loc-card-title">${esc(item.hubTatsNm)}</div> 249 <div class="loc-card-meta">${cat ? `<div><span class="loc-info-tag">${cat}</span></div>` : ''}</div> 250 </div> 251 </div>`; 252} 253async function renderHubspot(){ 254 const grid = document.getElementById('localGrid'); 255 const signgu = HUB_SIGNGU[localRegion]; 256 if(!signgu){ 257 grid.innerHTML = `<div class="local-empty">${t('local.hub.pick','ì¸ê¸° ê´ê´ì§ë ìêµ°(목í¬Â·ì ì·ìë ë±)ì ì ííë©´ ëí¹ì¼ë¡ íìë©ëë¤.')}</div>`; 258 document.getElementById('localSource').innerHTML = 259 '<span class="mock">â ' + t('local.src.curated','ìë¨í´ ê·¸ëë íë ì´ì ì ë³´') + '</span>'; 260 return; 261 } 262 grid.innerHTML = `<div class="local-empty">${t('local.loading','ë¶ë¬ì¤ë ì¤â¦')}</div>`; 263 try{ 264 let items = []; 265 for(const ym of ymCandidates(8)){ 266 const res = await fetch(`/api/datalab?svc=LocgoHubTarService1&op=areaBasedList1&areaCd=46&signguCd=${signgu}&baseYm=${ym}&numOfRows=20`, { signal: AbortSignal.timeout(8000) }); 267 const d = await res.json(); 268 if(d.error) continue; 269 let raw = d?.response?.body?.items?.item; 270 raw = Array.isArray(raw) ? raw : (raw ? [raw] : []); 271 if(raw.length){ items = raw; break; } 272 } 273 items.sort((a,b)=> Number(a.hubRank) - Number(b.hubRank)); 274 document.getElementById('localSource').innerHTML = 275 '<span class="live">â ' + t('local.src.hub','ì¤ìê° â íêµê´ê´ê³µì¬ 기ì´ì§ìì²´ ì¤ì¬ ê´ê´ì§(ë°ì´í°ë©)') + '</span>'; 276 grid.innerHTML = items.length 277 ? items.map(hubCard).join('') 278 : emptyStateHTML(); 279 }catch(e){ 280 document.getElementById('localSource').innerHTML = 281 '<span class="mock">â ' + t('local.trail.err','ë°ì´í°ë© ì°ê²° ë기 â API í¤/íë¡ì íì¸ íì') + '</span>'; 282 grid.innerHTML = emptyStateHTML(); 283 } 284} 285 286function cardHTML(item){ 287 const regionName = item.region ? esc(t('city.' + item.region, item.region)) : ''; 288 const imgUrl = safeImg(item.img); 289 const img = imgUrl 290 ? `<div class="loc-card-img" style="background-image:url('${imgUrl}')"></div>` 291 : `<div class="loc-card-img loc-card-noimg">${esc(item.icon) || 'ð'}</div>`; 292 return `<div class="loc-card"> 293 ${img} 294 <div class="loc-card-body"> 295 ${regionName ? `<span class="loc-card-region">â ${regionName}</span>` : ''} 296 <div class="loc-card-title">${esc(item.title)}</div> 297 <div class="loc-card-meta"> 298 ${item.addr ? `<div>ð <span>${esc(item.addr)}</span></div>` : ''} 299 ${item.tel ? `<div>â <span>${esc(item.tel)}</span></div>` : ''} 300 </div> 301 </div> 302 </div>`; 303} 304 305async function renderLocal(){ 306 // ë¿ë¦¬ì°¾ê¸° ì§ì ê²½ë¡(F-07 H3)ë ê³ ë ¤ì¸ ì¹´í ê³ ë¦¬ììë§ ë ¸ì¶ â ë¤ë¥¸ ì¹´í ê³ ë¦¬ ì í ì íì ë¹ì 307 var hcBox = document.getElementById('koryoinSupport'); 308 if(localCat === 'koryoin' && window.SGHomecoming) window.SGHomecoming.render(); 309 else if(hcBox) hcBox.innerHTML = ''; 310 // êµíµÂ·ìì â íë ì´ì (t()ê° íì¬ ì¸ì´ë¡ ë ëíë¯ë¡ applyLang ì¬í¸ì¶ ë¶íì = ì
310¬ê· ë°©ì§) 311 if(CURATED[localCat]){ 312 renderCurated(CURATED[localCat], CURATED_SRC[localCat]); 313 return; 314 } 315 // í¸ë í¹ â ë루ëë¹ API 316 if(localCat === 'trail'){ 317 await renderTrail(); 318 return; 319 } 320 // ì¸ê¸° ê´ê´ì§(ì¤ì¬ê´ê´ì§) â ë°ì´í°ë© LocgoHubTarService1 (ìêµ°ë³ ëí¹) 321 if(localCat === 'hubspot'){ 322 await renderHubspot(); 323 return; 324 } 325 // ìë°Â·ìì·문íì ì°Â·ì²´í·ë°ë ¤ë물 â TourAPI ìë, ì¤í¨ ì 목ì 326 const grid = document.getElementById('localGrid'); 327 const typeId = TOURAPI_TYPE[localCat]; 328 grid.innerHTML = `<div class="local-empty">${t('local.loading', 'ë¶ë¬ì¤ë ì¤â¦')}</div>`; 329 try { 330 const params = new URLSearchParams({ area: '38' }); 331 if(typeId) params.set('type', typeId); 332 if(TOURAPI_SVC[localCat]) params.set('service', TOURAPI_SVC[localCat]); 333 if(localRegion) params.set('region', localRegion); 334 const res = await fetch('/api/tour?' + params.toString(), { signal: AbortSignal.timeout(6000) }); 335 if(!res.ok) throw new Error('proxy ' + res.status); 336 const data = await res.json(); 337 if(data.error) throw new Error(data.error); 338 const raw = data?.response?.body?.items?.item; 339 const items = (Array.isArray(raw) ? raw : (raw ? [raw] : [])).map(it => ({ 340 title: it.title, 341 addr: it.addr1 || '', 342 tel: it.tel || '', 343 img: it.firstimage || it.firstimage2 || '', 344 icon: 'ð' 345 })); 346 // API ì ì ìëµ: ë°ì´í°ê° ìì¼ë©´ ì¤ë°ì´í°, 0ê±´ì´ë©´ ì ì§íê² ë¹ ìí(목ì ìë) 347 document.getElementById('localSource').innerHTML = 348 '<span class="live">â ' + t('local.src.live', 'ì¤ìê° â íêµê´ê´ê³µì¬ TourAPI') + '</span>'; 349 grid.innerHTML = items.length 350 ? items.map(cardHTML).join('') 351 : emptyStateHTML(); 352 } catch (e) { 353 // íë¡ì/ë¤í¸ìí¬/í¤ ì¤ë¥ â mock ë°ì´í° ê¸ì§ ìì¹: 목ì ëì ì ì§í "ì°ê²° ë기" ìë´(renderTrail/renderHubspotê³¼ ëì¼ í¨í´) 354 document.getElementById('localSource').innerHTML = 355 '<span class="mock">â ' + t('local.tour.err', 'íêµê´ê´ê³µì¬ TourAPI ì°ê²° ë기 â API í¤/íë¡ì íì¸ íì') + '</span>'; 356 grid.innerHTML = emptyStateHTML(); 357 } 358} 359 360// ì¸ì´ ë³ê²½ ì ëì ì¹´ëë ë¤ì ë ë (data-i18n 미ì ì© í목 ë°ì) 361window.onLangChange = function(){
362 if(document.getElementById('s-local') && document.getElementById('s-local').classList.contains('active')){ 363 renderLocal(); 364 } 365}; 366 367document.addEventListener('DOMContentLoaded', () => { 368 if(document.getElementById('localGrid')) renderLocal(); 369});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.