[cookbook] sequelize #3 Migration (Nodejs에서도 ORM 사용해보자)

Sequelize #3 migration Introduction

자 이제 3편이네요. 1편에서 sequelize 에 대한 개념과 CRUD를 익혔고, 2편에서는 Association에 대한 것을 익혔습니다. 이번편에서는 migration 명령에 대해서 알아보겠습니다.

orm

Usage

help

sequelize -h
sequelize --help
--> prints the help

version

sequelize -V
sequelize --version
--> prints the version

init

database 접속정보를 담은 config/config.json 파일을 생성합니다.

$ sequelize -i
$ sequelize --init
--> creates a migration folder
--> creates a config folder
--> saves a config.json inside the config folder

위 명령을 실행하면 아래와 같이 config/config.json 파일을 확인할 수 있습니다. 접속정보를 기입해주세요.

$ cat config/config.json
{                                 
  "username":"root",              
  "password":"pass",            
  "database":"test",              
  "host":"127.0.0.1"              
}                                 

create migration

migration-name 이름을 기입해주세요. 무엇을 migration 할지에 대한 컨셉을 기입해주시면 됩니다.

$ sequelize -c [migration-name]
$ sequelize --create-migration [migration-name]
--> creates the migrations folder
--> creates a file with current timestamp + migration-name
--> migration-name has the default 'unnamed-migration'

위 명령을 실행하면 아래와 같이 migration 디렉토리안에 timestamp 와 결합한 파일명을 확인할 수 있습니다. 이는 실행할때마다 history 로서 남게 됩니다.

$ sequelize -c foo                                      

path.existsSync is now called `fs.existsSync`.                    
Creating migrations folder.                                       
Migrations folder already exist.                                  

$ ll migrations/                                        
total 8                                                           
-rw-r--r--  1 nanhap  staff  167  8  8 21:40 20120808214023-foo.js

Migration Skeleton

$ cat migrations/20120808214023-foo.js  

module.exports = {                                
  up: function(migration, DataTypes) {            
    //
    // 실행할 명령                
    //
  },                                              
  down: function(migration) {                     
    //
    // 되돌릴 명령
    //
  }                                               
}                                      

execute migrate

migration 디렉토리안에 있는 파일을 실행합니다. migration skeleton에서 up에 해당합니다. config/config.json 파일에 접속정보를 정확히 기입하지 않으면, 아무런 에러메세지가 나오지 않고 실행되므로 당황하지 마시고, 데이타베이스 접속정보를 기입하세요.

$ sequelize -m
$ sequelize --migrate
--> needs a valid config.json
--> runs pending migrations
--> saves successfully executed migrations inside the database

undo execute migrate

migration 디렉토리안에 있는 파일을 실행합니다. migration skeleton에서 down에 해당합니다. migrate 실행을 되돌릴경우 사용합니다.

$ sequelize -m -u
$ sequelize --migrate --undo
--> needs a valid config.json
--> reverts the last successfull migration
--> when there were multiple executed migrations, all of them are reverted

Migration Table

create / drop table

create 와 drop 은 을 이루어 동시에 작성합니다.

syntax

migration.createTable('생성할 테이블명', {
  필드명#1: DataTypes.STRING,
  필드명#2: DataTypes.INTEGER,
  //
  // 부가정보를 object으로 정의가능
  //
  필드명#3: {
    type         : DataTypes.BOOLEAN,
    defaultValue : false,
    allowNull    : false
  }
  }, {
    engine  : 'MYISAM', // default: 'InnoDB'
    charset : 'latin1'  // default: null
  });


migration.dropTable('삭제할 테이블명')

example

$ cat migrations/20120808212517-foo.js

module.exports = {                                  
  up: function(migration, DataTypes) {              
    //
    // 실행할 명령을 기입합니다.
    //
    migration.createTable('foo', {                  
      name: DataTypes.STRING                        
    });                                             
  },                                                
  down: function(migration) {
    //
    // 되돌릴 명령을 기입합니다.
    //            
    migration.dropTable('foo');                                
  }                                                 
}                                                   


#
# migration 시작
# create table
#
$ sequelize -m                           
path.existsSync is now called `fs.existsSync`.     
Executing migration: 20120808212517-foo.js         
Executed migration: 20120808212517-foo.js          

mysql 확인해볼까요?

mysql> desc foo;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| name  | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
1 row in set (0.01 sec)

