๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป DEV/Database

[Database] Express/NodeJS ์—์„œ Redis ์‚ฌ์šฉํ•˜๊ธฐ

by vodkassi 2021. 7. 18.
728x90

 

โœจ Redis ๋ž€? 

Remote Dictionary Server (DB) ์ด๋ฉฐ, In-memory Data structure Store ๋กœ, ๋ฉ”๋ชจ๋ฆฌ ์ƒ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•œ๋‹ค. ๋‹ค์–‘ํ•œ ์ž๋ฃŒ ๊ตฌ์กฐ๋ฅผ ์ €์žฅํ•˜์ง€๋งŒ, ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋Š” Key-Value ์Œ์„ ์ด๋ฃจ๊ธฐ ๋•Œ๋ฌธ์— NoSQL DB ๋กœ ํ™œ์šฉ๋œ๋‹ค. ๋””์Šคํฌ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด ์†๋„๊ฐ€ ๋Š๋ฆฐ RDBMS ๋“ฑ์˜ DBMS ๋ณด๋‹ค ํ™œ์šฉ๋„๊ฐ€ ๋†’์€ ์ผ€์ด์Šค๊ฐ€ ์ข…์ข… ์žˆ๋Š”๋ฐ, ๊ทธ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๊ฐ€ ์บ์‹œ (Cache) ์ด๋‹ค.

 

ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„์—๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ฌ๋ผ๋Š” ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์„œ๋ฒ„๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์–ด์˜ค๊ฒŒ ๋˜๋Š”๋ฐ, ๋™์ผํ•œ ์š”์ฒญ์ด ์—ฌ๋Ÿฌ ๋ฒˆ ์˜ฌ ๊ฒฝ์šฐ ๊ณ„์† ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ๋Š”๋‹ค. ์ด๋Š” ์ƒ๋‹นํžˆ ๋น„ํšจ์œจ์ ์ด๋ฉฐ, ๋ฐ์ดํ„ฐ์˜ ํฌ๊ธฐ์— ๋”ฐ๋ผ ์‘๋‹ต ์†๋„๊ฐ€ ๊ณ„์† ์ง€์—ฐ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ข‹์ง€ ์•Š์€ ์‚ฌ์šฉ ๊ฒฝํ—˜์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค. 

 

์ด ๋•Œ, ์š”์ฒญ ๊ฒฐ๊ณผ๋ฅผ ๋ฏธ๋ฆฌ ์ €์žฅํ•ด๋‘์—ˆ๋‹ค๊ฐ€ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป๊ณ ์ž ํ•˜๋Š” ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์ €์žฅํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”๋กœ ์ค„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด "์บ์‹œ" ๋‹ค. ์บ์‹œ๋Š” ์ž์ฃผ ๋ฐ”๋€Œ์ง€ ์•Š์ง€๋งŒ ์ž์ฃผ ์ ‘๊ทผ๋˜๋ฉฐ, ์‚ฌ์šฉ์ž์—๊ฒŒ ๋น ๋ฅด๊ฒŒ ์ „๋‹ฌ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— SSD ๋“ฑ์˜ Storage ๊ฐ€ ์•„๋‹Œ Main memory ์— ์ €์žฅ๋œ๋‹ค. Redis ์—ญ์‹œ ์„œ๋ฒ„์— ์บ์‹œ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” in memory ์ค‘ ํ•˜๋‚˜์ด๋‹ค. 

 

โœจ Redis ์—์„œ ์ง€์›ํ•˜๋Š” ์ž๋ฃŒ ๊ตฌ์กฐ

  • String
  • List
  • Set
  • Sorted Set
  • Hash (Object)
  • Etc..

 

โœจ Redis ์™€ ์บ์‹œ ์„œ๋ฒ„ 

3-tier ์•„ํ‚คํ…์ณ์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ„์˜ ํ†ต์‹ ์ด ์ด๋ฃจ์–ด์ง„๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์—๊ฒŒ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์„œ๋ฒ„๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์–ด์™€ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์‘๋‹ตํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋ ๋Š”๋ฐ, ์ด ๋•Œ Redis ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์‘๋‹ต ์†๋„๋ฅผ ํ˜„์ €ํžˆ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์Œ ์ž‘์—…์„ ํ†ตํ•ด ์ด ๋‚ด์šฉ์˜ ์˜๋ฏธ๋ฅผ ๋ช…ํ™•ํžˆ ํ•˜๊ณ ์ž ํ•œ๋‹ค. 

 

