Node.js/Sequelize

[ORM] ๐Ÿ“š ์‹œํ€„๋ผ์ด์ฆˆ - ์ฟผ๋ฆฌ ๋ฌธ๋ฒ• ์ •๋ฆฌ

์ธํŒŒ_ 2021. 11. 10. 18:55

์‹œํ€„๋ผ์ด์ฆˆ-๋ฌธ๋ฒ•

์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ๋ฌธ

CRUD ์ž‘์—…์„ ํ•˜๊ธฐ ์œ„ํ•ด์„  ๋จผ์ € ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ๋ฅผ ์•Œ์•„์•ผํ•œ๋‹ค.

SQL๋ฌธ์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์‹œํ€„๋ผ์ด์ฆˆ์˜ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

์‹œํ€„๋ฆฌ์•„์ง€ ์ฟผ๋ฆฌ๋ฌธ์„ ๋น„๋™๊ธฐ๋กœ ๋ˆ์ž‘ํ•˜๋ฉฐ ํ”„๋กœ๋ฏธ์Šค ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ, then์„ ๋ถ™์—ฌ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ž˜์„œ async/await ๋ฌธ๋ฒ•๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.


ํ…Œ์ด๋ธ” ์กฐํšŒ (findAll, findOne)

 

findAll

  • ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฐ์—ด ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜

๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ณ  ์‹ถ์œผ๋ฉด findAll ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

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

// usersํ…Œ์ด๋ธ” ์ „์ฒด๋ฅผ ์กฐํšŒํ•ด์„œ ๊ทธ ๊ฒฐ๊ณผ๊ฐ’์„ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด user๋ณ€์ˆ˜์— ๋„ฃ์–ด์ค€๋‹ค.
const user = User.findAll({}); 

// user๋ณ€์ˆ˜์—๋Š” ์กฐํšŒ๋œ ๊ฒฐ๊ณผ ๊ฐ์ฒด๊ฐ€ ๋“ค์–ด์žˆ์–ด์„œ, ํ•ด๋‹น ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ๋“ค์„ ์กฐํšŒํ• ์ˆ˜ ์žˆ๋‹ค.
console.log(user[0].comment) // findAll๋Š” ์—ฌ๋Ÿฌ ํ–‰๋“ค์„ ์กฐํšŒํ•˜๊ธฐ์—, ๊ฐ ํ–‰๋“ค์ด ๋ฐฐ์—ด๋กœ ์ €์žฅ๋˜์–ด์žˆ๋‹ค.
							 // ๋”ฐ๋ผ์„œ ๋ฐฐ์—ด ์ธ๋ฑ์Šค๋กœ ์กฐํšŒํ•œ๋‹ค. ์ฒซ๋ฒˆ์งธ ํ–‰ usersํ…Œ์ด๋ธ”์— commentํ•„๋“œ๋ฅผ ์กฐํšŒํ•˜๊ธฐ

์œ„์˜ ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ๋ฌธ์€ ๋‹ค์Œ SQL๋ฌธ ์ฒ˜๋Ÿผ ๋™์ž‘ํ•œ๋‹ค.

SELECT * FROM users;

 

findOne

  • ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜

ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜๋งŒ ๊ฐ€์ ธ์˜จ๋‹ค. 

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

// usersํ…Œ์ด๋ธ” ์ „์ฒด๋ฅผ ์กฐํšŒํ•ด์„œ ๊ทธ ๊ฒฐ๊ณผ๊ฐ’์„ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด user๋ณ€์ˆ˜์— ๋„ฃ์–ด์ค€๋‹ค.
const user = User.findOne({}); 

// user๋ณ€์ˆ˜์—๋Š” ์กฐํšŒ๋œ ๊ฒฐ๊ณผ ๊ฐ์ฒด๊ฐ€ ๋“ค์–ด์žˆ์–ด์„œ, ํ•ด๋‹น ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ๋“ค์„ ์กฐํšŒํ• ์ˆ˜ ์žˆ๋‹ค.
console.log(user.comment) // findOne๋Š” ํ•˜๋‚˜๋งŒ ์กฐํšŒ๋œ๋‹ค. usersํ…Œ์ด๋ธ”์— commentํ•„๋“œ๋ฅผ ์กฐํšŒํ•˜๊ธฐ

