Sequelize Migration

도커를 통해 데이터베이스를 띄우고 express를 실행시켜 연결을 해보았다. MySQL과 연결하여 데이터베이스를 사용하기 위해서 Sequelize를 사용하여 연결까지 성공적으로 연결을 진행하였다.

기존에 시퀄라이즈를 사용할때는 모델을 모두 정의한 후 데이터베이스를 생성하고 모델의 수정사항이 있을때마다 **Sync({ force: false || true })**옵션을 통해 데이터베이스를 수정하며 진행했다. 물론 혼자 사용하는 데이터베이스고 서비스를 하지 않는 디비여서 이런방식으로 사용해도 무방하지만 실무에서 사용하는 데이터베이스는 많은 데이터가 있고 구조의 변경이 일어날때 마다 데이터를 백업한다던지 새로 덤프 한다는 것은 현실적으로 어려움이 있다.

이러한 문제점을 보완하기 위해 ORM에서는 마이그레이션 기능을 지원한다. 마이그레이션이란 어떤 운영환경에서 다른 환경으로 환경의 변화를 위해 옮겨지는 작업을 의미한다.

데이터베이스 마이그레이션이란.

데이터베이스 마이그레이션이란 하나의 데이터베이스를 다른 종류의 데이터베이스로 데이터를 옮기는 경우 혹은 두개의 데이터베이스를 하나의 시스템으로 합치거나 분할 혹은 데이터베이스 모델의 구조적 변경을 진행 하는 모든 과정을 의미한다.
데이터 베이스 마이그레이션이란?

그렇다면 시퀄라이즈에서 제공하는 Migration 기능에는 어떤 기능이 있을까? 시퀄라이즈에서는 몇가지 명령어를 통해 마이그레이션 기능을 사용할 수 있다고 한다.

Sequelize Migration

시퀄라이즈 마이그레이션 기능을 사용하기 위해서는 먼저 sequelize-cli 패키지를 설치해야한다.

1
npm install -g sequelize-cli 

기존에 시퀄라이즈를 연결했으므로 초기 세팅 과정은 넘어가도록 하고 초기 세팅 과정은 공식문서를 통해 확인이 가능하다.

  1. Create Model
    마이그레이션을 통해 모델을 생성하는 과정이다. **명령어는 sequelize-cli model:generate –name ${modelName} –attributes ${columnName:type}**을 통해 생성이 가능하다.
1
sequelize-cli model:generate --name User --attributes nick_name:string

명령어를 실행하면 models 디렉토리에 user.js migrations 디렉토리에 시간-create-user.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
// models/user.js
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
static associate(models) {
User.hasMany(models.Post, {
as: 'posts',
foreignkey: 'user_id',
});
}
};
User.init({
nick_name: {
type: DataTypes.STRING,
allowNull: false,
}
}, {
sequelize,
modelName: 'User',
tableName: 'users',
underscored: true,
charset: 'utf8',
coolate: 'utf8_general_ci',
});
return User;
};

User.init 내부에는 명령어 실행 시 –attributes에 지정했던 컬럼과 타입이 지정되며 추가적으로 컬럼을 생성하려면 파일 내부에서 직접 컬럼을 추가해주면 된다.

동일한 방식으로 유저 모델과 게시글 모델 마이그레이션 파일을 생성해주면 된다.

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
// models/post.js
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Post extends Model {
static associate(models) {
Post.belongsTo(models.User, {
as: 'user',
foreignkey: 'user_id',
});
}
};
Post.init({
title: {
type: DataTypes.STRING,
allowNull: false,
},
content: {
type: DataTypes.STRING,
allowNull: false,
},
user_id: {
type: DataTypes.INTEGER,
}
}, {
sequelize,
modelName: 'Post',
tableName: 'posts',
underscored: true,
charset: 'utf8',
coolate: 'utf8_general_ci',
});
return Post;
};

두개의 모델 파일이 생성되고 내부에 추가할 컬럼을 입력하며 테이블 생성시 설정할 옵션들을 지정해주면 옵션에 맞게 테이블이 생성이 된다.

또한 유저와 게시글의 1:N 관계를 지정하기 위해 hasMany, belongsTo 옵션을 통해 관계를 지정해주었다.

모델 파일이 성공적으로 생성이 되었으니 마이그레이션 스크립트를 확인해보자.

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
// migrations/20211231045301-create-user.js
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
nick_name: {
type: Sequelize.STRING,
allowNull: false,
},
created_at: {
allowNull: false,
type: Sequelize.DATE
},
updated_at: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('users');
}
};
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
// migrations/20211231045301-create-post.js
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('posts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING,
allowNull: false,
},
content: {
type: Sequelize.STRING,
allowNull: false,
},
created_at: {
allowNull: false,
type: Sequelize.DATE,
},
updated_at: {
allowNull: false,
type: Sequelize.DATE,
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('posts');
}
};

