MQL - Node.js ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ๋น๋
ํน์ง
- INSERT, UPDATE, WHERE ์ ๋ฑ์ ํ์ํ ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด๋ฅผ ํตํด ์ฝ๊ฒ ์์ฑํ ์ ์์ต๋๋ค.
- ์ผ๋ฐ์ ์ธ SQL ๋ฌธ๋ฒ์ ์ ์งํ ์ ์์ด, ์๋ธ ์ฟผ๋ฆฌ, ์กฐ์ธ ๋ฑ์ ์ฝ๊ฒ ์์ฑํ ์ ์์ต๋๋ค.
- SQL์ ์ธ๋ฐํ๊ฒ ํ๋ํ๊ณ ๋ฐ์ ์ํค๊ธฐ ์ฝ์ต๋๋ค.
- ๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ง์ํ๋ ๋ค์ํ ์ต์ Operator ๋ค์ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- SQL Injection ๊ณต๊ฒฉ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค.
- Associations์ ์ํด ๋ชจ๋ธ์ ๋ฏธ๋ฆฌ ๊ตฌ์ฑํด๋ ํ์๊ฐ ์์ต๋๋ค.
- Transaction์ ์ฌ์ฉํ๊ธฐ ์ฝ์ต๋๋ค.
- ์ธ์์ ๊ฒฐ๊ณผ ๊ฐ์ ์๋ฐ์คํฌ๋ฆฝํธ์ ๊ธฐ๋ณธ ๊ฐ์ผ๋ก๋ง(object, array, string, number, true, false, null) ๊ตฌ์ฑํ์ฌ, ์กฐํฉ์ฑ์ด ๋๊ณ JSON ๋ณํ ๋น์ฉ์ด ์์ต๋๋ค.
- PostgreSQL, MySQL ์ง์
๋ชฉ์ฐจ
์ค์น
npm i mql2
์ฐ๊ฒฐ
PostgreSQL
const { PostgreSQL } = require('mql2');
const { CONNECT } = PostgreSQL;
const POOL = await CONNECT({
host: 'localhost',
user: 'username',
password: '1234',
database: 'dbname'
});
PostgreSQL Connection ์ต์
MQL์ ๋ด๋ถ์ ์ผ๋ก node-postgres๋ฅผ ์ฌ์ฉํฉ๋๋ค. CONNECT
ํจ์์ ์ฌ์ฉ๋๋ ์ต์
์ node-postgres์ ๋์ผํฉ๋๋ค. ๋๋น ์ฐ๊ฒฐ์ด๋ ์ปค๋ฅ์
ํ๊ณผ ๊ด๋ จ๋ ์์ธํ ์ต์
์ node-postgres ์ฌ์ดํธ์์ ํ์ธํ ์ ์์ต๋๋ค.
MySQL
const { MySQL } = require('mql2');
const { CONNECT } = MySQL;
const POOL = await CONNECT({
host: 'localhost',
user: 'username',
password: '1234',
database: 'dbname'
});
MySQL Connection ์ต์
MQL์ ๋ด๋ถ์ ์ผ๋ก mysql๋ฅผ ์ฌ์ฉํฉ๋๋ค. CONNECT
ํจ์์ ์ฌ์ฉ๋๋ ์ต์
์ mysql๊ณผ ๋์ผํฉ๋๋ค. ๋๋น ์ฐ๊ฒฐ์ด๋ ์ปค๋ฅ์
ํ๊ณผ ๊ด๋ จ๋ ์์ธํ ์ต์
์ mysql ์ฌ์ดํธ์์ ํ์ธํ ์ ์์ต๋๋ค.
๊ฐ๋จํ ์ฟผ๋ฆฌ
const { QUERY } = POOL;
const id = 10;
const posts = await QUERY `SELECT * FROM posts WHERE id = ${id}`;
์๋ธ ์ฟผ๋ฆฌ, ์กฐ์ธ
const type = 'TYPE1';
const limit = 10;
QUERY `
SELECT * FROM table1 WHERE table2_id IN (
SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit}
)
`;
const status = 'STATUS1';
QUERY `
SELECT *
FROM table1 AS t1, table2 AS t2
WHERE t1.id = t2.table1_id AND t1.status = ${status}
ORDER BY id DESC
LIMIT 10
`;
CONNECT
๋ฅผ ํตํด ์ป์ QUERY
๋ connection pool์ ์ด์ฉํฉ๋๋ค.
ํจ์ ๋ถ๋ฌ์ค๊ธฐ
const POOL = await CONNECT();
const = {
VALUES, IN, NOT_IN, EQ, SET, COLUMN, CL, TABLE, TB, SQL, MQL_DEBUG,
QUERY,
ASSOCIATE,
LJOIN,
TRANSACTION
} = POOL;
์ง์ํ๋ ํฌํผ ํจ์
EQ
const users = await QUERY `SELECT * FROM users WHERE ${EQ({
email: 'dev@marpple.com',
password: '1234'
})}`;
IN
const users = await QUERY `SELECT * FROM users WHERE ${IN('id', [15, 19, 20, 40])}`;
NOT_IN
const users = await QUERY `SELECT * FROM users WHERE ${NOT_IN('id', [2, 4])} LIMIT 3 ORDER BY ID`;
VALUES
const post = { user_id: 10, body: 'hoho' };
await QUERY `
INSERT INTO posts ${VALUES(post)}
`;
await QUERY `
INSERT INTO coords ${VALUES([
{x: 20},
{y: 30},
{x: 10, y: 20}
])}`;
SET
await QUERY `
UPDATE posts ${SET({ body: 'yo!', updated_at: new Date() })} WHERE id = ${post.id}
`;
COLUMN, CL
COLUMN == CL;
await QUERY `
SELECT
COLUMN('id', 'bb as cc', 't2.name', 't2.name as name2', { a: 'c' }, { 't3.a': 'd' })
...
`;
TABLE, TB
TABLE == TB;
await QUERY `
SELECT
...
FROM TABLE('t1'), TABLE('tt as t2')
`;
Associations
๊ธฐ๋ณธ
ASSOCIATE
๋ connection pool์ ์ด์ฉํฉ๋๋ค.
const { ASSOCIATE } = POOL;
const posts = await ASSOCIATE `
posts
- user
< comments
- user
`;
posts[0].body;
posts[0]._.user.name
posts[0]._.comments[0].body
posts[0]._.comments[0]._.user.name
Polymorphic
await ASSOCIATE `
posts
- user
p - photo
p < photos
< comments
p < photos
`;
Many to many
const books = await ASSOCIATE `
books
x authors
`;
books[0]._.authors[0].name;
const authors = await ASSOCIATE `
authors
x books ${{ xtable: 'books_authors' }}
`;
authors[0]._.books[0].name;
์ต์
ASSOCIATE `
posts
- user
< comments
- user
p < likes
- user
p < likes
- user
x tags
`;
ASSOCIATE `
posts ${SQL `WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}
- user
< comments ${{
column: COLUMN('body', 'updated_at')
}}
- user
p < likes
- user
p < likes
- user
x tags
`;
const posts = await ASSOCIATE `
posts ${{
table: 'articles' // ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ ๋ช
์ด ๋ค๋ฅผ ๋
}}
- user ${{ // - ๋ฅผ ํ์ผ๋ฏ๋ก ํ๋๋ฅผ ๊ฐ์ฒด๋ก ๊ฐ์ ธ์ด
left_key: 'writer_id', // articles๊ฐ ๊ฐ์ง members.member_id๋ฅผ ๊ฐ๋ฆฌํค๋ ์ปฌ๋ผ
key: 'member_id', // members ํ
์ด๋ธ์ด ๊ฐ์ง ํค
table: 'members' // user์ ํ
์ด๋ธ ๋ช
}}
< comments ${{ // < ๋ฅผ ํ์ผ๋ฏ๋ก ๋ฐฐ์ด๋ก ์ฌ๋ฌ๊ฐ๋ฅผ ๊ฐ์ ธ์ด
key: 'article_id' // articles์ id๋ฅผ ๊ฐ๋ฆฌํค๋ comments๊ฐ ๊ฐ์ง ์ปฌ๋ผ
}}
- user ${{
left_key: 'writer_id', // articles๊ฐ ๊ฐ์ง members.member_id๋ฅผ ๊ฐ๋ฆฌํค๋ ์ปฌ๋ผ
key: 'member_id', // members ํ
์ด๋ธ์ด ๊ฐ์ง ํค
table: 'members' // user์ ํ
์ด๋ธ ๋ช
}}
p < likes ${{ // p < ๋ฅผ ์ด์ฉํด ํ๋์ likes ํ
์ด๋ธ์ ํตํด comments์ posts์ likes๋ฅผ ๊ตฌํ
poly_type: { parent_name: 'comments' },
key: 'parent_id'
}}
p < likes ${{ // p < ๋ฅผ ์ด์ฉํด ํ๋์ likes ํ
์ด๋ธ์ ํตํด comments์ posts์ likes๋ฅผ ๊ตฌํ
poly_type: { parent_name: 'articles' },
key: 'parent_id'
}}
x tags ${{ // x ๋ฅผ ํตํด ์ค๊ฐ ํ
์ด๋ธ์ join ํ์ฌ ๋ค๋๋ค ๊ด๊ณ ๊ตฌํ
left_key: 'id', // articles.id (articles.id = tags_articles.article_id)
left_xkey: 'article_id', // left_key์ ๋งค์นญ๋๋ tags_articles์ ํค article_id
xtable: 'tags_articles', // ์ค๊ฐ ํ
์ด๋ธ ์ด๋ฆ
xkey: 'tag_name', // key์ ๋งค์นญ๋๋ tags_articles์ ํค tag_name
key: 'name' // tags๊ฐ ๊ฐ์ง ํค (tags_articles.tag_name = tags.name)
}}
`;
์์ ๊ฐ์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ๋ช
๊ณผ ์ฌ์ฉํ๊ณ ์ํ๋ ์ด๋ฆ์ด ๋ค๋ฅด๊ฑฐ๋, ASSOCIATE
๊ฐ ์๋์์ฑํ๋ ์ปฌ๋ผ๋ช
๋ฑ๊ณผ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํ๊ฐ ๋ค๋ฅผ ๊ฒฝ์ฐ ์ต์
์ ์ด์ฉํ์ฌ ๋ง์ถฐ์ค ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ VIEW๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ฝ๋ ๊ด๋ฆฌ์ ์ข์ต๋๋ค.
Hook
hook
์ ์ด์ฉํ์ฌ ๊ฐ์ ์ปฌ๋ผ์ด๋, ์ ๋ ฌ, ํํฐ ๋ฑ์ ์ถ๊ฐ ์์
์ ํ ์ ์์ต๋๋ค. ์์ ์ ์์ชฝ ๋ฐ์ดํฐ๋ค์ด ๋ชจ๋ ๋ถ๋ ค์ง ํ ์คํ๋์ด ํ์ฉํ๊ธฐ ์ข์ต๋๋ค.
const users = await ASSOCIATE `
users ${{hook: users => users.map(u =>
Object.assign({}, u, { _popular: !!u._.posts.find(p => p._is_best) })
)}}
< posts ${{hook: posts => posts.map(
p => Object.assign({}, p, { _is_best: p._.comments.length > 1 }))}}
- user
< comments
- user
`;
users[0]._popular;
users[0]._.posts[0]._is_best;
users[0]._.posts[1]._is_best;
Transaction
const { PostgreSQL } = require('mql2');
const { CONNECT } = PostgreSQL;
const POOL = await CONNECT({
host: 'localhost',
user: 'username',
password: '1234',
database: 'dbname',
charset: 'utf8'
});
const { TRANSACTION } = POOL;
const { QUERY, COMMIT, ROLLBACK } = await TRANSACTION();
await QUERY `
INSERT INTO posts ${VALUES(post)}
`;
await QUERY `
UPDATE posts ${SET({ body: 'yo!', updated_at: new Date() })} WHERE id = ${post.id}
`;
await ROLLBACK();
TRANSACTION
์ ํตํด ์ป์ QUERY
๋ ํ๋์ connection์ ์ด์ฉํฉ๋๋ค. ROLLBACK
์ด๋ COMMIT
์ ํ๊ณ ๋๋ฉด ์์ ํจ๊ป ์ป์๋ QUERY
ํจ์์ ์ปค๋ฅ์
์ ํด์ ๋๊ณ ๋์ด์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
DEBUG
MQL_DEBUG.LOG
๋ฅผ true
๋ก ์ค์ ํ ํ QUERY
๋ฅผ ์คํํ๋ฉด ์ฝ์์ DB๋ก ๋ณด๋ธ ์ฟผ๋ฆฌ๋ค์ ์ถ๋ ฅํฉ๋๋ค.
MQL_DEBUG.LOG = true;
QUERY `SELECT ${"hi~"} as ho`;