안녕하세요. 휴먼스케이프에서 개발을 하고 있는 권주희(Victoria)입니다.
오늘은 서버개발의 핵심이라고 할 수 있는 ORM(Object Relational Mapping), 그 중에서도 NodeJS에서 사용할 수 있는 ORM Library Sequelize의 파트 7장 Querying에 대해서 정리해보도록 하겠습니다.
(ORM에 대한 간단한 정보를 원하신다면, 이 글을 참조하셔도 좋습니다. 🤔)
링크 : https://victoriagjh.github.io/devKnowledge/2019-10-23-ORM%EC%9D%98%20%EC%9E%A5%EB%8B%A8%EC%A0%90.html
Attributes (특성)
일반적으로 테이블의 열(Column)이라고 생각해도 되며, 열을 구성하는 값들은 같은 도메인(Domain)으로 구성되어 있습니다.
일부 특성을 select하고 싶다면, attributes 옵션을 사용하면 됩니다. 대부분의 경우 배열을 전달합니다.
Model.findAll({
attributes: [‘foo’, ‘bar’]
}); // select foo, bar 와 동일
2. 애트리뷰트 옵션에서 중첩된 배열을 사용할 수 있는데, 중첩된 배열을 사용할 경우 두번째 요소는 as와 같이 사용될 수 있습니다.
attributes:['foo',['bar','baz']] // bar AS baz 와 동일
3. 집계(aggregations)를 하고 싶다면, sequelize.fn()함수를 사용할 수 있습니다.
attributes: [[sequelize.fn('COUNT', sequelize.col('hats')), 'no_hats']] // SELECT COUNT(hats) AS no_hats // instance.get('no_hats'); 를 통해 결과 접근 가능
집계 함수를 사용할 때는, 반드시 해당 함수 결과에 대한 별칭을 부여해야 모델에서 해당 결과에 접근할 수 있습니다.
4. 쿼리 결과에 집계값만 추가하거나 어떤 특성만을 제외하고 싶어서 모든 특성을 일일히 나열하는 것이 귀찮다면, include 또는 exclude 옵션을 이용하세요!
attributes: { include: [[sequelize.fn('COUNT', sequelize.col('hats')), 'no_hats']] }
attributes: { exclude: ['baz'] }
참고로, 모델에서의 include(join)와 달리 attribute 속성으로 사용되는 include의 경우, 어떤 컬럼을 포함할지 제한해주는 옵션으로 exclude와 세트라고 생각하셔도 됩니다.
Where
Sequelize의 Querying에는 where 객체를 사용하여 쿼리 내용을 필터링 할 수 도 있습니다.
where은 “ 애트리뷰트: 값” 의 쌍으로 조건을 달듯이 사용할 수 있고, or과 and 조건을 추가하며 복잡한 쿼리를 만들어낼 수도 있습니다.
const Op = Sequelize.Op;
Post.findAll({ where: { authorId: 12, status: 'active' } }); // SELECT * FROM post WHERE authorId = 12 AND status = 'active';
Post.findAll({ where: { [Op.or]: [{authorId: 12}, {authorId: 13}] } }); // SELECT * FROM post WHERE authorId = 12 OR authorId = 13;
Post.update({ updatedAt: null, }, { where: { deletedAt: { [Op.ne]: null } } }); // UPDATE post SET updatedAt = null WHERE deletedAt NOT NULL; // ne operator는 != 과 동일합니다.
Post.findAll({ where: sequelize.where(sequelize.fn('char_length',sequelize.col('status')), 6) }); // SELECT * FROM post WHERE char_length(status) = 6;
Operator (연산자)
Sequelize는 다양한 연산자를 지원합니다.
기본적인 연산자는 다음과 같고, 더 자세히 알고싶으신 분들은 아래 링크를 참고해주세요.
링크 : https://sequelize.org/v5/manual/querying.html
[Op.and]: [{a: 5}, {b: 6}] // (a = 5) AND (b = 6) [Op.or]: [{a: 5}, {a: 6}] // (a = 5 OR a = 6) [Op.ne]: 20, // != 20 [Op.eq]: 3, // = 3 [Op.is]: null // IS NULL [Op.not]: true, // IS NOT TRUE [Op.gt]: 6, // > 6 [Op.lt]: 10, // < 10
2. Combinations
하나의 애트리뷰트에 여러 연산자로 조건을 달 수도 있고, 하나의 연산자에 여러 애트리뷰트 별로 조건을 달 수도 있습니다.
{ rank: { [Op.or]: { [Op.lt]: 1000, [Op.eq]: null } } } // rank < 1000 OR rank IS NULL
{ [Op.or]: [ { title: { [Op.like]: 'Boat%' } }, { description: { [Op.like]: '%boat%' } } ] } // title LIKE 'Boat%' OR description LIKE '%boat%'
3. Operator Aliases (연산자 별칭)
Sequelize는 연산자에 대하여 별칭을 부여할 수 있게 해줍니다.
const operatorsAliases = { $gt: Op.gt }
const connection = new Sequelize(db, user, pass, { operatorsAliases })
위의 코드와 같이 operatorAliases 변수에 원하는 연산자에 대한 별칭을 넣고, Sequelize 객체를 설정할 때 operatorAliases를 옵션으로 설정하면, 해당 별칭으로 연산자를 사용할 수 있습니다.
4. Operator security (연산자 보안)
위에 소개했던, 연산자 별칭을 사용하지 않으면 Sequelize 보안 향상에 도움이 된다고 합니다.
문자열 별칭이 없으면, 연산자가 SQL에 주입되는 가능성이 극히 적어지지만 그럼에도 불구하고 항상 사용자 입력을 적절히 검증하고 처리해야 합니다.
const connection = new Sequelize(db, user, pass, { operatorsAliases: false }); // Sequelize 객체를 생성할 때, 다음과 같이 operatorsAliases 옵션을 false로 하여 별칭을 사용하지 않을 수 있습니다.
const connection2 = new Sequelize(db, user, pass, { operatorsAliases: { $and: Op.and } }); // 위와 같이 사용할 별칭만을 추가하여 사용할 수 있습니다.
JSON
JSON 데이터 타입은 PostgreSQL, SQLite, MySQL과 MariaDB에서만 지원됩니다.
PostgreSQL에서는 JSON Data가 바이너리 값으로 저장되는 것이 아니라 평문으로 저장됩니다.
JSON 데이터를 단지 저장하거나 반환하고 싶다면 JSON을 사용하는 것을 추천합니다.
반면, JSON 데이터에 대하여 특정 연산을 수행하고 싶다면, JSONB를 사용하는 편이 더 좋습니다.
JSONB
JSONB는 세 가지 방식으로 쿼리할 수 있습니다.
Nested Object
{ meta: { video: { url: { [Op.ne]: null } } } } // 중첩된 방식으로 url이 null이 아닌 JSONB 조회
2. Nested Key
{ 'meta.audio.length': { [Op.gt]: 20 } } // meta.audio.length에 직접 접근하여 조회
3. Containment
{ 'meta': { [Op.contains]: { site: { url: 'http://google.com' } } } } // meta라는 요소에 contain 여부로 조회하기
Relations / Associations
Project.findAll({ include: [{ model: Task, where: { state: Sequelize.col('project.state') } }] }) //Task.state == Project.state가 같은 Project 조회
Pagination / Limiting
Project.findAll({ limit: 10 }) // 10개의 행을 가져온다
Project.findAll({ offset: 8 }) // 8개의 행을 pass한다.
Project.findAll({ offset: 5, limit: 5 }) // 5개 행을 넘어가고, 그 이후로 5개 행을 가져온다
마치면서..
지금까지 Sequelize Part 7. Querying 에 대해서 알아보았습니다.
공식 도큐먼트에 있는 내용을 나름대로 정리하면서 글을 써보았는데, 추가적인 정보나 다른 파트에 대해 궁금하신 분은 아래 링크에서 찾아보실 수 있습니다.
공식 도큐먼트 링크 : https://sequelize.org/v5/index.html
이상으로 읽어주셔서 감사합니다.
Get to know us better! Join our official channels below.
Telegram(EN) : t.me/Humanscape KakaoTalk(KR) : open.kakao.com/o/gqbUQEM Website : humanscape.io Medium : medium.com/humanscape-ico Facebook : www.facebook.com/humanscape Twitter : twitter.com/Humanscape_io Reddit : https://www.reddit.com/r/Humanscape_official Bitcointalk announcement : https://bit.ly/2rVsP4T Email : support@humanscape.io