Sequelize query 기본 정리

Hyunwoo Seo·2022년 9월 11일
1

Sequelize

목록 보기
3/3
post-thumbnail

CRUD 작업을 하기 위해선 먼저 시퀄라이즈 쿼리를 알아야한다.

SQL문을 자바스크립트로 생성하는 것이기 때문에, 시퀄라이즈의 방식을 사용해야 한다.

쿼리는 프로미스를 반환하므로, then을 붙여 결괏값을 받을 수 있다.

또는 async/await 문법과 함께 사용할 수도 있다.

1. 로우 생성 쿼리 (Create)

먼저 로우를 생성하는 쿼리를 알아보자. 다음과 같은 시퀄라이즈 쿼리를 작성하면 로우를 생성할 수 있다. 모델을 불러와 create 메서드를 사용하여 로우를 생성한다.

const { User } = require('./models');

User.create({
    name: 'beom seok',
    age: 23,
    married: false,
    comment: '안녕하세요.'
});

위 쿼리는 아래 SQL문과 같다.

INSERT INTO nodejs.users (name, age, married, comment) VALUES ('beom seok', 23, 0, '안녕하세요.');

주의해야할 점은 데이터를 넣을 때 MySQL의 자료형이 아닌 시퀄라이즈 모델에 정의한 자료형대로 넣어야한다.

가령 married의 경우 실제 MySQL에는 TINYINT로 되어 이전엔 0을 넣어주었었지만, 현재는 BOOLEAN으로 정의돼있으므로 false를 넣어주어야 한다.

사료형 또는 옵션에 부합하지 않는 데이터를 넣을 경우 시퀄라이즈가 에러를 발생시키며, 시퀄라이즈가 알아서 MySQL 자료형으로 바꿔주니 걱정말자.

2. 로우 조회 쿼리 (Read)

2.1. findAll

모든 데이터를 조회하고 싶으면 findAll 메서드를 사용한다.

User.findAll({});

위 쿼리는 아래 SQL문과 같다.

SELECT * FROM nodejs.users;

2.2. findOne

테이블의 데이터를 하나만 가져온다.

User.findOne({});

위 쿼리는 아래 SQL문과 같다.

SELECT * FROM nodejs.users LIMIT 1;

2.3. attributes, where

attributes 옵션을 사용하여 원하는 컬럼만 가져올 수도 있다.

또한, where 옵션으로 조건들을 나열할 수도 있다. where 옵션은 기본적으로 AND 옵션과 같다.

const { User } = require('./models');
const { Op } = require('sequelize');

User.findAll({
    attributes: ['name', 'age'],
    where: {
        married: 1,
        age: { [Op.gt]: 30 },
    },
});

위 쿼리는 아래 SQL문과 같다.

SELECT name, age FROM nodejs.users WHERE married = 1 AND age > 30;

비교 구문이 조금 특이한데, 시퀄라이즈는 자바스크립트 객체를 사용하여 쿼리를 생성하기 때문에 특수한 연산자들이 사용된다.

자주 쓰이는 연산자로는 Op.gt (greater than, 초과), Op.gte (greater than or equal to, 이상), Op.lt (less than, 미만), Op.lte (less than or equal to, 이하), Op.ne (not equal, 같지 않음), Op.or (or, 또는), Op.in (in, 배열 요소 중 하나), Op.notIn (not in, 배열 요소와 모두 다름) 등이 있다.

Op.or은 다음과 같이 배열 내에 적용할 쿼리들을 나열하여 사용한다.

User.findAll({
    attributes: ['name', 'age'],
    where: {
        [Op.or]: [
            { married: 0 },
            { age: { [Op.gt]: 30 } }
        ],
    },
});

위 쿼리는 아래 SQL문과 같다.

SELECT name, age FROM nodejs.users WHERE married = 1 OR age > 30;

2.4. order

정렬은 order 옵션으로 처리한다. 배열 안에 배열이 있다는 점에 주의하자.

이는 정렬은 꼭 컬럼 하나가 아닌 두 개 이상으로도 할 수 있기 때문이다.

User.findAll({
    attributes: ['name', 'age'],
    order: [['age', 'DESC']]
});

위 쿼리는 아래 SQL문과 같다.

SELECT id, name FROM users ORDER BY age DESC;

2.5. limit, offset

조회할 로우 개수는 limit으로, 스킵할 로우 개수는 offset으로 할 수 있다.

