반응형
05-14 05:47
Today
Total
«   2024/05   »
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
관리 메뉴

개발하는 고라니

[Node.js] Mongoose 본문

Framework/Node.js

[Node.js] Mongoose

조용한고라니 2021. 4. 26. 02:36
반응형

일전에 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 스키마와 모델과의 관계

출처: Do it! Node.js 프로그래밍[개정판] -ch06-09 유튜브 강좌

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
Comments