Skip to Content
API 레퍼런스데이터 모델링

데이터 모델링

ASAPJS 데이터 모델링은 두 가지 상호 보완적인 추상화를 기반으로 합니다. 클래스를 Sequelize 모델로 변환하는 @Table 데코레이터와, 컬럼 메타데이터, Swagger 스키마, 런타임 타입 변환 규칙을 하나의 데코레이터 호출로 정의하는 TypeIs 타입 시스템이 그것입니다.

@Table 데코레이터

@asapjs/sequelize에서 임포트합니다:

import { Table } from '@asapjs/sequelize';

시그니처

function Table(options: ModelOptions): ClassDecorator

ModelOptions는 Sequelize 고유의 타입입니다. 이 데코레이터는 Sequelize 모델 옵션 객체 전체를 받아 여러 가지 자동 동작을 추가로 적용합니다.

옵션

옵션타입기본값설명
tableNamestring클래스 이름실제 데이터베이스 테이블 이름입니다.
timestampsbooleantruetrue이거나 생략하면 created_atupdated_at 컬럼이 자동으로 추가됩니다. 비활성화하려면 false로 설정하세요.
charsetstring'utf8mb4'항상 utf8mb4로 강제됩니다 — 변경할 수 없습니다.
collatestring'utf8mb4_general_ci'항상 utf8mb4_general_ci로 강제됩니다 — 변경할 수 없습니다.

그 외 모든 ModelOptions 필드(예: paranoid, underscored, indexes)는 Sequelize에 그대로 전달됩니다.

자동 타임스탬프

timestampsfalse로 설정되지 않으면 데코레이터가 다음을 추가합니다:

컬럼매핑 필드Sequelize 옵션
created_atcreatedAttimestamps: true
updated_atupdatedAttimestamps: true

컬럼 이름은 Sequelize의 기본 camelCase 별칭이 아닌 snake_case(created_at, updated_at)를 사용합니다.

데코레이터 내부 동작

  1. getTypesData를 통해 클래스의 모든 TypeIs 데코레이터가 적용된 필드를 읽습니다.
  2. 각 필드를 addAttribute로 Sequelize 컬럼 속성으로 등록합니다(FOREIGNKEY / BELONGSTO 타입의 경우 addForeignKey / addAssociation 사용).
  3. timestamps !== false이면 created_at / updated_at 타임스탬프 옵션을 추가합니다.
  4. DBML 생성 및 콘솔 출력을 위한 테이블 메타데이터를 등록합니다.
  5. sequelize-typescriptsetModelNameaddOptions를 호출하여 모델 등록을 완료합니다.

사용법

import { Model } from 'sequelize-typescript'; import { Table, TypeIs } from '@asapjs/sequelize'; @Table({ tableName: 'users', timestamps: true, }) export default class UsersTable extends Model { @TypeIs.INT({ primaryKey: true, autoIncrement: true, comment: 'User ID' }) id: number; @TypeIs.STRING({ unique: true, comment: 'Email (unique)' }) email: string; @TypeIs.PASSWORD({ comment: 'Password (bcrypt)' }) password: string; @TypeIs.STRING({ comment: 'Display name' }) name: string; @TypeIs.DATETIME({ comment: 'Created at' }) created_at: Date; @TypeIs.DATETIME({ comment: 'Updated at' }) updated_at: Date; }

타임스탬프를 비활성화하려면:

@Table({ tableName: 'audit_logs', timestamps: false }) export default class AuditLogsTable extends Model { @TypeIs.STRING({ comment: 'Log message' }) message: string; }

TypeIs 타입 시스템

TypeIs는 하나의 데코레이터 호출로 세 가지를 인코딩하는 통합 타입 시스템입니다:

  • Sequelize 컬럼 정의 (toSequelize())
  • OpenAPI/Swagger 스키마 (toSwagger())
  • 런타임 값 변환 함수 (fixValue())

@asapjs/sequelize에서 임포트합니다:

import { TypeIs } from '@asapjs/sequelize';

이중 사용법: 데코레이터 vs. 값

모든 TypeIs.* 함수는 두 가지 방식으로 동작합니다.

프로퍼티 데코레이터로 (엔티티 또는 DTO 클래스에서):

