일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 타사인증
- join()
- 독립영화플랫폼
- MyPick31
- 장고 프로젝트 순서
- til
- 북마크앱
- 장고 프로젝트
- MYSQL
- 알고리즘
- 프로젝트
- Blog
- 장고
- Django
- JavaScript
- mongodb
- Django Blog
- 파이썬 웹프로그래밍 장고
- Exercism
- Bookmark
- 개발
- 예술영화추천
- 자바스크립트
- python
- ART_Cinema
- passport.js
- Node.js
- Algorithm
- 북마크만들기
- 장고 개발 순서
- Today
- Total
Juni_Dev_log
(node.js) [Part.7] 익스프레스 프로젝트를 모듈화하기 - 사용자 정보 관련 기능을 모듈화하기 본문
(node.js) [Part.7] 익스프레스 프로젝트를 모듈화하기 - 사용자 정보 관련 기능을 모듈화하기
Juni_K 2021. 1. 23. 16:01앞에서 만든 app5.js 파일에는 사용자 등록 / 사용자 로그인 / 사용자 리스트 조회 등의 기능이 들어있다.
이제 이 기능을 변경하여 별도의 파일로 분리를 해보도록 하자.
이미 익스프레스 웹 서버를 만들면 라우팅 미들웨어를 사용할 수 있다는 것과 이 미들웨어는 각각의 기능을 별도의 모듈 파일로 분리할 수 있다는 것은 알 수 있다.
여기에서는 사용할 라우팅 미들웨어는 등록된 모델 쪽으로 요청 객체와 응답 객체를 전달한다.
그렇다면, 왜 요청 객체와 응답 객체를 전달하는 것일까? 웹 서버는 클라이언트의 요청에 응답하는 것이 가장 중요한 작업 중 하나이므로, 라우팅 미드웨어에서 모듈 쪽으로 요청 객체와 응답 객체를 전달할 수 있다면 많은 작업을 분라하여 처리할 수 있기 때문이다.
그런데 라우팅 미들웨어에서 사용하는 모듈들은 대부분 데이터베이스 기능을 사용한다. 따라서 데이터베이스 기능도 각 기능을 독립적으로 분리하는 것이 좋다. 그러면, 우선 사용자 기능을 분리하기 전에 데이터베이스 관련 기능 중에서 스키마를 정의하는 부분을 분리해보자.
스키마 파일을 별도의 모듈 파일로 분리하기
스키마는 컬렉션의 구조를 결정하는 것으로, 데이터베이스와 분리해서 독립적으로 정의할 수 없다.
먼저, DateExample 프로젝트에 있는 app5.js 를 복사하여 ModuleExample 프로젝트 안에 app.js 파일을 만든다.
그리고 ModuleExample 폴더 안에 database 폴더를 만든 후 그 폴더 안에 user_schema.js 파일을 만든다.
새로 만든 파일 안에는 UserSchema 객체를 만들 때 사용하는 코드를 넣어준다.
#user_schema.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
var crypto = require('crypto');
var Schema = {};
Schema.createSchema = function(mongoose) {
// 스키마 정의
var UserSchema = mongoose.Schema({
id: {type: String, required: true, unique: true, 'default':''},
hashed_password: {type: String, required: true, 'default':''},
salt: {type:String, required:true},
name: {type: String, index: 'hashed', 'default':''},
age: {type: Number, 'default': -1},
created_at: {type: Date, index: {unique: false}, 'default': Date.now},
updated_at: {type: Date, index: {unique: false}, 'default': Date.now}
});
// password를 virtual 메소드로 정의 : MongoDB에 저장되지 않는 편리한 속성임. 특정 속성을 지정하고 set, get 메소드를 정의함
UserSchema
.virtual('password')
.set(function(password) {
this._password = password;
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(password);
console.log('virtual password 호출됨 : ' + this.hashed_password);
})
.get(function() { return this._password });
// 스키마에 모델 인스턴스에서 사용할 수 있는 메소드 추가
// 비밀번호 암호화 메소드
UserSchema.method('encryptPassword', function(plainText, inSalt) {
if (inSalt) {
return crypto.createHmac('sha1', inSalt).update(plainText).digest('hex');
} else {
return crypto.createHmac('sha1', this.salt).update(plainText).digest('hex');
}
});
// salt 값 만들기 메소드
UserSchema.method('makeSalt', function() {
return Math.round((new Date().valueOf() * Math.random())) + '';
});
// 인증 메소드 - 입력된 비밀번호와 비교 (true/false 리턴)
UserSchema.method('authenticate', function(plainText, inSalt, hashed_password) {
if (inSalt) {
console.log('authenticate 호출됨 : %s -> %s : %s', plainText, this.encryptPassword(plainText, inSalt), hashed_password);
return this.encryptPassword(plainText, inSalt) === hashed_password;
} else {
console.log('authenticate 호출됨 : %s -> %s : %s', plainText, this.encryptPassword(plainText), this.hashed_password);
return this.encryptPassword(plainText) === this.hashed_password;
}
});
// 값이 유효한지 확인하는 함수 정의
var validatePresenceOf = function(value) {
return value && value.length;
};
// 저장 시의 트리거 함수 정의 (password 필드가 유효하지 않으면 에러 발생)
UserSchema.pre('save', function(next) {
if (!this.isNew) return next();
if (!validatePresenceOf(this.password)) {
next(new Error('유효하지 않은 password 필드입니다.'));
} else {
next();
}
})
// 필수 속성에 대한 유효성 확인 (길이값 체크)
UserSchema.path('id').validate(function (id) {
return id.length;
}, 'id 칼럼의 값이 없습니다.');
UserSchema.path('name').validate(function (name) {
return name.length;
}, 'name 칼럼의 값이 없습니다.');
UserSchema.path('hashed_password').validate(function (hashed_password) {
return hashed_password.length;
}, 'hashed_password 칼럼의 값이 없습니다.');
// 스키마에 static 메소드 추가
UserSchema.static('findById', function(id, callback) {
return this.find({id:id}, callback);
});
UserSchema.static('findAll', function(callback) {
return this.find({}, callback);
});
console.log('UserSchema 정의함.');
return UserSchema;
};
// module.exports에 UserSchema 객체 직접 할당
module.exports = Schema;
|
cs |
스키마를 만들려면 mongoose 모듈 객체를 참조해야한다.
이 모듈 객체는 app.js 파일에서 불러들인 후 변수에 할당할 것이므로 user_schema.js 파일에서는 이 객체를 파라미터로 전달받아야 사용할 수 있다.
물론, require() 메소드로 mongoose 모듈을 직접 불러들일 수도 있지만, app.js 파일에서 mongoose 모듈에 설정한 정보를 똑같이 사용하려면 파라미터로 전달받아야한다.
이 때문에, createSchema 라는 함수를 새로 만들고 mongoose 객체를 파라미터로 전달받게한다.
module.exports에 객체를 할당하는 방식을 사용하기 위해서 먼저 Schema 객체를 정의한 후 그안에 createSchema 속성을 추가한다.
이렇게 하면 이 모듈을 불러오는 쪽에서 createSchema() 함수를 호출하면서 mongoose 객체를 파라미터로 전달할 수 있다. createSchema는 기존에 app.js 에서 사용하는 코드와 동일하다.
- require() 메소드로 crypto 모듈을 불러온다. (스키마를 만들고 저장할 때 암호화 모듈을 사용하기 때문에)
- module.exports에 Schema 객체를 할당한다.
app.js 에서 데이터베이스 스키마를 만들어야하는 코드 부분에 createSchema() 함수를 호출하도록 작성한다.
#app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
|
...
// user 스키마 및 모델 객체 생성
function createUserSchema(){
// user_schema.js 모듈 불러오기
UserSchema = require('./database/user_schema').createSchema(mongoose);
// UserModel 모델 정의
UserModel = mongoose.model("user3",UserSchema);
console.log('UserModel 정의함.');
}
...
|
cs |
스키마를 만드는 코드는 createUserSchema() 함수 안에 들어있다.
이 부분을 user_schema.js 에 분리했기 때문에 파일을 불러온다.
- require() 로 반환되는 객체는 모듈 파일에서 module.exports 에 할당한 Schema 객체이기 때문에 createSchema() 함수를 호출할 수 있다.
- createSchema() 함수를 호출하면서, mogoose 객체를 파라미터로 전달하면, 새로 만들어진 사용자 스키마 객체가 반환된다.
- 스키마 객체를 사용해 모델 객체를 만들고, UserModel 변수에 할당한다.
이후, app.js를 실행하면 똑같이 동작한다.
사용자 처리 함수를 별도의 모듈 파일로 분리해보기
스키마 파일을 별도로 분리했으니, 이제 사용자 처리 관련 함수들을 별도의 모듈 파일로 분리해보자.
사용자 관련 함수들은 익스프레스의 라우팅 미들웨어를 사용하지만, 모듈로 분리하는 방법은 일반적인 모듈을 만들어 사용할 때와 같다. 클라이언트에서 요청이 들어오면 라우팅하는 부분은 다음과 같이 세 가지가 있다.
router.route('/process/login').post(function(...) {...});
router.route('/process/adduser').post(function(...) {...});
router.route('/process/listuser').post(function(...) {...});
app.post() 함수의 두 번째 파라미터로 전달되는 것이 함수이므로 이 함수를 별도의 모듈로 분리할 수 있다.
사용자 관련 기능을 모두 하나의 모듈 파일에 넣어주면 구분하기 좋다.
- [routes] 폴더에 들어있는 user.js 파일을 수정하여 세 개의 함수를 만든다.
- app5.js 에 있는 세 개의 함수를 각각 옮겨 넣는다.
- module.exports 에 login/adduser/listuser 속성을 추가한다.
이렇게 하면 ./routes/user.js 파일을 불러온 후, user.login으로 모듈 파일의 login 함수를 지정한다.
#user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// 로그인 라우터 함수 - 데이터베이스의 정보와 비교
var login = function(req,res){
console.log('user 모델 안에 있는 login 호출됨.');
...
}
// 사용자 추가 함수
var adduser = function(req,res){
console.log('user 모듈 안에 있는 adduser 호출됨.');
...
};
//사용자 리스트 함수
var listuser = function(req,res){
console.log('user 모듈 안에 있는 listuser 호출됨.');
...
};
module.exports.login = login;
module.exports.adduser = adduser;
module.exports.listuser = listuser;
|
cs |
로그인 함수의 경우
- login 변수에 익명 함수를 만들어 module.exports 에 login 속성을 추가한다.
같은 방식으로 adduser, listuser도 추가한다.
그런데, 이 함수들은 addUser()함수와 authUser()함수도 사용하고 있다. 그래서 이 두 함수도 user.js 파일로 옮겨줘야한다.
이렇게 하면 사용자 처리 관련 코드는 모두 user.js 파일로 옮겨진다. 그런데, 실행하면 오류가 발생한다. 이는 모듈 파일 안에서 데이터베이스 객체나 스키마 객체를 참조할 수 없기 때문이다.
이처럼 모듈 파일안에서 이 모듈을 불러들여 사용하는 쪽의 변수를 참조해야할 때가 많다. 그 중에서도 데이터베이스 관련 객체(데이터베이스 객체, 스키마 객체, 모델 객체)를 별도의 다른 모듈에서 만들거나 app.js 같은 메인 파일에서 만들 때 모듈 파일쪽으로 기 객체를 전달할 수 있어야 한다.
익스프레스는 요청 객체가 app객체를 속성으로 가지고 있기 때문에, 메인 파일에서 모듈 파일로 객체를 전달할 때, app 객체에 속성으로 추가한 후 전달할 수 있다.
이때는 app객체의 set()과 get() 메소드를 사용하는 것이 좋다.
- 메인 파일에서는 데이터베이스 객체를 사용할 수 있는 시점이 되면 app 객체의 set() 메소드를 호출하여, 데이터베이스 객체를 추가한다.
- user.js 안에 정의한 login 함수를 호출할 때, 파라미터로 전달되는 요청에 있는 app객체의 get()메소드로 데이터베이스 객체를 꺼낼 수 있다.
여기에서는 user.js 파일 안에 세 개의 함수가 정의되어 있다.
모두 데이터베이스 객체, 스키마 객체, 모델 객체를 전달받아 참조해야하기때문에 메인 파일에서 만들어진 UserSchema 와 UserModel 객체는 database 객체의 속성을 넣어 전달한다.
- 데이터 베이스 객체들을 사용할 수 있는 시점이 되면 user 모듈의 init() 메소드를 호출하면서, 데이터베이스 객체 / 스키마 객체/ 모델 객체를 파라미터로 전달한다.
#user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var database;
var UserSchema;
var UserModel;
//데이터베이스 객체, 스키마 객체, 모델 객체를 이 모듈에서 사용할 수 있도록 전달함
var init = function(db, schema, model){
console.log('init 호출됨.');
database = db;
UserSchema = schema;
UserModel = model;
}
...
module.exports.init = init;
module.exports.login = login;
module.exports.adduser = adduser;
module.exports.listuser = listuser;
|
cs |
init() 함수를 호출하면서, 파라미터로 전달된 객체들은 모두 변수에 할당되며, login / adduser / listuser 함수 안에서 참조하여 사용할 수 있다.
이제 app.js 파일의 createUserSchema() 함수 안에 user 모듈의 init() 메소드를 호출하는 코드를 추가한다.
#app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | ... // user.js 참고 var user = require('./routes/user'); ... // user 스키마 및 모델 객체 생성 function createUserSchema(){ // user_schema.js 모듈 불러오기 UserSchema = require('./database/user_schema').createSchema(mongoose); // UserModel 모델 정의 UserModel = mongoose.model("user3",UserSchema); console.log('UserModel 정의함.'); //init 호출 user.init(database, UserSchema, UserModel); } ... // 라우터 객체 참조 var router = express.Router(); router.route('/process/login').post(user.login); router.route('/process/adduser').post(user.adduser); router.route('/process/listuser').post(user.listuser); ... | cs |
'Theorem (정리) > node.js' 카테고리의 다른 글
(node.js) [Part.7] 익스프레스 프로젝트를 모듈화하기 - UI 라이브러리로 웹 문서를 예쁘게 꾸미기 (semantic UI) (0) | 2021.01.28 |
---|---|
(node.js) [Part.7] 익스프레스 프로젝트를 모듈화하기 - 설정 파일 만들기 (0) | 2021.01.25 |
(node.js) [Part.7] 익스프레스 프로젝트를 모듈화하기 - 모듈화 방법 자세히 살펴보기 (0) | 2021.01.21 |
(node.js) [Part.6] 데이터 베이스 사용하기 - MySQL 데이터베이스 사용하기 (0) | 2020.12.27 |
(node.js) [Part.6] 데이터 베이스 사용하기 - 비밀번호 암호화하여 저장하기 (0) | 2020.12.23 |