๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
Subset์€ Entity JSON ํŒŒ์ผ์˜ subsets ๊ฐ์ฒด์—์„œ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ Subset ์ •์˜

Entity ๊ตฌ์กฐ

{
  "id": "User",
  "table": "users",
  "props": [
    { "name": "id", "type": "integer" },
    { "name": "username", "type": "string" },
    { "name": "email", "type": "string" },
    { "name": "role", "type": "enum", "id": "UserRole" }
  ],
  "subsets": {
    "A": ["id", "username", "email", "role"],
    "SS": ["id", "username"]
  }
}
Entity์—์„œ Subset ์ •์˜ ์˜ˆ์‹œ

Subset ์ •์˜ ๊ทœ์น™

{
  "subsets": {
    "A": [
      "id",
      "created_at",
      "username",
      "email",
      "role"
    ]
  }
}

๊ด€๊ณ„ ํ•„๋“œ ์„ ํƒ

OneToOne ๊ด€๊ณ„

{
  "props": [
    { "name": "id", "type": "integer" },
    { "name": "username", "type": "string" },
    {
      "type": "relation",
      "name": "employee",
      "with": "Employee",
      "relationType": "OneToOne"
    }
  ],
  "subsets": {
    "P": [
      "id",
      "username",
      "employee.id",
      "employee.employee_number",
      "employee.salary"
    ]
  }
}
OneToOne ๊ด€๊ณ„๋Š” ์ž๋™์œผ๋กœ LEFT JOIN๋ฉ๋‹ˆ๋‹ค. employee.id๋ฅผ ์„ ํƒํ•˜๋ฉด employees ํ…Œ์ด๋ธ”์ด ์กฐ์ธ๋ฉ๋‹ˆ๋‹ค.

์ค‘์ฒฉ ๊ด€๊ณ„ (2๋‹จ๊ณ„ ์ด์ƒ)

{
  "subsets": {
    "P": [
      "id",
      "username",
      "employee.salary",
      "employee.department.name",
      "employee.department.company.name"
    ]
  }
}
SQL ๊ฒฐ๊ณผ:
SELECT
  users.id,
  users.username,
  employees.salary AS employee__salary,
  departments.name AS employee__department__name,
  companies.name AS employee__department__company__name
FROM users
LEFT JOIN employees ON users.id = employees.user_id
LEFT JOIN departments ON employees.department_id = departments.id
LEFT JOIN companies ON departments.company_id = companies.id
Hydrate ๊ฒฐ๊ณผ:
{
  id: 1,
  username: "john",
  employee: {
    salary: "70000",
    department: {
      name: "Engineering",
      company: {
        name: "Tech Corp"
      }
    }
  }
}

Subset ๋„ค์ด๋ฐ ์ „๋žต

์ผ๋ฐ˜์ ์ธ ๊ทœ์น™

{
  "subsets": {
    "A": [...],   // All - ๋ชจ๋“  ํ•„๋“œ (๊ด€๋ฆฌ์ž ์ƒ์„ธ)
    "L": [...],   // List - ๋ชฉ๋ก์šฉ (ํ…Œ์ด๋ธ” ํ–‰)
    "P": [...],   // Profile - ํ”„๋กœํ•„ (๊ด€๊ณ„ ํฌํ•จ)
    "SS": [...],  // Summary - ์š”์•ฝ (๋“œ๋กญ๋‹ค์šด, ํƒœ๊ทธ)
    "C": [...]    // Card - ์นด๋“œ UI
  }
}
Subset ๋ช…๋ช… ๊ทœ์น™

๋„๋ฉ”์ธ๋ณ„ Subset

{
  "subsets": {
    "AdminList": ["id", "username", "email", "role", "created_at"],
    "UserCard": ["id", "username", "role"],
    "ProfileView": ["id", "username", "bio", "employee.department.name"]
  }
}
๋ช…๋ช… ํŒ:
  • ์งง๊ณ  ๋ช…ํ™•ํ•œ ์ด๋ฆ„ ์‚ฌ์šฉ (A, L, P ๋“ฑ)
  • ํŒ€ ๋‚ด์—์„œ ์ผ๊ด€๋œ ๊ทœ์น™ ์œ ์ง€
  • ๋„๋ฉ”์ธ๋ณ„๋กœ ์˜๋ฏธ ์žˆ๋Š” ์ด๋ฆ„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

Subset ์„ค๊ณ„ ์›์น™

