๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
Puri๋Š” TypeScript๋กœ ์•ˆ์ „ํ•˜๊ฒŒ SQL ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž… ์•ˆ์ „ํ•œ ์ฟผ๋ฆฌ ๋นŒ๋”์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์„œ๋Š” SELECT, INSERT, UPDATE, DELETE์˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ ์‹œ์ž‘ํ•˜๊ธฐ

SELECT

๋ฐ์ดํ„ฐ ์กฐํšŒํ•˜๊ธฐselect, selectAll, first

INSERT

๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ํ•˜๊ธฐinsert, upsert, returning

UPDATE

๋ฐ์ดํ„ฐ ์ˆ˜์ •ํ•˜๊ธฐupdate, increment, decrement

DELETE

๋ฐ์ดํ„ฐ ์‚ญ์ œํ•˜๊ธฐdelete, truncate

SELECT - ๋ฐ์ดํ„ฐ ์กฐํšŒ

๊ธฐ๋ณธ SELECT

const users = await db.table("users").select({
  id: "id",
  name: "username",
  email: "email",
});
// ๊ฒฐ๊ณผ: { id: number; name: string; email: string; }[]
select ๋ฉ”์„œ๋“œ๋Š” ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ์ปฌ๋Ÿผ์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ํ‚ค๋Š” ๊ฒฐ๊ณผ ํ•„๋“œ๋ช…, ๊ฐ’์€ ์‹ค์ œ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ๋ช…์ž…๋‹ˆ๋‹ค.

์ปฌ๋Ÿผ ๋ณ„์นญ(Alias)

const posts = await db.table("posts").select({
  postId: "id",              // ๋ณ„์นญ ์‚ฌ์šฉ
  postTitle: "title",
  authorName: "author_name", // snake_case โ†’ camelCase
  createdDate: "created_at",
});

// ๊ฒฐ๊ณผ ํƒ€์ž…์ด ์ž๋™์œผ๋กœ ์ถ”๋ก ๋จ
const firstPost = posts[0];
console.log(firstPost.postId);      // number
console.log(firstPost.postTitle);   // string

appendSelect - ์ปฌ๋Ÿผ ์ถ”๊ฐ€

์ด๋ฏธ ์„ ํƒํ•œ ์ปฌ๋Ÿผ์— ์ถ”๊ฐ€๋กœ ์ปฌ๋Ÿผ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
const query = db.table("users").select({
  id: "id",
  name: "username",
});

const users = await query.appendSelect({
  email: "email",
  role: "role",
});

// ๊ฒฐ๊ณผ: { id, name, email, role }

WHERE - ์กฐ๊ฑด ํ•„ํ„ฐ๋ง

๊ธฐ๋ณธ WHERE

const users = await db
  .table("users")
  .select({ id: "id", name: "username" })
  .where("role", "admin");

๋ณต์ˆ˜ ์กฐ๊ฑด (AND)

const users = await db
  .table("users")
  .select({ id: "id", name: "username" })
  .where("role", "admin")
  .where("is_active", true)
  .where("age", ">=", 18);

// SQL: WHERE role = 'admin' AND is_active = true AND age >= 18

OR ์กฐ๊ฑด

const users = await db
  .table("users")
  .select({ id: "id", name: "username" })
  .where("role", "admin")
  .orWhere("role", "moderator");

// SQL: WHERE role = 'admin' OR role = 'moderator'
orWhere๋Š” ๋‹จ์ˆœ OR ์กฐ๊ฑด์ž…๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ์กฐ๊ฑด ๊ทธ๋ฃน์€ whereGroup์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Advanced Patterns ์ฐธ๊ณ 

IN / NOT IN

const users = await db
  .table("users")
  .select({ id: "id" })
  .whereIn("role", ["admin", "moderator"]);

LIKE ๊ฒ€์ƒ‰

const users = await db
  .table("users")
  .select({ id: "id", name: "username" })
  .where("username", "like", "%john%");

// SQL: WHERE username LIKE '%john%'

INSERT - ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€

๋‹จ์ผ ๋ ˆ์ฝ”๋“œ ์ถ”๊ฐ€

const result = await db.table("users").insert({
  username: "john",
  email: "[email protected]",
  password: "hashed_password",
  role: "normal",
});

