엘리스 AI트랙 4기/프로젝트

[TypeScript Express] Sequelize로 MySQL 기본 모델 생성하기

남쪽마을밤송이 2022. 6. 14. 19:59

관련글 : [Typescript Express] Sequelize로 MySQL 연결하기

 

[Typescript Express] Sequelize로 MySQL 연결하기

 [01 Typescript란? ] TypeScript는 JavaScript 기반의 언어이다. JavaScript는 클라이언트 측 스크립팅 언어지만 TypeScript는 객체 지향 컴파일 언어이다. TypeScript는 JavaScript의 상위 집합으로 JavaScrip..

s0n9h2.tistory.com

 

 [01 model 생성하기 ] 

  • MongoDB와의 차이점은 일단 schema 폴더를 사용하지 않고 model을 정의하는 파일에서 생성도 같이하는게 더 편하다는 것이었다.
  • 그런데 이제 이게... 진짜 처음에 감도 못잡아서 많이 헤멘 부분인데 이 블로그 공식문서가 정말 도움이 되었다

 (1) user.ts 파일 

  • 지금 구현하는 User 모델의 ERD 설계는 다음과 같다.  model/user.ts  파일에 작성한다.


  • 먼저 sequelize에서 DataTypes와 Model을 import하고 만들어 둔 sequelize 연결 정보를 불러온다.
  • 그리고 Users 모델에 들어갈 컬럼들을 형식에 맞게, nullable을 확인하고 interface로 정의한다.
  • 생성한 interface에 맞는 Model 객체를 상속하는 Users 클래스를 만든다.
import { DataTypes, Model } from "sequelize";
import sequelize from "./index";

// These are all the attributes in the User model
interface UsersAttributes {
  pk_user_id: string;
  user_name: string;
  email: string;
  password: string;
  gender?: string;
  age_range?: string;
  job?: string;
}

export class Users extends Model<UsersAttributes> {
  pk_user_id: string;
  user_name: string;
  email: string;
  password: string;
  gender?: string;
  age_range?: string;
  job?: string;
}
  • 윗 부분은 TypeScript에서 접근할 모델을 정의한 느낌이라면 그 아래는 실제 DB에 들어갈 타입과 옵션을 정해 모델을 생성해준다.
    • 처음에 init과 define의 차이가 헷갈렸는데 결국 똑같은 기능을 한다고 한다. (define이 init을 호출하는 구조)
Users.init(
  {
    pk_user_id: {
      type: DataTypes.UUID,
      defaultValue: DataTypes.UUIDV4,
      primaryKey: true,
    },
    user_name: {
      type: DataTypes.STRING(20),
      allowNull: false,
    },
    email: {
      type: DataTypes.STRING(20),
      allowNull: false,
      unique: true,
    },
    password: {
      type: DataTypes.STRING(70),
      allowNull: false, // Oauth 로그인은 비번 필요없으니 Nullable하게 하기도 함
    },
    gender: {
      type: DataTypes.ENUM("F", "M"),
      allowNull: true,
    },
    age_range: {
      type: DataTypes.STRING(10),
      allowNull: true,
    },
    job: {
      type: DataTypes.STRING(20),
      allowNull: true,
    },
  },
  {
    sequelize, // 꼭 넣어줘야 함
    modelName: "Users",
    tableName: "tb_user",
    freezeTableName: true, // 테이블명 변경 불가
    timestamps: true, // create_at, updated_at 컬럼 생성
    paranoid: true, // deleted_at 컬럼 생성, soft delete 시 나중에 복구 가능
    underscored: true, // 위 세 가지 타임스탬프의 컬럼명 표기법 설정, true로 하면 snake case / false면 camel case
  },
);
  • 주의할 점은  unique = true  옵션을 잘못 설정했다가는  SequelizeUniqueConstraintError 가 날 수 있으므로 조심한다.
    • (당연한 말이지만) 위의 email처럼 테이블 전체에서 고유한 값일 경우에만 설정한다.
    • 1:n, n:m 관계에서는 설정에 신중하거나 아래의 indexes unique = true로 여러 fields를 사용하자.
  • 그리고 Users 모델에서는 따로 설정하지 않았지만 추가로  indexes  옵션이 있다.
