58일차 (03-03-2021)

조상래·2021년 3월 3일
0

코드스테이츠

목록 보기
56/73

sequelize 스프린트 2일차이다. 테스트는 어제 다 통과 하였고, 남겨진 advanced 과제를 하였다.

내용은,

  1. users 라는 새 테이블을 만든다.
  2. migration_skeleton을 만들어 urls 테이블에 userId 필드를 추가한 후 userId를 외래키로 users테이블의 id를 참조하게 한다.
  3. models에서 association을 이용해 hasMany, belongsTo 메소드를 이용해 1:N 관계를 설정해준다.

순서대로 작성해보겠다.

1. users 테이블 만들기

먼저 모델을 만들어 보자.

$ npx sequelize-cli model:generate --name user --attribute name: string, age: integer, email: string

user라는 모델을 만들고 필드는 임의로 설정하였다. 그리고 나는 자동으로 만들어지는 createdAt/ updatedAt을 사용하지 않고 임의로 created_at이라는 필드를 만들어 주고싶다. 원래 있는 createdAt을 쓰면되지 않냐고 하는데 기본값이 datetime이라 시간이 이상하게 나와서 바꾸었다.

models 디렉토리에 user 파일에서 손을 바줘야할 부분이 있다.

'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  // 생략
  };
  user.init({
    name: DataTypes.STRING,
    age: DataTypes.INTEGER,
    email: DataTypes.STRING,
    created_at: DataTypes.DATE
  }, {
    sequelize,
    modelName: 'user',
    underscored: true, // 자동으로 생성되는 필드를 snake_case로 바꿔준다.
    timestamps: false // false-기본적으로 만들어지는 created updated at 무시가능
  });
  return user;
};

timestamps라는 옵션은 true로 하면 model을 사용하여 데이터베이스를 조회하거나 업데이트 할 때 자동으로 createdAt, updatedAt을 고려해주는데 난 저것을 쓰지 않을 것이기 때문에 false로 두었다 그렇게하여야 나중에 쿼리문 메소드를 쓸 때 오류가 나지 않는다.

만약 true로 할 경우,
테이블의 구조와 쿼리문의 필드 구조가 맞지 않아 오류를 내는걸 볼 수 있다.

그리고 만약 createAt 과 updateAt을 snake_case로 사용할거면 underscored를 true로 해줘야한다. 나는 별도로 새로운 필드를 만들었고 timestamps를 false로 했기 때문에 필요없는 옵션이다.

underscored옵션이 없는 경우,
기본적으로 camel_case로 조회한다.

그리고 마이그레이션을 손보면 된다.

'use strict';
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      name: {
        type: Sequelize.STRING
      },
      age: {
        type: Sequelize.INTEGER
      },
      email: {
        type: Sequelize.STRING
      },
      created_at: {
        allowNull: false,
        type: 'TIMESTAMP',
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      },
    });
  },
  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable('users');
  }
};

그리고 명령어를 사용하여 migrate를 해주자.

$ npx sequelize-cli db:migrate

그리고 mysql에 접속하여 테이블이 잘 등록되었는지 확인하면된다.

2. urls 테이블에 userId 필드를 추가/ 외래키 설정

sequelize의 좋은 기능 중 하나는 migration 파일을 이용하여 테이블의 구조를 쉽게 바꿀수 있고, 또 독립적으로 관리 할 수 있다는 것이다.

먼저 migration-skeleton이라는 migration 파일을 새로 만들어 주자.

$ npx sequelize-cli migraion:generate --name migration-skeleton

이름엔 크게 의미는 없다. 다른 이름으로 해도 상관없다. migration:generate 명령어를 사용하면 기본 마이그레션 폼을 제공해준다.

만들어진 폼에 메소드를 이용하여 추가하면 된다.

'use strict';

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.addColumn('urls', 'userId', {
      type: Sequelize.INTEGER,
      references: {model: 'users', key: 'id'}
    })
  },

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

up 엔 추가하는 메소드를 이용하여 userId 필드를 추가하고 references를 이용하여 외래키로 만들어주면된다.

down엔 필드를 삭제하는 메소드를 쓰면 undo를 했을 때 이전 상태로 돌아가 문제가 없을 것이다.

그리고 migrate 명령어를 써서 추가해보자. (명령어 생략)

mysql을 통해 확인을 하면 되는데 터미널 보다 GUI를 이용해 관계도를 보면 더욱 확실하게 확인할 수 있다.

3. Association

다시 한번 말하자면 쿼리문을 적용할 때 실직적으로 이용되는 부분은 model이라고 하였다. model을 바꿔주지 않으면 분명 생각한대로 데이터베이스를 이용할 수가 없을것이다.

먼저 해줘야할 것은 url 모델에 userId를 추가해주는 것.

//생략
  };
  url.init({
    url: DataTypes.STRING,
    title: DataTypes.STRING,
    visits: {
      type: DataTypes.INTEGER,
      defaultValue: 0
    },
    userId: DataTypes.INTEGER // userId 추가.
  }, {
    sequelize,
    modelName: 'url',
  });
  return url;
};

그러면 다음 model을 이용할 때 자동적으로 userId가 고려될 것이다.

그리고 다음은 users.id : urls.userid 를 1 : N 관계로 설정해 주어야 한다.

// url Model.
'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class url extends Model {
    // associate를 수정
    static associate(models) {
      this.belongsTo(models.user, {
        onDelete: 'cascade',
      })
    }
  };
// 생략
};
// user Model.
'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class user extends Model {

    static associate(models) {
      this.hasMany(models.url, {
        foreignKey: 'userId'
      })
    }
  };
// 생략
};

위와같이 N의 위치에 있는 모델을 belongTo로 1의 위치에 있는 모델을 hasMany로 두면된다.

이렇게 설정해 주어야 쿼리문을 이용할 때 자동으로 외래키의 관계를 정리한다. 예를 들어 만약 users에 없는 id를 urls의 userId에 넣는다고 한다면 오류가 날 것이다.

그리고 중간에 onDelete라는 키워드를 볼 수 있는데 나도 정확히 저게 무슨 작동을 하는지 모르겠다. 다만, 'cascade' 라는 키워드를 봤을 때 종속관계에 있는 키가 삭제되면, 같이 제거가 되지 않을까 라는 추측만 하고있다. 예를 들면 id가 삭제되면 userId에 있는 동일한 키가 제거 될 것이라고 생각한다.

sequelize를 써보면서 뭔가 복잡 하다는 생각이 많이 들었다. 그리고 효율성 부분에서도 크게 좋다거나 그런 것을 느끼지 못했다. 오히려 신경 써줘야할 것이 많아 조금 까다롭게도 느껴졌다. 조금 간단하게 써서 그런가 싶기도하다. 만약 굉장히 긴 join체인을 대신할 때 편하다거나 그런 부분이 있지 않을까 생각한다.

profile
Codestates Full IM26기 수료

0개의 댓글