// result: number (์‚ฝ์ž…๋œ ๋ ˆ์ฝ”๋“œ ์ˆ˜)

RETURNING์œผ๋กœ ์‚ฝ์ž…๋œ ๋ฐ์ดํ„ฐ ๋ฐ›๊ธฐ

const inserted = await db
  .table("users")
  .insert({
    username: "john",
    email: "[email protected]",
    password: "hashed_password",
    role: "normal",
  })
  .returning({ id: "id", name: "username" });

console.log(inserted);
// [{ id: 1, name: "john" }]

์—ฌ๋Ÿฌ ๋ ˆ์ฝ”๋“œ ์ถ”๊ฐ€

const result = await db.table("users").insert([
  {
    username: "john",
    email: "[email protected]",
    password: "hash1",
    role: "normal",
  },
  {
    username: "jane",
    email: "[email protected]",
    password: "hash2",
    role: "normal",
  },
]);

UPDATE - ๋ฐ์ดํ„ฐ ์ˆ˜์ •

๊ธฐ๋ณธ UPDATE

const count = await db
  .table("users")
  .where("id", 1)
  .update({
    username: "updated_name",
    updated_at: new Date(),
  });

console.log(`${count} rows updated`);
WHERE ์ ˆ ํ•„์ˆ˜: UPDATE๋Š” ๋ฐ˜๋“œ์‹œ WHERE ์กฐ๊ฑด๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์กฐ๊ฑด ์—†์ด ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

increment / decrement

์ˆซ์ž ์ปฌ๋Ÿผ์„ ์ฆ๊ฐ€/๊ฐ์†Œ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
await db
  .table("posts")
  .where("id", 1)
  .increment("view_count", 1);

// SQL: UPDATE posts SET view_count = view_count + 1 WHERE id = 1

์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ ๋™์‹œ ์ˆ˜์ •

await db
  .table("users")
  .where("id", 1)
  .update({
    username: "new_name",
    email: "[email protected]",
    updated_at: new Date(),
  });

DELETE - ๋ฐ์ดํ„ฐ ์‚ญ์ œ

๊ธฐ๋ณธ DELETE

const count = await db
  .table("users")
  .where("id", 1)
  .delete();

console.log(`${count} rows deleted`);
WHERE ์ ˆ ํ•„์ˆ˜: DELETE๋„ ๋ฐ˜๋“œ์‹œ WHERE ์กฐ๊ฑด๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ

const count = await db
  .table("users")
  .whereIn("status", ["deleted", "banned"])
  .delete();

LIMIT & OFFSET - ํŽ˜์ด์ง€๋„ค์ด์…˜

LIMIT - ๊ฒฐ๊ณผ ๊ฐœ์ˆ˜ ์ œํ•œ

const users = await db
  .table("users")
  .select({ id: "id", name: "username" })
  .limit(10);

// ์ตœ๋Œ€ 10๊ฐœ๋งŒ ์กฐํšŒ

OFFSET - ๊ฑด๋„ˆ๋›ฐ๊ธฐ

const users = await db
  .table("users")
  .select({ id: "id", name: "username" })
  .limit(10)
  .offset(20);

// 20๊ฐœ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋‹ค์Œ 10๊ฐœ ์กฐํšŒ (21~30๋ฒˆ์งธ)

ํŽ˜์ด์ง€๋„ค์ด์…˜ ์˜ˆ์ œ

function getUsers(page: number, pageSize: number) {
  return db
    .table("users")
    .select({ id: "id", name: "username" })
    .limit(pageSize)
    .offset((page - 1) * pageSize);
}

// 1ํŽ˜์ด์ง€ (1~10)
await getUsers(1, 10);

// 2ํŽ˜์ด์ง€ (11~20)
await getUsers(2, 10);

ORDER BY - ์ •๋ ฌ

๊ธฐ๋ณธ ์ •๋ ฌ

const users = await db
  .table("users")
  .select({ id: "id", name: "username" })
  .orderBy("created_at", "asc");

์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ ์ •๋ ฌ

const users = await db
  .table("users")
  .select({ id: "id", name: "username", age: "age" })
  .orderBy("age", "desc")      // 1์ˆœ์œ„: ๋‚˜์ด ๋‚ด๋ฆผ์ฐจ์ˆœ
  .orderBy("created_at", "asc"); // 2์ˆœ์œ„: ์ƒ์„ฑ์ผ ์˜ค๋ฆ„์ฐจ์ˆœ