1. Redis ์„ค์น˜ ๋ฐ ์‹คํ–‰ํ•˜๊ธฐ  (MacOS)

 

Redis ์„ค์น˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋กœ์ปฌํ˜ธ์ŠคํŠธ์— ์„ค์น˜๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค. 

$ wget https://download.redis.io/releases/redis-6.2.4.tar.gz
$ tar xzf redis-6.2.4.tar.gz
$ cd redis-6.2.4
$ make
$ redis-server
$ redis-cli

redis-server ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ณ , redis-cli ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด redis ์„œ๋ฒ„์— ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ ‘์† ํ›„ cli ์ฐฝ์ด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค๋ฉด ์„ฑ๊ณต์ด๋‹ค.

 

( ์ด ๋•Œ ์ปดํ“จํ„ฐ๋ฅผ ์žฌ๋ถ€ํŒ…ํ•˜๊ฒŒ ๋  ๊ฒฝ์šฐ, redis-cli ์ž…๋ ฅ ์ „ ํ„ฐ๋ฏธ๋„์— redis-server ๋ช…๋ น์–ด๋ฅผ ๊ผญ ๋จผ์ € ์ž…๋ ฅํ•ด์ฃผ์–ด์•ผ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒฝ์šฐ Could not connect to Redis at 127.0.0.1:6379: Connection refused ์™€ ๊ฐ™์€ ์—๋Ÿฌ๋ฅผ ๋งŒ๋‚˜๊ฒŒ ๋œ๋‹ค.)

 

2. ExpressJs ์„œ๋ฒ„์™€ Redis ์„œ๋ฒ„ ์—ฐ๊ฒฐํ•˜๊ธฐ 

 

Redis ์บ์‹œ ์„œ๋ฒ„๋ฅผ ํ™œ์šฉํ•˜๋ ค๋ฉด ์ž‘๋™ํ•˜๋Š” ์„œ๋ฒ„ ํ•˜๋‚˜๋ฅผ ๋จผ์ € ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Express ๋ชจ๋“ˆ์„ ํ™œ์šฉํ•ด ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ ๋‹ค. 

 

const express = require('express');
const axios = require('axios');
const cors = require('cors');

const app = express()
app.use(cors())

app.listen(3001, () => console.log('Listening on port 3001'))

 

Redis ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด redis ๋ชจ๋“ˆ์„ ์„ค์น˜ํ•œ๋‹ค.

 

 

$ npm i redis

 

๋งŒ๋“ค์–ด ๋†“์€ ์„œ๋ฒ„์™€ Redis ์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•œ๋‹ค. 

 

const express = require('express');
const axios = require('axios');
const cors = require('cors');
const Redis = require('redis'); // Redis ๋ชจ๋“ˆ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

const redisClient = new Redis.createClient(); 
const DEFAULT_EXPIRATION = 3600 //seconds 

const app = express()
app.use(cors())

app.listen(3001, () => console.log('Listening on port 3001'))

 

createClient() ๋ฉ”์„œ๋“œ๋Š” ์ƒˆ๋กœ์šด RedisClient ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ, redis ์„œ๋ฒ„์™€ express ์„œ๋ฒ„๊ฐ€ ๊ฐ™์€ ํ˜ธ์ŠคํŠธ์—์„œ ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋‹ค๋ฉด createClient() ์— ๋ณ„๋„์˜ ์„ค์ •์„ ํ•ด ์ฃผ์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ๋งŒ์•ฝ ํ˜ธ์ŠคํŠธ๊ฐ€ ๋‹ค๋ฅด๋‹ค๋ฉด ํ˜ธ์ŠคํŠธ url, ํฌํŠธ ๋ฒˆํ˜ธ ๋“ฑ์˜ ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. 

 

