Sequelize 튜토리얼(4)_코드로 테이블 생성

차분한열정·2021년 4월 7일
0

Sequelize 튜토리얼

목록 보기
4/14

models/index.js 파일이 공개하는 db 객체를 한번 사용해보자. sequelize_sample 디렉토리 안에 app.js라는 파일을 만들고 아래와 같은 코드를 써보자.

// app.js
const db = require('./models/index.js');

const sequelize = db.sequelize;

(async () => {
 await sequelize.sync();
})();

지금 db 객체의 sequelize 객체는 각 모델 클래스를 생성할 때 옵션에 들어갔던 그 sequelize 객체이다. 바로 이 sequelize 객체의 sync라는 메소드를 호출하면 각 모델에 대응되는 테이블이 실제로 데이터베이스에도 생성이 된다. 말그대로 데이터베이스에도 싱크를 맞춘다는 뜻이다. 그리고 sync 메소드는 Promise 객체를 리턴하는 비동기 메소드이기 때문에 그 앞에 await을 붙여주었다. sequelize를 사용할 때는 거의 대부분의 메소드들이 이렇게 Promise 객체를 리턴하는 메소드들이기 때문에 그 앞에 await을 붙여줘야한다는 점을 잘 기억해야 한다. 이 코드를 실행해보면

node app.js

아래와 같이 생긴 SQL 문들이 출력되는 것을 알 수 있다.

자세히 읽어보면 결국 모두 테이블을 생성하는 SQL 문인 것을 알 수 있다. 우리는 단지 모델들을 정의하고 sequelize 객체의 sync 메소드를 호출했을 뿐인데 sequelize 객체가 알아서 실제 데이터베이스에 테이블들을 생성해주는 것이다. Workbench로 확인해보면

테이블들이 잘 생긴 것을 알 수 있다. 음, 그런데 뭔가 이상하다. 지금 테이블 이름들은 모두 복수형이다. 모델 이름들은 모두 단수형인데 테이블 이름들은 모두 끝에 s가 붙어있는 것을 알 수 있다. 이것은 sequelize 객체가 자동으로 이렇게 처리하는 것인데 옵션으로 이 동작을 막을 수는 있다. Student.js 파일을 예로 들면

// student.js
~~~
Student.init(
    {
      registrationNum: DataTypes.STRING,
      name: DataTypes.STRING,
      age: DataTypes.INTEGER,
    },
    {
      sequelize,
      modelName: 'Student',
      freezeTableName: true,  // 
    }
  );
  return Student;
~~~

freezeTableName이라는 옵션에 true 값을 주면 된다. 말 그대로 sequelize 보고 굳이 단수형 단어를 복수형으로 바꾸지 말고 모델 이름 그대로 사용하라는 뜻이다. 혹시 이렇게 할 사람은 생성된 테이블들을 다 삭제하고 다시 코드를 실행하길 바란다.

그럼 잠깐 Students 테이블을 보자.

그런데 컬럼들을 보니 이것도 좀 이상하다. 모델의 속성으로는 존재하지 않았던

  • id 컬럼
  • createdAt 컬럼
  • updatedAt 컬럼

이 존재하는 것을 볼 수 있다. 컬럼 구조만 따로 살펴보니

id 컬럼이 지금 Primary Key에 해당한다는 것을 알 수 있다.

바로 이런 점이 ORM을 사용하는 묘미라고 할 수 있는데, 이렇게 sequelize는 모델에서 특정 컬럼을 별도로 Primary Key라고 나타내주지 않으면 Primary Key 역할을 할 수 있는 id 컬럼을 자동으로 추가한다. Primary Key를 별도로 지정하려면 어떻게 해야할까? Student.js의 코드를 이렇게 바꿔주면 된다.

// student.js
~~~
Student.init(
    {
      registrationNum: { 
        primaryKey: true,
        type: DataTypes.STRING,
      }
      name: DataTypes.STRING,
      age: DataTypes.INTEGER,
    },
    {
      sequelize,
      modelName: 'Student',
      freezeTableName: true,  // 
    }
  );
  return Student;
~~~

