메인 μ½˜ν…μΈ λ‘œ κ±΄λ„ˆλ›°κΈ°
Enum은 미리 μ •μ˜λœ κ°’ λͺ©λ‘ 쀑 ν•˜λ‚˜λ§Œ 선택할 수 μžˆλŠ” μ—΄κ±°ν˜• νƒ€μž…μž…λ‹ˆλ‹€. νƒ€μž… μ•ˆμ „μ„±μ„ μ œκ³΅ν•˜κ³  μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€μ—μ„œ 선택 μ˜΅μ…˜μ„ μ‰½κ²Œ ꡬ성할 수 μžˆμŠ΅λ‹ˆλ‹€.

Enumμ΄λž€?

Enum은 μ œν•œλœ κ°’μ˜ 집합을 μ •μ˜ν•˜μ—¬ λ‹€μŒμ„ μ œκ³΅ν•©λ‹ˆλ‹€:

νƒ€μž… μ•ˆμ „μ„±

μ •μ˜λ˜μ§€ μ•Šμ€ κ°’ μž…λ ₯ λ°©μ§€TypeScriptμ—μ„œ Union νƒ€μž…μœΌλ‘œ μžλ™ λ³€ν™˜

UI μžλ™ 생성

선택 λͺ©λ‘ μžλ™ ꡬ성Select, ButtonSet μ»΄ν¬λ„ŒνŠΈμ— μ¦‰μ‹œ μ‚¬μš©

λ ˆμ΄λΈ” 관리

킀와 ν‘œμ‹œλͺ… 뢄리닀ꡭ어 지원 용이

μœ μ§€λ³΄μˆ˜μ„±

쀑앙 집쀑식 관리값 λ³€κ²½ μ‹œ ν•œ 곳만 μˆ˜μ •

Enum μ •μ˜ν•˜κΈ°

entity.jsonμ—μ„œ μ •μ˜

Entity의 enums μ„Ήμ…˜μ— ν‚€-λ ˆμ΄λΈ” 쌍으둜 μ •μ˜ν•©λ‹ˆλ‹€.
user.entity.json
{
  "id": "User",
  "props": [
    {
      "name": "role",
      "type": "enum",
      "id": "UserRole",
      "desc": "μ‚¬μš©μž μ—­ν• "
    },
    {
      "name": "status",
      "type": "enum",
      "id": "UserStatus",
      "desc": "계정 μƒνƒœ"
    }
  ],
  "enums": {
    "UserRole": {
      "admin": "κ΄€λ¦¬μž",
      "normal": "일반 μ‚¬μš©μž",
      "guest": "게슀트"
    },
    "UserStatus": {
      "active": "ν™œμ„±",
      "inactive": "λΉ„ν™œμ„±",
      "suspended": "μ •μ§€",
      "deleted": "μ‚­μ œλ¨"
    }
  }
}
ꡬ쑰:
  • Enum ID: PascalCase (예: UserRole, PostStatus)
  • Key: μ†Œλ¬Έμž, 숫자, μ–Έλ”μŠ€μ½”μ–΄ (예: admin, draft_saved)
  • Label: ν‘œμ‹œμš© ν…μŠ€νŠΈ, ν•œκΈ€ κ°€λŠ₯ (예: β€œκ΄€λ¦¬μžβ€, β€œμž„μ‹œμ €μž₯”)

Sonamu UIμ—μ„œ μ •μ˜

1

Entity νŽΈμ§‘ νŽ˜μ΄μ§€λ‘œ 이동

Sonamu UIμ—μ„œ Entityλ₯Ό μ„ νƒν•©λ‹ˆλ‹€.
2

Enums μ„Ήμ…˜μœΌλ‘œ 슀크둀

Props, Indexes, Subsets μ•„λž˜μ— Enums μ„Ήμ…˜μ΄ μžˆμŠ΅λ‹ˆλ‹€.
3

Add Enum 클릭

μƒˆ Enum을 μΆ”κ°€ν•©λ‹ˆλ‹€.Enum ID μž…λ ₯:
  • PascalCase둜 μž…λ ₯
  • Entity와 μ—°κ΄€μ‹œν‚€λ €λ©΄ {Entity} νŒ¨ν„΄ μ‚¬μš©
    • 예: $ModelRole β†’ UserRole (μžλ™ λ³€ν™˜)
