๋ฐ˜์‘ํ˜•

LRU ์บ์‹œ ํŠน์ง•


์บ์‹œ(Cache)๋Š” ๋ฐ์ดํ„ฐ๋‚˜ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ์ผ์‹œ์ ์œผ๋กœ ์ €์žฅํ•˜๋Š” ๊ฒƒ์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋‚˜ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์— ๋ณด๊ด€ํ•ด์„œ ๋™์ผํ•œ ์ •๋ณด๋ฅผ ์š”์ฒญ๋ฐ›์•˜์„ ๋•Œ ๋” ๋น ๋ฅธ ์†๋„๋กœ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

 

LRU ์บ์‹œ๋Š” ๋Œ€ํ‘œ์ ์ธ ์บ์‹œ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ค‘ ํ•˜๋‚˜๋กœ ์ œํ•œ๋œ ์ €์žฅ ๊ณต๊ฐ„์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€์žฅ ์˜ค๋ž˜์ „์— ์‚ฌ์šฉํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค. LRU๋Š” Least Recently Used์˜ ์•ฝ์ž๋กœ ์‚ฌ์šฉํ•œ์ง€ ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ์ •๋„๋กœ ํ•ด์„ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

LRU ์บ์‹œ์—์„  ์กฐํšŒ/์“ฐ๊ธฐ์‹œ ํ•ด๋‹น ๊ฐ’์„ ๊ฐ€์žฅ ์ตœ๊ทผ์— ์‚ฌ์šฉํ•œ ๊ฒƒ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฒŒ ํ•ต์‹ฌ์ด๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ Map ๋“ฑ์„ ์ด์šฉํ•ด์„œ ๊ตฌํ˜„ํ•  ๋• ๊ฐ’์ด ๋’ค์— ์œ„์น˜ํ•  ์ˆ˜๋ก ๊ฐ€์žฅ ์ตœ๊ทผ์— ์‚ฌ์šฉํ•œ ๊ฒƒ์œผ๋กœ ํ‘œ์‹œํ•œ๋‹ค.

 

  1. ์กฐํšŒ : ์บ์‹œ์— ๊ฐ’์ด ์กด์žฌํ•˜๋ฉด ํ•ด๋‹น ๊ฐ’์„ ์บ์‹œ ๋งˆ์ง€๋ง‰(์ตœ์‹ )์œผ๋กœ ์ด๋™์‹œํ‚จ๋‹ค
  2. ์“ฐ๊ธฐ :
    • ์บ์‹œ์— ๊ฐ’์ด ์กด์žฌํ•˜๋ฉด ๊ธฐ์กด ๊ฐ’์„ ์‚ญ์ œํ•˜๊ณ , ์บ์‹œ ๋งˆ์ง€๋ง‰์— ๊ธฐ์กด ๊ฐ’์„ ์ถ”๊ฐ€ํ•œ๋‹ค
    • ์บ์‹œ๊ฐ€ ๊ฐ€๋“ ์ฐผ๋‹ค๋ฉด ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ์•ž์ชฝ ๊ฐ’์„ ์ œ๊ฑฐํ•˜๊ณ , ์บ์‹œ ๋งˆ์ง€๋ง‰์— ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ถ”๊ฐ€ํ•œ๋‹ค

 

 

LRU ์บ์‹œ ๊ตฌํ˜„


class LRUCache {
  constructor(capacity) {
    /** ์บ์‹œ ์ตœ๋Œ€ ์šฉ๋Ÿ‰ ์„ค์ • */
    this.capacity = capacity;
    /** ์‚ฝ์ž… ์ˆœ์„œ๋ฅผ ๊ธฐ์–ตํ•˜๋Š” Map์œผ๋กœ ์บ์‹œ ๊ด€๋ฆฌ, ๊ฐ€์žฅ ์ตœ๊ทผ์— ์‚ฌ์šฉํ•œ key๊ฐ€ ๋’ค์— ์œ„์น˜ */
    this.cache = new Map();
  }

  /**
   * ์ฃผ์–ด์ง„ key์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์„ ์บ์‹œ์—์„œ ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์„œ๋“œ
   * ๊ฐ’์ด ์กด์žฌํ•˜๋ฉด key๋ฅผ ์บ์‹œ์˜ ๋์œผ๋กœ ์œ„์น˜์‹œ์ผœ์„œ ๊ฐ€์žฅ ์ตœ๊ทผ์— ์‚ฌ์šฉํ•œ ๊ฒƒ์œผ๋กœ ํ‘œ์‹œ (LRU ์บ์‹œ ํŠน์ง•)
   */
  get(key) {
    if (!this.cache.has(key)) return -1;

    const value = this.cache.get(key);
    this.cache.delete(key);
    this.cache.set(key, value); // ์กฐํšŒํ•œ key๋ฅผ ์บ์‹œ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์œ„์น˜ ์‹œํ‚ด
    return value;
  }

