백앤드(Back-End)/Node.JS

[Node.js] Sequelize 관계성 정의 테이블간에 관계정의

RyanSin 2021. 2. 11. 17:55
반응형

- 지난 시간

안녕하세요. 지난 시간에는 모델을 정의하고 생성하는 부분에 대해 알아봤습니다.

 

혹시 놓치고 오신 분들은 아래 링크를 통해 학습하고 오시는 걸 추천드리겠습니다.

any-ting.tistory.com/50

 

[Node.js] Sequelize 모델 정의 및 옵션 설정

- 지난 시간 안녕하세요. 지난 시간에는 Sequelize를 설치하고 DB와 간단한 Users Table(모델)을 만들어서 확인해 봤습니다. 혹시 놓치고 오신 분들은 아래 링크를 통해 진행하고 오시는 걸 추천드리겠

any-ting.tistory.com

 

- 개요

이번 시간에는 시퀄 라이즈에서 모델 간에 관계를 맺는 방법에 대해 알아보겠습니다.

 

보통 관계를 맺는 방식은 1 : 1(일 대 일), 1 : M (일 대 다), N : M (다 대 다)로 맺습니다.

 

관계를 맺어주는 코드는 associate라는 함수 안에 정의할 수 있으며, 그 안에 hasOne, hasMany, belongsTo, belongsToMany속성으로 해당 관계 방식을 정의합니다.

 

- 1 : 1 관계 방식

1 : 1 방식과 같은 경우 예를 들면 "유저" 모델과 "유저 정보" 모델로 매칭을 시킬 수 있습니다.

한 유저에 대한 정보가 크다면 하나에 데이블 보다는 분리를 하는 경우가 발생하는데요. 두 모델을 만들고 관계성을 맺어 사용합니다.

 

시퀄 라이즈에서 관계를 맺는 방식은 아래와 같습니다.

 

Users 모델

module.exports = (sequelize, DataTypes) => {

    const Users = sequelize.define("Users", {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
        comment: "고유번호 UUID",
      },
      email: {
        type: DataTypes.STRING(100),
        validate: {
          isEmail: true,
        },
        comment: "이메일",
      },
      password: {
        type: DataTypes.STRING(60),
        comment: "비밀번호",
      },
      name: {
        type: DataTypes.STRING(100),
        comment: "이름",
      },
      phone: {
        type: DataTypes.STRING(72),
        comment: "전화번호",
      },
    }, {
      charset: "utf8", // 한국어 설정
      collate: "utf8_general_ci", // 한국어 설정
      tableName: "Users", // 테이블 이름
      timestamps: true, // createAt & updateAt 활성화
      paranoid: true, // timestamps 가 활성화 되어야 사용 가능 > deleteAt 옵션 on
    });
  
    Users.associate = models => {
        /**
         * Users안에 있는 "id값"을 "user_id라는 컬럼 이름"으로 UserInfo모델에 새로운 컬럼으로 추가한다.
         */
        Users.hasOne(models.UserInfo, {foreignKey: "user_id", sourceKey: 'id'});
    };

    return Users;
};

UserInfo 모델

const STATUS = {
    PAUSE: 'PAUSE',
    ACTIVE: 'ACTIVE',
};

module.exports = (sequelize, DataTypes) => {

    const UserInfo = sequelize.define("UserInfo", {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
        comment: "고유번호 UUID",
      },
      permission: {
        type: DataTypes.STRING(45),
        comment: "유저 권한(Ex: 관리자, 일반 유저 등등 )",
      },
      status: {
        type: DataTypes.ENUM([STATUS.PAUSE, STATUS.ACTIVE]),
        defaultValue: STATUS.PAUSE,
        allowNull: false,
        comment: "계정상태(ACTIVE, PAUSE)",
      },
      block: {
        type: DataTypes.BOOLEAN,
        defaultValue: 0,
        comment: "계정 블락 유무",
      },
      account_expired: {
        type: DataTypes.STRING(45),
        comment: "계정 유효기간(패키지 별 설정) ex)2020-12-27",
      },
      password_expired: {
        type: DataTypes.STRING(45),
        comment: "비밀번호 유효기간(주기적 업데이트) ex)2020-12-31",
      },
    }, {
      charset: "utf8", // 한국어 설정
      collate: "utf8_general_ci", // 한국어 설정
      tableName: "UserInfo", // 테이블 이름
      timestamps: true, // createAt & updateAt 활성화
      paranoid: true, // timestamps 가 활성화 되어야 사용 가능 > deleteAt 옵션 on
    });
  
    UserInfo.associate = models => {
        /**
         * UserInfo모델 안에 "user_id라는 컬럼 이름"으로 Users모델에 있는 "id값"을 새로운 컬럼으로 추가한다.
         */
        UserInfo.belongsTo(models.Users, {foreignKey: "user_id", sourceKey: "id"});
    };

    return UserInfo;
};

 

