Jeongmin Seo

Release before connecting DB

1 -# Favorite restaurant platform
...\ No newline at end of file ...\ No newline at end of file
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 +
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 +
9 +const compression = require("compression");
10 +const methodOverride = require("method-override");
11 +var cors = require("cors");
12 +const { logger } = require("./src/config/winston");
13 +
14 +const app = express();
15 +//라우팅
16 +const home = require("./src/routes/home");
17 +
18 +const port = 3000;
19 +
20 +//controller갖고있는 모듈 생성
21 +// const index = require("../controllers/indexController");
22 +const jwtMiddleware = require("./src/config/jwtMiddleware");
23 +
24 +// 앱 세팅
25 +app.set("views", "./src/views");
26 +app.set("view engine", "ejs");
27 +app.use(express.static(`${__dirname}/src/public`));
28 +app.use(bodyParser.json());
29 +//url통해 전달되는 데이터에 한글, 공백 등의 문자 오류 해결
30 +app.use(bodyParser.urlencoded({extended: true}));
31 +app.use(compression()); // HTTP 요청을 압축 및 해제
32 +app.use(express.json());
33 +app.use(express.urlencoded({ extended: true }));
34 +app.use(methodOverride());
35 +app.use(cors());
36 +
37 +app.use("/", home); //미들웨어 등록해주는 method
38 +logger.info(`${process.env.NODE_ENV} - API Server Start At Port ${port}`);
39 +
40 +module.exports = app;
...\ No newline at end of file ...\ No newline at end of file
1 +"use strict";
2 +
3 +const app = require("../app");
4 +const PORT = process.env.PORT || 3000;
5 +
6 +app.listen(PORT, () => {
7 + console.log("서버 가동");
8 +});
This diff is collapsed. Click to expand it.
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 + "mysql": "^2.18.1",
20 + "mysql2": "^2.0.0",
21 + "regex-email": "^1.0.2",
22 + "request": "^2.88.2",
23 + "winston": "^3.2.1",
24 + "winston-daily-rotate-file": "^4.2.1"
25 + },
26 + "devDependencies": {},
27 + "scripts": {
28 + "start": "nodemon ./bin/www.js",
29 + "test": "echo \"Error: no test specified\" && exit 1"
30 + },
31 + "author": "",
32 + "license": "ISC",
33 + "keywords": [],
34 + "description": ""
35 +}
1 +const mysql = require("mysql");
2 +
3 +const db = mysql.createConnection({
4 + host: process.env.DB_HOST,
5 + user: process.env.DB_USER,
6 + password: process.env.DB_PASSWORD,
7 + database: process.env.DB_DATABASE, //schema
8 +});
9 +
10 +db.connect();
11 +
12 +module.exports = db;
...\ No newline at end of file ...\ No newline at end of file
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;
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
1 +'use strict';
2 +//for DB manipulate
3 +const RestaurantStorage = require("./RestaurantStorage");
4 +
5 +const { db } = require("../config/db");
6 +const { logger } = require("../config/winston");
7 +const jwt = require("jsonwebtoken");
8 +//controller가 db에 접근하기 위해 Dao folder
9 +//controller 실행하다가 db 접근해야하는 일 있을 때 Dao들어가서 query 실행하고 그 값을 반환
10 +// const indexDao = require("../dao/indexDao");
11 +
12 +
13 +class Restaurant {
14 + constructor(body) {
15 + this.body = body;
16 + }
17 +}
18 +
19 +// ex. 식당 테이블 조회
20 +exports.readRestaurants = async function (req, res){
21 + const {category} = req.query;
22 +
23 + if (category) {
24 + const validCategory = ["한식", "중식", "일식", "양식", "분식", "구이", "회/초밥", "기타",];
25 +
26 + if (!validCategory.includes(category)) {
27 + return res.send({
28 + isSuccess: false,
29 + code: 400,
30 + message: "유효한 카테고리가 아닙니다.",
31 + });
32 + }
33 + }
34 +
35 + try {
36 + const connection = await pool.getConnection(async (conn) => conn);
37 + try {
38 + //mysql접속 관련 부분 정의하는 함수
39 + //es6 비구조할당
40 + //indexDao에 selectRestaurants 정의해줘야 함. 그리고 거기서 query문.
41 + const [rows] = await indexDao.selectRestaurants(connection, category);
42 +
43 + return res.send({
44 + result: rows,
45 + isSuccess: true,
46 + code: 200, // 요청 성공시 200번대 코드를 뿌려주고, 실패시 400번대 코드
47 + message: "식당 목록 요청 성공",
48 + });
49 + } catch (err) {
50 + logger.error(`readRestaurants Query error\n: ${JSON.stringify(err)}`);
51 + return false;
52 + } finally {
53 + connection.release();
54 + }
55 + } catch (err) {
56 + logger.error(`readRestaurants DB Connection error\n: ${JSON.stringify(err)}`);
57 + return false;
58 + }
59 +
60 +}
...\ 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 +
5 +
6 +// class RestaurantStorage {
7 +
8 +// static getRestaurantInfo(id) {
9 +// return new Promise((resolve, reject) => {
10 +// const query = "SELECT * FROM users WHERE id = ?;";
11 +// db.query(query, [id], (err, data) => {
12 +// if (err) reject(`${err}`);
13 +// // console.log(data[0]);
14 +// resolve(data[0]);
15 +// });
16 +// });
17 +// }
18 +
19 +// static async save(userInfo) {
20 +// return new Promise((resolve, reject) => {
21 +// const query = "INSERT INTO users(id, name, password) VALUES(?, ?, ?);";
22 +// db.query(
23 +// query,
24 +// [userInfo.id, userInfo.name, userInfo.password],
25 +// (err, data) => {
26 +// if (err) reject(`${err}`);
27 +// // console.log(data[0]);
28 +// resolve({ success: true});
29 +// }
30 +// );
31 +// });
32 +// }
33 +// }
34 +
35 +// module.exports = UserStorage;
...\ No newline at end of file ...\ No newline at end of file
1 +'use strict';
2 +//for DB manipulate
3 +const UserStorage = require("./UserStorage");
4 +
5 +class User {
6 + constructor(body) {
7 + this.body = body;
8 + }
9 +
10 + async login() {
11 + const client = this.body;
12 + try {
13 +
14 + const {id, password} = await UserStorage.getUserInfo(client.id);
15 + // console.log(id, password);
16 +
17 + if (id) {
18 + if (id === client.id && password === client.password) {
19 + return { success: true};
20 + }
21 + return { success : false, msg: "비밀번호가 틀렸습니다."};
22 + }
23 + return {success: false, msg: "존재하지 않는 아이디입니다."};
24 + } catch (err) {
25 + return {success: false, msg: err};
26 + }
27 + }
28 +
29 + async register() {
30 + const client = this.body;
31 + try {
32 + const response = await UserStorage.save(client);
33 + // console.log(response);
34 + return response;
35 + } catch (err) {
36 +
37 + return {success: false, msg : err};
38 + }
39 + }
40 +}
41 +
42 +module.exports = User;
...\ 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 +
5 +
6 +class UserStorage {
7 +
8 + static getUserInfo(id) {
9 + return new Promise((resolve, reject) => {
10 + const query = "SELECT * FROM users WHERE id = ?;";
11 + db.query(query, [id], (err, data) => {
12 + if (err) reject(`${err}`);
13 + // console.log(data[0]);
14 + resolve(data[0]);
15 + });
16 + });
17 + }
18 +
19 + static async save(userInfo) {
20 + return new Promise((resolve, reject) => {
21 + const query = "INSERT INTO users(id, name, password) VALUES(?, ?, ?);";
22 + db.query(
23 + query,
24 + [userInfo.id, userInfo.name, userInfo.password],
25 + (err, data) => {
26 + if (err) reject(`${err}`);
27 + // console.log(data[0]);
28 + resolve({ success: true});
29 + }
30 + );
31 + });
32 + }
33 +}
34 +
35 +module.exports = UserStorage;
...\ No newline at end of file ...\ No newline at end of file
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
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
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 + async function getDataSet(category) {
22 + let qs = category;
23 + if(!qs) {
24 + qs = "";
25 + }
26 +
27 + const dataSet = await axios({
28 + method: "get", // http method
29 + url: `http://localhost:3000/restaurants?category=${qs}`,
30 + headers: {},
31 + data: {},
32 + });
33 +
34 + return dataSet.data.result;
35 + }
36 +
37 + getDataSet();
38 +
39 + /******************************************************************************
40 + * 3. 여러개 마커찍기
41 + */
42 +
43 + // 주소-좌표 변환 객체를 생성합니다
44 + var geocoder = new kakao.maps.services.Geocoder();
45 +
46 + function getCoordsByAddress(address) {
47 + return new Promise((resolve, reject) => {
48 + // 주소로 좌표를 검색합니다
49 + geocoder.addressSearch(address, function (result, status) {
50 + // 정상적으로 검색이 완료됐으면
51 + if (status === kakao.maps.services.Status.OK) {
52 + var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
53 + return resolve(coords);
54 + }
55 + reject(new Error("getCoordsByAddress Error: not valid Address"));
56 + });
57 + });
58 + }
59 +
60 + setMap(dataSet);
61 +
62 + /*
63 + *************************************************************
64 + 4. 마커에 인포윈도우 붙이기
65 + */
66 +
67 + function getContent(data) {
68 + // 인포윈도우 가공하기
69 + return `
70 + <div class="infowindow">
71 + <div class="infowindow-body">
72 + <h5 class="infowindow-title">${data.title}</h5>
73 + <p class="infowindow-address">${data.address}</p>
74 + <a href='/chat' class="infowindow-btn" target="_blank">채팅방이동</a>
75 + </div>
76 + </div>
77 + `;
78 + }
79 +
80 + async function setMap(dataSet) {
81 + for (var i = 0; i < dataSet.length; i++) {
82 + // 마커를 생성합니다
83 + let coords = await getCoordsByAddress(dataSet[i].address);
84 + var marker = new kakao.maps.Marker({
85 + map: map, // 마커를 표시할 지도
86 + position: coords,
87 + });
88 +
89 + markerArray.push(marker);
90 +
91 + // 마커에 표시할 인포윈도우를 생성합니다
92 + var infowindow = new kakao.maps.InfoWindow({
93 + content: getContent(dataSet[i]),// 인포윈도우에 표시할 내용
94 + });
95 +
96 + infowindowArray.push(infowindow);
97 +
98 + // 마커에 mouseover 이벤트와 mouseout 이벤트를 등록합니다
99 + // 이벤트 리스너로는 클로저를 만들어 등록합니다
100 + // for문에서 클로저를 만들어 주지 않으면 마지막 마커에만 이벤트가 등록됩니다
101 + kakao.maps.event.addListener(marker, 'click', makeOverListener(map, marker, infowindow, coords));
102 + kakao.maps.event.addListener(map, 'click', makeOutListener(infowindow));
103 + }
104 + }
105 +
106 + // 인포윈도우를 표시하는 클로저를 만드는 함수입니다
107 + function makeOverListener(map, marker, infowindow, coords) {
108 + return function() {
109 + // 1. 클릭시 다른 인포윈도우 닫기
110 + closeInfoWindow();
111 + infowindow.open(map, marker);
112 + // 2. 클릭한 곳으로 지도 중심 옮기기
113 + map.panTo(coords);
114 + };
115 + }
116 +
117 + let infowindowArray = [];
118 + function closeInfoWindow() {
119 + for (let infowindow of infowindowArray) {
120 + infowindow.close();
121 + }
122 + }
123 +
124 + // 인포윈도우를 닫는 클로저를 만드는 함수입니다
125 + function makeOutListener(infowindow) {
126 + return function() {
127 + infowindow.close();
128 + };
129 + }
130 +
131 + /*
132 + **********************************************
133 + 5. 카테고리 분류
134 + */
135 +
136 + // 카테고리
137 + const categoryMap = {
138 + korea: "한식",
139 + china: "중식",
140 + japan: "일식",
141 + america: "양식",
142 + wheat: "분식",
143 + meat: "구이",
144 + sushi: "회/초밥",
145 + etc: "기타",
146 + };
147 +
148 + const categoryList = document.querySelector(".category-list");
149 + categoryList.addEventListener("click", categoryHandler);
150 +
151 + async function categoryHandler(event) {
152 + const categoryId = event.target.id;
153 + const category = categoryMap[categoryId];
154 +
155 + try {
156 + // 데이터 분류
157 + let categorizedDataSet = await getDataSet(category);
158 +
159 + // 기존 마커 삭제
160 + closeMarker();
161 +
162 + // 기존 인포윈도우 닫기
163 + closeInfoWindow();
164 +
165 + setMap(categorizedDataSet);
166 +
167 + } catch (error) {
168 + console.error(error);
169 + }
170 + }
171 +
172 + let markerArray = [];
173 + function closeMarker() {
174 + for (marker of markerArray) {
175 + marker.setMap(null);
176 + }
177 + }
178 +
179 + async function setting() {
180 + try {
181 + const dataSet = await getDataSet();
182 + setMap(dataSet);
183 +
184 + } catch (error) {
185 + console.error(error);
186 + }
187 + }
188 +
189 + setting();
...\ No newline at end of file ...\ No newline at end of file
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 + fetch("/login", {
16 + method: "POST",
17 + headers: {
18 + "Content-Type": "application/json"
19 + },
20 + body: JSON.stringify(req),
21 + })
22 + .then((res) => res.json())
23 + .then((res) => {
24 + if (res.success) {
25 + //성공하면 이동
26 + location.href = "/";
27 + } else {
28 + alert(res.msg);
29 + }
30 + })
31 + .catch((err) => {
32 + console.error("로그인 중 에러 발생");
33 + });
34 +}
...\ 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 +function register() {
12 + if(!id.value) {
13 + return alert("아이디를 입력해주세요.")
14 + }
15 +
16 + if(!name.value) {
17 + return alert("이름을 입력해주세요.")
18 + }
19 + if(!password.value) {
20 + return alert("비밀번호를 입력해주세요.")
21 + }
22 + if(!confirmPassword.value) {
23 + return alert("비밀번호를 확인해주세요.")
24 + }
25 + if (password.value !== confirmPassword.value) {
26 + return alert("비밀번호가 일치하지 않습니다.")
27 + }
28 +
29 + const req = {
30 + id : id.value,
31 + name : name.value,
32 + password : password.value,
33 + };
34 +
35 + fetch("/register", {
36 + method: "POST",
37 + headers: {
38 + "Content-Type": "application/json"
39 + },
40 + body: JSON.stringify(req),
41 + })
42 + .then((res) => res.json())
43 + .then((res) => {
44 + if (res.success) {
45 + //성공하면 이동
46 + location.href = "/login";
47 + } else {
48 + alert(res.msg);
49 + }
50 + })
51 + .catch((err) => {
52 + console.error("회원가입 중 에러 발생");
53 + });
54 +}
...\ No newline at end of file ...\ No newline at end of file
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 + return res.json(response);
35 + },
36 +
37 + // restaurants: async (req, res) => {
38 + // const restaurant = new Restaurant(req.body);
39 + // const response = await restaurant.restaurants();
40 + // return res.json(response);
41 + // },
42 + // const id = req.body.id,
43 + // password = req.body.password;
44 + // const users = UserStorage.getUsers("id", "password");
45 + // // console.log(UserStorage.getUsers("id", "password","name"));
46 + // const response = {};
47 +
48 + // if (users.id.includes(id)) {
49 + // const idx = users.id.indexOf(id);
50 + // if (users.password[idx] === password) {
51 + // response.success = true;
52 + // return res.json(response);
53 + // }
54 + // }
55 +
56 + // response.success = false;
57 + // response.msg = "로그인에 실패하였습니다."
58 + // return res.json(response);
59 +};
60 +
61 +module.exports = {
62 + output,
63 + process,
64 +};
...\ No newline at end of file ...\ No newline at end of file
1 +"use strict";
2 +
3 +const express = require("express");
4 +const router = express.Router();
5 +// const index = require("../../controllers/indexController");
6 +
7 +const ctrl = require("./home.ctrl");
8 +
9 +router.get("/", ctrl.output.hello);
10 +router.get("/login", ctrl.output.login);
11 +router.get("/register", ctrl.output.register);
12 +router.get("/restaurants", ctrl.output.restaurants);
13 +
14 +router.post("/login", ctrl.process.login);
15 +router.post("/register", ctrl.process.register);
16 +
17 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
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 + </head>
9 + <body></body>
10 +</html>
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 + <script src="/js/home/axios-index.js"></script>
13 + </head>
14 + <body>
15 + <nav>
16 + <div class="inner">
17 + <div class="nav-container">
18 + <h1 class="nav-title">맛집지도</h1>
19 + <button class="nav-contact">Chatting Rooms</button>
20 + </div>
21 + </div>
22 + </nav>
23 +
24 + <main>
25 + <section id="category">
26 + <div class="inner">
27 + <div class="category-container">
28 + <h2 class="category-title">💜맛집지도 카테고리를 선택해보세요💜</h2>
29 + <div class="category-list">
30 + <button class="category-item" id="korea">한식🍚</button>
31 + <button class="category-item" id="china">중식🍜</button>
32 + <button class="category-item" id="japan">일식🍙</button>
33 + <button class="category-item" id="america">양식🍝</button>
34 + <button class="category-item" id="wheat">분식🍭</button>
35 + <button class="category-item" id="meat">구이🍖</button>
36 + <button class="category-item" id="sushi">회/초밥🍣</button>
37 + <button class="category-item" id="etc">기타🍴</button>
38 + </div>
39 + </div>
40 + </div>
41 + </section>
42 + <!-- 카테고리 -->
43 + <div id="map" class="inner"></div>
44 +
45 + <!-- 카카오지도 -->
46 + </main>
47 +
48 + <script
49 + type="text/javascript"
50 + src="//dapi.kakao.com/v2/maps/sdk.js?appkey=e55f753363b95e27b799aa6286a6c398&libraries=services"
51 + ></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>
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) -->
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) -->
1 +{
2 + "lockfileVersion": 1
3 +}