Jeongmin Seo

merge release_v0.1.0 branch

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.*
...\ No newline at end of file ...\ No newline at end of file
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 +//app이라는 express 객체 생성
14 +const app = express();
15 +//라우팅
16 +const home = require("./src/routes/home");
17 +
18 +const port = 3000;
19 +
20 +const jwtMiddleware = require("./src/config/jwtMiddleware");
21 +
22 +// 앱 세팅
23 +app.set("views", "./src/views");
24 +app.set("view engine", "ejs");
25 +app.use(express.static(`${__dirname}/src/public`));
26 +app.use(bodyParser.json());
27 +//url통해 전달되는 데이터에 한글, 공백 등의 문자 오류 해결
28 +app.use(bodyParser.urlencoded({extended: true}));
29 +app.use(compression()); // HTTP 요청을 압축 및 해제
30 +app.use(express.json());
31 +app.use(express.urlencoded({ extended: true }));
32 +app.use(methodOverride());
33 +app.use(cors());
34 +// app.use("/restaurants", require("../app/src/routes/home/restaurant.route"));
35 +require("../app/src/routes/home/restaurant.route")(app);
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.2.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 + "dev": "NODE_ENV=development node index.js",
31 + "prod": "NODE_ENV=production node index.js"
32 + },
33 + "author": "Jeongmin Seo, Jumi Yang",
34 + "license": "ISC",
35 + "keywords": [],
36 + "description": "Node.js API Server"
37 +}
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
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 {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
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;
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
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 +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
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 +async 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 + console.log("id : ", req.id);
36 + try {
37 + const response = await fetch("/register", {
38 + method: "POST",
39 + headers: {
40 + "Content-Type": "application/json"
41 + },
42 + body: JSON.stringify(req),
43 + });
44 + console.log("테스트 : ", response);
45 + } catch(e) {
46 + console.log(e);
47 + }
48 +
49 +}
...\ 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 + // console.log(res.json(response));
35 + // console.log(res.json(response).statusCode); => 이거도 잘 찍혔음.
36 + return res.json(response);
37 + },
38 +
39 + // restaurants: async (req, res) => {
40 + // const restaurant = new Restaurant(req.body);
41 + // const response = await restaurant.restaurants();
42 + // return res.json(response);
43 + // },
44 +};
45 +
46 +module.exports = {
47 + output,
48 + process,
49 +};
...\ 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 jwtMiddleware = require("../../config/jwtMiddleware");
6 +// const Restaurant = require("../../models/Restaurant");
7 +
8 +const ctrl = require("./home.ctrl");
9 +
10 +router.get("/", ctrl.output.hello);
11 +router.get("/login", ctrl.output.login);
12 +router.get("/register", ctrl.output.register);
13 +// router.get("/restaurants", Restaurant.restaurants);
14 +// router.get("/restaurants", ctrl.output.restaurants);
15 +
16 +router.post("/login", ctrl.process.login);
17 +router.post("/register", ctrl.process.register);
18 +
19 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
1 +module.exports = function (app) {
2 + //controller갖고있는 모듈 생성
3 + const index = require("../../models/Restaurant");
4 + // const user = require("../../models/User");
5 + const jwtMiddleware = require("../../config/jwtMiddleware");
6 +
7 + // 식당 목록 조회
8 + app.get("/restaurants", index.readRestaurants);
9 + };
10 +
...\ 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 +}