scalar.constant.ts

스칼라 constant는 가격이나 주소처럼 다른 모델 안에 들어가는 작은 값의 모양을 선언합니다. 그 값에 필드를 더하거나 빼거나 바꿀 때 이 파일을 엽니다.
service, signal, store 파일을 열지 않아도 읽힐 만큼 단순하게 유지합니다. 대부분은 via(), field(), 기본값과 선택 필드 몇 개, 가끔 작은 enum 하나면 충분합니다.
이 페이지에서 쓰는 말
스칼라
가격, 주소, 좌표처럼 다른 모델 안에 함께 저장되는 작은 값 객체입니다.
상위 모델
Price를 가진 Product처럼, 스칼라를 품고 있는 모델입니다.
via()
필드 목록을 클래스로 만들어 주며, akanjs/constant에서 가져옵니다.
field(Type)
값 하나와 그 타입을 선언하고, 기본값 같은 옵션도 여기에 붙입니다.
enumOf()
허용하는 값의 고정 목록을 선언하며, akanjs/base에서 가져옵니다.
클래스는 다섯 개가 아니라 하나
모듈 constant는 레코드를 따로 저장하는 모델을 위한 것이라 클래스를 다섯 개 선언합니다. 스칼라는 따로 저장되는 레코드가 없으니 클래스 하나와 그 enum이면 됩니다:
model.constant.ts
lib/<model>/<model>.constant.ts
  • 클래스 다섯 개: XInput → XObject → LightX → X → XInsight
  • id, createdAt, updatedAt, removedAt 기본 필드가 붙습니다.
  • 독립된 레코드로 저장됩니다.
scalar.constant.ts
lib/__scalar/<scalar>/<scalar>.constant.ts
  • via() 클래스 하나와 그 enum이 전부입니다.
  • 기본 필드가 없습니다.
  • 상위 모델 레코드의 일부로 저장됩니다.

기본 형태

via()에 값마다 field(Type)을 하나씩 돌려주는 함수를 넘기고, 그 결과를 상속한 클래스를 export합니다:
apps/myapp/lib/__scalar/price/price.constant.ts
  • 클래스 이름은 폴더 이름을 PascalCase로 바꾼 것입니다. __scalar/price/는 Price를, __scalar/contactInfo/는 ContactInfo를 export합니다.
  • 쓰는 모델이 아니라 값 자체에 이름을 붙입니다. Price는 상품, 주문, 청구서 어디에나 맞지만, ProductPrice는 그중 하나에 묶여 버립니다.
  • via는 akanjs/constant에서, Int, Float, ID, Any, enumOf는 akanjs/base에서 가져옵니다. String, Boolean, Date는 전역이라 import가 필요 없습니다.
필드 타입
타입은 값이 무엇인지에 맞춰 고릅니다. 마지막 두 줄은 직접 만든 클래스입니다:
StringBooleanDate
문자열, 참/거짓, 날짜와 시각입니다.
Int
개수나 수량 같은 정수입니다.
Float
금액이나 경도처럼 소수가 있는 수입니다.
ID
fileId처럼 다른 레코드의 id입니다.
Any
모양이 정말로 정해지지 않은 값으로, 필드로 적을 수 없을 때만 씁니다.
Map
문자열 key로 값을 찾는 맵이며, 값 타입을 { of: String }처럼 꼭 적습니다.
Currency
같은 파일의 enumOf() 클래스로, 정해진 값 중 하나입니다.
Coordinate
다른 스칼라로, 그 constant 파일에서 가져와 값으로 품습니다.

기본값과 선택 필드

알맞은 시작 값이 있어야 하는 필드에는 기본값을 줍니다. 상위 모델이 그 필드 없이도 완전하다면 .optional()을 붙입니다:
apps/myapp/lib/__scalar/price/price.constant.ts
  • currency의 기본값은 흔히 쓰는 값인 "KRW"입니다. 그래서 새 가격은 채워진 채로 시작합니다.
  • memo는 선택 필드입니다. 모든 가격에 메모가 필요하지는 않기 때문입니다.