User.findAll({
    attributes: ['name', 'age'],
    order: [['age', 'DESC']],
    limit: 1,
    offset: 1,
});

위 쿼리는 아래 SQL문과 같다.

SELECT id, name FROM users ORDER BY age DESC LIMIT 1 OFFSET 1;

limit이 1이라면 findAll 대신 findOne을 사용할 수 있다.

3. 로우 수정 쿼리 (Update)

update 메서드로 수정할 수도 있다.

첫 번째 인수는 수정할 내용이고, 두 번째 인수는 어떤 로우를 수정할지에 대한 조건이다.

where 옵션에 조건들을 적는다.

User.update({
    comment: '새로운 코멘트.',
}, {
    where: { id: 2 },
});

위 쿼리는 아래 SQL문과 같다.

UPDATE users SET comment = '새로운 코멘트.' WHERE id = 2;

4. 로우 삭제 쿼리 (Delete)

로우 삭제는 destroy 메서드로 삭제한다. where 옵션에 조건을 적는다.

User.destroy({
    where: { id: 2 },
});

위 쿼리는 아래 SQL문과 같다.

DELETE FROM users WHERE id = 2;

5. 관계 쿼리

findOne이나 findAll 메서드를 호출할 때 프로미스의 결과로 모델을 반환한다 (findAll은 모델의 배열을 반환한다).

const user = await User.findOne({});
console.log(user.name);

User 모델의 정보에도 바로 접근할 수 있겠지만, 더 편리한 점은 관계 쿼리를 지원한다는 것이다. MySQL로 따지면 JOIN 기능이다.

현재 User 모델은 Commenter 모델과 hasMany-belongsTo 관계가 맺어져 있으며, 만약 특정 사용자를 가져오면서 그 사람의 댓글까지 모두 가져오고 싶다면 include 속성을 사용한다.

const user = await User.findOne({
    include: [{
        model: Comment,
    }]
});
console.log(user.Comments);

관계가 있는 모델을 include 배열에 넣어주면 된다.

배열인 이유는 다양한 모델과 관계가 있을 수 있기 때문이다.

댓글은 여러 개일 수 있으므로 (hasMany) user.Comments로 접근 가능하다.

또는 다음과 같이 댓글에 접근할 수도 있다.

const user = await User.findOne({});
const comments = await user.getComments();
console.log(comments);

관계를 설정했다면, getComments (조회), setComments (수정), addComment (하나 생성), addComments (여러 개 생성), removeComments (삭제) 메서드를 지원한다. 동사 뒤에 모델의 이름이 붙는 형식으로 생성된다.

동사 뒤의 모델 이름을 바꾸고 싶다면 관계 설정 시 as 옵션을 사용한다 (아래는 user.js 모델 파일)

    ...
    static associate(db) {
        db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id', as: 'Answers' });
    }

위와 같이 관계를 설정했다면 댓글 객체도 user.Answers로 바뀌며, 쿼리 메서드들 또한 getAnswers 등으로 변한다.

include나 관계 쿼리 메서드에도 where나 attributes 같은 옵션들을 사용할 수 있다.

아래는 id가 1인 댓글만 가져오고, 그 중에서도 id 컬럼만 가져오도록 하고 있다.

const user = await User.findOne({
    include: [{
        model: Comment,
        where: {
            id: 1,
        },
        attributes: ['id'],
    }]
});

// 또는

const comments = await user.getComments({
    where: {
    	id: 1,
    },
    attributes: ['id'],
});

조회는 위와 같이 하지만, 수정, 생성, 삭제 때는 조금 다른 점이 있다.

// 생성
const user = await User.findOne({});
const comment = await Comment.create();
await user.addComment(comment);
// 또는
await user.addComment(comment.id);

// 여러 개 생성
const comment1 = await Comment.create();
const comment2 = await Comment.create();
await user.addComment([comment1, comment2]);

관계 쿼리 메서드의 인수로 추가할 댓글 모델을 넣거나 댓글의 아이디를 넣으면 된다. 수정이나 삭제 또한 마찬가지이다.

6. SQL 쿼리 그대로 사용하기

시퀄라이즈의 쿼리를 사용하기 싫거나 헷갈린다면 직접 SQL문을 통해 쿼리할 수도 있다.

const [result, metadata] = await sequelize.query('SELECT * FROM comments');

0개의 댓글