Jeongmin Seo

Release version0.2.0

1 -MIT License
2 -Copyright (c) 2022 Jeongmin Seo
3 -Permission is hereby granted, free of charge, to any person obtaining a copy
4 -of this software and associated documentation files (the "Software"), to deal
5 -in the Software without restriction, including without limitation the rights
6 -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 -copies of the Software, and to permit persons to whom the Software is
8 -furnished to do so, subject to the following conditions:
9 -The above copyright notice and this permission notice shall be included in all
10 -copies or substantial portions of the Software.
11 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 -SOFTWARE.
...\ No newline at end of file ...\ No newline at end of file
1 -# 맛집지도 : 실시간 맛집 정보 공유 플랫폼
2 -카카오맵 API와 실시간 채팅 기능을 통해 사용자들과 소통함으로써 맛집에 대한 정보를 얻을 수 있는 플랫폼입니다.
3 -
4 -<div align="center">
5 - <img src="/uploads/f2c62af097272859d8f37acda908d6aa/그림1.png" width="300" height="300">
6 -</div>
7 -
8 -### 📚 STACKS
9 -![node-16.14.2](https://img.shields.io/badge/Node-16.14.2-green)
10 -![express-4.18.1](https://img.shields.io/badge/Express-4.18.1-green)
11 -![html-latest](https://img.shields.io/badge/html-5.2-green)
12 -![css-latest](https://img.shields.io/badge/css-3-green)
13 -
14 -![nginx-latest](https://img.shields.io/badge/nginx-latest-blue)
15 -![socket.io-latest](https://img.shields.io/badge/socket.io-latest-blue)
16 -![AWS_RDS](https://img.shields.io/badge/AWS_RDS-blue)
17 -
18 -![mysql-2.18.1](https://img.shields.io/badge/Mysql-2.18.1-yellowgreen)
19 -![AWS_RDS](https://img.shields.io/badge/AWS_RDS-yellowgreen)
20 -
21 -
22 -## About the Project
23 -- 로그인을 통해 사이트에 들어갈 수 있으며, 지역별 맛집정보를 파악할 수 있습니다.
24 -- 타 유저들과 실시간 소통을 통해 맛집에 대한 정보를 빠르게 얻을 수 있습니다.
25 -
26 -### Overview
27 -- 추가 예정(이미지)
28 -
29 -### Project Architecture
30 -- 추가 예정(이미지)
31 -
32 -### Built With
33 -- [node.js](https://nodejs.org/ko/)
34 -- [express](https://expressjs.com/ko/)
35 -- [AWS_EC2](https://aws.amazon.com/ko/)
36 -- [AWS_RDS](https://aws.amazon.com/ko/)
37 -- [socket.io](https://socket.io/)
38 -- [nginx](https://www.nginx.com/)
39 -- [mysql](https://www.mysql.com/)
40 -
41 -## Getting Started ( Installation )
42 -### Prerequisites
43 -- 추가 예정
44 -
45 -### Installation
46 -- 추가 예정
47 -
48 -## Roadmap
49 -- [x] 1. 로그인/회원가입 창 구현
50 -- [x] 2. 맛집지도 UI 및 기능 구현
51 -- [x] 3. socket.io를 이용한 실시간 채팅 구현
52 -- [x] 4. AWS, Mysql을 이용한 데이터베이스 구축
53 -- [ ] 5. 서버 구축 및 배포
54 -
55 -## Contributing
56 -프로젝트에 기여하고 싶으신 분들은 아래 절차를 따라주시기 바랍니다.
57 -1. 프로젝트 fork
58 -2. feature branch 생성 (git checkout -b feature/name)
59 -3. commit (git commit -m "Add feature")
60 -4. push (git push origin feature/name)
61 -5. pull request 생성
62 -
63 -## License
64 -MIT 라이센스 아래 사용 가능합니다. LICENSE.txt를 통해 자세한 정보를 확인하세요.
65 -
66 -## Contact
67 -- 서정민 : balljm@khu.ac.kr
68 -- 양주미 : luckyyjm@khu.ac.kr
...\ No newline at end of file ...\ No newline at end of file
1 +# Logs
2 +logs
3 +*.log
4 +npm-debug.log*
5 +yarn-debug.log*
6 +yarn-error.log*
7 +lerna-debug.log*
8 +.pnpm-debug.log*
9 +app/log/*
10 +# Diagnostic reports (https://nodejs.org/api/report.html)
11 +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 +
13 +# Runtime data
14 +pids
15 +*.pid
16 +*.seed
17 +*.pid.lock
18 +
19 +# Directory for instrumented libs generated by jscoverage/JSCover
20 +lib-cov
21 +
22 +# Coverage directory used by tools like istanbul
23 +coverage
24 +*.lcov
25 +
26 +# nyc test coverage
27 +.nyc_output
28 +
29 +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 +.grunt
31 +
32 +# Bower dependency directory (https://bower.io/)
33 +bower_components
34 +
35 +# node-waf configuration
36 +.lock-wscript
37 +
38 +# Compiled binary addons (https://nodejs.org/api/addons.html)
39 +build/Release
40 +
41 +# Dependency directories
42 +**node_modules/
43 +jspm_packages/
44 +
45 +# Snowpack dependency directory (https://snowpack.dev/)
46 +web_modules/
47 +
48 +# TypeScript cache
49 +*.tsbuildinfo
50 +
51 +# Optional npm cache directory
52 +.npm
53 +
54 +# Optional eslint cache
55 +.eslintcache
56 +
57 +# Microbundle cache
58 +.rpt2_cache/
59 +.rts2_cache_cjs/
60 +.rts2_cache_es/
61 +.rts2_cache_umd/
62 +
63 +# Optional REPL history
64 +.node_repl_history
65 +
66 +# Output of 'npm pack'
67 +*.tgz
68 +
69 +# Yarn Integrity file
70 +.yarn-integrity
71 +
72 +# dotenv environment variables file
73 +.env
74 +.env.test
75 +.env.production
76 +
77 +# parcel-bundler cache (https://parceljs.org/)
78 +.cache
79 +.parcel-cache
80 +
81 +# Next.js build output
82 +.next
83 +out
84 +
85 +# Nuxt.js build / generate output
86 +.nuxt
87 +dist
88 +
89 +# Gatsby files
90 +.cache/
91 +# Comment in the public line in if your project uses Gatsby and not Next.js
92 +# https://nextjs.org/blog/next-9-1#public-directory-support
93 +# public
94 +
95 +# vuepress build output
96 +.vuepress/dist
97 +
98 +# Serverless directories
99 +.serverless/
100 +
101 +# FuseBox cache
102 +.fusebox/
103 +
104 +# DynamoDB Local files
105 +.dynamodb/
106 +
107 +# TernJS port file
108 +.tern-port
109 +
110 +# Stores VSCode versions used for testing VSCode extensions
111 +.vscode-test
112 +
113 +# yarn v2
114 +.yarn/cache
115 +.yarn/unplugged
116 +.yarn/build-state.yml
117 +.yarn/install-state.gz
118 +.pnp.*
119 +
120 +.package-lock.json
...\ No newline at end of file ...\ No newline at end of file
1 +"use strict";
2 +//모듈
3 +const express = require("express");
4 +const bodyParser = require("body-parser");
5 +//환경변수 (운영체제간 gap 없애고자)
6 +const dotenv = require("dotenv");
7 +dotenv.config();
8 +const compression = require("compression");
9 +const methodOverride = require("method-override");
10 +const path = require("path")
11 +const socketIO = require("socket.io")
12 +const moment = require("moment")
13 +const http = require("http");
14 +const app = express();
15 +const server = http.createServer(app);
16 +
17 +var cors = require("cors");
18 +const { logger } = require("./src/config/winston");
19 +//app이라는 express 객체 생성
20 +//라우팅
21 +const home = require("./src/routes/home");
22 +const port = 3000;
23 +const jwtMiddleware = require("./src/config/jwtMiddleware");
24 +const io = socketIO(server);
25 +
26 +// 앱 세팅
27 +app.set("views", "./src/views");
28 +app.set("view engine", "ejs");
29 +app.use(express.static(`${__dirname}/src/public`));
30 +app.use(express.static(path.join(__dirname, "src")))
31 +
32 +app.use(bodyParser.json());
33 +//url통해 전달되는 데이터에 한글, 공백 등의 문자 오류 해결
34 +app.use(bodyParser.urlencoded({extended: true}));
35 +
36 +app.use(compression()); // HTTP 요청을 압축 및 해제
37 +app.use(express.json());
38 +app.use(express.urlencoded({ extended: true }));
39 +app.use(methodOverride());
40 +app.use(cors());
41 +// app.use("/restaurants", require("../app/src/routes/home/restaurant.route"));
42 +// require("../app/src/routes/home/restaurant.route")(app);
43 +
44 +app.use("/", home); //미들웨어 등록해주는 method
45 +
46 +io.on('connection', (socket) => {
47 + socket.on("chatting", (data)=>{
48 + const { name, msg } = data;
49 + io.emit("chatting", {
50 + name,
51 + msg,
52 + time: moment.format("h:ss A")
53 + })
54 + })
55 +});
56 +
57 +logger.info(`${process.env.NODE_ENV} - API Server Start At Port ${port}`);
58 +
59 +module.exports = app;
...\ No newline at end of file ...\ No newline at end of file
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 + "moment": "^2.29.3",
20 + "mysql": "^2.18.1",
21 + "mysql2": "^2.2.0",
22 + "regex-email": "^1.0.2",
23 + "request": "^2.88.2",
24 + "socket.io": "^4.5.1",
25 + "winston": "^3.2.1",
26 + "winston-daily-rotate-file": "^4.2.1"
27 + },
28 + "devDependencies": {},
29 + "scripts": {
30 + "start": "nodemon ./bin/www.js",
31 + "test": "echo \"Error: no test specified\" && exit 1",
32 + "dev": "NODE_ENV=development node index.js",
33 + "prod": "NODE_ENV=production node index.js"
34 + },
35 + "author": "Jeongmin Seo, Jumi Yang",
36 + "license": "MIT",
37 + "keywords": [],
38 + "description": "Node.js API Server"
39 +}
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 +* {
2 + margin: 0;
3 + padding: 0;
4 +}
5 +
6 +html, body {
7 + height : 100%;
8 +}
9 +
10 +.wrapper {
11 + height : 100%;
12 + width: 100%;
13 + display: flex;
14 + flex-direction: column;
15 + overflow: hidden;
16 +}
17 +
18 +.user-container {
19 + background: rebeccapurple;
20 + flex: 1;
21 + display: flex;
22 + justify-content: flex-start;
23 + align-items: center;
24 + padding: 0.5rem;
25 +}
26 +
27 +.user-container .nickname {
28 + font-size : 14px;
29 + margin-right : 1.5rem;
30 + margin-left : 1rem;
31 + color:#fff;
32 +}
33 +
34 +.user-container input {
35 + border-radius: 3px;
36 + border: none;
37 + height: 80%;
38 +}
39 +
40 +.display-container {
41 + background: #D2D2FF;
42 + flex : 12;
43 + overflow-y:scroll;
44 +}
45 +
46 +.input-container {
47 + flex:1;
48 + display:flex;
49 + justify-content: stretch;
50 + align-items: stretch;
51 +}
52 +
53 +.input-container span {
54 + display: flex;
55 + justify-content: flex-start;
56 + align-items:center;
57 + padding: 0.3rem;
58 + width: 100%;
59 +}
60 +
61 +.chatting-input {
62 + font-size:12px;
63 + height:100%;
64 + flex:8;
65 + border:none;
66 +}
67 +
68 +.send-button {
69 + flex:1;
70 + background: rebeccapurple;
71 + color:#fff;
72 + border:none;
73 + height:100%;
74 + border-radius:3px;
75 +}
76 +
77 +.chatting-list li {
78 + width:50%;
79 + padding:0.3rem;
80 + display:flex;
81 + justify-content: flex-start;
82 + align-items:flex-end;
83 + margin-top:0.5rem;
84 +}
85 +
86 +.profile {
87 + display: flex;
88 + flex-direction: column;
89 + align-items: center;
90 + justify-content: center;
91 + flex: 1;
92 +}
93 +
94 +.profile .user {
95 + font-size: 10px;
96 + margin-bottom: 0.3rem;
97 +}
98 +
99 +.profile .image {
100 + border-radius: 50%;
101 + object-fit: cover;
102 + width: 50px;
103 + height: 50px;
104 +}
105 +
106 +.message {
107 + border-radius: 5px;
108 + padding: 0.5rem;
109 + font-size: 12px;
110 + margin: 0 5px;
111 + flex: 10;
112 +}
113 +
114 +.time {
115 + font-size: 10px;
116 + margin: 0 5px;
117 +}
118 +
119 +.sent {
120 + flex-direction: row-reverse;
121 + float: right;
122 +}
123 +
124 +.sent .message {
125 + background: #9986EE;
126 + color: #fff;
127 +}
128 +
129 +.received .message {
130 + background: #fff;
131 +}
...\ No newline at end of file ...\ No newline at end of file
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 +const socket = io.connect("http://localhost:3000/", {transports:['websocket']});
3 +
4 +const nickname = document.querySelector("#nickname")
5 +const chatlist = document.querySelector(".chatting-list")
6 +const chatInput = document.querySelector(".chatting-input")
7 +const sendButton = document.querySelector(".send-button")
8 +const displayContainer = document.querySelector(".display-container")
9 +
10 +chatInput.addEventListener("keypress", (event)=> {
11 + if(event.keyCode === 13) {
12 + send()
13 + }
14 +})
15 +
16 +function send() {
17 + const param = {
18 + name: nickname.value,
19 + msg: chatInput.value
20 + }
21 + socket.emit("chatting", param)
22 +}
23 +
24 +sendButton.addEventListener("click", send)
25 +
26 +socket.on("chatting", (data)=>{
27 + console.log(data)
28 + const {name, msg, time} = data;
29 + const item = new LiModel(name, msg, time);
30 + item.makeLi()
31 + displayContainer.scrollTo(0, displayContainer.scrollHeight)
32 +})
33 +
34 +//console.log(socket);
35 +
36 +function LiModel(name, msg, time) {
37 + this.name = name;
38 + this.msg = msg;
39 + this.time = time;
40 +
41 + this.makeLi = ()=>{
42 + const li = document.createElement("li");
43 + li.classList.add(nickname.value === this.name ? "sent":"received")
44 + const dom = `<span class="profile">
45 + <span class="user">${this.name}</span>
46 + <img class="image" src="https://placeimg.com/50/50/any" alt="any">
47 + </span>
48 + <span class="message">${this.msg}</span>
49 + <span class="time">${this.time}</span>`;
50 +
51 + li.innerHTML = dom;
52 + chatlist.appendChild(li)
53 + }
54 +}
...\ No newline at end of file ...\ No newline at end of file
1 +'use strict';
2 +
3 +const id = document.querySelector("#id"),
4 + password = document.querySelector("#password"),
5 + loginBtn = document.querySelector("#button");
6 +
7 +loginBtn.addEventListener("click", login);
8 +
9 +function login() {
10 + const req = {
11 + id : id.value,
12 + password : password.value,
13 + };
14 +
15 + // console.log("login value : ", id.value);
16 + fetch("/login", {
17 + method: "POST",
18 + headers: {
19 + "Content-Type": "application/json"
20 + },
21 + body: JSON.stringify(req),
22 + })
23 + .then((res) => res.json())
24 + .then((res) => {
25 + if (res.success) {
26 + //성공하면 이동
27 + location.href = "/";
28 + } else {
29 + alert(res.msg);
30 + }
31 + })
32 + .catch((err) => {
33 + console.error("로그인 중 에러 발생");
34 + });
35 +}
...\ No newline at end of file ...\ No newline at end of file
1 +'use strict';
2 +
3 +const id = document.querySelector("#id"),
4 + name = document.querySelector("#name"),
5 + password = document.querySelector("#password"),
6 + confirmPassword = document.querySelector("#confirm-password"),
7 + registerBtn = document.querySelector("#button");
8 +
9 +registerBtn.addEventListener("click", register);
10 +
11 +async function register() {
12 + if(!id.value) {
13 + return alert("아이디를 입력해주세요.")
14 + }
15 + if(!name.value) {
16 + return alert("이름을 입력해주세요.")
17 + }
18 + if(!password.value) {
19 + return alert("비밀번호를 입력해주세요.")
20 + }
21 + if(!confirmPassword.value) {
22 + return alert("비밀번호를 확인해주세요.")
23 + }
24 + if (password.value !== confirmPassword.value) {
25 + return alert("비밀번호가 일치하지 않습니다.")
26 + }
27 +
28 + console.log(1);
29 + const req = {
30 + id: id.value,
31 + name: name.value,
32 + password: password.value,
33 + };
34 +
35 + console.log("여기 안찍히나????");
36 +
37 + fetch("/register", {
38 + method: "POST",
39 + headers: {
40 + "Content-Type": "application/json",
41 + },
42 + body: JSON.stringify(req),
43 + })
44 + .then((res) => res.json())
45 + .then((res) => {
46 + if (res.success) {
47 + location.href = "/login";
48 + } else {
49 + if (res.err) return alert(res.err);
50 + alert(res.msg);
51 + }
52 + })
53 + .catch((err) => {
54 + console.error("회원가입 중 에러 발생");
55 + });
56 +
57 + // try {
58 + // const response = await fetch("/register", {
59 + // method: "POST",
60 + // headers: {
61 + // "Content-Type": "application/json"
62 + // },
63 + // body: JSON.stringify(req),
64 + // });
65 + // console.log("테스트 : ", response);
66 + // } catch(e) {
67 + // console.log(e);
68 + // }
69 +
70 +}
...\ No newline at end of file ...\ No newline at end of file
1 +"use strict";
2 +
3 +const User = require("../../models/User");
4 +// const Restaurant = require("../../models/Restaurant");
5 +
6 +const output = {
7 + hello: (req, res) => {
8 + res.render("home/index");
9 + },
10 +
11 + login: (req, res) => {
12 + res.render("home/login");
13 + },
14 +
15 + register: (req, res) => {
16 + res.render("home/register");
17 + },
18 +
19 + // restaurants: (req, res) => {
20 + // res.render("home/restaurants");
21 + // }
22 +};
23 +
24 +const process = {
25 + login: async (req, res) => {
26 + const user = new User(req.body);
27 + const response = await user.login();
28 + return res.json(response);
29 + },
30 +
31 + register: async (req, res) => {
32 + const user = new User(req.body);
33 + const response = await user.register();
34 + // console.log("req.body", req.body);
35 + // console.log(res.json(response));
36 + // console.log(res.json(response).statusCode); => 이거도 잘 찍혔음.
37 + return res.json(response);
38 + },
39 +
40 + // restaurants: async (req, res) => {
41 + // const restaurant = new Restaurant(req.body);
42 + // const response = await restaurant.restaurants();
43 + // return res.json(response);
44 + // },
45 +};
46 +
47 +module.exports = {
48 + output,
49 + process,
50 +};
...\ No newline at end of file ...\ No newline at end of file
1 +"use strict";
2 +
3 +const express = require("express");
4 +const router = express.Router();
5 +const jwtMiddleware = require("../../config/jwtMiddleware");
6 +// const Restaurant = require("../../models/Restaurant");
7 +
8 +const ctrl = require("./home.ctrl");
9 +const index = require("../../models/Restaurant");
10 +
11 +router.get("/", ctrl.output.hello);
12 +router.get("/login", ctrl.output.login);
13 +router.get("/register", ctrl.output.register);
14 +router.get("/restaurants", index.readRestaurants);
15 +// router.get("/restaurants", Restaurant.restaurants);
16 +// router.get("/restaurants", ctrl.output.restaurants);
17 +
18 +router.post("/login", ctrl.process.login);
19 +router.post("/register", ctrl.process.register);
20 +
21 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
1 +<!DOCTYPE html>
2 +<html lang="en">
3 + <head>
4 + <meta charset="UTF-8" />
5 + <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6 + <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 + <title>Document</title>
8 + <link rel="stylesheet" href="/css/home/chat.css" />
9 + </head>
10 + <body>
11 + <div class="wrapper">
12 + <div class="user-container">
13 + <lable class="nickname" for="nickname">닉네임설정</lable>
14 + <input type="text" id="nickname" />
15 + </div>
16 + <div class="display-container">
17 + <ul class="chatting-list"></ul>
18 + </div>
19 + <div class="input-container">
20 + <span>
21 + <input type="text" class="chatting-input" />
22 + <button class="send-button">전송</button>
23 + </span>
24 + </div>
25 + </div>
26 +
27 + <script src="/socket.io/socket.io.js"></script>
28 + <script src="/js/home/chat.js"></script>
29 + </body>
30 +</html>
1 +<!DOCTYPE html>
2 +<html lang="en">
3 + <head>
4 + <meta charset="UTF-8" />
5 + <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6 + <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 + <title>맛집지도</title>
8 + <meta name="author" content="양주미" />
9 + <meta name="description" content="맛집지도 서비스" />
10 + <meta name="keywords" content="맛집지도, 맛집추천, 실시간채팅" />
11 + <link rel="stylesheet" href="/css/home/index.css" />
12 + </head>
13 + <body>
14 + <nav>
15 + <div class="inner">
16 + <div class="nav-container">
17 + <h1 class="nav-title">맛집지도</h1>
18 + <button class="nav-contact">Chatting Rooms</button>
19 + </div>
20 + </div>
21 + </nav>
22 +
23 + <main>
24 + <section id="category">
25 + <div class="inner">
26 + <div class="category-container">
27 + <h2 class="category-title">💜맛집지도 카테고리를 선택해보세요💜</h2>
28 + <div class="category-list">
29 + <button class="category-item" id="korea">한식🍚</button>
30 + <button class="category-item" id="china">중식🍜</button>
31 + <button class="category-item" id="japan">일식🍙</button>
32 + <button class="category-item" id="america">양식🍝</button>
33 + <button class="category-item" id="wheat">분식🍭</button>
34 + <button class="category-item" id="meat">구이🍖</button>
35 + <button class="category-item" id="sushi">회/초밥🍣</button>
36 + <button class="category-item" id="etc">기타🍴</button>
37 + </div>
38 + </div>
39 + </div>
40 + </section>
41 + <!-- 카테고리 -->
42 + <div id="map" class="inner"></div>
43 +
44 + <!-- 카카오지도 -->
45 + </main>
46 +
47 + <script
48 + type="text/javascript"
49 + src="//dapi.kakao.com/v2/maps/sdk.js?appkey=e55f753363b95e27b799aa6286a6c398&libraries=services"
50 + ></script>
51 + <script src="/js/home/axios-index.js"></script>
52 + <!-- <script
53 + src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.0.0-alpha.1/axios.min.js"
54 + integrity="sha512-xIPqqrfvUAc/Cspuj7Bq0UtHNo/5qkdyngx6Vwt+tmbvTLDszzXM0G6c91LXmGrRx8KEPulT+AfOOez+TeVylg=="
55 + crossorigin="anonymous"
56 + referrerpolicy="no-referrer"
57 + ></script> -->
58 + </body>
59 +</html>
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 +}