@TypeIs.STRING({ comment: 'Email address' }) email: string;

일반 값으로 (라우트 IOptions 또는 복합 타입의 인수로):

// 라우트 응답 옵션 response: TypeIs.BOOLEAN() // 타입 조합 TypeIs.ARRAY(TypeIs.INT()) TypeIs.PAGING(UserInfoDto)

TypeIs 전체 레퍼런스

숫자 타입

TypeIs.INT
TypeIs.INT(options?: { decimals?: number; precision?: number; scale?: number; comment?: string; // + Sequelize ModelAttributeColumnOptions (primaryKey, autoIncrement, unique, allowNull, defaultValue, …) })
항목
Sequelize 타입DataTypes.INTEGER
Swagger 타입integer / 포맷 int32
fixValueparseInt(String(o), 10) — 32비트 정수로 변환; null/undefined는 그대로 반환
엔티티
DTO
라우트 옵션
@TypeIs.INT({ primaryKey: true, autoIncrement: true, comment: 'User ID' }) id: number;

TypeIs.BIGINT
TypeIs.BIGINT(options?: { decimals?: number; precision?: number; scale?: number; comment?: string; })
항목
Sequelize 타입DataTypes.BIGINT
Swagger 타입integer / 포맷 int32
fixValueparseInt(String(o), 10)
엔티티
DTO
라우트 옵션

TypeIs.LONG
TypeIs.LONG(options?: { decimals?: number; precision?: number; scale?: number; comment?: string; })
항목
Sequelize 타입DataTypes.INTEGER
Swagger 타입integer / 포맷 int64
fixValueparseInt(String(o), 10)
엔티티
DTO
라우트 옵션

Swagger 포맷이 int64여야 하는 경우(예: Unix 타임스탬프) INT 대신 LONG을 사용하세요.


TypeIs.FLOAT
TypeIs.FLOAT(options?: { length?: number; decimals?: number; comment?: string; })
항목
Sequelize 타입DataTypes.FLOAT(length, decimals)
Swagger 타입integer / 포맷 float
fixValueparseFloat(String(o))
엔티티
DTO
라우트 옵션

TypeIs.DECIMAL
TypeIs.DECIMAL(options: { precision: number; // 필수 scale: number; // 필수 comment?: string; })
항목
Sequelize 타입DataTypes.DECIMAL({ precision, scale })
Swagger 타입integer / 포맷 float
fixValueparseFloat(String(o))
엔티티
DTO
라우트 옵션

DataTypes.DECIMAL은 컬럼 타입을 정확하게 설정하기 위해 precisionscale이 반드시 필요합니다.

@TypeIs.DECIMAL({ precision: 10, scale: 2, comment: 'Price' }) price: number;

TypeIs.DOUBLE
TypeIs.DOUBLE(options?: { length?: number; decimals?: number; comment?: string; })
항목
Sequelize 타입DataTypes.DOUBLE(length, decimals)
Swagger 타입number / 포맷 double
fixValueparseFloat(String(o))
엔티티
DTO
라우트 옵션

문자열 타입

TypeIs.STRING
TypeIs.STRING(options?: { length?: number; binary?: boolean; comment?: string; })
항목
Sequelize 타입DataTypes.STRING(length, binary)
Swagger 타입string
fixValueString(o) — 임의의 값을 문자열로 변환; null/undefined는 그대로 반환
엔티티
DTO
라우트 옵션

기본 STRING은 대부분의 데이터베이스에서 VARCHAR(255)로 매핑됩니다.

@TypeIs.STRING({ unique: true, comment: 'Email (unique)' }) email: string;

TypeIs.TEXT
TypeIs.TEXT(options?: { length?: TextLength; // 'tiny' | 'medium' | 'long' | undefined comment?: string; })
항목
Sequelize 타입DataTypes.TEXT({ length })
Swagger 타입string
fixValueString(o) (STRING과 동일)
엔티티
DTO
라우트 옵션

VARCHAR(255)로 부족한 긴 텍스트 콘텐츠(블로그 본문, 설명 등)에는 TEXT를 사용하세요.

@TypeIs.TEXT({ comment: 'Post content' }) content: string;

TypeIs.PASSWORD
TypeIs.PASSWORD(options?: { comment?: string; })
항목
Sequelize 타입DataTypes.STRING(512)
Swagger 포맷string / 포맷 password
fixValue없음 (변환 없음 — 패스워드 값을 그대로 반환)
엔티티
DTO
라우트 옵션

PASSWORD는 데이터베이스에 항상 STRING(512)로 저장됩니다. Swagger에서는 format: 'password'로 렌더링되어 UI 클라이언트가 입력 필드를 마스킹합니다.

@TypeIs.PASSWORD({ comment: 'Hashed password (bcrypt)' }) password: string;

특수 값 타입

TypeIs.ENUM
TypeIs.ENUM(options: { values: string[]; // 필수 — 허용 값 목록 comment?: string; })
항목
Sequelize 타입DataTypes.ENUM({ values })
Swagger 타입string with enum 배열
fixValuevalues에 존재하면 해당 값 반환, 없으면 undefined
엔티티
DTO
라우트 옵션
enum UserRole { ADMIN = 'ADMIN', USER = 'USER' } @TypeIs.ENUM({ values: Object.keys(UserRole), comment: 'User role', }) role: UserRole;

TypeIs.JSON
TypeIs.JSON(options?: { comment?: string; })
항목
Sequelize 타입DataTypes.JSON
Swagger 타입object
fixValue값이 문자열이면 JSON.parse 시도; 그렇지 않으면 그대로 반환. 파싱 실패 시 원본 문자열 반환.
엔티티
DTO
라우트 옵션
@TypeIs.JSON({ comment: 'Arbitrary metadata' }) metadata: Record<string, unknown>;

TypeIs.BASE64
TypeIs.BASE64(options?: { length?: BlobSize; // 'tiny' | 'medium' | 'long' | undefined comment?: string; })
항목
Sequelize 타입DataTypes.BLOB(length)
Swagger 타입string / 포맷 byte
fixValue없음
엔티티
DTO
라우트 옵션

JSON으로 전송되는 base64 인코딩된 바이너리 데이터에 사용합니다. Swagger 포맷 byte는 값이 base64임을 나타냅니다.


TypeIs.BINARY
TypeIs.BINARY(options?: { length?: BlobSize; comment?: string; })
항목
Sequelize 타입DataTypes.BLOB(length)
Swagger 포맷string / 포맷 binary
fixValue없음
엔티티
DTO
라우트 옵션

원시 바이너리 / 파일 업로드 필드에 사용합니다. Swagger 포맷 binary는 octet stream을 나타냅니다.


TypeIs.BOOLEAN
TypeIs.BOOLEAN(options?: { comment?: string; })
항목
Sequelize 타입DataTypes.BOOLEAN
Swagger 타입boolean
fixValue'true'true, 'false'false, 그 외 → !!oBoolean(v)
엔티티
DTO
라우트 옵션

이 변환은 불리언이 문자열로 전달되는 쿼리 스트링 파라미터를 처리합니다.

@TypeIs.BOOLEAN({ comment: 'Whether the account is active' }) is_active: boolean;

날짜 / 시간 타입

TypeIs.DATEONLY
TypeIs.DATEONLY(options?: { comment?: string; })
항목
Sequelize 타입DataTypes.DATEONLY()
Swagger 타입string / 포맷 date
fixValue없음
엔티티
DTO
라우트 옵션

시간 없이 날짜만 저장합니다 (YYYY-MM-DD).


TypeIs.DATETIME
TypeIs.DATETIME(options?: { comment?: string; })
항목
Sequelize 타입DataTypes.DATE()
Swagger 타입string / 포맷 date-time
fixValue없음
엔티티
DTO
라우트 옵션

전체 타임스탬프를 저장합니다. 명시적으로 선언하는 created_atupdated_at 컬럼에 사용됩니다.

@TypeIs.DATETIME({ comment: 'Created at' }) created_at: Date;

관계 타입

TypeIs.FOREIGNKEY
TypeIs.FOREIGNKEY(options: { table: () => typeof Model; comment?: string; [key: string]: any })
항목
Sequelizesequelize-typescriptaddForeignKey 호출; extraData에 타입이 없으면 기본값으로 TypeIs.INT() 사용
SwaggerSwagger 스키마에 포함되지 않음
fixValue없음
엔티티
DTO아니오
라우트 옵션아니오

객체 인자로 table과 추가 컬럼 옵션을 전달합니다. 모듈 평가 시 순환 임포트 문제를 피하기 위해 table은 반드시 썽크(() => Model) 형태여야 합니다:

// 예제 앱의 표준 사용법 @TypeIs.FOREIGNKEY({ table: () => UsersTable, comment: '작성자 ID' }) user_id: number;

TypeIs.BELONGSTO
TypeIs.BELONGSTO(associatedClassGetter: () => typeof Model, optionsOrForeignKey?: string | AssociationOptions)
항목
SequelizeBelongsToAssociation으로 addAssociation 호출; as는 기본적으로 프로퍼티 이름 사용
SwaggerSwagger 스키마에 포함되지 않음
fixValue없음
엔티티
DTO아니오
라우트 옵션아니오

두 번째 인수는 외래 키 문자열이거나 전체 AssociationOptions 객체입니다. 옵션에 as가 없으면 프로퍼티 이름이 사용됩니다.

@TypeIs.FOREIGNKEY({ table: () => UsersTable, comment: 'Author ID' }) user_id: number; @TypeIs.BELONGSTO(() => UsersTable, 'user_id') user: UsersTable;

복합 / DTO 전용 타입

이 타입들은 Sequelize 컬럼 매핑이 없습니다. DTO 클래스와 라우트 IOptions에서만 사용됩니다.

TypeIs.DTO
TypeIs.DTO(options: { dto: typeof ExtendableDto; as: string; comment?: string; })
항목
Sequelizemiddleware() 호출 시 include 항목 생성
Swagger중첩된 DTO의 스키마를 가리키는 $ref
fixValue없음 — map()이 중첩 DTO의 자체 map()을 호출하여 DTO 필드를 처리
엔티티아니오
DTO
라우트 옵션

as는 엔티티의 BELONGSTO 선언에서 사용한 Sequelize 연관 별칭과 일치해야 합니다.

@TypeIs.DTO({ dto: UserInfoDto, as: 'user', comment: 'Post author' }) user: UserInfoDto;

TypeIs.QUERY
TypeIs.QUERY(options: { query: (props: { user?: any; association?: string }) => string; type: () => TypeIsData; })
항목
SequelizeSequelize.literal(...) 속성 항목 생성
Swaggertype TypeIs 함수 자체의 Swagger 출력에 위임
fixValuetype TypeIs 함수 자체의 fixValue에 위임
엔티티아니오
DTO
라우트 옵션아니오

원시 SQL 표현식이 필요한 계산 컬럼에 QUERY를 사용합니다. query 함수는 현재 association 경로와 JWT 페이로드의 user 객체를 받습니다.

@TypeIs.QUERY({ query: ({ association }) => `(SELECT COUNT(*) FROM posts WHERE posts.user_id = \`${association}\`.\`id\`)`, type: () => TypeIs.INT(), }) post_count: number;

TypeIs.ARRAY
TypeIs.ARRAY(options: DtoOrTypeIs)

DtoOrTypeIstypeof ExtendableDto | (() => TypeIsData)입니다.

항목
Sequelize컬럼 매핑 없음
Swagger{ type: 'array', items: ... }로 네임드 스키마 컴포넌트에 등록
fixValue값이 문자열이면 JSON.parse 시도; 그렇지 않으면 그대로 반환
엔티티아니오
DTO
라우트 옵션
// DTO 클래스 사용 response: TypeIs.ARRAY(UserInfoDto) // 기본 TypeIs 사용 response: TypeIs.ARRAY(TypeIs.INT()) TypeIs.ARRAY(TypeIs.STRING())

TypeIs.PAGING
TypeIs.PAGING(options: DtoOrTypeIs)
항목
Sequelize컬럼 매핑 없음
Swaggerdata(배열), page, page_size, max_page, has_prev, has_next, total_elements 프로퍼티를 가진 object
fixValue없음
엔티티아니오
DTO
라우트 옵션

PAGING은 DTO 또는 TypeIs를 표준 페이지네이션 엔벨로프 스키마로 감쌉니다. Swagger 스키마에는 모든 페이지네이션 메타데이터 필드가 포함됩니다:

필드타입설명
dataarray항목 타입의 배열
pageinteger (int32)현재 페이지 번호
page_sizeinteger (int32)페이지당 항목 수
max_pageinteger (int32)전체 페이지 수
has_prevboolean이전 페이지 존재 여부
has_nextboolean다음 페이지 존재 여부
total_elementsinteger (int32)전체 레코드 수

참고: 현재 Swagger 스키마(paging.ts)에서는 has_Next(대문자 N)로 정의되어 있으나, Repository의 실제 반환값은 has_next(소문자 n)입니다. 향후 코드 수정으로 통일될 예정입니다.

response: TypeIs.PAGING(PostInfoDto)

TypeIs 빠른 참조 테이블

TypeIsSequelize 타입Swagger 타입Swagger 포맷fixValue 동작엔티티DTO라우트
INTINTEGERintegerint32parseInt(str, 10)
BIGINTBIGINTintegerint32parseInt(str, 10)
LONGINTEGERintegerint64parseInt(str, 10)
FLOATFLOATintegerfloatparseFloat(str)
DECIMALDECIMALintegerfloatparseFloat(str)
DOUBLEDOUBLEnumberdoubleparseFloat(str)
STRINGSTRINGstringString(o)
TEXTTEXTstringString(o)
PASSWORDSTRING(512)stringpassword없음
ENUMENUMstring (enum)values에 있으면 반환, 없으면 undefined
JSONJSONobject문자열이면 JSON.parse; 오류 시 그대로 반환
BASE64BLOBstringbyte없음
BINARYBLOBstringbinary없음
BOOLEANBOOLEANboolean'true'/'false' → bool, 그 외 Boolean(!!o)
DATEONLYDATEONLYstringdate없음
DATETIMEDATEstringdate-time없음
FOREIGNKEY(FK 제약)없음아니오아니오
BELONGSTO(연관)없음아니오아니오
DTO(include)$ref없음 (중첩 map() 사용)아니오
QUERYliteral(...)(type에서)type에 위임아니오아니오
ARRAYarray문자열이면 JSON.parse아니오
PAGINGobject없음아니오

전체 엔티티 예제

다음 두 엔티티는 참조 구현에서 직접 가져온 것입니다. FOREIGNKEYBELONGSTO를 사용한 사용자와 게시글 간의 일대다 관계를 보여줍니다.

// packages/example/src/user/domain/entity/UsersTable.ts import { Model } from 'sequelize-typescript'; import { Table, TypeIs } from '@asapjs/sequelize'; @Table({ tableName: 'users', timestamps: true, }) export default class UsersTable extends Model { @TypeIs.INT({ primaryKey: true, autoIncrement: true, comment: 'User ID' }) id: number; @TypeIs.STRING({ unique: true, comment: 'Email (unique)' }) email: string; @TypeIs.PASSWORD({ comment: 'Password (bcrypt)' }) password: string; @TypeIs.STRING({ comment: 'Display name' }) name: string; @TypeIs.DATETIME({ comment: 'Created at' }) created_at: Date; @TypeIs.DATETIME({ comment: 'Updated at' }) updated_at: Date; }
// packages/example/src/post/domain/entity/PostsTable.ts import { Model } from 'sequelize-typescript'; import { Table, TypeIs } from '@asapjs/sequelize'; import UsersTable from '../../../user/domain/entity/UsersTable'; @Table({ tableName: 'posts', timestamps: true, }) export default class PostsTable extends Model { @TypeIs.INT({ primaryKey: true, autoIncrement: true, comment: 'Post ID' }) id: number; @TypeIs.STRING({ comment: 'Post title' }) title: string; @TypeIs.TEXT({ comment: 'Post content' }) content: string; // 외래 키 컬럼 — 정수 컬럼 생성 및 FK 제약 등록 @TypeIs.FOREIGNKEY({ table: () => UsersTable, comment: 'Author user ID' }) user_id: number; // 연관 — 컬럼 없음, BelongsTo 관계 등록 @TypeIs.BELONGSTO(() => UsersTable, 'user_id') user: UsersTable; @TypeIs.DATETIME({ comment: 'Created at' }) created_at: Date; @TypeIs.DATETIME({ comment: 'Updated at' }) updated_at: Date; }

관련 항목

Last updated on