짱짱해커가 되고 싶은 나

sequelize 본문

Web/Node.js

sequelize

동로시 2020. 11. 22. 03:54

sequelize: 노드에서 MySQL 작업을 쉽게 할 수 있도록 도와주는 라이브러리, ORM(object-relational mapping)으로 자바스크립트 객체와 데이터베이스의 릴레이션을 매핑해주는 도구이다.

 

시퀄라이즈는 자바스크립트 구문을 알아서 SQL로 바꿔주기 때문에 SQL언어를 직접 사용하지 않고 js만으로 MySQL을 조작할 수 있기 때문에 편리하다.

 

sequelize와 mysql2를 설치하고 sequelize init을 하면 config, models, migrations, seeders 폴더가 생성된다.

 

1. MySQL 연결하기 - app.js

sequelize를 통해 express 앱과 MySQL을 연결해야 한다.

따라서 다음과 같이 app.js를 수정한다.

var sequelize = require('./models').sequelize;

var app = express();
sequelize.sync();

 

2. 모델 정의하기 - models 폴더

MySQL에 정의한 테이블을 시퀄라이즈에서도 정의해야한다.

MySQL의 테이블은 시퀄라이즈의 모델에 대응된다.

시퀄라이즈는 모델과 MySQL의 테이블을 연결해주는 역할을 한다.

 

만들고자 하는 DB는 users와 comments다.

users와 comment의 column은 다음과 같다.

comments 테이블에서 commenter로는 외래키로 users의 id를 저장할 것이다.

id name age married comment created_at
id commenter comment created_at

 

이런 형태의 DB를 models폴더에서 sequelize로 정의해보겠다. (models/user.js)

module.exports = (sequelize, DataTypes) =>{
    return sequelize.define('user', {
        name: {
            type: DataTypes.STRING(20),
            allowNull: false,
            unique: true,
        },
        age: {
            type: DataTypes.INTEGR.UNSIGNED,
            allowNull: false,
        },
        married: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
        },
        comment: {
            type: DataTypes.TEXT,
            allowNull: ture,
        },
        created_at: {
            type: DataTypes.DATE,
            allowNull: false,
            defaultValue: DataTypes.NOW,
        },
    }, {
        timestamps: false,
    });
};

일반적으로 시퀄라이즈의 모델의 이름은 단수형으로 지정하고 MySQL의 테이블은 복수형으로 지정한다.

시퀄라이즈는 자동으로 id를 기본 키로 연결하기 때문에 id컬럼을 작성하지 않아도 된다.

모델의 칼럼은 sequelize.define()를 이용해 정의할 수 있다. 

시퀄라이즈의 데이터 타입은 MySQL의 자료형과 조금 다르다. 

STRING : VARCHAR, INTEGER: INT, BOOLEAN: TINYINT, DATE: DATETIME

allowNull: NOT NULL, unique: UNIQUE

defalutValue: DEFAULT 로 대응된다.

 

define()의 3번째 인자는 테이블의 옵션이다. 현재 테이블의 옵션으로 timestamps가 들어가 있다. 

만약에 timestamps 속성이 true라면 자동으로 createdAt과 updatedAt 컬럼을 추가한다. 

우리는 DB column에 created_at 컬럼을 만들어서 이 부분이 필요없기 때문에 false로 했다.

 

이 외에도 자주 사용하는 테이블 옵션으로는 paranoid, underscored, tableName 옵션이 있다.

보통 timestamps: true와 paranoid: ture를 자주 사용한다.

paranoid를  true로 설정하면 deletedAt이라는 컬럼이 추가된다.

로우를 삭제하는 명령을 실행했을 때 로우를 제거하는 대신 deletedAt에 제거된 날짜를 입력하는 것이다.

그래서 일반적으로 로우를 조회하면 deletedAt의 값이 null인 로우를 조회한다.(삭제가 안 된 로우)

이렇게 완전히 삭제하지 않는 이유는 데이터를 복구를 고려했기 때문이다.

 

underscored 옵션은 createdAt, updatedAt, deletedAt 컬럼과 시퀄라이즈가 자동으로 생성해주는 관계 컬럼의 이름을 스네이크케이스 형식으로 바꿔준다. (스네이크케이스 : 대문자 대신_사용)

tableName 옵션은 테이블 이름을 다른 것으로 설정하고 싶을 때 사용한다. 시퀄라이즈는 자동으로 define메소드의 첫 번째 인자를 복수형으로 만들어 테이블 이름으로 사용하는데 이 옵션을 사용하면 해당 값으로 테이블 이름을 만들 수 있다.

 

이제 comment모델도 만들어보자. (models/comment.js)

module.exports = (sequelize, DataTypes) =>{
    return sequelize.define('comment', {
        comment: {
            type: DataTypes.STRING(1000),
            allowNull: false,
        },
        created_at: {
            type: DataTypes.DATE,
            allowNull: true,
            defaultValue: DataTypes.NOW,
        },
    }, {
        timestamps: false,
    });
};

 

이렇게 모델을 생성하면 다음과 같이 models/index.js와 연결한다.

db.User = require('./user')(sequelize, Sequelize);
db.Comment = require('./comment')(sequelize, Sequelize);

 

그리고 config/config.json을 수정한다.

{
    "development": {
        "username": "root",
        "password": "[root 비밀번호]",
        "database": "nodejs",
        "host": "127.0.0.1",
        "dialect": "mysql",
        "operatorsAliases": false
    },
}

development의 password는 실제 내가 MySQL에서 사용하는 비밀번호를 입력하고, database를 현재 MySQL 커넥션과 일치하게 수정하면 된다. operatorsAliases는 보안에 취약한 연산자를 사용할지의 여부를 설정하는 옵션이다.