마이그레이션 파일은 up down 메서드로 구성되며 up은 마이그레이션 실행시 동작하고 down은 실패시 동작할 명령이다. 다양한 옵션과 명령들은 문서를 통해 확인이 가능하다.
sequelize migrations docs

모델을 생성하기 위해 up 내부에 createTable 메서드를 사용하고 내부에는 모델 파일과 동일하게 컬럼을 입력해야하며 create_at, updated_at 컬럼은 자동으로 생성되는 컬럼이므로 모델 파일에 정의해줄 필요는 없다.

모델 파일과 마이그레이션 파일을 생성했다면 이제 마이그레이션을 실행해주면 된다.

실행하기 위한 명령어는 sequelizee-cli db:migrate 이며 기본적으로는 env=development 으로 마이그레이션이 실행되니 production환경이나 test환경이라면 env 옵션값을 입력해주면 된다.

1
2
3
4
5
6
7
8
9
10
11
root@0e9dcef32c40:/usr/src/app# sequelize-cli db:migrate         

Sequelize CLI [Node: 12.22.8, CLI: 6.3.0, ORM: 6.12.0]

Loaded configuration file "config/config.json".
Using environment "development".
== 20211231045323-create-user: migrating =======
== 20211231045323-create-user: migrated (0.024s)

== 20211231045909-create-post: migrating =======
== 20211231045909-create-post: migrated (0.008s)

두개의 마이그레이션 파일이 실행되었고 데이터베이스를 확인해보면 users. posts 테이블이 생성된 것을 확인하면 된다. 도커로 실행중인 mysql을 bash로 접속하여 확인해보자

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
44
45
46
47
48
49
50
51
52
53
54
55
56
# mysql docker connection
docker exec -it docker-test_mysql_1 bash

# mysql connection
root@4921e7558de3:/# mysql -U test -p
Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.36 MySQL Community Server (GPL)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use test
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| SequelizeMeta |
| posts |
| users |
+----------------+
3 rows in set (0.01 sec)

mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| nick_name | varchar(255) | NO | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> desc posts;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(255) | NO | | NULL | |
| content | varchar(255) | NO | | NULL | |
| user_id | int(11) | YES | MUL | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

테이블 두개가 생성되었고 모델 파일과 마이그레이션에 정의한 컬럼이 모두 생성되고 외래키 지정까지 정상적으로 등록이 되었다. 이렇게 마이그레이션 실행에 대한 정보는 테이블 목록에서 보이는 SequelizeMeta 테이블에 저장되고 관리된다.

1
2
3
4
5
6
7
8
9
# SequelizeMeta table info
mysql> select * from SequelizeMeta;
+-------------------------------+
| name |
+-------------------------------+
| 20211231045323-create-user.js |
| 20211231045909-create-post.js |
+-------------------------------+
2 rows in set (0.00 sec)

실행했던 두개의 마이그레이션 정보가 입력이 되어있으며 실행한 스크립트의 이름을 알 수 있다. 이렇게 마이그레이션을 실행했다면 되돌리기 위한 명령어로는 db:migrate:undo, db:migrate:undo:all이 있고 특정버전 파일을 입력하면 해당 위치까지 롤백이 가능하다.

seed migration

테이블을 생성했다면 가상의 데이터도 마이그레이션을 통해 삽입이 가능하다. 마이그레이션 파일을 생성하기 위해서 seed:migerate –name #{name} 명령어를 사용하면 된다.

유저 테이블과 게시글 테이블에 하나씩 데이터를 저장해보자.

1
sequelize-cli seed:migarate --name insert-data

명령어를 실행하면 seeders 폴더에 seed파일이 생성된다.

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
// seeders/202112311653-insert-data.js
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.bulkInsert('users', [{
nick_name: '한주련',
created_at: new Date(),
updated_at: new Date(),
}], {});
await queryInterface.bulkInsert('posts', [
{
title: 'sample title1',
content: 'sample content1',
user_id: 1,
created_at: new Date(),
updated_at: new Date(),
},
{
title: 'sample title2',
content: 'sample content2',
user_id: 1,
created_at: new Date(),
updated_at: new Date(),
}
])
},

down: async (queryInterface, Sequelize) => {
/**
* Add commands to revert seed here.
*
* Example:
* await queryInterface.bulkDelete('People', null, {});
*/
}
};

테이블 생성 마이그레이션 파일과 동일한 구조로 생성이 되며 간단하게 유저테이블에 데이터를 넣고 게시글 테이블에도 글 하나를 입력하였다. 해당 마이그레이션을 실행하기 위한 과정은 다음과 같다.

1
2
3
4
5
6
7
8
root@0e9dcef32c40:/usr/src/app# sequelize-cli db:seed:all     

Sequelize CLI [Node: 12.22.8, CLI: 6.3.0, ORM: 6.12.0]

Loaded configuration file "config/config.json".
Using environment "development".
== 20211231051943-insert_user_sample: migrating =======
== 20211231051943-insert_user_sample: migrated (0.019s)

