μΉ΄ν…Œκ³ λ¦¬ μ—†μŒ

λŒ€μš©λŸ‰ 데이터 μ„œλΉ™ 속도 κ°œμ„  λ°©μ•ˆ

parkjiu 2024. 11. 26. 11:33

ν˜„μž¬ λ§Œλ“€κ³  μžˆλŠ” μ„œλΉ„μŠ€μ˜ λͺ©μ μ€ 보유 데이터λ₯Ό μœ μ €μ—κ²Œ λ³΄μ—¬μ£ΌλŠ” 것이닀.

개발 μ΄ˆκΈ°μ—λŠ” μ΄λ ‡κ²Œ 데이터가 클지 λͺ¨λ₯΄κ³ , κ°„λ‹¨ν•˜κ²Œ λ§Œλ“€μ–΄λ³΄κ³ μž λͺ¨λ“  데이터λ₯Ό ν”„λ‘ νŠΈλ‹¨μ— λͺ¨λ‘ 두고 κ°œλ°œμ„ μ‹œμž‘ν–ˆλ‹€. (μ²˜μŒμ—λŠ” 100λ©”κ°€ μ •λ„μ˜€λ‹€...)

μ–΄λŠμ •λ„ λ§Œλ“  ν›„, μƒˆλ‘œμš΄ 데이터λ₯Ό μ£Όμ‹œκ³  λ””λ²¨λ‘­ν–ˆμ–΄μ•Ό ν–ˆλŠ”λ°, λ°›μ•„λ³΄λ‹ˆ 거의 2.8κΈ°κ°€μ˜€λ‹€λŠ”...γ…Žγ…Žγ…Žγ…Žγ…Ž

λ‹Ήμ—°νžˆ μ†λ„λŠ” 느릴 μˆ˜λ°–μ— μ—†λŠ” μ—„μ²­λ‚œ λ°μ΄ν„°μ˜€λ‹€..^^;; geoJSON은 κ²λ‚˜,,, κ²λ‚˜ 컀,,,,,,,,,, 

이런 데이터λ₯Ό μ²˜λ¦¬ν•΄ λ³Έ 적이 μ—†μœΌλ‹ˆ μ’€ λ‹Ήν™©μŠ€λŸ¬μ›Œμ„œ μƒˆλ‘œμš΄ λ°©μ•ˆμ„ 고민해보기 μ‹œμž‘ν–ˆλ‹€. 

 


1. λ°±μ•€λ“œ λ„μž…

 

λ‹Ήμ—°νžˆ κ°€μž₯ 처음 μƒκ°ν–ˆλ˜ 것은 λ°±μ•€λ“œ λ„μž…μ΄μ—ˆλ‹€. 이미 κΈ°μ‘΄ μ½”λ“œλ₯Ό node.js둜 λ§Œλ“€μ—ˆμœΌλ‹ˆ, λ°±μ•€λ“œλ„ node.js둜 λ§Œλ“€λ©΄ μ–Όλ§ˆ μ•ˆκ±Έλ¦΄ κ²ƒμ΄λΌλŠ” 생각과, docker ν™˜κ²½μ„ 이미 λ„μž…ν•˜λ©΄μ„œ server.jsλ₯Ό κ΅¬μ„±ν•΄λ‘μ—ˆκΈ°μ— μ–΄λ ΅μ§€ μ•Šκ²Œ ꡬ성할 수 있으리라 μƒκ°ν–ˆλ‹€. μ‹€μ œλ‘œ node둜 λ°±μ•€λ“œ ꡬ성은 μ²˜μŒμ΄μ—ˆλŠ”λ°λ„ 1μ‹œκ°„ λ‚΄λ‘œ μ™„μ„±ν–ˆλ‹€.  μ•„λž˜λŠ” server.js 내에 κ΅¬μ„±ν•œ μ½”λ“œ