1. ์ตœ์†Œ ํ•„๋“œ ์›์น™

// โŒ ๋‚˜์จ: ๋ถˆํ•„์š”ํ•œ ํ•„๋“œ ํฌํ•จ
{
  "L": [
    "id", "created_at", "updated_at", "deleted_at",
    "username", "email", "password", "bio",
    "last_login_at", "is_verified"
  ]
}

// โœ… ์ข‹์Œ: ๋ชฉ๋ก์— ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ
{
  "L": [
    "id",
    "username",
    "role",
    "created_at"
  ]
}

2. ์šฉ๋„๋ณ„ ๋ถ„๋ฆฌ

{
  "subsets": {
    // ๋ชฉ๋ก ์กฐํšŒ์šฉ
    "L": ["id", "username", "role", "created_at"],
    
    // ์ƒ์„ธ ์กฐํšŒ์šฉ (๊ด€๊ณ„ ํฌํ•จ)
    "P": [
      "id", "username", "email", "bio",
      "employee.salary",
      "employee.department.name"
    ],
    
    // ๋“œ๋กญ๋‹ค์šด์šฉ
    "SS": ["id", "username"]
  }
}

3. ์„ฑ๋Šฅ ๊ณ ๋ ค

// โœ… ์ข‹์Œ: ํ•„์š”ํ•œ ๊ด€๊ณ„๋งŒ JOIN
{
  "P": [
    "id",
    "username",
    "employee.department.name"
  ]
}

// โŒ ๋‚˜์จ: ๋ถˆํ•„์š”ํ•œ JOIN
{
  "P": [
    "id",
    "username",
    "employee.id",
    "employee.company.id",
    "employee.projects.id"
  ]
}
์„ฑ๋Šฅ ํŒ:
  • JOIN์€ ์ตœ์†Œํ•œ์œผ๋กœ (ํ•„์š”ํ•œ ๊ด€๊ณ„๋งŒ)
  • ๋ชฉ๋ก ์กฐํšŒ๋Š” L Subset ์‚ฌ์šฉ (๊ด€๊ณ„ ์ œ์™ธ)
  • ์ƒ์„ธ ์กฐํšŒ๋งŒ P Subset ์‚ฌ์šฉ (๊ด€๊ณ„ ํฌํ•จ)
Subset ์„ฑ๋Šฅ ๋น„๊ต

์‹ค์ „ ์˜ˆ์ œ

User Entity

{
  "id": "User",
  "table": "users",
  "props": [
    { "name": "id", "type": "integer" },
    { "name": "created_at", "type": "date" },
    { "name": "username", "type": "string" },
    { "name": "email", "type": "string" },
    { "name": "role", "type": "enum", "id": "UserRole" },
    { "name": "bio", "type": "string", "nullable": true },
    {
      "type": "relation",
      "name": "employee",
      "with": "Employee",
      "relationType": "OneToOne",
      "nullable": true
    }
  ],
  "subsets": {
    "A": [
      "id", "created_at", "username", "email",
      "role", "bio"
    ],
    "L": [
      "id", "username", "role", "created_at"
    ],
    "P": [
      "id", "username", "email", "bio",
      "employee.salary",
      "employee.department.name"
    ],
    "SS": [
      "id", "username"
    ]
  }
}

Code Generation

Subset์„ ์ •์˜ํ•˜๋ฉด ์ž๋™์œผ๋กœ ํƒ€์ž…์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค:
# Subset ๋ณ€๊ฒฝ ํ›„ ์ฝ”๋“œ ์žฌ์ƒ์„ฑ
pnpm sonamu generate
Subset์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฐ˜๋“œ์‹œ Code Generation์„ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์•ผ TypeScript ํƒ€์ž…์ด ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.
์ƒ์„ฑ๋˜๋Š” ํƒ€์ž…:
// sonamu.generated.ts

export type UserSubsetKey = "A" | "L" | "P" | "SS";

export type UserSubsetMapping = {
  A: {
    id: number;
    created_at: Date;
    username: string;
    email: string;
    role: UserRole;
    bio: string | null;
  };
  L: {
    id: number;
    username: string;
    role: UserRole;
    created_at: Date;
  };
  P: {
    id: number;
    username: string;
    email: string;
    bio: string | null;
    employee: {
      salary: string;
      department: {
        name: string;
      };
    } | null;
  };
  SS: {
    id: number;
    username: string;
  };
};
์ƒ์„ฑ๋œ TypeScript ํƒ€์ž…

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