Jeongmin Seo

Merge branch 'master'

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
app/log/*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
**node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.env.production
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.package-lock.json
\ No newline at end of file
"use strict";
//모듈
const express = require("express");
const bodyParser = require("body-parser");
//환경변수 (운영체제간 gap 없애고자)
const dotenv = require("dotenv");
dotenv.config();
const compression = require("compression");
const methodOverride = require("method-override");
const path = require("path")
const socketIO = require("socket.io")
const moment = require("moment")
const http = require("http");
const app = express();
const server = http.createServer(app);
var cors = require("cors");
const { logger } = require("./src/config/winston");
//app이라는 express 객체 생성
//라우팅
const home = require("./src/routes/home");
const port = 3000;
const jwtMiddleware = require("./src/config/jwtMiddleware");
const io = socketIO(server);
// 앱 세팅
app.set("views", "./src/views");
app.set("view engine", "ejs");
app.use(express.static(`${__dirname}/src/public`));
app.use(express.static(path.join(__dirname, "src")))
app.use(bodyParser.json());
//url통해 전달되는 데이터에 한글, 공백 등의 문자 오류 해결
app.use(bodyParser.urlencoded({extended: true}));
app.use(compression()); // HTTP 요청을 압축 및 해제
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(methodOverride());
app.use(cors());
// app.use("/restaurants", require("../app/src/routes/home/restaurant.route"));
// require("../app/src/routes/home/restaurant.route")(app);
app.use("/", home); //미들웨어 등록해주는 method
io.on("connection", (socket) => {
socket.on("chatting", (data) => {
const { name, msg } = data;
io.emit("chatting", {
name,
msg,
time: moment(new Date()).format("h:ss A"),
});
});
});
logger.info(`${process.env.NODE_ENV} - API Server Start At Port ${port}`);
module.exports = server;
\ No newline at end of file
"use strict";
const server = require("../app");
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log("서버 가동");
});
\ No newline at end of file
This diff is collapsed. Click to expand it.
{
"name": "login",
"version": "1.0.0",
"main": "app.js",
"bin": {
"login": "www.js"
},
"dependencies": {
"body-parser": "^1.20.0",
"compression": "^1.7.4",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dateformat": "^4.3.1",
"dotenv": "^16.0.1",
"ejs": "^3.1.8",
"express": "^4.18.1",
"jsonwebtoken": "^8.5.1",
"method-override": "^3.0.0",
"moment": "^2.29.3",
"mysql": "^2.18.1",
"mysql2": "^2.2.0",
"regex-email": "^1.0.2",
"request": "^2.88.2",
"socket.io": "^4.5.1",
"winston": "^3.2.1",
"winston-daily-rotate-file": "^4.2.1"
},
"devDependencies": {},
"scripts": {
"start": "nodemon ./bin/www.js",
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "NODE_ENV=development node index.js",
"prod": "NODE_ENV=production node index.js"
},
"author": "Jeongmin Seo, Jumi Yang",
"license": "MIT",
"keywords": [],
"description": "Node.js API Server"
}
// const mysql = require("mysql");
const { logger } = require("./winston");
const mysql2 = require("mysql2/promise");
// const db = mysql.createConnection({
// host: process.env.DB_HOST,
// user: process.env.DB_USER,
// password: process.env.DB_PASSWORD,
// database: process.env.DB_DATABASE, //schema
// });
const pool = mysql2.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE, //schema
connectionLimit: 10000,
multipleStatements: true,
});
// db.connect();
module.exports = {
pool: pool,
};
\ No newline at end of file
const jwt = require("jsonwebtoken");
const secret_config = require("./db");
const jwtMiddleware = function (req, res, next) {
// read the token from header or url
const token = req.headers["x-access-token"] || req.query.token;
// token does not exist
if (!token) {
return res.status(403).json({
isSuccess: false,
code: 403,
message: "로그인이 되어 있지 않습니다.",
});
}
try {
const verifiedToken = jwt.verify(token, secret_config.jwtsecret);
req.verifiedToken = verifiedToken;
next();
} catch {
res.status(403).json({
isSuccess: false,
code: 403,
message: "검증 실패",
});
}
};
module.exports = jwtMiddleware;
const { createLogger, format, transports } = require('winston');
require('winston-daily-rotate-file');
const fs = require('fs');
const env = process.env.NODE_ENV || 'development';
const logDir = 'log';
// https://lovemewithoutall.github.io/it/winston-example/
// Create the log directory if it does not exist
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir)
}
const dailyRotateFileTransport = new transports.DailyRotateFile({
level: 'debug',
filename: `${logDir}/%DATE%-smart-push.log`,
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d'
});
const logger = createLogger({
level: env === 'development' ? 'debug' : 'info',
format: format.combine(
format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
format.json()
),
transports: [
new transports.Console({
level: 'info',
format: format.combine(
format.colorize(),
format.printf(
info => `${info.timestamp} ${info.level}: ${info.message}`
)
)
}),
dailyRotateFileTransport
]
});
module.exports = {
logger: logger
};
\ No newline at end of file
'use strict';
//for DB manipulate
const RestaurantStorage = require("./RestaurantStorage");
const {pool} = require("../config/db");
const { logger } = require("../config/winston");
const jwt = require("jsonwebtoken");
exports.readRestaurants = async function (req,res) {
const {category} = req.query;
if (category) {
const validCategory = ["한식", "중식", "일식", "양식", "분식", "구이", "회/초밥", "기타",];
if (!validCategory.includes(category)) {
return res.send({
isSuccess: false,
code: 400,
message: "유효한 카테고리가 아닙니다.",
});
}
}
try {
const connection = await pool.getConnection(async (conn) => conn);
try {
//mysql접속 관련 부분 정의하는 함수
//es6 비구조할당
const [rows] = await RestaurantStorage.selectRestaurants(connection, category);
return res.send({
result: rows,
isSuccess: true,
code: 200, // 요청 성공시 200번대 코드를 뿌려주고, 실패시 400번대 코드
message: "식당 목록 요청 성공",
});
} catch (err) {
logger.error(`readRestaurants Query error\n: ${JSON.stringify(err)}`);
return false;
} finally {
connection.release();
}
} catch (err) {
logger.error(`readRestaurants DB Connection error\n: ${JSON.stringify(err)}`);
return false;
}
}
\ No newline at end of file
'use strict';
//for DB CRUD
// const db = require("../config/db");
const { pool } = require("../config/db");
exports.selectRestaurants = async function (connection, category) {
const selectAllRestaurantsQuery = `select title, address, category from restaurants where status='A';`;
const selectCategorizedRestaurantsQuery = `select title, address, category from restaurants where status='A' and category=?;`;
const Params = [category];
const Query = category ? selectCategorizedRestaurantsQuery : selectAllRestaurantsQuery;
const rows = await connection.query(Query, Params);
return rows;
}
\ No newline at end of file
'use strict';
//for DB manipulate
const UserStorage = require("./UserStorage");
const {pool} = require("../config/db");
const { logger } = require("../config/winston");
const jwt = require("jsonwebtoken");
class User {
constructor(body) {
this.body = body;
}
async login() {
const client = this.body;
try {
const connection = await pool.getConnection(async (conn) => conn);
try {
const { id, password } = await UserStorage.getUserInfo(
connection,
client.id
);
if (id) {
if (id === client.id && password === client.password) {
return { success: true };
}
return { success: false, msg: "비밀번호가 틀렸습니다." };
}
return { success: false, msg: "존재하지 않는 아이디입니다." };
} catch (err) {
return { success: false, msg: err };
} finally {
connection.release();
}
} catch (err) {
logger.error(`login DB Connection error\n: ${JSON.stringify(err)}`);
return false;
}
}
async register() {
const client = this.body;
try {
const connection = await pool.getConnection(async (conn) => conn);
// console.log(client);
try {
const response = await UserStorage.save(connection, client);
// console.log("테스트2 : ", response);
return response;
} catch (err) {
console.log(err);
return {success: false, msg : err};
} finally {
connection.release();
}
} catch (err) {
logger.error(`usersaving DB Connection error\n: ${JSON.stringify(err)}`);
return false;
}
}
}
module.exports = User;
'use strict';
const { pool } = require("../config/db");
//for DB CRUD
class UserStorage {
constructor(body) {
this.body = body;
// this.connection = await pool.getConnection(async (conn) => conn);
}
// static getUsers(isAll, ...fields) {}
static async getUserInfo(connection, id) {
const query = `SELECT * FROM users WHERE id = '${id}';`;
console.log(query);
let [row] = await connection.query(query);
console.log(row[0]);
return row[0];
// , [id], (err, data) => {
// console.log("44444444");
// if (err) reject(`${err}`);
// resolve(data[0]);
// pool.releaseConnection(conn);
// });
}
static async save (connection, userInfo) {
const query = "INSERT INTO users(id, name, password) VALUES(?, ?, ?);";
try {
const [rows] = await connection.query({
sql: query,
timeout: 30000,
values: [userInfo.id, userInfo.name, userInfo.password]
});
// console.log(fields);
if (rows.affectedRows) {
return {success: true};
} else {
return {success: false};
}
} catch (error) {
console.log(error);
}
}
}
// static getUserInfo(id) {
// return new Promise((resolve, reject) => {
// const query = "SELECT * FROM users WHERE id = ?;";
// pool.query(query, [id], (err, data) => {
// if (err) reject(`${err}`);
// // console.log(data[0]);
// resolve(data[0]);
// });
// });
// }
module.exports = UserStorage;
\ No newline at end of file
* {
margin: 0;
padding: 0;
}
html, body {
height : 100%;
}
.wrapper {
height : 100%;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.user-container {
background: rebeccapurple;
flex: 1;
display: flex;
justify-content: flex-start;
align-items: center;
padding: 0.5rem;
}
.user-container .nickname {
font-size : 14px;
margin-right : 1.5rem;
margin-left : 1rem;
color:#fff;
}
.user-container input {
border-radius: 3px;
border: none;
height: 80%;
}
.display-container {
background: #D2D2FF;
flex : 12;
overflow-y:scroll;
}
.input-container {
flex:1;
display:flex;
justify-content: stretch;
align-items: stretch;
}
.input-container span {
display: flex;
justify-content: flex-start;
align-items:center;
padding: 0.3rem;
width: 100%;
}
.chatting-input {
font-size:12px;
height:100%;
flex:8;
border:none;
}
.send-button {
flex:1;
background: rebeccapurple;
color:#fff;
border:none;
height:100%;
border-radius:3px;
}
.chatting-list li {
width:50%;
padding:0.3rem;
display:flex;
justify-content: flex-start;
align-items:flex-end;
margin-top:0.5rem;
}
.profile {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
}
.profile .user {
font-size: 10px;
margin-bottom: 0.3rem;
}
.profile .image {
border-radius: 50%;
object-fit: cover;
width: 50px;
height: 50px;
}
.message {
border-radius: 5px;
padding: 0.5rem;
font-size: 12px;
margin: 0 5px;
flex: 10;
}
.time {
font-size: 10px;
margin: 0 5px;
}
.sent {
flex-direction: row-reverse;
float: right;
}
.sent .message {
background: #9986EE;
color: #fff;
}
.received .message {
background: #fff;
}
\ No newline at end of file
@font-face {
font-family: 'Noto Sans KR', sans-serif;
src: url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@700&display=swap');
font-weight: normal;
font-style: normal;
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html {
font-size: 10px;
font-family: 'Noto Sans KR', sans-serif;
}
nav {
/* background-color: #e69a06; */
}
.nav-container {
padding: 1rem 0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.nav-title {
font-size: 2.5rem;
color :rebeccapurple;
}
.nav-contact {
font-size: 1.5rem;
border: 0;
background: none;
cursor: pointer;
font-family: inherit;
color :lightslategray;
margin-right: 0px;
}
.category-title {
font-size: 2rem;
padding : 0 30%;
}
.category-list {
padding: 15px 1rem;
}
.category-item {
width: 24%;
height: 5rem;
background: none;
border: none;
font-family: inherit;
font-size: 1.6rem;
}
.category-item:hover {
color: #e69a06;
cursor: pointer;
}
.inner {
padding: 0 1.5rem;
}
@media all and (min-width: 1024px) {
.inner {
max-width: 1024px;
margin: 0 auto;
}
}
/* 카카오맵 CSS */
body {
height: 100vh;
}
nav {
height: 59px;
}
main {
padding-top: 1.5rem;
height: calc(100% - 59px);
display: flex;
flex-direction: column;
}
#map {
flex-grow: 1;
width: 100%;
height: 100%;
}
\ No newline at end of file
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
.login-page {
width: 360px;
padding: 12% 0 0;
margin: auto;
}
.form {
position: relative;
z-index: 1;
background: #FFFFFF;
max-width: 360px;
margin: 0 auto 100px;
padding: 45px;
text-align: center;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
.form input {
font-family: "Roboto", sans-serif;
outline: 0;
background: #f2f2f2;
width: 100%;
border: 0;
margin: 0 0 15px;
padding: 15px;
box-sizing: border-box;
font-size: 14px;
}
.form #button {
font-family: "Roboto", sans-serif;
text-transform: uppercase;
outline: 0;
background: rebeccapurple;
width: 89%;
border: 0;
margin: 0 auto;
padding: 15px;
color: #FFFFFF;
font-size: 14px;
-webkit-transition: all 0.3 ease;
transition: all 0.3 ease;
cursor: pointer;
}
.form #button:hover,.form #button:active,.form #button:focus {
background: rebeccapurple;
}
.form .message {
margin: 15px 0 0;
color: #b3b3b3;
font-size: 12px;
}
.form .message a {
color: rebeccapurple;
text-decoration: none;
}
.form .register-form {
display: none;
}
.container {
position: relative;
z-index: 1;
max-width: 300px;
margin: 0 auto;
}
.container:before, .container:after {
content: "";
display: block;
clear: both;
}
.container .info {
margin: 50px auto;
text-align: center;
}
.container .info h1 {
margin: 0 0 15px;
padding: 0;
font-size: 36px;
font-weight: 300;
color: #1a1a1a;
}
.container .info span {
color: #4d4d4d;
font-size: 12px;
}
.container .info span a {
color: #000000;
text-decoration: none;
}
.container .info span .fa {
color: #EF3B3A;
}
/* #id::placeholder #password::placeholder {
color: black;
font-style: italic;
font-weight: bold;
} */
body {
background: rebeccapurple; /* fallback for old browsers */
/* background: rebeccapurple; */
background: linear-gradient(90deg, rebeccapurple 0%, rebeccapurple 0%);
font-family: "Roboto", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) */
\ No newline at end of file
/*인포윈도우 설정*/
.infowindow {
width : 25rem;
border : 1px solid black;
border-radius: 5px;
background-color : white;
}
.infowindow-title {
font-size: 15px;
color: rebeccapurple;
font-weight: 600;
}
.infowindow-address {
font-size: 8px;
}
.infowindow-btn {
font-size: 8px;
}
\ No newline at end of file
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
.login-page {
width: 360px;
padding: 8% 0 0;
margin: auto;
}
.form {
position: relative;
z-index: 1;
background: #FFFFFF;
max-width: 360px;
margin: 0 auto 100px;
padding: 45px;
text-align: center;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
.form input {
font-family: "Roboto", sans-serif;
outline: 0;
background: #f2f2f2;
width: 100%;
border: 0;
margin: 0 0 15px;
padding: 15px;
box-sizing: border-box;
font-size: 14px;
}
.form #button {
font-family: "Roboto", sans-serif;
text-transform: uppercase;
outline: 0;
background: rebeccapurple;
width: 89%;
border: 0;
margin: 0 auto;
padding: 15px;
color: #FFFFFF;
font-size: 14px;
-webkit-transition: all 0.3 ease;
transition: all 0.3 ease;
cursor: pointer;
}
.form #button:hover,.form #button:active,.form #button:focus {
background: rebeccapurple;
}
.form .message {
margin: 15px 0 0;
color: #b3b3b3;
font-size: 12px;
}
.form .message a {
color: rebeccapurple;
text-decoration: none;
}
.form .register-form {
display: none;
}
.container {
position: relative;
z-index: 1;
max-width: 300px;
margin: 0 auto;
}
.container:before, .container:after {
content: "";
display: block;
clear: both;
}
.container .info {
margin: 50px auto;
text-align: center;
}
.container .info h1 {
margin: 0 0 15px;
padding: 0;
font-size: 36px;
font-weight: 300;
color: #1a1a1a;
}
.container .info span {
color: #4d4d4d;
font-size: 12px;
}
.container .info span a {
color: #000000;
text-decoration: none;
}
.container .info span .fa {
color: #EF3B3A;
}
/* #id::placeholder #password::placeholder {
color: black;
font-style: italic;
font-weight: bold;
} */
body {
background: rebeccapurple; /* fallback for old browsers */
/* background: rebeccapurple; */
background: linear-gradient(90deg, rebeccapurple 0%, rebeccapurple 0%);
font-family: "Roboto", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) */
\ No newline at end of file
/*********************************************************************************
* 1. 지도 생성 및 확대 축소 컨트롤러
*/
var container = document.getElementById('map'); //지도를 담을 영역의 DOM 레퍼런스
var options = { //지도를 생성할 때 필요한 기본 옵션
center: new kakao.maps.LatLng(37.248, 127.08), //지도의 중심좌표.
level: 4 //지도의 레벨(확대, 축소 정도)
};
var map = new kakao.maps.Map(container, options); //지도 생성 및 객체 리턴
// 확대 축소 컨트롤러
var zoomControl = new kakao.maps.ZoomControl();
map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHT);
/******************************************************************************
* 2. 데이터 준비하기(제목, 주소, 카테고리)
*/
// const dataSet = [
// {
// title: "희락돈까스",
// address: "서울 영등포구 양산로 210",
// category: "양식",
// },
// {
// title: "즉석우동짜장",
// address: "서울 영등포구 대방천로 260",
// category: "한식",
// },
// {
// title: "아카사카",
// address: "서울 서초구 서초대로74길 23",
// category: "일식",
// }
// ];
async function getDataSet(category) {
let qs = category;
if(!qs) {
qs = "";
}
const dataSet = await axios({
method: "get", // http method
url: `http://52.54.201.217:3000/restaurants?category=${qs}`,
headers: {},
data: {},
});
// console.log(dataSet);
return dataSet.data.result;
}
// }
getDataSet();
/******************************************************************************
* 3. 여러개 마커찍기
*/
// 주소-좌표 변환 객체를 생성합니다
var geocoder = new kakao.maps.services.Geocoder();
function getCoordsByAddress(address) {
return new Promise((resolve, reject) => {
// 주소로 좌표를 검색합니다
geocoder.addressSearch(address, function (result, status) {
// 정상적으로 검색이 완료됐으면
if (status === kakao.maps.services.Status.OK) {
var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
return resolve(coords);
}
reject(new Error("getCoordsByAddress Error: not valid Address"));
});
});
}
// setMap(dataSet);
/*
*************************************************************
4. 마커에 인포윈도우 붙이기
*/
function getContent(data) {
// 인포윈도우 가공하기
return `
<div class="infowindow">
<div class="infowindow-body">
<h5 class="infowindow-title">${data.title}</h5>
<p class="infowindow-address">${data.address}</p>
<a href='/chat' class="infowindow-btn" target="_blank">채팅방이동</a>
</div>
</div>
`;
}
async function setMap(dataSet) {
for (var i = 0; i < dataSet.length; i++) {
// 마커를 생성합니다
let coords = await getCoordsByAddress(dataSet[i].address);
var marker = new kakao.maps.Marker({
map: map, // 마커를 표시할 지도
position: coords,
});
markerArray.push(marker);
// 마커에 표시할 인포윈도우를 생성합니다
var infowindow = new kakao.maps.InfoWindow({
content: getContent(dataSet[i]),// 인포윈도우에 표시할 내용
});
infowindowArray.push(infowindow);
// 마커에 mouseover 이벤트와 mouseout 이벤트를 등록합니다
// 이벤트 리스너로는 클로저를 만들어 등록합니다
// for문에서 클로저를 만들어 주지 않으면 마지막 마커에만 이벤트가 등록됩니다
kakao.maps.event.addListener(marker, 'click', makeOverListener(map, marker, infowindow, coords));
kakao.maps.event.addListener(map, 'click', makeOutListener(infowindow));
}
}
// 인포윈도우를 표시하는 클로저를 만드는 함수입니다
function makeOverListener(map, marker, infowindow, coords) {
return function() {
// 1. 클릭시 다른 인포윈도우 닫기
closeInfoWindow();
infowindow.open(map, marker);
// 2. 클릭한 곳으로 지도 중심 옮기기
map.panTo(coords);
};
}
let infowindowArray = [];
function closeInfoWindow() {
for (let infowindow of infowindowArray) {
infowindow.close();
}
}
// 인포윈도우를 닫는 클로저를 만드는 함수입니다
function makeOutListener(infowindow) {
return function() {
infowindow.close();
};
}
/*
**********************************************
5. 카테고리 분류
*/
// 카테고리
const categoryMap = {
korea: "한식",
china: "중식",
japan: "일식",
america: "양식",
wheat: "분식",
meat: "구이",
sushi: "회/초밥",
etc: "기타",
};
const categoryList = document.querySelector(".category-list");
categoryList.addEventListener("click", categoryHandler);
async function categoryHandler(event) {
const categoryId = event.target.id;
const category = categoryMap[categoryId];
try {
// 데이터 분류
let categorizedDataSet = await getDataSet(category);
// 기존 마커 삭제
closeMarker();
// 기존 인포윈도우 닫기
closeInfoWindow();
setMap(categorizedDataSet);
} catch (error) {
console.error(error);
}
}
let markerArray = [];
function closeMarker() {
for (marker of markerArray) {
marker.setMap(null);
}
}
async function setting() {
try {
const dataSet = await getDataSet();
setMap(dataSet);
} catch (error) {
console.error(error);
}
}
setting();
\ No newline at end of file
"use strict";
const socket = io.connect("http://52.54.201.217:3000/", {
transports: ["websocket"],
});
const nickname = document.querySelector("#nickname")
const chatlist = document.querySelector(".chatting-list")
const chatInput = document.querySelector(".chatting-input")
const sendButton = document.querySelector(".send-button")
const displayContainer = document.querySelector(".display-container")
chatInput.addEventListener("keypress", (event)=> {
if(event.keyCode === 13) {
send()
}
})
function send() {
const param = {
name: nickname.value,
msg: chatInput.value
}
socket.emit("chatting", param)
}
sendButton.addEventListener("click", send)
socket.on("chatting", (data)=>{
console.log(data)
const {name, msg, time} = data;
const item = new LiModel(name, msg, time);
item.makeLi()
displayContainer.scrollTo(0, displayContainer.scrollHeight)
})
//console.log(socket);
function LiModel(name, msg, time) {
this.name = name;
this.msg = msg;
this.time = time;
this.makeLi = ()=>{
const li = document.createElement("li");
li.classList.add(nickname.value === this.name ? "sent":"received")
const dom = `<span class="profile">
<span class="user">${this.name}</span>
<img class="image" src="https://placeimg.com/50/50/any" alt="any">
</span>
<span class="message">${this.msg}</span>
<span class="time">${this.time}</span>`;
li.innerHTML = dom;
chatlist.appendChild(li)
}
}
\ No newline at end of file
'use strict';
const id = document.querySelector("#id"),
password = document.querySelector("#password"),
loginBtn = document.querySelector("#button");
loginBtn.addEventListener("click", login);
function login() {
const req = {
id : id.value,
password : password.value,
};
// console.log("login value : ", id.value);
fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(req),
})
.then((res) => res.json())
.then((res) => {
if (res.success) {
//성공하면 이동
location.href = "/";
} else {
alert(res.msg);
}
})
.catch((err) => {
console.error("로그인 중 에러 발생");
});
}
\ No newline at end of file
'use strict';
const id = document.querySelector("#id"),
name = document.querySelector("#name"),
password = document.querySelector("#password"),
confirmPassword = document.querySelector("#confirm-password"),
registerBtn = document.querySelector("#button");
registerBtn.addEventListener("click", register);
async function register() {
if(!id.value) {
return alert("아이디를 입력해주세요.")
}
if(!name.value) {
return alert("이름을 입력해주세요.")
}
if(!password.value) {
return alert("비밀번호를 입력해주세요.")
}
if(!confirmPassword.value) {
return alert("비밀번호를 확인해주세요.")
}
if (password.value !== confirmPassword.value) {
return alert("비밀번호가 일치하지 않습니다.")
}
const req = {
id: id.value,
name: name.value,
password: password.value,
};
fetch("/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(req),
})
.then((res) => res.json())
.then((res) => {
if (res.success) {
location.href = "/login";
} else {
if (res.err) return alert(res.err);
alert(res.msg);
}
})
.catch((err) => {
console.error("회원가입 중 에러 발생");
});
// try {
// const response = await fetch("/register", {
// method: "POST",
// headers: {
// "Content-Type": "application/json"
// },
// body: JSON.stringify(req),
// });
// console.log("테스트 : ", response);
// } catch(e) {
// console.log(e);
// }
}
\ No newline at end of file
"use strict";
const User = require("../../models/User");
// const Restaurant = require("../../models/Restaurant");
const output = {
hello: (req, res) => {
res.render("home/index");
},
login: (req, res) => {
res.render("home/login");
},
register: (req, res) => {
res.render("home/register");
},
chat: (req, res) => {
res.render("home/chat");
},
// restaurants: (req, res) => {
// res.render("home/restaurants");
// }
};
const process = {
login: async (req, res) => {
const user = new User(req.body);
const response = await user.login();
return res.json(response);
},
register: async (req, res) => {
const user = new User(req.body);
const response = await user.register();
// console.log("req.body", req.body);
// console.log(res.json(response));
// console.log(res.json(response).statusCode); => 이거도 잘 찍혔음.
return res.json(response);
},
// restaurants: async (req, res) => {
// const restaurant = new Restaurant(req.body);
// const response = await restaurant.restaurants();
// return res.json(response);
// },
};
module.exports = {
output,
process,
};
\ No newline at end of file
"use strict";
const express = require("express");
const router = express.Router();
const jwtMiddleware = require("../../config/jwtMiddleware");
// const Restaurant = require("../../models/Restaurant");
const ctrl = require("./home.ctrl");
const index = require("../../models/Restaurant");
router.get("/", ctrl.output.hello);
router.get("/login", ctrl.output.login);
router.get("/register", ctrl.output.register);
router.get("/restaurants", index.readRestaurants);
router.get("/chat", ctrl.output.chat);
// router.get("/restaurants", Restaurant.restaurants);
// router.get("/restaurants", ctrl.output.restaurants);
router.post("/login", ctrl.process.login);
router.post("/register", ctrl.process.register);
module.exports = router;
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>채팅방</title>
<link rel="stylesheet" href="/css/home/chat.css" />
</head>
<body>
<div class="wrapper">
<div class="user-container">
<lable class="nickname" for="nickname">닉네임설정</lable>
<input type="text" id="nickname" />
</div>
<div class="display-container">
<ul class="chatting-list"></ul>
</div>
<div class="input-container">
<span>
<input type="text" class="chatting-input" />
<button class="send-button">전송</button>
</span>
</div>
</div>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.1/socket.io.js"
integrity="sha512-9mpsATI0KClwt+xVZfbcf2lJ8IFBAwsubJ6mI3rtULwyM3fBmQFzj0It4tGqxLOGQwGfJdk/G+fANnxfq9/cew=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script src="/js/home/chat.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>맛집지도</title>
<meta name="author" content="양주미" />
<meta name="description" content="맛집지도 서비스" />
<meta name="keywords" content="맛집지도, 맛집추천, 실시간채팅" />
<link rel="stylesheet" href="/css/home/index.css" />
<link rel="stylesheet" href="/css/home/map.css" />
</head>
<body>
<nav>
<div class="inner">
<div class="nav-container">
<h1 class="nav-title">맛집지도</h1>
<button class="nav-contact">Contact : balljm@naver.com</button>
</div>
</div>
</nav>
<main>
<section id="category">
<div class="inner">
<div class="category-container">
<h2 class="category-title">💜맛집지도 카테고리를 선택해보세요💜</h2>
<div class="category-list">
<button class="category-item" id="korea">한식🍚</button>
<button class="category-item" id="china">중식🍜</button>
<button class="category-item" id="japan">일식🍙</button>
<button class="category-item" id="america">양식🍝</button>
<button class="category-item" id="wheat">분식🍭</button>
<button class="category-item" id="meat">구이🍖</button>
<button class="category-item" id="sushi">회/초밥🍣</button>
<button class="category-item" id="etc">기타🍴</button>
</div>
</div>
</div>
</section>
<!-- 카테고리 -->
<div id="map" class="inner"></div>
<!-- 카카오지도 -->
</main>
<script
type="text/javascript"
src="//dapi.kakao.com/v2/maps/sdk.js?appkey=e55f753363b95e27b799aa6286a6c398&libraries=services"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.0.0-alpha.1/axios.min.js"
integrity="sha512-xIPqqrfvUAc/Cspuj7Bq0UtHNo/5qkdyngx6Vwt+tmbvTLDszzXM0G6c91LXmGrRx8KEPulT+AfOOez+TeVylg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script src="/js/home/axios-index.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="/css/home/login.css" />
<script src="/js/home/login.js" defer></script>
<title>로그인</title>
</head>
<body>
<div class="login-page">
<div class="form">
<!-- <form class="register-form">
<input type="text" placeholder="name" />
<input type="password" placeholder="password" />
<input type="text" placeholder="email address" />
<button>create</button>
<p class="message">Already registered? <a href="#">Sign In</a></p>
</form> -->
<form class="login-form">
<input id="id" type="text" placeholder="아이디" />
<input id="password" type="password" placeholder="비밀번호" />
<p id="button">LOGIN</p>
<p class="message">
계정이 없으신가요? <a href="/register">회원가입</a>
</p>
</form>
</div>
</div>
</body>
</html>
<!-- Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="/css/home/register.css" />
<script src="/js/home/register.js" defer></script>
<title>회원가입</title>
</head>
<body>
<div class="login-page">
<div class="form">
<!-- <form class="register-form">
<input type="text" placeholder="name" />
<input type="password" placeholder="password" />
<input type="text" placeholder="email address" />
<button>create</button>
<p class="message">Already registered? <a href="#">Sign In</a></p>
</form> -->
<form class="login-form">
<input id="id" type="text" placeholder="아이디" />
<input id="name" type="text" placeholder="이름" />
<input id="password" type="password" placeholder="비밀번호" />
<input
id="confirm-password"
type="password"
placeholder="비밀번호 확인"
/>
<p id="button">SIGN UP</p>
<p class="message">계정이 있으신가요? <a href="/login">로그인</a></p>
</form>
</div>
</div>
</body>
</html>
<!-- Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) -->
{
"lockfileVersion": 1
}