Home sequelize 모듈 - 02
Post
X

sequelize 모듈 - 02

Sequelize 관계 정의하기

참고 글 : Sequelize - associate

Sequelize에서는 테이블 간의 관계 (1:1, 1:N, N:M)를 정의할 수 있습니다.

모델에 속하는 BeingTo, hasOne, hasMany, BelongsToMany 함수 중 하나를 호출하고 다른 모델을 첫 번째 인수로 제공하여 사용합니다.


1:N 관계

1:N 관계에서는 hasManybelongsTo 메서드를 사용합니다.

sequelize-one-to-many

User와 Comment를 정의해보겠습니다.

한 User가 많은 댓글을 가질 수 있으니 User.hasMany를 사용하고, Comment는 한 User에 속할 수 있으니 Comment.belongsTo를 사용합니다.

즉, 외래키가 존재하는 테이블이 belogsTo를 사용합니다.

또한, 두 번째 매개변수로 다양한 옵션을 전달할 수 있습니다.

  • Options

    • ForeignKey : 외래 키를 직접 명시

      foreignKey를 따로 지정하지 않는다면 모델명+기본 키로 이름이 자동 설정됩니다.

    • sourceKey : hasMany에서 사용하며 외래 키로 전할 속성을 명시

    • targetKey : belongsTo에서 사용하며 외래 키로 가져올 속성을 명시

    • onDelete, onUpdate : 엔티티의 상태 변화를 전파시키는 옵션, (RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL)

1
2
3
db.User.hasMany(db.Comment, { foreignKey: "commenter", sourceKey: "id" });

db.Comment.belongsTo(db.User, { foreignKey: "commenter", targetKey: "id" });

1:1 관계

1:1 관계에서는 hasOnebelongsTo를 사용합니다.

sequelize-one-to-one

1
2
User.hasOne(Info);
Info.belongsTo(User);

M:N 관계

M:N 관계에서는 belongsToMany를 사용합니다.

  • Options

    • through : 생성될 중간 모델(연결 테이블)의 이름을 명시
1
2
3
4
5
// Post
Post.belongsToMany(Hashtag, { through: "PostHashtag" });

// Hashtag
Hashtag.belongsToMany(Post, { through: "PostHashtag" });

예시로 User와 Post를 1 : N 관계로 설정해보겠습니다.


models/posts.js 생성

models 폴더에 posts.js 파일을 생성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// posts.js
import { DataTypes, Model } from "sequelize";

class Post extends Model {
  static init(sequelize) {
    return super.init(
      {
        post_id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true,
          allowNull: false,
          // unique
          // defaultValue
          // validate
        },
        title: {
          type: DataTypes.STRING(30),
          allowNull: false,
        },
        content: {
          type: DataTypes.STRING(30),
          allowNull: false,
        },
      },
      {
        sequelize, // Sequelize 인스턴스
        timestamps: false, // true : created_at, updated_at 컬럼이 생성되며 데이터의 생성, 수정 시 시간이 자동으로 입력됩니다.
        paranoid: false /* true : deletedAt 컬럼이 생성되며 지운 시각이 기록됩니다. */,
        underscored: true, // true : 카멜케이스 대신 스네이크케이스를 사용
        freezeTableName: true, // 모델이름 변환 X, 지정한 이름 사용
        ModelName: "Post", // 모델의 이름을 지정합니다.
        tableName: "posts", // 테이블 이름 명시적 설정
      }
    );
  }

  static associate(db) {
    db.Post.belongsTo(db.User, { foreignKey: "user_id", targetKey: "user_id" });
  }
}

export default Post;

models/users.js

models 폴더의 users.js 파일에도 관계 설정 부분을 추가해줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// users.js
import { DataTypes, Model } from "sequelize";

class User extends Model {
  static init(sequelize) {
    return super.init(
      {
        user_id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true,
          allowNull: false,
          // unique
          // defaultValue
          // validate
        },
        name: {
          type: DataTypes.STRING(30),
          allowNull: false,
        },
        age: {
          type: DataTypes.INTEGER,
          allowNull: true,
        }
      },
      {
        sequelize, // Sequelize 인스턴스
        timestamps: false, // true : created_at, updated_at 컬럼이 생성되며 데이터의 생성, 수정 시 시간이 자동으로 입력됩니다.
        paranoid: false // true : deletedAt 컬럼이 생성되며 지운 시각이 기록됩니다. ,
        underscored: true, // true : 카멜케이스 대신 스네이크케이스를 사용
        freezeTableName: true, // 모델이름 변환 X, 지정한 이름 사용
        modelName: "User", // 모델 이름 명시적 지정
        tableName: "users", // 테이블 이름 명시적 설정
      }
    );
  }

  static associate(db) {
    db.User.hasMany(db.Post, { foreignKey: "user_id", sourceKey: "user_id" });
  }
}

export default User;

index.js 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import Sequelize from "sequelize";

// 클래스를 불러옵니다.
import User from "./users.js";
import Post from "./posts.js";

const config = {
  username: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_DATABASE,
  host: process.env.DB_HOST,
  dialect: "mysql",
};

const db = {};

// new Sequelize를 통해 MySQL 연결 객체를 생성합니다.
const sequelize = new Sequelize(
  config.database,
  config.username,
  config.password,
  config
);

// 연결객체를 나중에 재사용하기 위해 db.sequelize에 넣어줍니다.
db.sequelize = sequelize;

// 모델 클래스를 넣어줍니다.
db.User = User;
db.Post = Post;

// 모델 정의
User.init(sequelize);
Post.init(sequelize);

