3-3. 모델 메소드 만들기
우리가 2장에서는, API 컨트롤러 파일에서 데이터 모델의 내장함수들에 직접 접근하여 데이터를 생성하고 또 조회도 했었습니다.
이번 섹션에서는 데이터 모델에 임의 메소드, 그리고 쿼리 헬퍼를 만들어서 데이터 작업을 좀 더 용이하게 하는 방법을 알아보도록 하겠습니다.
모델 메소드는 두 종류로 만들 수 가 있습니다. .statics
와 .methods
인데요, 각 종류는 서로 가르키는 this
값이 다른데요, 전자의 경우엔 모델 자체를 가르키고, 후자의 경우엔 데이터 인스턴스를 가르킵니다.
다음 예제를 살펴보면 쉽게 이해가 갈 것입니다.
static 메소드
bookSchema.statics.findByTitle = function(title) {
return this.findOne({title}).exec();
}
// 사용예제:
Book.findByTitle('React Tutorials');
인스턴스 메소드
bookSchema.methods.printTitle = function() {
console.log(this.title);
}
// 사용예제:
book = await Book.findById(id).exec();
book.printTitle();
// React Tutorials
메소드를 만들땐, 스키마를 모델화 하기 전에, .statics
혹은 .methods
를 사용하여 정의를 해주어야합니다.
이렇게 메소드를 만들면, 우리가 원하는 작업마다 이름을 붙여 줄 수있게 되고 코드를 분리시킬 수 있게 되어 가독성도 높아지고, 쿼리를 작성 할 때 데이터 구조를 확인하기 위하여 컨트롤러 파일과 모델 파일을 동시에 볼 필요도 없어서 편해집니다.
이제, account 모델에서 필요한, findByEmail
, findByUsername
, localRegister
, validatePassword
메소드들을 구현해보록 하겠습니다.
validatePassword
를 제외한 모든 메소드들은 static 메소드입니다.
src/models/account.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const crypto = require('crypto');
function hash(password) {
return crypto.createHmac('sha256', process.env.SECRET_KEY).update(password).digest('hex');
}
const Account = new Schema({
profile: {
username: String,
thumbnail: { type: String, default: '/static/images/default_thumbnail.png' }
},
email: { type: String },
// 소셜 계정으로 회원가입을 할 경우에는 각 서비스에서 제공되는 id 와 accessToken 을 저장합니다
social: {
facebook: {
id: String,
accessToken: String
},
google: {
id: String,
accessToken: String
}
},
password: String, // 로컬계정의 경우엔 비밀번호를 해싱해서 저장합니다
thoughtCount: { type: Number, default: 0 }, // 서비스에서 포스트를 작성 할 때마다 1씩 올라갑니다
createdAt: { type: Date, default: Date.now }
});
Account.statics.findByUsername = function(username) {
// 객체에 내장되어있는 값을 사용 할 때는 객체명.키 이런식으로 쿼리하면 됩니다
return this.findOne({'profile.username': username}).exec();
};
Account.statics.findByEmail = function(email) {
return this.findOne({email}).exec();
};
Account.statics.findByEmailOrUsername = function({username, email}) {
return this.findOne({
// $or 연산자를 통해 둘중에 하나를 만족하는 데이터를 찾습니다
$or: [
{ 'profile.username': username },
{ email }
]
}).exec();
};
Account.statics.localRegister = function({ username, email, password }) {
// 데이터를 생성 할 때는 new this() 를 사용합니다.
const account = new this({
profile: {
username
// thumbnail 값을 설정하지 않으면 기본값으로 설정됩니다.
},
email,
password: hash(password)
});
return account.save();
};
Account.methods.validatePassword = function(password) {
// 함수로 전달받은 password 의 해시값과, 데이터에 담겨있는 해시값과 비교를 합니다.
const hashed = hash(password);
return this.password === hashed;
};
module.exports = mongoose.model('Account', Account);
로컬인증에서 필요한 모든 메소드들을 준비하였습니다. 이제, 회원인증을 위한 API 라우트를 만들어 위 메소드들을 사용하여 구현을 하면됩니다.