이 부분은 process.env.NODE_ENV === development일 때 적용이 된다. 따라서 상황에 맞는 속성을 수정하면 된다.

 

3. 관계 정의하기

user모델과 comment모델의 관계를 생각해보자.

한 사람은 여러 댓글을 달 수 있지만 한 댓글의 사용자가 여러 명일 수는 없다.

따라서 user: comment = 1: N 관계다.

 

우리가 구상한 DB comments 테이블에서는 commenter이라는 컬럼에 users 테이블의 id를 외래키를 가지고 와서 사용하기로 했다.  models/index.js에서 이 부분을 추가해 보겠다.

const path = require('path');
const Sequelize = require('seqeulize');

const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, '..' , 'config', 'config.json'))[env];
const db = {};

const sequelize = new Sequelize(config.database, config.username, config.password, config);

db.sequelize = sequelize;
db.Sequelize = Sequelize;

db.User = require('./user')(sequelize, Sequelize);
db.Comment = require('./comment')(sequelize, Sequelize);

db.User.hasMany(db.Comment, {foreignKey: 'commenter', sourceKey: 'id'});
db.Comment.belongsTo(db.User, {foreignKey: 'commenter', targetKey: 'id'});

module.exports = db;

hasMany(1->N)를 이용해 user모델의 id를 comment 모델에 commenter라는 외래키를 사용한다.

belongsTo(N->1)를 이용해 comment모델에 user의 id를 가지고 commenter라는 외래키를 사용한다고 정의한다.

 

만약 1:1 관계일 경우에는 hasMany대신 hasOne()을 사용한다.

 

그리고 N:M 관계라면 모두 belongsToMany()를 사용한다.

예를 들어 게시글 정보를 담고 있는 Post모델과 해시태그 정보를 담고 있는 Hashtag 모델이 있다고 하면 게시글 하나에 해시태그가 여러개일 수 있고, 하나의 해시태그에도 여러개의 게시물이 존재하므로 N:M 관계다.

이럴 경우 다음과 같이 정의할 수 있다. 

db.Post.belongsToMany(db.Hashtag, {through: 'PostHashtag'});
db.Hashtag.belongsToMany(db.Post, {through: 'PostHashtag'});

N:M 관계 특성상 새로운 모델이 새로운 모델이 생성된다. through 속성에 그 이름을 적어주면 된다.

그러면 새롭게 생긴 PostHashtag에는 각각 대응되는 Post와 Hashtag 테이블이 생성되는 것이다.

 

그래서 N:M 관계에서 데이터를 조회할 때는 여러 단계를 거친다. 해시태그를 사용해서 게시물을 조회한다면 Hashtag 모델에서 조회하고 해당 Hashtag  id를 바탕으로 PostHashtag 모델에서 해당하는 id인 postid를 찾아서 post모델에서 정보를 갖고 오는 것이다.

 

시퀄라이즈에서는 이 과정을 좀 더 편하게 사용할 수 있도로 몇 개의 메소드를 지원한다.

async(req, res, next) => {
	const tag = await Hashtag.findOne({where: {title: '노드'}});
	const posts = await tag.getPosts();
};

getPosts()는 get+모델이름의 복수형이다.

 

이 외에도 addPosts()도 있다. add + 모델 이름의 복수형

이 함수를 이용해 두 테이블 간의 N:M 관계를 추가해 줄 수 있다.

 

4. 쿼리 작성하기

INSERT INTO nodejs.users (name, age, married, comment) VALUES ('zero', 24, 0, '자기소개1');
const {User} = require('../models')
User.create({
	name: 'zero',
	age: 24,
	married: false,
	comment: '자기소개1',
});

 

SELECT * FROM nodejs.users;
User.findAll({});

 

SELECT name, married FROM nodejs.users;
User.findAll({
	attributes: ['name', 'married'],
});

 

SELECT * FROM nodejs.users LIMIT 1;
User.findOne({});
User.findAll({
	limit: 1,
});

 

SELECT name, age FROM nodejs.users WHERE married=1 AND age>30;
const{User, Sequelize: {Op}} = require('../models');
User.findAll({
	attributes: ['name', 'married'],
	where: {
		married: 1,
		age: {[Op.gt]:30},
    },
});

시퀄라이즈는 js 객체를 사용해서 쿼리를 생성해야 하기 때문에 Seqeulize 객체 내부의 Op 객체를 불러와서 사용한다.

 

SELECT id, name FROM users WHERE married=0 OR age>30;
const {User, Sequelize: {Op}} = require('../models');
User.findAll({
	attributes: ['id', 'name'],
	where: {
		[Op.or]: [{maried:0}, {age: {[Op.gt]:30}}],
	},
});

Op.or 속성은 OR연산을 할 쿼리들을 배열로 나타내면 된다.

 

SELECT id, name FROM users ORDER BY age DESC;
User.findAll({
	attributes: ['id', 'name'],
	order: [['age', 'DESC']],
});

 

UPDATE nodejs.users SET comment='바꿀 내용' WHERE id=2;
User.update({
	comment: '바꿀 내용',
}, {
	where: {id:2},
});

 

DELETE FROM nodejs.users WHERE id=2;
User.destory({
	where: {id:2},
});

'Web > Node.js' 카테고리의 다른 글

사용자와 댓글 관리 서버 - 2  (0) 2020.12.05
사용자와 댓글 관리 서버 - 1  (0) 2020.12.04
[http 모듈] 쿠키  (0) 2020.10.25
Node.js 훑어보기  (1) 2020.08.07
Comments