예제 모델

// 기존의 sequelize Profile 모델

class Profile extends Sequelize.Model {
  static init(sequelize, DataTypes) {
    super.init({
      id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true,
        autoIncrement: true,
      },
      name: {
        type: DataTypes.STRING(200),
        allowNull: false
      },
      email: {
        type: DataTypes.STRING(200),
        allowNull: false,
      },
      profile_url: {
        type: DataTypes.TEXT,
        allowNull: true,
      }
    });
    return api_profile;
  }
}

위와 같은 Profile 모델을 예시로 들겠습니다. 다른 스택오버플로우에 나와있는 방식은 전부 스택오버플로우 예시 같은 ES5 방식으로 함수 내에 선언되는 메서드로 많이 소개되어 있습니다.

 

How to add custom function to sequelize.js in Node.JS?

For example I have a Client model. I want to add new function "sendEmail" The function needs to work send email to one client, or to send email to many clients at once? Where to define those func...

stackoverflow.com

module.exports = function(sequelize, DataTypes) {
  return sequelize.define('Client', {
    first_name: DataTypes.STRING,
    last_name: DataTypes.STRING,
  }, {
    instanceMethods: {
      getFullName: function() {
        return this.first_name + ' ' + this.last_name;
      }
    }
  });
};

하지만 제가 현재 사용하는 sequelize 모델은 ES6 기반 class 로 선언되어 있습니다. 위에 소개해드린 예시에서는 function 내에서 instanceMethods 라는 속성에 메서드를 하나씩 추가하는 구조입니다. 그래서 class 로 선언되어 있는 모델 또한 비슷하지 않을까? 라는 오해를 하기 쉬운 것 같습니다. 하지만 생각과 달리 정말 단순하게 사용할 수 있었습니다.

// 기존의 sequelize Profile 모델

class Profile extends Sequelize.Model {
  static init(sequelize, DataTypes) {
    super.init({
      id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true,
        autoIncrement: true,
      },
      name: {
        type: DataTypes.STRING(200),
        allowNull: false
      },
      email: {
        type: DataTypes.STRING(200),
        allowNull: false,
      },
      profile_url: {
        type: DataTypes.TEXT,
        allowNull: true,
      }
    });
    return api_profile;
  }

  static getEmailById = async (id) => {
    const result = await this.findOne({
      where: { id },
      raw: true
    });

    return result;
  }
}

위와 같이 클래스에 메소드를 선언하듯이 똑같이 선언하면 되겠습니다. 저는 static 메소드로 선언하였는데요. 아래에서 정적메서드를 어떻게 활용하는지, 커스텀 메서드 사용으로 router 처리 방식이 어떻게 바뀌는지 그리고 코드 중복을 방지할 수 있는 예제를 작성하겠습니다.

router.get('users/email/:id', async (req, res) => {
  try {
    const id = req.params.id;

    // 쿼리가 더 복잡해진다면 비지니스 로직이 더 길어지게 됩니다.
    const result = await DB.Profile.findOne({
      where: { id },
      raw: true
    });

    return res.json(result);
  } catch (error) {
    res.status(500).send(error.message);
    next();
  }
});

아래는 커스텀 메소드로 짧아진 로직입니다.

router.get('users/email/:id', async (req, res) => {
  try {
    const id = req.params.id;

    const result = await DB.Profile.getEmailById(id);

    return res.json(result);
  } catch (error) {
    res.status(500).send(error.message);
    next();
  }
});

이런 간단한 예제로 본다면 큰 차이가 없어 보입니다. 만약 사용자의 Profile 을 가져오는 값들이 테이블 3개를 Join 해서 가져와야 하는 값이라면 이야기가 달라집니다. 단순 쿼리 로직만 20~30줄이 될 수 있습니다. 그리고 사용자의 정보를 가져오는 쿼리는 다른 API에서도 많이 사용하는 데이터이기 때문에 custom 한 메서드로 분리하는 게 좋습니다.