app.get('/api/geojson', (req, res) => {
  const { scenario, hazardtype } = req.query;

  try {
    // 필터링 둜직
    const filteredData = geoJsonData.features.filter(feature => {
      const matchesScenario = scenario ? feature.properties.scenario === scenario : true;
      const matchesHazardType = hazardtype ? feature.properties.hazard_type.toLowerCase() === hazardtype.toLowerCase() : true;
      return matchesScenario && matchesHazardType;
    });

    res.json({
      type: 'FeatureCollection',
      features: filteredData,
    });
  } catch (error) {
    console.error('Error filtering geoJSON data:', error);
    res.status(500).json({ message: 'Server error' });
  }
});

κ·ΈλŸ¬λ‚˜ ...

이전과 λ‹€λ₯Ό λ°” μ—†λŠ” 처리 속도...!!! 거의 0.9μ΄ˆμ—μ„œ 1초 정도 κ±Έλ¦¬λŠ”λ°, 체감상 1λΆ„μ΄μ—ˆμŒ. μ™œλƒλ©΄...γ…Žγ…Ž

DBλ„μž…κΉŒμ§€ ν•  λ§Œν•œ λ°μ΄ν„°λŠ” 또 μ•„λ‹ˆλΌκ³  μƒκ°ν•΄μ„œ DBλ₯Ό κ΅¬μΆ•ν•˜μ§€ μ•Šμ•˜λŠ”λ°, (즉, λ°μ΄ν„°λŠ” ν”„λ‘ νŠΈλ‹¨μ— μžˆλŠ”κ±°μž„)
사싀상 κ·Έλ ‡λ‹€λ©΄ 크게 μ˜λ―Έκ°€ μ—†λ‹€λŠ” 것이닀. 

달라진 점은 λΌλ””μ˜€ λ²„νŠΌ 클릭이 λΉ λ₯΄λ‹€λŠ” 것? γ…Žγ…Ž ν”„λ‘ νŠΈμ—μ„œ ν•„ν„°λ§ν•΄μ„œ κ°€μ Έμ˜¬λ•ŒλŠ” 데이터 μ²˜λ¦¬κ°€ μ™„λ£Œλœ 이후에 λΌλ””μ˜€ λ²„νŠΌμ΄ λˆŒλ ΈλŠ”λ°, 이제 데이터 처리 μžμ²΄λŠ” λ°±μ•€λ“œμ—μ„œ λ‹΄λ‹Ήν•˜λ‹ˆ, λΌλ””μ˜€ λ²„νŠΌ 처리 둜직만 μˆ˜ν–‰ν•˜λŠ” ν”„λ‘ νŠΈλŠ” λΉ λ₯΄κ²Œ 이 과정을 μˆ˜ν–‰ν•  수 있게 λ˜μ—ˆλ‹€.

 

γ…‹γ…‹.. μ§„μ§œ 의미 μ—†λ‹€λŠ” 말을 ν•˜λŠ”κ±°μž„....................

 

κ·Έλž˜μ„œ κ³ λ―Όν•˜κΈ° μ‹œμž‘ν–ˆλ‹€. 

ν•˜... DBλ₯Ό μ§„μ§œ ν•΄...? 

κ·ΈλŸ¬λ‹€ 문득 λ„€νŠΈμ›Œν¬ 탭을 보닀가 필터링 ν•˜λŠ” λ‘œμ§μ„ 반볡적으둜 ν•˜κ³  μžˆλ‹€λŠ” 사싀을 μ•Œκ²Œ λ˜μ—ˆλ‹€. 씁 그러면 ν•„ν„°λ§ν•˜λŠ” κ±Έ ν•œ 번만 ν•˜λ©΄ μ•ˆλ˜λ‚˜? κ·Έλž˜μ„œ λ°”λ‘œ 캐싱이 λ– μ˜¬λΌμ„œ 이λ₯Ό λ¨Όμ € ν•΄λ³΄κ³ μž ν–ˆλ‹€. μ•„μ£Όμ•„μ£Ό λΉ λ₯Έ μ†λ„κΉŒμ§€ ν•„μš”ν•œ 것은 μ•„λ‹ˆκ³ , MVPλ₯Ό λ§Œλ“œλŠ”κ²Œ λͺ©μ μ΄κΈ° λ•Œλ¬Έμ—.

 

