Merge 'database'
# Conflicts: # app/routes/signup.js
Showing
13 changed files
with
332 additions
and
13 deletions
.gitignore
0 → 100644
1 | +node_modules | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -15,6 +15,12 @@ var csvRouter = require('./routes/csv') | ... | @@ -15,6 +15,12 @@ var csvRouter = require('./routes/csv') |
15 | var app = express(); | 15 | var app = express(); |
16 | var router = express.Router(); | 16 | var router = express.Router(); |
17 | 17 | ||
18 | +var cookieParser = require('cookie-parser') | ||
19 | +var ExpressSession = require('express-session') | ||
20 | + | ||
21 | +var database = require('./database/database'); | ||
22 | +var config = require('./config'); | ||
23 | + | ||
18 | 24 | ||
19 | // get port | 25 | // get port |
20 | var port = process.env.PORT || 3000; | 26 | var port = process.env.PORT || 3000; |
... | @@ -46,6 +52,13 @@ app.use('/signup',signupRouter); // sign up page route | ... | @@ -46,6 +52,13 @@ app.use('/signup',signupRouter); // sign up page route |
46 | app.use('/', indexRouter); // main page route | 52 | app.use('/', indexRouter); // main page route |
47 | 53 | ||
48 | 54 | ||
55 | +//Session 처리 | ||
56 | +app.use(cookieParser()); | ||
57 | +app.use(ExpressSession({ | ||
58 | + secret:'key', | ||
59 | + resave: true, | ||
60 | + saveUninitialized:true | ||
61 | +})); | ||
49 | 62 | ||
50 | //모든 router 처리가 끝난 후 404 오류 페이지 처리 | 63 | //모든 router 처리가 끝난 후 404 오류 페이지 처리 |
51 | var errorHandler = expressErrorHandler({ | 64 | var errorHandler = expressErrorHandler({ |
... | @@ -57,9 +70,25 @@ app.use(expressErrorHandler.httpError(404)); | ... | @@ -57,9 +70,25 @@ app.use(expressErrorHandler.httpError(404)); |
57 | app.use(errorHandler); | 70 | app.use(errorHandler); |
58 | 71 | ||
59 | 72 | ||
73 | +// 프로세스 종료 시에 데이터베이스 연결 해제 | ||
74 | +process.on('SIGTERM', function () { | ||
75 | + console.log("프로세스가 종료됩니다."); | ||
76 | + app.close(); | ||
77 | +}); | ||
78 | + | ||
79 | +app.on('close', function () { | ||
80 | + console.log("Express 서버 객체가 종료됩니다."); | ||
81 | + if (database.db) { | ||
82 | + database.db.close(); | ||
83 | + } | ||
84 | +}); | ||
85 | + | ||
60 | 86 | ||
61 | // for server listening | 87 | // for server listening |
62 | var server = http.createServer(app) | 88 | var server = http.createServer(app) |
63 | server.listen(port,function(){ | 89 | server.listen(port,function(){ |
64 | console.log('익스프레스 서버를 시작했습니다.'); | 90 | console.log('익스프레스 서버를 시작했습니다.'); |
91 | + | ||
92 | + database.init(app, config); | ||
65 | }) | 93 | }) |
94 | + | ... | ... |
app/config.js
0 → 100644
1 | +module.exports = { | ||
2 | + server_port: 3000, | ||
3 | + db_url: 'mongodb://oss:12341234@cluster0.us5lm.mongodb.net/?retryWrites=true&w=majority', | ||
4 | + db_schemas: [ | ||
5 | + {file:'./user_schema', collection:'users3', schemaName:'UserSchema', modelName:'UserModel'} | ||
6 | + ] | ||
7 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
app/database/database.js
0 → 100644
1 | +var mongoose = require('mongoose'); | ||
2 | +var db_url = 'mongodb+srv://oss:12341234@cluster0.us5lm.mongodb.net/?retryWrites=true&w=majority'; | ||
3 | +// database 객체에 db, schema, model 모두 추가 | ||
4 | +var database = {}; | ||
5 | + | ||
6 | +// 초기화를 위해 호출하는 함수 | ||
7 | +database.init = function(app, config) { | ||
8 | + console.log('init() 호출됨.'); | ||
9 | + | ||
10 | + connect(app, config); | ||
11 | +} | ||
12 | + | ||
13 | +//데이터베이스에 연결하고 응답 객체의 속성으로 db 객체 추가 | ||
14 | +function connect(app, config) { | ||
15 | + console.log('connect() 호출됨.'); | ||
16 | + | ||
17 | + // 데이터베이스 연결 : config의 설정 사용 | ||
18 | + mongoose.Promise = global.Promise; // mongoose의 Promise 객체는 global의 Promise 객체 사용하도록 함 | ||
19 | + mongoose.connect(db_url); | ||
20 | + | ||
21 | + database.db = mongoose.connection; | ||
22 | + | ||
23 | + database.db.on('error', console.error.bind(console, 'mongoose connection error.')); | ||
24 | + database.db.on('open', function () { | ||
25 | + console.log('데이터베이스에 연결되었습니다. : ' + db_url); | ||
26 | + | ||
27 | + // config에 등록된 스키마 및 모델 객체 생성 | ||
28 | + createSchema(app, config); | ||
29 | + | ||
30 | + }); | ||
31 | + database.db.on('disconnected', connect); | ||
32 | + | ||
33 | +} | ||
34 | + | ||
35 | +// config에 정의된 스키마 및 모델 객체 생성 | ||
36 | +function createSchema(app, config) { | ||
37 | + var schemaLen = config.db_schemas.length; | ||
38 | + console.log('설정에 정의된 스키마의 수 : %d', schemaLen); | ||
39 | + | ||
40 | + for (var i = 0; i < schemaLen; i++) { | ||
41 | + var curItem = config.db_schemas[i]; | ||
42 | + | ||
43 | + // 모듈 파일에서 모듈 불러온 후 createSchema() 함수 호출하기 | ||
44 | + var curSchema = require(curItem.file).createSchema(mongoose); | ||
45 | + console.log('%s 모듈을 불러들인 후 스키마 정의함.', curItem.file); | ||
46 | + | ||
47 | + // User 모델 정의 | ||
48 | + var curModel = mongoose.model(curItem.collection, curSchema); | ||
49 | + console.log('%s 컬렉션을 위해 모델 정의함.', curItem.collection); | ||
50 | + | ||
51 | + // database 객체에 속성으로 추가 | ||
52 | + database[curItem.schemaName] = curSchema; | ||
53 | + database[curItem.modelName] = curModel; | ||
54 | + console.log('스키마 이름 [%s], 모델 이름 [%s] 이 database 객체의 속성으로 추가됨.', curItem.schemaName, curItem.modelName); | ||
55 | + } | ||
56 | + | ||
57 | + app.set('database', database); | ||
58 | + console.log('database 객체가 app 객체의 속성으로 추가됨.'); | ||
59 | +} | ||
60 | + | ||
61 | + | ||
62 | +// database 객체를 module.exports에 할당 | ||
63 | +module.exports = database; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
app/database/user_schema.js
0 → 100644
1 | +var crypto = require('crypto'); | ||
2 | + | ||
3 | +var Schema = {}; | ||
4 | + | ||
5 | +Schema.createSchema = function(mongoose) { | ||
6 | + | ||
7 | + // 스키마 정의 | ||
8 | + var UserSchema = mongoose.Schema({ | ||
9 | + id: {type: String, required: true, unique: true, 'default':''}, | ||
10 | + hashed_password: {type: String, required: true, 'default':''}, | ||
11 | + salt: {type:String, required:true}, | ||
12 | + name: {type: String, index: 'hashed', 'default':''}, | ||
13 | + age: {type: Number, 'default': -1}, | ||
14 | + created_at: {type: Date, index: {unique: false}, 'default': Date.now}, | ||
15 | + updated_at: {type: Date, index: {unique: false}, 'default': Date.now} | ||
16 | + }); | ||
17 | + | ||
18 | + // password를 virtual 메소드로 정의 : MongoDB에 저장되지 않는 편리한 속성임. 특정 속성을 지정하고 set, get 메소드를 정의함 | ||
19 | + UserSchema | ||
20 | + .virtual('password') | ||
21 | + .set(function(password) { | ||
22 | + this._password = password; | ||
23 | + this.salt = this.makeSalt(); | ||
24 | + this.hashed_password = this.encryptPassword(password); | ||
25 | + console.log('virtual password 호출됨 : ' + this.hashed_password); | ||
26 | + }) | ||
27 | + .get(function() { return this._password }); | ||
28 | + | ||
29 | + // 스키마에 모델 인스턴스에서 사용할 수 있는 메소드 추가 | ||
30 | + // 비밀번호 암호화 메소드 | ||
31 | + UserSchema.method('encryptPassword', function(plainText, inSalt) { | ||
32 | + if (inSalt) { | ||
33 | + return crypto.createHmac('sha1', inSalt).update(plainText).digest('hex'); | ||
34 | + } else { | ||
35 | + return crypto.createHmac('sha1', this.salt).update(plainText).digest('hex'); | ||
36 | + } | ||
37 | + }); | ||
38 | + | ||
39 | + // salt 값 만들기 메소드 | ||
40 | + UserSchema.method('makeSalt', function() { | ||
41 | + return Math.round((new Date().valueOf() * Math.random())) + ''; | ||
42 | + }); | ||
43 | + | ||
44 | + // 인증 메소드 - 입력된 비밀번호와 비교 (true/false 리턴) | ||
45 | + UserSchema.method('authenticate', function(plainText, inSalt, hashed_password) { | ||
46 | + if (inSalt) { | ||
47 | + console.log('authenticate 호출됨 : %s -> %s : %s', plainText, this.encryptPassword(plainText, inSalt), hashed_password); | ||
48 | + return this.encryptPassword(plainText, inSalt) === hashed_password; | ||
49 | + } else { | ||
50 | + console.log('authenticate 호출됨 : %s -> %s : %s', plainText, this.encryptPassword(plainText), this.hashed_password); | ||
51 | + return this.encryptPassword(plainText) === this.hashed_password; | ||
52 | + } | ||
53 | + }); | ||
54 | + | ||
55 | + // 값이 유효한지 확인하는 함수 정의 | ||
56 | + var validatePresenceOf = function(value) { | ||
57 | + return value && value.length; | ||
58 | + }; | ||
59 | + | ||
60 | + // 저장 시의 트리거 함수 정의 (password 필드가 유효하지 않으면 에러 발생) | ||
61 | + UserSchema.pre('save', function(next) { | ||
62 | + if (!this.isNew) return next(); | ||
63 | + | ||
64 | + if (!validatePresenceOf(this.password)) { | ||
65 | + next(new Error('유효하지 않은 password 필드입니다.')); | ||
66 | + } else { | ||
67 | + next(); | ||
68 | + } | ||
69 | + }) | ||
70 | + | ||
71 | + // 필수 속성에 대한 유효성 확인 (길이값 체크) | ||
72 | + UserSchema.path('id').validate(function (id) { | ||
73 | + return id.length; | ||
74 | + }, 'id 칼럼의 값이 없습니다.'); | ||
75 | + | ||
76 | + UserSchema.path('name').validate(function (name) { | ||
77 | + return name.length; | ||
78 | + }, 'name 칼럼의 값이 없습니다.'); | ||
79 | + | ||
80 | + UserSchema.path('hashed_password').validate(function (hashed_password) { | ||
81 | + return hashed_password.length; | ||
82 | + }, 'hashed_password 칼럼의 값이 없습니다.'); | ||
83 | + | ||
84 | + | ||
85 | + // 스키마에 static 메소드 추가 | ||
86 | + UserSchema.static('findById', function(id, callback) { | ||
87 | + return this.find({id:id}, callback); | ||
88 | + }); | ||
89 | + | ||
90 | + UserSchema.static('findAll', function(callback) { | ||
91 | + return this.find({}, callback); | ||
92 | + }); | ||
93 | + | ||
94 | + console.log('UserSchema 정의함.'); | ||
95 | + | ||
96 | + return UserSchema; | ||
97 | +}; | ||
98 | + | ||
99 | +// module.exports에 UserSchema 객체 직접 할당 | ||
100 | +module.exports = Schema; | ||
101 | + |
1 | +var Conn = require('../database/database') | ||
1 | var express = require('express') | 2 | var express = require('express') |
2 | -var router = express.Router() | 3 | +var router = express.Router(); |
3 | 4 | ||
4 | //라우팅 함수 등록 | 5 | //라우팅 함수 등록 |
5 | 6 | ||
6 | router.get('/',function(req,res){ | 7 | router.get('/',function(req,res){ |
8 | + | ||
7 | res.render('login.html') | 9 | res.render('login.html') |
8 | }); | 10 | }); |
9 | 11 | ||
10 | router.post('/process', function(req, res) { | 12 | router.post('/process', function(req, res) { |
11 | - console.log('/process/login 처리함'); | 13 | + console.log('/login/process 처리함'); |
12 | 14 | ||
13 | var paramId = req.body.id || req.query.id; | 15 | var paramId = req.body.id || req.query.id; |
14 | var paramPassword = req.body.password || req.query.password; | 16 | var paramPassword = req.body.password || req.query.password; |
15 | //GET, POST 모두 고려해서 둘 다 검사 | 17 | //GET, POST 모두 고려해서 둘 다 검사 |
16 | 18 | ||
17 | - res.writeHead('200', { 'Content-Type': 'text/html;charset=utf8' }); | 19 | + // 데이터베이스 객체 참조 |
18 | - res.write('<h1>Result form Express Server</h1>'); | 20 | + var database = req.app.get('database'); |
19 | - res.write('<div><p>Param id : ' + paramId + '</p></div>'); | 21 | + |
20 | - res.write('<div><p>Param password : ' + paramPassword + '</p></div>'); | 22 | + // 데이터베이스 객체가 초기화된 경우, authUser 함수 호출하여 사용자 인증 |
21 | - res.write("<br><br><a href ='/login.html'>로그인 페이지로 돌아가기</a>"); | 23 | + if (database.db) { |
22 | - res.end(); | 24 | + authUser(database, paramId, paramPassword, function(err, docs) { |
25 | + // 에러 발생 시, 클라이언트로 에러 전송 | ||
26 | + if (err) { | ||
27 | + console.error('사용자 로그인 중 에러 발생 : ' + err.stack); | ||
28 | + | ||
29 | + res.writeHead('200', {'Content-Type':'text/html;charset=utf8'}); | ||
30 | + res.write('<h2>사용자 로그인 중 에러 발생</h2>'); | ||
31 | + res.write('<p>' + err.stack + '</p>'); | ||
32 | + res.end(); | ||
33 | + | ||
34 | + return; | ||
35 | + } | ||
36 | + | ||
37 | + // 조회된 레코드가 있으면 성공 응답 전송 | ||
38 | + if (docs) { | ||
39 | + console.dir(docs); | ||
40 | + | ||
41 | + // 조회 결과에서 사용자 이름 확인 | ||
42 | + var username = docs[0].name; | ||
43 | + | ||
44 | + res.writeHead('200', {'Content-Type':'text/html;charset=utf8'}); | ||
45 | + res.write('<h1>로그인 성공</h1>'); | ||
46 | + res.write('<div><p>사용자 아이디 : ' + paramId + '</p></div>'); | ||
47 | + res.write('<div><p>사용자 이름 : ' + username + '</p></div>'); | ||
48 | + res.write("<br><br><a href='/login'>다시 로그인하기</a>"); | ||
49 | + res.end(); | ||
50 | + | ||
51 | + } else { // 조회된 레코드가 없는 경우 실패 응답 전송 | ||
52 | + res.writeHead('200', {'Content-Type':'text/html;charset=utf8'}); | ||
53 | + res.write('<h1>로그인 실패</h1>'); | ||
54 | + res.write('<div><p>아이디와 패스워드를 다시 확인하십시오.</p></div>'); | ||
55 | + res.write("<br><br><a href='/login'>다시 로그인하기</a>"); | ||
56 | + res.end(); | ||
57 | + } | ||
58 | + }); | ||
59 | + } else { // 데이터베이스 객체가 초기화되지 않은 경우 실패 응답 전송 | ||
60 | + res.writeHead('200', {'Content-Type':'text/html;charset=utf8'}); | ||
61 | + res.write('<h2>데이터베이스 연결 실패</h2>'); | ||
62 | + res.write('<div><p>데이터베이스에 연결하지 못했습니다.</p></div>'); | ||
63 | + res.end(); | ||
64 | + } | ||
65 | + | ||
23 | }); | 66 | }); |
24 | 67 | ||
68 | +//사용자를 인증하는 함수 : 아이디로 먼저 찾고 비밀번호를 그 다음에 비교하도록 함 | ||
69 | +var authUser = function(database, id, password, callback) { | ||
70 | + console.log('authUser 호출됨.'); | ||
71 | + | ||
72 | + // 1. 아이디를 이용해 검색 | ||
73 | + database.UserModel.findById(id, function(err, results) { | ||
74 | + if (err) { | ||
75 | + callback(err, null); | ||
76 | + return; | ||
77 | + } | ||
78 | + | ||
79 | + console.log('아이디 [%s]로 사용자 검색결과', id); | ||
80 | + console.dir(results); | ||
81 | + | ||
82 | + if (results.length > 0) { | ||
83 | + console.log('아이디와 일치하는 사용자 찾음.'); | ||
84 | + | ||
85 | + // 2. 패스워드 확인 : 모델 인스턴스를 객체를 만들고 authenticate() 메소드 호출 | ||
86 | + var user = new database.UserModel({id:id}); | ||
87 | + var authenticated = user.authenticate(password, results[0]._doc.salt, results[0]._doc.hashed_password); | ||
88 | + if (authenticated) { | ||
89 | + console.log('비밀번호 일치함'); | ||
90 | + callback(null, results); | ||
91 | + } else { | ||
92 | + console.log('비밀번호 일치하지 않음'); | ||
93 | + callback(null, null); | ||
94 | + } | ||
95 | + | ||
96 | + } else { | ||
97 | + console.log("아이디와 일치하는 사용자를 찾지 못함."); | ||
98 | + callback(null, null); | ||
99 | + } | ||
100 | + | ||
101 | + }); | ||
102 | + | ||
103 | +} | ||
104 | + | ||
25 | module.exports = router | 105 | module.exports = router |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -53,9 +53,9 @@ | ... | @@ -53,9 +53,9 @@ |
53 | <div class="row justify-content-center"> | 53 | <div class="row justify-content-center"> |
54 | <div class="col-lg-8 col-xxl-6"> | 54 | <div class="col-lg-8 col-xxl-6"> |
55 | <div class="text-center my-5"> | 55 | <div class="text-center my-5"> |
56 | - <h1>로그인</h1> | 56 | + <h1>회원가입</h1> |
57 | <br> | 57 | <br> |
58 | - <form method="post" action="/login/process"> | 58 | + <form method="post" action="/signup/process"> |
59 | <table> | 59 | <table> |
60 | <tr> | 60 | <tr> |
61 | <td><label>이름</label></td> | 61 | <td><label>이름</label></td> | ... | ... |
1 | -../ejs/bin/cli.js | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +#!/bin/sh | ||
2 | +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") | ||
3 | + | ||
4 | +case `uname` in | ||
5 | + *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;; | ||
6 | +esac | ||
7 | + | ||
8 | +if [ -x "$basedir/node" ]; then | ||
9 | + exec "$basedir/node" "$basedir/../ejs/bin/cli.js" "$@" | ||
10 | +else | ||
11 | + exec node "$basedir/../ejs/bin/cli.js" "$@" | ||
12 | +fi | ... | ... |
1 | -../jake/bin/cli.js | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +#!/bin/sh | ||
2 | +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") | ||
3 | + | ||
4 | +case `uname` in | ||
5 | + *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;; | ||
6 | +esac | ||
7 | + | ||
8 | +if [ -x "$basedir/node" ]; then | ||
9 | + exec "$basedir/node" "$basedir/../jake/bin/cli.js" "$@" | ||
10 | +else | ||
11 | + exec node "$basedir/../jake/bin/cli.js" "$@" | ||
12 | +fi | ... | ... |
1 | -../mime/cli.js | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +#!/bin/sh | ||
2 | +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") | ||
3 | + | ||
4 | +case `uname` in | ||
5 | + *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;; | ||
6 | +esac | ||
7 | + | ||
8 | +if [ -x "$basedir/node" ]; then | ||
9 | + exec "$basedir/node" "$basedir/../mime/cli.js" "$@" | ||
10 | +else | ||
11 | + exec node "$basedir/../mime/cli.js" "$@" | ||
12 | +fi | ... | ... |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
... | @@ -11,10 +11,15 @@ | ... | @@ -11,10 +11,15 @@ |
11 | "dependencies": { | 11 | "dependencies": { |
12 | "body-parser": "^1.20.0", | 12 | "body-parser": "^1.20.0", |
13 | "bootstrap": "^5.1.3", | 13 | "bootstrap": "^5.1.3", |
14 | + "cookie-parser": "^1.4.6", | ||
15 | + "crypto": "^1.0.1", | ||
14 | "ejs": "^3.1.7", | 16 | "ejs": "^3.1.7", |
15 | "express": "^4.18.1", | 17 | "express": "^4.18.1", |
16 | "express-error-handler": "^1.1.0", | 18 | "express-error-handler": "^1.1.0", |
19 | + "express-session": "^1.17.3", | ||
17 | "http": "^0.0.1-security", | 20 | "http": "^0.0.1-security", |
21 | + "mongoose": "^6.3.4", | ||
22 | + "mysql": "^2.18.1", | ||
18 | "path": "^0.12.7", | 23 | "path": "^0.12.7", |
19 | "serve-static": "^1.15.0" | 24 | "serve-static": "^1.15.0" |
20 | } | 25 | } | ... | ... |
-
Please register or login to post a comment