hasOne : 관계를 맺는 대상(자식)에게 자신의 외래 키를 추가합니다. (Users => UserInfo) UserInfo 외래 키가 추가됩니다.

belongsTo : 관계를 맺는 대상(부모)에게 외래 키를 받아 추가합니다. (UserInfo => Users) UserInfo 외래 키가 추가됩니다.

 

테이블 정보 조회

Users 모델
UserInfo 모델

 

- 1 : M 관계 방식

1 : M 방식과 같은 경우 예를 들면 "회사" 모델과 "회사 소속 유저" 모델로 매칭을 시킬 수 있습니다.

 

한 회사에 소속된 유저는 여러 명일 수 있다.

 

시퀄 라이즈에서 관계를 맺는 방식은 아래와 같습니다.

 

CompanyInformation 모델

module.exports = (sequelize, DataTypes) => {
  const CompanyInformation = sequelize.define("CompanyInformation", {
    id: {
      type: DataTypes.UUID,
      defaultValue: DataTypes.UUIDV4,
      primaryKey: true,
      comment: "고유번호 UUID",
    },
    hospital_name:{
      type: DataTypes.STRING(255),
      comment:"회사 이름"
    },
    email: {
      type: DataTypes.STRING(100),
      validate: {
        isEmail: true,
      },
      comment: "회사 이메일",
    },
    name: {
      type: DataTypes.STRING(100),
      comment: "회사(관리자) 이름"
    },
    phone: {
      type: DataTypes.STRING(72),
      comment: "회사 전화번호",
    },
    fax: {
      type: DataTypes.STRING(72),
      comment: "팩스번호",
    },
    business_number: {
      type: DataTypes.STRING(45),
      comment: "사업자등록번호",
    },
    address: {
      type: DataTypes.TEXT,
      comment: "주소",
    },
    package: {
      type: DataTypes.JSON,
      defaultValue: {cloud:"true"},
      comment: "각 프로젝트는 결제 후 사용가능",
    },
  }, {
    charset: "utf8", // 한국어 설정
    collate: "utf8_general_ci", // 한국어 설정
    tableName: "CompanyInformation", // 테이블 이름
    timestamps: true, // createAt & updateAt 활성화
  });

  CompanyInformation.associate = models => {

    /**
     * CompanyInformation안에 있는 "id값"을 "company_id라는 컬럼 이름"으로 Users모델에 새로운 컬럼으로 추가한다.
     */
    CompanyInformation.hasMany(models.Users, {foreignKey : "company_id", sourceKey:"id"});
  };

  return CompanyInformation;
};

Users 모델

module.exports = (sequelize, DataTypes) => {

    const Users = sequelize.define("Users", {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
        comment: "고유번호 UUID",
      },
      email: {
        type: DataTypes.STRING(100),
        validate: {
          isEmail: true,
        },
        comment: "이메일",
      },
      password: {
        type: DataTypes.STRING(60),
        comment: "비밀번호",
      },
      name: {
        type: DataTypes.STRING(100),
        comment: "이름",
      },
      phone: {
        type: DataTypes.STRING(72),
        comment: "전화번호",
      },
    }, {
      charset: "utf8", // 한국어 설정
      collate: "utf8_general_ci", // 한국어 설정
      tableName: "Users", // 테이블 이름
      timestamps: true, // createAt & updateAt 활성화
      paranoid: true, // timestamps 가 활성화 되어야 사용 가능 > deleteAt 옵션 on
    });
  
    Users.associate = models => {
        /**
         * Users안에 있는 "id값"을 "user_id라는 컬럼 이름"으로 UserInfo모델에 새로운 컬럼으로 추가한다.
         */
        Users.hasOne(models.UserInfo, {foreignKey: "user_id", sourceKey: 'id'});
        

        /**
         *  Users모델 안에 "company_id라는 컬럼 이름"으로 CompanyInformation모델에 있는 "id값"을 새로운 컬럼으로 추가한다.
         */
        Users.belongsTo(models.CompanyInformation, {foreignKey: "company_id", sourceKey: "id"});

    };

    return Users;
};

hasMany : 관계를 맺는 대상(자식)에게 자신의 외래 키를 추가합니다. 그리고 복수에 데이터를 추가가 가능하며 관계를 맺습니다. (CompanyInfo => Users) Users 외래 키가 추가됩니다.

 

belongsTo : 관계를 맺는 대상(부모)에게 외래 키를 받아 추가합니다. (Users => CompanyInformation) Users 외래 키가 추가됩니다.

 

 

테이블 조회 정보

CompanyInformation 모델
Users 모델

 

 

 