sequelize-cli db:seed:all 명령어를 통해서 데이터를 저장하면 데이터베이스에는 새로운 데이터가 저장 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> select * from users;
+----+-----------+---------------------+---------------------+
| id | nick_name | created_at | updated_at |
+----+-----------+---------------------+---------------------+
| 1 | 한주련 | 2021-12-31 08:13:09 | 2021-12-31 08:13:09 |
+----+-----------+---------------------+---------------------+
1 row in set (0.00 sec)

mysql> select * from posts;
+----+---------------+-----------------+---------+---------------------+---------------------+
| id | title | content | user_id | created_at | updated_at |
+----+---------------+-----------------+---------+---------------------+---------------------+
| 1 | sample title1 | sample content1 | 1 | 2021-12-31 08:13:09 | 2021-12-31 08:13:09 |
| 2 | sample title2 | sample content2 | 1 | 2021-12-31 08:13:09 | 2021-12-31 08:13:09 |
+----+---------------+-----------------+---------+---------------------+---------------------+
2 rows in set (0.00 sec)

각각의 데이터가 잘 저장되었다. seed 스크립트는 마이그레이션 파일처럼 따로 기록되고 관리되지 않으므로 여러번 실행하면 실행 횟수 만큼 데이터가 저장되니 유의해야 한다.

마이그레이션을 통한 모델 수정

마이그레이션을 통해 테이블을 생성하고 데이터도 넣었으니 모델을 수정하는 방법을 알아보자. 모델 수정 마이그레이션 파일 생성 명령어는 다음과 같다. sequelzie-cli migration:generate –name ${fileName}

명령어를 실행하면 migrations 폴더에 파일이 생성되고 구조는 기존과 동일하게 up down 메서드로 구성되어있다.

users 테이블에 email 컬럼을 추가해보자.

1
2
3
4
5
6
root@0e9dcef32c40:/usr/src/app# sequelize-cli migration:generate --name add_column_email_to_users 

Sequelize CLI [Node: 12.22.8, CLI: 6.3.0, ORM: 6.12.0]

migrations folder at "/usr/src/app/migrations" already exists.
New migration was created at /usr/src/app/migrations/20220101083719-add_column_email_to_users.js .
1
2
3
4
5
6
7
8
9
10
11
12
13
// migrations/20220101174356-add_column_email_to_users.js
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('users', 'email', {
type: Sequelize.STRING(50),
unique: true,
}, {});
},

down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('users', 'email');
}
};

실행시 일어날 동작과 실패시 일어날 동작으로 구성하여 작성해주었다.

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
# express container
root@0e9dcef32c40:/usr/src/app# sequelize-cli db:migrate

Sequelize CLI [Node: 12.22.8, CLI: 6.3.0, ORM: 6.12.0]

Loaded configuration file "config/config.json".
Using environment "development".
== 20220101083719-add_column_email_to_users: migrating =======
== 20220101083719-add_column_email_to_users: migrated (0.114s)

# mysql container
mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| nick_name | varchar(255) | NO | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
| email | varchar(50) | YES | UNI | NULL | |
+------------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

mysql> select * from users;
+----+-----------+---------------------+---------------------+-------+
| id | nick_name | created_at | updated_at | email |
+----+-----------+---------------------+---------------------+-------+
| 1 | 한주련 | 2021-12-31 08:13:09 | 2021-12-31 08:13:09 | NULL |
+----+-----------+---------------------+---------------------+-------+
1 row in set (0.00 sec)

email 컬럼이 추가되었다. 이런 방식으로 컬럼의 추가 삭제 옵션 변경 등 다양한 작업을 할 수 있다.


시퀄라이즈에서 지원하는 마이그레이션 기능을 사용하여 데이터 베이스를 관리하니 기존 모델의 수정사항이 생길 때마다 데이터베이스를 갈아 엎어버렸던 과정이 상당히 비효율 적이였다고 생각이 되었다. 테이블 날리고 다시 생성하고… 또 데이터 넣어주고.. 이러한 과정을 마이그레이션을 통하면 원할하게 이루어진다는 것을 알게 되었고 마이그레이션 기능을 통해 데이터베이스를 관리하면 안전하고 데이터를 보존할 수 있다는 것을 확실하게 알게되었으며 어떤 과정을 ORM에서 마이그레이션을 지원하는지 학습하게 되어 좋았다.

그리고 대부분의 ORM들이 지원하는 마이그레이션의 기능이 비슷하다는 것을 알게된 시간이기도 했다. 회사에서 루비 온 레일스의 액티브레코드를 사용하며 마이그레이션 기능을 처음 사용해보았고.. 보다 효과적으로 사용하기 위해 공부해야 겠다는 생각이였는데 도커를 공부하면서 하다니.. 겸사겸사 좋은 시간이였다.

Author

han Ju Ryeon

Posted on

2022-01-01

Updated on

2022-01-01

Licensed under

댓글