Showing
30 changed files
with
1582 additions
and
85 deletions
LICENSE.txt
deleted
100644 → 0
1 | -MIT License | ||
2 | -Copyright (c) 2022 Jeongmin Seo | ||
3 | -Permission is hereby granted, free of charge, to any person obtaining a copy | ||
4 | -of this software and associated documentation files (the "Software"), to deal | ||
5 | -in the Software without restriction, including without limitation the rights | ||
6 | -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
7 | -copies of the Software, and to permit persons to whom the Software is | ||
8 | -furnished to do so, subject to the following conditions: | ||
9 | -The above copyright notice and this permission notice shall be included in all | ||
10 | -copies or substantial portions of the Software. | ||
11 | -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
12 | -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
13 | -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
14 | -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
15 | -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
16 | -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
17 | -SOFTWARE. | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
README.md
deleted
100644 → 0
1 | -# 맛집지도 : 실시간 맛집 정보 공유 플랫폼 | ||
2 | -카카오맵 API와 실시간 채팅 기능을 통해 사용자들과 소통함으로써 맛집에 대한 정보를 얻을 수 있는 플랫폼입니다. | ||
3 | - | ||
4 | -<div align="center"> | ||
5 | - <img src="/uploads/f2c62af097272859d8f37acda908d6aa/그림1.png" width="300" height="300"> | ||
6 | -</div> | ||
7 | - | ||
8 | -### 📚 STACKS | ||
9 | -![node-16.14.2](https://img.shields.io/badge/Node-16.14.2-green) | ||
10 | -![express-4.18.1](https://img.shields.io/badge/Express-4.18.1-green) | ||
11 | -![html-latest](https://img.shields.io/badge/html-5.2-green) | ||
12 | -![css-latest](https://img.shields.io/badge/css-3-green) | ||
13 | - | ||
14 | -![nginx-latest](https://img.shields.io/badge/nginx-latest-blue) | ||
15 | -![socket.io-latest](https://img.shields.io/badge/socket.io-latest-blue) | ||
16 | -![AWS_RDS](https://img.shields.io/badge/AWS_RDS-blue) | ||
17 | - | ||
18 | -![mysql-2.18.1](https://img.shields.io/badge/Mysql-2.18.1-yellowgreen) | ||
19 | -![AWS_RDS](https://img.shields.io/badge/AWS_RDS-yellowgreen) | ||
20 | - | ||
21 | - | ||
22 | -## About the Project | ||
23 | -- 로그인을 통해 사이트에 들어갈 수 있으며, 지역별 맛집정보를 파악할 수 있습니다. | ||
24 | -- 타 유저들과 실시간 소통을 통해 맛집에 대한 정보를 빠르게 얻을 수 있습니다. | ||
25 | - | ||
26 | -### Overview | ||
27 | -- 추가 예정(이미지) | ||
28 | - | ||
29 | -### Project Architecture | ||
30 | -- 추가 예정(이미지) | ||
31 | - | ||
32 | -### Built With | ||
33 | -- [node.js](https://nodejs.org/ko/) | ||
34 | -- [express](https://expressjs.com/ko/) | ||
35 | -- [AWS_EC2](https://aws.amazon.com/ko/) | ||
36 | -- [AWS_RDS](https://aws.amazon.com/ko/) | ||
37 | -- [socket.io](https://socket.io/) | ||
38 | -- [nginx](https://www.nginx.com/) | ||
39 | -- [mysql](https://www.mysql.com/) | ||
40 | - | ||
41 | -## Getting Started ( Installation ) | ||
42 | -### Prerequisites | ||
43 | -- 추가 예정 | ||
44 | - | ||
45 | -### Installation | ||
46 | -- 추가 예정 | ||
47 | - | ||
48 | -## Roadmap | ||
49 | -- [x] 1. 로그인/회원가입 창 구현 | ||
50 | -- [x] 2. 맛집지도 UI 및 기능 구현 | ||
51 | -- [x] 3. socket.io를 이용한 실시간 채팅 구현 | ||
52 | -- [x] 4. AWS, Mysql을 이용한 데이터베이스 구축 | ||
53 | -- [ ] 5. 서버 구축 및 배포 | ||
54 | - | ||
55 | -## Contributing | ||
56 | -프로젝트에 기여하고 싶으신 분들은 아래 절차를 따라주시기 바랍니다. | ||
57 | -1. 프로젝트 fork | ||
58 | -2. feature branch 생성 (git checkout -b feature/name) | ||
59 | -3. commit (git commit -m "Add feature") | ||
60 | -4. push (git push origin feature/name) | ||
61 | -5. pull request 생성 | ||
62 | - | ||
63 | -## License | ||
64 | -MIT 라이센스 아래 사용 가능합니다. LICENSE.txt를 통해 자세한 정보를 확인하세요. | ||
65 | - | ||
66 | -## Contact | ||
67 | -- 서정민 : balljm@khu.ac.kr | ||
68 | -- 양주미 : luckyyjm@khu.ac.kr | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/.gitignore
0 → 100644
1 | +# Logs | ||
2 | +logs | ||
3 | +*.log | ||
4 | +npm-debug.log* | ||
5 | +yarn-debug.log* | ||
6 | +yarn-error.log* | ||
7 | +lerna-debug.log* | ||
8 | +.pnpm-debug.log* | ||
9 | +app/log/* | ||
10 | +# Diagnostic reports (https://nodejs.org/api/report.html) | ||
11 | +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | ||
12 | + | ||
13 | +# Runtime data | ||
14 | +pids | ||
15 | +*.pid | ||
16 | +*.seed | ||
17 | +*.pid.lock | ||
18 | + | ||
19 | +# Directory for instrumented libs generated by jscoverage/JSCover | ||
20 | +lib-cov | ||
21 | + | ||
22 | +# Coverage directory used by tools like istanbul | ||
23 | +coverage | ||
24 | +*.lcov | ||
25 | + | ||
26 | +# nyc test coverage | ||
27 | +.nyc_output | ||
28 | + | ||
29 | +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | ||
30 | +.grunt | ||
31 | + | ||
32 | +# Bower dependency directory (https://bower.io/) | ||
33 | +bower_components | ||
34 | + | ||
35 | +# node-waf configuration | ||
36 | +.lock-wscript | ||
37 | + | ||
38 | +# Compiled binary addons (https://nodejs.org/api/addons.html) | ||
39 | +build/Release | ||
40 | + | ||
41 | +# Dependency directories | ||
42 | +**node_modules/ | ||
43 | +jspm_packages/ | ||
44 | + | ||
45 | +# Snowpack dependency directory (https://snowpack.dev/) | ||
46 | +web_modules/ | ||
47 | + | ||
48 | +# TypeScript cache | ||
49 | +*.tsbuildinfo | ||
50 | + | ||
51 | +# Optional npm cache directory | ||
52 | +.npm | ||
53 | + | ||
54 | +# Optional eslint cache | ||
55 | +.eslintcache | ||
56 | + | ||
57 | +# Microbundle cache | ||
58 | +.rpt2_cache/ | ||
59 | +.rts2_cache_cjs/ | ||
60 | +.rts2_cache_es/ | ||
61 | +.rts2_cache_umd/ | ||
62 | + | ||
63 | +# Optional REPL history | ||
64 | +.node_repl_history | ||
65 | + | ||
66 | +# Output of 'npm pack' | ||
67 | +*.tgz | ||
68 | + | ||
69 | +# Yarn Integrity file | ||
70 | +.yarn-integrity | ||
71 | + | ||
72 | +# dotenv environment variables file | ||
73 | +.env | ||
74 | +.env.test | ||
75 | +.env.production | ||
76 | + | ||
77 | +# parcel-bundler cache (https://parceljs.org/) | ||
78 | +.cache | ||
79 | +.parcel-cache | ||
80 | + | ||
81 | +# Next.js build output | ||
82 | +.next | ||
83 | +out | ||
84 | + | ||
85 | +# Nuxt.js build / generate output | ||
86 | +.nuxt | ||
87 | +dist | ||
88 | + | ||
89 | +# Gatsby files | ||
90 | +.cache/ | ||
91 | +# Comment in the public line in if your project uses Gatsby and not Next.js | ||
92 | +# https://nextjs.org/blog/next-9-1#public-directory-support | ||
93 | +# public | ||
94 | + | ||
95 | +# vuepress build output | ||
96 | +.vuepress/dist | ||
97 | + | ||
98 | +# Serverless directories | ||
99 | +.serverless/ | ||
100 | + | ||
101 | +# FuseBox cache | ||
102 | +.fusebox/ | ||
103 | + | ||
104 | +# DynamoDB Local files | ||
105 | +.dynamodb/ | ||
106 | + | ||
107 | +# TernJS port file | ||
108 | +.tern-port | ||
109 | + | ||
110 | +# Stores VSCode versions used for testing VSCode extensions | ||
111 | +.vscode-test | ||
112 | + | ||
113 | +# yarn v2 | ||
114 | +.yarn/cache | ||
115 | +.yarn/unplugged | ||
116 | +.yarn/build-state.yml | ||
117 | +.yarn/install-state.gz | ||
118 | +.pnp.* | ||
119 | + | ||
120 | +.package-lock.json | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/app.js
0 → 100644
1 | +"use strict"; | ||
2 | +//모듈 | ||
3 | +const express = require("express"); | ||
4 | +const bodyParser = require("body-parser"); | ||
5 | +//환경변수 (운영체제간 gap 없애고자) | ||
6 | +const dotenv = require("dotenv"); | ||
7 | +dotenv.config(); | ||
8 | +const compression = require("compression"); | ||
9 | +const methodOverride = require("method-override"); | ||
10 | +const path = require("path") | ||
11 | +const socketIO = require("socket.io") | ||
12 | +const moment = require("moment") | ||
13 | +const http = require("http"); | ||
14 | +const app = express(); | ||
15 | +const server = http.createServer(app); | ||
16 | + | ||
17 | +var cors = require("cors"); | ||
18 | +const { logger } = require("./src/config/winston"); | ||
19 | +//app이라는 express 객체 생성 | ||
20 | +//라우팅 | ||
21 | +const home = require("./src/routes/home"); | ||
22 | +const port = 3000; | ||
23 | +const jwtMiddleware = require("./src/config/jwtMiddleware"); | ||
24 | +const io = socketIO(server); | ||
25 | + | ||
26 | +// 앱 세팅 | ||
27 | +app.set("views", "./src/views"); | ||
28 | +app.set("view engine", "ejs"); | ||
29 | +app.use(express.static(`${__dirname}/src/public`)); | ||
30 | +app.use(express.static(path.join(__dirname, "src"))) | ||
31 | + | ||
32 | +app.use(bodyParser.json()); | ||
33 | +//url통해 전달되는 데이터에 한글, 공백 등의 문자 오류 해결 | ||
34 | +app.use(bodyParser.urlencoded({extended: true})); | ||
35 | + | ||
36 | +app.use(compression()); // HTTP 요청을 압축 및 해제 | ||
37 | +app.use(express.json()); | ||
38 | +app.use(express.urlencoded({ extended: true })); | ||
39 | +app.use(methodOverride()); | ||
40 | +app.use(cors()); | ||
41 | +// app.use("/restaurants", require("../app/src/routes/home/restaurant.route")); | ||
42 | +// require("../app/src/routes/home/restaurant.route")(app); | ||
43 | + | ||
44 | +app.use("/", home); //미들웨어 등록해주는 method | ||
45 | + | ||
46 | +io.on('connection', (socket) => { | ||
47 | + socket.on("chatting", (data)=>{ | ||
48 | + const { name, msg } = data; | ||
49 | + io.emit("chatting", { | ||
50 | + name, | ||
51 | + msg, | ||
52 | + time: moment.format("h:ss A") | ||
53 | + }) | ||
54 | + }) | ||
55 | +}); | ||
56 | + | ||
57 | +logger.info(`${process.env.NODE_ENV} - API Server Start At Port ${port}`); | ||
58 | + | ||
59 | +module.exports = app; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/bin/www.js
0 → 100644
login_main/app/package-lock.json
0 → 100644
This diff is collapsed. Click to expand it.
login_main/app/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "login", | ||
3 | + "version": "1.0.0", | ||
4 | + "main": "app.js", | ||
5 | + "bin": { | ||
6 | + "login": "www.js" | ||
7 | + }, | ||
8 | + "dependencies": { | ||
9 | + "body-parser": "^1.20.0", | ||
10 | + "compression": "^1.7.4", | ||
11 | + "cors": "^2.8.5", | ||
12 | + "crypto": "^1.0.1", | ||
13 | + "dateformat": "^4.3.1", | ||
14 | + "dotenv": "^16.0.1", | ||
15 | + "ejs": "^3.1.8", | ||
16 | + "express": "^4.18.1", | ||
17 | + "jsonwebtoken": "^8.5.1", | ||
18 | + "method-override": "^3.0.0", | ||
19 | + "moment": "^2.29.3", | ||
20 | + "mysql": "^2.18.1", | ||
21 | + "mysql2": "^2.2.0", | ||
22 | + "regex-email": "^1.0.2", | ||
23 | + "request": "^2.88.2", | ||
24 | + "socket.io": "^4.5.1", | ||
25 | + "winston": "^3.2.1", | ||
26 | + "winston-daily-rotate-file": "^4.2.1" | ||
27 | + }, | ||
28 | + "devDependencies": {}, | ||
29 | + "scripts": { | ||
30 | + "start": "nodemon ./bin/www.js", | ||
31 | + "test": "echo \"Error: no test specified\" && exit 1", | ||
32 | + "dev": "NODE_ENV=development node index.js", | ||
33 | + "prod": "NODE_ENV=production node index.js" | ||
34 | + }, | ||
35 | + "author": "Jeongmin Seo, Jumi Yang", | ||
36 | + "license": "MIT", | ||
37 | + "keywords": [], | ||
38 | + "description": "Node.js API Server" | ||
39 | +} |
login_main/app/src/config/db.js
0 → 100644
1 | +// const mysql = require("mysql"); | ||
2 | +const { logger } = require("./winston"); | ||
3 | +const mysql2 = require("mysql2/promise"); | ||
4 | + | ||
5 | +// const db = mysql.createConnection({ | ||
6 | +// host: process.env.DB_HOST, | ||
7 | +// user: process.env.DB_USER, | ||
8 | +// password: process.env.DB_PASSWORD, | ||
9 | +// database: process.env.DB_DATABASE, //schema | ||
10 | +// }); | ||
11 | + | ||
12 | +const pool = mysql2.createPool({ | ||
13 | + host: process.env.DB_HOST, | ||
14 | + user: process.env.DB_USER, | ||
15 | + password: process.env.DB_PASSWORD, | ||
16 | + database: process.env.DB_DATABASE, //schema | ||
17 | + connectionLimit: 10000, | ||
18 | + multipleStatements: true, | ||
19 | +}); | ||
20 | + | ||
21 | +// db.connect(); | ||
22 | + | ||
23 | +module.exports = { | ||
24 | + pool: pool, | ||
25 | +}; | ||
26 | + | ||
27 | + | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/config/jwtMiddleware.js
0 → 100644
1 | +const jwt = require("jsonwebtoken"); | ||
2 | +const secret_config = require("./db"); | ||
3 | +const jwtMiddleware = function (req, res, next) { | ||
4 | + // read the token from header or url | ||
5 | + const token = req.headers["x-access-token"] || req.query.token; | ||
6 | + // token does not exist | ||
7 | + if (!token) { | ||
8 | + return res.status(403).json({ | ||
9 | + isSuccess: false, | ||
10 | + code: 403, | ||
11 | + message: "로그인이 되어 있지 않습니다.", | ||
12 | + }); | ||
13 | + } | ||
14 | + | ||
15 | + try { | ||
16 | + const verifiedToken = jwt.verify(token, secret_config.jwtsecret); | ||
17 | + req.verifiedToken = verifiedToken; | ||
18 | + next(); | ||
19 | + } catch { | ||
20 | + res.status(403).json({ | ||
21 | + isSuccess: false, | ||
22 | + code: 403, | ||
23 | + message: "검증 실패", | ||
24 | + }); | ||
25 | + } | ||
26 | +}; | ||
27 | + | ||
28 | +module.exports = jwtMiddleware; |
login_main/app/src/config/winston.js
0 → 100644
1 | +const { createLogger, format, transports } = require('winston'); | ||
2 | +require('winston-daily-rotate-file'); | ||
3 | +const fs = require('fs'); | ||
4 | + | ||
5 | +const env = process.env.NODE_ENV || 'development'; | ||
6 | +const logDir = 'log'; | ||
7 | + | ||
8 | +// https://lovemewithoutall.github.io/it/winston-example/ | ||
9 | +// Create the log directory if it does not exist | ||
10 | +if (!fs.existsSync(logDir)) { | ||
11 | + fs.mkdirSync(logDir) | ||
12 | +} | ||
13 | + | ||
14 | +const dailyRotateFileTransport = new transports.DailyRotateFile({ | ||
15 | + level: 'debug', | ||
16 | + filename: `${logDir}/%DATE%-smart-push.log`, | ||
17 | + datePattern: 'YYYY-MM-DD', | ||
18 | + zippedArchive: true, | ||
19 | + maxSize: '20m', | ||
20 | + maxFiles: '14d' | ||
21 | +}); | ||
22 | + | ||
23 | +const logger = createLogger({ | ||
24 | + level: env === 'development' ? 'debug' : 'info', | ||
25 | + format: format.combine( | ||
26 | + format.timestamp({ | ||
27 | + format: 'YYYY-MM-DD HH:mm:ss' | ||
28 | + }), | ||
29 | + format.json() | ||
30 | + ), | ||
31 | + transports: [ | ||
32 | + new transports.Console({ | ||
33 | + level: 'info', | ||
34 | + format: format.combine( | ||
35 | + format.colorize(), | ||
36 | + format.printf( | ||
37 | + info => `${info.timestamp} ${info.level}: ${info.message}` | ||
38 | + ) | ||
39 | + ) | ||
40 | + }), | ||
41 | + dailyRotateFileTransport | ||
42 | + ] | ||
43 | +}); | ||
44 | + | ||
45 | +module.exports = { | ||
46 | + logger: logger | ||
47 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/models/Restaurant.js
0 → 100644
1 | +'use strict'; | ||
2 | +//for DB manipulate | ||
3 | +const RestaurantStorage = require("./RestaurantStorage"); | ||
4 | + | ||
5 | +const {pool} = require("../config/db"); | ||
6 | +const { logger } = require("../config/winston"); | ||
7 | +const jwt = require("jsonwebtoken"); | ||
8 | + | ||
9 | +exports.readRestaurants = async function (req,res) { | ||
10 | + const {category} = req.query; | ||
11 | + | ||
12 | + if (category) { | ||
13 | + const validCategory = ["한식", "중식", "일식", "양식", "분식", "구이", "회/초밥", "기타",]; | ||
14 | + | ||
15 | + if (!validCategory.includes(category)) { | ||
16 | + return res.send({ | ||
17 | + isSuccess: false, | ||
18 | + code: 400, | ||
19 | + message: "유효한 카테고리가 아닙니다.", | ||
20 | + }); | ||
21 | + } | ||
22 | + } | ||
23 | + | ||
24 | + try { | ||
25 | + const connection = await pool.getConnection(async (conn) => conn); | ||
26 | + try { | ||
27 | + //mysql접속 관련 부분 정의하는 함수 | ||
28 | + //es6 비구조할당 | ||
29 | + const [rows] = await RestaurantStorage.selectRestaurants(connection, category); | ||
30 | + | ||
31 | + return res.send({ | ||
32 | + result: rows, | ||
33 | + isSuccess: true, | ||
34 | + code: 200, // 요청 성공시 200번대 코드를 뿌려주고, 실패시 400번대 코드 | ||
35 | + message: "식당 목록 요청 성공", | ||
36 | + }); | ||
37 | + } catch (err) { | ||
38 | + logger.error(`readRestaurants Query error\n: ${JSON.stringify(err)}`); | ||
39 | + return false; | ||
40 | + } finally { | ||
41 | + connection.release(); | ||
42 | + } | ||
43 | + } catch (err) { | ||
44 | + logger.error(`readRestaurants DB Connection error\n: ${JSON.stringify(err)}`); | ||
45 | + return false; | ||
46 | + } | ||
47 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +'use strict'; | ||
2 | +//for DB CRUD | ||
3 | +// const db = require("../config/db"); | ||
4 | +const { pool } = require("../config/db"); | ||
5 | + | ||
6 | +exports.selectRestaurants = async function (connection, category) { | ||
7 | + | ||
8 | + const selectAllRestaurantsQuery = `select title, address, category from restaurants where status='A';`; | ||
9 | + const selectCategorizedRestaurantsQuery = `select title, address, category from restaurants where status='A' and category=?;`; | ||
10 | + | ||
11 | + const Params = [category]; | ||
12 | + | ||
13 | + const Query = category ? selectCategorizedRestaurantsQuery : selectAllRestaurantsQuery; | ||
14 | + | ||
15 | + const rows = await connection.query(Query, Params); | ||
16 | + | ||
17 | + return rows; | ||
18 | + | ||
19 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/models/User.js
0 → 100644
1 | +'use strict'; | ||
2 | +//for DB manipulate | ||
3 | +const UserStorage = require("./UserStorage"); | ||
4 | +const {pool} = require("../config/db"); | ||
5 | +const { logger } = require("../config/winston"); | ||
6 | +const jwt = require("jsonwebtoken"); | ||
7 | + | ||
8 | +class User { | ||
9 | + constructor(body) { | ||
10 | + this.body = body; | ||
11 | + } | ||
12 | + | ||
13 | + async login() { | ||
14 | + const client = this.body; | ||
15 | + | ||
16 | + try { | ||
17 | + try { | ||
18 | + const {id, password} = await UserStorage.getUserInfo(connection, client.id); | ||
19 | + // console.log(id, password); | ||
20 | + | ||
21 | + if (id) { | ||
22 | + if (id === client.id && password === client.password) { | ||
23 | + return { success: true}; | ||
24 | + } | ||
25 | + return { success : false, msg: "비밀번호가 틀렸습니다."}; | ||
26 | + } | ||
27 | + return {success: false, msg: "존재하지 않는 아이디입니다."}; | ||
28 | + } catch (err) { | ||
29 | + return {success: false, msg: err}; | ||
30 | + } finally { | ||
31 | + connection.release(); | ||
32 | + } | ||
33 | + } catch (err) { | ||
34 | + logger.error(`login DB Connection error\n: ${JSON.stringify(err)}`); | ||
35 | + return false; | ||
36 | + } | ||
37 | + } | ||
38 | + | ||
39 | + async register() { | ||
40 | + const client = this.body; | ||
41 | + try { | ||
42 | + const connection = await pool.getConnection(async (conn) => conn); | ||
43 | + // console.log(client); | ||
44 | + try { | ||
45 | + const response = await UserStorage.save(connection, client); | ||
46 | + // console.log("테스트2 : ", response); | ||
47 | + return response; | ||
48 | + } catch (err) { | ||
49 | + console.log(err); | ||
50 | + return {success: false, msg : err}; | ||
51 | + } finally { | ||
52 | + connection.release(); | ||
53 | + } | ||
54 | + } catch (err) { | ||
55 | + logger.error(`usersaving DB Connection error\n: ${JSON.stringify(err)}`); | ||
56 | + return false; | ||
57 | + } | ||
58 | + } | ||
59 | +} | ||
60 | +module.exports = User; |
login_main/app/src/models/UserStorage.js
0 → 100644
1 | +'use strict'; | ||
2 | + | ||
3 | +const { pool } = require("../config/db"); | ||
4 | + | ||
5 | + //for DB CRUD | ||
6 | + | ||
7 | + | ||
8 | +class UserStorage { | ||
9 | + constructor(body) { | ||
10 | + this.body = body; | ||
11 | + // this.connection = await pool.getConnection(async (conn) => conn); | ||
12 | + } | ||
13 | + | ||
14 | + // static getUsers(isAll, ...fields) {} | ||
15 | + static async getUserInfo(connection, id) { | ||
16 | + return new Promise((resolve, reject) => { | ||
17 | + const query = "SELECT * FROM users WHERE id = ?;"; | ||
18 | + connection.query(query, [id], (err, data) => { | ||
19 | + if (err) reject(`${err}`); | ||
20 | + // console.log(data[0]); | ||
21 | + resolve(data[0]); | ||
22 | + pool.releaseConnection(conn); | ||
23 | + }); | ||
24 | + }); | ||
25 | + } | ||
26 | + | ||
27 | + static async save (connection, userInfo) { | ||
28 | + const query = "INSERT INTO users(id, name, password) VALUES(?, ?, ?);"; | ||
29 | + try { | ||
30 | + const [rows] = await connection.query({ | ||
31 | + sql: query, | ||
32 | + timeout: 30000, | ||
33 | + values: [userInfo.id, userInfo.name, userInfo.password] | ||
34 | + }); | ||
35 | + // console.log(fields); | ||
36 | + if (rows.affectedRows) { | ||
37 | + return {success: true}; | ||
38 | + } else { | ||
39 | + return {success: false}; | ||
40 | + } | ||
41 | + } catch (error) { | ||
42 | + console.log(error); | ||
43 | + } | ||
44 | + } | ||
45 | + | ||
46 | +} | ||
47 | +// static getUserInfo(id) { | ||
48 | +// return new Promise((resolve, reject) => { | ||
49 | +// const query = "SELECT * FROM users WHERE id = ?;"; | ||
50 | +// pool.query(query, [id], (err, data) => { | ||
51 | +// if (err) reject(`${err}`); | ||
52 | +// // console.log(data[0]); | ||
53 | +// resolve(data[0]); | ||
54 | +// }); | ||
55 | +// }); | ||
56 | +// } | ||
57 | + | ||
58 | +module.exports = UserStorage; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/public/css/home/chat.css
0 → 100644
1 | +* { | ||
2 | + margin: 0; | ||
3 | + padding: 0; | ||
4 | +} | ||
5 | + | ||
6 | +html, body { | ||
7 | + height : 100%; | ||
8 | +} | ||
9 | + | ||
10 | +.wrapper { | ||
11 | + height : 100%; | ||
12 | + width: 100%; | ||
13 | + display: flex; | ||
14 | + flex-direction: column; | ||
15 | + overflow: hidden; | ||
16 | +} | ||
17 | + | ||
18 | +.user-container { | ||
19 | + background: rebeccapurple; | ||
20 | + flex: 1; | ||
21 | + display: flex; | ||
22 | + justify-content: flex-start; | ||
23 | + align-items: center; | ||
24 | + padding: 0.5rem; | ||
25 | +} | ||
26 | + | ||
27 | +.user-container .nickname { | ||
28 | + font-size : 14px; | ||
29 | + margin-right : 1.5rem; | ||
30 | + margin-left : 1rem; | ||
31 | + color:#fff; | ||
32 | +} | ||
33 | + | ||
34 | +.user-container input { | ||
35 | + border-radius: 3px; | ||
36 | + border: none; | ||
37 | + height: 80%; | ||
38 | +} | ||
39 | + | ||
40 | +.display-container { | ||
41 | + background: #D2D2FF; | ||
42 | + flex : 12; | ||
43 | + overflow-y:scroll; | ||
44 | +} | ||
45 | + | ||
46 | +.input-container { | ||
47 | + flex:1; | ||
48 | + display:flex; | ||
49 | + justify-content: stretch; | ||
50 | + align-items: stretch; | ||
51 | +} | ||
52 | + | ||
53 | +.input-container span { | ||
54 | + display: flex; | ||
55 | + justify-content: flex-start; | ||
56 | + align-items:center; | ||
57 | + padding: 0.3rem; | ||
58 | + width: 100%; | ||
59 | +} | ||
60 | + | ||
61 | +.chatting-input { | ||
62 | + font-size:12px; | ||
63 | + height:100%; | ||
64 | + flex:8; | ||
65 | + border:none; | ||
66 | +} | ||
67 | + | ||
68 | +.send-button { | ||
69 | + flex:1; | ||
70 | + background: rebeccapurple; | ||
71 | + color:#fff; | ||
72 | + border:none; | ||
73 | + height:100%; | ||
74 | + border-radius:3px; | ||
75 | +} | ||
76 | + | ||
77 | +.chatting-list li { | ||
78 | + width:50%; | ||
79 | + padding:0.3rem; | ||
80 | + display:flex; | ||
81 | + justify-content: flex-start; | ||
82 | + align-items:flex-end; | ||
83 | + margin-top:0.5rem; | ||
84 | +} | ||
85 | + | ||
86 | +.profile { | ||
87 | + display: flex; | ||
88 | + flex-direction: column; | ||
89 | + align-items: center; | ||
90 | + justify-content: center; | ||
91 | + flex: 1; | ||
92 | +} | ||
93 | + | ||
94 | +.profile .user { | ||
95 | + font-size: 10px; | ||
96 | + margin-bottom: 0.3rem; | ||
97 | +} | ||
98 | + | ||
99 | +.profile .image { | ||
100 | + border-radius: 50%; | ||
101 | + object-fit: cover; | ||
102 | + width: 50px; | ||
103 | + height: 50px; | ||
104 | +} | ||
105 | + | ||
106 | +.message { | ||
107 | + border-radius: 5px; | ||
108 | + padding: 0.5rem; | ||
109 | + font-size: 12px; | ||
110 | + margin: 0 5px; | ||
111 | + flex: 10; | ||
112 | +} | ||
113 | + | ||
114 | +.time { | ||
115 | + font-size: 10px; | ||
116 | + margin: 0 5px; | ||
117 | +} | ||
118 | + | ||
119 | +.sent { | ||
120 | + flex-direction: row-reverse; | ||
121 | + float: right; | ||
122 | +} | ||
123 | + | ||
124 | +.sent .message { | ||
125 | + background: #9986EE; | ||
126 | + color: #fff; | ||
127 | +} | ||
128 | + | ||
129 | +.received .message { | ||
130 | + background: #fff; | ||
131 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/public/css/home/index.css
0 → 100644
1 | +@font-face { | ||
2 | + font-family: 'Noto Sans KR', sans-serif; | ||
3 | + src: url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@700&display=swap'); | ||
4 | + font-weight: normal; | ||
5 | + font-style: normal; | ||
6 | + } | ||
7 | + | ||
8 | + * { | ||
9 | + padding: 0; | ||
10 | + margin: 0; | ||
11 | + box-sizing: border-box; | ||
12 | + } | ||
13 | + | ||
14 | + html { | ||
15 | + font-size: 10px; | ||
16 | + font-family: 'Noto Sans KR', sans-serif; | ||
17 | + } | ||
18 | + | ||
19 | + nav { | ||
20 | + /* background-color: #e69a06; */ | ||
21 | + } | ||
22 | + | ||
23 | + .nav-container { | ||
24 | + padding: 1rem 0; | ||
25 | + display: flex; | ||
26 | + flex-direction: row; | ||
27 | + justify-content: space-between; | ||
28 | + align-items: center; | ||
29 | + } | ||
30 | + | ||
31 | + .nav-title { | ||
32 | + font-size: 2.5rem; | ||
33 | + color :rebeccapurple; | ||
34 | + } | ||
35 | + | ||
36 | + .nav-contact { | ||
37 | + font-size: 1.5rem; | ||
38 | + border: 0; | ||
39 | + background: none; | ||
40 | + cursor: pointer; | ||
41 | + font-family: inherit; | ||
42 | + color :lightslategray; | ||
43 | + margin-right: 30px; | ||
44 | + } | ||
45 | + | ||
46 | + .category-title { | ||
47 | + font-size: 2rem; | ||
48 | + padding : 0 30%; | ||
49 | + } | ||
50 | + | ||
51 | + .category-list { | ||
52 | + padding: 15px 1rem; | ||
53 | + } | ||
54 | + | ||
55 | + .category-item { | ||
56 | + width: 24%; | ||
57 | + height: 5rem; | ||
58 | + background: none; | ||
59 | + border: none; | ||
60 | + font-family: inherit; | ||
61 | + font-size: 1.6rem; | ||
62 | + } | ||
63 | + | ||
64 | + .category-item:hover { | ||
65 | + color: #e69a06; | ||
66 | + cursor: pointer; | ||
67 | + } | ||
68 | + | ||
69 | + .inner { | ||
70 | + padding: 0 1.5rem; | ||
71 | + } | ||
72 | + | ||
73 | + @media all and (min-width: 1024px) { | ||
74 | + .inner { | ||
75 | + max-width: 1024px; | ||
76 | + margin: 0 auto; | ||
77 | + } | ||
78 | + } | ||
79 | + | ||
80 | + /* 카카오맵 CSS */ | ||
81 | + | ||
82 | + body { | ||
83 | + height: 100vh; | ||
84 | + } | ||
85 | + | ||
86 | + nav { | ||
87 | + height: 59px; | ||
88 | + } | ||
89 | + | ||
90 | + main { | ||
91 | + padding-top: 1.5rem; | ||
92 | + height: calc(100% - 59px); | ||
93 | + display: flex; | ||
94 | + flex-direction: column; | ||
95 | + } | ||
96 | + | ||
97 | + #map { | ||
98 | + flex-grow: 1; | ||
99 | + width: 100%; | ||
100 | + height: 100%; | ||
101 | + } | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/public/css/home/login.css
0 → 100644
1 | +@import url(https://fonts.googleapis.com/css?family=Roboto:300); | ||
2 | + | ||
3 | +.login-page { | ||
4 | + width: 360px; | ||
5 | + padding: 12% 0 0; | ||
6 | + margin: auto; | ||
7 | +} | ||
8 | +.form { | ||
9 | + position: relative; | ||
10 | + z-index: 1; | ||
11 | + background: #FFFFFF; | ||
12 | + max-width: 360px; | ||
13 | + margin: 0 auto 100px; | ||
14 | + padding: 45px; | ||
15 | + text-align: center; | ||
16 | + box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24); | ||
17 | +} | ||
18 | +.form input { | ||
19 | + font-family: "Roboto", sans-serif; | ||
20 | + outline: 0; | ||
21 | + background: #f2f2f2; | ||
22 | + width: 100%; | ||
23 | + border: 0; | ||
24 | + margin: 0 0 15px; | ||
25 | + padding: 15px; | ||
26 | + box-sizing: border-box; | ||
27 | + font-size: 14px; | ||
28 | +} | ||
29 | +.form #button { | ||
30 | + font-family: "Roboto", sans-serif; | ||
31 | + text-transform: uppercase; | ||
32 | + outline: 0; | ||
33 | + background: rebeccapurple; | ||
34 | + width: 89%; | ||
35 | + border: 0; | ||
36 | + margin: 0 auto; | ||
37 | + padding: 15px; | ||
38 | + color: #FFFFFF; | ||
39 | + font-size: 14px; | ||
40 | + -webkit-transition: all 0.3 ease; | ||
41 | + transition: all 0.3 ease; | ||
42 | + cursor: pointer; | ||
43 | +} | ||
44 | +.form #button:hover,.form #button:active,.form #button:focus { | ||
45 | + background: rebeccapurple; | ||
46 | +} | ||
47 | +.form .message { | ||
48 | + margin: 15px 0 0; | ||
49 | + color: #b3b3b3; | ||
50 | + font-size: 12px; | ||
51 | +} | ||
52 | +.form .message a { | ||
53 | + color: rebeccapurple; | ||
54 | + text-decoration: none; | ||
55 | +} | ||
56 | +.form .register-form { | ||
57 | + display: none; | ||
58 | +} | ||
59 | +.container { | ||
60 | + position: relative; | ||
61 | + z-index: 1; | ||
62 | + max-width: 300px; | ||
63 | + margin: 0 auto; | ||
64 | +} | ||
65 | +.container:before, .container:after { | ||
66 | + content: ""; | ||
67 | + display: block; | ||
68 | + clear: both; | ||
69 | +} | ||
70 | +.container .info { | ||
71 | + margin: 50px auto; | ||
72 | + text-align: center; | ||
73 | +} | ||
74 | +.container .info h1 { | ||
75 | + margin: 0 0 15px; | ||
76 | + padding: 0; | ||
77 | + font-size: 36px; | ||
78 | + font-weight: 300; | ||
79 | + color: #1a1a1a; | ||
80 | +} | ||
81 | +.container .info span { | ||
82 | + color: #4d4d4d; | ||
83 | + font-size: 12px; | ||
84 | +} | ||
85 | +.container .info span a { | ||
86 | + color: #000000; | ||
87 | + text-decoration: none; | ||
88 | +} | ||
89 | +.container .info span .fa { | ||
90 | + color: #EF3B3A; | ||
91 | +} | ||
92 | + | ||
93 | +/* #id::placeholder #password::placeholder { | ||
94 | + color: black; | ||
95 | + font-style: italic; | ||
96 | + font-weight: bold; | ||
97 | +} */ | ||
98 | + | ||
99 | +body { | ||
100 | + background: rebeccapurple; /* fallback for old browsers */ | ||
101 | + /* background: rebeccapurple; */ | ||
102 | + background: linear-gradient(90deg, rebeccapurple 0%, rebeccapurple 0%); | ||
103 | + font-family: "Roboto", sans-serif; | ||
104 | + -webkit-font-smoothing: antialiased; | ||
105 | + -moz-osx-font-smoothing: grayscale; | ||
106 | +} | ||
107 | + | ||
108 | +/* Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) */ | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/public/css/home/map.css
0 → 100644
1 | +/*인포윈도우 설정*/ | ||
2 | +.infowindow { | ||
3 | + width : 25rem; | ||
4 | + border : 1px solid black; | ||
5 | + border-radius: 5px; | ||
6 | + background-color : white; | ||
7 | +} | ||
8 | + | ||
9 | +.infowindow-title { | ||
10 | + font-size: 3rem; | ||
11 | +} | ||
12 | + | ||
13 | +.infowindow-address { | ||
14 | + font-size: 1.6rem; | ||
15 | +} | ||
16 | + | ||
17 | +.infowindow-btn { | ||
18 | + font-size: 1.6rem; | ||
19 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +@import url(https://fonts.googleapis.com/css?family=Roboto:300); | ||
2 | + | ||
3 | +.login-page { | ||
4 | + width: 360px; | ||
5 | + padding: 8% 0 0; | ||
6 | + margin: auto; | ||
7 | +} | ||
8 | +.form { | ||
9 | + position: relative; | ||
10 | + z-index: 1; | ||
11 | + background: #FFFFFF; | ||
12 | + max-width: 360px; | ||
13 | + margin: 0 auto 100px; | ||
14 | + padding: 45px; | ||
15 | + text-align: center; | ||
16 | + box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24); | ||
17 | +} | ||
18 | +.form input { | ||
19 | + font-family: "Roboto", sans-serif; | ||
20 | + outline: 0; | ||
21 | + background: #f2f2f2; | ||
22 | + width: 100%; | ||
23 | + border: 0; | ||
24 | + margin: 0 0 15px; | ||
25 | + padding: 15px; | ||
26 | + box-sizing: border-box; | ||
27 | + font-size: 14px; | ||
28 | +} | ||
29 | +.form #button { | ||
30 | + font-family: "Roboto", sans-serif; | ||
31 | + text-transform: uppercase; | ||
32 | + outline: 0; | ||
33 | + background: rebeccapurple; | ||
34 | + width: 89%; | ||
35 | + border: 0; | ||
36 | + margin: 0 auto; | ||
37 | + padding: 15px; | ||
38 | + color: #FFFFFF; | ||
39 | + font-size: 14px; | ||
40 | + -webkit-transition: all 0.3 ease; | ||
41 | + transition: all 0.3 ease; | ||
42 | + cursor: pointer; | ||
43 | +} | ||
44 | +.form #button:hover,.form #button:active,.form #button:focus { | ||
45 | + background: rebeccapurple; | ||
46 | +} | ||
47 | +.form .message { | ||
48 | + margin: 15px 0 0; | ||
49 | + color: #b3b3b3; | ||
50 | + font-size: 12px; | ||
51 | +} | ||
52 | +.form .message a { | ||
53 | + color: rebeccapurple; | ||
54 | + text-decoration: none; | ||
55 | +} | ||
56 | +.form .register-form { | ||
57 | + display: none; | ||
58 | +} | ||
59 | +.container { | ||
60 | + position: relative; | ||
61 | + z-index: 1; | ||
62 | + max-width: 300px; | ||
63 | + margin: 0 auto; | ||
64 | +} | ||
65 | +.container:before, .container:after { | ||
66 | + content: ""; | ||
67 | + display: block; | ||
68 | + clear: both; | ||
69 | +} | ||
70 | +.container .info { | ||
71 | + margin: 50px auto; | ||
72 | + text-align: center; | ||
73 | +} | ||
74 | +.container .info h1 { | ||
75 | + margin: 0 0 15px; | ||
76 | + padding: 0; | ||
77 | + font-size: 36px; | ||
78 | + font-weight: 300; | ||
79 | + color: #1a1a1a; | ||
80 | +} | ||
81 | +.container .info span { | ||
82 | + color: #4d4d4d; | ||
83 | + font-size: 12px; | ||
84 | +} | ||
85 | +.container .info span a { | ||
86 | + color: #000000; | ||
87 | + text-decoration: none; | ||
88 | +} | ||
89 | +.container .info span .fa { | ||
90 | + color: #EF3B3A; | ||
91 | +} | ||
92 | + | ||
93 | +/* #id::placeholder #password::placeholder { | ||
94 | + color: black; | ||
95 | + font-style: italic; | ||
96 | + font-weight: bold; | ||
97 | +} */ | ||
98 | + | ||
99 | +body { | ||
100 | + background: rebeccapurple; /* fallback for old browsers */ | ||
101 | + /* background: rebeccapurple; */ | ||
102 | + background: linear-gradient(90deg, rebeccapurple 0%, rebeccapurple 0%); | ||
103 | + font-family: "Roboto", sans-serif; | ||
104 | + -webkit-font-smoothing: antialiased; | ||
105 | + -moz-osx-font-smoothing: grayscale; | ||
106 | +} | ||
107 | + | ||
108 | +/* Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) */ | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +/********************************************************************************* | ||
2 | + * 1. 지도 생성 및 확대 축소 컨트롤러 | ||
3 | + */ | ||
4 | + | ||
5 | + var container = document.getElementById('map'); //지도를 담을 영역의 DOM 레퍼런스 | ||
6 | + var options = { //지도를 생성할 때 필요한 기본 옵션 | ||
7 | + center: new kakao.maps.LatLng(37.54, 126.96), //지도의 중심좌표. | ||
8 | + level: 7 //지도의 레벨(확대, 축소 정도) | ||
9 | + }; | ||
10 | + | ||
11 | + var map = new kakao.maps.Map(container, options); //지도 생성 및 객체 리턴 | ||
12 | + | ||
13 | + // 확대 축소 컨트롤러 | ||
14 | + var zoomControl = new kakao.maps.ZoomControl(); | ||
15 | + map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHT); | ||
16 | + | ||
17 | + /****************************************************************************** | ||
18 | + * 2. 데이터 준비하기(제목, 주소, 카테고리) | ||
19 | + */ | ||
20 | + | ||
21 | +const dataSet = [ | ||
22 | + { | ||
23 | + title: "희락돈까스", | ||
24 | + address: "서울 영등포구 양산로 210", | ||
25 | + category: "양식", | ||
26 | + }, | ||
27 | + { | ||
28 | + title: "즉석우동짜장", | ||
29 | + address: "서울 영등포구 대방천로 260", | ||
30 | + category: "한식", | ||
31 | + }, | ||
32 | + { | ||
33 | + title: "아카사카", | ||
34 | + address: "서울 서초구 서초대로74길 23", | ||
35 | + category: "일식", | ||
36 | + } | ||
37 | +]; | ||
38 | + | ||
39 | +// async function getDataSet(category) { | ||
40 | +// let qs = category; | ||
41 | +// if(!qs) { | ||
42 | +// qs = ""; | ||
43 | +// } | ||
44 | + | ||
45 | +// const dataSet = await axios({ | ||
46 | +// method: "get", // http method | ||
47 | +// url: `http://localhost:3000/restaurants?category=${qs}`, | ||
48 | +// headers: {}, | ||
49 | +// data: {}, | ||
50 | +// }); | ||
51 | + | ||
52 | +// return dataSet.data.result; | ||
53 | +// } | ||
54 | + | ||
55 | +// getDataSet(); | ||
56 | + | ||
57 | + /****************************************************************************** | ||
58 | + * 3. 여러개 마커찍기 | ||
59 | + */ | ||
60 | + | ||
61 | + // 주소-좌표 변환 객체를 생성합니다 | ||
62 | + var geocoder = new kakao.maps.services.Geocoder(); | ||
63 | + | ||
64 | + function getCoordsByAddress(address) { | ||
65 | + return new Promise((resolve, reject) => { | ||
66 | + // 주소로 좌표를 검색합니다 | ||
67 | + geocoder.addressSearch(address, function (result, status) { | ||
68 | + // 정상적으로 검색이 완료됐으면 | ||
69 | + if (status === kakao.maps.services.Status.OK) { | ||
70 | + var coords = new kakao.maps.LatLng(result[0].y, result[0].x); | ||
71 | + return resolve(coords); | ||
72 | + } | ||
73 | + reject(new Error("getCoordsByAddress Error: not valid Address")); | ||
74 | + }); | ||
75 | + }); | ||
76 | + } | ||
77 | + | ||
78 | + setMap(dataSet); | ||
79 | + | ||
80 | + /* | ||
81 | + ************************************************************* | ||
82 | + 4. 마커에 인포윈도우 붙이기 | ||
83 | + */ | ||
84 | + | ||
85 | + function getContent(data) { | ||
86 | + // 인포윈도우 가공하기 | ||
87 | + return ` | ||
88 | + <div class="infowindow"> | ||
89 | + <div class="infowindow-body"> | ||
90 | + <h5 class="infowindow-title">${data.title}</h5> | ||
91 | + <p class="infowindow-address">${data.address}</p> | ||
92 | + <a href='/chat' class="infowindow-btn" target="_blank">채팅방이동</a> | ||
93 | + </div> | ||
94 | + </div> | ||
95 | + `; | ||
96 | + } | ||
97 | + | ||
98 | + async function setMap(dataSet) { | ||
99 | + for (var i = 0; i < dataSet.length; i++) { | ||
100 | + // 마커를 생성합니다 | ||
101 | + let coords = await getCoordsByAddress(dataSet[i].address); | ||
102 | + var marker = new kakao.maps.Marker({ | ||
103 | + map: map, // 마커를 표시할 지도 | ||
104 | + position: coords, | ||
105 | + }); | ||
106 | + | ||
107 | + markerArray.push(marker); | ||
108 | + | ||
109 | + // 마커에 표시할 인포윈도우를 생성합니다 | ||
110 | + var infowindow = new kakao.maps.InfoWindow({ | ||
111 | + content: getContent(dataSet[i]),// 인포윈도우에 표시할 내용 | ||
112 | + }); | ||
113 | + | ||
114 | + infowindowArray.push(infowindow); | ||
115 | + | ||
116 | + // 마커에 mouseover 이벤트와 mouseout 이벤트를 등록합니다 | ||
117 | + // 이벤트 리스너로는 클로저를 만들어 등록합니다 | ||
118 | + // for문에서 클로저를 만들어 주지 않으면 마지막 마커에만 이벤트가 등록됩니다 | ||
119 | + kakao.maps.event.addListener(marker, 'click', makeOverListener(map, marker, infowindow, coords)); | ||
120 | + kakao.maps.event.addListener(map, 'click', makeOutListener(infowindow)); | ||
121 | + } | ||
122 | + } | ||
123 | + | ||
124 | + // 인포윈도우를 표시하는 클로저를 만드는 함수입니다 | ||
125 | + function makeOverListener(map, marker, infowindow, coords) { | ||
126 | + return function() { | ||
127 | + // 1. 클릭시 다른 인포윈도우 닫기 | ||
128 | + closeInfoWindow(); | ||
129 | + infowindow.open(map, marker); | ||
130 | + // 2. 클릭한 곳으로 지도 중심 옮기기 | ||
131 | + map.panTo(coords); | ||
132 | + }; | ||
133 | + } | ||
134 | + | ||
135 | + let infowindowArray = []; | ||
136 | + function closeInfoWindow() { | ||
137 | + for (let infowindow of infowindowArray) { | ||
138 | + infowindow.close(); | ||
139 | + } | ||
140 | + } | ||
141 | + | ||
142 | + // 인포윈도우를 닫는 클로저를 만드는 함수입니다 | ||
143 | + function makeOutListener(infowindow) { | ||
144 | + return function() { | ||
145 | + infowindow.close(); | ||
146 | + }; | ||
147 | + } | ||
148 | + | ||
149 | + /* | ||
150 | + ********************************************** | ||
151 | + 5. 카테고리 분류 | ||
152 | + */ | ||
153 | + | ||
154 | + // 카테고리 | ||
155 | + const categoryMap = { | ||
156 | + korea: "한식", | ||
157 | + china: "중식", | ||
158 | + japan: "일식", | ||
159 | + america: "양식", | ||
160 | + wheat: "분식", | ||
161 | + meat: "구이", | ||
162 | + sushi: "회/초밥", | ||
163 | + etc: "기타", | ||
164 | + }; | ||
165 | + | ||
166 | + const categoryList = document.querySelector(".category-list"); | ||
167 | + categoryList.addEventListener("click", categoryHandler); | ||
168 | + | ||
169 | + async function categoryHandler(event) { | ||
170 | + const categoryId = event.target.id; | ||
171 | + const category = categoryMap[categoryId]; | ||
172 | + | ||
173 | + try { | ||
174 | + // 데이터 분류 | ||
175 | + let categorizedDataSet = await getDataSet(category); | ||
176 | + | ||
177 | + // 기존 마커 삭제 | ||
178 | + closeMarker(); | ||
179 | + | ||
180 | + // 기존 인포윈도우 닫기 | ||
181 | + closeInfoWindow(); | ||
182 | + | ||
183 | + setMap(categorizedDataSet); | ||
184 | + | ||
185 | + } catch (error) { | ||
186 | + console.error(error); | ||
187 | + } | ||
188 | + } | ||
189 | + | ||
190 | + let markerArray = []; | ||
191 | + function closeMarker() { | ||
192 | + for (marker of markerArray) { | ||
193 | + marker.setMap(null); | ||
194 | + } | ||
195 | + } | ||
196 | + | ||
197 | + setMap(dataSet); | ||
198 | + | ||
199 | +// async function setting() { | ||
200 | +// try { | ||
201 | +// const dataSet = await getDataSet(); | ||
202 | +// setMap(dataSet); | ||
203 | + | ||
204 | +// } catch (error) { | ||
205 | +// console.error(error); | ||
206 | +// } | ||
207 | +// } | ||
208 | + | ||
209 | +// setting(); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/public/js/home/chat.js
0 → 100644
1 | +"use strict" | ||
2 | +const socket = io.connect("http://localhost:3000/", {transports:['websocket']}); | ||
3 | + | ||
4 | +const nickname = document.querySelector("#nickname") | ||
5 | +const chatlist = document.querySelector(".chatting-list") | ||
6 | +const chatInput = document.querySelector(".chatting-input") | ||
7 | +const sendButton = document.querySelector(".send-button") | ||
8 | +const displayContainer = document.querySelector(".display-container") | ||
9 | + | ||
10 | +chatInput.addEventListener("keypress", (event)=> { | ||
11 | + if(event.keyCode === 13) { | ||
12 | + send() | ||
13 | + } | ||
14 | +}) | ||
15 | + | ||
16 | +function send() { | ||
17 | + const param = { | ||
18 | + name: nickname.value, | ||
19 | + msg: chatInput.value | ||
20 | + } | ||
21 | + socket.emit("chatting", param) | ||
22 | +} | ||
23 | + | ||
24 | +sendButton.addEventListener("click", send) | ||
25 | + | ||
26 | +socket.on("chatting", (data)=>{ | ||
27 | + console.log(data) | ||
28 | + const {name, msg, time} = data; | ||
29 | + const item = new LiModel(name, msg, time); | ||
30 | + item.makeLi() | ||
31 | + displayContainer.scrollTo(0, displayContainer.scrollHeight) | ||
32 | +}) | ||
33 | + | ||
34 | +//console.log(socket); | ||
35 | + | ||
36 | +function LiModel(name, msg, time) { | ||
37 | + this.name = name; | ||
38 | + this.msg = msg; | ||
39 | + this.time = time; | ||
40 | + | ||
41 | + this.makeLi = ()=>{ | ||
42 | + const li = document.createElement("li"); | ||
43 | + li.classList.add(nickname.value === this.name ? "sent":"received") | ||
44 | + const dom = `<span class="profile"> | ||
45 | + <span class="user">${this.name}</span> | ||
46 | + <img class="image" src="https://placeimg.com/50/50/any" alt="any"> | ||
47 | + </span> | ||
48 | + <span class="message">${this.msg}</span> | ||
49 | + <span class="time">${this.time}</span>`; | ||
50 | + | ||
51 | + li.innerHTML = dom; | ||
52 | + chatlist.appendChild(li) | ||
53 | + } | ||
54 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/public/js/home/login.js
0 → 100644
1 | +'use strict'; | ||
2 | + | ||
3 | +const id = document.querySelector("#id"), | ||
4 | + password = document.querySelector("#password"), | ||
5 | + loginBtn = document.querySelector("#button"); | ||
6 | + | ||
7 | +loginBtn.addEventListener("click", login); | ||
8 | + | ||
9 | +function login() { | ||
10 | + const req = { | ||
11 | + id : id.value, | ||
12 | + password : password.value, | ||
13 | + }; | ||
14 | + | ||
15 | + // console.log("login value : ", id.value); | ||
16 | + fetch("/login", { | ||
17 | + method: "POST", | ||
18 | + headers: { | ||
19 | + "Content-Type": "application/json" | ||
20 | + }, | ||
21 | + body: JSON.stringify(req), | ||
22 | + }) | ||
23 | + .then((res) => res.json()) | ||
24 | + .then((res) => { | ||
25 | + if (res.success) { | ||
26 | + //성공하면 이동 | ||
27 | + location.href = "/"; | ||
28 | + } else { | ||
29 | + alert(res.msg); | ||
30 | + } | ||
31 | + }) | ||
32 | + .catch((err) => { | ||
33 | + console.error("로그인 중 에러 발생"); | ||
34 | + }); | ||
35 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +'use strict'; | ||
2 | + | ||
3 | +const id = document.querySelector("#id"), | ||
4 | + name = document.querySelector("#name"), | ||
5 | + password = document.querySelector("#password"), | ||
6 | + confirmPassword = document.querySelector("#confirm-password"), | ||
7 | + registerBtn = document.querySelector("#button"); | ||
8 | + | ||
9 | +registerBtn.addEventListener("click", register); | ||
10 | + | ||
11 | +async function register() { | ||
12 | + if(!id.value) { | ||
13 | + return alert("아이디를 입력해주세요.") | ||
14 | + } | ||
15 | + if(!name.value) { | ||
16 | + return alert("이름을 입력해주세요.") | ||
17 | + } | ||
18 | + if(!password.value) { | ||
19 | + return alert("비밀번호를 입력해주세요.") | ||
20 | + } | ||
21 | + if(!confirmPassword.value) { | ||
22 | + return alert("비밀번호를 확인해주세요.") | ||
23 | + } | ||
24 | + if (password.value !== confirmPassword.value) { | ||
25 | + return alert("비밀번호가 일치하지 않습니다.") | ||
26 | + } | ||
27 | + | ||
28 | + console.log(1); | ||
29 | + const req = { | ||
30 | + id: id.value, | ||
31 | + name: name.value, | ||
32 | + password: password.value, | ||
33 | + }; | ||
34 | + | ||
35 | + console.log("여기 안찍히나????"); | ||
36 | + | ||
37 | + fetch("/register", { | ||
38 | + method: "POST", | ||
39 | + headers: { | ||
40 | + "Content-Type": "application/json", | ||
41 | + }, | ||
42 | + body: JSON.stringify(req), | ||
43 | + }) | ||
44 | + .then((res) => res.json()) | ||
45 | + .then((res) => { | ||
46 | + if (res.success) { | ||
47 | + location.href = "/login"; | ||
48 | + } else { | ||
49 | + if (res.err) return alert(res.err); | ||
50 | + alert(res.msg); | ||
51 | + } | ||
52 | + }) | ||
53 | + .catch((err) => { | ||
54 | + console.error("회원가입 중 에러 발생"); | ||
55 | + }); | ||
56 | + | ||
57 | + // try { | ||
58 | + // const response = await fetch("/register", { | ||
59 | + // method: "POST", | ||
60 | + // headers: { | ||
61 | + // "Content-Type": "application/json" | ||
62 | + // }, | ||
63 | + // body: JSON.stringify(req), | ||
64 | + // }); | ||
65 | + // console.log("테스트 : ", response); | ||
66 | + // } catch(e) { | ||
67 | + // console.log(e); | ||
68 | + // } | ||
69 | + | ||
70 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/routes/home/home.ctrl.js
0 → 100644
1 | +"use strict"; | ||
2 | + | ||
3 | +const User = require("../../models/User"); | ||
4 | +// const Restaurant = require("../../models/Restaurant"); | ||
5 | + | ||
6 | +const output = { | ||
7 | + hello: (req, res) => { | ||
8 | + res.render("home/index"); | ||
9 | + }, | ||
10 | + | ||
11 | + login: (req, res) => { | ||
12 | + res.render("home/login"); | ||
13 | + }, | ||
14 | + | ||
15 | + register: (req, res) => { | ||
16 | + res.render("home/register"); | ||
17 | + }, | ||
18 | + | ||
19 | + // restaurants: (req, res) => { | ||
20 | + // res.render("home/restaurants"); | ||
21 | + // } | ||
22 | +}; | ||
23 | + | ||
24 | +const process = { | ||
25 | + login: async (req, res) => { | ||
26 | + const user = new User(req.body); | ||
27 | + const response = await user.login(); | ||
28 | + return res.json(response); | ||
29 | + }, | ||
30 | + | ||
31 | + register: async (req, res) => { | ||
32 | + const user = new User(req.body); | ||
33 | + const response = await user.register(); | ||
34 | + // console.log("req.body", req.body); | ||
35 | + // console.log(res.json(response)); | ||
36 | + // console.log(res.json(response).statusCode); => 이거도 잘 찍혔음. | ||
37 | + return res.json(response); | ||
38 | + }, | ||
39 | + | ||
40 | + // restaurants: async (req, res) => { | ||
41 | + // const restaurant = new Restaurant(req.body); | ||
42 | + // const response = await restaurant.restaurants(); | ||
43 | + // return res.json(response); | ||
44 | + // }, | ||
45 | +}; | ||
46 | + | ||
47 | +module.exports = { | ||
48 | + output, | ||
49 | + process, | ||
50 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/routes/home/index.js
0 → 100644
1 | +"use strict"; | ||
2 | + | ||
3 | +const express = require("express"); | ||
4 | +const router = express.Router(); | ||
5 | +const jwtMiddleware = require("../../config/jwtMiddleware"); | ||
6 | +// const Restaurant = require("../../models/Restaurant"); | ||
7 | + | ||
8 | +const ctrl = require("./home.ctrl"); | ||
9 | +const index = require("../../models/Restaurant"); | ||
10 | + | ||
11 | +router.get("/", ctrl.output.hello); | ||
12 | +router.get("/login", ctrl.output.login); | ||
13 | +router.get("/register", ctrl.output.register); | ||
14 | +router.get("/restaurants", index.readRestaurants); | ||
15 | +// router.get("/restaurants", Restaurant.restaurants); | ||
16 | +// router.get("/restaurants", ctrl.output.restaurants); | ||
17 | + | ||
18 | +router.post("/login", ctrl.process.login); | ||
19 | +router.post("/register", ctrl.process.register); | ||
20 | + | ||
21 | +module.exports = router; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
login_main/app/src/views/home/chat.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="en"> | ||
3 | + <head> | ||
4 | + <meta charset="UTF-8" /> | ||
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
7 | + <title>Document</title> | ||
8 | + <link rel="stylesheet" href="/css/home/chat.css" /> | ||
9 | + </head> | ||
10 | + <body> | ||
11 | + <div class="wrapper"> | ||
12 | + <div class="user-container"> | ||
13 | + <lable class="nickname" for="nickname">닉네임설정</lable> | ||
14 | + <input type="text" id="nickname" /> | ||
15 | + </div> | ||
16 | + <div class="display-container"> | ||
17 | + <ul class="chatting-list"></ul> | ||
18 | + </div> | ||
19 | + <div class="input-container"> | ||
20 | + <span> | ||
21 | + <input type="text" class="chatting-input" /> | ||
22 | + <button class="send-button">전송</button> | ||
23 | + </span> | ||
24 | + </div> | ||
25 | + </div> | ||
26 | + | ||
27 | + <script src="/socket.io/socket.io.js"></script> | ||
28 | + <script src="/js/home/chat.js"></script> | ||
29 | + </body> | ||
30 | +</html> |
login_main/app/src/views/home/index.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="en"> | ||
3 | + <head> | ||
4 | + <meta charset="UTF-8" /> | ||
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
7 | + <title>맛집지도</title> | ||
8 | + <meta name="author" content="양주미" /> | ||
9 | + <meta name="description" content="맛집지도 서비스" /> | ||
10 | + <meta name="keywords" content="맛집지도, 맛집추천, 실시간채팅" /> | ||
11 | + <link rel="stylesheet" href="/css/home/index.css" /> | ||
12 | + </head> | ||
13 | + <body> | ||
14 | + <nav> | ||
15 | + <div class="inner"> | ||
16 | + <div class="nav-container"> | ||
17 | + <h1 class="nav-title">맛집지도</h1> | ||
18 | + <button class="nav-contact">Chatting Rooms</button> | ||
19 | + </div> | ||
20 | + </div> | ||
21 | + </nav> | ||
22 | + | ||
23 | + <main> | ||
24 | + <section id="category"> | ||
25 | + <div class="inner"> | ||
26 | + <div class="category-container"> | ||
27 | + <h2 class="category-title">💜맛집지도 카테고리를 선택해보세요💜</h2> | ||
28 | + <div class="category-list"> | ||
29 | + <button class="category-item" id="korea">한식🍚</button> | ||
30 | + <button class="category-item" id="china">중식🍜</button> | ||
31 | + <button class="category-item" id="japan">일식🍙</button> | ||
32 | + <button class="category-item" id="america">양식🍝</button> | ||
33 | + <button class="category-item" id="wheat">분식🍭</button> | ||
34 | + <button class="category-item" id="meat">구이🍖</button> | ||
35 | + <button class="category-item" id="sushi">회/초밥🍣</button> | ||
36 | + <button class="category-item" id="etc">기타🍴</button> | ||
37 | + </div> | ||
38 | + </div> | ||
39 | + </div> | ||
40 | + </section> | ||
41 | + <!-- 카테고리 --> | ||
42 | + <div id="map" class="inner"></div> | ||
43 | + | ||
44 | + <!-- 카카오지도 --> | ||
45 | + </main> | ||
46 | + | ||
47 | + <script | ||
48 | + type="text/javascript" | ||
49 | + src="//dapi.kakao.com/v2/maps/sdk.js?appkey=e55f753363b95e27b799aa6286a6c398&libraries=services" | ||
50 | + ></script> | ||
51 | + <script src="/js/home/axios-index.js"></script> | ||
52 | + <!-- <script | ||
53 | + src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.0.0-alpha.1/axios.min.js" | ||
54 | + integrity="sha512-xIPqqrfvUAc/Cspuj7Bq0UtHNo/5qkdyngx6Vwt+tmbvTLDszzXM0G6c91LXmGrRx8KEPulT+AfOOez+TeVylg==" | ||
55 | + crossorigin="anonymous" | ||
56 | + referrerpolicy="no-referrer" | ||
57 | + ></script> --> | ||
58 | + </body> | ||
59 | +</html> |
login_main/app/src/views/home/login.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="ko"> | ||
3 | + <head> | ||
4 | + <meta charset="UTF-8" /> | ||
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
7 | + <link rel="stylesheet" href="/css/home/login.css" /> | ||
8 | + <script src="/js/home/login.js" defer></script> | ||
9 | + <title>로그인</title> | ||
10 | + </head> | ||
11 | + <body> | ||
12 | + <div class="login-page"> | ||
13 | + <div class="form"> | ||
14 | + <!-- <form class="register-form"> | ||
15 | + <input type="text" placeholder="name" /> | ||
16 | + <input type="password" placeholder="password" /> | ||
17 | + <input type="text" placeholder="email address" /> | ||
18 | + <button>create</button> | ||
19 | + <p class="message">Already registered? <a href="#">Sign In</a></p> | ||
20 | + </form> --> | ||
21 | + <form class="login-form"> | ||
22 | + <input id="id" type="text" placeholder="아이디" /> | ||
23 | + <input id="password" type="password" placeholder="비밀번호" /> | ||
24 | + <p id="button">LOGIN</p> | ||
25 | + <p class="message"> | ||
26 | + 계정이 없으신가요? <a href="/register">회원가입</a> | ||
27 | + </p> | ||
28 | + </form> | ||
29 | + </div> | ||
30 | + </div> | ||
31 | + </body> | ||
32 | +</html> | ||
33 | + | ||
34 | +<!-- Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) --> |
login_main/app/src/views/home/register.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="ko"> | ||
3 | + <head> | ||
4 | + <meta charset="UTF-8" /> | ||
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
7 | + <link rel="stylesheet" href="/css/home/register.css" /> | ||
8 | + <script src="/js/home/register.js" defer></script> | ||
9 | + <title>회원가입</title> | ||
10 | + </head> | ||
11 | + <body> | ||
12 | + <div class="login-page"> | ||
13 | + <div class="form"> | ||
14 | + <!-- <form class="register-form"> | ||
15 | + <input type="text" placeholder="name" /> | ||
16 | + <input type="password" placeholder="password" /> | ||
17 | + <input type="text" placeholder="email address" /> | ||
18 | + <button>create</button> | ||
19 | + <p class="message">Already registered? <a href="#">Sign In</a></p> | ||
20 | + </form> --> | ||
21 | + <form class="login-form"> | ||
22 | + <input id="id" type="text" placeholder="아이디" /> | ||
23 | + <input id="name" type="text" placeholder="이름" /> | ||
24 | + <input id="password" type="password" placeholder="비밀번호" /> | ||
25 | + <input | ||
26 | + id="confirm-password" | ||
27 | + type="password" | ||
28 | + placeholder="비밀번호 확인" | ||
29 | + /> | ||
30 | + <p id="button">SIGN UP</p> | ||
31 | + <p class="message">계정이 있으신가요? <a href="/login">로그인</a></p> | ||
32 | + </form> | ||
33 | + </div> | ||
34 | + </div> | ||
35 | + </body> | ||
36 | +</html> | ||
37 | + | ||
38 | +<!-- Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) --> |
login_main/package-lock.json
0 → 100644
-
Please register or login to post a comment