다시 되돌려 보겠습니다.

#
# migration 시작전으로 되돌리기
# drop table
#
$ sequelize -m -u                        
path.existsSync is now called `fs.existsSync`.     
Executing migration: 20120808212517-foo.js         
Executed migration: 20120808212517-foo.js        

mysql 확인해보죠.

mysql> desc foo;
ERROR 1146 (42S02): Table 'test.foo' doesn't exist

rename table

syntax

renameTable(before, after)

example

위에서 foo 테이블 생성한 migration 파일 이후로 또다른 migration 파일이 생성됩니다.

$ sequelize -c foo_rename
"migrations/20120808215842-foo_rename.js" 9L, 209C written

$ ll migrations/                                               
total 16                                                                 
-rw-r--r--  1 nanhap  staff  272  8  8 21:54 20120808212517-foo.js       
-rw-r--r--  1 nanhap  staff  209  8  8 21:59 20120808215842-foo_rename.js

migration 실행하면 어떻게 될까요?

$ sequelize -m                                                       
path.existsSync is now called `fs.existsSync`.                                 
Executing migration: 20120808215842-foo_rename.js                              
Executed migration: 20120808215842-foo_rename.js                               

foo 테이블에 생성된 후, foo 테이블의 이름이 bar 테이블로 변경됩니다.

mysql> desc bar;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| name  | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
1 row in set (0.01 sec)

etc.

기타 함수들은 여기서 참고하세요.

Migration Column

add column table

syntax

migration.addColumn(
  '대상 테이블명',
  '추가할 필드명',
  {
    type      : DataTypes.STRING,
    allowNull : false
  }
)

example

위의 migration history 는 연결됩니다. bar table 으로 변경되었기에 migration-name을 bar table 에 add column 한다는 내용으로 실행합니다.

$ sequelize -c bar_addcolumn        
path.existsSync is now called `fs.existsSync`.
Creating migrations folder.                   
Migrations folder already exist.              

생성된 새로운 migration 파일을 수정합니다.

$ cat 20120808220450-bar_addcolumn.js

module.exports = {                          
  up: function(migration, DataTypes) {      
    // add altering commands here           
    migration.addColumn('bar', 'address', { 
      type      : DataTypes.STRING,         
      allowNull : false                     
    });                                     
  },                                        
  down: function(migration) {
    //
    // sequelize -m -u 실행되기 위해서는
    // 이 부분은 추가된 address 필드를 삭제하는 구문이 들어가야 합니다.
    //               
  }                                         
}                                           

migration 을 실행합니다.

$ sequelize -m                                      
path.existsSync is now called `fs.existsSync`.                
Executing migration: 20120808220450-bar_addcolumn.js          
Executed migration: 20120808220450-bar_addcolumn.js           

신기하게도 이전 migration 파일이 실행되지 않습니다. 변화가 없는 구성은 적용하지 않는거 같습니다.

mysql

address 필드가 추가된것을 확인합니다.

mysql> desc bar;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| name    | varchar(255) | YES  |     | NULL    |       |
| address | varchar(255) | NO   |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

remove column table

syntax

migration.removeColumn('대상 테이블', '삭제할 필드명');

example

$ cat 20120808220450-bar_addcolumn.js

module.exports = {                          
  up: function(migration, DataTypes) {      
    // add altering commands here           
    migration.addColumn('bar', 'address', { 
      type      : DataTypes.STRING,         
      allowNull : false                     
    });                                     
  },                                        
  down: function(migration) {
    //
    // sequelize -m -u 실행되기 위해서는
    // 이 부분은 추가된 address 필드를 삭제하는 구문이 들어가야 합니다.
    //
    migration.removeColumn('bar', 'address');
  }                                         
}                        

이렇게 down 부분을 추가해주고, migration undo 를 실행합니다.

$ sequelize -m -u                                 
path.existsSync is now called `fs.existsSync`.              
Executing migration: 20120808220450-bar_addcolumn.js        
Executed migration: 20120808220450-bar_addcolumn.js         

mysql

후후. 필드가 삭제되었네요.

mysql> desc bar;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| name  | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
1 row in set (0.01 sec)

etc.

기타 함수들은 여기서 참고하세요.

Migration Index

add / remove index

syntax #1

migration.addIndex('대상 테이블', ['firstname', 'lastname'])
migration.removeIndex('대상 테이블', ['firstname', 'lastname'])