2. μ›Ή μ›Œμ»€μ™€ 캐싱

2.1 μ›Ήμ›Œμ»€

λ¨Όμ € μ›Ή μ›Œμ»€λ₯Ό μ‚¬μš©ν•΄μ„œ 데이터 필터링 μž‘μ—…μ„ 메인 μ“°λ ˆλ“œμ—μ„œ λΆ„λ¦¬ν–ˆλ‹€. μ›Ή μ›Œμ»€λŠ” 메인 μ“°λ ˆλ“œμ™€ λ³„λ„λ‘œ λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ‹€ν–‰λ˜λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ μ½”λ“œλ‘œμ„œ, λ³΅μž‘ν•œ 계산 및 데이터 처리 μž‘μ—…μ„ λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μˆ˜ν–‰ν•΄μ€€λ‹€. 이 μ½”λ“œλ₯Ό μ‚¬μš©ν•¨μœΌλ‘œμ¨ 메인 μŠ€λ ˆλ“œμ˜ μœ μ € μΈν„°νŽ˜μ΄μŠ€ 및 μƒν˜Έμž‘μš©μ— 영ν–₯을 쀄인닀. κ·Έλž˜μ„œ 뢈-νŽΈν•˜κ²Œ 느껴쑌던 λΌλ””μ˜€ λ²„νŠΌ 멈좀, 느린 응닡 등이 κ°œμ„ λ  수 μžˆλŠ” 것이닀. 

 

worker.js μ½”λ“œλ₯Ό λ‹€μŒκ³Ό 같이 μƒμ„±ν–ˆλ‹€. 

onmessage = function(e) {
    const { data, scenario, hazardtype } = e.data;
    const filteredData = data.features.filter(feature => {
      const matchesScenario = scenario ? feature.properties.scenario === scenario : true;
      const matchesHazardType = hazardtype ? feature.properties.hazard_type.toLowerCase() === hazardtype.toLowerCase() : true;
      return matchesScenario && matchesHazardType;
    });
    postMessage({ type: 'FeatureCollection', features: filteredData });
  };

