Merge branch 'develop' into 'master'
Develop See merge request !7
Showing
25 changed files
with
663 additions
and
63 deletions
... | @@ -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 | } | ... | ... |
backend/package-lock.json
0 → 100644
This diff is collapsed. Click to expand it.
... | @@ -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", | ... | ... |
backend/routes/airCondition.js
0 → 100644
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; | ... | ... |
frontend/package-lock.json
0 → 100644
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> | ... | ... |
frontend/src/API.js
0 → 100644
... | @@ -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 { |
6 | - return ( | 7 | + constructor(props) { |
7 | - <div className="App"> | 8 | + super(props); |
8 | - <header className="App-header"> | 9 | + } |
9 | - <img src={logo} className="App-logo" alt="logo" /> | 10 | + render() { |
10 | - <p> | 11 | + return ( |
11 | - Edit <code>src/App.js</code> and save to reload. | 12 | + <HashRouter basename={process.env.PUBLIC_URL}> |
12 | - </p> | 13 | + <ScrollToTop> |
13 | - <a | 14 | + <div className="App"> |
14 | - className="App-link" | 15 | + <Route exact path="/" component={Home} /> |
15 | - href="https://reactjs.org" | 16 | + </div> |
16 | - target="_blank" | 17 | + </ScrollToTop> |
17 | - rel="noopener noreferrer" | 18 | + </HashRouter> |
18 | - > | 19 | + ); |
19 | - Learn React | 20 | + } |
20 | - </a> | ||
21 | - </header> | ||
22 | - </div> | ||
23 | - ); | ||
24 | } | 21 | } |
25 | 22 | ||
26 | export default App; | 23 | export default App; | ... | ... |
frontend/src/ScrollToTop.js
0 → 100644
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); |
frontend/src/gradeIcon/angry.png
0 → 100644
27.5 KB
frontend/src/gradeIcon/cool.png
0 → 100644
23.4 KB
frontend/src/gradeIcon/devil.png
0 → 100644
29.4 KB
frontend/src/gradeIcon/finish.png
0 → 100644
30 KB
frontend/src/gradeIcon/loading.gif
0 → 100644
17.1 KB
frontend/src/gradeIcon/next.png
0 → 100644
5.67 KB
frontend/src/gradeIcon/smile.png
0 → 100644
27.5 KB
frontend/src/gradeIcon/start.png
0 → 100644
13.3 KB
frontend/src/home.css
0 → 100644
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 | +} |
frontend/src/home.js
0 → 100644
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment