몽구스 스키마 정리하기

GonnabeAlright·2021년 11월 29일
4
post-thumbnail
const mongoose = require('mongoose');
// mongoDB를 node.js에서 사용할 수 있도록 만든 것이 mongoose 모듈

const bcrypt = require('bcrypt');
// Blowfish를 기반으로 만들어진 단방향 해시 함수, 흔히 사용되는 해시 알고리즘인 
// SHA-256을 사용해서 데이터를 해싱한다. 단방향 암호화이기 때문에 복호화 불가능.

const saltRounds = 10;
// bcrypt 해시를 계산하는 데 필요한 시간을 제어하며
// saltRounds가 높을수록 더 많은 해싱 라운드가 수행되게 된다.
/*
	라운드란 ?
    블록체인이나 해시그래프 등 탈중앙화 분산형 시스템에서 다수의 노드들이 일정한 합의 알고	리즘에 따라 의살결정을 내리는 최소환의 시간 단위를 말한다.
*/
const jwt = require('jsonwebtoken');

스키마는 데이터베이스의 테이블, 컬렉션의 타입과 속성을 정의해 각각의 필드에 저장하는 값에 의존성을 부여합니다. mongoDB에는 스키마가 없기 때문에 몽구스를 사용해서 스키마를 생성합니다.

const userSchema = mongoose.Schema({
  email: { type: String, trim: true, unique: 1 },
  password: { type: String, minlength: 10 },
  name: { type: String, maxlength: 100 },
  github: { type: String, maxlength: 100 },
  gender: { type: String, maxlength: 10 },
  university: { type: String, maxlength: 10 },
  major: { type: String, maxlength: 10 },
  role: { type: Number, default: 0 },
  title: String,
  image: String,
  token: { type: String },
  tokenExp: { type: Number }    
});

스키마 타입

  • String: 문자열
  • Number: 정수
  • Schema.Types.ObjectId: 명시적으로 id 타입을 사용할 때는 이렇게 사용해야 한다. 스키마에 정의를 안해도 자동적으로 몽고에서는 생성하며 행을 구분하는 아이디 값으로 사용된다.
  • Date: 날짜
  • Buffer: 바이너리 타입
  • Boolean: 참과 거짓만 값을 저장하고 나타낸다.
  • Schema.Types.Mixed: 이름 그대로 다양한 타입을 저장할 수 있다. 객첸 배열 JSON으로 사용합니다. 타입이 없다고 생각해도 된다.
  • Array: []사용해서 배열을 표시 ex) [Number] 정수 배열

스키마 속성

  • required: 필수 입력
  • unique: 다른 행과 중복되면 안됨.
  • trim: 공백 제거(문자열 타입에 사용)
  • default: 문서가 생성되면 기본값으로 저장된다.
  • lowercase: 대문자를 소문자로 저장
  • match: 정규시긍로 저장하려는 값과 비교
  • validate: 함수로 개발자가 조건을 만듦.
  • set: 값을 입력할 때 함수로 조건을 만듦.
  • get: 값을 출력할 때 함수로 조건을 만듦.
  • ref: 해당하는 모델을 참조할 때 사용.
// 회원가입 시 비밀번호 암호화
// .pre()를 통해 해당 스키마에 데이터가 저장(.save)되기 전 수행할 작업들을 지정한다.
userSchema.pre('save', function(next) {
  var user = this;
  // 패스워드가 변경될 때만 해싱작업 처리
  if(user.isModified('password')) {
    // geSalt()를 사용해 salt값 생성
    // salt: 공격자가 암호를 유추할 수 없도록, 평문 데이터에 의미 없는 데이터를 뿌려 넣는데 이것을 salt라고 한다.
    
    // salt값 생성
    bcrypt.genSalt(saltRounds, function(err, salt) {
      if(err) return next(err);
      
      // 생성된 salt값과 비밀번호를 인자로 넘겨준다.
      // hash 생성
      bcrypt.hash(user.password, salt, function(err, hash) {
        if(err) return next(err);
        // hash값을 user.password에 저장
        user.password = hash
        next()		// save() 처리                
      });                  
    });        
  } else {
   	next(); 
  }  
}, {});

인스턴스 메소드 생성 1

// 몽구스 메서드 생성 (methods)
// 로그인 시 비밀번호 암호화 -> 디비에 저장된 비밀번호와 비교
userSchema.methods.comparePassword = function(plainPassword, cb) {
  // 입력한 값: plainPassword가 this.password와 같습니까 ?
  // 같은지 비교하려면 입력한 값을 암호화해서 이미 해싱된 db의 비밀번호와 같은지 확인
  // 
  bcypt.compare(plainPassword, this.password, function(err, isMatch){
    if(err) return cb(err);		// cb(callback) => err
    cb(null, isMatch);			// err은 null, isMatch는 true를 반환    
  });       
}

인스턴스 메소드 생성 2

// 몽구스 메서드 생성 (methods)
// 로그인 시 토큰 생성
userSchema.methods.generateToken = function(cb) {
  var user = this;		// 만들어진 인스턴스를 user 변수에 저장
  `
  // jsonwebtoken을 이용해서 토큰 생성 (토큰발급)
  // user._id는 db에 이미 저장된 _id
  var token = jwt.sign(user._id.toHexString(), 'secretToken');
`
  // 생성된 토큰을 user.token에 저장
  user.token = token;
  user.save(function(err, user) {
    if(err) return cb(err);
    cb(null, user)	
  });      
}

전역 메소드 생성

// 몽구스 메서드 생성 (statics)
// 인증 시 토큰과 디비의 토큰을 복호화하여 비교
userSchema.statics.findByToken = function(token, cb) {
  var user = this;
  // 토큰 인증(확인)
  jwt.verify(token, 'secretToken', function(err, decoded) {
    user.findOne({ "_id": decoded, "token": token }, function(err, user) {
      if(err) return cb(err);
      cb(null, user);      
    });
  });
}
const User = mongoose.model('User', userSchema);
module.exports = { User }

몽구스 메서드 정리

조회

  • find(조건, 필드, 옵션, 콜백);
  • findOne(조건, 필드, 옵션, 콜백);

삽입

  • create(조건, 콜백);
  • save(콜백);

수정

  • update(조건, 수정, 옵션, 콜백);
  • findOneAndUpdate(조건, 수정, 옵션, 콜백);
  • findByIdAndUpdate(아이디, 수정, 옵션, 콜백);

삭제

  • remove(조건, 콜백);
  • findOneAndRemove(조건, 옵션, 콜백);
  • findByIdAndRemove(아이디, 옵션, 콜백);

1개의 댓글

comment-user-thumbnail
2022년 8월 7일

정리가 깔끔하게 잘 되있네요 종종와서 참고하겠습니다.
감사합니다.

답글 달기