그럼 μ›Ή λΈŒλΌμš°μ €λŠ” μ–΄λ–€ μ“°λ ˆλ“œλ“€λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆμ„κΉŒ? μ›Ή λΈŒλΌμš°μ €μ˜ μ£Όμš” μ“°λ ˆλ“œ ꡬ성은 λ‹€μŒκ³Ό κ°™λ‹€.

 

  • 메인 μ“°λ ˆλ“œ (Main Thread)
    • μ—­ν• : μžλ°”μŠ€ν¬λ¦½νŠΈ μ‹€ν–‰, DOM μ‘°μž‘, μŠ€νƒ€μΌ 계산, λ Œλ” 트리 생성 및 λ ˆμ΄μ•„μ›ƒ 계산, νŽ˜μΈνŒ… λ“±
    • νŠΉμ§•: λΈŒλΌμš°μ €μ˜ UI μ—…λ°μ΄νŠΈ 및 μ‚¬μš©μž μƒν˜Έμž‘μš© 처리λ₯Ό λ‹΄λ‹Ήν•©λ‹ˆλ‹€. 단일 μ“°λ ˆλ“œλ‘œ μž‘λ™ν•˜λ©°, 이둜 인해 λΈ”λ‘œν‚Ή μž‘μ—…μ΄ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ Œλ”λŸ¬ ν”„λ‘œμ„ΈμŠ€ (Renderer Process)
    • μ—­ν• : μ›Ή νŽ˜μ΄μ§€μ˜ λͺ¨λ“  μ½˜ν…μΈ λ₯Ό λ Œλ”λ§ν•©λ‹ˆλ‹€. HTML, CSS νŒŒμ‹±, μžλ°”μŠ€ν¬λ¦½νŠΈ μ‹€ν–‰, λ ˆμ΄μ•„μ›ƒ 계산, νŽ˜μΈνŒ… 등을 ν¬ν•¨ν•©λ‹ˆλ‹€.
    • νŠΉμ§•: 일반적으둜 각 νƒ­λ§ˆλ‹€ ν•˜λ‚˜μ˜ λ Œλ”λŸ¬ ν”„λ‘œμ„ΈμŠ€λ₯Ό κ°€μ§‘λ‹ˆλ‹€. 메인 μ“°λ ˆλ“œμ™€ ν˜‘λ ₯ν•˜μ—¬ μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
  • 컴포지터 μ“°λ ˆλ“œ (Compositor Thread)
    • μ—­ν• : λ ˆμ΄μ–΄λ₯Ό λ³‘ν•©ν•˜κ³  GPU에 μ „λ‹¬ν•˜μ—¬ μ΅œμ’… 화면을 λ Œλ”λ§ν•©λ‹ˆλ‹€.
    • νŠΉμ§•: νŽ˜μ΄μ§€μ˜ 슀크둀링 및 μ• λ‹ˆλ©”μ΄μ…˜μ„ λΆ€λ“œλŸ½κ²Œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.
  • μ›Ή μ›Œμ»€ μ“°λ ˆλ“œ (Web Worker Thread)
    • μ—­ν• : λ°±κ·ΈλΌμš΄λ“œ μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€. λ³΅μž‘ν•œ κ³„μ‚°μ΄λ‚˜ 데이터 처리 μž‘μ—…μ„ 메인 μ“°λ ˆλ“œμ™€ λΆ„λ¦¬ν•˜μ—¬ μ‹€ν–‰ν•©λ‹ˆλ‹€.
    • νŠΉμ§•: 메인 μ“°λ ˆλ“œμ™€ λ…λ¦½μ μœΌλ‘œ λ™μž‘ν•˜λ©°, λ©”μ‹œμ§€λ₯Ό 톡해 데이터λ₯Ό μ£Όκ³ λ°›μŠ΅λ‹ˆλ‹€.
  • λ„€νŠΈμ›Œν¬ μ“°λ ˆλ“œ (Network Thread)
    • μ—­ν• : λ„€νŠΈμ›Œν¬ μš”μ²­μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€. HTTP μš”μ²­, μ›Ήμ†ŒμΌ“ μ—°κ²° 등을 λ‹΄λ‹Ήν•©λ‹ˆλ‹€.
    • νŠΉμ§•: λΉ„λ™κΈ°μ μœΌλ‘œ λ„€νŠΈμ›Œν¬ μž‘μ—…μ„ μˆ˜ν–‰ν•˜μ—¬ 메인 μ“°λ ˆλ“œλ₯Ό μ°¨λ‹¨ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • 파일 IO μ“°λ ˆλ“œ (File IO Thread)
    • μ—­ν• : 파일 μ‹œμŠ€ν…œ 접근을 μ²˜λ¦¬ν•©λ‹ˆλ‹€. 파일 읽기, μ“°κΈ° 등을 λ‹΄λ‹Ήν•©λ‹ˆλ‹€.
    • νŠΉμ§•: λΉ„λ™κΈ°μ μœΌλ‘œ 파일 μž‘μ—…μ„ μˆ˜ν–‰ν•˜μ—¬ 메인 μ“°λ ˆλ“œλ₯Ό μ°¨λ‹¨ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

 

이제 데이터 μ²˜λ¦¬λŠ” λλ‚¬μœΌλ‹ˆ, μ΄λ ‡κ²Œ 데이터 μ²˜λ¦¬κ³Όμ •μ„ λ°˜λ³΅ν•˜μ§€ μ•Šλ„λ‘ ν•΄μ„œ λ‘œλ”© 속도λ₯Ό λ†’μ—¬λ³΄μž.

2.2 캐싱