이렇게 코드를 수정하면 Student 테이블이 생성될 때 id 컬럼은 없고 registrationNum 컬럼이 Primary Key로 설정된다. 만약 직접 Primary Key를 설정하고 싶으면 이 방법을 쓰면 되겠다.

자, 이제 createdAt, updatedAt 컬럼을 생각해보자. 이 컬럼도 id 컬럼처럼 Sequelize가 자동으로 생성하는 컬럼이다. 어떤 row를 추가했을 때(createdAt), 기존 row를 수정했을 때(updatedAt) 해당 컬럼에 값이 채워지는 것이다. 만약 이런 컬럼을 만들고 싶지 않다면

// student.js
~~~
Student.init(
    {
      registrationNum: { 
        primaryKey: true,
        type: DataTypes.STRING,
      }
      name: DataTypes.STRING,
      age: DataTypes.INTEGER,
    },
    {
      sequelize,
      modelName: 'Student',
      timestamps: false  // 
    }
  );
  return Student;
~~~

timestamps라는 옵션의 값을 false로 줘야 한다. 아무 표시도 없으면 timestamps라는 옵션의 값은 true가 기본값이어서 자동으로 createdAt, updatedAt 컬럼이 생성된다.

모델 객체의 init 메소드의 두 번째 객체의 옵션에는 여러 가지 종류가 있다. 하지만 그것을 지금 모두 다룰 수는 없기에 그 부분은 공식 문서의 내용을 참고하길 바란다. 컬럼에 Not null 속성을 설정하는 방법, Unique 인덱스를 설정하는 방법, 물리 삭제 대신 논리 삭제를 할 수 있는 방법(paranoid 옵션) 등이 모두 잘 나와있다. 혹시 필요로 하는 분들이 있다면 이 부분만 따로 정리를 해서 새 글을 쓰겠다.

자, 그런데 한 가지만 기억하고 넘어가자. 지금 app.js 파일을 보면

// app.js
const db = require('./models/index.js');

const sequelize = db.sequelize;

(async () => {
  await sequelize.sync();
})();

sequelize 객체의 sync 메소드를 호출하고 있다. 그런데 이렇게 호출하면 우리가 각 모델 객체의 옵션 등을 바꾸어도 실제 테이블에는 반영되지 않는다. 위의 첫 번째 이미지에서 본 것처럼 'CREATE TABLE IF NOT EXISTS~' 이렇게 기존 테이블이 있으면 건드리지 않기 때문이다. sequelize를 배울 때는 코드를 이렇게 저렇게 바꿔가며 실제 테이블에 반영되는 모습을 보는 것이 중요한데 이것을 가능하게 하려면 sync 메소드에 옵션 객체를 전달하면 된다.

// app.js
const db = require('./models/index.js');

const sequelize = db.sequelize;

(async () => {
  await sequelize.sync({ force: true });
})();

이렇게 { force: true } 라는 옵션을 주게 되면 기존 테이블이 있어도 그것을 날리고 다시 새 코드에 맞게 테이블을 생성한다. 물론 실제 서비스에서는 절대 사용하면 안 되는 옵션이지만, 연습을 할 때는 이 옵션을 유용하게 사용하면 좋다.

그리고 참고로 각 모델별로 하나씩 테이블을 별도로 생성하는 것도 가능하다.

// app.js

const db = require('./models/index.js');

const sequelize = db.sequelize;
const Student = db.Student;
const ScholarshipAccount = db.ScholarshipAccount;
const Course = db.Course;
const Professor = db.Professor;

(async () => {
  // await sequelize.sync({ force: true });
  await Student.sync();
  await ScholarshipAccount.sync();
  await Course.sync();
  await Professor.sync();
})();

이렇게 db 객체에 있던 각각의 모델들을 하나씩 꺼내고, 각 모델의 sync 메소드를 호출하면 해당 테이블이 생성된다. 모든 모델을 한번에 테이블로 만들 때는 sequelize 객체의 sync 메소드를, 특정 모델만 테이블로 반영하려면 해당 모델의 sync 메소드를 호출하면 되는 것이다.

profile
성장의 기쁨

0개의 댓글