์—ฌ๊ธฐ๊นŒ์ง€ ์™”๋‹ค๋ฉด redisClient ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด redis ์„œ๋ฒ„์— ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆด ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. ์ง€๊ธˆ๋ถ€ํ„ฐ ๋ณธ๊ฒฉ์ ์œผ๋กœ redis ์„œ๋ฒ„๊ฐ€ ์–ด๋–ป๊ฒŒ ์บ์‹ฑ์„ ํ•˜๊ฒŒ ๋˜๋Š”์ง€ ๋ณด์ž. 

 

3. DB ๋ฅผ ์–ป์–ด์˜ค๊ธฐ ์ „, Redis ์„œ๋ฒ„์—์„œ ์บ์‹œ๋ฅผ ๋จผ์ € ํ™•์ธํ•˜๊ธฐ 

 

ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„์— "photos ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋‹ฌ๋ผ" ๋Š” ์š”์ฒญ์„ ๋ณด๋‚ผ ๊ฒฝ์šฐ, ์ผ๋ฐ˜์ ์œผ๋กœ ์„œ๋ฒ„๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค (๋˜๋Š” api) ์— ์š”์ฒญ์— ๋งž๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•œ ์ฝ”๋“œ์™€ ์‘๋‹ต์— ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 

 

// get all photos from api
app.get('/photos', async(req, res) => {
    const albumId = req.query.albumId
    const {data} = await axios.get(
        'https://jsonplaceholder.typicode.com/photos',
        {params : {albumId}},
        )
    res.json(data)
})

3.25 ์ดˆ๋ผ๋‹ˆ 

 

๋ณด์ด๋‹ค์‹œํ”ผ ์ฝ”๋“œ๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐ๋Š” ๋ฌธ์ œ๊ฐ€ ์—†๊ฒ ์ง€๋งŒ, '/photos' path ๋กœ ์š”์ฒญ์ด ์˜ฌ ๋•Œ๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋™์ผํ•œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋  ๊ฒƒ์ด๋ฉฐ, ๋งค๋ฒˆ 3.25 ์ดˆ์— ๋‹ฌํ•˜๋Š” ์‘๋‹ต ์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ตœ์ดˆ ์š”์ฒญ ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ redis ์„œ๋ฒ„์— ์บ์‹ฑํ•ด ๋‘์—ˆ๋‹ค๊ฐ€ ๋‹ค์Œ์— ๋™์ผํ•œ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ ์บ์‹œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์‘๋‹ต ์‹œ๊ฐ„์ด ์ค„๊ฒŒ ๋œ๋‹ค. 

 

์ด๋ฅผ ์œ„ํ•œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

