메인 콘텐츠로 건너뛰기
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} 패턴 사용
    • 예: $ModelRoleUserRole (자동 변환)
4

Enum 값 추가

각 Enum은 별도 탭으로 표시되며, “Add Row” 버튼으로 키-레이블 쌍을 추가합니다.
  • Key: 영문 소문자, 숫자, 언더스코어
  • Label: 한글 또는 표시명
5

저장

Entity를 저장하면 Enum이 함께 저장됩니다.

📸 필요: Sonamu UI의 Enums 섹션 - 여러 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)
별도 테이블 사용 예시:
  • 지역 목록 (서울, 부산, …)
  • 상품 카테고리 (계층 구조)
  • 국가 코드 (추가 정보 많음)

다음 단계