diff --git a/.gitignore b/.gitignore index 3bfa646..0925b7f 100644 --- a/.gitignore +++ b/.gitignore @@ -131,4 +131,6 @@ dist .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz -.pnp.* \ No newline at end of file +.pnp.* + +scripts/data/* \ No newline at end of file diff --git a/package.json b/package.json index 5b1bb55..f740488 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ "lint": "eslint .", "lint:fix": "eslint . --fix", "format": "prettier --write .", - "prettier": "prettier --write --config ./.prettierrc './src/**'" + "prettier": "prettier --write --config ./.prettierrc './src/**'", + "generate:user": "node ./scripts/generate-users-data.js", + "generate:word": "node ./scripts/generate-words-data.js" }, "author": "", "license": "ISC", diff --git a/scripts/generate-users-data.js b/scripts/generate-users-data.js new file mode 100644 index 0000000..efe21d0 --- /dev/null +++ b/scripts/generate-users-data.js @@ -0,0 +1,50 @@ +const fs = require('fs'); + +function generateRandomId() { + return Array(24) + .fill(0) + .map(() => Math.floor(Math.random() * 16).toString(16)) + .join(''); +} + +function generateRandomNickname(index) { + return `user${index}`; +} + +function generateUser(index) { + const now = new Date().toISOString(); + return { + _id: { $oid: generateRandomId() }, + deletedAt: null, + nickname: generateRandomNickname(index), + email: `user${index}@example.com`, + password: '$2b$10$NWhqlRrE5W0YMHlnaBi.CODbFjPZzvgT5G993eKoziVzrhH3M6KNC', + role: 'user', + snsId: null, + provider: null, + recentSearches: [], + requests: [], + createdAt: { $date: now }, + updatedAt: { $date: now }, + __v: 0, + }; +} + +function generateUsers(count) { + const users = []; + for (let i = 0; i < count; i++) { + users.push(generateUser(i)); + } + return users; +} + +async function generateAndSaveData(totalCount) { + const userData = generateUsers(totalCount); + const fileName = './scripts/data/test-users-data.json'; + fs.writeFileSync(fileName, JSON.stringify(userData, null, 2)); + + console.log('✅ 10000개의 유저 데이터가 생성되었습니다.'); + console.log('📁 파일 위치: users.json'); +} + +generateAndSaveData(10000).catch(console.error); diff --git a/scripts/generate-words-data.js b/scripts/generate-words-data.js new file mode 100644 index 0000000..87506be --- /dev/null +++ b/scripts/generate-words-data.js @@ -0,0 +1,58 @@ +const fs = require('fs'); + +const prefixes = ['A', 'Ab', 'Ac', 'Ad', 'Af', 'Ag', 'Al', 'Am', 'An', 'Ap', 'Ar', 'As', 'At', 'Au']; +const middles = ['sync', 'pi', 'data', 'net', 'web', 'cloud', 'tech', 'dev', 'sys', 'log']; +const suffixes = ['Service', 'API', 'Hub', 'Lab', 'Flow', 'Base', 'Core', 'Plus', 'Pro', 'Box']; + +function generateRandomWord(index) { + const prefix = prefixes[Math.floor(Math.random() * prefixes.length)]; + const middle = middles[Math.floor(Math.random() * middles.length)]; + const suffix = suffixes[Math.floor(Math.random() * suffixes.length)]; + return `${prefix}${middle}${suffix}${index}`; +} + +function* generateWordBatch(totalCount, batchSize = 1000) { + for (let i = 0; i < totalCount; i += batchSize) { + const batch = []; + const currentBatchSize = Math.min(batchSize, totalCount - i); + + for (let j = 0; j < currentBatchSize; j++) { + const word = generateRandomWord(i + j); + batch.push({ + word, + awkPron: `어색한_${word}`, + comPron: `일반적인_${word}`, + info: `${word}에 대한 설명입니다. (테스트 데이터 ${i + j + 1})`, + suggestedBy: 'admin', + freq: Math.floor(Math.random() * 100), + }); + } + yield batch; + } +} + +async function generateAndSaveData(totalCount) { + console.time('Data Generation'); + const fileName = './scripts/data/test-words-data.json'; + const batchSize = 1000; + let currentBatch = 1; + const totalBatches = Math.ceil(totalCount / batchSize); + + fs.writeFileSync(fileName, '[\n', 'utf8'); + + for (const batch of generateWordBatch(totalCount, batchSize)) { + console.log(`처리 중: ${currentBatch}/${totalBatches} 배치`); + + const batchData = batch.map((item) => JSON.stringify(item)).join(',\n'); + fs.appendFileSync(fileName, currentBatch === 1 ? batchData : ',\n' + batchData, 'utf8'); + + currentBatch++; + } + + fs.appendFileSync(fileName, '\n]', 'utf8'); + + console.log(`✅ ${totalCount}개의 테스트 데이터 생성 완료!`); + console.log(`📁 파일 위치: ${fileName}`); +} + +generateAndSaveData(100000).catch(console.error); diff --git a/src/routes/word/word.model.js b/src/routes/word/word.model.js index f1e03d2..8a95bc0 100644 --- a/src/routes/word/word.model.js +++ b/src/routes/word/word.model.js @@ -3,7 +3,7 @@ const redisClient = require('../../common/modules/redis'); const wordSchema = new mongoose.Schema( { - word: { type: String, required: true, unique: true }, + word: { type: String, required: true }, awkPron: { type: String }, comPron: { type: String, required: true }, info: { type: String }, @@ -13,18 +13,7 @@ const wordSchema = new mongoose.Schema( { timestamps: true } ); -wordSchema.index({ word: 1 }); - -wordSchema.post(/^findOne/, async function (doc) { - const word = typeof this.getQuery().word === 'string' ? this.getQuery().word : doc?.word; - if (!word) { - console.error('❌ Error: No valid word found for Redis update'); - return; - } - - await redisClient.sendCommand(['ZINCRBY', 'popular_words', '1', word]); - await redisClient.expire('popular_words', 7200); -}); +wordSchema.index({ word: 1 }, { unique: true }); wordSchema.pre(/^find|update|save|remove|delete|count/, function (next) { this._startTime = Date.now(); @@ -33,8 +22,19 @@ wordSchema.pre(/^find|update|save|remove|delete|count/, function (next) { wordSchema.post(/^find|update|save|remove|delete|count/, function (result, next) { const latency = Date.now() - this._startTime; + console.log(this.getQuery()); console.log(`[${this.mongooseCollection.modelName}] ${this.op} query - ${latency}ms`); next(); }); +wordSchema.post(/^findOne/, async function (doc) { + const word = typeof this.getQuery().word === 'string' ? this.getQuery().word : doc?.word; + if (!word) { + console.error('❌ Error: No valid word found for Redis update'); + return; + } + await redisClient.sendCommand(['ZINCRBY', 'popular_words', '1', word]); + await redisClient.expire('popular_words', 7200); +}); + module.exports = mongoose.model('Word', wordSchema);