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"]
}
}

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"
]
}
}
복사
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
복사
{
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
복사
{
"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 사용 (관계 포함)

실전 예제
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;
};
};

