- Today
- Total
개발하는 고라니
[Node.js] Mongoose 본문
일전에 Node를 통해 MongoDB에 데이터를 저장하고, 가져오고, 수정하고 삭제하는 것을 구현해보았다. 우리에게 익숙한 RDBMS와 다르게 NoSQL인 MongoDB는 스키마(Schema)가 없다는 특징이 있었다. 이는 때로는 편리할지도 모르지만, 어쩌면 더 복잡할 지도 모르겠다. 그래서 사용하는 것이 mongoose이다. mongoose는 MongoDB를 사용할 때 스키마를 지정해서 사용할 수 있게 해준다.
스키마 타입 (Schema Type)
String | 문자열 타입 |
Number | 숫자 타입 |
Boolean | 이진 타입 |
Array | 배열 타입 |
Buffer | 버퍼 타입, 바이너리 데이터를 저장할 수 있다. |
Date | 날짜 타입 |
ObjectId | 각 문서(Document)마다 만들어지는 ObjectId를 저장할 수 있는 타입. 다른 document를 참조하는 용도인 것 같다. |
Mixed | 혼합 타입 |
var userSchema = new mongoose.Schema({
id: {type: String, required: true, unique: true},
password : {type: String, required: true},
name: String,
age: Number,
regDate: Date
});
/*
type : 자료형 지정
required : NOT NULL과 같은 기능
unique : true면 고유한 값이 들어가야 함
*/
※ 인덱스와 메소드
스키마를 정의할 때 아까처럼 단순히 타입만 정의하는 것이 아니라 객체를 지정하고 다른 것도 넣을 수 있다. 여기에 'index'를 넣을 수 있는데 이는 검색 및 조회할 때 조금 더 빠르게 수행될 수 있도록 내부적으로 만들어줄 수 있다.
var userSchema = new mongoose.Schema({
id: {type: String, required: true, unique: true},
password : {type: String, required: true},
name: {type: String, index: 'hashed'},
age: {type: Number, 'default': -1},
regDate: {type: Date, index: {unique: false, expires: '1d'}},
});
userSchema.static('findById', (id, callback) => {
return this.find({id: id}, callback);
});
userSchema.static('findAll', (callback) => {
return this.find({}, callback);
});
/*
static(name, fn) : 모델 객체에서 사용할 수 있는 함수를 등록한다. 함수의 이름과 함수 객체를 파라미터로 전달한다.
method(name, fn) : 모델 인스턴스 객체(ex: new userModel()) 에서 사용할 수 있는 함수를 등록한다. 함수의 이름과 함수 객체를 파라미터로 전달한다
*/
데이터베이스의 이벤트
- open
- error
- disconnected
mongoose 스키마와 모델과의 관계
mongoose CRUD 메서드
userModel.where({id: 'gorany'}).update({name: 'first'}, (err, docs) => { ... });
메서드 | 설명 |
find( [criteria], [callback] ) | 조회 조건을 사용해 컬렉션의 데이터를 조회한다. 조회 결과는 콜백 함수로 전달된다. |
save( [options], [callback] ) | 모델 인스턴스 객체의 데이터를 저장한다. 저장 결과는 콜백 함수로 전달된다. |
update( [criteria], [doc], [options], [callback] ) | 컬렉션의 데이터를 조회한 후 업데이트 한다. where() 메서드와 함께 사용된다. |
remove( [criteria], [callback] ) | 컬렉션의 데이터를 삭제한다. |
User CRUD
mongoose의 schema를 작성해 규정된 데이터를 채워넣으며 회원가입 / 로그인 / 회원정보 수정 / 회원 탈퇴를 구현해보자.
Schema 정의
회원에 필요한 정보를 ID, password, name, age, regDate, salt 정도로 설정할 수 있겠다. 이 때 password는 암호화해서 하도록 해보려 한다. 그래서 virtual을 이용해 가상의 데이터로 'password'를 넣고 실제 저장되는 데이터는 'hashed_password'와 암호화에 사용된 salt(일종의 seed)를 저장하도록 한다.
connectDB: function(){
var mongoURL = 'mongodb://localhost:27017/local';
mongoose.Promise = global.Promise;
mongoose.connect(mongoURL);
database = mongoose.connection;
database.on('open', function() {
/* Schema */
schema = mongoose.Schema({
id: {type:String, required:true, unique:true},
name: {type:String, required:true, index: 'hashed', default: 'name'},
hashed_password: {type:String, required: true},
age: {type:Number, required:true, default: '-1'},
salt: {type:String, required:true},
regDate: {type: Date, index:{unique:false}, default: Date.now()}
});
console.log('Def Schema');
/* virtual */
schema.virtual('password').set(function(password) {
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(password, this.salt);
console.log('hashed_password 저장 : ' + this.hashed_password);
});
/* static */
schema.static('findById', function (id, callback) {
return this.find({id: id}, callback);
});
schema.static('findAll', function(callback) {
return this.find({}, callback);
});
/* Method */
schema.method('encryptPassword', function(plainText, salt) {
if(salt) {
return crypto.createHmac('sha1', salt).update(plainText).digest('hex');
}
else {
return crypto.createHmac('sha1', this.salt).update(plainText).digest('hex');
}
});
schema.method('makeSalt', function() {
return Math.round(Date.now().valueOf() * Math.random) + '';
});
schema.method('auth', function(password, salt, hashed_password){
if(salt){
return this.encryptPassword(password, salt) === hashed_password;
}
else{
return this.encryptPassword(password, this.salt) === hashed_password;
}
});
/* Model */
model = mongoose.model('users3', schema);
console.log('Def Model');
});
database.on('disconnected', () => {
console.log('Database Disconnected!');
});
database.on('error', () => {
console.log('Database has an error!');
});
}
회원 가입 - Create
addUser: function(id, name, password, callback) {
var user = new model({
id: id,
name: name,
password: password
});
user.save((err) => {
if(err) {
console.log('AddUser() 도중 에러 발생');
callback(err, null);
return;
}
console.log('AddUser() 성공');
console.dir(user);
callback(null, user);
});
}
클라이언트에서 ID와 password 그리고 name을 보내주면 DB에 등록해주는 함수이다.
로그인 - Read
authUser: function(id, password, callback){
model.findById(id, function(err, docs) {
if(err) {
console.log('AuthUser() 도중 에러 발생');
callback(err, null);
return;
}
if(docs.length > 0){
var user = new model({id: id});
if(user.auth(password, docs[0].salt, docs[0].hashed_password)){
console.log('ID: 일치 / PW: 일치');
callback(null, user);
}
else{
console.log('ID: 일치 / PW: 불일치');
callback(null, null);
}
}
else{
console.log('ID: 불일치 / PW: 불일치');
callback(null, null);
}
});
}
클라이언트에서 id와 password를 보내주면 그 ID에 해당하는 model을 찾아 비밀번호를 비교한 뒤 로그인 할지 말지 결정해주는 함수이다.
회원 정보 변경 - Update
updateUser: function(id, name, callback) {
console.log(id + ', ' + name);
model.findOneAndUpdate({id: id}, {$set: {name: name}}, (err, output) => {
if(err) {
console.log('UpdateUser() 도중 에러 발생');
callback(err, null);
return;
};
console.log('UpdateUser() 성공');
callback(null, output);
});
}
회원 탈퇴 - Delete
deleteUser: function(id, callback) {
console.log('deleteUser() 호출 : ' + id);
model.deleteOne({id: id}, (err, result) => {
if(err) {
console.log('deleteUser() 도중 에러 발생');
callback(err, null);
}
if(result){
console.log(result);
callback(null, result);
}
else{
callback(null, null);
}
});
}
# Code </>
var mongoose = require('mongoose');
var crypto = require('crypto');
var database, model, schema;
var dbUser = {
connectDB: function(){
var mongoURL = 'mongodb://localhost:27017/local';
mongoose.Promise = global.Promise;
mongoose.connect(mongoURL);
database = mongoose.connection;
database.on('open', function() {
/* Schema */
schema = mongoose.Schema({
id: {type:String, required:true, unique:true},
name: {type:String, required:true, index: 'hashed', default: 'name'},
hashed_password: {type:String, required: true},
age: {type:Number, required:true, default: '-1'},
salt: {type:String, required:true},
regDate: {type: Date, index:{unique:false}, default: Date.now()}
});
console.log('Def Schema');
/* virtual */
schema.virtual('password').set(function(password) {
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(password, this.salt);
console.log('hashed_password 저장 : ' + this.hashed_password);
});
/* static */
schema.static('findById', function (id, callback) {
return this.find({id: id}, callback);
});
schema.static('findAll', function(callback) {
return this.find({}, callback);
});
/* Method */
schema.method('encryptPassword', function(plainText, salt) {
if(salt) {
return crypto.createHmac('sha1', salt).update(plainText).digest('hex');
}
else {
return crypto.createHmac('sha1', this.salt).update(plainText).digest('hex');
}
});
schema.method('makeSalt', function() {
return Math.round(Date.now().valueOf() * Math.random) + '';
});
schema.method('auth', function(password, salt, hashed_password){
if(salt){
return this.encryptPassword(password, salt) === hashed_password;
}
else{
return this.encryptPassword(password, this.salt) === hashed_password;
}
});
/* Model */
model = mongoose.model('users3', schema);
console.log('Def Model');
});
database.on('disconnected', () => {
console.log('Database Disconnected!');
});
database.on('error', () => {
console.log('Database has an error!');
});
},
addUser: function(id, name, password, callback) {
var user = new model({
id: id,
name: name,
password: password
});
user.save((err) => {
if(err) {
console.log('AddUser() 도중 에러 발생');
callback(err, null);
return;
}
console.log('AddUser() 성공');
console.dir(user);
callback(null, user);
});
},
authUser: function(id, password, callback){
model.findById(id, function(err, docs) {
if(err) {
console.log('AuthUser() 도중 에러 발생');
callback(err, null);
return;
}
if(docs.length > 0){
var user = new model({id: id});
if(user.auth(password, docs[0].salt, docs[0].hashed_password)){
console.log('ID: 일치 / PW: 일치');
callback(null, user);
}
else{
console.log('ID: 일치 / PW: 불일치');
callback(null, null);
}
}
else{
console.log('ID: 불일치 / PW: 불일치');
callback(null, null);
}
});
},
updateUser: function(id, name, callback) {
console.log(id + ', ' + name);
model.findOneAndUpdate({id: id}, {$set: {name: name}}, (err, output) => {
if(err) {
console.log('UpdateUser() 도중 에러 발생');
callback(err, null);
return;
};
console.log('UpdateUser() 성공');
callback(null, output);
});
},
deleteUser: function(id, callback) {
console.log('deleteUser() 호출 : ' + id);
model.deleteOne({id: id}, (err, result) => {
if(err) {
console.log('deleteUser() 도중 에러 발생');
callback(err, null);
}
if(result){
console.log(result);
callback(null, result);
}
else{
callback(null, null);
}
});
},
getUser: function(id, callback){
model.findById(id, (err, user) => {
if(err) {
callback(err, null);
return;
}
if(user){
console.log('user 존재');
callback(null, user);
}
else{
console.log('user 없음');
callback(null, null);
}
});
}
}
module.exports = dbUser;
# References
www.youtube.com/watch?v=KZpVZpRAy0Q&list=PLG7te9eYUi7tHH-hJ2yzBJ9h6dwBu1FUy&index=43
'Framework > Node.js' 카테고리의 다른 글
[Node.js] 파일 쓰기 - Console (0) | 2021.05.04 |
---|---|
[Node.js] ejs (0) | 2021.05.01 |
[Node.js] MongoDB CRUD (0) | 2021.04.25 |
[Node.js] 파일 업로드 (0) | 2021.04.24 |
[Node.js] 요청 라우팅 - Router (0) | 2021.04.23 |