Sonamuλ Entity μ μλ‘λΆν° λ€μν νμ
μ μλ μμ±ν©λλ€. Base νμ
λΏλ§ μλλΌ API νλΌλ―Έν°, Subset νμ
, Enum λ±μ΄ μλμΌλ‘ μμ±λμ΄ μλ²½ν νμ
μμ μ±μ μ 곡ν©λλ€.
μμ± νμ
κ°μ
Base νμ
Entityμ κΈ°λ³Έ νμ
User, Post, Comment
Subset νμ
λΆλΆ μ‘°ν νμ
UserA, UserP, UserSS
Params νμ
API νλΌλ―Έν° νμ
ListParams, SaveParams
Enum νμ
Enumκ³Ό ν¬νΌ ν¨μ UserRole, userRoleLabel
Base νμ
Entityμ λͺ¨λ νλλ₯Ό ν¬ν¨νλ κΈ°λ³Έ νμ
μ
λλ€.
user.entity.json
user.types.ts (μλ μμ±)
{
"id" : "User" ,
"props" : [
{ "name" : "id" , "type" : "integer" },
{ "name" : "email" , "type" : "string" },
{ "name" : "username" , "type" : "string" },
{ "name" : "created_at" , "type" : "date" }
]
}
νΉμ§ :
Relationμ HasMany, ManyToManyλ μ μΈ
BelongsToOne, OneToOne(hasJoinColumn)μ {name}_id ννλ‘ ν¬ν¨
Virtual νλλ νμ
λ§ μ μ, κ°μ Enhancerμμ κ³μ°
Subset νμ
Entityμ λΆλΆ νλλ§ ν¬ν¨νλ νμ
μ
λλ€.
user.entity.json
sonamu.generated.ts (μλ μμ±)
{
"subsets" : {
"A" : [ "id" , "email" , "username" , "created_at" ],
"P" : [ "id" , "email" , "employee.id" , "employee.department.name" ],
"SS" : [ "id" , "email" ]
}
}
μ¬μ© μμ :
import type { UserSubsetKey , UserSubsetMapping } from "./sonamu.generated" ;
// μ λ€λ¦μΌλ‘ Subset νμ
μ ν
async function findMany < T extends UserSubsetKey >(
subset : T
) : Promise < UserSubsetMapping [ T ][]> {
// ...
}
const users = await findMany ( "A" ); // UserA[] νμ
const profiles = await findMany ( "P" ); // UserP[] νμ
ListParams νμ
λͺ©λ‘ μ‘°ν APIμ νλΌλ―Έν° νμ
μ
λλ€.
user.entity.json
user.types.ts (μλ μμ±)
{
"props" : [
{ "name" : "email" , "type" : "string" , "toFilter" : true },
{ "name" : "username" , "type" : "string" , "toFilter" : true }
]
}
μ¬μ© μμ :
// API νΈμΆ
const result = await findManyUsers ({
num: 20 ,
page: 1 ,
search: "email" ,
keyword: "john" ,
orderBy: "created_at-desc" ,
email: [ "[email protected] " , "[email protected] " ], // λ°°μ΄λ κ°λ₯
});
zArrayable : λ¨μΌ κ°κ³Ό λ°°μ΄μ λͺ¨λ νμ©νλ ν¬νΌμ
λλ€.
SaveParams νμ
μ μ₯ APIμ νλΌλ―Έν° νμ
μ
λλ€.
user.types.ts (μλ μμ±)
μ¬μ© μμ
// SaveParams = Base νμ
μ idλ₯Ό optionalλ‘
export const UserSaveParams = User . partial ({ id: true });
export type UserSaveParams = z . infer < typeof UserSaveParams >;
// λμΌ:
// type UserSaveParams = {
// id?: number;
// email: string;
// username: string;
// created_at: Date;
// }
νΉμ§ :
idλ§ optional, λλ¨Έμ§ νλλ νμ
idκ° μμΌλ©΄ UPDATE, μμΌλ©΄ INSERT
λ°°μ΄λ‘ μ¬λ¬ λ μ½λ λμ μ μ₯ κ°λ₯
Enum νμ
Entityμ Enumμ΄ μλμΌλ‘ μμ±λ©λλ€.
user.entity.json
sonamu.generated.ts (μλ μμ±)
{
"enums" : {
"UserRole" : {
"admin" : "κ΄λ¦¬μ" ,
"moderator" : "μ΄μμ" ,
"normal" : "μΌλ° μ¬μ©μ"
}
}
}
μ¬μ© μμ :
import { UserRole , userRoleLabel } from "./sonamu.generated" ;
// νμ
μμ ν Enum μ¬μ©
const role : UserRole = "admin" ;
// λΌλ²¨ νμ
console . log ( userRoleLabel ( role )); // "κ΄λ¦¬μ"
// UIμμ μ ν μ΅μ
μμ±
const options = UserRole . options . map (( value ) => ({
value ,
label: userRoleLabel ( value ),
}));
// [
// { value: "admin", label: "κ΄λ¦¬μ" },
// { value: "moderator", label: "μ΄μμ" },
// { value: "normal", label: "μΌλ° μ¬μ©μ" }
// ]
컀μ€ν
Params νμ
Modelμμ μΆκ° APIλ₯Ό λ§λ€ λ 컀μ€ν
νλΌλ―Έν° νμ
μ μ μν©λλ€.
user.types.ts
user.model.ts
// 컀μ€ν
νλΌλ―Έν° μΆκ°
export const UserLoginParams = z . object ({
email: z . string (). email (),
password: z . string (). min ( 8 ),
rememberMe: z . boolean (). optional (),
});
export type UserLoginParams = z . infer < typeof UserLoginParams >;
export const UserRegisterParams = User . pick ({
email: true ,
username: true ,
}). extend ({
password: z . string (). min ( 8 ),
passwordConfirm: z . string (). min ( 8 ),
}). refine (
( data ) => data . password === data . passwordConfirm ,
{ message: "λΉλ°λ²νΈκ° μΌμΉνμ§ μμ΅λλ€" , path: [ "passwordConfirm" ] }
);
export type UserRegisterParams = z . infer < typeof UserRegisterParams >;
μλ μμ± :
// services.generated.ts
export async function loginUser (
params : UserLoginParams
) : Promise <{ user : User ; token : string }> {
const { data } = await axios . post ( "/user/login" , { params });
return data ;
}
export function useLoginUser () {
return useMutation ({
mutationFn : ( params : UserLoginParams ) => loginUser ( params ),
});
}
Action Params νμ
νΉμ μ‘μ
μ μν νλΌλ―Έν° νμ
μ
λλ€.
user.types.ts
user.model.ts
// ID λ°°μ΄ νλΌλ―Έν°
export const UserBulkDeleteParams = z . object ({
ids: z . number (). int (). array (). min ( 1 ),
});
export type UserBulkDeleteParams = z . infer < typeof UserBulkDeleteParams >;
// μν λ³κ²½ νλΌλ―Έν°
export const UserStatusUpdateParams = z . object ({
ids: z . number (). int (). array (). min ( 1 ),
status: z . enum ([ "active" , "suspended" , "deleted" ]),
reason: z . string (). optional (),
});
export type UserStatusUpdateParams = z . infer < typeof UserStatusUpdateParams >;
ListResult νμ
λͺ©λ‘ μ‘°ν κ²°κ³Όμ νμ
μ
λλ€.
export type ListResult <
LP extends { num ?: number ; page ?: number ; queryMode ?: string },
T
> = LP [ "queryMode" ] extends "list"
? { rows : T [] }
: LP [ "queryMode" ] extends "count"
? { total : number }
: { rows : T []; total : number };
μ¬μ© μμ :
// queryModeμ λ°λΌ νμ
μ΄ μλμΌλ‘ κ²°μ λ¨
// queryMode: "both" (κΈ°λ³Έκ°)
const result = await findManyUsers ({ num: 10 , page: 1 });
// ListResult<UserListParams, User> = { rows: User[]; total: number }
// queryMode: "list"
const result = await findManyUsers ({ num: 10 , page: 1 , queryMode: "list" });
// ListResult<UserListParams, User> = { rows: User[] }
// queryMode: "count"
const result = await findManyUsers ({ queryMode: "count" });
// ListResult<UserListParams, User> = { total: number }
μ‘°κ±΄λΆ νμ
: ListResultλ TypeScriptμ μ‘°κ±΄λΆ νμ
μ νμ©νμ¬
queryModeμ λ°λΌ λ€λ₯Έ νμ
μ λ°νν©λλ€.
νμ
λ΄λ³΄λ΄κΈ°
μμ±λ νμ
μ μλμΌλ‘ exportλ©λλ€.
// Entityλ³ νμ
μ λΆ re-export
export * from "./user/user.model" ;
export * from "./post/post.model" ;
export * from "./comment/comment.model" ;
// Model μΈμ€ν΄μ€λ export
export { UserModel } from "./user/user.model" ;
export { PostModel } from "./post/post.model" ;
μ¬μ© :
// ν κ³³μμ import
import {
User ,
UserListParams ,
UserSaveParams ,
UserRole ,
} from "./sonamu.generated" ;
// λλ κ°λ³ import
import type { User } from "./user/user.types" ;
import { UserModel } from "./user/user.model" ;
νμ
μ νΈλ¦¬ν°
Sonamuκ° μ 곡νλ μ μ©ν νμ
μ νΈλ¦¬ν°μ
λλ€.
zArrayable
λ¨μΌ κ°κ³Ό λ°°μ΄μ λͺ¨λ νμ©νλ νμ
μ
λλ€.
import { zArrayable } from "sonamu" ;
const UserListParams = z . object ({
id: zArrayable ( z . number (). int ()). optional (),
email: zArrayable ( z . string ()). optional (),
});
// λ λ€ κ°λ₯
{
id : 1 ;
}
{
id : [ 1 , 2 , 3 ];
}
DistributiveOmit
μΌλ° Omitλ³΄λ€ Union νμ
μμ μμ ν©λλ€.
import type { DistributiveOmit } from "sonamu" ;
type UserOrAdmin =
| { type : "user" ; userId : number }
| { type : "admin" ; adminId : number };
// DistributiveOmitμ κ° νμ
μ κ°λ³ μ μ©
type WithoutId = DistributiveOmit < UserOrAdmin , "userId" | "adminId" >;
// = { type: "user" } | { type: "admin" }
νμ
μμ± μ»€μ€ν°λ§μ΄μ§
μλ μμ±λ νμ
μ νμ₯νκ±°λ μμ ν μ μμ΅λλ€.
Params νμ₯
// κΈ°λ³Έ ListParams νμ₯
export const ExtendedUserListParams = UserListParams . extend ({
includeDeleted: z . boolean (). optional (),
dateRange: z
. object ({
from: z . date (),
to: z . date (),
})
. optional (),
});
export type ExtendedUserListParams = z . infer < typeof ExtendedUserListParams >;
Subset νμ
νμ₯
// Subset νμ
μ κ³μ° νλ μΆκ°
export type UserAWithStats = UserA & {
post_count : number ;
follower_count : number ;
};
μ‘°κ±΄λΆ νμ
μμ±
// μν μ λ°λΌ λ€λ₯Έ νμ
export type UserByRole < T extends UserRole > = T extends "admin"
? UserA & { permissions : string [] }
: T extends "normal"
? UserSS
: User ;
μ€μ μμ
μ€μ νλ‘μ νΈμμ μμ± νμ
μ νμ©νλ μμμ
λλ€.
user.model.ts
UserList.tsx
import type {
UserSubsetKey ,
UserSubsetMapping ,
UserListParams ,
UserSaveParams ,
UserLoginParams ,
} from "./user.types" ;
class UserModelClass extends BaseModelClass <
UserSubsetKey ,
UserSubsetMapping ,
typeof userSubsetQueries ,
typeof userLoaderQueries
> {
> @ api ({ httpMethod: "GET" })
> async findMany < T extends UserSubsetKey >(
subset : T ,
params ?: UserListParams
) : Promise < ListResult < UserListParams , UserSubsetMapping [ T ]>> {
// νμ
μμ ν ꡬν
}
@ api ({ httpMethod: "POST" })
async save ( params : UserSaveParams []) : Promise < number []> {
// νμ
μμ ν ꡬν
}
@ api ({ httpMethod: "POST" })
async login ( params : UserLoginParams ) : Promise <{ user : User ; token : string }> {
// νμ
μμ ν ꡬν
}
}
λ€μ λ¨κ³