권주희

Merge branch 'develop' into 'master'

Develop



See merge request !7
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
2 2
3 # dependencies 3 # dependencies
4 frontend/node_modules/* 4 frontend/node_modules/*
5 +backend/node_modules/*
5 /.pnp 6 /.pnp
6 .pnp.js 7 .pnp.js
7 8
...@@ -21,3 +22,4 @@ frontend/node_modules/* ...@@ -21,3 +22,4 @@ frontend/node_modules/*
21 npm-debug.log* 22 npm-debug.log*
22 yarn-debug.log* 23 yarn-debug.log*
23 yarn-error.log* 24 yarn-error.log*
25 +secrets.json
...\ No newline at end of file ...\ No newline at end of file
......
1 -var createError = require('http-errors'); 1 +var createError = require("http-errors");
2 -var express = require('express'); 2 +var express = require("express");
3 -var path = require('path'); 3 +var cors = require("cors");
4 -var cookieParser = require('cookie-parser'); 4 +var path = require("path");
5 -var logger = require('morgan'); 5 +var cookieParser = require("cookie-parser");
6 +var logger = require("morgan");
6 7
7 -var indexRouter = require('./routes/index'); 8 +var indexRouter = require("./routes/index");
8 -var usersRouter = require('./routes/users'); 9 +var usersRouter = require("./routes/users");
10 +var airConditionRouter = require("./routes/airCondition");
9 11
10 var app = express(); 12 var app = express();
13 +app.use(cors());
11 14
12 // view engine setup 15 // view engine setup
13 -app.set('views', path.join(__dirname, 'views')); 16 +app.set("views", path.join(__dirname, "views"));
14 -app.set('view engine', 'pug'); 17 +app.set("view engine", "pug");
15 18
16 -app.use(logger('dev')); 19 +app.use(logger("dev"));
17 app.use(express.json()); 20 app.use(express.json());
18 app.use(express.urlencoded({ extended: false })); 21 app.use(express.urlencoded({ extended: false }));
19 app.use(cookieParser()); 22 app.use(cookieParser());
20 -app.use(express.static(path.join(__dirname, 'public'))); 23 +app.use(express.static(path.join(__dirname, "public")));
21 24
22 -app.use('/', indexRouter); 25 +app.use("/", indexRouter);
23 -app.use('/users', usersRouter); 26 +app.use("/users", usersRouter);
27 +app.use("/airCondition", airConditionRouter);
24 28
25 // catch 404 and forward to error handler 29 // catch 404 and forward to error handler
26 -app.use(function(req, res, next) { 30 +app.use(function (req, res, next) {
27 next(createError(404)); 31 next(createError(404));
28 }); 32 });
29 33
30 // error handler 34 // error handler
31 -app.use(function(err, req, res, next) { 35 +app.use(function (err, req, res, next) {
32 // set locals, only providing error in development 36 // set locals, only providing error in development
33 res.locals.message = err.message; 37 res.locals.message = err.message;
34 - res.locals.error = req.app.get('env') === 'development' ? err : {}; 38 + res.locals.error = req.app.get("env") === "development" ? err : {};
35 39
36 // render the error page 40 // render the error page
37 res.status(err.status || 500); 41 res.status(err.status || 500);
38 - res.render('error'); 42 + res.render("error");
39 }); 43 });
40 44
41 module.exports = app; 45 module.exports = app;
......
...@@ -4,16 +4,16 @@ ...@@ -4,16 +4,16 @@
4 * Module dependencies. 4 * Module dependencies.
5 */ 5 */
6 6
7 -var app = require('../app'); 7 +var app = require("../app");
8 -var debug = require('debug')('backend:server'); 8 +var debug = require("debug")("backend:server");
9 -var http = require('http'); 9 +var http = require("http");
10 10
11 /** 11 /**
12 * Get port from environment and store in Express. 12 * Get port from environment and store in Express.
13 */ 13 */
14 14
15 -var port = normalizePort(process.env.PORT || '3000'); 15 +var port = normalizePort(process.env.PORT || "3001");
16 -app.set('port', port); 16 +app.set("port", port);
17 17
18 /** 18 /**
19 * Create HTTP server. 19 * Create HTTP server.
...@@ -26,8 +26,8 @@ var server = http.createServer(app); ...@@ -26,8 +26,8 @@ var server = http.createServer(app);
26 */ 26 */
27 27
28 server.listen(port); 28 server.listen(port);
29 -server.on('error', onError); 29 +server.on("error", onError);
30 -server.on('listening', onListening); 30 +server.on("listening", onListening);
31 31
32 /** 32 /**
33 * Normalize a port into a number, string, or false. 33 * Normalize a port into a number, string, or false.
...@@ -54,22 +54,20 @@ function normalizePort(val) { ...@@ -54,22 +54,20 @@ function normalizePort(val) {
54 */ 54 */
55 55
56 function onError(error) { 56 function onError(error) {
57 - if (error.syscall !== 'listen') { 57 + if (error.syscall !== "listen") {
58 throw error; 58 throw error;
59 } 59 }
60 60
61 - var bind = typeof port === 'string' 61 + var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;
62 - ? 'Pipe ' + port
63 - : 'Port ' + port;
64 62
65 // handle specific listen errors with friendly messages 63 // handle specific listen errors with friendly messages
66 switch (error.code) { 64 switch (error.code) {
67 - case 'EACCES': 65 + case "EACCES":
68 - console.error(bind + ' requires elevated privileges'); 66 + console.error(bind + " requires elevated privileges");
69 process.exit(1); 67 process.exit(1);
70 break; 68 break;
71 - case 'EADDRINUSE': 69 + case "EADDRINUSE":
72 - console.error(bind + ' is already in use'); 70 + console.error(bind + " is already in use");
73 process.exit(1); 71 process.exit(1);
74 break; 72 break;
75 default: 73 default:
...@@ -83,8 +81,6 @@ function onError(error) { ...@@ -83,8 +81,6 @@ function onError(error) {
83 81
84 function onListening() { 82 function onListening() {
85 var addr = server.address(); 83 var addr = server.address();
86 - var bind = typeof addr === 'string' 84 + var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
87 - ? 'pipe ' + addr 85 + debug("Listening on " + bind);
88 - : 'port ' + addr.port;
89 - debug('Listening on ' + bind);
90 } 86 }
......
1 +{
2 + "name": "backend",
3 + "version": "0.0.0",
4 + "lockfileVersion": 1,
5 + "requires": true,
6 + "dependencies": {
7 + "@types/babel-types": {
8 + "version": "7.0.7",
9 + "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz",
10 + "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ=="
11 + },
12 + "@types/babylon": {
13 + "version": "6.16.5",
14 + "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz",
15 + "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==",
16 + "requires": {
17 + "@types/babel-types": "*"
18 + }
19 + },
20 + "accepts": {
21 + "version": "1.3.7",
22 + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
23 + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
24 + "requires": {
25 + "mime-types": "~2.1.24",
26 + "negotiator": "0.6.2"
27 + }
28 + },
29 + "acorn": {
30 + "version": "3.3.0",
31 + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
32 + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
33 + },
34 + "acorn-globals": {
35 + "version": "3.1.0",
36 + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz",
37 + "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=",
38 + "requires": {
39 + "acorn": "^4.0.4"
40 + },
41 + "dependencies": {
42 + "acorn": {
43 + "version": "4.0.13",
44 + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
45 + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
46 + }
47 + }
48 + },
49 + "align-text": {
50 + "version": "0.1.4",
51 + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
52 + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
53 + "requires": {
54 + "kind-of": "^3.0.2",
55 + "longest": "^1.0.1",
56 + "repeat-string": "^1.5.2"
57 + }
58 + },
59 + "amdefine": {
60 + "version": "1.0.1",
61 + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
62 + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
63 + },
64 + "array-flatten": {
65 + "version": "1.1.1",
66 + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
67 + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
68 + },
69 + "asap": {
70 + "version": "2.0.6",
71 + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
72 + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
73 + },
74 + "axios": {
75 + "version": "0.19.2",
76 + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
77 + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
78 + "requires": {
79 + "follow-redirects": "1.5.10"
80 + }
81 + },
82 + "babel-runtime": {
83 + "version": "6.26.0",
84 + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
85 + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
86 + "requires": {
87 + "core-js": "^2.4.0",
88 + "regenerator-runtime": "^0.11.0"
89 + }
90 + },
91 + "babel-types": {
92 + "version": "6.26.0",
93 + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
94 + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
95 + "requires": {
96 + "babel-runtime": "^6.26.0",
97 + "esutils": "^2.0.2",
98 + "lodash": "^4.17.4",
99 + "to-fast-properties": "^1.0.3"
100 + }
101 + },
102 + "babylon": {
103 + "version": "6.18.0",
104 + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
105 + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
106 + },
107 + "basic-auth": {
108 + "version": "2.0.1",
109 + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
110 + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
111 + "requires": {
112 + "safe-buffer": "5.1.2"
113 + }
114 + },
115 + "body-parser": {
116 + "version": "1.18.3",
117 + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
118 + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
119 + "requires": {
120 + "bytes": "3.0.0",
121 + "content-type": "~1.0.4",
122 + "debug": "2.6.9",
123 + "depd": "~1.1.2",
124 + "http-errors": "~1.6.3",
125 + "iconv-lite": "0.4.23",
126 + "on-finished": "~2.3.0",
127 + "qs": "6.5.2",
128 + "raw-body": "2.3.3",
129 + "type-is": "~1.6.16"
130 + }
131 + },
132 + "bytes": {
133 + "version": "3.0.0",
134 + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
135 + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
136 + },
137 + "camelcase": {
138 + "version": "1.2.1",
139 + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
140 + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
141 + },
142 + "center-align": {
143 + "version": "0.1.3",
144 + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
145 + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
146 + "requires": {
147 + "align-text": "^0.1.3",
148 + "lazy-cache": "^1.0.3"
149 + }
150 + },
151 + "character-parser": {
152 + "version": "2.2.0",
153 + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
154 + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=",
155 + "requires": {
156 + "is-regex": "^1.0.3"
157 + }
158 + },
159 + "clean-css": {
160 + "version": "3.4.28",
161 + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz",
162 + "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=",
163 + "requires": {
164 + "commander": "2.8.x",
165 + "source-map": "0.4.x"
166 + }
167 + },
168 + "cliui": {
169 + "version": "2.1.0",
170 + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
171 + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
172 + "requires": {
173 + "center-align": "^0.1.1",
174 + "right-align": "^0.1.1",
175 + "wordwrap": "0.0.2"
176 + }
177 + },
178 + "commander": {
179 + "version": "2.8.1",
180 + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
181 + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
182 + "requires": {
183 + "graceful-readlink": ">= 1.0.0"
184 + }
185 + },
186 + "constantinople": {
187 + "version": "3.1.2",
188 + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz",
189 + "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==",
190 + "requires": {
191 + "@types/babel-types": "^7.0.0",
192 + "@types/babylon": "^6.16.2",
193 + "babel-types": "^6.26.0",
194 + "babylon": "^6.18.0"
195 + }
196 + },
197 + "content-disposition": {
198 + "version": "0.5.2",
199 + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
200 + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
201 + },
202 + "content-type": {
203 + "version": "1.0.4",
204 + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
205 + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
206 + },
207 + "cookie": {
208 + "version": "0.4.0",
209 + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
210 + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
211 + },
212 + "cookie-parser": {
213 + "version": "1.4.5",
214 + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
215 + "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
216 + "requires": {
217 + "cookie": "0.4.0",
218 + "cookie-signature": "1.0.6"
219 + }
220 + },
221 + "cookie-signature": {
222 + "version": "1.0.6",
223 + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
224 + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
225 + },
226 + "core-js": {
227 + "version": "2.6.11",
228 + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
229 + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
230 + },
231 + "cors": {
232 + "version": "2.8.5",
233 + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
234 + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
235 + "requires": {
236 + "object-assign": "^4",
237 + "vary": "^1"
238 + }
239 + },
240 + "debug": {
241 + "version": "2.6.9",
242 + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
243 + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
244 + "requires": {
245 + "ms": "2.0.0"
246 + }
247 + },
248 + "decamelize": {
249 + "version": "1.2.0",
250 + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
251 + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
252 + },
253 + "depd": {
254 + "version": "1.1.2",
255 + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
256 + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
257 + },
258 + "destroy": {
259 + "version": "1.0.4",
260 + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
261 + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
262 + },
263 + "doctypes": {
264 + "version": "1.1.0",
265 + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
266 + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk="
267 + },
268 + "ee-first": {
269 + "version": "1.1.1",
270 + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
271 + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
272 + },
273 + "encodeurl": {
274 + "version": "1.0.2",
275 + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
276 + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
277 + },
278 + "escape-html": {
279 + "version": "1.0.3",
280 + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
281 + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
282 + },
283 + "esutils": {
284 + "version": "2.0.3",
285 + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
286 + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
287 + },
288 + "etag": {
289 + "version": "1.8.1",
290 + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
291 + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
292 + },
293 + "express": {
294 + "version": "4.16.4",
295 + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
296 + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
297 + "requires": {
298 + "accepts": "~1.3.5",
299 + "array-flatten": "1.1.1",
300 + "body-parser": "1.18.3",
301 + "content-disposition": "0.5.2",
302 + "content-type": "~1.0.4",
303 + "cookie": "0.3.1",
304 + "cookie-signature": "1.0.6",
305 + "debug": "2.6.9",
306 + "depd": "~1.1.2",
307 + "encodeurl": "~1.0.2",
308 + "escape-html": "~1.0.3",
309 + "etag": "~1.8.1",
310 + "finalhandler": "1.1.1",
311 + "fresh": "0.5.2",
312 + "merge-descriptors": "1.0.1",
313 + "methods": "~1.1.2",
314 + "on-finished": "~2.3.0",
315 + "parseurl": "~1.3.2",
316 + "path-to-regexp": "0.1.7",
317 + "proxy-addr": "~2.0.4",
318 + "qs": "6.5.2",
319 + "range-parser": "~1.2.0",
320 + "safe-buffer": "5.1.2",
321 + "send": "0.16.2",
322 + "serve-static": "1.13.2",
323 + "setprototypeof": "1.1.0",
324 + "statuses": "~1.4.0",
325 + "type-is": "~1.6.16",
326 + "utils-merge": "1.0.1",
327 + "vary": "~1.1.2"
328 + },
329 + "dependencies": {
330 + "cookie": {
331 + "version": "0.3.1",
332 + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
333 + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
334 + }
335 + }
336 + },
337 + "finalhandler": {
338 + "version": "1.1.1",
339 + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
340 + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
341 + "requires": {
342 + "debug": "2.6.9",
343 + "encodeurl": "~1.0.2",
344 + "escape-html": "~1.0.3",
345 + "on-finished": "~2.3.0",
346 + "parseurl": "~1.3.2",
347 + "statuses": "~1.4.0",
348 + "unpipe": "~1.0.0"
349 + }
350 + },
351 + "follow-redirects": {
352 + "version": "1.5.10",
353 + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
354 + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
355 + "requires": {
356 + "debug": "=3.1.0"
357 + },
358 + "dependencies": {
359 + "debug": {
360 + "version": "3.1.0",
361 + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
362 + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
363 + "requires": {
364 + "ms": "2.0.0"
365 + }
366 + }
367 + }
368 + },
369 + "forwarded": {
370 + "version": "0.1.2",
371 + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
372 + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
373 + },
374 + "fresh": {
375 + "version": "0.5.2",
376 + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
377 + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
378 + },
379 + "graceful-readlink": {
380 + "version": "1.0.1",
381 + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
382 + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
383 + },
384 + "has-symbols": {
385 + "version": "1.0.1",
386 + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
387 + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
388 + },
389 + "http-errors": {
390 + "version": "1.6.3",
391 + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
392 + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
393 + "requires": {
394 + "depd": "~1.1.2",
395 + "inherits": "2.0.3",
396 + "setprototypeof": "1.1.0",
397 + "statuses": ">= 1.4.0 < 2"
398 + }
399 + },
400 + "iconv-lite": {
401 + "version": "0.4.23",
402 + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
403 + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
404 + "requires": {
405 + "safer-buffer": ">= 2.1.2 < 3"
406 + }
407 + },
408 + "inherits": {
409 + "version": "2.0.3",
410 + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
411 + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
412 + },
413 + "ipaddr.js": {
414 + "version": "1.9.1",
415 + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
416 + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
417 + },
418 + "is-buffer": {
419 + "version": "1.1.6",
420 + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
421 + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
422 + },
423 + "is-expression": {
424 + "version": "3.0.0",
425 + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz",
426 + "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=",
427 + "requires": {
428 + "acorn": "~4.0.2",
429 + "object-assign": "^4.0.1"
430 + },
431 + "dependencies": {
432 + "acorn": {
433 + "version": "4.0.13",
434 + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
435 + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
436 + }
437 + }
438 + },
439 + "is-promise": {
440 + "version": "2.2.2",
441 + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
442 + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="
443 + },
444 + "is-regex": {
445 + "version": "1.1.0",
446 + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz",
447 + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==",
448 + "requires": {
449 + "has-symbols": "^1.0.1"
450 + }
451 + },
452 + "js-stringify": {
453 + "version": "1.0.2",
454 + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
455 + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds="
456 + },
457 + "jstransformer": {
458 + "version": "1.0.0",
459 + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz",
460 + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=",
461 + "requires": {
462 + "is-promise": "^2.0.0",
463 + "promise": "^7.0.1"
464 + }
465 + },
466 + "kind-of": {
467 + "version": "3.2.2",
468 + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
469 + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
470 + "requires": {
471 + "is-buffer": "^1.1.5"
472 + }
473 + },
474 + "lazy-cache": {
475 + "version": "1.0.4",
476 + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
477 + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
478 + },
479 + "lodash": {
480 + "version": "4.17.15",
481 + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
482 + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
483 + },
484 + "longest": {
485 + "version": "1.0.1",
486 + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
487 + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
488 + },
489 + "media-typer": {
490 + "version": "0.3.0",
491 + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
492 + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
493 + },
494 + "merge-descriptors": {
495 + "version": "1.0.1",
496 + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
497 + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
498 + },
499 + "methods": {
500 + "version": "1.1.2",
501 + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
502 + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
503 + },
504 + "mime": {
505 + "version": "1.4.1",
506 + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
507 + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
508 + },
509 + "mime-db": {
510 + "version": "1.44.0",
511 + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
512 + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
513 + },
514 + "mime-types": {
515 + "version": "2.1.27",
516 + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
517 + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
518 + "requires": {
519 + "mime-db": "1.44.0"
520 + }
521 + },
522 + "morgan": {
523 + "version": "1.9.1",
524 + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
525 + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
526 + "requires": {
527 + "basic-auth": "~2.0.0",
528 + "debug": "2.6.9",
529 + "depd": "~1.1.2",
530 + "on-finished": "~2.3.0",
531 + "on-headers": "~1.0.1"
532 + }
533 + },
534 + "ms": {
535 + "version": "2.0.0",
536 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
537 + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
538 + },
539 + "negotiator": {
540 + "version": "0.6.2",
541 + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
542 + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
543 + },
544 + "object-assign": {
545 + "version": "4.1.1",
546 + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
547 + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
548 + },
549 + "on-finished": {
550 + "version": "2.3.0",
551 + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
552 + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
553 + "requires": {
554 + "ee-first": "1.1.1"
555 + }
556 + },
557 + "on-headers": {
558 + "version": "1.0.2",
559 + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
560 + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
561 + },
562 + "parseurl": {
563 + "version": "1.3.3",
564 + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
565 + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
566 + },
567 + "path-parse": {
568 + "version": "1.0.6",
569 + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
570 + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
571 + },
572 + "path-to-regexp": {
573 + "version": "0.1.7",
574 + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
575 + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
576 + },
577 + "promise": {
578 + "version": "7.3.1",
579 + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
580 + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
581 + "requires": {
582 + "asap": "~2.0.3"
583 + }
584 + },
585 + "proxy-addr": {
586 + "version": "2.0.6",
587 + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
588 + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
589 + "requires": {
590 + "forwarded": "~0.1.2",
591 + "ipaddr.js": "1.9.1"
592 + }
593 + },
594 + "pug": {
595 + "version": "2.0.0-beta11",
596 + "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-beta11.tgz",
597 + "integrity": "sha1-Favmr1AEx+LPRhPksnRlyVRrXwE=",
598 + "requires": {
599 + "pug-code-gen": "^1.1.1",
600 + "pug-filters": "^2.1.1",
601 + "pug-lexer": "^3.0.0",
602 + "pug-linker": "^2.0.2",
603 + "pug-load": "^2.0.5",
604 + "pug-parser": "^2.0.2",
605 + "pug-runtime": "^2.0.3",
606 + "pug-strip-comments": "^1.0.2"
607 + }
608 + },
609 + "pug-attrs": {
610 + "version": "2.0.4",
611 + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz",
612 + "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==",
613 + "requires": {
614 + "constantinople": "^3.0.1",
615 + "js-stringify": "^1.0.1",
616 + "pug-runtime": "^2.0.5"
617 + }
618 + },
619 + "pug-code-gen": {
620 + "version": "1.1.1",
621 + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-1.1.1.tgz",
622 + "integrity": "sha1-HPcnRO8qA56uajNAyqoRBYcSWOg=",
623 + "requires": {
624 + "constantinople": "^3.0.1",
625 + "doctypes": "^1.1.0",
626 + "js-stringify": "^1.0.1",
627 + "pug-attrs": "^2.0.2",
628 + "pug-error": "^1.3.2",
629 + "pug-runtime": "^2.0.3",
630 + "void-elements": "^2.0.1",
631 + "with": "^5.0.0"
632 + }
633 + },
634 + "pug-error": {
635 + "version": "1.3.3",
636 + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz",
637 + "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ=="
638 + },
639 + "pug-filters": {
640 + "version": "2.1.5",
641 + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.5.tgz",
642 + "integrity": "sha512-xkw71KtrC4sxleKiq+cUlQzsiLn8pM5+vCgkChW2E6oNOzaqTSIBKIQ5cl4oheuDzvJYCTSYzRaVinMUrV4YLQ==",
643 + "requires": {
644 + "clean-css": "^3.3.0",
645 + "constantinople": "^3.0.1",
646 + "jstransformer": "1.0.0",
647 + "pug-error": "^1.3.2",
648 + "pug-walk": "^1.1.5",
649 + "resolve": "^1.1.6",
650 + "uglify-js": "^2.6.1"
651 + }
652 + },
653 + "pug-lexer": {
654 + "version": "3.1.0",
655 + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-3.1.0.tgz",
656 + "integrity": "sha1-/QhzdtSmdbT1n4/vQiiDQ06VgaI=",
657 + "requires": {
658 + "character-parser": "^2.1.1",
659 + "is-expression": "^3.0.0",
660 + "pug-error": "^1.3.2"
661 + }
662 + },
663 + "pug-linker": {
664 + "version": "2.0.3",
665 + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-2.0.3.tgz",
666 + "integrity": "sha1-szH/olc33eacEntWwQ/xf652bco=",
667 + "requires": {
668 + "pug-error": "^1.3.2",
669 + "pug-walk": "^1.1.2"
670 + }
671 + },
672 + "pug-load": {
673 + "version": "2.0.12",
674 + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz",
675 + "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==",
676 + "requires": {
677 + "object-assign": "^4.1.0",
678 + "pug-walk": "^1.1.8"
679 + }
680 + },
681 + "pug-parser": {
682 + "version": "2.0.2",
683 + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-2.0.2.tgz",
684 + "integrity": "sha1-U6aAz9BQOdywwn0CkJS8SnkmibA=",
685 + "requires": {
686 + "pug-error": "^1.3.2",
687 + "token-stream": "0.0.1"
688 + }
689 + },
690 + "pug-runtime": {
691 + "version": "2.0.5",
692 + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz",
693 + "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw=="
694 + },
695 + "pug-strip-comments": {
696 + "version": "1.0.4",
697 + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz",
698 + "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==",
699 + "requires": {
700 + "pug-error": "^1.3.3"
701 + }
702 + },
703 + "pug-walk": {
704 + "version": "1.1.8",
705 + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz",
706 + "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA=="
707 + },
708 + "qs": {
709 + "version": "6.5.2",
710 + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
711 + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
712 + },
713 + "range-parser": {
714 + "version": "1.2.1",
715 + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
716 + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
717 + },
718 + "raw-body": {
719 + "version": "2.3.3",
720 + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
721 + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
722 + "requires": {
723 + "bytes": "3.0.0",
724 + "http-errors": "1.6.3",
725 + "iconv-lite": "0.4.23",
726 + "unpipe": "1.0.0"
727 + }
728 + },
729 + "regenerator-runtime": {
730 + "version": "0.11.1",
731 + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
732 + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
733 + },
734 + "repeat-string": {
735 + "version": "1.6.1",
736 + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
737 + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
738 + },
739 + "resolve": {
740 + "version": "1.17.0",
741 + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
742 + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
743 + "requires": {
744 + "path-parse": "^1.0.6"
745 + }
746 + },
747 + "right-align": {
748 + "version": "0.1.3",
749 + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
750 + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
751 + "requires": {
752 + "align-text": "^0.1.1"
753 + }
754 + },
755 + "safe-buffer": {
756 + "version": "5.1.2",
757 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
758 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
759 + },
760 + "safer-buffer": {
761 + "version": "2.1.2",
762 + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
763 + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
764 + },
765 + "send": {
766 + "version": "0.16.2",
767 + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
768 + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
769 + "requires": {
770 + "debug": "2.6.9",
771 + "depd": "~1.1.2",
772 + "destroy": "~1.0.4",
773 + "encodeurl": "~1.0.2",
774 + "escape-html": "~1.0.3",
775 + "etag": "~1.8.1",
776 + "fresh": "0.5.2",
777 + "http-errors": "~1.6.2",
778 + "mime": "1.4.1",
779 + "ms": "2.0.0",
780 + "on-finished": "~2.3.0",
781 + "range-parser": "~1.2.0",
782 + "statuses": "~1.4.0"
783 + }
784 + },
785 + "serve-static": {
786 + "version": "1.13.2",
787 + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
788 + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
789 + "requires": {
790 + "encodeurl": "~1.0.2",
791 + "escape-html": "~1.0.3",
792 + "parseurl": "~1.3.2",
793 + "send": "0.16.2"
794 + }
795 + },
796 + "setprototypeof": {
797 + "version": "1.1.0",
798 + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
799 + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
800 + },
801 + "source-map": {
802 + "version": "0.4.4",
803 + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
804 + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
805 + "requires": {
806 + "amdefine": ">=0.0.4"
807 + }
808 + },
809 + "statuses": {
810 + "version": "1.4.0",
811 + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
812 + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
813 + },
814 + "to-fast-properties": {
815 + "version": "1.0.3",
816 + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
817 + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc="
818 + },
819 + "token-stream": {
820 + "version": "0.0.1",
821 + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz",
822 + "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo="
823 + },
824 + "type-is": {
825 + "version": "1.6.18",
826 + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
827 + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
828 + "requires": {
829 + "media-typer": "0.3.0",
830 + "mime-types": "~2.1.24"
831 + }
832 + },
833 + "uglify-js": {
834 + "version": "2.8.29",
835 + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
836 + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
837 + "requires": {
838 + "source-map": "~0.5.1",
839 + "uglify-to-browserify": "~1.0.0",
840 + "yargs": "~3.10.0"
841 + },
842 + "dependencies": {
843 + "source-map": {
844 + "version": "0.5.7",
845 + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
846 + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
847 + }
848 + }
849 + },
850 + "uglify-to-browserify": {
851 + "version": "1.0.2",
852 + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
853 + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
854 + "optional": true
855 + },
856 + "unpipe": {
857 + "version": "1.0.0",
858 + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
859 + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
860 + },
861 + "utils-merge": {
862 + "version": "1.0.1",
863 + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
864 + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
865 + },
866 + "vary": {
867 + "version": "1.1.2",
868 + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
869 + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
870 + },
871 + "void-elements": {
872 + "version": "2.0.1",
873 + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
874 + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
875 + },
876 + "window-size": {
877 + "version": "0.1.0",
878 + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
879 + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0="
880 + },
881 + "with": {
882 + "version": "5.1.1",
883 + "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz",
884 + "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=",
885 + "requires": {
886 + "acorn": "^3.1.0",
887 + "acorn-globals": "^3.0.0"
888 + }
889 + },
890 + "wordwrap": {
891 + "version": "0.0.2",
892 + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
893 + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
894 + },
895 + "yargs": {
896 + "version": "3.10.0",
897 + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
898 + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
899 + "requires": {
900 + "camelcase": "^1.0.2",
901 + "cliui": "^2.1.0",
902 + "decamelize": "^1.0.0",
903 + "window-size": "0.1.0"
904 + }
905 + }
906 + }
907 +}
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
6 "start": "node ./bin/www" 6 "start": "node ./bin/www"
7 }, 7 },
8 "dependencies": { 8 "dependencies": {
9 + "axios": "^0.19.2",
9 "cookie-parser": "~1.4.3", 10 "cookie-parser": "~1.4.3",
11 + "cors": "^2.8.5",
10 "debug": "~2.6.9", 12 "debug": "~2.6.9",
11 "express": "~4.16.0", 13 "express": "~4.16.0",
12 "http-errors": "~1.6.2", 14 "http-errors": "~1.6.2",
......
1 +var express = require("express");
2 +var router = express.Router();
3 +var axios = require("axios");
4 +
5 +const openAPIKey = require("./secrets.json").openAPIKey;
6 +const googleMapKey = require("./secrets.json").googleAPIKey;
7 +const weatherAPIKey = require("./secrets.json").weatherAPIKey;
8 +
9 +axios.create({
10 + // TODO : 웹을 AWS에 올릴때, 해당 baseURL이 달라져야할 수 있음
11 + baseURL: "http://localhost:3001",
12 + responseType: "json",
13 +});
14 +
15 +/* GET airCondition listing. */
16 +router.get("/", async function (req, res, next) {
17 + console.log("경도:", req.query.latitude);
18 + console.log("경도:", req.query.longitude);
19 + let airCondition = "";
20 + let response = await getPosition(req.query.latitude, req.query.longitude)
21 + .then((encodedStation) => getCondition(encodedStation))
22 + .then((result) => {
23 + airCondition = result;
24 + });
25 +
26 + res.send(airCondition);
27 +});
28 +
29 +router.get("/weather", async function (req, res, next) {
30 + console.log("경도:", req.query.latitude);
31 + console.log("경도:", req.query.longitude);
32 +
33 + let airCondition = "";
34 + let response = await getEnglishPosition(
35 + req.query.latitude,
36 + req.query.longitude
37 + )
38 + .then((encodedStation) => getWeather(encodedStation))
39 + .then((result) => {
40 + airCondition = result;
41 + });
42 +
43 + res.send(airCondition);
44 +});
45 +
46 +const getWeather = (encodedStation) => {
47 + return axios
48 + .get(
49 + "https://api.openweathermap.org/data/2.5/weather?q=" +
50 + encodedStation +
51 + "&appid=" +
52 + weatherAPIKey
53 + )
54 + .then(function (response) {
55 + return response["data"];
56 + })
57 + .catch(function (error) {
58 + console.log(error.response);
59 + });
60 +};
61 +
62 +const getPosition = (lat, lon) => {
63 + return axios
64 + .get(
65 + "https://maps.googleapis.com/maps/api/geocode/json?latlng=" +
66 + lat +
67 + "," +
68 + lon +
69 + "&location_type=ROOFTOP&result_type=street_address&key=" +
70 + googleMapKey +
71 + "&language=ko"
72 + )
73 + .then(function (response) {
74 + console.log("KEY : ", googleMapKey);
75 + let stationName = "";
76 + for (
77 + let i = 0;
78 + i < response["data"].results[0]["address_components"].length;
79 + i++
80 + ) {
81 + let temp =
82 + response["data"].results[0]["address_components"][i]["long_name"];
83 + if (temp[temp.length - 1] == "구") {
84 + stationName = temp;
85 + break;
86 + }
87 + }
88 + console.log("STATION : ", stationName);
89 + return (encodedStation = encodeURI(stationName));
90 + })
91 + .catch(function (error) {
92 + console.log(error.response);
93 + });
94 +};
95 +
96 +const getEnglishPosition = (lat, lon) => {
97 + return axios
98 + .get(
99 + "https://maps.googleapis.com/maps/api/geocode/json?latlng=" +
100 + lat +
101 + "," +
102 + lon +
103 + "&location_type=ROOFTOP&result_type=street_address&key=" +
104 + googleMapKey +
105 + "&language=en"
106 + )
107 + .then(function (response) {
108 + let stationName =
109 + response["data"].results[0]["address_components"][3]["long_name"];
110 + console.log("STATION : ", stationName);
111 + return (encodedStation = encodeURI(stationName));
112 + })
113 + .catch(function (error) {
114 + console.log(error.response);
115 + });
116 +};
117 +
118 +/* GET route airCondition listing. */
119 +router.get("/route", async function (req, res, next) {
120 + console.log("출발지:", req.query.departure);
121 + console.log("도착지:", req.query.arrival);
122 +
123 + let dep = JSON.parse(req.query.departure);
124 + let depLat = dep["Ha"];
125 + let depLon = dep["Ga"];
126 +
127 + let arr = JSON.parse(req.query.arrival);
128 + let arrLat = arr["Ha"];
129 + let arrLon = arr["Ga"];
130 + let airCondition = "";
131 +
132 + let response = await getRoute(depLat, depLon, arrLat, arrLon)
133 + .then((routeInformation) =>
134 + routeAirCondition(depLat, depLon, routeInformation)
135 + )
136 + .then((routeInformation) => {
137 + airCondition = routeInformation;
138 + });
139 +
140 + res.send(airCondition);
141 +});
142 +
143 +const getCondition = (encodedStation) => {
144 + return axios
145 + .get(
146 + "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty?serviceKey=" +
147 + openAPIKey +
148 + "&numOfRows=10&pageNo=1&stationName=" +
149 + encodedStation +
150 + "&dataTerm=DAILY&ver=1.3&_returnType=json"
151 + )
152 + .then(function (response) {
153 + // console.log("RES :: ", response);
154 + result = response["data"]["list"][0];
155 + return result;
156 + })
157 + .catch(function (error) {
158 + console.log(error.response);
159 + });
160 +};
161 +
162 +const getRoute = (depLat, depLon, arrLat, arrLon) => {
163 + return axios
164 + .get(
165 + "https://maps.googleapis.com/maps/api/directions/json?origin=" +
166 + depLat +
167 + "," +
168 + depLon +
169 + "&destination=" +
170 + arrLat +
171 + "," +
172 + arrLon +
173 + "&mode=transit&departure_time=now&key=" +
174 + googleMapKey +
175 + "&language=ko"
176 + )
177 + .then(function (response) {
178 + console.log(response["data"]);
179 + let routeInformation = [];
180 + for (
181 + let i = 0;
182 + i < response["data"].routes[0]["legs"][0]["steps"].length;
183 + i++
184 + ) {
185 + let info = {};
186 + info["instruction"] =
187 + response["data"].routes[0]["legs"][0]["steps"][i][
188 + "html_instructions"
189 + ];
190 + info["location"] =
191 + response["data"].routes[0]["legs"][0]["steps"][i]["end_location"];
192 + info["duration"] =
193 + response["data"].routes[0]["legs"][0]["steps"][i]["duration"]["text"];
194 + info["travel_mode"] =
195 + response["data"].routes[0]["legs"][0]["steps"][i]["travel_mode"];
196 + routeInformation.push(info);
197 + }
198 + // console.log(routeInformation);
199 + return routeInformation;
200 + })
201 + .catch(function (error) {
202 + console.log(error.response);
203 + });
204 +};
205 +
206 +const routeAirCondition = async (depLat, depLon, routeInformation) => {
207 + await getPosition(depLat, depLon)
208 + .then((encodedStation) => getCondition(encodedStation))
209 + .then((result) => {
210 + let info = {};
211 + info["airCondition"] = result;
212 + routeInformation.push(info);
213 + });
214 + for (let i = 0; i < routeInformation.length - 1; i++) {
215 + await getPosition(
216 + routeInformation[i]["location"]["lat"],
217 + routeInformation[i]["location"]["lng"]
218 + )
219 + .then((encodedStation) => getCondition(encodedStation))
220 + .then((result) => {
221 + routeInformation[i]["airCondition"] = result;
222 + });
223 + }
224 + console.log(routeInformation);
225 + return routeInformation;
226 +};
227 +
228 +module.exports = router;
1 -var express = require('express'); 1 +var express = require("express");
2 var router = express.Router(); 2 var router = express.Router();
3 3
4 /* GET home page. */ 4 /* GET home page. */
5 -router.get('/', function(req, res, next) { 5 +router.get("/", function (req, res, next) {
6 - res.render('index', { title: 'Express' }); 6 + res.render("index", { title: "Express" });
7 }); 7 });
8 8
9 module.exports = router; 9 module.exports = router;
......
...@@ -3,3 +3,4 @@ extends layout ...@@ -3,3 +3,4 @@ extends layout
3 block content 3 block content
4 h1= title 4 h1= title
5 p Welcome to #{title} 5 p Welcome to #{title}
6 + p This is HowsTheWeather Backend
......
This diff could not be displayed because it is too large.
...@@ -6,8 +6,14 @@ ...@@ -6,8 +6,14 @@
6 "@testing-library/jest-dom": "^4.2.4", 6 "@testing-library/jest-dom": "^4.2.4",
7 "@testing-library/react": "^9.3.2", 7 "@testing-library/react": "^9.3.2",
8 "@testing-library/user-event": "^7.1.2", 8 "@testing-library/user-event": "^7.1.2",
9 + "axios": "^0.19.2",
10 + "bootstrap": "^3.4.1",
11 + "daum-map-api": "^1.0.2",
9 "react": "^16.13.1", 12 "react": "^16.13.1",
13 + "react-bootstrap": "^1.0.1",
10 "react-dom": "^16.13.1", 14 "react-dom": "^16.13.1",
15 + "react-kakao-maps": "0.0.13",
16 + "react-router-dom": "^5.2.0",
11 "react-scripts": "3.4.1" 17 "react-scripts": "3.4.1"
12 }, 18 },
13 "scripts": { 19 "scripts": {
......
...@@ -10,6 +10,16 @@ ...@@ -10,6 +10,16 @@
10 content="Web site created using create-react-app" 10 content="Web site created using create-react-app"
11 /> 11 />
12 <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> 12 <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13 + <script
14 + type="text/javascript"
15 + src="//dapi.kakao.com/v2/maps/sdk.js?appkey=61abec34d0855ba1d434ea222263d4a5&libraries=services,clusterer,drawing"
16 + ></script>
17 + <link
18 + rel="stylesheet"
19 + href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
20 + integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
21 + crossorigin="anonymous"
22 + />
13 <!-- 23 <!--
14 manifest.json provides metadata used when your web app is installed on a 24 manifest.json provides metadata used when your web app is installed on a
15 user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ 25 user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
...@@ -24,7 +34,7 @@ ...@@ -24,7 +34,7 @@
24 work correctly both with client-side routing and a non-root public URL. 34 work correctly both with client-side routing and a non-root public URL.
25 Learn how to configure a non-root public URL by running `npm run build`. 35 Learn how to configure a non-root public URL by running `npm run build`.
26 --> 36 -->
27 - <title>React App</title> 37 + <title>HowsTheWeather</title>
28 </head> 38 </head>
29 <body> 39 <body>
30 <noscript>You need to enable JavaScript to run this app.</noscript> 40 <noscript>You need to enable JavaScript to run this app.</noscript>
......
1 +import axios from "axios";
2 +
3 +export default axios.create({
4 + baseURL: "http://localhost:3001",
5 + responseType: "json",
6 +});
...@@ -15,13 +15,14 @@ ...@@ -15,13 +15,14 @@
15 15
16 .App-header { 16 .App-header {
17 background-color: #282c34; 17 background-color: #282c34;
18 - min-height: 100vh; 18 + min-height: 13vh;
19 display: flex; 19 display: flex;
20 flex-direction: column; 20 flex-direction: column;
21 align-items: center; 21 align-items: center;
22 justify-content: center; 22 justify-content: center;
23 font-size: calc(10px + 2vmin); 23 font-size: calc(10px + 2vmin);
24 color: white; 24 color: white;
25 + font-weight: bold;
25 } 26 }
26 27
27 .App-link { 28 .App-link {
......
1 -import React from 'react'; 1 +import React, { Component } from "react";
2 -import logo from './logo.svg'; 2 +import { HashRouter, Route } from "react-router-dom";
3 -import './App.css'; 3 +import ScrollToTop from "./ScrollToTop";
4 +import Home from "./home";
4 5
5 -function App() { 6 +class App extends Component {
7 + constructor(props) {
8 + super(props);
9 + }
10 + render() {
6 return ( 11 return (
12 + <HashRouter basename={process.env.PUBLIC_URL}>
13 + <ScrollToTop>
7 <div className="App"> 14 <div className="App">
8 - <header className="App-header"> 15 + <Route exact path="/" component={Home} />
9 - <img src={logo} className="App-logo" alt="logo" />
10 - <p>
11 - Edit <code>src/App.js</code> and save to reload.
12 - </p>
13 - <a
14 - className="App-link"
15 - href="https://reactjs.org"
16 - target="_blank"
17 - rel="noopener noreferrer"
18 - >
19 - Learn React
20 - </a>
21 - </header>
22 </div> 16 </div>
17 + </ScrollToTop>
18 + </HashRouter>
23 ); 19 );
20 + }
24 } 21 }
25 22
26 export default App; 23 export default App;
......
1 +import { Component } from "react";
2 +import { withRouter } from "react-router-dom";
3 +
4 +class ScrollToTop extends Component {
5 + componentDidUpdate(prevProps) {
6 + if (this.props.location !== prevProps.location) {
7 + window.scrollTo(0, 0);
8 + }
9 + }
10 +
11 + render() {
12 + return this.props.children;
13 + }
14 +}
15 +
16 +export default withRouter(ScrollToTop);
1 +.Home {
2 + text-align: center;
3 +}
4 +
5 +.Home-logo {
6 + height: 40vmin;
7 + pointer-events: none;
8 +}
9 +
10 +@media (prefers-reduced-motion: no-preference) {
11 + .Home-logo {
12 + animation: Home-logo-spin infinite 20s linear;
13 + }
14 +}
15 +
16 +.Home-header {
17 + background-color: #282c34;
18 + min-height: 13vh;
19 + display: flex;
20 + flex-direction: column;
21 + align-items: center;
22 + justify-content: center;
23 + font-size: calc(10px + 2vmin);
24 + color: white;
25 + font-weight: bold;
26 +}
27 +
28 +.Home-link {
29 + color: #61dafb;
30 +}
31 +
32 +@keyframes Home-logo-spin {
33 + from {
34 + transform: rotate(0deg);
35 + }
36 + to {
37 + transform: rotate(360deg);
38 + }
39 +}
40 +
41 +.strong {
42 + font-size: calc(10px + 1vmin);
43 + font-weight: bold;
44 +}
45 +.map_wrap,
46 +.map_wrap * {
47 + margin: 0;
48 + padding: 0;
49 + font-family: "Malgun Gothic", dotum, "돋움", sans-serif;
50 + font-size: 12px;
51 +}
52 +.map_wrap a,
53 +.map_wrap a:hover,
54 +.map_wrap a:active {
55 + color: #000;
56 + text-decoration: none;
57 +}
58 +.map_wrap {
59 + position: relative;
60 + width: 100%;
61 + height: 500px;
62 +}
63 +
64 +#menu_wrap {
65 + position: absolute;
66 + top: 0;
67 + left: 0;
68 + bottom: 0;
69 + width: 250px;
70 + margin: 10px 0 30px 10px;
71 + padding: 5px;
72 + overflow-y: auto;
73 + background: rgba(53, 53, 53, 0.8);
74 + z-index: 1;
75 + font-size: 12px;
76 + border-radius: 10px;
77 +}
78 +.bg_white {
79 + background: #fff;
80 +}
81 +x #menu_wrap hr {
82 + display: block;
83 + height: 1px;
84 + border: 0;
85 + border-top: 2px solid #5f5f5f;
86 + margin: 3px 0;
87 +}
88 +#menu_wrap .option {
89 + text-align: center;
90 + color: #ffffff;
91 + font-weight: bold;
92 +}
93 +#menu_wrap .option p {
94 + margin: 10px 0;
95 +}
96 +#menu_wrap .option button {
97 + margin-left: 5px;
98 +}
99 +
100 +#placesList li {
101 + list-style: none;
102 +}
103 +#placesList .item {
104 + position: relative;
105 + border-bottom: 1px solid #888;
106 + overflow: hidden;
107 + cursor: pointer;
108 + min-height: 65px;
109 +}
110 +#placesList .item span {
111 + display: block;
112 + margin-top: 4px;
113 +}
114 +#placesList .item h5,
115 +#placesList .item .info {
116 + text-overflow: ellipsis;
117 + overflow: hidden;
118 + white-space: nowrap;
119 +}
120 +#placesList .item .info {
121 + padding: 10px 0 10px 55px;
122 + color: #ffffff;
123 +}
124 +#placesList .info .gray {
125 + color: #eaeaea;
126 +}
127 +#placesList .info .jibun {
128 + padding-left: 26px;
129 + background: url(http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/places_jibun.png)
130 + no-repeat;
131 +}
132 +#placesList .info .tel {
133 + color: #ffbb00;
134 + font-weight: bold;
135 +}
136 +#placesList .item .markerbg {
137 + float: left;
138 + position: absolute;
139 + width: 36px;
140 + height: 37px;
141 + margin: 10px 0 0 10px;
142 + background: url(http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png)
143 + no-repeat;
144 +}
145 +#placesList .item .marker_1 {
146 + background-position: 0 -10px;
147 +}
148 +#placesList .item .marker_2 {
149 + background-position: 0 -56px;
150 +}
151 +#placesList .item .marker_3 {
152 + background-position: 0 -102px;
153 +}
154 +#placesList .item .marker_4 {
155 + background-position: 0 -148px;
156 +}
157 +#placesList .item .marker_5 {
158 + background-position: 0 -194px;
159 +}
160 +#placesList .item .marker_6 {
161 + background-position: 0 -240px;
162 +}
163 +#placesList .item .marker_7 {
164 + background-position: 0 -286px;
165 +}
166 +#placesList .item .marker_8 {
167 + background-position: 0 -332px;
168 +}
169 +#placesList .item .marker_9 {
170 + background-position: 0 -378px;
171 +}
172 +#placesList .item .marker_10 {
173 + background-position: 0 -423px;
174 +}
175 +#placesList .item .marker_11 {
176 + background-position: 0 -470px;
177 +}
178 +#placesList .item .marker_12 {
179 + background-position: 0 -516px;
180 +}
181 +#placesList .item .marker_13 {
182 + background-position: 0 -562px;
183 +}
184 +#placesList .item .marker_14 {
185 + background-position: 0 -608px;
186 +}
187 +#placesList .item .marker_15 {
188 + background-position: 0 -654px;
189 +}
190 +#pagination {
191 + margin: 10px auto;
192 + text-align: center;
193 +}
194 +#pagination a {
195 + display: inline-block;
196 + margin-right: 10px;
197 +}
198 +#pagination .on {
199 + font-weight: bold;
200 + cursor: default;
201 + color: #777;
202 +}
203 +
204 +.wraps {
205 + position: absolute;
206 + left: 0;
207 + bottom: 40px;
208 + width: 288px;
209 + height: 132px;
210 + margin-left: -144px;
211 + text-align: left;
212 + overflow: hidden;
213 + font-size: 12px;
214 + font-family: "Malgun Gothic", dotum, "돋움", sans-serif;
215 + line-height: 1.5;
216 +}
217 +.wraps * {
218 + padding: 0;
219 + margin: 0;
220 +}
221 +.wraps .infos {
222 + width: 286px;
223 + height: 120px;
224 + border-radius: 5px;
225 + border-bottom: 2px solid #ccc;
226 + border-right: 1px solid #ccc;
227 + overflow: hidden;
228 + background: #fff;
229 +}
230 +.wraps .infos:nth-child(1) {
231 + border: 0;
232 + box-shadow: 0px 1px 2px #888;
233 +}
234 +.infos .title {
235 + padding: 5px 0 0 10px;
236 + height: 30px;
237 + background: #eee;
238 + border-bottom: 1px solid #ddd;
239 + font-size: 18px;
240 + font-weight: bold;
241 +}
242 +.infos .close {
243 + position: absolute;
244 + top: 10px;
245 + right: 10px;
246 + color: #888;
247 + width: 17px;
248 + height: 17px;
249 + background: url("http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/overlay_close.png");
250 +}
251 +.infos .close:hover {
252 + cursor: pointer;
253 +}
254 +.infos .body {
255 + position: relative;
256 + overflow: hidden;
257 +}
258 +.infos .desc {
259 + position: relative;
260 + margin: 13px 0 0 90px;
261 + height: 75px;
262 +}
263 +.desc .ellipsis {
264 + overflow: hidden;
265 + text-overflow: ellipsis;
266 + white-space: nowrap;
267 +}
268 +.desc .jibun {
269 + font-size: 11px;
270 + color: #888;
271 + margin-top: -2px;
272 +}
273 +.infos .img {
274 + position: absolute;
275 + top: 6px;
276 + left: 5px;
277 + width: 73px;
278 + height: 71px;
279 + border: 1px solid #ddd;
280 + color: #888;
281 + overflow: hidden;
282 +}
283 +.infos:after {
284 + content: "";
285 + position: absolute;
286 + margin-left: -12px;
287 + left: 50%;
288 + bottom: 0;
289 + width: 22px;
290 + height: 12px;
291 + background: url("http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/vertex_white.png");
292 +}
293 +.infos .link {
294 + color: #5085bb;
295 +}
296 +
297 +#footer {
298 + position: absolute;
299 + bottom: 0;
300 + width: 100%;
301 + height: 100px;
302 + background: #ccc;
303 +}
304 +
305 +.left-box {
306 + float: left;
307 + width: 20%;
308 +}
309 +.left-box h5 {
310 + font-size: 1rem;
311 + font-style: italic;
312 + padding-left: 10px;
313 +}
314 +.right-box {
315 + float: right;
316 + width: 80%;
317 +}
318 +.right-box h5 {
319 + font-size: 1rem;
320 + font-style: italic;
321 + padding-left: 10px;
322 +}
323 +#middle {
324 + text-align: center;
325 +}
326 +.info-button {
327 + border-radius: 4px;
328 + background-color: #4641d9;
329 + width: 100px;
330 + color: white;
331 +}
1 +import React, { Component } from "react";
2 +import logo from "./logo.svg";
3 +import "./App.css";
4 +import Button from "react-bootstrap/Button";
5 +import "./home.css";
6 +import { Map, Marker, MarkerClusterer, Polyline } from "react-kakao-maps";
7 +import "bootstrap/dist/css/bootstrap.min.css";
8 +import API from "./API";
9 +import veryGood from "./gradeIcon/cool.png";
10 +import good from "./gradeIcon/smile.png";
11 +import bad from "./gradeIcon/angry.png";
12 +import veryBad from "./gradeIcon/devil.png";
13 +import next from "./gradeIcon/next.png";
14 +import start from "./gradeIcon/start.png";
15 +import finish from "./gradeIcon/finish.png";
16 +import loading from "./gradeIcon/loading.gif";
17 +/* global kakao */
18 +
19 +export default class Home extends Component {
20 + constructor(props) {
21 + super(props);
22 + this.state = {
23 + map: null,
24 + markers: [],
25 + markers: [],
26 + curMarker: new kakao.maps.Marker({
27 + position: new kakao.maps.LatLng(37.503716, 127.044844),
28 + }),
29 + placeSearch: new kakao.maps.services.Places(),
30 + infoWindow: new kakao.maps.CustomOverlay({}),
31 + region: "은평구청",
32 + curAirCondition: null,
33 + routeInformation: null,
34 + temperature: null,
35 + humidity: null,
36 + weather: null,
37 + icon: null,
38 + wind: null,
39 + cloud: null,
40 + };
41 + }
42 +
43 + searchPlaces = () => {
44 + var keyword = document.getElementById("keyword").value;
45 +
46 + if (!keyword.replace(/^\s+|\s+$/g, "")) {
47 + alert("키워드를 입력해주세요!");
48 + return false;
49 + }
50 +
51 + // 장소검색 객체를 통해 키워드로 장소검색을 요청합니다
52 + this.state.placeSearch.keywordSearch(
53 + keyword,
54 + this.placesSearchCB.bind(this)
55 + );
56 + };
57 +
58 + // 장소검색이 완료됐을 때 호출되는 콜백함수 입니다
59 + placesSearchCB = (data, status, pagination) => {
60 + if (status === kakao.maps.services.Status.OK) {
61 + // 정상적으로 검색이 완료됐으면
62 + // 검색 목록과 마커를 표출합니다
63 + this.displayPlaces(data);
64 +
65 + // 페이지 번호를 표출합니다
66 + this.displayPagination(pagination);
67 + } else if (status === kakao.maps.services.Status.ZERO_RESULT) {
68 + alert("검색 결과가 존재하지 않습니다.");
69 + return;
70 + } else if (status === kakao.maps.services.Status.ERROR) {
71 + alert("검색 결과 중 오류가 발생했습니다.");
72 + return;
73 + }
74 + };
75 +
76 + displayPlaces = (places) => {
77 + var listEl = document.getElementById("placesList"),
78 + menuEl = document.getElementById("menu_wrap"),
79 + fragment = document.createDocumentFragment(),
80 + bounds = new kakao.maps.LatLngBounds(),
81 + listStr = "";
82 +
83 + // 검색 결과 목록에 추가된 항목들을 제거합니다
84 + // 페이지별로 보여주기 때문에 추가한 것 같음.
85 + this.removeAllChildNods(listEl);
86 +
87 + // 지도에 표시되고 있는 마커를 제거합니다
88 + this.removeMarker();
89 +
90 + for (var i = 0; i < places.length; i++) {
91 + // 마커를 생성하고 지도에 표시합니다
92 + var placePosition = new kakao.maps.LatLng(places[i].y, places[i].x),
93 + marker = this.addMarker(placePosition, i),
94 + itemEl = this.getListItem(i, places[i]); // 검색 결과 항목 Element를 생성합니다
95 +
96 + // 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해
97 + // LatLngBounds 객체에 좌표를 추가합니다
98 + bounds.extend(placePosition);
99 +
100 + // 마커와 검색결과 항목에 mouseover 했을때
101 + // 해당 장소에 인포윈도우에 장소명을 표시합니다
102 + // mouseout 했을 때는 인포윈도우를 닫습니다
103 + ((marker, title) => {
104 + kakao.maps.event.addListener(marker, "click", () => {
105 + this.displayInfowindow(marker, title);
106 + });
107 + kakao.maps.event.addListener(marker, "mouseover", () => {
108 + this.displayInfowindow(marker, title);
109 + });
110 +
111 + itemEl.onmouseover = () => {
112 + this.displayInfowindow(marker, title);
113 + };
114 + })(marker, places[i].place_name);
115 + // 함수를 조건문처럼 사용하여 해당 함수가 true일때 marker,places[i].place_name함수가 불리워짐.
116 +
117 + fragment.appendChild(itemEl);
118 + }
119 +
120 + // 검색결과 항목들을 검색결과 목록 Elemnet에 추가합니다
121 + listEl.appendChild(fragment);
122 + menuEl.scrollTop = 0;
123 +
124 + // 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
125 + this.state.map.setBounds(bounds);
126 + };
127 + // 커스텀 오버레이를 닫기 위해 호출되는 함수입니다
128 + closeOverlay = () => {
129 + this.state.infoWindow.setMap(null);
130 + };
131 +
132 + getListItem = (index, places) => {
133 + var el = document.createElement("li"),
134 + itemStr =
135 + '<span class="markerbg marker_' +
136 + (index + 1) +
137 + '"></span>' +
138 + '<div class="info">' +
139 + " <h5>" +
140 + places.place_name +
141 + "</h5>";
142 +
143 + if (places.road_address_name) {
144 + itemStr +=
145 + " <span>" +
146 + places.road_address_name +
147 + "</span>" +
148 + ' <span class="jibun gray">' +
149 + places.address_name +
150 + "</span>";
151 + } else {
152 + itemStr += " <span>" + places.address_name + "</span>";
153 + }
154 +
155 + itemStr += ' <span class="tel">' + places.phone + "</span>" + "</div>";
156 +
157 + el.innerHTML = itemStr;
158 + el.className = "item";
159 +
160 + return el;
161 + };
162 +
163 + getRouteAirCondition = () => {
164 + this.setState({
165 + buttonClicked: true,
166 + });
167 + API.get("/airCondition/route", {
168 + params: {
169 + departure: this.state.departure,
170 + arrival: this.state.arrival,
171 + },
172 + })
173 + .then((response) => {
174 + this.setState({
175 + routeInformation: response.data,
176 + });
177 + console.log(this.state.routeInformation);
178 + })
179 + .catch(function (error) {
180 + console.log(error);
181 + })
182 + .finally(function () {
183 + // always executed
184 + });
185 + };
186 +
187 + addMarker = (position, idx, title) => {
188 + var imageSrc =
189 + "http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png", // 마커 이미지 url, 스프라이트 이미지를 씁니다
190 + imageSize = new kakao.maps.Size(36, 37), // 마커 이미지의 크기
191 + imgOptions = {
192 + spriteSize: new kakao.maps.Size(36, 691), // 스프라이트 이미지의 크기
193 + spriteOrigin: new kakao.maps.Point(0, idx * 46 + 10), // 스프라이트 이미지 중 사용할 영역의 좌상단 좌표
194 + offset: new kakao.maps.Point(13, 37), // 마커 좌표에 일치시킬 이미지 내에서의 좌표
195 + },
196 + markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imgOptions),
197 + marker = new kakao.maps.Marker({
198 + position: position, // 마커의 위치
199 + image: markerImage,
200 + });
201 +
202 + marker.setMap(this.state.map); // 지도 위에 마커를 표출합니다
203 + this.setState({ curMarker: marker });
204 + this.state.markers.push(marker); // 배열에 생성된 마커를 추가합니다
205 +
206 + return marker;
207 + };
208 +
209 + // 지도 위에 표시되고 있는 마커를 모두 제거합니다
210 + removeMarker = () => {
211 + for (var i = 0; i < this.state.markers.length; i++) {
212 + this.state.markers[i].setMap(null);
213 + }
214 + this.setState({
215 + markers: [],
216 + });
217 + };
218 +
219 + // 검색결과 목록 하단에 페이지번호를 표시는 함수입니다
220 + displayPagination = (pagination) => {
221 + var paginationEl = document.getElementById("pagination"),
222 + fragment = document.createDocumentFragment(),
223 + i;
224 +
225 + // 기존에 추가된 페이지번호를 삭제합니다
226 + while (paginationEl.hasChildNodes()) {
227 + paginationEl.removeChild(paginationEl.lastChild);
228 + }
229 +
230 + for (i = 1; i <= pagination.last; i++) {
231 + var el = document.createElement("a");
232 + el.href = "#";
233 + el.innerHTML = i;
234 +
235 + if (i === pagination.current) {
236 + el.className = "on";
237 + } else {
238 + el.onclick = (function (i) {
239 + return function () {
240 + pagination.gotoPage(i);
241 + };
242 + })(i);
243 + }
244 +
245 + fragment.appendChild(el);
246 + }
247 + paginationEl.appendChild(fragment);
248 + };
249 +
250 + removeAllChildNods(el) {
251 + while (el.hasChildNodes()) {
252 + el.removeChild(el.lastChild);
253 + }
254 + }
255 +
256 + // 검색결과 목록 또는 마커를 클릭했을 때 호출되는 함수입니다
257 + // 인포윈도우에 장소명을 표시합니다
258 + displayInfowindow = (marker, title) => {
259 + console.log(marker);
260 +
261 + let content = document.createElement("div");
262 + content.className = "wraps";
263 +
264 + let info = document.createElement("div");
265 + info.className = "infos";
266 +
267 + let titles = document.createElement("div");
268 + titles.className = "title";
269 + titles.innerHTML = title;
270 +
271 + let close = document.createElement("div");
272 + close.className = "close";
273 + close.onclick = () => {
274 + this.state.infoWindow.setMap(null);
275 + };
276 +
277 + let body = document.createElement("div");
278 + body.className = "body";
279 + let desc = document.createElement("div");
280 + let list = document.createElement("list");
281 + let second = document.createElement("LI");
282 + let br = document.createElement("div");
283 + br.innerHTML += "<br>";
284 + let second_ = document.createElement("LI");
285 +
286 + let getMise = document.createElement("Button");
287 + getMise.innerHTML = "미세먼지 정보";
288 + getMise.className = "info-button";
289 + getMise.onclick = () => {
290 + let position = marker.getPosition();
291 + console.log(marker.getPosition());
292 + console.log(position["Ga"]);
293 + API.get("/airCondition", {
294 + params: {
295 + latitude: position["Ha"],
296 + longitude: position["Ga"],
297 + },
298 + })
299 + .then((response) => {
300 + console.log("Success");
301 + console.log(response.data);
302 + this.setState({
303 + curAirCondition: response.data,
304 + });
305 + })
306 + .catch(function (error) {
307 + console.log(error);
308 + })
309 + .finally(function () {
310 + // always executed
311 + });
312 +
313 + API.get("airCondition/weather", {
314 + params: {
315 + latitude: position["Ha"],
316 + longitude: position["Ga"],
317 + },
318 + })
319 + .then((response) => {
320 + let resp = response["data"];
321 + console.log(resp);
322 + console.log("현재온도 : " + (resp.main.temp - 273.15));
323 + console.log("현재습도 : " + resp.main.humidity);
324 + console.log("날씨 : " + resp.weather[0].main);
325 + console.log("상세날씨설명 : " + resp.weather[0].description);
326 + console.log("날씨 이미지 : " + resp.weather[0].icon);
327 + console.log("바람 : " + resp.wind.speed);
328 + console.log("구름 : " + resp.clouds.all + "%");
329 + this.setState({
330 + temperature: (resp.main.temp - 273.15).toFixed(3),
331 + humidity: resp.main.humidity,
332 + weather: resp.weather[0].main,
333 + icon: resp.weather[0].icon,
334 + wind: resp.wind.speed,
335 + cloud: resp.clouds.all + "%",
336 + });
337 + })
338 + .catch(function (error) {
339 + console.log(error);
340 + });
341 + };
342 + let setDepart = document.createElement("Button");
343 + setDepart.innerHTML = "출발지로 설정하기";
344 + setDepart.onclick = () => {
345 + this.setState({
346 + departure: marker.getPosition(),
347 + departureTitle: title,
348 + });
349 + };
350 + setDepart.className = "info-button";
351 + let setArrive = document.createElement("Button");
352 + setArrive.innerHTML = "도착지로 설정하기";
353 + setArrive.onclick = () => {
354 + this.setState({
355 + arrival: marker.getPosition(),
356 + arrivalTitle: title,
357 + });
358 + };
359 + setArrive.className = "info-button";
360 + second.appendChild(getMise);
361 + second.appendChild(br);
362 + second_.appendChild(setDepart);
363 + second_.appendChild(setArrive);
364 +
365 + list.appendChild(second);
366 + list.appendChild(second_);
367 + desc.appendChild(list);
368 + body.appendChild(desc);
369 + titles.appendChild(close);
370 + info.appendChild(titles);
371 + info.appendChild(body);
372 + content.appendChild(info);
373 +
374 + this.state.infoWindow.setContent(content);
375 + this.state.infoWindow.setPosition(marker.getPosition());
376 + this.state.infoWindow.setMap(this.state.map);
377 + };
378 +
379 + componentDidMount() {
380 + // 컴포넌트가 만들어지고, render 함수가 호출된 이후에 호출되는 메소
381 + // AJAX나 타이머를 생성하는 코드를 작성하는 파트이다.
382 + let el = document.getElementById("map");
383 + this.setState({
384 + map: new kakao.maps.Map(el, {
385 + //map option 설정
386 + center: new kakao.maps.LatLng(37.503716, 127.044844),
387 + level: 3,
388 + }),
389 + });
390 + this.searchPlaces();
391 + // marker.setMap(map);
392 + }
393 +
394 + render() {
395 + let currentAirCondition = null;
396 + if (this.state.curAirCondition != null) {
397 + let pm10Image = null;
398 + let pm25Image = null;
399 + switch (this.state.curAirCondition.pm10Grade) {
400 + case "1":
401 + pm10Image = <img src={veryGood} width="50px" heigth="100px" />;
402 + break;
403 + case "2":
404 + pm10Image = <img src={good} width="50px" heigth="100px" />;
405 + break;
406 + case "3":
407 + pm10Image = <img src={bad} width="50px" heigth="100px" />;
408 + break;
409 + case "4":
410 + pm10Image = <img src={veryBad} width="50px" heigth="100px" />;
411 + break;
412 + default:
413 + pm10Image = null;
414 + }
415 + switch (this.state.curAirCondition.pm25Grade) {
416 + case "1":
417 + pm25Image = <img src={veryGood} width="50px" heigth="100px" />;
418 + break;
419 + case "2":
420 + pm25Image = <img src={good} width="50px" heigth="100px" />;
421 + break;
422 + case "3":
423 + pm25Image = <img src={bad} width="50px" heigth="100px" />;
424 + break;
425 + case "4":
426 + pm25Image = <img src={veryBad} width="50px" heigth="100px" />;
427 + break;
428 + default:
429 + pm25Image = null;
430 + }
431 + currentAirCondition = (
432 + <h5>
433 + <br />
434 + <br />
435 + <br /> 미세먼지 등급 <br /> {pm10Image} <br />
436 + 미세먼지 지수 : {this.state.curAirCondition.pm10Value} <br />
437 + 초미세먼지 등급 <br /> {pm25Image} <br />
438 + 초미세먼지 지수 : {this.state.curAirCondition.pm25Value} <br />
439 + 현재 온도 : {this.state.temperature} <br />
440 + 현재 습도 : {this.state.humidity} <br />
441 + 날씨 : {this.state.weather} <br />
442 + 바람 : {this.state.wind} <br />
443 + 구름 : {this.state.cloud} <br />{" "}
444 + </h5>
445 + );
446 + }
447 + let routeAirCondition = null;
448 + let tempRouteAirCondition = [];
449 + if (this.state.buttonClicked != null) {
450 + routeAirCondition = <img src={loading} width="300px" heigth="300px" />;
451 + }
452 + if (this.state.routeInformation != null) {
453 + for (let i = 0; i < this.state.routeInformation.length - 1; i++) {
454 + tempRouteAirCondition.push(this.state.routeInformation[i]);
455 + }
456 + let pm10ImageArray = new Array();
457 + let pm25ImageArray = new Array();
458 + for (let i = 0; i < this.state.routeInformation.length; i++) {
459 + let pm10Image = null;
460 + let pm25Image = null;
461 + switch (this.state.routeInformation[i]["airCondition"].pm10Grade) {
462 + case "1":
463 + pm10Image = <img src={veryGood} width="50px" heigth="100px" />;
464 + break;
465 + case "2":
466 + pm10Image = <img src={good} width="50px" heigth="100px" />;
467 + break;
468 + case "3":
469 + pm10Image = <img src={bad} width="50px" heigth="100px" />;
470 + break;
471 + case "4":
472 + pm10Image = <img src={veryBad} width="50px" heigth="100px" />;
473 + break;
474 + default:
475 + pm10Image = null;
476 + }
477 + switch (this.state.routeInformation[i]["airCondition"].pm25Grade) {
478 + case "1":
479 + pm25Image = <img src={veryGood} width="50px" heigth="100px" />;
480 + break;
481 + case "2":
482 + pm25Image = <img src={good} width="50px" heigth="100px" />;
483 + break;
484 + case "3":
485 + pm25Image = <img src={bad} width="50px" heigth="100px" />;
486 + break;
487 + case "4":
488 + pm25Image = <img src={veryBad} width="50px" heigth="100px" />;
489 + break;
490 + default:
491 + pm25Image = null;
492 + }
493 + pm10ImageArray[i] = pm10Image;
494 + pm25ImageArray[i] = pm25Image;
495 + }
496 + routeAirCondition = (
497 + <table>
498 + <thead>
499 + <tr>
500 + <th>
501 + <br />
502 + 경로별 미세먼지 정보
503 + </th>
504 + </tr>
505 + </thead>
506 + <tbody>
507 + <tr>
508 + <td>
509 + <img src={start} width="50px" heigth="30px" /> <br />
510 + <h6>
511 + {this.state.departureTitle} <br /> 미세먼지 등급
512 + <br />
513 + {pm10ImageArray[this.state.routeInformation.length - 1]}
514 + <br />
515 + 미세먼지 지수 :{" "}
516 + {
517 + this.state.routeInformation[
518 + this.state.routeInformation.length - 1
519 + ]["airCondition"].pm10Value
520 + }
521 + <br />
522 + </h6>
523 + </td>
524 + {tempRouteAirCondition.map((listValue, index) => {
525 + if (index != tempRouteAirCondition.length - 1) {
526 + return (
527 + <td key={index}>
528 + <img src={next} width="40px" heigth="25px" />
529 + <h6>
530 + {listValue.instruction}
531 + <br />
532 + 소요시간 : {listValue.duration}
533 + <br />
534 + 미세먼지 등급
535 + <br />
536 + {pm10ImageArray[index]}
537 + <br />
538 + 미세먼지 지수 : <br />
539 + {listValue.airCondition.pm10Value}
540 + <br />
541 + </h6>
542 + </td>
543 + );
544 + } else {
545 + return (
546 + <td key={index}>
547 + <img src={finish} width="50px" heigth="30px" />
548 + <br />
549 + <h6>
550 + {listValue.instruction}
551 + <br />
552 + 소요시간 : {listValue.duration}
553 + <br />
554 + 미세먼지 등급
555 + <br />
556 + {pm10ImageArray[index]}
557 + <br />
558 + 미세먼지 지수 : {listValue.airCondition.pm10Value}
559 + <br />
560 + </h6>
561 + </td>
562 + );
563 + }
564 + })}
565 + </tr>
566 + </tbody>
567 + </table>
568 + );
569 + }
570 +
571 + return (
572 + <section id="home">
573 + <div className="cover">
574 + <div className="Home-header">How's the Weather?!</div>
575 + </div>
576 + <div className="map_wrap">
577 + <div
578 + id="map"
579 + style={{
580 + width: "1000px",
581 + height: "600px",
582 + align: "middle",
583 + }}
584 + ></div>
585 + <div id="menu_wrap" className="bg_white">
586 + <div className="option">
587 + 지역 검색 :{" "}
588 + <input
589 + type="text"
590 + value={this.state.region}
591 + id="keyword"
592 + size="15"
593 + onChange={(e) => this.setState({ region: e.target.value })}
594 + />
595 + <Button variant="light" type="submit" onClick={this.searchPlaces}>
596 + 검색하기
597 + </Button>
598 + </div>
599 + <ul id="placesList"></ul>
600 + <div id="pagination"></div>
601 + </div>
602 + <ul id="placesList"></ul>
603 + <div id="pagination"></div>
604 + </div>
605 + <div id="footer">
606 + <div className="left-box">
607 + <h5> 현재위치 : {this.state.region} </h5>
608 + <br />
609 + <div id="middle">{currentAirCondition}</div>
610 + </div>
611 + <div className="right-box">
612 + <h5>
613 + 출발지 : {this.state.departureTitle} <br />
614 + 도착지 : {this.state.arrivalTitle} <br />
615 + <Button variant="secondary" onClick={this.getRouteAirCondition}>
616 + {" "}
617 + 경로별 미세먼지 정보 알아보기{" "}
618 + </Button>{" "}
619 + <br /> <br />
620 + </h5>
621 + <div id="middle">{routeAirCondition}</div>
622 + </div>
623 + </div>
624 + </section>
625 + );
626 + }
627 +}