์œ„์˜ ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ๋ฌธ์€ ๋‹ค์Œ SQL๋ฌธ ์ฒ˜๋Ÿผ ๋™์ž‘ํ•œ๋‹ค.

SELECT * FROM users limit 1;

์กฐ๊ฑด ์กฐํšŒ (attributes, where)

attributes ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ์ปฌ๋Ÿผ๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜๋„ ์žˆ๋‹ค.

๋˜ํ•œ, where ์˜ต์…˜์œผ๋กœ ์กฐ๊ฑด๋“ค์„ ๋‚˜์—ดํ•  ์ˆ˜๋„ ์žˆ๋‹ค. where ์˜ต์…˜์€ ๊ธฐ๋ณธ์ ์œผ๋กœ AND ์˜ต์…˜๊ณผ ๊ฐ™๋‹ค.

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

const user = User.findAll({
    attributes: ['name', 'age'],
    where: {
        married: true, // married = 1
        age: { [Op.gt]: 30 }, // age > 30;
    },
});

console.log(user.comment)
SELECT name, age FROM users WHERE married = 1 AND age > 30;

๋น„๊ต ๊ตฌ๋ฌธ์ด ์กฐ๊ธˆ ํŠน์ดํ•œ๋ฐ, ์‹œํ€„๋ผ์ด์ฆˆ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŠน์ˆ˜ํ•œ ์—ฐ์‚ฐ์ž๋“ค์ด ์‚ฌ์šฉ๋œ๋‹ค.

๋‹ค์Œ์€ ์‹œํ€„๋ผ์ด์ € ํŠน์ˆ˜ ์—ฐ์‚ฐ์ž ์ •๋ฆฌ์ด๋‹ค.

 

๐Ÿ’ก ์ž์ฃผ ์“ฐ์ด๋Š” Op๊ฐ์ฒด

Op.gt ์ดˆ๊ณผ
Op.gte ์ด์ƒ
Op.lt ๋ฏธ๋งŒ
Op.lte ์ดํ•˜
Op.ne ๊ฐ™์ง€ ์•Š์Œ
Op.or ๋˜๋Š”
Op.in ๋ฐฐ์—ด ์š”์†Œ ์ค‘ ํ•˜๋‚˜
Op.notIn ๋ฐฐ์—ด ์š”์†Œ์™€ ๋ชจ๋‘ ๋‹ค๋ฆ„
const Op = Sequelize.Op

[Op.and]: [{a: 5}, {b: 6}] // (a = 5) AND (b = 6)
[Op.or]: [{a: 5}, {a: 6}]  // (a = 5 OR a = 6)

[Op.gt]: 6,                // > 6
[Op.gte]: 6,               // >= 6

[Op.lt]: 10,               // < 10
[Op.lte]: 10,              // <= 10

[Op.ne]: 20,               // != 20
[Op.eq]: 3,                // = 3

[Op.is]: null              // IS NULL
[Op.not]: true,            // IS NOT TRUE