4

Enum κ°’ μΆ”κ°€

각 Enum은 별도 νƒ­μœΌλ‘œ ν‘œμ‹œλ˜λ©°, β€œAdd Row” λ²„νŠΌμœΌλ‘œ ν‚€-λ ˆμ΄λΈ” μŒμ„ μΆ”κ°€ν•©λ‹ˆλ‹€.
  • Key: 영문 μ†Œλ¬Έμž, 숫자, μ–Έλ”μŠ€μ½”μ–΄
  • Label: ν•œκΈ€ λ˜λŠ” ν‘œμ‹œλͺ…
5

μ €μž₯

Entityλ₯Ό μ €μž₯ν•˜λ©΄ Enum이 ν•¨κ»˜ μ €μž₯λ©λ‹ˆλ‹€.

μžλ™ μƒμ„±λ˜λŠ” μ½”λ“œ

Enum을 μ •μ˜ν•˜λ©΄ Sonamuκ°€ μžλ™μœΌλ‘œ λ‹€μŒμ„ μƒμ„±ν•©λ‹ˆλ‹€:

1. Zod μŠ€ν‚€λ§ˆ

sonamu.generated.ts
import { z } from "zod";

// Enum μŠ€ν‚€λ§ˆ
export const UserRole = z.enum(["admin", "normal", "guest"]);

// Enum λ ˆμ΄λΈ”
export const UserRoleLabels = {
  admin: "κ΄€λ¦¬μž",
  normal: "일반 μ‚¬μš©μž",
  guest: "게슀트",
} as const;

2. TypeScript νƒ€μž…

user.types.ts
import { UserRole } from "./sonamu.generated";

// Union νƒ€μž…μœΌλ‘œ μΆ”λ‘ 
export type UserRole = z.infer<typeof UserRole>;
// κ²°κ³Ό: "admin" | "normal" | "guest"

3. λ ˆμ΄λΈ” 헬퍼 ν•¨μˆ˜

sonamu.generated.ts
// λ ˆμ΄λΈ” κ°€μ Έμ˜€κΈ° ν•¨μˆ˜
export function getUserRoleLabel(role: UserRole): string {
  return UserRoleLabels[role];
}

Enum μ‚¬μš©ν•˜κΈ°

Propsμ—μ„œ μ‚¬μš©

{
  "props": [
    {
      "name": "role",
      "type": "enum",
      "id": "UserRole",
      "desc": "μ‚¬μš©μž μ—­ν• ",
      "dbDefault": "normal"
    }
  ]
}
λ°μ΄ν„°λ² μ΄μŠ€:
  • 컬럼 νƒ€μž…: text
  • μ €μž₯ κ°’: Key κ°’ (예: "admin")
TypeScript:
type User = {
  id: number;
  role: "admin" | "normal" | "guest"; // Union νƒ€μž…
};

APIμ—μ„œ μ‚¬μš©

import { UserRole } from "./user.types";

export class UserModel extends BaseModel {
  @api({ httpMethod: "POST" })
  async updateRole(userId: number, role: UserRole) {
    // role은 νƒ€μž… μ•ˆμ „ν•¨
    await this.puri()
      .where("id", userId)
      .update({ role });
      
    return { success: true };
  }
}

λ ˆμ΄λΈ” ν‘œμ‹œ

import { getUserRoleLabel } from "./sonamu.generated";

const user = await UserModel.findById(1);
const roleLabel = getUserRoleLabel(user.role);
// "κ΄€λ¦¬μž"

자주 μ‚¬μš©λ˜λŠ” Enum νŒ¨ν„΄

1. OrderBy Enum

μ •λ ¬ μ˜΅μ…˜μ„ μ •μ˜ν•©λ‹ˆλ‹€.
{
  "enums": {
    "UserOrderBy": {
      "id-desc": "ID μ΅œμ‹ μˆœ",
      "id-asc": "ID 였래된순",
      "created_at-desc": "등둝일 μ΅œμ‹ μˆœ",
      "created_at-asc": "등둝일 였래된순",
      "username-asc": "μ΄λ¦„μˆœ"
    }
  }
}
ν™œμš©:
async findAll(orderBy: UserOrderBy = "id-desc") {
  const [field, direction] = orderBy.split("-");
  return this.puri()
    .orderBy(field, direction as "asc" | "desc")
    .many();
}

