๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
Subset์€ Entity์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์–‘ํ•œ ํ˜•ํƒœ๋กœ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•œ ๋ฏธ๋ฆฌ ์ •์˜๋œ ์ฟผ๋ฆฌ ํ…œํ”Œ๋ฆฟ์ž…๋‹ˆ๋‹ค.

Subset ๊ฐœ์š”

์œ ์—ฐํ•œ ์กฐํšŒ

์ƒํ™ฉ์— ๋งž๋Š” ๋ฐ์ดํ„ฐ ํ˜•์‹A, P, SS ๋“ฑ

ํƒ€์ž… ์•ˆ์ „์„ฑ

์ปดํŒŒ์ผ ํƒ€์ž„ ๊ฒ€์ฆ์ž๋™ ํƒ€์ž… ์ถ”๋ก 

๊ด€๊ณ„ ๋กœ๋”ฉ

JOIN๊ณผ Loader ํ†ตํ•ฉ1:N, N:M ์ง€์›

์„ฑ๋Šฅ ์ตœ์ ํ™”

ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ ์„ ํƒN+1 ๋ฌธ์ œ ํ•ด๊ฒฐ

Subset์ด ํ•„์š”ํ•œ ์ด์œ 

๋ฌธ์ œ: ๋‹จ์ผ ์ฟผ๋ฆฌ์˜ ํ•œ๊ณ„

API๋งˆ๋‹ค ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ํ˜•์‹์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค:
// โŒ ๋ฌธ์ œ 1: ๋ชฉ๋ก ์กฐํšŒ - ๋„ˆ๋ฌด ๋งŽ์€ ๋ฐ์ดํ„ฐ
const users = await db.table("users").selectAll();
// โ†’ ๋ชจ๋“  ์ปฌ๋Ÿผ์„ ๊ฐ€์ ธ์˜ด (password, bio, ๋ถˆํ•„์š”ํ•œ ์ •๋ณด ํฌํ•จ)

// โŒ ๋ฌธ์ œ 2: ์ƒ์„ธ ์กฐํšŒ - ๊ด€๊ณ„ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ๋ณต์žก
const user = await db.table("users").where("id", 1).first();
const employee = await db.table("employees").where("user_id", user.id).first();
const department = await db.table("departments").where("id", employee.department_id).first();
// โ†’ N+1 ๋ฌธ์ œ, ์ฝ”๋“œ ์ค‘๋ณต

// โŒ ๋ฌธ์ œ 3: ํƒ€์ž… ์•ˆ์ „์„ฑ ๋ถ€์กฑ
type UserList = {
  id: number;
  username: string;
  role: string;
}; // ์ˆ˜๋™์œผ๋กœ ํƒ€์ž… ์ •์˜ ํ•„์š”

ํ•ด๊ฒฐ: Subset์œผ๋กœ ํ†ตํ•ฉ

// โœ… Subset A: ์ „์ฒด ์ •๋ณด
const admin = await UserModel.findById(1, ["A"]);
// ํƒ€์ž…: { id, created_at, email, username, role, bio, ... }

// โœ… Subset P: ํ”„๋กœํ•„ + ๊ด€๊ณ„
const profile = await UserModel.findById(1, ["P"]);
// ํƒ€์ž…: { id, username, employee: { salary, department: { name } } }

// โœ… Subset SS: ์š”์•ฝ ์ •๋ณด
const summary = await UserModel.findById(1, ["SS"]);
// ํƒ€์ž…: { id, username, role, last_login_at }

Subset์˜ ๊ตฌ์„ฑ ์š”์†Œ

1. SubsetQuery - ๊ธฐ๋ณธ ์ฟผ๋ฆฌ

Entity์—์„œ ์ •์˜๋œ ํ•„๋“œ ์„ ํƒ:
{
  "subsets": {
    "A": ["id", "username", "email", "role"],
    "P": ["id", "username", "employee.department.name"],
    "SS": ["id", "username"]
  }
}

2. LoaderQuery - ๊ด€๊ณ„ ๋กœ๋”ฉ

1:N, N:M ๊ด€๊ณ„ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ:
// Loader ์ •์˜ (์ž๋™ ์ƒ์„ฑ๋จ)
const loaders = {
  employees: {
    as: "employees",
    refId: "department_id",
    qb: (qb, fromIds) => 
      qb.table("employees")
        .whereIn("department_id", fromIds)
        .select({ id: "id", name: "username" }),
  },
};

3. Hydrate - ๊ฒฐ๊ณผ ๋ณ€ํ™˜

