권주희

Merge branch 'develop' into 'master'

Develop



See merge request !7
......@@ -2,6 +2,7 @@
# dependencies
frontend/node_modules/*
backend/node_modules/*
/.pnp
.pnp.js
......@@ -21,3 +22,4 @@ frontend/node_modules/*
npm-debug.log*
yarn-debug.log*
yarn-error.log*
secrets.json
\ No newline at end of file
......
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var createError = require("http-errors");
var express = require("express");
var cors = require("cors");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var airConditionRouter = require("./routes/airCondition");
var app = express();
app.use(cors());
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "pug");
app.use(logger('dev'));
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, "public")));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use("/", indexRouter);
app.use("/users", usersRouter);
app.use("/airCondition", airConditionRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
res.render("error");
});
module.exports = app;
......
......@@ -4,16 +4,16 @@
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('backend:server');
var http = require('http');
var app = require("../app");
var debug = require("debug")("backend:server");
var http = require("http");
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
var port = normalizePort(process.env.PORT || "3001");
app.set("port", port);
/**
* Create HTTP server.
......@@ -26,8 +26,8 @@ var server = http.createServer(app);
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
server.on("error", onError);
server.on("listening", onListening);
/**
* Normalize a port into a number, string, or false.
......@@ -54,22 +54,20 @@ function normalizePort(val) {
*/
function onError(error) {
if (error.syscall !== 'listen') {
if (error.syscall !== "listen") {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
case "EACCES":
console.error(bind + " requires elevated privileges");
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
case "EADDRINUSE":
console.error(bind + " is already in use");
process.exit(1);
break;
default:
......@@ -83,8 +81,6 @@ function onError(error) {
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
debug("Listening on " + bind);
}
......
This diff is collapsed. Click to expand it.
......@@ -6,7 +6,9 @@
"start": "node ./bin/www"
},
"dependencies": {
"axios": "^0.19.2",
"cookie-parser": "~1.4.3",
"cors": "^2.8.5",
"debug": "~2.6.9",
"express": "~4.16.0",
"http-errors": "~1.6.2",
......
var express = require("express");
var router = express.Router();
var axios = require("axios");
const openAPIKey = require("./secrets.json").openAPIKey;
const googleMapKey = require("./secrets.json").googleAPIKey;
const weatherAPIKey = require("./secrets.json").weatherAPIKey;
axios.create({
// TODO : 웹을 AWS에 올릴때, 해당 baseURL이 달라져야할 수 있음
baseURL: "http://localhost:3001",
responseType: "json",
});
/* GET airCondition listing. */
router.get("/", async function (req, res, next) {
console.log("경도:", req.query.latitude);
console.log("경도:", req.query.longitude);
let airCondition = "";
let response = await getPosition(req.query.latitude, req.query.longitude)
.then((encodedStation) => getCondition(encodedStation))
.then((result) => {
airCondition = result;
});
res.send(airCondition);
});
router.get("/weather", async function (req, res, next) {
console.log("경도:", req.query.latitude);
console.log("경도:", req.query.longitude);
let airCondition = "";
let response = await getEnglishPosition(
req.query.latitude,
req.query.longitude
)
.then((encodedStation) => getWeather(encodedStation))
.then((result) => {
airCondition = result;
});
res.send(airCondition);
});
const getWeather = (encodedStation) => {
return axios
.get(
"https://api.openweathermap.org/data/2.5/weather?q=" +
encodedStation +
"&appid=" +
weatherAPIKey
)
.then(function (response) {
return response["data"];
})
.catch(function (error) {
console.log(error.response);
});
};
const getPosition = (lat, lon) => {
return axios
.get(
"https://maps.googleapis.com/maps/api/geocode/json?latlng=" +
lat +
"," +
lon +
"&location_type=ROOFTOP&result_type=street_address&key=" +
googleMapKey +
"&language=ko"
)
.then(function (response) {
console.log("KEY : ", googleMapKey);
let stationName = "";
for (
let i = 0;
i < response["data"].results[0]["address_components"].length;
i++
) {
let temp =
response["data"].results[0]["address_components"][i]["long_name"];
if (temp[temp.length - 1] == "구") {
stationName = temp;
break;
}
}
console.log("STATION : ", stationName);
return (encodedStation = encodeURI(stationName));
})
.catch(function (error) {
console.log(error.response);
});
};
const getEnglishPosition = (lat, lon) => {
return axios
.get(
"https://maps.googleapis.com/maps/api/geocode/json?latlng=" +
lat +
"," +
lon +
"&location_type=ROOFTOP&result_type=street_address&key=" +
googleMapKey +
"&language=en"
)
.then(function (response) {
let stationName =
response["data"].results[0]["address_components"][3]["long_name"];
console.log("STATION : ", stationName);
return (encodedStation = encodeURI(stationName));
})
.catch(function (error) {
console.log(error.response);
});
};
/* GET route airCondition listing. */
router.get("/route", async function (req, res, next) {
console.log("출발지:", req.query.departure);
console.log("도착지:", req.query.arrival);
let dep = JSON.parse(req.query.departure);
let depLat = dep["Ha"];
let depLon = dep["Ga"];
let arr = JSON.parse(req.query.arrival);
let arrLat = arr["Ha"];
let arrLon = arr["Ga"];
let airCondition = "";
let response = await getRoute(depLat, depLon, arrLat, arrLon)
.then((routeInformation) =>
routeAirCondition(depLat, depLon, routeInformation)
)
.then((routeInformation) => {
airCondition = routeInformation;
});
res.send(airCondition);
});
const getCondition = (encodedStation) => {
return axios
.get(
"http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty?serviceKey=" +
openAPIKey +
"&numOfRows=10&pageNo=1&stationName=" +
encodedStation +
"&dataTerm=DAILY&ver=1.3&_returnType=json"
)
.then(function (response) {
// console.log("RES :: ", response);
result = response["data"]["list"][0];
return result;
})
.catch(function (error) {
console.log(error.response);
});
};
const getRoute = (depLat, depLon, arrLat, arrLon) => {
return axios
.get(
"https://maps.googleapis.com/maps/api/directions/json?origin=" +
depLat +
"," +
depLon +
"&destination=" +
arrLat +
"," +
arrLon +
"&mode=transit&departure_time=now&key=" +
googleMapKey +
"&language=ko"
)
.then(function (response) {
console.log(response["data"]);
let routeInformation = [];
for (
let i = 0;
i < response["data"].routes[0]["legs"][0]["steps"].length;
i++
) {
let info = {};
info["instruction"] =
response["data"].routes[0]["legs"][0]["steps"][i][
"html_instructions"
];
info["location"] =
response["data"].routes[0]["legs"][0]["steps"][i]["end_location"];
info["duration"] =
response["data"].routes[0]["legs"][0]["steps"][i]["duration"]["text"];
info["travel_mode"] =
response["data"].routes[0]["legs"][0]["steps"][i]["travel_mode"];
routeInformation.push(info);
}
// console.log(routeInformation);
return routeInformation;
})
.catch(function (error) {
console.log(error.response);
});
};
const routeAirCondition = async (depLat, depLon, routeInformation) => {
await getPosition(depLat, depLon)
.then((encodedStation) => getCondition(encodedStation))
.then((result) => {
let info = {};
info["airCondition"] = result;
routeInformation.push(info);
});
for (let i = 0; i < routeInformation.length - 1; i++) {
await getPosition(
routeInformation[i]["location"]["lat"],
routeInformation[i]["location"]["lng"]
)
.then((encodedStation) => getCondition(encodedStation))
.then((result) => {
routeInformation[i]["airCondition"] = result;
});
}
console.log(routeInformation);
return routeInformation;
};
module.exports = router;
var express = require('express');
var express = require("express");
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
router.get("/", function (req, res, next) {
res.render("index", { title: "Express" });
});
module.exports = router;
......
......@@ -3,3 +3,4 @@ extends layout
block content
h1= title
p Welcome to #{title}
p This is HowsTheWeather Backend
......
This diff could not be displayed because it is too large.
......@@ -6,8 +6,14 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"axios": "^0.19.2",
"bootstrap": "^3.4.1",
"daum-map-api": "^1.0.2",
"react": "^16.13.1",
"react-bootstrap": "^1.0.1",
"react-dom": "^16.13.1",
"react-kakao-maps": "0.0.13",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1"
},
"scripts": {
......
......@@ -10,6 +10,16 @@
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<script
type="text/javascript"
src="//dapi.kakao.com/v2/maps/sdk.js?appkey=61abec34d0855ba1d434ea222263d4a5&libraries=services,clusterer,drawing"
></script>
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
......@@ -24,7 +34,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>HowsTheWeather</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
......
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:3001",
responseType: "json",
});
......@@ -15,13 +15,14 @@
.App-header {
background-color: #282c34;
min-height: 100vh;
min-height: 13vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
font-weight: bold;
}
.App-link {
......
import React from 'react';
import logo from './logo.svg';
import './App.css';
import React, { Component } from "react";
import { HashRouter, Route } from "react-router-dom";
import ScrollToTop from "./ScrollToTop";
import Home from "./home";
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<HashRouter basename={process.env.PUBLIC_URL}>
<ScrollToTop>
<div className="App">
<Route exact path="/" component={Home} />
</div>
</ScrollToTop>
</HashRouter>
);
}
}
export default App;
......
import { Component } from "react";
import { withRouter } from "react-router-dom";
class ScrollToTop extends Component {
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
window.scrollTo(0, 0);
}
}
render() {
return this.props.children;
}
}
export default withRouter(ScrollToTop);
.Home {
text-align: center;
}
.Home-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.Home-logo {
animation: Home-logo-spin infinite 20s linear;
}
}
.Home-header {
background-color: #282c34;
min-height: 13vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
font-weight: bold;
}
.Home-link {
color: #61dafb;
}
@keyframes Home-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.strong {
font-size: calc(10px + 1vmin);
font-weight: bold;
}
.map_wrap,
.map_wrap * {
margin: 0;
padding: 0;
font-family: "Malgun Gothic", dotum, "돋움", sans-serif;
font-size: 12px;
}
.map_wrap a,
.map_wrap a:hover,
.map_wrap a:active {
color: #000;
text-decoration: none;
}
.map_wrap {
position: relative;
width: 100%;
height: 500px;
}
#menu_wrap {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 250px;
margin: 10px 0 30px 10px;
padding: 5px;
overflow-y: auto;
background: rgba(53, 53, 53, 0.8);
z-index: 1;
font-size: 12px;
border-radius: 10px;
}
.bg_white {
background: #fff;
}
x #menu_wrap hr {
display: block;
height: 1px;
border: 0;
border-top: 2px solid #5f5f5f;
margin: 3px 0;
}
#menu_wrap .option {
text-align: center;
color: #ffffff;
font-weight: bold;
}
#menu_wrap .option p {
margin: 10px 0;
}
#menu_wrap .option button {
margin-left: 5px;
}
#placesList li {
list-style: none;
}
#placesList .item {
position: relative;
border-bottom: 1px solid #888;
overflow: hidden;
cursor: pointer;
min-height: 65px;
}
#placesList .item span {
display: block;
margin-top: 4px;
}
#placesList .item h5,
#placesList .item .info {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
#placesList .item .info {
padding: 10px 0 10px 55px;
color: #ffffff;
}
#placesList .info .gray {
color: #eaeaea;
}
#placesList .info .jibun {
padding-left: 26px;
background: url(http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/places_jibun.png)
no-repeat;
}
#placesList .info .tel {
color: #ffbb00;
font-weight: bold;
}
#placesList .item .markerbg {
float: left;
position: absolute;
width: 36px;
height: 37px;
margin: 10px 0 0 10px;
background: url(http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png)
no-repeat;
}
#placesList .item .marker_1 {
background-position: 0 -10px;
}
#placesList .item .marker_2 {
background-position: 0 -56px;
}
#placesList .item .marker_3 {
background-position: 0 -102px;
}
#placesList .item .marker_4 {
background-position: 0 -148px;
}
#placesList .item .marker_5 {
background-position: 0 -194px;
}
#placesList .item .marker_6 {
background-position: 0 -240px;
}
#placesList .item .marker_7 {
background-position: 0 -286px;
}
#placesList .item .marker_8 {
background-position: 0 -332px;
}
#placesList .item .marker_9 {
background-position: 0 -378px;
}
#placesList .item .marker_10 {
background-position: 0 -423px;
}
#placesList .item .marker_11 {
background-position: 0 -470px;
}
#placesList .item .marker_12 {
background-position: 0 -516px;
}
#placesList .item .marker_13 {
background-position: 0 -562px;
}
#placesList .item .marker_14 {
background-position: 0 -608px;
}
#placesList .item .marker_15 {
background-position: 0 -654px;
}
#pagination {
margin: 10px auto;
text-align: center;
}
#pagination a {
display: inline-block;
margin-right: 10px;
}
#pagination .on {
font-weight: bold;
cursor: default;
color: #777;
}
.wraps {
position: absolute;
left: 0;
bottom: 40px;
width: 288px;
height: 132px;
margin-left: -144px;
text-align: left;
overflow: hidden;
font-size: 12px;
font-family: "Malgun Gothic", dotum, "돋움", sans-serif;
line-height: 1.5;
}
.wraps * {
padding: 0;
margin: 0;
}
.wraps .infos {
width: 286px;
height: 120px;
border-radius: 5px;
border-bottom: 2px solid #ccc;
border-right: 1px solid #ccc;
overflow: hidden;
background: #fff;
}
.wraps .infos:nth-child(1) {
border: 0;
box-shadow: 0px 1px 2px #888;
}
.infos .title {
padding: 5px 0 0 10px;
height: 30px;
background: #eee;
border-bottom: 1px solid #ddd;
font-size: 18px;
font-weight: bold;
}
.infos .close {
position: absolute;
top: 10px;
right: 10px;
color: #888;
width: 17px;
height: 17px;
background: url("http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/overlay_close.png");
}
.infos .close:hover {
cursor: pointer;
}
.infos .body {
position: relative;
overflow: hidden;
}
.infos .desc {
position: relative;
margin: 13px 0 0 90px;
height: 75px;
}
.desc .ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.desc .jibun {
font-size: 11px;
color: #888;
margin-top: -2px;
}
.infos .img {
position: absolute;
top: 6px;
left: 5px;
width: 73px;
height: 71px;
border: 1px solid #ddd;
color: #888;
overflow: hidden;
}
.infos:after {
content: "";
position: absolute;
margin-left: -12px;
left: 50%;
bottom: 0;
width: 22px;
height: 12px;
background: url("http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/vertex_white.png");
}
.infos .link {
color: #5085bb;
}
#footer {
position: absolute;
bottom: 0;
width: 100%;
height: 100px;
background: #ccc;
}
.left-box {
float: left;
width: 20%;
}
.left-box h5 {
font-size: 1rem;
font-style: italic;
padding-left: 10px;
}
.right-box {
float: right;
width: 80%;
}
.right-box h5 {
font-size: 1rem;
font-style: italic;
padding-left: 10px;
}
#middle {
text-align: center;
}
.info-button {
border-radius: 4px;
background-color: #4641d9;
width: 100px;
color: white;
}
This diff is collapsed. Click to expand it.