2. SearchField Enum

검색 κ°€λŠ₯ν•œ ν•„λ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
{
  "enums": {
    "UserSearchField": {
      "email": "이메일",
      "username": "이름",
      "phone": "μ „ν™”λ²ˆν˜Έ"
    }
  }
}
ν™œμš©:
async search(field: UserSearchField, keyword: string) {
  return this.puri()
    .whereLike(field, `%${keyword}%`)
    .many();
}

3. Status Enum

μƒνƒœλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
{
  "enums": {
    "PostStatus": {
      "draft": "μž„μ‹œμ €μž₯",
      "published": "λ°œν–‰λ¨",
      "archived": "보관됨",
      "deleted": "μ‚­μ œλ¨"
    }
  }
}
ν™œμš©:
async publish(postId: number) {
  await this.puri()
    .where("id", postId)
    .update({ status: "published" });
}

4. Type/Category Enum

λΆ„λ₯˜λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
{
  "enums": {
    "NotificationType": {
      "email": "이메일",
      "sms": "λ¬Έμžλ©”μ‹œμ§€",
      "push": "ν‘Έμ‹œ μ•Œλ¦Ό",
      "in_app": "인앱 μ•Œλ¦Ό"
    }
  }
}

Enum λ°°μ—΄ νƒ€μž…

μ—¬λŸ¬ Enum 값을 λ°°μ—΄λ‘œ μ €μž₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
{
  "props": [
    {
      "name": "permissions",
      "type": "enum[]",
      "id": "Permission",
      "desc": "κΆŒν•œ λͺ©λ‘"
    }
  ],
  "enums": {
    "Permission": {
      "read": "읽기",
      "write": "μ“°κΈ°",
      "delete": "μ‚­μ œ",
      "admin": "κ΄€λ¦¬μž"
    }
  }
}
λ°μ΄ν„°λ² μ΄μŠ€:
  • 컬럼 νƒ€μž…: text[]
  • μ €μž₯ κ°’: ["read", "write"]
TypeScript:
type User = {
  id: number;
  permissions: ("read" | "write" | "delete" | "admin")[];
};
ν™œμš©:
// κΆŒν•œ 체크
function hasPermission(user: User, permission: Permission): boolean {
  return user.permissions.includes(permission);
}

// κΆŒν•œ μΆ”κ°€
async grantPermission(userId: number, permission: Permission) {
  const user = await this.findById(userId);
  if (!user.permissions.includes(permission)) {
    await this.puri()
      .where("id", userId)
      .update({
        permissions: [...user.permissions, permission]
      });
  }
}

ν”„λ‘ νŠΈμ—”λ“œμ—μ„œ Enum μ‚¬μš©

Select μ»΄ν¬λ„ŒνŠΈ

import { UserRoleSelect } from "@/components/UserRoleSelect";

function UserForm() {
  const [role, setRole] = useState<UserRole>("normal");
  
  return (
    <UserRoleSelect
      value={role}
      onChange={setRole}
    />
  );
}

ButtonSet μ»΄ν¬λ„ŒνŠΈ

import { UserStatusButtonSet } from "@/components/UserStatusButtonSet";

function StatusFilter() {
  const [status, setStatus] = useState<UserStatus | null>(null);
  
  return (
    <UserStatusButtonSet
      value={status}
      onChange={setStatus}
      allowNull // 선택 ν•΄μ œ κ°€λŠ₯
    />
  );
}

Enum 검증

Zodλ₯Ό μ‚¬μš©ν•˜μ—¬ μžλ™μœΌλ‘œ Enum 값을 κ²€μ¦ν•©λ‹ˆλ‹€.
import { UserRole } from "./sonamu.generated";

export const UpdateRoleParams = z.object({
  userId: z.number(),
  role: UserRole, // μžλ™ 검증
});

// 잘λͺ»λœ κ°’ μž…λ ₯ μ‹œ
UpdateRoleParams.parse({
  userId: 1,
  role: "invalid_role" // Error!
});

Enum 넀이밍 κ·œμΉ™

ꢌμž₯ νŒ¨ν„΄

