Skip to content

Commit f8138f4

Browse files
authored
[Feat] seed 구조 리팩토링 (#75)
2 parents 32d5d22 + e172806 commit f8138f4

5 files changed

Lines changed: 1020 additions & 210 deletions

File tree

package.json

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
"scripts": {
88
"test": "echo \"Error: no test specified\" && exit 1",
99
"start": "node src/app.js",
10-
"dev": "nodemon -e js,json,prisma --exec \"prisma generate && node src/app.js\"",
11-
"seed": "node prisma/seed.js"
10+
"dev": "nodemon -e js,json,prisma --exec \"prisma generate && node src/app.js\""
1211
},
1312
"author": "",
1413
"license": "ISC",
@@ -46,9 +45,6 @@
4645
"devDependencies": {
4746
"prisma": "^6.18.0",
4847
"nodemon": "^3.1.10"
49-
},
50-
"prisma": {
51-
"seed": "node prisma/seed.js"
5248
}
53-
5449
}
50+

prisma/seed-categories.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { PrismaClient } from "@prisma/client";
2+
3+
const prisma = new PrismaClient();
4+
5+
async function main() {
6+
console.log("📁 기본 카테고리 초기화 중...");
7+
8+
// 기존 기본 카테고리 삭제
9+
await prisma.category.deleteMany({
10+
where: { isDefault: true },
11+
});
12+
13+
console.log("🗑️ 기존 기본 카테고리 삭제 완료\n");
14+
15+
const defaultCategoryNames = [
16+
"최근사용",
17+
"즐겨찾기",
18+
"기본",
19+
"사람",
20+
"행동",
21+
"감정",
22+
"음식",
23+
"장소",
24+
"신체",
25+
];
26+
27+
for (let i = 0; i < defaultCategoryNames.length; i++) {
28+
const categoryName = defaultCategoryNames[i];
29+
30+
await prisma.category.create({
31+
data: {
32+
categoryName: categoryName,
33+
displayOrder: i,
34+
isDefault: true,
35+
},
36+
});
37+
console.log(` ✨ "${categoryName}" 카테고리 생성`);
38+
}
39+
40+
console.log(`\n✅ 카테고리 ${defaultCategoryNames.length}개 생성 완료`);
41+
}
42+
43+
main()
44+
.catch((e) => {
45+
console.error("❌ Seed error:", e);
46+
process.exit(1);
47+
})
48+
.finally(async () => {
49+
await prisma.$disconnect();
50+
});

prisma/seed.js renamed to prisma/seed-terms.js