// 모델간의 관계 정의
User.associate(db);
Post.associate(db);

export default db;

연결 테스트

sequelize-create-table-associate-01

sequelize-create-table-associate-02

에러 없이 posts 테이블에 외래 키로 user_id가 잘 생성된 것을 확인할 수 있습니다.


Sequelize로 CRUD 해보기

기존에 SQL문을 사용했던 CRUD (Create, Read, Update, Delete) 작업을 Sequelize에서는 JS 코드로 구현할 수 있습니다.


데이터 추가 (Create)

주로 create 메서드를 사용하여 레코드를 생성합니다.

  • create() 메서드

    1
    2
    3
    4
    5
    6
    7
    8
    
    /* SQL */
    INSERT INTO users (name, age) VALUES ('psmin', 27);
    
    /* 시퀄라이즈 */
    db.User.create({
      name: 'psmin',
      age: 27,
    });
    
  • findOrCreate() 메서드

    findOrCreate() 메서드는 값이 없으면 생성하고, 값이 있다면 값을 가져옵니다.


데이터 조회 (findAll, findOne)

  • findAll(options)

    findAll() 메서드는 모든 데이터를 조회합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    /* SQL */
    SELECT * FROM users;
    
    /* 시퀄라이즈 */
    let user = db.User.findAll({});
    
    console.log(user[0].name)
    // findAll는 여러 행들을 조회하기에, 각 행들이 배열로 저장됩니다.
    
  • findOne(options)

    findOne() 메서드는 데이터를 하나만 조회합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    /* SQL */
    SELECT * FROM users limit 1;
    
    /* 시퀄라이즈 */
    let user = db.User.findOne({});
    
    console.log(user.name)
    // findOne는 하나만 조회하기에, 객체로 저장됩니다.
    

조건 조회

  • attributes : 가져올 속성를 배열 형태로 지정합니다.

    1
    2
    3
    4
    5
    6
    7
    
    /* SQL */
    SELECT user_id, name FROM users;
    
    /* 시퀄라이즈 */
    db.User.findAll({
      attributes: ["user_id", "name"],
    });
    
  • where : 조건을 지정하여 특정 레코드만 가져옵니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    /* SQL */
    SELECT * FROM users WHERE user_id = 2;
    
    /* 시퀄라이즈 */
    db.User.findAll({
      where : {
        user_id : 2
      }
    });
    
  • order : 결과를 정렬합니다. (2차원 배열 형태)

    1
    2
    3
    4
    5
    6
    7
    
    /* SQL */
    SELECT * FROM users ORDER BY age DESC name ASC;
    
    /* 시퀄라이즈 */
    db.User.findAll({
      order:[['age':'DESC'], ['name':'ASC']],
    });
    
  • limit : 조회 결과의 개수를 제한합니다.
    offset : 오프셋을 설정하여 페이징을 지원합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    
     /* SQL */
    SELECT * FROM users LIMIT 10 OFFSET 5;
    
    /* 시퀄라이즈 */
    db.User.findAll({
      limit: 10,
      offset: 5,
    });
    

특수연산자 (Op)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const Op = Sequelize.Op

[Op.and]: [{a: 5}, {b: 6}] // (a = 5) AND (b = 6)
[Op.or]: [{a: 5}, {a: 6}]  // (a = 5 OR a = 6)

[Op.gt]: 6,                // > 6
[Op.gte]: 6,               // >= 6

[Op.lt]: 10,               // < 10
[Op.lte]: 10,              // <= 10

[Op.ne]: 20,               // != 20
[Op.eq]: 3,                // = 3

[Op.is]: null              // IS NULL
[Op.not]: true,            // IS NOT TRUE

[Op.between]: [6, 10],     // BETWEEN 6 AND 10
[Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15

[Op.in]: [1, 2],           // IN [1, 2]
[Op.notIn]: [1, 2],        // NOT IN [1, 2]

[Op.like]: '%hat',         // LIKE '%hat'
[Op.notLike]: '%hat'       // NOT LIKE '%hat'
[Op.startsWith]: 'hat'     // LIKE 'hat%'
[Op.endsWith]: 'hat'       // LIKE '%hat'
[Op.substring]: 'hat'      // LIKE '%hat%'

[Op.regexp]: '^[h|a|t]'    // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
[Op.notRegexp]: '^[h|a|t]' // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)

  • 예시

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    /* SQL */
    SELECT name, age FROM users WHERE name = 'Jack' OR age > 30;
    
    /* 시퀄라이즈 */
    User.findAll({
        attributes: ['name', 'age'],
        where: {
            [Op.or]: [
                { name : 'Jack' },
                { age: { [Op.gt]: 30 } }
            ],
            // married = 0 or age > 30
        },
    });
    

데이터 수정 (update)

update 메서드를 사용해 데이터를 수정합니다.

첫 번째 인수는 수정할 내용, 두 번째 인수는 수정할 레코드를 특정할 조건을 작성합니다.

1
2
3
4
5
6
7
8
9
/* SQL */
UPDATE users SET name = '새로운 이름' WHERE user_id = 2;

/* 시퀄라이즈 */
db.User.update({
    name : '새로운 이름',
}, {
    where: { user_id: 2 },
});

데이터 삭제 (destroy)

destroy 메서드를 사용해 데이터를 삭제합니다.

1
2
3
4
5
6
7
/* SQL */
DELETE FROM users WHERE user_id = 2;

/* 시퀄라이즈 */
db.User.destroy({
    where: { user_id: 2 },
});
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.