💡 여기서 잠깐, 인덱스란? 💡
- 인덱스 설정은 insert, update, delete의 성능을 희생하고 대신 select의 성능을 향상시킨다.
- 따라서 인덱스가 없는 컬럼을 조건으로 update, delete를 하게 되면 굉장히 느려 많은 양의 데이터를 삭제해야 하는 상황에선 인덱스로 지정된 컬럼을 기준으로 진행하는 것이 빠르다.
- 또한 인덱스를 설정할 때는 중복도가 낮은(카디널리티가 높은) 컬럼을 선택하는 것이 좋다.
- 마지막으로 여러 컬럼으로 인덱스를 설정했을 시 field에서 첫 번째 컬럼이 포함돼야 인덱스를 타고 쿼리를 조회한다.
- 이 블로그에 엄청 자세하게 나와있어서 읽어보면 도움이 될 것이다.

 

  • 기본적으로 Sequelize를 사용해서 테이블을 생성하면 primary key, foreign key 혹은 unique = true 설정이 되어 있는 컬럼에 대해서는 indexes가 자동으로 설정되었다.
  • 이외의 컬럼 중 자주 조회하거나 자주 같이 조회하는 조합이 있다면, 다음과 같이 indexes 옵션을 사용해서 인덱스 설정을 추가한다.
    • name은 인덱스 이름, field는 인덱스를 걸 컬럼들을 의미하고, 여기에는 없지만 unique key로 사용하고 싶다면 unique = true 옵션을 줄 수 있다.
{
    sequelize,
    modelName: "Schedules",
    tableName: "tb_schedule",
    freezeTableName: true,
    timestamps: true,
    paranoid: true,
    underscored: true,
    indexes: [
      { name: "IDX_type", using: "BTREE", fields: ["type"] },
      { name: "IDX_start_finish", using: "BTREE", fields: ["start", "finish"] },
    ],
  },

 (2) 1.create-table-user.ts 파일 

  • 정의한 모델을 테이블로 만들기 위해서는  migration 이라는 작업을 거쳐 DB와 sync를 맞춰주어야 한다.
  • 따라서  db/migrations/1.create-table-user.ts  와 같이 파일을 생성하고 다음 코드를 넣어준다.
    •  이 때 처음 테이블을 생성하는 경우라면 상관 없지만 데이터가 쌓이고 나서 테이블 설정을 변경하는 경우에 기존의 table을 Drop하고 새롭게 만들고 싶다면  sync( )  안에  force: true  옵션을 넣어주고, 그렇지 않다면 반드시 빼줘야 한다.
import { Users } from "../models/user";

console.log("======Create user Table======");
const create_table_user = async () => {
  // model 설정 변경만 반영하고 싶을 때
  await Users.sync()
  // 기존 테이블 Drop 후 새롭게 만들 때
  // await Users.sync({ force: true })
    .then(() => {
      console.log("✅Success Create user Table");
    })
    .catch((err: Error) => {
      console.log("❗️Error in Create user Table : ", err);
    });
};

create_table_user();
  • 그리고 powershell이나 cmd 등의 터미널 창에서 다음 명령어를 실행해준다.
./node_modules/.bin/ts-node .\src\db\migrations\1.create-table-user.ts

 

  • 이 때 명령어가 실행되지 않는다면 VSC를 관리자 권한으로 실행하고 터미널에 다음 명령어를 순서대로 입력하고 다시 시도한다. (참고)
Get-ExecutionPolicy
Set-ExecutionPolicy RemoteSigned

 

  • 정상적으로 실행되었다면 Workbench, MySQL-CLI 등에서 확인해볼 수 있다.