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 라우트를 만들어 위 메소드들을 사용하여 구현을 하면됩니다.

results matching ""

    No results matching ""