새 값의 시작 값
기본값도 .optional()도 없는 필드는 필수입니다. 필드를 어떻게 쓰느냐에 따라 새 값의 시작 값과, 비워 둔 채 상위 모델을 저장할 수 있는지가 정해집니다:
field(String)기본값 ""
필수라서, 비어 있으면 상위 모델의 폼이 저장되지 않습니다.
field(Float)기본값 0
필수지만 0도 값이라 그대로 저장되며, field(Int)도 같습니다.
field(Boolean)기본값 false
필수지만 false도 값이라 그대로 저장됩니다.
field(String, { default: "KRW" })기본값 "KRW"
필수지만 채워진 채로 시작하고, 지우면 저장이 막힙니다.
field(String).optional()기본값 null
선택 필드이며, 빈 문자열은 null로 저장됩니다.
field([String])기본값 []
빈 목록도 올바른 값이라 그대로 저장됩니다.
  • 단순한 값은 그대로, 만들어야 하는 값은 함수로 씁니다. default: 0은 그대로 쓰고, 날짜는 default: () => dayjs()로 써서 새 값마다 자기 시각을 갖게 합니다.
  • default: ""를 주면 String 필드도 선택 필드가 됩니다. note: field(String, { default: "" })는 빈 메모를 받아들입니다.

배열 필드

값이 원래 같은 항목을 여러 개 담는다면 타입을 대괄호로 감쌉니다. 작은 예로, 연락처 하나에는 이메일이 여러 개일 수 있으므로 emails는 field([String])입니다:
apps/myapp/lib/__scalar/contactInfo/contactInfo.constant.ts
  • 배열은 []로 시작합니다. 새 ContactInfo의 emails는 null이 아니라 빈 목록입니다.
  • 어떤 필드 타입이든 배열로 만들 수 있습니다. [Int], [Currency] 같은 enum, [Coordinate] 같은 다른 스칼라도 됩니다.
  • 개수는 minlength와 maxlength로 제한합니다. 배열에서는 상위 모델의 폼이 저장할 수 있는 항목 수를 제한합니다.
  • 스칼라 자체가 여러 개라면 배열은 상위 모델 쪽에 둡니다. 상위 모델이 contacts: field([ContactInfo])로 쓰고, 스칼라는 연락처 하나로 남습니다.

enum 필드

필드에 정해진 값 중 하나만 들어가야 한다면 enumOf()를 씁니다. enum은 같은 파일의 스칼라 클래스 위에 선언합니다:
apps/myapp/lib/__scalar/price/price.constant.ts
  • enum 이름은 클래스 이름을 camelCase로 씁니다. Currency는 "currency", libs/shared의 LeaveType은 "leaveType"입니다.
  • dictionary가 이 이름을 그대로 씁니다. price.dictionary.ts는 .enum<Currency>("currency", …)에서 값마다 레이블을 붙이고, 다른 문자열을 쓰면 타입 에러입니다.
  • 짧고, 바뀌지 않고, 겹치지 않게 짓습니다. 컴포넌트가 l("currency.KRW")로 레이블을 읽으므로 이름을 바꾸면 깨지고, 다른 모델이나 enum과 같은 이름을 써서도 안 됩니다.
  • 값 목록 끝에는 as const를 붙입니다. 빠지면 모든 값이 string으로 넓어져, default: "KRW"가 목록에 있는 값인지 검사하지 못합니다.

작은 헬퍼 메서드

동작이 값 자체에 속한다면 작은 메서드를 클래스에 둘 수 있습니다. 서버 요청, 데이터베이스 호출, 외부 서비스 없이 순수하게 유지합니다.
enum과 헬퍼 하나까지 넣은 파일 전체입니다:
apps/myapp/lib/__scalar/price/price.constant.ts
  • 인스턴스 메서드는 값 하나를 다룹니다. 모델 인스턴스 안의 스칼라도 자기 클래스로 만들어지므로 product.price.isFree()처럼 부를 수 있습니다.
  • 여러 값을 함께 다루면 static 메서드로 둡니다. libs/util의 Coordinate.getDistanceKm(a, b)는 두 좌표 사이 거리를 잽니다.
constant 파일에 쓸 수 없는 것
이 파일은 서버와 브라우저 양쪽에서 실행되므로, lint는 양쪽 규칙을 모두 적용합니다:
import dayjs from "dayjs"
외부 패키지이므로, akanjs/base의 dayjs처럼 re-export된 것을 가져옵니다.
*.service.ts*.document.tssrvkit/db
서버 전용 코드이며, 그 일은 service에 맡깁니다.
*.store.tsui/st
클라이언트 전용 코드이며, 그 일은 store와 컴포넌트에 맡깁니다.
#private
constant 파일에서는 쓸 수 없으니 TypeScript의 private 메서드로 씁니다.
//!
이 주석은 번들에 실려 브라우저까지 가므로, 대신 // FIXME:를 씁니다.
다음에 읽을 곳

MIT 라이선스 하에 배포되었습니다.

내 AI에 이 문서 연결하기

MCPhttps://akanjs.com/mcp
Copyright © 2026 Akan.js 모든 권리 보유.시스템 관리자bassman