이전 글에서는 코드로 실제 테이블들을 생성해보았다. 하지만 이렇게 코드로 하지 않고 sequelize-cli를 사용해서도 할 수 있다. 그리고 개인적으로는 이 방법을 추천한다. 잠깐 생성되었던 4개의 테이블을 모두 삭제해보자.
그리고 떠올려보자. 혹시 이전에 모델들을 생성했을 때 migrations 디렉토리에 이런 파일들이 생겼던 것 기억나는가?
지금 이 migrations라는 디렉토리에는 sequelize-cli로 수행한 모든 명령들이 각각 하나의 파일으로 남는다. (데이터베이스 세계에서는 보통 테이블의 컬럼을 추가, 수정, 삭제하는 등 데이터베이스의 스키마가 수정되어 가는 과정을 migration이라고 한다) 이 파일 하나를 하나의 migration(변경사항)이라고 생각하면 된다.
npx sequelize model:generate --name Student --attributes registrationNum:string,name:string,age:integer
npx sequelize model:generate --name ScholarshipAccount --attributes accountNum:string,validDate:date,balance:integer
npx sequelize model:generate --name Course --attributes courseCode:string,title:string,description:string
npx sequelize model:generate --name Professor --attributes employeeNum:string,age:integer,registrationNum:string
이 명령들을 실행해서 모델을 만들었을 때 현재 migrations 디렉토리 안의 파일들도 생긴 것인데 도대체 어떤 내용인지 파일 하나를 살펴보자.
// 20210406..-create-student.js
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Students', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
registrationNum: {
type: Sequelize.STRING
},
name: {
type: Sequelize.STRING
},
age: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Students');
}
};
이 파일에서 외부로 공개하는 객체는
를 갖고 있다. up 메소드는 이 마이그레이션을 적용했을 때, 실행될 내용들을 담고 있고, down 메소드는 이 마이그레이션을 적용 해제했을 때 원상복구시키기 위해 필요한 내용들이 들어가 있다.
일단 up 메소드를 보면
await queryInterface.createTable('Students', {
...
이런 코드가 보인다. 뭔가 딱 봐도 테이블을 생성하는 코드 같지 않은가? 이 queryInterface라는 객체는 sequelize가 좀더 저수준의 작업을 할 때 사용하는 객체로 개발자도 그 사용법을 알아두는 게 좋다. 이 코드 뒤에 각 컬럼의 이름과 속성들을 설정하는 코드가 보인다.
그리고 down 메소드를 보면
await queryInterface.dropTable('Students');
...
이렇게 Students 테이블을 삭제하는 코드가 보인다. 이 마이그레이션을 적용 해제 한다는 것은 Students 테이블이 존재하기 전으로 돌아가는 것이기 때문에 알맞은 코드가 적혀있는 것을 확인할 수 있다.
자! migrations 디렉토리의 다른 파일들도 모두 이런 식으로 생겼을 것이다. 직접 확인해보길 바란다.
그럼 migrations 디렉토리가 뭔지, 그 안의 파일들이 무엇인지는 알았다.
이제 이것들을 사용해서 데이터베이스에 실제로 테이블들을 생성할 것이다. 방법은 쉽다. 아래 명령을 실행하면 된다.
npx sequelize db:migrate
그럼 migration들이 각 파일 이름 맨 앞에 있는 timestamp 순서대로 적용되는 모습을 보여주는 결과가 출력된다. 확인해보면
정말로 4개의 테이블들이 잘 생성된다. 그런데 지금 처음 보는 테이블이 보인다. SequelizeMeta라는 테이블이다. 이 테이블을 살펴보면
이런 내용이 보인다. 이것은 현재 데이터베이스에 적용된 migration 파일들이 그 적용된 순서대로 적혀있는 것이다. 현재 어느 migration 파일까지 적용되었는지를 알아야하는 이유는 그래야 이미 적용된 migration 파일은 제외하고 그 이후의 migration 파일들만 적용할 수 있기 때문이다. 왜 아까 내가 sequelize-cli 명령을 사용해서 테이블을 생성하는 것을 더 좋아한다고 했는지 이유가 느껴지지 않은가?
await sequelize.sync()
와 같은 코드로 테이블을 생성해도 동작에는 문제가 없지만 실제 서비스를 운영할 때는 migration이 중요한 이슈 중의 하나이다. 이번 글에서처럼 sequelize-cli 명령으로 테이블을 생성하면 이런 마이그레이션 상황을 한눈에 볼 수 있기 때문에 더 좋다고 생각하는 것이다.
위에서 본 queryInterface 객체를 사용하면 테이블 생성 뿐만 아니라 컬럼 추가 등의 원하는 거의 모든 작업을 할 수 있다. 자세한 내용은 공식 문서를 참조하면 된다.
그럼 잠깐 어떻게 하는 건지 배워보자. 현재 migrations 디렉토리에 있는 파일들은 sequelize-cli 명령으로 모델들을 생성할 때 생긴 부산물들이다. 하지만 migration 파일을 직접 생성하는 것도 가능하다. Students 테이블에 학생의 전공을 나타내는 major라는 컬럼을 생성하는 migration을 만들겠다. 아래 명령을 실행하자.
npx sequelize migration:generate --name 'student(add_major_column)'
그럼 migrations 디렉토리에
이렇게 새롭게 생긴 migration 파일이 생긴다. 이 파일을 클릭하고 up 메소드를 채워주면 된다. 아래와 같이 쓰겠다.
// (타임스탬프)-student(add_major_column).js
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('Students', 'major', Sequelize.STRING);
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('Students', 'major');
},
};
up 메소드에는 Students 테이블에 STRING 데이터 타입을 가진 major라는 컬럼을 추가하는 코드를,
down 메소드에는 Students 테이블에서 major 컬럼을 삭제하는 코드를 썼다. 이대로 파일을 저장하고 다시
npx sequelize db:migrate
명령을 실행해보자. 그럼 방금 만든 migration 파일만 적용되는 듯한 결과가 출력된다.
아까 말했듯이 SequelizeMeta 테이블에서 이미 수행한 migration 파일들의 이름이 적혀있기 때문에 거기 적혀있지 않던 migration 파일만 적용을 하는 것이다. Students 테이블을 확인해보면
major 컬럼이 잘 추가된 것을 알 수 있고 SequelizeMeta 테이블을 보면
가장 최근에 적용된 migration 파일의 이름도 잘 추가된 것을 알 수 있다.
자, 그럼 가장 최근 migration 파일을 적용 해제(undo)하는 법도 배워보자. 이 명령을 실행하자.
npx sequelize db:migrate:undo
그리고 다시 확인해보면
Students 테이블에서 major 컬럼이 다시 삭제되었고
SequelizeMeta 테이블에서도 최신 migration 파일의 이름이 보이지 않는 것을 알 수 있다.
최신 migration 파일 뿐만 아니라 과거의 특정 migration 파일만 적용 해제(undo) 하는 것도 가능하다. 예를 들어, 지금 이미지에서 couurse 테이블을 만드는 migration 파일인 20210406141947-create-course.js 파일을 undo 해보자. 아래 명령을 실행하면 된다.
npx sequelize db:migrate:undo --name 20210406141947-create-course.js
--name 옵션으로 해당 migration 파일을 특정해주면 된다. 실행하면
Courses 테이블이 삭제되어 보이지 않고
SequelizeMeta 테이블에서도 해당 migrations 파일의 이름이 보이지 않는 것을 알 수 있다.
자, 다시 해당 20210406141947-create-course.js migration 파일을 적용하자. 이때는 --name 옵션이 아니라 --to 옵션을 사용해야 한다.
npx sequelize db:migrate -to 20210406141947-create-course.js
--to에 지정한 migration 파일까지를 모두 적용하는 명령이다. 실행하면
Courses 테이블이 잘 생성되고
SequelizeMeta 테이블을 보면 해당 migration 파일이 다시 잘 삽입되어 있는 것을 볼 수 있다.
데이터베이스의 스키마를 변경하는 migration은 실제 서비스를 운영할 때 중요한 주제이다. sequelize-cli 명령어들을 잘 공부함으로써 효율적으로 작업을 할 수 있도록 하자.
npx sequelize --help
을 실행하면 어떤 명령어들이 있는지 한 눈에 볼 수 있고
또 각각의 명령 뒤에 --help 옵션을 붙여서 실행하면 각 명령에 어떤 옵션들을 쓸 수 있는지 확인할 수 있으니까 꼭 한번씩 확인해보길 바란다.