Flat ๋ฐ์ดํ„ฐ โ†’ ์ค‘์ฒฉ ๊ฐ์ฒด:
// Flat ๊ฒฐ๊ณผ (DB์—์„œ ์กฐํšŒ)
{
  id: 1,
  username: "john",
  employee__department__name: "Engineering"
}

// โ†“ Hydrate ๋ณ€ํ™˜

// ์ค‘์ฒฉ ๊ฐ์ฒด (์ตœ์ข… ๊ฒฐ๊ณผ)
{
  id: 1,
  username: "john",
  employee: {
    department: {
      name: "Engineering"
    }
  }
}

Subset ๋ช…๋ช… ๊ทœ์น™

์ผ๋ฐ˜์ ์ธ Subset ์ด๋ฆ„ ํŒจํ„ด:
Subset์˜๋ฏธ์‚ฌ์šฉ ์˜ˆ์‹œ
AAll (์ „์ฒด)๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€ ์ƒ์„ธ
PProfile (ํ”„๋กœํ•„)์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์กฐํšŒ
LList (๋ชฉ๋ก)๋ชฉ๋ก API, ํ…Œ์ด๋ธ” ํ–‰
SSSummary (์š”์•ฝ)๋“œ๋กญ๋‹ค์šด, ๊ฐ„๋‹จํ•œ ์ •๋ณด
CCard (์นด๋“œ)์นด๋“œ UI ์ปดํฌ๋„ŒํŠธ
Subset ์ด๋ฆ„์€ ์ž์œ ๋กญ๊ฒŒ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŒ€ ๋‚ด์—์„œ ์ผ๊ด€๋œ ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

์‹ค์ „ ์˜ˆ์ œ

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

// GET /users - ์‚ฌ์šฉ์ž ๋ชฉ๋ก
async list(params: UserListParams) {
  const users = await UserModel.findMany({
    listParams: params,
    subsetKey: "L",  // List Subset ์‚ฌ์šฉ
  });
  
  // ํƒ€์ž…: { id: number; username: string; role: string; created_at: Date }[]
  return users.map(user => ({
    id: user.id,
    username: user.username,
    role: user.role,
    createdAt: user.created_at,
  }));
}

์‚ฌ์šฉ์ž ํ”„๋กœํ•„ API

// GET /users/:id/profile
async getProfile(userId: number) {
  const user = await UserModel.findById(userId, ["P"]);
  
  if (!user) {
    throw new Error("User not found");
  }
  
  // ํƒ€์ž…: {
  //   id: number;
  //   username: string;
  //   employee: {
  //     salary: string;
  //     department: { name: string }
  //   }
  // }
  
  return {
    id: user.id,
    username: user.username,
    department: user.employee?.department?.name,
    salary: user.employee?.salary,
  };
}

Subset vs Raw Puri

Subset ์‚ฌ์šฉ (๊ถŒ์žฅ)

// โœ… Subset ์‚ฌ์šฉ
const user = await UserModel.findById(1, ["P"]);
// - ํƒ€์ž… ์•ˆ์ „
// - ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ
// - ์œ ์ง€๋ณด์ˆ˜ ์‰ฌ์›€

Raw Puri ์‚ฌ์šฉ

// โš ๏ธ Raw Puri (ํŠน์ˆ˜ํ•œ ๊ฒฝ์šฐ๋งŒ)
const user = await UserModel.getPuri("r")
  .table("users")
  .join("employees", "users.id", "employees.user_id")
  .leftJoin("departments", "employees.department_id", "departments.id")
  .select({
    id: "users.id",
    username: "users.username",
    deptName: "departments.name",
  })
  .where("users.id", 1)
  .first();
// - ํƒ€์ž… ์ถ”๋ก  ๋ณต์žก
// - ์ฝ”๋“œ ์ค‘๋ณต
// - Subset์œผ๋กœ ํ‘œํ˜„ ์•ˆ ๋˜๋Š” ํŠน์ˆ˜ ์ฟผ๋ฆฌ๋งŒ ์‚ฌ์šฉ
์–ธ์ œ Raw Puri๋ฅผ ์‚ฌ์šฉํ•˜๋‚˜์š”?
  • Subset์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†๋Š” ๋ณต์žกํ•œ ์ฟผ๋ฆฌ
  • ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•œ ํŠน์ˆ˜ ์ผ€์ด์Šค
  • ์ผํšŒ์„ฑ ๋ฐ์ดํ„ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
์ผ๋ฐ˜์ ์ธ CRUD๋Š” ํ•ญ์ƒ Subset ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

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