syntax #2

migration.addIndex('대상 테이블', ['firstname', 'lastname'], {
   indexName: '추가할 인덱스명'
})
migration.removeIndex('대상 테이블', '삭제할 인덱스명'); 

example

bar table의 name 필드에 index 를 추가하겠습니다.

$ sequelize -c bar_name_addindex    

path.existsSync is now called `fs.existsSync`.
Creating migrations folder.                   
Migrations folder already exist.              

$ cat 20120808221821-bar_name_addindex.js

module.exports = {                          
  up: function(migration, DataTypes) {      
    migration.addIndex('bar', ['name'], {   
      indexName: 'Index1'                   
    });                                     
  },                                        
  down: function(migration) {               
    migration.removeIndex('bar', 'Index1'); 
  }                                         
}                                           

sequelize $ sequelize -m                                    
path.existsSync is now called `fs.existsSync`.              
Executing migration: 20120808221821-bar_name_addindex.js    
Executed migration: 20120808221821-bar_name_addindex.js     

mysql 확인해보죠.

mysql> show index from bar;                                                                                                                                                                                 
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| bar   |          1 | Index1   |            1 | name        | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

index 를 지워볼까요?

$ sequelize -m -u                                 
path.existsSync is now called `fs.existsSync`.              
Executing migration: 20120808221821-bar_name_addindex.js    
Executed migration: 20120808221821-bar_name_addindex.js     

mysql 확인해보죠.

mysql> show index from bar;                                                                                                                                                                                 
Empty set (0.00 sec)

etc.

기타 함수들은 여기서 참고하세요.

Migration Meta

migration 한 결과는 config/config.json에 정의된 데이타베이스안에 SequelizeMeta 라는 테이블을 만들고, 마지막 실행한 timestamp 값을 저장하여 재실행하는것을 방지합니다.

mysql> select * from SequelizeMeta;
+----------------+----------------+----+
| from           | to             | id |
+----------------+----------------+----+
| 20120808220450 | 20120808220450 |  8 |
| 20120808221821 | 20120808221821 |  9 |
+----------------+----------------+----+
2 rows in set (0.00 sec)

하지만, 다시 이를 돌이켜 사용할 수 있는 API 는 지원되지 않고 있습니다.

start ~ end

migration을 처음부터 모두 적용하고, 다시 원래 최초 상태로 돌이켜보겠습니다.

delete sequelize meta

mysql> deleterom SequelizeMeta;                           
Query OK, 4 rows affected (0.00 sec)

execute migrate

sequelize $ sequelize -m                                                 
path.existsSync is now called `fs.existsSync`.                           
Executing migration: 20120808212517-foo.js                               
Executed migration: 20120808212517-foo.js                                
Executing migration: 20120808215842-foo_rename.js                        
Executed migration: 20120808215842-foo_rename.js                         
Executing migration: 20120808220450-bar_addcolumn.js                     
Executed migration: 20120808220450-bar_addcolumn.js                      
Executing migration: 20120808221821-bar_name_addindex.js                 
Executed migration: 20120808221821-bar_name_addindex.js                  l

mysql data

mysql> desc bar;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| name    | varchar(255) | YES  | MUL | NULL    |       |
| address | varchar(255) | NO   |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

mysql> show index from bar;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| bar   |          1 | Index1   |            1 | name        | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

undo execute migrate

sequelize $ sequelize -m -u                                            
path.existsSync is now called `fs.existsSync`.                         
Executing migration: 20120808221821-bar_name_addindex.js               
Executed migration: 20120808221821-bar_name_addindex.js                
Executing migration: 20120808220450-bar_addcolumn.js                   
Executed migration: 20120808220450-bar_addcolumn.js                    
Executing migration: 20120808215842-foo_rename.js                      
Executed migration: 20120808215842-foo_rename.js                       
Executing migration: 20120808212517-foo.js                             
Executed migration: 20120808212517-foo.js                              

mysql data

mysql> show tables;
+-----------------+
| Tables_in_test  |
+-----------------+
+-----------------+
0 rows in set (0.00 sec)

mysql> select * from SequelizeMeta;
Empty set (0.00 sec)

Conclusion

점점 빠져드는 ORM 입니다. 사용해보시고, 불편한 점 있으시면 의견 나누어 보아요. 더위 조심하시고, KIN플하세요. 감사합니다.

추천 관련글