Lines changed: 1 addition & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,6 @@ async function main() {
99
await prisma.termsAgreement.deleteMany({});
1010
await prisma.terms.deleteMany({});
1111

12-
// 낱말 및 이력 데이터 정리를 위해 추가 (FK 제약 고려)
13-
await prisma.conversationHistory.deleteMany({});
14-
await prisma.userWord.deleteMany({}); // 즐겨찾기 데이터 초기화
15-
await prisma.word.deleteMany({});
16-
await prisma.category.deleteMany({});
17-
1812
// ==========================================
1913
// 약관 데이터 생성
2014
// ==========================================
@@ -114,204 +108,7 @@ async function main() {
114108

115109
console.log(`✅ 약관 ${terms.count}개 생성 완료`);
116110

117-
// ==========================================
118-
// 테스트용 사용자 생성
119-
// ==========================================
120-
console.log("👤 테스트 사용자 생성 중...");
121-
122-
const TEST_USER_ID = "test-user-uuid-0001";
123-
124-
const user = await prisma.user.upsert({
125-
where: { id: TEST_USER_ID },
126-
update: { nickname: "테스트유저" },
127-
create: {
128-
id: TEST_USER_ID,
129-
nickname: "테스트유저",
130-
email: "test@moduwa.com",
131-
accountType: "SOCIAL",
132-
},
133-
});
134-
console.log(`✅ 테스트 사용자 설정 완료: ${user.nickname}`);
135-
136-
/* =====================================================
137-
기본 카테고리 (category 테이블용)
138-
⚠ userCategory에는 넣지 않음
139-
===================================================== */
140-
141-
console.log("📁 기본 카테고리 생성 중...");
142-
143-
const defaultCategoryNames = [
144-
"최근사용",
145-
"즐겨찾기",
146-
"기본",
147-
"사람",
148-
"행동",
149-
"감정",
150-
"음식",
151-
"장소",
152-
"신체",
153-
];
154-
155-
const createdCategories = [];
156-
157-
for (let i = 0; i < defaultCategoryNames.length; i++) {
158-
const category = await prisma.category.create({
159-
data: {
160-
categoryName: defaultCategoryNames[i],
161-
displayOrder: i,
162-
isDefault: true,
163-
},
164-
});
165-
166-
createdCategories.push(category);
167-
}
168-
169-
console.log("✅ 기본 카테고리 9개 생성 완료");
170-
171-
// const categories = await Promise.all([
172-
// prisma.category.create({ data: { categoryName: "인사", displayOrder: 1 } }),
173-
// prisma.category.create({ data: { categoryName: "음식", displayOrder: 2 } }),
174-
// prisma.category.create({ data: { categoryName: "장소", displayOrder: 3 } }),
175-
// prisma.category.create({ data: { categoryName: "감정", displayOrder: 4 } }),
176-
// ]);
177-
178-
// console.log(`✅ 카테고리 ${categories.length}개 생성 완료`);
179-
180-
// ==========================================
181-
// 기본 낱말 데이터 (Word 테이블)
182-
// ==========================================
183-
console.log("📝 낱말 데이터 생성 중...");
184-
185-
const wordData = [
186-
{ word: "물", catIdx: 1, pos: "NOUN" },
187-
{ word: "밥", catIdx: 1, pos: "NOUN" },
188-
{ word: "사과", catIdx: 1, pos: "NOUN" },
189-
{ word: "화장실", catIdx: 2, pos: "NOUN" },
190-
{ word: "학교", catIdx: 2, pos: "NOUN" },
191-
{ word: "집", catIdx: 2, pos: "NOUN" },
192-
{ word: "안녕", catIdx: 0, pos: "NOUN" },
193-
{ word: "도움", catIdx: 0, pos: "NOUN" },
194-
{ word: "아프다", catIdx: 3, pos: "ADJECTIVE" },
195-
{ word: "좋다", catIdx: 3, pos: "ADJECTIVE" },
196-
{ word: "먹다", catIdx: 1, pos: "VERB" },
197-
{ word: "가다", catIdx: 2, pos: "VERB" },
198-
{ word: "주세요", catIdx: 0, pos: "VERB" },
199-
{ word: "졸리다", catIdx: 3, pos: "ADJECTIVE" },
200-
];
201-
202-
const createdWords = [];
203-
for (const item of wordData) {
204-
const w = await prisma.word.create({
205-
data: {
206-
word: item.word,
207-
categoryId: createdCategories[item.catIdx].id,
208-
partOfSpeech: item.pos,
209-
imageUrl: `https://api.moduwa.com/images/${item.word}.png`,
210-
isDefault: true,
211-
},
212-
});
213-
createdWords.push(w);
214-
}
215-
216-
console.log(`✅ 낱말 ${createdWords.length}개 생성 완료`);
217-
218-
// ==========================================
219-
// 즐겨찾기(Favorites) 데이터 생성
220-
// ==========================================
221-
console.log("⭐️ 즐겨찾기 설정 중...");
222-
223-
const favIndices = [0, 3, 6]; // '물', '화장실', '안녕'
224-
225-
for (let i = 0; i < favIndices.length; i++) {
226-
const idx = favIndices[i];
227-
await prisma.userWord.create({
228-
data: {
229-
userId: user.id,
230-
wordId: createdWords[idx].id,
231-
categoryId: createdWords[idx].categoryId,
232-
isFavorite: true,
233-
isDeleted: false,
234-
partOfSpeech: createdWords[idx].partOfSpeech,
235-
displayOrder: i + 1,
236-
},
237-
});
238-
}
239-
240-
console.log(`✅ 즐겨찾기 ${favIndices.length}개 생성 완료`);
241-
242-
// ==========================================
243-
// 대화 이력 생성 (빈도수 테스트용)
244-
// ==========================================
245-
console.log("💬 대화 이력 생성 중...");
246-
247-
for (let i = 0; i < createdWords.length; i++) {
248-
const targetWord = createdWords[i];
249-
const usageCount = 15 - i; // 상위 단어일수록 많이 사용 (최대 15회)
250-
251-
for (let j = 0; j < usageCount; j++) {
252-
await prisma.conversationHistory.create({
253-
data: {
254-
userId: user.id,
255-
inputType: "WORD_ONLY",
256-
inputWords: [
257-
{ wordId: targetWord.id, word: targetWord.word, order: 1 },
258-
{ wordId: createdWords[12].id, word: "주세요", order: 2 },
259-
],
260-
suggestedSentences: [`${targetWord.word} 주세요`],
261-
selectedSentence: `${targetWord.word} 주세요`,
262-
isOutputted: true,
263-
createdAt: new Date(new Date().getTime() - i * 60000),
264-
},
265-
});
266-
}
267-
}
268-
269-
console.log(`✅ 대화 이력 생성 완료`);
270-
271-
// ==========================================
272-
// 1주일 이상 지난 대화 이력 생성 (필터링 테스트용)
273-
// ==========================================
274-
console.log('📅 오래된 대화 이력 생성 중 (1주일 초과)...');
275-
276-
const tenDaysAgo = new Date(new Date().getTime() - (10 * 24 * 60 * 60 * 1000)); // 10일 전
277-
const fifteenDaysAgo = new Date(new Date().getTime() - (15 * 24 * 60 * 60 * 1000)); // 15일 전
278-
279-
// 10일 전 데이터 (이건 recent-words에서 제외되어야 함)
280-
// 최근 데이터에 없는 고유한 단어 사용
281-
await prisma.conversationHistory.create({
282-
data: {
283-
userId: user.id,
284-
inputType: 'WORD_ONLY',
285-
inputWords: [
286-
{ wordId: null, word: '오래된단어1', order: 1 },
287-
{ wordId: null, word: '테스트', order: 2 }
288-
],
289-
suggestedSentences: ['오래된단어1 테스트'],
290-
selectedSentence: '오래된단어1 테스트',
291-
isOutputted: true,
292-
createdAt: tenDaysAgo
293-
}
294-
});
295-
296-
// 15일 전 데이터 (이것도 recent-words에서 제외되어야 함)
297-
await prisma.conversationHistory.create({
298-
data: {
299-
userId: user.id,
300-
inputType: 'WORD_ONLY',
301-
inputWords: [
302-
{ wordId: null, word: '오래된단어2', order: 1 },
303-
{ wordId: null, word: '검증', order: 2 }
304-
],
305-
suggestedSentences: ['오래된단어2 검증'],
306-
selectedSentence: '오래된단어2 검증',
307-
isOutputted: true,
308-
createdAt: fifteenDaysAgo
309-
}
310-
});
311-
312-
console.log(`✅ 오래된 대화 이력 2개 생성 완료 (10일 전, 15일 전)`);
313-
314-
console.log('\n🎉 All data seeded successfully!');
111+
console.log('\n🎉 Database seeding completed!');
315112
}
316113

317114
main()

prisma/seed-words.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { PrismaClient } from "@prisma/client";
2+
import { readFileSync } from "fs";
3+
import { fileURLToPath } from "url";
4+
import { dirname, join } from "path";
5+
6+
const __filename = fileURLToPath(import.meta.url);
7+
const __dirname = dirname(__filename);
8+
9+
const prisma = new PrismaClient();
10+
11+
async function main() {
12+
console.log("📝 기본 낱말 초기화 중...");
13+
14+
// 기존 기본 낱말 삭제
15+
await prisma.word.deleteMany({
16+
where: { isDefault: true },
17+
});
18+
19+
console.log("🗑️ 기존 기본 낱말 삭제 완료\n");
20+
21+
// JSON 파일 읽기
22+
const wordsJsonPath = join(__dirname, "seeds", "words.json");
23+
const wordsData = JSON.parse(readFileSync(wordsJsonPath, "utf-8"));
24+
25+
let createdCount = 0;
26+
let errorCount = 0;
27+
28+
for (const categoryData of wordsData) {
29+
console.log(`📁 카테고리: ${categoryData.categoryName}`);
30+
31+
// 카테고리 찾기
32+
const category = await prisma.category.findFirst({
33+
where: {
34+
categoryName: categoryData.categoryName,
35+
isDefault: true,
36+
},
37+
});
38+
39+
if (!category) {
40+
console.log(` ⚠️ 카테고리 "${categoryData.categoryName}"를 찾을 수 없습니다. 스킵합니다.`);
41+
errorCount += categoryData.words.length;
42+
continue;
43+
}
44+
45+
// 각 낱말 처리
46+
for (const wordData of categoryData.words) {
47+
const createData = {
48+
word: wordData.word,
49+
categoryId: category.id,
50+
partOfSpeech: wordData.partOfSpeech || "NOUN", // 빈 문자열 처리
51+
imageUrl: wordData.imageUrl,
52+
isDefault: true,
53+
};
54+
55+
// uuid가 있으면 id로 사용
56+
if (wordData.uuid) {
57+
createData.id = wordData.uuid;
58+
}
59+
60+
await prisma.word.create({
61+
data: createData,
62+
});
63+
console.log(` ✨ "${wordData.word}" 생성`);
64+
createdCount++;
65+
}
66+
}
67+
68+
console.log(`\n✅ 완료: 생성 ${createdCount}개 / 오류 ${errorCount}개`);
69+
}
70+
71+
main()
72+
.catch((e) => {
73+
console.error("❌ Seed error:", e);
74+
process.exit(1);
75+
})
76+
.finally(async () => {
77+
await prisma.$disconnect();
78+
});

0 commit comments

Comments
 (0)