[Op.between]: [6, 10],     // BETWEEN 6 AND 10
[Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15

[Op.in]: [1, 2],           // IN [1, 2]
[Op.notIn]: [1, 2],        // NOT IN [1, 2]

[Op.like]: '%hat',         // LIKE '%hat'
[Op.notLike]: '%hat'       // NOT LIKE '%hat'
[Op.startsWith]: 'hat'     // LIKE 'hat%'
[Op.endsWith]: 'hat'       // LIKE '%hat'
[Op.substring]: 'hat'      // LIKE '%hat%'

[Op.regexp]: '^[h|a|t]'    // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
[Op.notRegexp]: '^[h|a|t]' // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)

[Op.like]: { // LIKE ANY ARRAY['cat', 'hat'] - also works for iLike and notLike
	[Op.any]: ['cat', 'hat']
} 

[Op.gt]: { // > ALL (SELECT 1)
	[Op.all]: literal('SELECT 1') 
}

 

Op.or์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐฐ์—ด ๋‚ด์— ์ ์šฉํ•  ์ฟผ๋ฆฌ๋“ค์„ ๋‚˜์—ดํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค.

const user = User.findAll({
    attributes: ['name', 'age'],
    where: { 
        [Op.or]: [ // married = 0 or age > 30
            { married: false },
            { age: { [Op.gt]: 30 } }
        ],
    },
});

console.log(user.comment)
SELECT name, age FROM users WHERE married = 1 OR age > 30;

๋ฐ์ดํ„ฐ ๋„ฃ๊ธฐ (Create)

๋ชจ๋ธ์„ ๋ถˆ๋Ÿฌ์™€ create ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ์šฐ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ ํ–‡๊น”๋ฆฌ๋Š”๊ฒŒ SQL์—์„œ์˜ create๋Š” ํ…Œ์ด๋ธ”์„ ๋งŒ๋“œ๋Š” ๊ฑฐ์ง€๋งŒ ORM์—์„œ์˜ create๋Š” insert๋กœ ์“ฐ์ธ๋‹ค๋Š” ์ ์„ ์ˆ™์ง€ํ•˜์ž.

const result = User.create({ // ์ƒ์„ฑ๋œ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์–ป๋Š”๋‹ค.
    name: 'beom seok',
    age: 23,
    married: false,
    comment: '์•ˆ๋…•ํ•˜์„ธ์š”.'
});
INSERT INTO users (name, age, married, comment) 
VALUES ('beom seok', 23, 0, '์•ˆ๋…•ํ•˜์„ธ์š”.');

์ฃผ์˜ํ•ด์•ผํ•  ์ ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์„ ๋•Œ MySQL์˜ ์ž๋ฃŒํ˜•์ด ์•„๋‹Œ ์‹œํ€„๋ผ์ด์ฆˆ ๋ชจ๋ธ์— ์ •์˜ํ•œ ์ž๋ฃŒํ˜•๋Œ€๋กœ ๋„ฃ์–ด์•ผํ•œ๋‹ค.

๊ฐ€๋ น married์˜ ๊ฒฝ์šฐ ์‹ค์ œ MySQL์—๋Š” TINYINT๋กœ ๋˜์–ด ์ด์ „์—” 0์„ ๋„ฃ์–ด์ฃผ์—ˆ์—ˆ์ง€๋งŒ, ํ˜„์žฌ๋Š” BOOLEAN์œผ๋กœ ์ •์˜๋ผ์žˆ์œผ๋ฏ€๋กœ false๋ฅผ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

์ž๋ฃŒํ˜• ๋˜๋Š” ์˜ต์…˜์— ๋ถ€ํ•ฉํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์„ ๊ฒฝ์šฐ ์‹œํ€„๋ผ์ด์ฆˆ๊ฐ€ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉฐ, ์‹œํ€„๋ผ์ด์ฆˆ๊ฐ€ ์•Œ์•„์„œ MySQL ์ž๋ฃŒํ˜•์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ๋‹ˆ ๊ฑฑ์ •๋ง์ž.

 

 

findOrCreate()

findOrCreate() ๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์กฐํšŒ ํ›„์— ์—†๋Š” ๊ฐ’์ด๋ฉด ์ƒ์„ฑํ•˜๊ณ , ์žˆ๋Š” ๊ฐ’์ด๋ฉด ๊ทธ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ํ˜•ํƒœ์˜ ์˜คํผ๋ ˆ์ด์…˜๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

// find์™€ create ๋‘ ์กฐ๊ฑด์„ ์ดํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜ํ™˜๊ฐ’์ด 2๊ฐœ ์ฆ‰ ๋ฐฐ์—ด์ด๋‹ค.
const [user, created] = await User.findOrCreate({
    where: { username: 'sdepold' },
    defaults: {
    job: 'Technical Lead JavaScript'
}
});
if (created) {
    // ๋งŒ์•ฝ findํ•˜์ง€ ๋ชปํ•˜์—ฌ ์ƒˆ๋กœ create ๋ ๊ฒฝ์šฐ
    console.log(user.job); // 'Technical Lead JavaScript'
} else {
	// ๋งŒ์•ฝ find๊ฐ€ ๋  ๊ฒฝ์šฐ
}
where์— ํ•จ๊ป˜ ์ „๋‹ฌ๋˜๋Š” defaults ์˜ต์…˜์€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๋Š” ์š”์†Œ๊ฐ€ ๊ฐ–๋Š” ๊ธฐ๋ณธ๊ฐ’์ด๋‹ค.

์ •๋ ฌ (order)

์ •๋ ฌ์€ order ์˜ต์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

2์ฐจ์› ๋ฐฐ์—ด์ด๋ผ๋Š” ์ ์— ์ฃผ์˜ํ•˜์ž. ์ด๋Š” ์ •๋ ฌ์€ ๊ผญ ์ปฌ๋Ÿผ ํ•˜๋‚˜๊ฐ€ ์•„๋‹Œ ๋‘ ๊ฐœ ์ด์ƒ์œผ๋กœ๋„ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

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

ํŽ˜์ด์ง• (limit, offset)

์กฐํšŒํ•  ๋กœ์šฐ ๊ฐœ์ˆ˜๋Š” limit์œผ๋กœ,

์กฐํšŒ๋ฅผ ์‹œ์ž‘ํ•  ๋กœ์šฐ ์œ„์น˜๋Š” offset์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

SELECT id, name FROM users ORDER BY age DESC LIMIT 10 OFFSET 5;
limit์ด 1์ด๋ผ๋ฉด findAll ๋Œ€์‹  findOne์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ˆ˜์ • (Update)

update ๋ฉ”์„œ๋“œ๋กœ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋Š” ์ˆ˜์ •ํ•  ๋‚ด์šฉ์ด๊ณ , ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋Š” ์–ด๋–ค ๋กœ์šฐ๋ฅผ ์ˆ˜์ •ํ• ์ง€์— ๋Œ€ํ•œ ์กฐ๊ฑด์ด๋‹ค.

where ์˜ต์…˜์— ์กฐ๊ฑด๋“ค์„ ์ ๋Š”๋‹ค.

User.update({
    comment: '์ƒˆ๋กœ์šด ์ฝ”๋ฉ˜ํŠธ.',
}, {
    where: { id: 2 },
});
UPDATE users SET comment = '์ƒˆ๋กœ์šด ์ฝ”๋ฉ˜ํŠธ.' WHERE id = 2;

 

upsert()

update์™€ / insert ํ•ฉ์„ฑ ๋ฒ„์ ผ.

๋ฌธ๋ฒ•์€ ์œ„์—์„œ ๋ฐฐ์šด findorcreate์™€ ๋ณ„ ๋‹ค๋ฅด์ง€ ์•Š๋‹ค.

const [city, created] = await City.upsert({
  cityName: "York",
  population: 20000,
});
console.log(created); // true or false
console.log(city); // City object
INSERT INTO Cities (cityName, population)
VALUES (?, ?)
ON DUPLICATE KEY
  UPDATE
    `cityName` = VALUES (`cityName`),
    `population` = VALUES (`population`);

์‚ญ์ œ (Delete)

๋กœ์šฐ ์‚ญ์ œ๋Š” destroy ๋ฉ”์„œ๋“œ๋กœ ์‚ญ์ œํ•œ๋‹ค.

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

User.destroy({
    where: { id: { [Op.in]: [1,3,5] } },
});
DELETE FROM users WHERE id = 2;

DELETE FROM users WHERE id in(1,3,5);

์‹œํ€„๋ผ์ด์ฆˆ ๊ด€๊ณ„ ์ฟผ๋ฆฌ๋ฌธ

 

include

์œ„์˜ ์‚ฌ์ง„์ฒ˜๋Ÿผ, ํ˜„์žฌ User ๋ชจ๋ธ์€ Commenter ๋ชจ๋ธ๊ณผ hasMany-belongsTo ๊ด€๊ณ„๊ฐ€ ๋งบ์–ด์ ธ ์žˆ์œผ๋ฉฐ, ๋งŒ์•ฝ ํŠน์ • ์‚ฌ์šฉ์ž๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด์„œ ๊ทธ ์‚ฌ๋žŒ์˜ ๋Œ“๊ธ€๊นŒ์ง€ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ๋‹ค๋ฉด include ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

๊ด€๊ณ„๊ฐ€ ์žˆ๋Š” ๋ชจ๋ธ์„ include ๋ฐฐ์—ด์— ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

๋ฐฐ์—ด์ธ ์ด์œ ๋Š” ๋‹ค์–‘ํ•œ ๋ชจ๋ธ๊ณผ ๊ด€๊ณ„๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋Œ“๊ธ€์€ ์—ฌ๋Ÿฌ ๊ฐœ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ (hasMany) user.Comments๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค.

const user = await User.findOne({
    include: [{ // joinํ•œ๋‹ค.
        model: Comment, // joinํ•  ํ…Œ์ด๋ธ”์„ ๊ณ ๋ฅธ๋‹ค.
    }]
});

console.log(user.Comments[0]); 
// ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์ธ user๊ฐ์ฒด์•ˆ์— Comments๋ผ๋Š” ํ‚ค์— includeํ•œ commentsํ…Œ์ด๋ธ” ์ฟผ๋ฆฌ๋“ค์ด ๋ฐฐ์—ด ๊ฐ’์œผ๋กœ์„œ ๋‹ด๊ฒจ ์ž‡๋‹ค.
// Comments ํ‚ค์˜ ์ด๋ฆ„์€ User๋ชจ๋ธ์€ hasMany๋‹ˆ๊นŒ ๋ณต์ˆ˜ํ˜•์œผ๋กœ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜๋˜์„œ ์ƒ์„ฑ๋œ ๊ฒƒ์ด๋‹ค. (๊ตฌ๋ถ„ํ•˜๊ธฐ ํŽธํ•˜๊ฒŒ)
// => hasOne์ด๋ฉด ๋‹จ์ˆ˜ํ˜•. M:N์ด๋ฉด ํ•ญ์ƒ ๋ณต์ˆ˜ํ˜•.
select u.*, c.*
from users u
inner join comments c -- sequelize include๋Š” ๊ธฐ๋ณธ๋™์ž‘์€ inner join์ด๋‹ค.
on u.id = c.commenter;
 

[ORM] ๐Ÿ“š Sequelize - left join ํ•˜๋Š”๋ฒ•

Sequelize left join ์‹œํ€„๋ผ์ด์ €์—์„œ include๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ inner join ์ฒ˜๋ฆฌ์ด๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋ณด์ž. [Associate ์„ค์ •] Shop.hasMany(ShopAd, {foreignKey : 'shop_id', as : 'ads'}); ShopAd.belongsTo(Shop, {f..

inpa.tistory.com

 

 

get๋ชจ๋ธ๋ช…

์œ„์˜ include๋กœ๋„ ์ถฉ๋ถ„ํžˆ ๊ด€๊ณ„๋ฅผ ๋งบ์–ด ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆด์ˆ˜์žˆ์ง€๋งŒ ์ข€๋” ๊ฐ„ํŽธํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ๊ด€๊ณ„์ฟผ๋ฆฌ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋Œ“๊ธ€์— ์ ‘๊ทผํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๋ฌธ๋ฒ•์ด ๋„ˆ๋ฌด ์ถ”์ƒ์ ์ด๋ผ include๋ณด๋‹ค ์•ˆ ์™€๋‹ฟ์„์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ ORM๋ฌธ๋ฒ•์„ ๊ฐ„์†Œํ™”์‹œํ‚ค๊ณ  ์ง๊ด€์ ์ธ ๋ฉ”์†Œ๋“œ๋ช…์„ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

const user = await User.findOne({});
const comments = await user.getComments(); 
// ๋”ฐ๋กœ includeํ•  ํ•„์š”์—†์ด ๋ฐ”๋กœ get๋ฉ”์†Œ๋“œ๋ฅผ ์“ฐ๋ฉด ๋œ๋‹ค.
// ์™œ๋ƒํ•˜๋ฉด associate๋กœ ๋ชจ๋ธ์„ ์ •์˜ํ•œ ์ˆœ๊ฐ„ ์‹œํ€„๋ผ์ด์ €๊ฐ€ ๊ด€๊ณ„๊ฐ€ ๋งบ์–ด์ง„ ๋ฉ”์†Œ๋“œ๋ฅผ ์•Œ์•„์„œ ๋งŒ๋“ค์–ด ์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

console.log(comments);

๊ด€๊ณ„๋ฅผ ์„ค์ •ํ–ˆ๋‹ค๋ฉด, ์‹œํ€„๋ผ์ด์ฆˆ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด

  • getComment() / getComments( [ ] ) : ์กฐํšŒ
  • setComment() / setComments( [ ] ) : ์ˆ˜์ • - ๋‹จ ํ†ต์งธ๋กœ ์ง€์šฐ๊ณ  ์ถ”๊ฐ€ํ•˜๋Š” ์‹์ด๋ผ ์กฐ์‹ฌ
  • addComment() / addComments( [ ] ) : ์ƒ์„ฑ
  • removeComment() / removeComments( [ ] ) : ์‚ญ์ œ

๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ์ง€์›ํ•œ๋‹ค.

๋™์‚ฌ ๋’ค์— ๋ชจ๋ธ์˜ ์ด๋ฆ„์ด ๋ถ™๋Š” ํ˜•์‹์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค.

s๋ฅผ ๋ถ™์—ฌ๋„ ๋˜๊ณ  ์•ˆ๋ถ™์—ฌ๋„ ์ƒ๊ด€ ์—†๋‹ค.

๋‹ค๋งŒ, ๋ณด๊ธฐ ํŽธํ•˜๊ฒŒ s๋ฅผ ๋ถ™์ธ ๋ฉ”์†Œ๋“œ๋Š” ๋ฐฐ์—ด์„ ์ธ์ž๋กœ ๋ฐ›๋Š” ๊ฒƒ์œผ๋กœ ๊ทœ์น™์„ ์ •ํ•ด๋†“์œผ๋ฉด ์ข‹๋‹ค.

* ์ด ๋ฐฉ๋ฒ•์€, DB์— ์š”์ฒญ์€ ๋‘๋ฒˆ ๋ณด๋‚ด์ง€๋งŒ, ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ๋งŒ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์— ์œ ์˜ ํ•˜์ž.
* ํ•˜๋‚˜๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ˆœ์„œ๋Š” ๋‹น์—ฐํžˆ ์˜ค๋ฆ„์ฐจ์ˆœ์ด๋‹ค.

 

์กฐํšŒ๋Š” ์œ„์™€ ๊ฐ™์ด ํ•˜๋ฉด ๋˜์ง€๋งŒ,

์ˆ˜์ •, ์ƒ์„ฑ, ์‚ญ์ œ ๋•Œ๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅธ ์ ์ด ์žˆ๋‹ค.

// ์ƒ์„ฑ
const user = await User.findOne({ ์˜ต์…˜ }); // ์ฐธ์กฐ ๊ด€๊ณ„์— ๋ถ€ํ•ฉํ•œ ์œ ์ €๋ฅผ ์„ ํƒํ•˜๊ณ 
const comment = await Comment.create({ ์˜ต์…˜ }); // ๋ฐ์ดํ„ฐ๋ฅผ commentํ…Œ์ด๋ธ”์— ๋„ฃ๋Š”๋‹ค.

await user.addComment(comment); // user์˜ ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ด€๊ณ„์„ค์ •๋œ comment row์— ์ถ”๊ฐ€/์—…๋Žƒ ํ•œ๋‹ค
// ๋งŒ์ผ user์˜ ์™ธ๋ž˜ํ‚ค๋ฅผ ์œ„ Comment.create()๊ณผ์ •์—์„œ ๋„ฃ์—ˆ๋‹ค๋ฉด ๊ตณ์ด addComment() ์ฟผ๋ฆฌ๋ฅผ ์•ˆ๋‚ ๋ ค๋„ ๋œ๋‹ค.
// FK๊ฐ€ nullํ—ˆ์šฉ์ด๊ณ  insert๊ณผ์ •์—์„œ FK์— ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ๋„ฃ์—ˆ์„ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

// ์—ฌ๋Ÿฌ ๊ฐœ ์ƒ์„ฑ
const user = await User.findOne({ ์˜ต์…˜ });
const comment1 = await Comment.create({ ์˜ต์…˜ });
const comment2 = await Comment.create({ ์˜ต์…˜ });

await user.addComment([comment1, comment2]);
insert into comments (ํ•„๋“œ) 
( select ํ•„๋“œ from user where ์กฐ๊ฑด )

 

๊ด€๊ณ„ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ์ธ์ˆ˜๋กœ ์ถ”๊ฐ€ํ•  ๋Œ“๊ธ€ ๋ชจ๋ธ์„ ๋„ฃ๊ฑฐ๋‚˜ ๋Œ“๊ธ€์˜ ์•„์ด๋””๋ฅผ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค.

์ˆ˜์ •์ด๋‚˜ ์‚ญ์ œ ๋˜ํ•œ ๋งˆ์ฐฌ๊ฐ€์ง€์ด๋‹ค.

 

 

as ๋ณ„๋ช…

๋งŒ์ผ ๋™์‚ฌ ๋’ค์˜ ๋ชจ๋ธ ์ด๋ฆ„์„ ๋ฐ”๊พธ๊ณ  ์‹ถ๋‹ค๋ฉด ๊ด€๊ณ„ ์„ค์ • ์‹œ 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: [{ // left outer join
        model: Comment, // joinํ•  ๋ชจ๋ธ
        attributes: ['id'], // selectํ•ด์„œ ํ‘œ์‹œํ•  ํ•„๋“œ ์ง€์ •
        where: { 
            id: 1, // on Comment.id = 1
        },
    }]
});

// ๋˜๋Š”

const comments = await user.getComments({ // user๊ฐ€ comments๋ฅผ getํ•œ๋‹ค. (user์™€ comments๋Š” ๊ด€๊ณ„๋˜์–ด ์žˆ๋‹ค.)
    attributes: ['id'],  // selectํ•ด์„œ ํ‘œ์‹œํ•  ํ•„๋“œ ์ง€์ •
    where: {
    	id: 1, // on Comment.id = 1
    },
});
select users.*, comments.id 
from users
inner join comments
on comments.commenter = users.id
where comments.id = 1
LIMIT 1;

์ด๋ฐ–์˜ ์‹œํ€„๋ฆฌ์•„์ฆˆ ์ฟผ๋ฆฌ๋ฌธ

 

๋ชจ๋ธ์‚ฌ์šฉ — Sequelize Of Node.js translate V5 5 documentation

๋ฐœ๊ฒฌํ•˜๋Š” ํ•จ์ˆ˜๋“ค์€ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜์ด๋‹ค. ๊ทธ๋“ค์€ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ•œํ•˜์ง€ ์•Š๋Š” ๋Œ€์‹ ์— ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋ฐœ๊ฒฌํ•˜๋Š” ํ•จ์ˆ˜๋Š” ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ

pjt3591oo.github.io

 

์ฟผ๋ฆฌ ๋ฌธ์ž์—ด (literal)

์‹œํ€„๋ผ์ด์ฆˆ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋งŒ์œผ๋กœ๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.

literal() ์€ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์„ ์ถ”๊ฐ€ํ•ด ์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

User.findAll({
  attributes: [
    ["name", "username"],
    [Sequelize.literal("age + 1"), "age"],
  ],
})
SELECT `name` AS `username`, age + 1 AS `age` FROM `users` AS `user`

๋ณด๋Š”๋ฐ”์™€ ๊ฐ™์ด, sql๋ฌธ์—์„œ๋Š” ๋ช…๋ น์ค„์— ๊ทธ๋Œ€๋กœ ์‚ฐ์ˆ ์—ฐ์‚ฐ์ž๋ฅผ ์จ์„œ ๊ณง๋ฐ”๋กœ ๊ณ„์‚ฐ๋œ ๊ฐ’์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆด ์ˆ˜ ์žˆ๋Š”๋ฐ,

์‹œํ€„๋ผ์ด์ฆˆ๋Š” ์ด๋Ÿฌํ•œ ๋ฆฌํ„ฐ๋Ÿด ์‚ฐ์ˆ ์—ฐ์‚ฐ์„ ์ง€์›ํ•˜์ง€ ์•Š์•„์„œ literal() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.


SQL ์ฟผ๋ฆฌ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ธฐ

์‹œํ€„๋ผ์ด์ฆˆ์˜ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹ซ๊ฑฐ๋‚˜ ํ—ท๊ฐˆ๋ฆฐ๋‹ค๋ฉด ์ง์ ‘ SQL๋ฌธ์„ ํ†ตํ•ด ์ฟผ๋ฆฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

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

์ฐธ๊ณ ๋ฌธํ—Œ

Node.js ๊ต๊ณผ์„œ - ๊ธฐ๋ณธ๋ถ€ํ„ฐ ํ”„๋กœ์ ํŠธ ์‹ค์Šต๊นŒ์ง€ ๊ฐ•์˜ - ์กฐํ˜„์˜