λ‹€μŒκ³Ό 같이 cache.jsλ₯Ό κ΅¬μ„±ν•œλ‹€. μΊμ‹œλ₯Ό μ‚¬μš©ν•˜λ©΄ μ΄λŸ¬ν•œ 필터링 μž‘μ—…μ€ 각 μ‹œλ‚˜λ¦¬μ˜€λ³„λ‘œ ν•œ λ²ˆμ”©λ§Œ μˆ˜ν–‰ν•˜λ©΄ μ΄ν›„μ—λŠ” λ‹€μ‹œ μˆ˜ν–‰ν•˜μ§€ μ•Šμ•„λ„ λ˜λŠ” 것이닀. 

const cache = new Map();

export function getCachedData(key) {
  return cache.get(key);
}

export function setCachedData(key, data) {
  cache.set(key, data);
}

 

2.3 ν”„λ‘ νŠΈμ—μ„œ 적용

 

μΊμ‹œμ™€ μ›Ή μ›Œμ»€λ₯Ό ν”„λ‘ νŠΈμ—μ„œ μ μš©ν•œλ‹€. λ§Œμ•½ μΊμ‹±λœ 데이터가 μ—†μœΌλ©΄ λ°±μ•€λ“œμ—μ„œ 필터링 ν›„ ν΄λΌμ΄μ–ΈνŠΈλ‘œ μ „μ†‘ν•˜κ³ , 이λ₯Ό μ›Ήμ›Œμ»€κ°€ λ°›μ•„ 필터링 λ“± 좔가적인 μž‘μ—…μ„ μˆ˜ν–‰ν•œλ‹€. 이런 데이터λ₯Ό μΊμ‹±ν•˜μ—¬ λ™μΌν•œ μš”μ²­ μ‹œμ— λΉ λ₯΄κ²Œ μ‘λ‹΅ν•˜κ²Œ ν•΄μ€€λ‹€.

 

사싀 μ—¬κΈ°μ„œ λ°±μ•€λ“œμ—μ„œ μ½μ–΄μ˜€λŠ”κ²Œ ν˜„μž¬ μƒν™©μ—μ„œλŠ” ꡳ이 ν•„μš”ν•˜μ§€ μ•Šμ„ 수 μžˆμ§€λ§Œ, ν™•μž₯성을 μœ„ν•΄ κ·Έλƒ₯ 남겨뒀닀.

import { getCachedData, setCachedData } from './cache.js'

const worker = new Worker('worker.js');

        const cacheKey = `${scenario}:${selectedHazardType.toLowerCase()}`;
        const cachedData = getCachedData(cacheKey);

        if (cachedData) {
            console.log('Using cached data');
            processFilteredData(cachedData)
        }else{
            fetch(`http://localhost:5001/api/geojson?scenario=${encodeURIComponent(scenario)}&hazardtype=${encodeURIComponent(selectedHazardType.toLowerCase())}`)
                .then(response => {
                    if (!response.ok) {
                        console.error('Error fetching filtered geoJSON data:', response.statusText);
                        return;
                    }
                    return response.json();
                })
                .then(geoJsonData => {
                    console.log('Filtered Data:', geoJsonData);

                    if (geoJsonData.features.length === 0) {
                        console.log('No features found for scenario:', scenario, 'and hazard type:', selectedHazardType);
                        return;
                    }

                    worker.postMessage({ data: geoJsonData, scenario, hazardtype: selectedHazardType.toLowerCase() });
                    worker.onmessage = function(e) {
                    setCachedData(cacheKey, e.data);
                    processFilteredData(e.data); };

                    worker.onerror = function(e) {
                        console.error('Error processing data in worker:', e);
                    };
                });
        }
    }

 

... 사싀 ν”„λ‘ νŠΈλŠ” 거의 λͺ¨λ₯΄λŠ” μƒνƒœλ‘œ κ°œλ°œμ„ μ‹œμž‘ν–ˆλŠ”λ°, κ³ λ €ν•  뢀뢄이 정말 λ§Žλ‹€λŠ” κ±Έ 느끼고 μžˆλ‹€... μ—­μ‹œ ν•˜λ©΄μ„œ λ°°μš°λ‹ˆκΉŒ μžΌλ”° γ…Žγ…Žγ…Ž