Skip to content

Commit ad3e2eb

Browse files
authored
Merge pull request #184 from yuja201/fix/183-null-generation
[fix] null generation issue and stabilize faker mappings
2 parents 403fccf + 63d7c61 commit ad3e2eb

5 files changed

Lines changed: 116 additions & 80 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
UPDATE domains
2+
SET locales = '["en","ko"]'
3+
WHERE id = 51; -- HEX
4+

src/main/database/rules.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export function createRule(data: RuleInput): Rule {
9999
data.domain,
100100
data.model_id ?? null,
101101
data.prompt ?? null,
102-
data.locale ?? null,
102+
data.locale ?? 'en',
103103
now,
104104
now
105105
)

src/main/database/schema.ts

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ CREATE TABLE IF NOT EXISTS domains (
4848
name TEXT NOT NULL UNIQUE,
4949
description TEXT,
5050
logical_type TEXT NOT NULL DEFAULT 'string',
51+
locales TEXT NOT NULL DEFAULT '["en"]',
5152
created_at NUMERIC NOT NULL DEFAULT (strftime('%s', 'now')),
5253
updated_at NUMERIC NOT NULL DEFAULT (strftime('%s', 'now')),
5354
FOREIGN KEY (category_id) REFERENCES domain_categories(id) ON DELETE CASCADE
@@ -61,7 +62,7 @@ CREATE TABLE IF NOT EXISTS rules (
6162
domain_id INTEGER NOT NULL,
6263
model_id NUMERIC,
6364
prompt TEXT,
64-
locale TEXT,
65+
locale TEXT NOT NULL DEFAULT 'en',
6566
created_at NUMERIC NOT NULL DEFAULT (strftime('%s', 'now')),
6667
updated_at NUMERIC NOT NULL DEFAULT (strftime('%s', 'now')),
6768
FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE
@@ -123,60 +124,60 @@ INSERT OR IGNORE INTO domain_categories (id, name) VALUES
123124

124125
// 도메인 종류 저장
125126
export const insertDefaultDomainData = `
126-
INSERT OR IGNORE INTO domains (id, category_id, name, description, logical_type) VALUES
127-
(1, 1, '이름', '이름(First Name)', 'string'),
128-
(2, 1, '성', '성(Last Name)', 'string'),
129-
(3, 1, '전체 이름', '이름과 성을 합친 전체 이름', 'string'),
130-
(4, 1, '직업명', '직업 또는 직책', 'string'),
131-
(5, 2, '도로명 주소', '상세 도로명 주소', 'string'),
132-
(6, 2, '도시', '도시 이름', 'string'),
133-
(7, 2, '국가', '국가 이름', 'string'),
134-
(8, 2, '우편번호', '우편번호', 'string'),
135-
(9, 3, '이메일', '이메일 주소', 'string'),
136-
(10, 3, '전화번호', '전화번호', 'string'),
137-
(11, 3, '사용자명', '계정 아이디 또는 닉네임', 'string'),
138-
(12, 3, '도메인', '도메인 이름', 'string'),
139-
(13, 4, '회사명', '회사 이름', 'string'),
140-
(14, 4, '슬로건', '회사 슬로건 또는 문구', 'string'),
141-
(15, 4, '부서', '부서 이름', 'string'),
142-
(16, 4, '직종', '직무 분야', 'string'),
143-
(17, 5, '계좌번호', '은행 계좌번호', 'string'),
144-
(18, 5, '금액', '거래 금액', 'number'),
145-
(19, 5, '카드번호', '신용/체크카드 번호', 'string'),
146-
(20, 5, '거래유형', '입금, 출금 등 거래 종류', 'string'),
147-
(21, 6, '상품명', '제품 이름', 'string'),
148-
(22, 6, '카테고리', '제품 분류', 'string'),
149-
(23, 6, '가격', '제품 가격', 'number'),
150-
(24, 6, '설명', '제품 설명 또는 소개문', 'string'),
151-
(25, 7, '제조사', '차량 브랜드명', 'string'),
152-
(26, 7, '모델명', '모델 이름', 'string'),
153-
(27, 7, '차량번호', '차량 번호판', 'string'),
154-
(28, 7, '차량 타입', '차량 종류 (SUV, 트럭 등)', 'string'),
155-
(29, 8, '과거 날짜', '무작위 과거 일시', 'date'),
156-
(30, 8, '미래 날짜', '무작위 미래 일시', 'date'),
157-
(31, 8, '최근 날짜', '최근 시점의 일시', 'date'),
158-
(32, 8, '타임스탬프', 'UNIX timestamp', 'number'),
159-
(33, 9, 'UUID', '고유 식별자(UUID)', 'string'),
160-
(34, 9, '파일명', '파일 이름', 'string'),
161-
(35, 9, '버전', '소프트웨어 버전 문자열', 'string'),
162-
(36, 9, '커밋 메시지', 'Git 커밋 메시지', 'string'),
163-
(37, 10, '정수', '랜덤 정수 값', 'number'),
164-
(38, 10, '실수', '랜덤 실수 값', 'number'),
165-
(39, 10, '불리언', 'true 또는 false 값', 'boolean'),
166-
(40, 10, '랜덤 문자열', '무작위 문자열 데이터', 'string'),
167-
(41, 11, '문장', '짧은 문장', 'string'),
168-
(42, 11, '문단', '랜덤 긴 문단', 'string'),
169-
(43, 11, '단어', '단어 하나', 'string'),
170-
(44, 11, '키워드', '주제나 태그용 단어', 'string'),
171-
(45, 12, '화학 원소', '화학 원소 이름', 'string'),
172-
(46, 12, '단위', '물리 단위', 'string'),
173-
(47, 12, '과목명', '학교 과목 이름', 'string'),
174-
(48, 12, '학교명', '학교 또는 기관 이름', 'string'),
175-
(49, 13, '색상명', '사람이 읽을 수 있는 색상 이름', 'string'),
176-
(50, 13, 'RGB 값', 'RGB 형태의 색상값', 'string'),
177-
(51, 13, 'HEX 코드', '16진수 색상 코드', 'string'),
178-
(52, 13, '이미지 URL', '랜덤 이미지 주소', 'string'),
179-
(53, 14, '음악 장르', '랜덤 음악 장르', 'string'),
180-
(54, 14, '노래 이름', '랜덤 노래 제목', 'string'),
181-
(55, 14, '동물 이름', '동물 이름', 'string');
127+
INSERT OR IGNORE INTO domains (id, category_id, name, description, logical_type, locales) VALUES
128+
(1, 1, '이름', '이름(First Name)', 'string', '["en","ko"]'),
129+
(2, 1, '성', '성(Last Name)', 'string', '["en","ko"]'),
130+
(3, 1, '전체 이름', '이름과 성을 합친 전체 이름', 'string', '["en","ko"]'),
131+
(4, 1, '직업명', '직업 또는 직책', 'string', '["en"]'),
132+
(5, 2, '도로명 주소', '상세 도로명 주소', 'string', '["en"]'),
133+
(6, 2, '도시', '도시 이름', 'string', '["en","ko"]'),
134+
(7, 2, '국가', '국가 이름', 'string', '["en"]'),
135+
(8, 2, '우편번호', '우편번호', 'string', '["en","ko"]'),
136+
(9, 3, '이메일', '이메일 주소', 'string', '["en","ko"]'),
137+
(10, 3, '전화번호', '전화번호', 'string', '["en","ko"]'),
138+
(11, 3, '사용자명', '계정 아이디 또는 닉네임', 'string', '["en"]'),
139+
(12, 3, '도메인', '도메인 이름', 'string', '["en","ko"]'),
140+
(13, 4, '회사명', '회사 이름', 'string', '["en","ko"]'),
141+
(14, 4, '슬로건', '회사 슬로건 또는 문구', 'string', '["en"]'),
142+
(15, 4, '부서', '부서 이름', 'string', '["en"]'),
143+
(16, 4, '직종', '직무 분야', 'string', '["en"]'),
144+
(17, 5, '계좌번호', '은행 계좌번호', 'string', '["en"]'),
145+
(18, 5, '금액', '거래 금액', 'number', '["en"]'),
146+
(19, 5, '카드번호', '신용/체크카드 번호', 'string', '["en"]'),
147+
(20, 5, '거래유형', '입금, 출금 등 거래 종류', 'string', '["en"]'),
148+
(21, 6, '상품명', '제품 이름', 'string', '["en"]'),
149+
(22, 6, '카테고리', '제품 분류', 'string', '["en"]'),
150+
(23, 6, '가격', '제품 가격', 'number', '["en"]'),
151+
(24, 6, '설명', '제품 설명 또는 소개문', 'string', '["en"]'),
152+
(25, 7, '제조사', '차량 브랜드명', 'string', '["en"]'),
153+
(26, 7, '모델명', '모델 이름', 'string', '["en"]'),
154+
(27, 7, '차량번호', '차량 번호판', 'string', '["en"]'),
155+
(28, 7, '차량 타입', '차량 종류 (SUV, 트럭 등)', 'string', '["en"]'),
156+
(29, 8, '과거 날짜', '무작위 과거 일시', 'date', '["en","ko"]'),
157+
(30, 8, '미래 날짜', '무작위 미래 일시', 'date', '["en","ko"]'),
158+
(31, 8, '최근 날짜', '최근 시점의 일시', 'date', '["en","ko"]'),
159+
(32, 8, '타임스탬프', 'UNIX timestamp', 'number', '["en"]'),
160+
(33, 9, 'UUID', '고유 식별자(UUID)', 'string', '["en"]'),
161+
(34, 9, '파일명', '파일 이름', 'string', '["en"]'),
162+
(35, 9, '버전', '소프트웨어 버전 문자열', 'string', '["en","ko"]'),
163+
(36, 9, '커밋 메시지', 'Git 커밋 메시지', 'string', '["en"]'),
164+
(37, 10, '정수', '랜덤 정수 값', 'number', '["en","ko"]'),
165+
(38, 10, '실수', '랜덤 실수 값', 'number', '["en","ko"]'),
166+
(39, 10, '불리언', 'true 또는 false 값', 'boolean', '["en","ko"]'),
167+
(40, 10, '랜덤 문자열', '무작위 문자열 데이터', 'string', '["en","ko"]'),
168+
(41, 11, '문장', '짧은 문장', 'string', '["en","ko"]'),
169+
(42, 11, '문단', '랜덤 긴 문단', 'string', '["en","ko"]'),
170+
(43, 11, '단어', '단어 하나', 'string', '["en","ko"]'),
171+
(44, 11, '키워드', '주제나 태그용 단어', 'string', '["en"]'),
172+
(45, 12, '화학 원소', '화학 원소 이름', 'string', '["en"]'),
173+
(46, 12, '단위', '물리 단위', 'string', '["en"]'),
174+
(47, 12, '과목명', '학교 과목 이름', 'string', '["en"]'),
175+
(48, 12, '학교명', '학교 또는 기관 이름', 'string', '["en"]'),
176+
(49, 13, '색상명', '사람이 읽을 수 있는 색상 이름', 'string', '["en","ko"]'),
177+
(50, 13, 'RGB 값', 'RGB 형태의 색상값', 'string', '["en","ko"]'),
178+
(51, 13, 'HEX 코드', '16진수 색상 코드', 'string', '["en","ko"]'),
179+
(52, 13, '이미지 URL', '랜덤 이미지 주소', 'string', '["en"]'),
180+
(53, 14, '음악 장르', '랜덤 음악 장르', 'string', '["en"]'),
181+
(54, 14, '노래 이름', '랜덤 노래 제목', 'string', '["en"]'),
182+
(55, 14, '동물 이름', '동물 이름', 'string', '["en"]');
182183
`

src/main/services/data-generator/faker-generator.ts

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const LOCALE_FAKERS = {
88
ko: fakerKO
99
} as const
1010

11+
type FakerOptions = Record<string, unknown> | undefined
12+
1113
/**
1214
* Faker 생성 요청 파라미터
1315
*/
@@ -98,37 +100,66 @@ export async function* generateFakeStream({
98100
if (!fakerPath) {
99101
throw new Error(`No faker mapping for domain: ${domainName}`)
100102
}
101-
102-
const [category, method] = fakerPath.split('.') as [keyof Faker, string]
103+
const [category, method, optionName, optionValue] = fakerPath.split('.') as [
104+
keyof Faker,
105+
string,
106+
string?,
107+
string?
108+
]
103109
const fakerCategory = faker[category]
110+
111+
if (!fakerCategory) {
112+
throw new Error(`❌ Invalid faker path: ${fakerPath}`)
113+
}
114+
104115
const fn = (fakerCategory as Record<string, unknown>)[method]
105116

106117
if (typeof fn !== 'function') {
107118
throw new Error(`❌ Invalid faker path: ${fakerPath}`)
108119
}
109120

121+
let opts: FakerOptions = undefined
122+
123+
if (optionName && optionValue) {
124+
const parsedValue = Number(optionValue)
125+
opts = {
126+
[optionName]: Number.isNaN(parsedValue) ? optionValue : parsedValue
127+
}
128+
}
129+
110130
// 단일 값을 생성하는 헬퍼 함수
111131
const generateSingleValue = (): string => {
112-
// 숫자 범위 제약 적용
113-
if (
114-
typeof min === 'number' &&
115-
typeof max === 'number' &&
116-
/INT|DECIMAL|NUMERIC|FLOAT|DOUBLE/i.test(sqlType)
117-
) {
118-
return String(
119-
(fn as (opts: { min: number; max: number }) => number)({
120-
min,
121-
max
122-
})
123-
)
132+
const callOpts: Record<string, unknown> = { ...(opts ?? {}) }
133+
134+
// 숫자 범위 제약
135+
if (/INT|DECIMAL|NUMERIC|FLOAT|DOUBLE/i.test(sqlType)) {
136+
if (typeof min === 'number') callOpts.min = min
137+
if (typeof max === 'number') callOpts.max = max
138+
}
139+
140+
const raw = Object.keys(callOpts).length > 0 ? fn(callOpts) : fn()
141+
142+
// 날짜 처리
143+
if (/DATE|DATETIME|TIMESTAMP/i.test(sqlType)) {
144+
if (raw instanceof Date) {
145+
const iso = raw.toISOString()
146+
147+
if (/^DATE$/i.test(sqlType)) {
148+
return iso.slice(0, 10)
149+
}
150+
151+
return iso.slice(0, 19).replace('T', ' ')
152+
}
153+
154+
return String(raw)
124155
}
125-
// 문자열 길이 제약 적용
156+
157+
// 문자열 길이
126158
if (typeof maxLength === 'number') {
127-
const raw = String((fn as () => string)())
128-
return raw.slice(0, maxLength)
159+
return String(raw).slice(0, maxLength)
129160
}
130-
// 제약조건 없음
131-
return String((fn as () => string | number)())
161+
162+
return String(raw)
132163
}
133164

134165
// 고유값 보장이 필요 없는 경우

src/main/utils/faker-mapper.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const fakerMapper: Record<string, string> = {
1111

1212
이메일: 'internet.email',
1313
전화번호: 'phone.number',
14-
사용자명: 'internet.userName',
14+
사용자명: 'internet.username',
1515
도메인: 'internet.domainName',
1616

1717
회사명: 'company.name',
@@ -54,8 +54,8 @@ export const fakerMapper: Record<string, string> = {
5454
단위: 'science.unit',
5555

5656
색상명: 'color.human',
57-
'RGB 값': 'color.rgb',
58-
'HEX 코드': 'color.hex',
57+
'RGB 값': 'color.rgb.format.css',
58+
'HEX 코드': 'color.rgb.casing.upper',
5959
'이미지 URL': 'image.url',
6060

6161
'음악 장르': 'music.genre',

0 commit comments

Comments
 (0)