first() - ๋‹จ์ผ ๊ฒฐ๊ณผ ์กฐํšŒ

first()๋Š” ์ฒซ ๋ฒˆ์งธ ๊ฒฐ๊ณผ๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
const user = await db
  .table("users")
  .select({ id: "id", name: "username" })
  .where("email", "[email protected]")
  .first();

if (user) {
  console.log(user.name); // string
} else {
  console.log("User not found");
}

// ํƒ€์ž…: { id: number; name: string; } | undefined
first()๋Š” ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด undefined๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•˜์„ธ์š”.

pluck() - ๋‹จ์ผ ์ปฌ๋Ÿผ ์ถ”์ถœ

ํŠน์ • ์ปฌ๋Ÿผ์˜ ๊ฐ’๋“ค๋งŒ ๋ฐฐ์—ด๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
const userIds = await db
  .table("users")
  .where("role", "admin")
  .pluck("id");

// [1, 2, 3, 4, 5]
// ํƒ€์ž…: number[]

count() - ๊ฐœ์ˆ˜ ์„ธ๊ธฐ

๋ ˆ์ฝ”๋“œ ๊ฐœ์ˆ˜๋ฅผ ๋น ๋ฅด๊ฒŒ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
const count = await db
  .table("users")
  .where("role", "admin")
  .count();

console.log(`Total admins: ${count}`);
// ํƒ€์ž…: number
count()๋Š” ์ง‘๊ณ„ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹Œ ๊ฐ„๋‹จํ•œ ๊ฐœ์ˆ˜ ์กฐํšŒ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ์ง‘๊ณ„๋Š” Aggregations ์ฐธ๊ณ 

์‹ค์ „ ์˜ˆ์ œ

์‚ฌ์šฉ์ž ๋ชฉ๋ก ์กฐํšŒ API

async findUsers(params: {
  role?: string;
  search?: string;
  page: number;
  pageSize: number;
}) {
  const { role, search, page, pageSize } = params;
  
  let query = this.getPuri("r")
    .table("users")
    .select({
      id: "id",
      username: "username",
      email: "email",
      role: "role",
      createdAt: "created_at",
    });
  
  // ์กฐ๊ฑด ์ถ”๊ฐ€
  if (role) {
    query = query.where("role", role);
  }
  
  if (search) {
    query = query.where("username", "like", `%${search}%`);
  }
  
  // ํŽ˜์ด์ง€๋„ค์ด์…˜
  const users = await query
    .orderBy("created_at", "desc")
    .limit(pageSize)
    .offset((page - 1) * pageSize);
  
  // ์ „์ฒด ๊ฐœ์ˆ˜
  const total = await this.getPuri("r")
    .table("users")
    .where("role", role)
    .count();
  
  return { users, total };
}

๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ API

async createPost(data: {
  title: string;
  content: string;
  userId: number;
}) {
  const inserted = await this.getPuri("w")
    .table("posts")
    .insert({
      title: data.title,
      content: data.content,
      user_id: data.userId,
      status: "draft",
      created_at: new Date(),
    })
    .returning({
      id: "id",
      title: "title",
      createdAt: "created_at",
    });
  
  return inserted[0];
}

์กฐํšŒ์ˆ˜ ์ฆ๊ฐ€

async incrementViewCount(postId: number) {
  await this.getPuri("w")
    .table("posts")
    .where("id", postId)
    .increment("view_count", 1);
}

์ฟผ๋ฆฌ ๋””๋ฒ„๊น…

debug() - SQL ์ถœ๋ ฅ

const users = await db
  .table("users")
  .select({ id: "id" })
  .where("role", "admin")
  .debug(); // ์ฝ˜์†”์— SQL ์ถœ๋ ฅ

// ์ถœ๋ ฅ:
// SELECT "users"."id" AS `id` FROM "users" WHERE "role" = 'admin'

rawQuery() - Knex QueryBuilder ์–ป๊ธฐ

๋‚ด๋ถ€ Knex ์ฟผ๋ฆฌ ๋นŒ๋”์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
const knexQuery = db
  .table("users")
  .select({ id: "id" })
  .rawQuery();

console.log(knexQuery.toQuery());

๋‹ค์Œ ๋‹จ๊ณ„