본문 바로가기

공부/Node.js

[Node.js] 모듈화

저번 공부 때는 사실 여기서 포기하고 다시 시작한거여서 굉장히 조심스럽게 모듈화를 진행하였다.

한 번 꼬이니 답이 안나와서 갈아치우고 다시 한 건데 한 번 다시 보고나니 뭔가 좀 알 것 같더라... 그래도 오류가 나면 겁은 난다. 아무튼 하나씩 해보려고 한다.

 

파일은 총 3가지로 app.js, user.js, user_schema.js 이렇게 3가지다.

 

user.js : 로그인, 회원가입, 사용자 목록

user_schema.js : database를 처리를 필요로 하는 함수 (사용자 확인 시 DB 검색, 사용자 추가시 DB 삽입)

app.js : 메인 서버로 동작

 

user_schema.js

 

var crypto = require('crypto');

var Schema = { };

Schema.createSchema = function(mongoose){
     UserSchema = mongoose.Schema({
        id:{type : String, required : true, unique : true},
        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}
    });
    
    UserSchema
        .virtual('password')
        .set(function(password){
            this._password = password;
            this.salt = this.makeSalt();
            this.hashed_password = this.encryptPassword(password); //password를 암호화 하여 hashed_password로 들어감
            console.log('virtual passowrd 호출됨 : ' + this.hashed_password);
        })
        .get(function(){
        return this._password;
    });
        
    
    UserSchema.static('findById', function(id, callback){
         return this.find({id : id}, callback);
     });
        
    UserSchema.static('findAll', function(callback){
        return this.find({}, callback);
    });
    
    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');
        }
    });
    
    UserSchema.method('makeSalt', function(){
        return Math.round((new Date().valueOf() * Math.random())) + '';
    });
    
    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('autenticate 호출됨 : %s -> %s : %s', plainText, this.encryptPassword(plainText), this.hashed_password);
            return this.encryptPassword(plainText) === this.hashed_password;
        }
    });
    
    //유효성 검사로 필수 속성이 들어가 있는지 아닌지를 확인하는 코드
    UserSchema.path('id').validate(function(id){
        return id.length;
    }, 'id 칼럼의 값이 없습니다.');
    
    UserSchema.path('name').validate(function(name){
        return name.length;
    }, 'name 칼럼의 값이 없습니다.');
    
    console.log('UserSchema 정의함');
    
    return UserSchema;
};

module.exports = Schema;

 

필요한 모듈은 crypto 하나 뿐이고 나머지는 UserSchema를 설정하고 가상 함수로 password 암호화해서 저장하고 findById나 findAll 같은 database에 접촉하는 함수, 나머지는 암호화 및 유효성 검사 함수로 이루어져 있다. 이전과 달라진 점은 마지막에 UserSchema를 반환해주는 것과 module.exports로 외부 모듈 사용 선언 해주는 것 두 개 뿐이다.

 

노심초사 하며 움직인건 마찬가지긴 하지만 그래도 user_schema.js의 모듈화는 별거 없었고 나도 납득하면서 진행했다.(즉, 어느정도 이해를 전제하에 움직인 것이다.)

 

user.js

 

var database;
var UserSchema;
var UserModel;

var init = function(db, schema, model){
    console.log('init 호출됨');
    
    database = db;
    UserSchema = schema;
    UserModel = model;
}

var authUser = function(database, id, password, callback){
    ...
};

var addUser = function(database, id, password, name, callback){
    ...
}

var login = function(req,res){
    ...
}

var adduser = function(req, res){
    ...
}

var listuser = function(req, res){
    ...
}

module.exports.init = init;
module.exports.login = login;
module.exports.adduser = adduser;
module.exports.listuser = listuser;

 

되게 간단 명료해 보이지만 진짜 어마어마 하게 길다. 일단 모든 코드를 줄여서 그나마 적어보이는 것이다. 여기서 실수로 route 함수를 가지고 왔다가 감각적으로 되돌려놓았는데 잘못하면 꼬일뻔 했다.(2번이나 중도 하차할 순 없지....)

 

여기도 마찬가지로 크게 다를 건 없지만 가장 중요한건 init을 통해서 authUser, addUser, login, adduser, listuser 이 5가지 함수를 위해서 필요한 databse, UserSchema, UserModel을 미리 init에서 초기화 시켜주는 것이다.

 

그리고 마지막에는 route에서 사용가능하도록 함수들을 외부 모듈에서 사용할 수 있게 해주면 완료다.

 

app.js

 

...

var user = require('./routes/user');

var database;

var UserSchema;

var UserModel;

function connectDB(){

    ...
    
    database = mongoose.connection;
    
    ...
    
    database.on('open', function(){
        console.log('데이터베이스에 연결되었습니다. : ' + databaseUrl);
        createUserSchema();
        UserModel = mongoose.model("users3", UserSchema);
        console.log('UserModel 정의함.');
    });
   
   ...
   
};

function createUserSchema(){

    UserSchema = require('./database/user_schema').createSchema(mongoose);
    UserModel = mongoose.model("users3", UserSchema);
    console.log('UserModel 정의함');
    
    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);

app.use ('/', router);

...

 

알아 보는데 필요없다고 생각되는 부분은 다 지웠다. 일단 가장 위에 외부 모듈을 불러왔다.

 

이 이후에 connectDB 내부의 open 메소드 선언에서 DB가 연결될 때 createUserSchema()함수를 실행시키는데 이 함수는 database, UserSchema, UserModel을 받아서 user 내부를 초기화 시키는 함수로 연결이 되면 실행 되도록 했다.

 

그리고 라우팅 부분에서는 원래 callback 함수가 들어가는 자리에 대신들어가는 function을 지워버리고 외부 모듈인 user의 login, adduser, listuser 세 가지 함수를 집어넣었다.

 

이렇게 정리를 한 번 하고나니 300줄에 다다르던 app.js가 100줄로 줄어들었다. 그리고 에러를 찾는 것도 한층 수월해지지 않을까 생각해본다. 어느 정도 일궈 놓은 프로그램이라 건드리는게 너무 두려웠다. 하지만 깔끔한 모습을 보니 꽤나 보람을 느끼고 오류를 해결하면서 좀 익숙해졌나?라는 생각도 해본다.