๋™์  ์ฟผ๋ฆฌ ๋นŒ๋”ฉ

๊ธฐ๋ณธ์ ์œผ๋กœ Drizzle์˜ ๋ชจ๋“  ์ฟผ๋ฆฌ ๋นŒ๋”๋Š” ๊ฐ€๋Šฅํ•œ ํ•œ SQL์„ ์ค€์ˆ˜ํ•˜๋ ค๊ณ  ํ•˜๋ฏ€๋กœ ๋Œ€๋ถ€๋ถ„์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, SELECT ๋ฌธ์—๋Š” WHERE ์ ˆ์ด ํ•˜๋‚˜๋งŒ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ .where()๋ฅผ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

const query = db
	.select()
	.from(users)
	.where(eq(users.id, 1))
	.where(eq(users.name, 'John')); // โŒ ํƒ€์ž… ์˜ค๋ฅ˜ - where()๋Š” ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ ๊ฐ€๋Šฅ

์ด๋Ÿฌํ•œ ์ œํ•œ์ด ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜๋˜ ์ด์ „ ORM ๋ฒ„์ „์—์„œ๋Š” ์ด ์˜ˆ์ œ๊ฐ€ ํŠนํžˆ ๋งŽ์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ˜ผ๋ž€์˜ ์›์ธ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋“ค์€ ์ฟผ๋ฆฌ ๋นŒ๋”๊ฐ€ ์—ฌ๋Ÿฌ .where() ํ˜ธ์ถœ์„ ๋‹จ์ผ ์กฐ๊ฑด์œผ๋กœ โ€œ๋ณ‘ํ•ฉโ€ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด ๋™์ž‘์€ ์ „์ฒด ์ฟผ๋ฆฌ๋ฅผ ํ•œ ๋ฒˆ์— ์ƒ์„ฑํ•˜๋Š” ๊ธฐ์กด์˜ ์ฟผ๋ฆฌ ๋นŒ๋”ฉ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ฟผ๋ฆฌ ๋นŒ๋”๋ฅผ ๋ฐ›์•„์„œ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๊ณต์œ  ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์ฒ˜๋Ÿผ ๋™์ ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋นŒ๋“œํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Drizzle์€ ์ฟผ๋ฆฌ ๋นŒ๋”๋ฅผ ์œ„ํ•œ ํŠน๋ณ„ํ•œ โ€˜dynamicโ€™ ๋ชจ๋“œ๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ์ด๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•˜๋Š” ์ œํ•œ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด ์ฟผ๋ฆฌ ๋นŒ๋”์—์„œ .$dynamic()์„ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ œ๊ณต๋œ ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ์™€ ์„ ํƒ์  ํŽ˜์ด์ง€ ํฌ๊ธฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฟผ๋ฆฌ์— LIMIT ๋ฐ OFFSET ์ ˆ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฐ„๋‹จํ•œ withPagination ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์ž‘๋™ ๋ฐฉ์‹์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

function withPagination<T extends PgSelect>(
	qb: T,
	page: number = 1,
	pageSize: number = 10,
) {
	return qb.limit(pageSize).offset((page - 1) * pageSize);
}

const query = db.select().from(users).where(eq(users.id, 1));
withPagination(query, 1); // โŒ ํƒ€์ž… ์˜ค๋ฅ˜ - ์ฟผ๋ฆฌ ๋นŒ๋”๊ฐ€ ๋™์  ๋ชจ๋“œ๊ฐ€ ์•„๋‹˜

const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // โœ… OK

withPagination ํ•จ์ˆ˜๋Š” ์ œ๋„ค๋ฆญ์ด๋ฏ€๋กœ ์˜ˆ๋ฅผ ๋“ค์–ด ์กฐ์ธ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ฟผ๋ฆฌ ๋นŒ๋”์˜ ๊ฒฐ๊ณผ ํƒ€์ž…์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

function withFriends<T extends PgSelect>(qb: T) {
	return qb.leftJoin(friends, eq(friends.userId, users.id));
}

let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);

์ด๋Š” PgSelect ๋ฐ ๊ธฐํƒ€ ์œ ์‚ฌํ•œ ํƒ€์ž…์ด ๋™์  ์ฟผ๋ฆฌ ๋นŒ๋”ฉ์—์„œ ์‚ฌ์šฉํ•˜๋„๋ก ํŠน๋ณ„ํžˆ ์„ค๊ณ„๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋“ค์€ ๋™์  ๋ชจ๋“œ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋™์  ์ฟผ๋ฆฌ ๋นŒ๋”ฉ์—์„œ ์ œ๋„ค๋ฆญ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ํƒ€์ž… ๋ชฉ๋ก์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

DialectType
QuerySelectInsertUpdateDelete
PostgresPgSelectPgInsertPgUpdatePgDelete
PgSelectQueryBuilder
MySQLMySqlSelectMySqlInsertMySqlUpdateMySqlDelete
MySqlSelectQueryBuilder
SQLiteSQLiteSelectSQLiteInsertSQLiteUpdateSQLiteDelete
SQLiteSelectQueryBuilder

...QueryBuilder ํƒ€์ž…์€ ๋…๋ฆฝํ˜• ์ฟผ๋ฆฌ ๋นŒ๋” ์ธ์Šคํ„ด์Šค์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. DB ์ฟผ๋ฆฌ ๋นŒ๋”๋Š” ์ด๋“ค์˜ ํ•˜์œ„ ํด๋ž˜์Šค์ด๋ฏ€๋กœ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

	import { QueryBuilder } from 'drizzle-orm/pg-core';

	function withFriends<T extends PgSelectQueryBuilder>(qb: T) {
		return qb.leftJoin(friends, eq(friends.userId, users.id));
	}

	const qb = new QueryBuilder();
	let query = qb.select().from(users).where(eq(users.id, 1)).$dynamic();
	query = withFriends(query);