viewlogic 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,352 +0,0 @@
1
- /**
2
- * ViewLogic Cache Management System
3
- * 캐시 관리 시스템
4
- */
5
- export class CacheManager {
6
- constructor(router, options = {}) {
7
- this.config = {
8
- cacheMode: options.cacheMode || 'memory', // 'memory' 또는 'lru'
9
- cacheTTL: options.cacheTTL || 300000, // 5분 (밀리초)
10
- maxCacheSize: options.maxCacheSize || 50, // LRU 캐시 최대 크기
11
- debug: options.debug || false
12
- };
13
-
14
- // 라우터 인스턴스 참조 (필요시 라우터 상태 확인용)
15
- this.router = router;
16
-
17
- // 캐시 저장소들
18
- this.cache = new Map();
19
- this.cacheTimestamps = new Map();
20
- this.lruOrder = []; // LRU 순서 추적
21
-
22
- this.log('info', 'CacheManager initialized with config:', this.config);
23
- }
24
-
25
- /**
26
- * 로깅 래퍼 메서드
27
- */
28
- log(level, ...args) {
29
- if (this.router?.errorHandler) {
30
- this.router.errorHandler.log(level, 'CacheManager', ...args);
31
- }
32
- }
33
-
34
- /**
35
- * 캐시에 값 저장
36
- */
37
- setCache(key, value) {
38
- const now = Date.now();
39
-
40
- if (this.config.cacheMode === 'lru') {
41
- // LRU 캐시 관리
42
- if (this.cache.size >= this.config.maxCacheSize && !this.cache.has(key)) {
43
- const oldestKey = this.lruOrder.shift();
44
- if (oldestKey) {
45
- this.cache.delete(oldestKey);
46
- this.cacheTimestamps.delete(oldestKey);
47
- this.log('debug', `🗑️ LRU evicted cache key: ${oldestKey}`);
48
- }
49
- }
50
-
51
- // 기존 키가 있으면 LRU 순서에서 제거
52
- const existingIndex = this.lruOrder.indexOf(key);
53
- if (existingIndex > -1) {
54
- this.lruOrder.splice(existingIndex, 1);
55
- }
56
-
57
- // 최신 순서로 추가
58
- this.lruOrder.push(key);
59
- }
60
-
61
- this.cache.set(key, value);
62
- this.cacheTimestamps.set(key, now);
63
-
64
- this.log('debug', `💾 Cached: ${key} (size: ${this.cache.size})`);
65
- }
66
-
67
- /**
68
- * 캐시에서 값 가져오기
69
- */
70
- getFromCache(key) {
71
- const now = Date.now();
72
- const timestamp = this.cacheTimestamps.get(key);
73
-
74
- // TTL 체크
75
- if (timestamp && (now - timestamp) > this.config.cacheTTL) {
76
- this.cache.delete(key);
77
- this.cacheTimestamps.delete(key);
78
-
79
- if (this.config.cacheMode === 'lru') {
80
- const index = this.lruOrder.indexOf(key);
81
- if (index > -1) {
82
- this.lruOrder.splice(index, 1);
83
- }
84
- }
85
-
86
- this.log('debug', `⏰ Cache expired and removed: ${key}`);
87
- return null;
88
- }
89
-
90
- const value = this.cache.get(key);
91
-
92
- if (value && this.config.cacheMode === 'lru') {
93
- // LRU 순서 업데이트
94
- const index = this.lruOrder.indexOf(key);
95
- if (index > -1) {
96
- this.lruOrder.splice(index, 1);
97
- this.lruOrder.push(key);
98
- }
99
- }
100
-
101
- if (value) {
102
- this.log('debug', `🎯 Cache hit: ${key}`);
103
- } else {
104
- this.log('debug', `❌ Cache miss: ${key}`);
105
- }
106
-
107
- return value;
108
- }
109
-
110
- /**
111
- * 캐시에 키가 있는지 확인
112
- */
113
- hasCache(key) {
114
- return this.cache.has(key) && this.getFromCache(key) !== null;
115
- }
116
-
117
- /**
118
- * 특정 키 패턴의 캐시 삭제
119
- */
120
- invalidateByPattern(pattern) {
121
- const keysToDelete = [];
122
-
123
- for (const key of this.cache.keys()) {
124
- if (key.includes(pattern) || key.startsWith(pattern)) {
125
- keysToDelete.push(key);
126
- }
127
- }
128
-
129
- keysToDelete.forEach(key => {
130
- this.cache.delete(key);
131
- this.cacheTimestamps.delete(key);
132
-
133
- if (this.config.cacheMode === 'lru') {
134
- const index = this.lruOrder.indexOf(key);
135
- if (index > -1) {
136
- this.lruOrder.splice(index, 1);
137
- }
138
- }
139
- });
140
-
141
- this.log('debug', `🧹 Invalidated ${keysToDelete.length} cache entries matching: ${pattern}`);
142
- return keysToDelete.length;
143
- }
144
-
145
- /**
146
- * 특정 컴포넌트 캐시 무효화
147
- */
148
- invalidateComponentCache(routeName) {
149
- const patterns = [
150
- `component_${routeName}`,
151
- `script_${routeName}`,
152
- `template_${routeName}`,
153
- `style_${routeName}`,
154
- `layout_${routeName}`
155
- ];
156
-
157
- let totalInvalidated = 0;
158
- patterns.forEach(pattern => {
159
- totalInvalidated += this.invalidateByPattern(pattern);
160
- });
161
-
162
- this.log(`🔄 Invalidated component cache for route: ${routeName} (${totalInvalidated} entries)`);
163
- return totalInvalidated;
164
- }
165
-
166
- /**
167
- * 모든 컴포넌트 캐시 삭제
168
- */
169
- clearComponentCache() {
170
- const componentPatterns = ['component_', 'script_', 'template_', 'style_', 'layout_'];
171
- let totalCleared = 0;
172
-
173
- componentPatterns.forEach(pattern => {
174
- totalCleared += this.invalidateByPattern(pattern);
175
- });
176
-
177
- this.log(`🧽 Cleared all component caches (${totalCleared} entries)`);
178
- return totalCleared;
179
- }
180
-
181
- /**
182
- * 전체 캐시 삭제
183
- */
184
- clearCache() {
185
- const size = this.cache.size;
186
- this.cache.clear();
187
- this.cacheTimestamps.clear();
188
- this.lruOrder = [];
189
-
190
- this.log(`🔥 Cleared all cache (${size} entries)`);
191
- }
192
-
193
- /**
194
- * 만료된 캐시 항목들 정리
195
- */
196
- cleanExpiredCache() {
197
- const now = Date.now();
198
- const expiredKeys = [];
199
-
200
- for (const [key, timestamp] of this.cacheTimestamps.entries()) {
201
- if ((now - timestamp) > this.config.cacheTTL) {
202
- expiredKeys.push(key);
203
- }
204
- }
205
-
206
- expiredKeys.forEach(key => {
207
- this.cache.delete(key);
208
- this.cacheTimestamps.delete(key);
209
-
210
- if (this.config.cacheMode === 'lru') {
211
- const index = this.lruOrder.indexOf(key);
212
- if (index > -1) {
213
- this.lruOrder.splice(index, 1);
214
- }
215
- }
216
- });
217
-
218
- if (expiredKeys.length > 0) {
219
- this.log(`⏱️ Cleaned ${expiredKeys.length} expired cache entries`);
220
- }
221
-
222
- return expiredKeys.length;
223
- }
224
-
225
- /**
226
- * 캐시 통계 정보
227
- */
228
- getCacheStats() {
229
- return {
230
- size: this.cache.size,
231
- maxSize: this.config.maxCacheSize,
232
- mode: this.config.cacheMode,
233
- ttl: this.config.cacheTTL,
234
- memoryUsage: this.getMemoryUsage(),
235
- hitRatio: this.getHitRatio(),
236
- categories: this.getCategorizedStats()
237
- };
238
- }
239
-
240
- /**
241
- * 메모리 사용량 추정
242
- */
243
- getMemoryUsage() {
244
- let estimatedBytes = 0;
245
-
246
- for (const [key, value] of this.cache.entries()) {
247
- // 키 크기
248
- estimatedBytes += key.length * 2; // UTF-16
249
-
250
- // 값 크기 추정
251
- if (typeof value === 'string') {
252
- estimatedBytes += value.length * 2;
253
- } else if (typeof value === 'object' && value !== null) {
254
- estimatedBytes += JSON.stringify(value).length * 2;
255
- } else {
256
- estimatedBytes += 8; // 대략적인 크기
257
- }
258
- }
259
-
260
- return {
261
- bytes: estimatedBytes,
262
- kb: Math.round(estimatedBytes / 1024 * 100) / 100,
263
- mb: Math.round(estimatedBytes / (1024 * 1024) * 100) / 100
264
- };
265
- }
266
-
267
- /**
268
- * 히트 비율 계산 (간단한 추정)
269
- */
270
- getHitRatio() {
271
- // 실제 히트/미스 추적을 위해서는 별도의 카운터가 필요
272
- // 현재는 캐시 크기 기반 추정치 반환
273
- const ratio = this.cache.size > 0 ? Math.min(this.cache.size / this.config.maxCacheSize, 1) : 0;
274
- return Math.round(ratio * 100);
275
- }
276
-
277
- /**
278
- * 카테고리별 캐시 통계
279
- */
280
- getCategorizedStats() {
281
- const categories = {
282
- components: 0,
283
- scripts: 0,
284
- templates: 0,
285
- styles: 0,
286
- layouts: 0,
287
- others: 0
288
- };
289
-
290
- for (const key of this.cache.keys()) {
291
- if (key.startsWith('component_')) categories.components++;
292
- else if (key.startsWith('script_')) categories.scripts++;
293
- else if (key.startsWith('template_')) categories.templates++;
294
- else if (key.startsWith('style_')) categories.styles++;
295
- else if (key.startsWith('layout_')) categories.layouts++;
296
- else categories.others++;
297
- }
298
-
299
- return categories;
300
- }
301
-
302
- /**
303
- * 캐시 키 목록 반환
304
- */
305
- getCacheKeys() {
306
- return Array.from(this.cache.keys());
307
- }
308
-
309
- /**
310
- * 특정 패턴의 캐시 키들 반환
311
- */
312
- getCacheKeysByPattern(pattern) {
313
- return this.getCacheKeys().filter(key =>
314
- key.includes(pattern) || key.startsWith(pattern)
315
- );
316
- }
317
-
318
- /**
319
- * 자동 정리 시작 (백그라운드에서 만료된 캐시 정리)
320
- */
321
- startAutoCleanup(interval = 60000) { // 기본 1분 간격
322
- if (this.cleanupInterval) {
323
- clearInterval(this.cleanupInterval);
324
- }
325
-
326
- this.cleanupInterval = setInterval(() => {
327
- this.cleanExpiredCache();
328
- }, interval);
329
-
330
- this.log(`🤖 Auto cleanup started (interval: ${interval}ms)`);
331
- }
332
-
333
- /**
334
- * 자동 정리 중지
335
- */
336
- stopAutoCleanup() {
337
- if (this.cleanupInterval) {
338
- clearInterval(this.cleanupInterval);
339
- this.cleanupInterval = null;
340
- this.log('debug', '🛑 Auto cleanup stopped');
341
- }
342
- }
343
-
344
- /**
345
- * 정리 (메모리 누수 방지)
346
- */
347
- destroy() {
348
- this.stopAutoCleanup();
349
- this.clearCache();
350
- this.log('debug', 'CacheManager destroyed');
351
- }
352
- }