  /**
   * ์ƒˆ๋กœ์šด key-value ์Œ์„ ์บ์‹œ์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ฉ”์„œ๋“œ
   * key๊ฐ€ ์กด์žฌํ•˜๋ฉด ํ•ด๋‹น key๋ฅผ ์‚ญ์ œํ•˜๊ณ  ์บ์‹œ ๋งˆ์ง€๋ง‰์— ์ƒˆ ํ•ญ๋ชฉ์œผ๋กœ ์ถ”๊ฐ€
   * ์บ์‹œ๊ฐ€ ๊ฐ€๋“ ์ฐผ๋‹ค๋ฉด, ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ์•ž์ชฝ key๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ์บ์‹œ ๋งˆ์ง€๋ง‰์— ์ƒˆ ํ•ญ๋ชฉ ์ถ”๊ฐ€
   */
  put(key, value) {
    if (this.cache.has(key)) {
      this.cache.delete(key); // key๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๋ฉด ์‚ญ์ œ
    } else if (this.cache.size === this.capacity) {
      const oldestKey = this.cache.keys().next().value; // this.cache์˜ ์ฒซ๋ฒˆ์งธ ๊ฐ’(์‚ฌ์šฉํ•œ์ง€ ๊ฐ€์žฅ ์˜ค๋ž˜๋œ key)
      this.cache.delete(oldestKey);
    }
    this.cache.set(key, value); // ์ƒˆ ํ•ญ๋ชฉ์„ ์บ์‹œ ๋งˆ์ง€๋ง‰์— ์ถ”๊ฐ€
  }
}

 

ํ”„๋กœํผํ‹ฐ

this.capacity ํ”„๋กœํผํ‹ฐ๋ฅผ ํ†ตํ•ด ์บ์‹œ๊ฐ€ ๋ณด๊ด€ํ•  ์ˆ˜ ์žˆ๋Š” ํ•ญ๋ชฉ์˜ ์ˆ˜๋ฅผ ์ œํ•œํ•œ๋‹ค. ์ด ๊ฐ’์„ ์ดˆ๊ณผํ•  ๊ฒฝ์šฐ ์บ์‹œ์˜ ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ํ•ญ๋ชฉ์„ ์ œ๊ฑฐํ•˜์—ฌ ์ƒˆ ํ•ญ๋ชฉ์„ ์ €์žฅํ•  ๊ณต๊ฐ„์„ ๋งŒ๋“ ๋‹ค.

 

์บ์‹œ ๋ฐ์ดํ„ฐ this.cache๋Š” ์‚ฝ์ž… ์ˆœ์„œ๋ฅผ ๊ธฐ์–ตํ•˜๋Š” Map์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค. LRU ์บ์‹œ์—์„œ ๊ฐ€์žฅ ์ตœ๊ทผ์— ์‚ฌ์šฉํ•œ ํ‚ค๋Š” Map ๋’ท๋ถ€๋ถ„์— ์œ„์น˜์‹œํ‚จ๋‹ค. ์ƒˆ ํ•ญ๋ชฉ์„ ์บ์‹œ์— ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ธฐ์กด ํ•ญ๋ชฉ์„ ๋‹ค์‹œ ์‚ฌ์šฉํ•  ๋•, ํ•ด๋‹น ํ•ญ๋ชฉ์„ Map ๋’ท๋ถ€๋ถ„์œผ๋กœ ์ด๋™ํ•˜์—ฌ ์ตœ๊ทผ ์‚ฌ์šฉํ•จ์œผ๋กœ ๋‚˜ํƒ€๋‚ธ๋‹ค.

 

Get

์บ์‹œ์—์„œ ์ฃผ์–ด์ง„ key์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์„œ๋“œ. ์กฐํšŒํ•  key๊ฐ€ ์บ์‹œ์— ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด -1์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์กด์žฌํ•œ๋‹ค๋ฉด ํ•ด๋‹น key๋ฅผ ์บ์‹œ ๋งˆ์ง€๋ง‰์— ์œ„์น˜์‹œ์ผœ์„œ ์ตœ๊ทผ์— ์‚ฌ์šฉํ•œ ๊ฒƒ์œผ๋กœ ํ‘œ์‹œํ•œ๋‹ค.

 

Set

์ƒˆ๋กœ์šด key-value ์Œ์„ ์บ์‹œ์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ฉ”์„œ๋“œ. ์บ์‹œ์— ์ด๋ฏธ key๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด ๊ธฐ์กด key๋ฅผ ์‚ญ์ œํ•˜๊ณ  ์บ์‹œ ๋งˆ์ง€๋ง‰์— ์ƒˆ ํ•ญ๋ชฉ์œผ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค. ์บ์‹œ๊ฐ€ ๊ฐ€๋“ ์ฐผ๋‹ค๋ฉด ์‚ฌ์šฉํ•œ์ง€ ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ์•ž์ชฝ key๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ์บ์‹œ ๋งˆ์ง€๋ง‰์— ์ƒˆ ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

 

์ดํ„ฐ๋ ˆ์ดํ„ฐ โญ