저희는 Category, ProductCategory, Product 모델을 만들어 보겠습니다.

 

1번 방식

Category 모델

module.exports = (sequelize, DataTypes) => {

    const Category = sequelize.define("Category", {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
        comment: "고유번호 UUID",
      },
      name: {
        type: DataTypes.STRING(100),
        comment: "이름",
      },
    }, {
      charset: "utf8", // 한국어 설정
      collate: "utf8_general_ci", // 한국어 설정
      tableName: "Category", // 테이블 이름
      timestamps: true, // createAt & updateAt 활성화
      paranoid: true, // timestamps 가 활성화 되어야 사용 가능 > deleteAt 옵션 on
    });
  
    Category.associate = models => {
        Category.belongsToMany(models.Product, {through: "ProductCategory"})
    };

    return Category;
};

 

Product 모델

module.exports = (sequelize, DataTypes) => {

    const Product = sequelize.define("Product", {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
        comment: "고유번호 UUID",
      },
      name: {
        type: DataTypes.STRING(100),
        comment: "이름",
      },
    }, {
      charset: "utf8", // 한국어 설정
      collate: "utf8_general_ci", // 한국어 설정
      tableName: "Product", // 테이블 이름
      timestamps: true, // createAt & updateAt 활성화
      paranoid: true, // timestamps 가 활성화 되어야 사용 가능 > deleteAt 옵션 on
    });
  
    Product.associate = models => {
        Product.belongsToMany(models.Category, {through: "ProductCategory"})
    };

    return Product;
};

 

 

 

ProductCategory 테이블이 존재하며, 칼럼으로 각각에 primaryKey가 생성된 걸 확인할 수 있습니다. 

 

2번 방법

 

두 번째 방법은 ProcutCategory 테이블을 직접 만들어 생성하는 방법입니다.

 

Category 모델

module.exports = (sequelize, DataTypes) => {

    const Category = sequelize.define("Category", {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
        comment: "고유번호 UUID",
      },
      name: {
        type: DataTypes.STRING(100),
        comment: "이름",
      },
    }, {
      charset: "utf8", // 한국어 설정
      collate: "utf8_general_ci", // 한국어 설정
      tableName: "Category", // 테이블 이름
      timestamps: true, // createAt & updateAt 활성화
      paranoid: true, // timestamps 가 활성화 되어야 사용 가능 > deleteAt 옵션 on
    });
  
    Category.associate = models => {
        Category.belongsToMany(models.Product, {through: "ProductCategory", foreignKey: "category_id", sourceKey: "id"})
    };

    return Category;
};

 

Product 모델

module.exports = (sequelize, DataTypes) => {

    const Product = sequelize.define("Product", {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
        comment: "고유번호 UUID",
      },
      name: {
        type: DataTypes.STRING(100),
        comment: "이름",
      },
    }, {
      charset: "utf8", // 한국어 설정
      collate: "utf8_general_ci", // 한국어 설정
      tableName: "Product", // 테이블 이름
      timestamps: true, // createAt & updateAt 활성화
      paranoid: true, // timestamps 가 활성화 되어야 사용 가능 > deleteAt 옵션 on
    });
  
    Product.associate = models => {
        Product.belongsToMany(models.Category, { through: "ProductCategory", foreignKey: "product_id", sourceKey: "id"})
    };

    return Product;
};

 

ProductCategory 모델

module.exports = (sequelize, DataTypes) => {

    const ProductCategory = sequelize.define("ProductCategory", {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
        comment: "고유번호 UUID",
      },
      name: {
        type: DataTypes.STRING(100),
        comment: "이름",
      },
    }, {
      charset: "utf8", // 한국어 설정
      collate: "utf8_general_ci", // 한국어 설정
      tableName: "ProductCategory", // 테이블 이름
      timestamps: true, // createAt & updateAt 활성화
      paranoid: true, // timestamps 가 활성화 되어야 사용 가능 > deleteAt 옵션 on
    });
  
    ProductCategory.associate = models => {
      ProductCategory.belongsTo(models.Product, {
        foreignKey: 'product_id', sourceKey: "id"
      });
      ProductCategory.belongsTo(models.Category, {
        foreignKey: 'category_id', sourceKey: "id"
      });
    };

    return ProductCategory;
};

 

연결하고자 하는 모델들은 belongsToMany와 belongsTo를 사용해서 연결하면 됩니다.

 

 

동일하게 테이블이 생성됐지만, ProductCategory 테이블 칼럼 정보는 다르다는 걸 확인할 수 있습니다.

 

직접 실습을 해보시는 걸 추천드리겠습니다. :) 기술을 정확히 알기 위해서는 시간이 들지만., 알고 나면 편안하게 쓸 수 있어요 :)