Junhyuyk Seo

Integrate server with apihandler

Showing 50 changed files with 741 additions and 100 deletions
1 +node_modules
2 +npm-debug.log
...\ No newline at end of file ...\ No newline at end of file
1 +FROM node:14
2 +
3 +LABEL title="TFT-APIHandler"
4 +LABEL version="1.00"
5 +
6 +# set working directory
7 +WORKDIR /app
8 +
9 +# install modules and dependencies
10 +COPY package*.json ./
11 +RUN npm install
12 +
13 +# copy source codes
14 +COPY ./ ./
15 +
16 +#start application
17 +CMD [ "node", "server.js" ]
...\ No newline at end of file ...\ No newline at end of file
1 +exports.COORDINATES = {
2 + "서울특별시" : {
3 + "종로구" : [60, 127],
4 + "중구" : [60, 127],
5 + "용산구" : [60, 126],
6 + "성동구" : [61, 127],
7 + "광진구" : [62, 126],
8 + "동대문구" : [61, 127],
9 + "중랑구" : [62, 128],
10 + "성북구" : [61, 127],
11 + "강북구" : [61, 128],
12 + "도봉구" : [61, 129],
13 + "노원구" : [61, 129],
14 + "은평구" : [59, 127],
15 + "서대문구" : [59, 127],
16 + "마포구" : [59, 127],
17 + "양천구" : [58, 126],
18 + "강서구" : [58, 126],
19 + "구로구" : [58, 125],
20 + "금천구" : [59, 124],
21 + "영등포구" : [58, 126],
22 + "동작구" : [59, 125],
23 + "관악구" : [59, 125],
24 + "서초구" : [61, 125],
25 + "강남구" : [61, 126],
26 + "송파구" : [62, 126],
27 + "강동구" : [62, 126]
28 + },
29 + "부산광역시" : {
30 + "중구" : [97, 74],
31 + "서구" : [97, 74],
32 + "동구" : [98, 75],
33 + "영도구" : [98, 74],
34 + "부산진구" : [97, 75],
35 + "동래구" : [98, 76],
36 + "남구" : [98, 75],
37 + "북구" : [96, 76],
38 + "해운대구" : [99, 75],
39 + "사하구" : [96, 74],
40 + "금정구" : [98, 77],
41 + "강서구" : [96, 76],
42 + "연제구" : [98, 76],
43 + "수영구" : [99, 75],
44 + "사상구" : [96, 75],
45 + "기장군" : [100, 77]
46 + },
47 + "대구광역시" : {
48 + "중구" : [89, 90],
49 + "동구" : [90, 91],
50 + "서구" : [88, 90],
51 + "남구" : [89, 90],
52 + "북구" : [89, 91],
53 + "수성구" : [89, 90],
54 + "달서구" : [88, 90],
55 + "달성군" : [86, 88]
56 + },
57 + "인천광역시" : {
58 + "중구" : [54, 125],
59 + "동구" : [54, 125],
60 + "미추홀구" : [54, 124],
61 + "연수구" : [55, 123],
62 + "남동구" : [56, 124],
63 + "부평구" : [55, 125],
64 + "계양구" : [56, 126],
65 + "서구" : [55, 126],
66 + "강화군" : [51, 130],
67 + "옹진군" : [54, 124]
68 + },
69 + "광주광역시" : {
70 + "동구" : [60, 74],
71 + "서구" : [59, 74],
72 + "남구" : [59, 73],
73 + "북구" : [59, 75],
74 + "광산구" : [57, 74]
75 + },
76 + "대전광역시" : {
77 + "동구" : [68, 100],
78 + "중구" : [68, 100],
79 + "서구" : [67, 100],
80 + "유성구" : [67, 101],
81 + "대덕구" : [68, 100]
82 + },
83 + "울산광역시" : {
84 + "중구" : [102, 84],
85 + "남구" : [102, 84],
86 + "동구" : [104, 83],
87 + "북구" : [103, 85],
88 + "울주군" : [101, 84]
89 + },
90 + "세종특별자치시" : {
91 + "세종특별자치시": [66, 103]
92 + },
93 + "경기도" : {
94 + "수원시" : [61, 120],
95 + "성남시" : [63, 124],
96 + "의정부시" : [61, 130],
97 + "안양시" : [59, 123],
98 + "부천시" : [56, 125],
99 + "광명시" : [58, 125],
100 + "평택시" : [62, 114],
101 + "동두천시" : [61, 134],
102 + "안산시" : [58, 121],
103 + "고양시" : [57, 128],
104 + "과천시" : [60, 124],
105 + "구리시" : [62, 127],
106 + "남양주시" : [64, 128],
107 + "오산시" : [62, 118],
108 + "시흥시" : [57, 123],
109 + "군포시" : [59, 122],
110 + "의왕시" : [60, 122],
111 + "하남시" : [64, 126],
112 + "용인시" : [64, 119],
113 + "파주시" : [56, 131],
114 + "이천시" : [68, 121],
115 + "안성시" : [65, 115],
116 + "김포시" : [55, 128],
117 + "화성시" : [57, 119],
118 + "광주시" : [65, 123],
119 + "양주시" : [61, 131],
120 + "포천시" : [64, 134],
121 + "여주시" : [71, 121],
122 + "연천군" : [61, 138],
123 + "가평군" : [69, 133],
124 + "양평군" : [69, 125]
125 + },
126 + "강원도" : {
127 + "춘천시" : [73, 134],
128 + "원주시" : [76, 122],
129 + "강릉시" : [92, 131],
130 + "동해시" : [97, 127],
131 + "태백시" : [95, 119],
132 + "속초시" : [87, 141],
133 + "삼척시" : [98, 125],
134 + "홍천군" : [75, 130],
135 + "횡성군" : [77, 125],
136 + "영월군" : [86, 119],
137 + "평창군" : [84, 123],
138 + "정선군" : [89, 123],
139 + "철원군" : [65, 139],
140 + "화천군" : [72, 139],
141 + "양구군" : [77, 139],
142 + "인제군" : [80, 138],
143 + "고성군" : [85, 145],
144 + "양양군" : [88, 138]
145 + },
146 + "충청북도" : {
147 + "청주시" : [69, 106],
148 + "충주시" : [76, 114],
149 + "제천시" : [81, 118],
150 + "보은군" : [73, 103],
151 + "옥천군" : [71, 99],
152 + "영동군" : [74, 97],
153 + "증평군" : [71, 110],
154 + "진천군" : [68, 111],
155 + "괴산군" : [74, 111],
156 + "음성군" : [72, 113],
157 + "단양군" : [84, 115]
158 + },
159 + "충청남도" : {
160 + "천안시" : [63, 110],
161 + "공주시" : [63, 102],
162 + "보령시" : [54, 100],
163 + "아산시" : [60, 110],
164 + "서산시" : [51, 110],
165 + "논산시" : [62, 97],
166 + "계룡시" : [65, 99],
167 + "당진시" : [54, 112],
168 + "금산군" : [69, 95],
169 + "부여군" : [59, 99],
170 + "서천군" : [55, 94],
171 + "청양군" : [57, 103],
172 + "홍성군" : [55, 106],
173 + "예산군" : [58, 107],
174 + "태안군" : [48, 109]
175 + },
176 + "전라북도" : {
177 + "전주시" : [63, 89],
178 + "군산시" : [56, 92],
179 + "익산시" : [60, 91],
180 + "정읍시" : [58, 83],
181 + "남원시" : [68, 80],
182 + "김제시" : [59, 88],
183 + "완주군" : [63, 89],
184 + "진안군" : [68, 88],
185 + "무주군" : [72, 93],
186 + "장수군" : [70, 85],
187 + "임실군" : [66, 84],
188 + "순창군" : [63, 79],
189 + "고창군" : [56, 80],
190 + "부안군" : [56, 87]
191 + },
192 + "전라남도" : {
193 + "목포시" : [50, 67],
194 + "여수시" : [73, 66],
195 + "순천시" : [70, 70],
196 + "나주시" : [56, 71],
197 + "광양시" : [73, 70],
198 + "담양군" : [61, 78],
199 + "곡성군" : [66, 77],
200 + "구례군" : [69, 75],
201 + "고흥군" : [66, 62],
202 + "보성군" : [62, 66],
203 + "화순군" : [61, 72],
204 + "장흥군" : [59, 64],
205 + "강진군" : [57, 63],
206 + "해남군" : [54, 61],
207 + "영암군" : [56, 66],
208 + "무안군" : [52, 71],
209 + "함평군" : [52, 72],
210 + "영광군" : [52, 77],
211 + "장성군" : [57, 77],
212 + "완도군" : [57, 56],
213 + "진도군" : [48, 59],
214 + "신안군" : [50, 66]
215 + },
216 + "경상북도" : {
217 + "포항시" : [102, 94],
218 + "경주시" : [100, 91],
219 + "김천시" : [80, 96],
220 + "안동시" : [91, 106],
221 + "구미시" : [84, 96],
222 + "영주시" : [89, 111],
223 + "영천시" : [95, 93],
224 + "상주시" : [81, 102],
225 + "문경시" : [81, 106],
226 + "경산시" : [91, 90],
227 + "군위군" : [88, 99],
228 + "의성군" : [90, 101],
229 + "청송군" : [96, 103],
230 + "영양군" : [97, 108],
231 + "영덕군" : [102, 103],
232 + "청도군" : [91, 86],
233 + "고령군" : [83, 87],
234 + "성주군" : [83, 91],
235 + "칠곡군" : [85, 93],
236 + "예천군" : [86, 107],
237 + "봉화군" : [90, 113],
238 + "울진군" : [102, 115],
239 + "울릉군" : [127, 127]
240 + },
241 + "경상남도" : {
242 + "창원시" : [90, 77],
243 + "진주시" : [81, 75],
244 + "통영시" : [87, 68],
245 + "사천시" : [80, 71],
246 + "김해시" : [95, 77],
247 + "밀양시" : [92, 83],
248 + "거제시" : [90, 69],
249 + "양산시" : [97, 79],
250 + "의령군" : [83, 78],
251 + "함안군" : [86, 77],
252 + "창녕군" : [87, 83],
253 + "고성군" : [85, 71],
254 + "남해군" : [77, 68],
255 + "하동군" : [74, 73],
256 + "산청군" : [76, 80],
257 + "함양군" : [74, 82],
258 + "거창군" : [77, 86],
259 + "합천군" : [81, 84]
260 + },
261 + "제주특별자치도" : {
262 + "제주시" : [53, 38],
263 + "서귀포시" : [52, 33]
264 + }
265 +}
...\ No newline at end of file ...\ No newline at end of file
1 +const mongoose =require('mongoose');
2 +const { Schema } =mongoose;
3 +
4 +const festivalSchema = new Schema({
5 + title: String,
6 + addr: String,
7 + tel: String,
8 + contentid : Number,
9 + mapx : Number,
10 + mapy : Number,
11 + eventstartdate : String,
12 + eventenddate : String,
13 + overview : String,
14 + firstimage : String,
15 + homepage : String,
16 + weathers : String
17 + // weather : {
18 + // date1: { weather : String, temp : Number},
19 + // date2: { weather : String, temp : Number},
20 + // date3: { weather : String, temp : Number},
21 + // }
22 +},
23 +{
24 + versionKey: false
25 +});
26 +
27 +
28 +module.exports = mongoose.model('Festival',festivalSchema);
This diff is collapsed. Click to expand it.
1 +{
2 + "name": "REST-API",
3 + "version": "1.0.0",
4 + "description": "",
5 + "main": "index.js",
6 + "scripts": {
7 + "test": "echo \"Error: no test specified\" && exit 1"
8 + },
9 + "keywords": [],
10 + "author": "",
11 + "license": "ISC",
12 + "dependencies": {
13 + "dotenv": "^10.0.0",
14 + "mongoose": "^6.0.13",
15 + "request": "^2.88.2",
16 + "request-promise-native": "^1.0.9"
17 + }
18 +}
1 +const mongoose = require('mongoose');
2 +// const db = require('mongodb');
3 +const Festival = require('./models/Festival');
4 +const request = require('request-promise-native');
5 +
6 +const url = 'mongodb://mongo:27017';
7 +
8 +const ServiceKey = '3zrQDvoNwUV9Se%2BHZv8DjCCNWRGJisQ7jjHP6LsbJqoRQ2cJpQKrHUGC4uslgXSVO9Dzb06BSC3kp9BunvIPSw%3D%3D';
9 +const ServiceKey2 ='%2FGjtI8kwZeJTzJm%2BxUxz%2Bjh15wnmV3rwFuRvrq3oRSqyklfiZfbUaqmsG0McVPJMdXSUYetGaCXl0ZkbfMI0BQ%3D%3D'
10 +const ServiceKey3 ='%2FsBWti235XX%2Fg1%2FqBZfiNQ6A%2BJmF3WL%2FboaNqJH4v3eWic59SiHc6W5vgZKU7Hjocj%2BAntIqHfhXOpmE5CpAFw%3D%3D'
11 +
12 +const WeatherServiceKey = '2lFkvQJYgzOOhwUKiUt8aZVNpd1PpBOf%2FfMNW17cl25DE0GUEDddeR9iGnuSUpggjUoIUgamfhcvnKQ3eH1dAw%3D%3D';
13 +const COORDINATES = require('./coordinates')['COORDINATES'];
14 +const DISTRICT = [
15 + "서울특별시", "부산광역시", "울산광역시", "대구광역시", "대전광역시",
16 + "인천광역시", "광주광역시", "세종특별자치시", "제주특별자치도",
17 + "경기도", "강원도", "충청북도", "충청남도", "경상북도",
18 + "경상남도", "전라북도", "전라남도"
19 +]
20 +const WEATHERTYPE = [
21 + '맑음', '비', '비/눈', '눈', '소나기'
22 +];
23 +
24 +function parseDistrict(addr) {
25 + const words = addr.split(" ");
26 + if( DISTRICT.includes(words[0]) ) {
27 + return [words[0], words[1]];
28 + } else {
29 + return [];
30 + }
31 +}
32 +function leftPad(value) { if (value >= 10) { return value; } return `0${value}`; }
33 +
34 +writeDB()
35 +setInterval(() => {
36 + writeDB();
37 +}, 86400000);
38 +
39 +function writeDB() {
40 +
41 +var today = new Date();
42 +var yesterday = new Date();
43 +yesterday.setDate(yesterday.getDate() - 1);
44 +let todayString = "" + (today.getFullYear())+leftPad(today.getMonth()+1)+leftPad(today.getDate());
45 +let yesterdayString = "" + (yesterday.getFullYear())+leftPad(yesterday.getMonth()+1)+leftPad(yesterday.getDate()-1);
46 +var todayTime = leftPad(today.getHours()) + "00";
47 +
48 +mongoose.connect(url,(err)=>{
49 + if(err) {
50 + console.log(err);
51 + } else {
52 + mongoose.connection.db.dropCollection('festivals',function(err, result) {
53 + if(err) {
54 + console.log(err + "Reset Failed!");
55 + } else {
56 + console.log(result + "Reset Success!");
57 +
58 + for(let i = 1; i <= 5; i++) {
59 + let options = {
60 + 'method': 'GET',
61 + 'url' : 'http://api.visitkorea.or.kr/openapi/service/rest/KorService/areaBasedList'
62 + + '?ServiceKey=' + ServiceKey2
63 + + '&contentTypeId=15&areaCode=&sigunguCode=&cat1=&cat2=&cat3=&listYN=Y&MobileOS=ETC&MobileApp=TourAPI3.0_Guide&arrange=C&numOfRows=12'
64 + + '&pageNo='+ i
65 + + '&_type=json',
66 +
67 + 'headers': {}
68 + };
69 +
70 + request(options, async function (error, response, body) {
71 + if (error) {
72 + throw new Error(error);
73 + }
74 + let info = JSON.parse(body);
75 +
76 + let items = info['response']['body']['items']['item'];
77 + for(item of items) {
78 + let Info = {
79 + 'public': {
80 + 'method': 'GET',
81 + 'url': 'http://api.visitkorea.or.kr/openapi/service/rest/KorService/detailCommon?'
82 + + 'ServiceKey=' + ServiceKey2
83 + + '&contentTypeId=' + '15'
84 + + '&contentId=' + item['contentid']
85 + + '&MobileOS=ETC&MobileApp=TourAPI3.0_Guide&defaultYN=Y&firstImageYN=Y&areacodeYN=Y&catcodeYN=Y&addrinfoYN=Y&mapinfoYN=Y&overviewYN=Y&transGuideYN=Y&_type=json',
86 + 'headers': {}
87 + },
88 + 'detail': {
89 + 'method': 'GET',
90 + 'url': 'http://api.visitkorea.or.kr/openapi/service/rest/KorService/detailIntro?'
91 + + 'ServiceKey=' + ServiceKey2
92 + + '&contentTypeId=' + '15'
93 + + '&contentId=' + item['contentid']
94 + + '&MobileOS=ETC&MobileApp=TourAPI3.0_Guide&introYN=Y&_type=json',
95 + 'headers': {}
96 + },
97 + 'weather': {
98 + 'method': 'GET',
99 + 'url': '',
100 + 'headers': {}
101 + }
102 + };
103 + await request(Info.public, async function (error, response, body) {
104 + if (error) {
105 + throw new Error(error);
106 + }
107 +
108 + let toSave = true;
109 + let pinfo = JSON.parse(body);
110 + let distriction = parseDistrict(pinfo['response']['body']['items']['item']['addr1']);
111 + if (distriction.length == 0) toSave = false;
112 +
113 + const newFestival = new Festival();
114 + newFestival.title = pinfo['response']['body']['items']['item']['title'];
115 + newFestival.contentid = pinfo['response']['body']['items']['item']['contentid'];
116 + newFestival.addr = pinfo['response']['body']['items']['item']['addr1'];
117 + newFestival.tel = pinfo['response']['body']['items']['item']['tel'];
118 + newFestival.mapx = pinfo['response']['body']['items']['item']['mapx'];
119 + newFestival.mapy = pinfo['response']['body']['items']['item']['mapy'];
120 + newFestival.overview= pinfo['response']['body']['items']['item']['overview'];
121 + newFestival.firstimage = pinfo['response']['body']['items']['item']['firstimage'];
122 + newFestival.homepage = pinfo ['response']['body']['items']['item']['homepage'];
123 +
124 +
125 + await request(Info.detail, function (error, response, body) {
126 + if (error) {
127 + throw new Error(error);
128 + }
129 + let dinfo = JSON.parse(body);
130 + newFestival.eventstartdate = dinfo['response']['body']['items']['item']['eventstartdate'];
131 + newFestival.eventenddate = dinfo['response']['body']['items']['item']['eventenddate'];
132 +
133 + if (newFestival.eventenddate < todayString) toSave = false;
134 + });
135 +
136 + let [nx, ny] = COORDINATES[distriction[0]][distriction[1]];
137 + let curDate = ('0500' < todayTime ? todayString : yesterdayString);
138 +
139 + Info.weather.url = 'http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?'
140 + + 'serviceKey=' + WeatherServiceKey
141 + + '&pageNo=' + '1'
142 + + '&numOfRows=' + '2000'
143 + + '&dataType=' + 'JSON'
144 + + '&base_date=' + curDate
145 + + '&base_time=' + '0500'
146 + + '&nx=' + nx
147 + + '&ny=' + ny;
148 +
149 + await request(Info.weather, function (error, response, body) {
150 + if (error) {
151 + throw new Error(error);
152 + }
153 + let winfo = JSON.parse(body);
154 +
155 + let weathers = {};
156 + for( let item of winfo['response']['body']['items']['item'] ) {
157 + if(item['fcstTime'] === '1200') { // 최고기온 + 날씨
158 + let fcstDate = item['fcstDate'];
159 + if(!weathers[fcstDate]) weathers[fcstDate] = {};
160 + if(item['category']=='TMP') {
161 + weathers[fcstDate]['temp'] = item['fcstValue'];
162 + } else if(item['category']=='PTY') {
163 + weathers[fcstDate]['weather'] = WEATHERTYPE[item['fcstValue']];
164 + }
165 + }
166 + }
167 + newFestival.weathers = JSON.stringify(weathers);
168 + });
169 +
170 + if (toSave)
171 + await newFestival.save().then((festival) => {
172 + console.log(festival, "Save success!");
173 + });
174 + })
175 + }
176 + });
177 + }
178 + }
179 + });
180 + }
181 + });
182 +}
1 +node_modules
2 +npm-debug.log
...\ No newline at end of file ...\ No newline at end of file
1 FROM node:14 1 FROM node:14
2 2
3 LABEL title="TFT-Webserver" 3 LABEL title="TFT-Webserver"
4 -LABEL version="1.01" 4 +LABEL version="1.00"
5 5
6 # set working directory 6 # set working directory
7 WORKDIR /app 7 WORKDIR /app
......
...@@ -42,6 +42,20 @@ ...@@ -42,6 +42,20 @@
42 "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", 42 "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz",
43 "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==" 43 "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw=="
44 }, 44 },
45 + "@types/webidl-conversions": {
46 + "version": "6.1.1",
47 + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz",
48 + "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q=="
49 + },
50 + "@types/whatwg-url": {
51 + "version": "8.2.1",
52 + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz",
53 + "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==",
54 + "requires": {
55 + "@types/node": "*",
56 + "@types/webidl-conversions": "*"
57 + }
58 + },
45 "@webassemblyjs/ast": { 59 "@webassemblyjs/ast": {
46 "version": "1.9.0", 60 "version": "1.9.0",
47 "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", 61 "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
...@@ -505,8 +519,7 @@ ...@@ -505,8 +519,7 @@
505 "base64-js": { 519 "base64-js": {
506 "version": "1.3.1", 520 "version": "1.3.1",
507 "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 521 "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
508 - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", 522 + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
509 - "dev": true
510 }, 523 },
511 "base64id": { 524 "base64id": {
512 "version": "2.0.0", 525 "version": "2.0.0",
...@@ -776,6 +789,25 @@ ...@@ -776,6 +789,25 @@
776 "pako": "~1.0.5" 789 "pako": "~1.0.5"
777 } 790 }
778 }, 791 },
792 + "bson": {
793 + "version": "4.6.0",
794 + "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.0.tgz",
795 + "integrity": "sha512-8jw1NU1hglS+Da1jDOUYuNcBJ4cNHCFIqzlwoFNnsTOg2R/ox0aTYcTiBN4dzRa9q7Cvy6XErh3L8ReTEb9AQQ==",
796 + "requires": {
797 + "buffer": "^5.6.0"
798 + },
799 + "dependencies": {
800 + "buffer": {
801 + "version": "5.7.1",
802 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
803 + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
804 + "requires": {
805 + "base64-js": "^1.3.1",
806 + "ieee754": "^1.1.13"
807 + }
808 + }
809 + }
810 + },
779 "buffer": { 811 "buffer": {
780 "version": "4.9.2", 812 "version": "4.9.2",
781 "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 813 "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
...@@ -1375,6 +1407,11 @@ ...@@ -1375,6 +1407,11 @@
1375 } 1407 }
1376 } 1408 }
1377 }, 1409 },
1410 + "denque": {
1411 + "version": "2.0.1",
1412 + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
1413 + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ=="
1414 + },
1378 "depd": { 1415 "depd": {
1379 "version": "1.1.2", 1416 "version": "1.1.2",
1380 "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 1417 "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
...@@ -2326,8 +2363,7 @@ ...@@ -2326,8 +2363,7 @@
2326 "ieee754": { 2363 "ieee754": {
2327 "version": "1.1.13", 2364 "version": "1.1.13",
2328 "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 2365 "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
2329 - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", 2366 + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
2330 - "dev": true
2331 }, 2367 },
2332 "iferr": { 2368 "iferr": {
2333 "version": "0.1.5", 2369 "version": "0.1.5",
...@@ -2796,6 +2832,12 @@ ...@@ -2796,6 +2832,12 @@
2796 "readable-stream": "^2.0.1" 2832 "readable-stream": "^2.0.1"
2797 } 2833 }
2798 }, 2834 },
2835 + "memory-pager": {
2836 + "version": "1.5.0",
2837 + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
2838 + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
2839 + "optional": true
2840 + },
2799 "merge-descriptors": { 2841 "merge-descriptors": {
2800 "version": "1.0.1", 2842 "version": "1.0.1",
2801 "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 2843 "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
...@@ -2939,6 +2981,26 @@ ...@@ -2939,6 +2981,26 @@
2939 "minimist": "^1.2.5" 2981 "minimist": "^1.2.5"
2940 } 2982 }
2941 }, 2983 },
2984 + "mongodb": {
2985 + "version": "4.2.0",
2986 + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.2.0.tgz",
2987 + "integrity": "sha512-lg3MJ9dAKxhogRnIB6/j63gfD7JryZwRC0nNzZ82RhENw4nCmscZVqRfOmNzTvSNndJx9ZhxZpm9JvnKuH/GTA==",
2988 + "requires": {
2989 + "bson": "^4.5.4",
2990 + "denque": "^2.0.1",
2991 + "mongodb-connection-string-url": "^2.2.0",
2992 + "saslprep": "^1.0.3"
2993 + }
2994 + },
2995 + "mongodb-connection-string-url": {
2996 + "version": "2.2.0",
2997 + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.2.0.tgz",
2998 + "integrity": "sha512-U0cDxLUrQrl7DZA828CA+o69EuWPWEJTwdMPozyd7cy/dbtncUZczMw7wRHcwMD7oKOn0NM2tF9jdf5FFVW9CA==",
2999 + "requires": {
3000 + "@types/whatwg-url": "^8.2.1",
3001 + "whatwg-url": "^11.0.0"
3002 + }
3003 + },
2942 "move-concurrently": { 3004 "move-concurrently": {
2943 "version": "1.0.1", 3005 "version": "1.0.1",
2944 "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", 3006 "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
...@@ -3557,8 +3619,7 @@ ...@@ -3557,8 +3619,7 @@
3557 "punycode": { 3619 "punycode": {
3558 "version": "2.1.1", 3620 "version": "2.1.1",
3559 "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 3621 "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
3560 - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 3622 + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
3561 - "dev": true
3562 }, 3623 },
3563 "pupa": { 3624 "pupa": {
3564 "version": "2.1.1", 3625 "version": "2.1.1",
...@@ -3816,6 +3877,15 @@ ...@@ -3816,6 +3877,15 @@
3816 "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 3877 "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
3817 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 3878 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
3818 }, 3879 },
3880 + "saslprep": {
3881 + "version": "1.0.3",
3882 + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
3883 + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
3884 + "optional": true,
3885 + "requires": {
3886 + "sparse-bitfield": "^3.0.3"
3887 + }
3888 + },
3819 "schema-utils": { 3889 "schema-utils": {
3820 "version": "2.6.5", 3890 "version": "2.6.5",
3821 "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz", 3891 "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz",
...@@ -4205,6 +4275,15 @@ ...@@ -4205,6 +4275,15 @@
4205 "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", 4275 "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
4206 "dev": true 4276 "dev": true
4207 }, 4277 },
4278 + "sparse-bitfield": {
4279 + "version": "3.0.3",
4280 + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
4281 + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
4282 + "optional": true,
4283 + "requires": {
4284 + "memory-pager": "^1.0.2"
4285 + }
4286 + },
4208 "split-string": { 4287 "split-string": {
4209 "version": "3.1.0", 4288 "version": "3.1.0",
4210 "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", 4289 "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
...@@ -4501,6 +4580,14 @@ ...@@ -4501,6 +4580,14 @@
4501 "nopt": "~1.0.10" 4580 "nopt": "~1.0.10"
4502 } 4581 }
4503 }, 4582 },
4583 + "tr46": {
4584 + "version": "3.0.0",
4585 + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
4586 + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
4587 + "requires": {
4588 + "punycode": "^2.1.1"
4589 + }
4590 + },
4504 "tslib": { 4591 "tslib": {
4505 "version": "1.11.1", 4592 "version": "1.11.1",
4506 "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", 4593 "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
...@@ -4940,6 +5027,11 @@ ...@@ -4940,6 +5027,11 @@
4940 } 5027 }
4941 } 5028 }
4942 }, 5029 },
5030 + "webidl-conversions": {
5031 + "version": "7.0.0",
5032 + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
5033 + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
5034 + },
4943 "webpack": { 5035 "webpack": {
4944 "version": "4.42.1", 5036 "version": "4.42.1",
4945 "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz", 5037 "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz",
...@@ -5092,6 +5184,15 @@ ...@@ -5092,6 +5184,15 @@
5092 "source-map": "~0.6.1" 5184 "source-map": "~0.6.1"
5093 } 5185 }
5094 }, 5186 },
5187 + "whatwg-url": {
5188 + "version": "11.0.0",
5189 + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
5190 + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
5191 + "requires": {
5192 + "tr46": "^3.0.0",
5193 + "webidl-conversions": "^7.0.0"
5194 + }
5195 + },
5095 "which": { 5196 "which": {
5096 "version": "2.0.2", 5197 "version": "2.0.2",
5097 "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 5198 "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
23 "dependencies": { 23 "dependencies": {
24 "express": "^4.17.1", 24 "express": "^4.17.1",
25 "jquery": "^3.6.0", 25 "jquery": "^3.6.0",
26 + "mongodb": "^4.2.0",
26 "nodemon": "^2.0.15", 27 "nodemon": "^2.0.15",
27 "socket.io": "^4.3.2", 28 "socket.io": "^4.3.2",
28 "socket.io-client": "^4.3.2" 29 "socket.io-client": "^4.3.2"
......
This diff could not be displayed because it is too large.
...@@ -5,29 +5,29 @@ const MongoClient = mongodb.MongoClient; ...@@ -5,29 +5,29 @@ const MongoClient = mongodb.MongoClient;
5 5
6 var app = express() 6 var app = express()
7 7
8 -const PORT = 1697; 8 +const PORT = 8484;
9 const url = 'mongodb://mongo:27017'; 9 const url = 'mongodb://mongo:27017';
10 var db; 10 var db;
11 app.use(express.urlencoded({ extended: true })); 11 app.use(express.urlencoded({ extended: true }));
12 12
13 app.use("/public", express.static('./public')); 13 app.use("/public", express.static('./public'));
14 14
15 -app.get("/*", (req, res) => {
16 - res.sendFile(path.join(__dirname, "./public/index.html"))
17 -})
18 -
19 app.get('/festivalList', (req, res) => { // localhost:3000/festivalList 입력하면 list.ejs에 저장한 형식대로 정보 불러와짐 15 app.get('/festivalList', (req, res) => { // localhost:3000/festivalList 입력하면 list.ejs에 저장한 형식대로 정보 불러와짐
20 //디비에 저장된 festivals 라는 collection안의 데이터(제목 또는 내용 등)를 꺼내기 16 //디비에 저장된 festivals 라는 collection안의 데이터(제목 또는 내용 등)를 꺼내기
21 db.collection('festivals').find().toArray((err, rslt) => { //DB에서 데이터를 찾음 festivals라는 collection안의 데이터를 꺼내게 됨 17 db.collection('festivals').find().toArray((err, rslt) => { //DB에서 데이터를 찾음 festivals라는 collection안의 데이터를 꺼내게 됨
22 if (err) throw err; 18 if (err) throw err;
23 - console.log(rslt); 19 + res.json(rslt); // 찾은 데이터를 json으로 전송
24 - res.render('list.ejs', { posts: rslt }); // 찾은 데이터를 ejs 파일에 넣음
25 }); 20 });
26 }); 21 });
27 22
23 +app.get("/*", (req, res) => {
24 + res.sendFile(path.join(__dirname, "./public/index.html"))
25 +})
26 +
27 +
28 MongoClient.connect(url, (error, client) => { // 서버열때 url 사용 mongoDB와 연결시키기 28 MongoClient.connect(url, (error, client) => { // 서버열때 url 사용 mongoDB와 연결시키기
29 if (error) return console.log(error); 29 if (error) return console.log(error);
30 - db = client.db('myFirstDatabase'); 30 + db = client.db('test');
31 app.listen(PORT, () => { 31 app.listen(PORT, () => {
32 console.log(`Server lauched on port ${PORT}`); 32 console.log(`Server lauched on port ${PORT}`);
33 }); 33 });
......
...@@ -9,8 +9,14 @@ ...@@ -9,8 +9,14 @@
9 function LoadFestas() { 9 function LoadFestas() {
10 let url = "/festivalList"; 10 let url = "/festivalList";
11 jQuery.getJSON(url, (json) => { 11 jQuery.getJSON(url, (json) => {
12 - AllFestas.set(json.response.body.items.item); 12 + AllFestas.set(json);
13 + // console.log(json);
13 }); 14 });
15 + // let url = "http://api.visitkorea.or.kr/openapi/service/rest/KorService/areaBasedList?ServiceKey=2lFkvQJYgzOOhwUKiUt8aZVNpd1PpBOf%2FfMNW17cl25DE0GUEDddeR9iGnuSUpggjUoIUgamfhcvnKQ3eH1dAw%3D%3D&contentTypeId=15&areaCode=&sigunguCode=&cat1=&cat2=&cat3=&listYN=Y&MobileOS=ETC&MobileApp=TourAPI3.0_Guide&arrange=A&numOfRows=12&pageNo=1&_type=json";
16 + // jQuery.getJSON(url, (json) => {
17 + // AllFestas.set(json.response.body.items.item);
18 + // console.log(json.response.body.items.item);
19 + // });
14 } 20 }
15 </script> 21 </script>
16 22
......
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
80 return function() { 80 return function() {
81 Festa = festa; 81 Festa = festa;
82 ShowInfo = true; 82 ShowInfo = true;
83 - console.log(Festa); 83 + // console.log(Festa);
84 } 84 }
85 }; 85 };
86 kakao.maps.event.addListener(marker, 'click', showInfo(data[i])); 86 kakao.maps.event.addListener(marker, 'click', showInfo(data[i]));
......
...@@ -23,9 +23,9 @@ ...@@ -23,9 +23,9 @@
23 23
24 let festaChecked = []; 24 let festaChecked = [];
25 $: festaList = $AllFestas.filter( v => { 25 $: festaList = $AllFestas.filter( v => {
26 - if(v.addr1) { 26 + if(v.addr) {
27 - let district = v.addr1.split(" ")[0]; 27 + let district = v.addr.split(" ")[0];
28 - let city = v.addr1.split(" ")[1]; 28 + let city = v.addr.split(" ")[1];
29 return ($District === "" || district === $District) && 29 return ($District === "" || district === $District) &&
30 ($City === "" || city === $City); 30 ($City === "" || city === $City);
31 } else { 31 } else {
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
33 } 33 }
34 }); 34 });
35 $: festaParsed = festaList.map( (v, i) => { 35 $: festaParsed = festaList.map( (v, i) => {
36 - return { "id" : i, "title" : v.title, "addr1" : v.addr1, "contentid" : v.contentid, "checked" : false } 36 + return { "id" : i, "title" : v.title, "addr" : v.addr, "contentid" : v.contentid, "checked" : false }
37 }); 37 });
38 $: if ($Changed) { 38 $: if ($Changed) {
39 let len = festaList.length >= 9 ? 9 : festaList.length; 39 let len = festaList.length >= 9 ? 9 : festaList.length;
...@@ -142,7 +142,7 @@ ...@@ -142,7 +142,7 @@
142 <div class="{festa.checked ? "selected" : "festa"}" 142 <div class="{festa.checked ? "selected" : "festa"}"
143 on:click={() => {check(festa.id)}}> 143 on:click={() => {check(festa.id)}}>
144 <div class="title">{festa.title}</div> 144 <div class="title">{festa.title}</div>
145 - <div class="addr"><img alt="pin" src="/public/map-pin.png"><div>{festa.addr1}</div></div> 145 + <div class="addr"><img alt="pin" src="/public/map-pin.png"><div>{festa.addr}</div></div>
146 </div> 146 </div>
147 {/each} 147 {/each}
148 {:else} 148 {:else}
......
1 +<script>
2 + import SideBar from "./SideBar.svelte"
3 +
4 + export let festa;
5 + export let sidebar_show = false;
6 + var side = "right";
7 + var weatherData, weathers;
8 +
9 + const WEATHERIMG = {
10 + "맑음" : "./public/sunny.png",
11 + "비" : "./public/rain.png",
12 + "비/눈" : "./public/rainsnow.png",
13 + "눈" : "./public/snow.png"
14 + }
15 +
16 + function hide() {
17 + if (window.scrollY > 400) {
18 + sidebar_show = false;
19 + }
20 + }
21 +
22 + $: if(festa.weathers) weatherData = JSON.parse(festa.weathers);
23 + $: if(festa.weathers) weathers = Array.from(Object.keys(weatherData)).map((v) => {
24 + return { "date" : v.slice(4), "temp" : weatherData[v].temp, "weather" : weatherData[v].weather };
25 + });
26 +
27 +</script>
28 +
29 +<style>
30 + .info {
31 + display: flex;
32 + flex-direction: column;
33 + align-items: center;
34 + }
35 +
36 + .title {
37 + font-size: 18pt;
38 + font-weight: bold;
39 + }
40 +
41 + .content {
42 + padding: 0.5rem 0.5rem 0.5rem;
43 + text-align: left;
44 + }
45 +
46 + .festaimg {
47 + max-width: 560px;
48 + }
49 +
50 + .locpin, .telpin, .calpin {
51 + width: 20px;
52 + height: 20px;
53 + }
54 +
55 + .weather {
56 + /* border: 1px solid #999999; */
57 + border-collapse: collapse;
58 + }
59 +
60 + .weather td {
61 + padding: 0.3rem;
62 + border: 1px solid #cccccc;
63 + }
64 +
65 + .weatherimg {
66 + width : 100px;
67 + }
68 +
69 +
70 +</style>
71 +
72 +<svelte:window on:scroll={hide}></svelte:window>
73 +<SideBar bind:show={sidebar_show} {side}>
74 + <div class="info">
75 + <div class="title">{festa.title}</div>
76 + <img class="festaimg" alt="festaimg" src={festa.firstimage}><br>
77 + {#if weathers}
78 + <table class="weather"><tr>
79 + {#each weathers as weather}
80 + <td class="weathercell">
81 + <img class="weatherimg" alt="weather" src={WEATHERIMG[weather.weather]}><br>
82 + {weather.date.slice(0, 2) + '/' + weather.date.slice(2, 4)}<br>
83 + {weather.temp}℃<br>
84 + </td>
85 + {/each}
86 + </tr></table>
87 + {/if}
88 + <div class="content">
89 + <img class="locpin" alt="pin" src="/public/map-pin.png"> 개최지 : {festa.addr}<br>
90 + <img class="telpin" alt="pin" src="/public/tel-pin.jpeg"> 전화번호 : {festa.tel}<br>
91 + <img class="calpin" alt="pin" src="/public/cal-pin.png"> 행사일 : {festa.eventstartdate} - {festa.eventenddate}<br>
92 + </div>
93 + </div>
94 +
95 +
96 +
97 +</SideBar>
This diff could not be displayed because it is too large.
1 -<script>
2 - import SideBar from "./SideBar.svelte"
3 -
4 - export let festa;
5 - export let sidebar_show = false;
6 - let side = "right";
7 -
8 - function hide() {
9 - if (window.scrollY > 400) {
10 - sidebar_show = false;
11 - }
12 - }
13 -</script>
14 -
15 -<style>
16 - .title {
17 - font-size: 18pt;
18 - font-weight: bold;
19 - text-align: center;
20 - }
21 -
22 - .content {
23 - padding: 0.5rem 0.5rem 0.5rem;
24 - text-align: center;
25 - }
26 -
27 - .info img {
28 - max-height: 20rem;
29 - }
30 -
31 -</style>
32 -
33 -<svelte:window on:scroll={hide}></svelte:window>
34 -<SideBar bind:show={sidebar_show} {side}>
35 - <div class="info">
36 - <div class="title">{festa.title}</div>
37 - <img alt="festaImg" src={festa.firstimage}><br>
38 - <div class="content">
39 - 개최지 : {festa.addr1}<br>
40 - 전화번호 : {festa.tel}<br>
41 - <!-- 행사 시작일 : {festa.startdate}<br>
42 - 행사 시작일 : {festa.enddate}<br>
43 - 날씨 : {festa.weather 어쩌구} -->
44 - </div>
45 - </div>
46 -
47 -
48 -
49 -</SideBar>
1 -<!DOCTYPE html>
2 -<html>
3 -
4 -<head>
5 - <meta charset="UTF-8">
6 - <meta http-equiv="X-UA-Compatible" content="IE=edge">
7 - <meta name="viewport" content="width=device-width, initial-scale=1.0">
8 - <title>Festival Information</title>
9 -</head>
10 -
11 -<body>
12 - <% for(let i=0; i < posts.length ; i++){ %>
13 - <h4>제목 : <%= posts[i].title %>
14 - </h4>
15 - <h4>주소 : <%= posts[i].addr %>
16 - </h4>
17 - <h4>전화번호 : <%= posts[i].tel %>
18 - </h4>
19 - <h4>mapx : <%= posts[i].mapx %>
20 - </h4>
21 - <h4>mapy : <%= posts[i].mapy %>
22 - </h4>
23 - <h4>갱신일 : <%= posts[i].updatedAt %>
24 - </h4>
25 - <h4> ------------------- </h4>
26 - <% } %>
27 -</body>
28 -
29 -</html>
...\ No newline at end of file ...\ No newline at end of file