μš©λ„νŒ¨ν„΄μ˜ˆμ‹œ
Entity μƒνƒœ{Entity}StatusPostStatus, OrderStatus
Entity νƒ€μž…{Entity}TypeNotificationType, PaymentType
Entity μ—­ν• {Entity}RoleUserRole, MemberRole
μ •λ ¬ μ˜΅μ…˜{Entity}OrderByUserOrderBy, PostOrderBy
검색 ν•„λ“œ{Entity}SearchFieldUserSearchField
μΉ΄ν…Œκ³ λ¦¬{Entity}CategoryProductCategory
κΆŒν•œ{Entity}PermissionUserPermission

$Model νŒ¨ν„΄

Entity 이름을 λ™μ μœΌλ‘œ ν¬ν•¨ν•˜λ €λ©΄ $Model을 μ‚¬μš©ν•©λ‹ˆλ‹€:
{
  "enums": {
    "$ModelRole": {
      "admin": "κ΄€λ¦¬μž",
      "normal": "일반"
    },
    "$ModelStatus": {
      "active": "ν™œμ„±",
      "inactive": "λΉ„ν™œμ„±"
    }
  }
}
λ³€ν™˜ κ²°κ³Ό:
  • User Entity β†’ UserRole, UserStatus
  • Post Entity β†’ PostRole, PostStatus

μ£Όμ˜μ‚¬ν•­

Enum κ°’ λ³€κ²½ μ‹œ 주의이미 λ°μ΄ν„°λ² μ΄μŠ€μ— μ €μž₯된 Enum ν‚€λ₯Ό λ³€κ²½ν•˜λ©΄ κΈ°μ‘΄ 데이터와 λΆˆμΌμΉ˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.λ³€κ²½ 방법:
  1. μƒˆ ν‚€ μΆ”κ°€
  2. 데이터 λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ (κΈ°μ‘΄ ν‚€ β†’ μƒˆ ν‚€)
  3. ꡬ ν‚€ μ‚­μ œ
Key 넀이밍 μ œν•œEnum KeyλŠ” λ‹€μŒλ§Œ μ‚¬μš© κ°€λŠ₯ν•©λ‹ˆλ‹€:
  • μ†Œλ¬Έμž (a-z)
  • 숫자 (0-9)
  • μ–Έλ”μŠ€μ½”μ–΄ (_)
ν•˜μ΄ν”ˆ(-)은 μ‚¬μš© λΆˆκ°€ν•©λ‹ˆλ‹€.
λ ˆμ΄λΈ” 변경은 자유둭게Label은 ν‘œμ‹œμš©μ΄λ―€λ‘œ μ–Έμ œλ“ μ§€ λ³€κ²½ κ°€λŠ₯ν•©λ‹ˆλ‹€. λ°μ΄ν„°λ² μ΄μŠ€μ—λŠ” Key만 μ €μž₯λ©λ‹ˆλ‹€.

Enum vs 별도 ν…Œμ΄λΈ”

κΈ°μ€€Enum별도 ν…Œμ΄λΈ”
κ°’ 개수적음 (< 20개)많음 (> 20개)
λ³€κ²½ λΉˆλ„κ±°μ˜ μ—†μŒμžμ£Ό λ³€κ²½
μΆ”κ°€ μ •λ³΄λΆˆν•„μš”ν•„μš” (μ„€λͺ…, μˆœμ„œ λ“±)
μ„±λŠ₯빠름 (JOIN λΆˆν•„μš”)느림 (JOIN ν•„μš”)
ꢌμž₯ μ‚¬μš©μƒνƒœ, μ—­ν• , νƒ€μž…μΉ΄ν…Œκ³ λ¦¬, μ½”λ“œ ν…Œμ΄λΈ”
Enum μ‚¬μš© μ˜ˆμ‹œ:
  • μ‚¬μš©μž μ—­ν•  (admin, user, guest)
  • κ²Œμ‹œκΈ€ μƒνƒœ (draft, published, deleted)
  • μ•Œλ¦Ό νƒ€μž… (email, sms, push)
별도 ν…Œμ΄λΈ” μ‚¬μš© μ˜ˆμ‹œ:
  • μ§€μ—­ λͺ©λ‘ (μ„œμšΈ, λΆ€μ‚°, …)
  • μƒν’ˆ μΉ΄ν…Œκ³ λ¦¬ (계측 ꡬ쑰)
  • κ΅­κ°€ μ½”λ“œ (μΆ”κ°€ 정보 많음)

λ‹€μŒ 단계