// check if redis server has data, if not get from api, store in redis and return
app.get('/photos', async(req, res) => {
    const albumId = req.query.albumId
    redisClient.get('photos', async (err, data) => { // check data from redis server 
        if (err) console.error(error)
        if (data) {
            res.json(JSON.parse(data)) // cache hit
        } else {  // cache miss 
            const {data} = await axios.get(
                'https://jsonplaceholder.typicode.com/photos',
                {params : {albumId}},
                )
            redisClient.setex('photos', DEFAULT_EXPIRATION, JSON.stringify(data)) // set with an expiration time (or can use other redis expressions
            // redis can only store strings, so we need to convert the data to a string        
            res.json(data)
        }
    })

 

์œ„ ์ฝ”๋“œ์˜ ๋กœ์ง์„ ์„ธ๋ถ€์ ์œผ๋กœ ์‚ดํŽด๋ณด์ž. 

 

์šฐ์„ , '/photos' ๋กœ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด redisClient ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด redis ์„œ๋ฒ„์— ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹ฑ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. get ๋ฉ”์„œ๋“œ๋Š” ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ key ๊ฐ’์„ ๋ฐ›์•„๋“ค์ด๊ณ , ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค. ์ด ๊ฒฝ์šฐ 'photos' ๋กœ ์ €์žฅ๋˜์–ด ์žˆ๋Š” Value ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ์บ์‹œ๊ฐ’์ด ์กด์žฌํ•œ๋‹ค๋ฉด (Cache hit) ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

'photos' ๋กœ ์ €์žฅ๋˜์–ด ์žˆ๋Š” value ๊ฐ€ ์—†๋‹ค๋ฉด (Cache miss), DB ์—์„œ ๋ฐ›์•„์™€ ์šฐ์„  redis ์— ์บ์‹ฑํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ setex ๋กœ, ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  key ๊ฐ’๊ณผ ์œ ํšจ์‹œ๊ฐ„, ์ €์žฅํ•  value ๊ฐ’์„ ์ „๋‹ฌ์ธ์ž๋กœ ๋ฐ›๋Š”๋‹ค. ์บ์‹ฑํ•œ ์ดํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

์•ž์„œ ์ด๋ฏธ ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹ฑ๋˜์—ˆ์œผ๋ฏ€๋กœ, ๋‘ ๋ฒˆ์งธ ์š”์ฒญ์— ๊ฑธ๋ฆฌ๋Š” ์‘๋‹ถ ์‹œ๊ฐ„์€ 22 ms (millisecond) ์ด๋‹ค. ์ฒซ ๋ฒˆ์งธ ์š”์ฒญ ๋•Œ 3.5 ์ดˆ๊ฐ€ ๊ฑธ๋ ธ๋˜ ๊ฒƒ์„ ์ƒ๊ฐํ•˜๋ฉด ๋งค์šฐ ํฐ ๋ฐœ์ „์ด๋‹ค.  

 

์บ์‹ฑ์˜ ์œ„๋ ฅ

 

4. session-store ์™€ ํ•จ๊ป˜ ํ™œ์šฉํ•˜๊ธฐ

 

Redis ๋ฅผ ์œ ์šฉํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ ๋˜ ํ•˜๋‚˜๋Š” session ์ด๋‹ค. http ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฌด์ƒํƒœ์„ฑ (stateless) ์„ ๋ ๋Š”๋ฐ, ์œ ์ €์˜ ์„ธ์…˜ ์ •๋ณด๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ˆ˜๋‹จ์œผ๋กœ ์„ธ์…˜ ๋˜๋Š” ํ† ํฐ์„ ํ™œ์šฉํ•˜๊ณค ํ•œ๋‹ค. ์„ธ์…˜์„ ์‚ฌ์šฉํ•  ๋•Œ ์„ธ์…˜ ์ •๋ณด๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ฒ„์˜ ๋ฉ”๋ชจ๋ฆฌ์— ์— ์ €์žฅ๋˜๋Š”๋ฐ, ์„œ๋ฒ„๊ฐ€ ๊บผ์ง€๋ฉด ์„ธ์…˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ค ๋‚ ์•„๊ฐ€๊ฒŒ ๋œ๋‹ค. redis ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ธ์…˜๊ด€๋ฆฌ๋ฅผ ํ•˜๋ฉด ์„œ๋ฒ„๊ฐ€ ๊บผ์ ธ๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ง€๋˜๋ฉฐ, ๋ณต์ˆ˜ ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ๋„  ์„ธ์…˜์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. 

์•ž์„œ ์ƒ์„ฑํ–ˆ๋˜ redisClient ๊ฐ์ฒด๋ฅผ session ์˜ ์ €์žฅ์†Œ๋กœ ์ง€์ •ํ•ด์ฃผ๋ฉด session store ์™€ Redis ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

const session = require('express-session')
let RedisStore = require('connect-redis')(session)

// use redis for session store 
app.use(session({
    secret: "secret",
    saveUninitialized: false,
    resave: false,
    store: new RedisStore(redisClient)
}))

 

 

โœจ ๋งˆ๋ฌด๋ฆฌ

Redis ๋Š” ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์†๋„์™€ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์—ญํ• ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฌผ๋ก  ๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด in-memory ์™€ disk ์˜ ์ฐจ์ด, RAM ๋ฐ์ดํ„ฐ์˜ ํœ˜๋ฐœ์„ฑ ์ด์Šˆ ๋“ฑ์„ ์‚ฌ์ „์— ์ž˜ ์•Œ์•„๋‘์–ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค. 

 

 

โœจ ์ฐธ๊ณ  ์ž๋ฃŒ

What is Redis and What Does It Do?

Redis Crash Course

Redis ๊ธฐ๋ณธ ์ •๋ฆฌ 

 

๋Œ“๊ธ€