์—ฌ๊ธฐ์„œ ์ฃผ๋ชฉํ•  ์ ์€ ๊ฐ€์žฅ ์˜ค๋ž˜๋œ key๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์‚ฌ์šฉํ•œ next() ๋ฉ”์„œ๋“œ๋‹ค. this.cache.keys()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์บ์‹œ ํ‚ค๋“ค์„ ์ˆœํšŒํ•  ์ˆ˜ ์žˆ๋Š” ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๐Ÿ” ๋ฐฐ์—ด์—์„  Symbol.iterator ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•ด์„œ ํ˜ธ์ถœํ•˜๋ฉด ์ดํ„ฐ๋ ˆ์ดํ„ฐ ๊ฐ์ฒด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

const cache = new Map();
cache.set('a', 1);
cache.set('b', 2);
cache.set('c', 3);
cache.keys(); // MapIterator {'a', 'b', 'c'}

 

Symbol.iterator ๋ฉ”์„œ๋“œ๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ์ดํ„ฐ๋Ÿฌ๋ธ” ๊ฐ์ฒด๋ผ๊ณ  ๋ถ€๋ฅด๋Š”๋ฐ, ๋ฐฐ์—ด / ๋ฌธ์ž์—ด / Map / Set ๋“ฑ์ด ๋Œ€ํ‘œ์ ์ธ ์ดํ„ฐ๋Ÿฌ๋ธ”์ด๋‹ค. Symbol.iterator ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ดํ„ฐ๋ ˆ์ดํ„ฐ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ดํ„ฐ๋ ˆ์ดํ„ฐ ๊ฐ์ฒด ์•ˆ์—๋Š” next() ๋ฉ”์„œ๋“œ๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค.

 

next() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด value, done ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ–๋Š” ์ดํ„ฐ๋ ˆ์ดํ„ฐ ๋ฆฌ์ ˆํŠธ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. value๋Š” ํ˜„์žฌ ์ˆœํšŒ์ค‘์ธ ์ดํ„ฐ๋Ÿฌ๋ธ”์˜ ๊ฐ’์ด๊ณ , done์€ ๋ฐ˜๋ณต ์ข…๋ฃŒ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

 

์ด๋Ÿฌํ•œ ํŠน์„ฑ ๋•Œ๋ฌธ์— next() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค value ํ”„๋กœํผํ‹ฐ๋ฅผ ํ†ตํ•ด ๊ฐ’์„ ์ˆœ์ฐจ์ ์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. for of ๋ฌธ์œผ๋กœ ๊ฐ’์„ ์ˆœํšŒํ•  ๋•Œ๋„ ๋‚ด๋ถ€์ ์œผ๋กœ next() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ value ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ.

const array = [1, 2, 3]; // ๋ฐฐ์—ด์€ ์ดํ„ฐ๋Ÿฌ๋ธ” ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜ํ•œ ์ดํ„ฐ๋Ÿฌ๋ธ”์ด๋‹ค
const iterator = array[Symbol.iterator](); // next ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ–๋Š” ์ดํ„ฐ๋ ˆ์ดํ„ฐ ๊ฐ์ฒด ๋ฐ˜ํ™˜

// next() ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ดํ„ฐ๋ ˆ์ดํ„ฐ ๋ฆฌ์ ˆํŠธ ๊ฐ์ฒด๋Š” value์™€ done ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ–๋Š”๋‹ค
iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
iterator.next(); // { value: 3, done: false }
iterator.next(); // { value: undefined, done: true }

 

this.cache.keys()๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ดํ„ฐ๋ ˆ์ดํ„ฐ์˜ ํ”„๋กœํ† ํƒ€์ž…์„ ์‚ดํŽด๋ณด๋ฉด next() ๋ฉ”์„œ๋“œ๊ฐ€ ๊ตฌํ˜„๋ผ ์žˆ๋‹ค. ์œ„์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ next() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ดํ„ฐ๋ ˆ์ดํ„ฐ ๋ฆฌ์ ˆํŠธ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ด ๊ฐ์ฒด์˜ value ํ”„๋กœํผํ‹ฐ๋ฅผ ํ†ตํ•ด ์ˆœํšŒ์ค‘์ธ ์ดํ„ฐ๋Ÿฌ๋ธ”์˜ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

 

 

const iterator = cache.keys();
iterator.next(); // {value: 'a', done: false}
iterator.next(); // {value: 'b', done: false}
iterator.next(); // {value: 'c', done: false}
iterator.next(); // {value: undefined, done: true}

 

์œ„์—์„œ ๊ตฌํ˜„ํ•œ LRU Cache ์—์„  put ๋ฉ”์„œ๋“œ์— ์žˆ๋Š” this.cache.keys().next()๋ฅผ 1ํšŒ ํ˜ธ์ถœ ํ–ˆ์œผ๋ฏ€๋กœ next().value๋Š” ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ๊ฐ’์„ ๊ฐ€๋ฆฌํ‚ค๋Š”, this.cache์˜ ์ฒซ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•