DBMS/Redis

[REDIS] ๐Ÿ“š Node.js ์—์„œ redis ๋ชจ๋“ˆ ์‚ฌ์šฉ๋ฒ• (์บ์‹ฑ & ์„ธ์…˜ ์Šคํ† ์–ด)

์ธํŒŒ_ 2022. 7. 12. 09:09

node-redis

Node ํ”„๋กœ์ ํŠธ์—์„œ pm2๋กœ ๋‹ค์ค‘ ํด๋Ÿฌ์Šคํ„ฐ ์ธํ”„๋ผ๋ฅผ ๊ตฌ์ถ•ํ–ˆ๋‹ค๋ฉด ์„ธ์…˜ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋งˆ๋ จ์ด๋‹ค.

๋งŒ์ผ ์„œ๋ฒ„๊ฐ€ ์ข…๋ฃŒ๋˜์–ด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋‚ ๋ผ๊ฐ€๋ฉด ์ ‘์†์ž๋“ค์˜ ๋กœ๊ทธ์ธ์ด ๋ชจ๋‘ ํ’€๋ ค๋ฒ„๋ฆฌ๊ฒŒ ๋œ๋‹ค.

๋”ฐ๋ผ์„œ ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์„ธ์…˜ ์•„์ด๋””์™€ ์‹ค์ œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ํŽธ์ด๋‹ค.

์ด๋•Œ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด Redis db์ด๋‹ค.

๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ, ์„ธ์…˜์€ ๋น ๋ฆฟ๋น ๋ฆฟํ•˜๊ฒŒ ์‘๋‹ต์„ ํ•ด์•ผ๋˜๊ธฐ ๋–„๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ธ ๋ ˆ๋””์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

์ง€๊ธˆ๋ถ€ํ„ฐ Node ํ”„๋กœ์ ํŠธ์—์„œ Redis์™€ ์—ฐ๊ฒฐํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๋ฒ•์„ ์•Œ์•„๋ณด์ž.


Redis Database ์„ค์น˜

๋ ˆ๋””์Šค๋ฅผ ๋กœ์ปฌ์— ์„ค์น˜ํ•ด๋„ ๋˜๊ณ , ํด๋ผ์šฐ๋“œ๋กœ๋„ ์‚ฌ์šฉํ•  ์ˆ˜๊ฐ€ ์žˆ๋‹ค. ๋งˆ์Œ์— ๋“œ๋Š” ๊ฒƒ์„ ๊ณจ๋ผ ์„ค์น˜ํ•˜์ž.

๊ฐœ์ธ์ ์œผ๋กœ ๋กœ์ปฌ ๋ณด๋‹ค๋Š” ํด๋ผ์šฐ๋“œ๋กœ ํ•˜๋Š”๊ฒŒ ๊ด€๋ฆฌ๋‚˜ ํ˜‘์—… ์ธก๋ฉด์—์„œ ํŽธ๋ฆฌํ•˜๊ณ  ๋” ์ข‹์•˜๋‹ค.

 

Redis ๋กœ์ปฌ(์œˆ๋„์šฐ) ์„ค์น˜

 

[REDIS] ๐Ÿ“š Window10 ํ™˜๊ฒฝ์— Redis ์„ค์น˜ & ์„ค์ •

Redis ์œˆ๋„์šฐ ์„ค์น˜ Redis ๋‹ค์šด๋กœ๋“œ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜์—ฌ ์„ค์น˜ ํ”„๋กœ๊ทธ๋žจ์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์„ค์น˜๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค. Releases · microsoftarchive/redis Redis is an in-memory database that persists on disk. The data mo..

inpa.tistory.com

 

Redis ํด๋ผ์šฐ๋“œ ์„ค์น˜

 

[REDIS] ๐Ÿ“š Redis๋ฅผ ํด๋ผ์šฐ๋“œ๋กœ ์‚ฌ์šฉํ•˜์ž [Redislabs]

Redis ํด๋ผ์šฐ๋“œ MySQL์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์›Œํฌ๋ฒค์น˜๋ฅผ ์„ค์น˜ํ–ˆ๋˜ ๊ฒƒ ์ฒ˜๋Ÿผ, ๋ ˆ๋””์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ ˆ๋””์Šค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค. ์ง์ ‘ ์„œ๋ฒ„ ์ปดํ“จํ„ฐ์— ์ง์ ‘ ์„ค์น˜ํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋ ˆ๋””์Šค๋ฅผ ํ˜ธ์Šค

inpa.tistory.com

 

redislabs ๋“ฑ๋ก์„ ๋งž์ท„์œผ๋ฉด, ์„œ๋ฒ„์—์„œ ์œ„์˜ redislabs ์‚ฌ์ดํŠธ์˜ ๋ ˆ๋””์Šค db์— ์ ‘์†ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, ํ•„์š”ํ•œ ์ •๋ณด๊ฐ€ ์žˆ๋Š”๋ฐ ๋ ˆ๋””์Šค host์™€ port, username, password 4๊ฐ€์ง€๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค. (๋งํฌ ์ฐธ๊ณ )

๊ณ„์ •์„ ๋“ฑ๋กํ•˜๊ณ  ์–ป์€ redis db์˜ host์™€ password ์ •๋ณด๋“ค์„ env ํŒŒ์ผ์— ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์ €์žฅํ•ด์ค€๋‹ค.

# .env ํŒŒ์ผ
REDIS_HOST=redis-10943.c114.us-east-1-4.ec2.cloud.redislabs.com
REDIS_PORT=10943
REDIS_USERNAME=default
REDIS_PASSWORD=abcdefghijklmnopqrstuvwsyz

๋…ธ๋“œ์—์„œ Cache ๊ธฐ๋Šฅ ์‚ฌ์šฉํ•˜๊ธฐ

 

Node ↔ Redis v4 ์—ฐ๊ฒฐํ•˜๊ธฐ

 

redis

A modern, high performance Redis client. Latest version: 4.1.0, last published: a month ago. Start using redis in your project by running `npm i redis`. There are 8397 other projects in the npm registry using redis.

www.npmjs.com

> npm i redis # redis ํŒจํ‚ค์ง€ ์„ค์น˜
ํ•ด๋‹น redis ๋ฒ„์ ผ์€ ์ตœ์‹  v4 ๊ธฐ์ค€์œผ๋กœ ์—ฐ์žฌํ•œ๋‹ค.
์ตœ๊ทผ redis v3์—์„œ v4๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ ๋ฌ๋Š”๋ฐ, ๊ทธ๋ƒฅ ์ƒˆ๋กœ์šด ํŒจํ‚ค์ง€ ํ•˜๋‚˜๋ฅผ ๋งŒ๋“ ๊ฒƒ๊ณผ ๊ฐ™์„์ •๋„๋กœ ๋ฌธ๋ฒ•๊ณผ ๋‚ด๋ถ€๋™์ž‘์ด ์™„์ „ํžˆ ๋‹ฌ๋ผ์กŒ๋‹ค.
์ด์ œ redis v4 ๋ฒ„์ ผ์€ ์ฝœ๋ฐฑ ๊ธฐ๋ฐ˜์ด ์•„๋‹Œ ๊ธฐ๋ณธ์ ์œผ๋กœ promise ๊ฐ์ฒด ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ๋กœ ๋™์ž‘๋˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ๋‹ค.
๊ทธ๋ž˜๋„ ์˜›๋‚  ๋ฌธ๋ฒ•์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, createClient({  legacyMode: true }) ๋กœ ๋ ˆ๊ฑฐ์‹œ ๋ชจ๋“œ๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ์—†์ด ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
๊ฐ„๋‹จํ•˜๊ฒŒ v4์—์„œ ๋ฌด์—‡์ด ๋ฐ”๋€Œ์—†๋Š”์ง€ ์‚ดํŽด๋ณด๋ ค๋ฉด, ๋‹ค์Œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜๊ธธ ๋ฐ”๋ž€๋‹ค.
https://github.com/redis/node-redis/blob/HEAD/docs/v3-to-v4.md

 

๋กœ์ปฌ redis์— ์—ฐ๊ฒฐํ•˜๊ธฐ

const express = require('express');
const dotenv = require('dotenv');
const redis = require('redis');

dotenv.config(); // envํ™˜๊ฒฝ๋ณ€์ˆ˜ ํŒŒ์ผ ๊ฐ€์ ธ์˜ค๊ธฐ

//* Redis ์—ฐ๊ฒฐ
const redisClient = redis.createClient({ legacyMode: true }); // legacy ๋ชจ๋“œ ๋ฐ˜๋“œ์‹œ ์„ค์ • !!
redisClient.on('connect', () => {
   console.info('Redis connected!');
});
redisClient.on('error', (err) => {
   console.error('Redis Client Error', err);
});
redisClient.connect().then(); // redis v4 ์—ฐ๊ฒฐ (๋น„๋™๊ธฐ)
const redisCli = redisClient.v4; // ๊ธฐ๋ณธ redisClient ๊ฐ์ฒด๋Š” ์ฝœ๋ฐฑ๊ธฐ๋ฐ˜์ธ๋ฐ v4๋ฒ„์ ผ์€ ํ”„๋กœ๋ฏธ์Šค ๊ธฐ๋ฐ˜์ด๋ผ ์‚ฌ์šฉ

 

redis ํด๋ผ์šฐ๋“œ์— ์—ฐ๊ฒฐํ•˜๊ธฐ

const express = require('express');
const dotenv = require('dotenv');
const redis = require('redis');

dotenv.config(); // envํ™˜๊ฒฝ๋ณ€์ˆ˜ ํŒŒ์ผ ๊ฐ€์ ธ์˜ค๊ธฐ

//* Redis ์—ฐ๊ฒฐ
// redis[s]://[[username][:password]@][host][:port][/db-number]
const redisClient = redis.createClient({
   url: `redis://${process.env.REDIS_USERNAME}:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}/0`,
   legacyMode: true, // ๋ฐ˜๋“œ์‹œ ์„ค์ • !!
});
redisClient.on('connect', () => {
   console.info('Redis connected!');
});
redisClient.on('error', (err) => {
   console.error('Redis Client Error', err);
});
redisClient.connect().then(); // redis v4 ์—ฐ๊ฒฐ (๋น„๋™๊ธฐ)
const redisCli = redisClient.v4; // ๊ธฐ๋ณธ redisClient ๊ฐ์ฒด๋Š” ์ฝœ๋ฐฑ๊ธฐ๋ฐ˜์ธ๋ฐ v4๋ฒ„์ ผ์€ ํ”„๋กœ๋ฏธ์Šค ๊ธฐ๋ฐ˜์ด๋ผ ์‚ฌ์šฉ

Javascript Redis ๋ฌธ๋ฒ•

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ redis ๋ฌธ๋ฒ•์€ ๋Œ€๋ถ€๋ถ„ redis cli ๋ฌธ๋ฒ• ๊ทธ๋Œ€๋กœ ๋ฉ”์„œ๋“œ ํ˜•ํƒœ๋กœ ๋ถ™์—ฌ ์“ฐ๋ฉด ๋œ๋‹ค.

๋งŒ์ผ ์•„๋ž˜์™€ ๊ฐ™์ด String ํ‚ค-๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” redis cli ๋ช…๋ น์–ด๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ redis db์™€ ์—ฐ๊ฒฐํ•ด์ค€ ๊ฐ์ฒด์— redisClient.๋ช…๋ น์–ด(key, value) ์ด๋Ÿฐ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

> set key 123
> get key
// redis v3 ๋ฌธ๋ฒ• (์ฝœ๋ฐฑ ํ•จ์ˆ˜ ๊ธฐ๋ฐ˜)
redisClient.set('key', '123');
redisClient.get('key', (err, value) => {
	console.log(value); // 123
})

// redis v4 ๋ฌธ๋ฒ• (ํ”„๋กœ๋ฏธ์Šค ๊ฐ์ฒด ๊ธฐ๋ฐ˜)
let bool = await redisClient.v4.set('key', '123'); // OK
let data = await redisClient.v4.get('key'); // 123

 

Redis CRUD ๊ตฌํ˜„

const redisCli = redisClient.v4 // ๊ธฐ๋ณธ redisClient ๊ฐ์ฒด๋Š” ์ฝœ๋ฐฑ๊ธฐ๋ฐ˜์ธ๋ฐ v4๋ฒ„์ ผ์€ ํ”„๋กœ๋ฏธ์Šค ๊ธฐ๋ฐ˜์ด๋ผ ์‚ฌ์šฉ

// GET
router.get('/', (req, res, next) => {
   await redisCli.get('username');
});

// POST
router.post('/set', (req, res, next) => {
   await redisCli.set('username', 'inpa');
});

// DELETE
router.delete('/del', (req, res, next) => {
   // exist : ํ‚ค๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€
   const n = await redisCli.exists('username'); // true: 1 , false: 0
   if(n) await redisCli.del('username');
});

// PUT
router.put('/rename', (req, res, next) => {
   // username์ด๋ผ๋Š” ํ‚ค๊ฐ’์ด ์žˆ๋‹ค๋ฉด ๊ทธ ๊ฐ’์„ helloname์œผ๋กœ ๋ฐ”๊ฟˆ
   redisCli.rename('username', 'helloname');
});

 

Redis ์ž๋ฃŒํ˜• ๋ช…๋ น์–ด

 

[REDIS] ๐Ÿ“š ์ž๋ฃŒ๊ตฌ์กฐ ๋ช…๋ น์–ด ์ข…๋ฅ˜ & ํ™œ์šฉ ์‚ฌ๋ก€ ๐Ÿ’ฏ ์ด์ •๋ฆฌ

Redis ๋ฐ์ดํ„ฐ ํƒ€์ž… (Collection) Redis์˜ ์žฅ์  ์ค‘ ํ•˜๋‚˜๋Š” Key-Value ์Šคํ† ๋ฆฌ์ง€์—์„œ Value๋Š” ๋‹จ์ˆœํ•œ Object๊ฐ€ ์•„๋‹ˆ๋ผ ๋‹ค์–‘ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๊ฐ–๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. String, Set, Sorted Set, Hash, List ๋“ฑ ๋‹ค์–‘ํ•œ ํƒ€์ž…์„..

inpa.tistory.com

const redisCli = redisClient.v4 // ๊ธฐ๋ณธ redisClient ๊ฐ์ฒด๋Š” ์ฝœ๋ฐฑ๊ธฐ๋ฐ˜์ธ๋ฐ v4๋ฒ„์ ผ์€ ํ”„๋กœ๋ฏธ์Šค ๊ธฐ๋ฐ˜์ด๋ผ ์‚ฌ์šฉ

// String
await redisCli.set('name', 'nyong')
await redisCli.get('name') // nyong

// List
await redisCli.rpush('fruits', 'apple', 'orange', 'pineapple')
await redisCli.lpush('fruits', 'banana', 'pear')
await redisCli.lrange('fruits', 0, -1) // ['pear', 'banana', 'apple', 'orange', 'apple']
// 0 ๊ณผ -1์€ ์‹œ์ž‘๊ณผ ๋ ์ธ๋ฑ์Šค๋ฅผ ์˜๋ฏธ

// Hash
await redisCli.hmset('friends', 'name', 'nyong', 'age', 30)
await redisCli.hgetall('friends') // { name : 'nyong', age : 30 }

// Set
await redisCli.sadd('fruits', 'apple', 'orange', 'pear', 'banana', 'apple')
await redisCli.smembers('fruits') // ['banana', 'apple', 'orange', 'pear'] // apple์€ 2๊ฐœ์—ฌ์„œ ์ค‘๋ณต์ œ๊ฑฐ

// Sorted Set
await redisCli.zadd('fruits', 1, 'apple', 5, 'orange', 3, 'pear', 4, 'banana', 8, 'grape')
await redisCli.zrange('fruits', 0, -1) // ['apple', 'pear', 'banana', 'orange', 'grape']

 

Redis Events ์ข…๋ฅ˜

connect ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ๋Œ€ํ•œ ์—ฐ๊ฒฐ์ด ๋  ๊ฒฝ์šฐ
ready ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ๋Œ€ํ•œ ์—ฐ๊ฒฐ์„ ์„ฑ๊ณต์ ์œผ๋กœ ์‹œ์ž‘ํ•  ๊ฒฝ์šฐ
end ํด๋ผ์ด์–ธํŠธ๊ฐ€ .quit() ๋˜๋Š” .disconnect()๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„ ์—ฐ๊ฒฐ์„ ๋Š์—ˆ์„ ๊ฒฝ์šฐ
error ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†๊ฑฐ๋‚˜ ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ ์—ฐ๊ฒฐ์ด ๋Š์–ด์ง€๋Š” ๋“ฑ ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ.
reconnecting ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ๋‹ค์‹œ ์—ฐ๊ฒฐ์„ ์‹œ๋„ํ•˜๊ณ  ์žˆ์„ ๊ฒฝ์šฐ
redisClient.on('connect', () => {
  console.info('Redis connected!');
});
redisClient.on('error', (err) => {
  console.error('Redis Client Error', err);
});

 

Redis ํŠธ๋žœ์žญ์…˜(Transactions)

.multi() ๋ฅผ ํ˜ธ์ถœํ•œ ๋‹ค์Œ ๋ช…๋ น ๋ฉ”์†Œ๋“œ๋“ค์„ ์ฒด์ด๋‹์œผ๋กœ ์—ฐ๊ฒฐํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

ํŠธ๋žœ์žญ์…˜์ด ์™„๋ฃŒ๋˜๋ฉด .exec()๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๊ฒฐ๊ณผ๊ฐ€ ํฌํ•จ๋œ ๋ฐฐ์—ด์„ ๋‹ค์‹œ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

await redisCli.set('another-key', 'another-value');

const [setKeyReply, otherKeyValue] = await redisCli
  .multi()
  .set('key', 'value')
  .get('another-key')
  .exec(); // ['OK', 'another-value']

 

์ธ๋ผ์ธ Redis Command

๋งŒ์ผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒจํ‚ค์ง€์—์„œ ์ง€์›ํ•˜๋Š” redis cli ๋ช…๋ น์–ด api๊ฐ€ ์—†์„ ๊ฒฝ์šฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ธ๋ผ์ธ์œผ๋กœ ์ง์ ‘ ์ง€์ •ํ•ด์„œ ๋ณด๋‚ผ์ˆ˜ ์žˆ๋‹ค. (orm์˜ ์ธ๋ผ์ธ ์ฟผ๋ฆฌ ๊ฐ™์€ ๊ฐœ๋…)

await client.sendCommand(['SET', 'key', 'value', 'NX']); // 'OK'

await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2']

Redis ์บ์‹ฑ ์ „๋žต ๊ตฌํ˜„ํ•˜๊ธฐ

 

Cache Aside ํŒจํ„ด ๊ตฌํ˜„ํ•˜๊ธฐ

 

[REDIS] ๐Ÿ“š ์บ์‹œ(Cache) ์„ค๊ณ„ ์ „๋žต ์ง€์นจ ์ด์ •๋ฆฌ

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

inpa.tistory.com

Cache Aside ํŒจํ„ด

app.get('/get', async (req, res) => {
   let value = await redisCli.get(req.key); // redis get key ๋ฅผ ํ•œ๋‹ค.
   if (value) {
      // ๋งŒ์•ฝ redis(์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ)์— ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ → Cache Hit
      res.send(value);
   } else {
      // ๋งŒ์•ฝ redis(์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ)์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค๋ฉด DB์—์„œ ์กฐํšŒ → Cache Miss
      let data = await sequelize.query('SELECT data FROM tables where id = key');
      await redisCli.set(key, data); // ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•˜๊ณ  ๋ฐ˜ํ™˜
      res.send(value);
   }
});

 

Cache Check ๋ฏธ๋“ค์›จ์–ด ํ™œ์šฉ

๋ณด๋‹ค ๊น”๋”ํ•œ ๋ผ์šฐํŒ… ์ฝ”๋“œ๋ฅผ ์›ํ•œ๋‹ค๋ฉด, express์˜ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ• ์ˆ˜๋„ ์žˆ๋‹ค.

// ์บ์‹œ ์ฒดํฌ๋ฅผ ์œ„ํ•œ ๋ฏธ๋“ค์›จ์–ด
const checkCache = (req, res, next) => {
   let value = await redisCli.get(req.key); // redis get key ๋ฅผ ํ•œ๋‹ค.
   if (value) {
      // Redis์— ์ €์žฅ๋œ๊ฒŒ ์กด์žฌํ•˜๋ฉด ๋ฐ”๋กœ ํด๋ผ์ด์–ธํŠธ์— ์‘๋‹ต
      res.send(data);
   } else {
      // Redis์— ์ €์žฅ๋œ๊ฒŒ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด ์‹คํ–‰
      next();
   }
};

app.get('/get', checkCache, async (req, res) => {
   // ๋งŒ์•ฝ redis(์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ)์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค๋ฉด DB์—์„œ ์กฐํšŒ → Cache Miss
   let data = await sequelize.query(`SELECT data FROM tables where id = ${req.key}`);
   await redisCli.set(key, data); // ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•˜๊ณ  ๋ฐ˜ํ™˜
   res.send(value);
});

 

์บ์‹ฑ ๊ธฐ๊ฐ„ ์„ค์ •ํ•˜๊ธฐ

์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์šฉ๋Ÿ‰์ด ๋งค์šฐ ์ ๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹ฑํ• ๋•Œ ํ•ญ์ƒ ๊ธฐ๊ฐ„์„ ์„ค์ •ํ•˜์—ฌ ์ž๋™ ์‚ญ์ œ๋˜๋„๋ก ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.

router.post('/set', (req, res, next) => {
   await redisCli.set('username', 'inpa');
   await redisCli.expire('username', 3600); // 3600์ดˆ ํ›„์— username ํ‚ค ์‚ญ์ œ
   
   if(redisCli.ttl('username') < 300) { 
     // usename ํ‚ค ๋‚จ์€ ์‹œ๊ฐ„์ด 300์ดˆ ์ดํ•˜ ์ผ ๊ฒฝ์šฐ...
   }
});
await redisCli.setex('username', 3600, 'inpa'); // setex ๋ช…๋ น์–ด๋กœ ํ‚ค-๋ฐธ๋ฅ˜์™€ ๊ธฐ๊ฐ„์„ ํ•œ๋ฒˆ์— ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๋…ธ๋“œ์—์„œ Session Store ์‚ฌ์šฉํ•˜๊ธฐ

Redis ๋ฅผ ์œ ์šฉํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ ๋˜ ํ•˜๋‚˜๋Š” session ์ด๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ express-session์—์„œ์˜ ์„ธ์…˜์€ ํœ˜๋ฐœ์„ฑ ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ž ๊น ์ €์žฅ๋˜๊ณ  ์„œ๋ฒ„๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด ์‚ฌ๋ผ์ง€๊ฒŒ ๋œ๋‹ค.

์ด๋•Œ redis db์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ธ์…˜๊ด€๋ฆฌ๋ฅผ ํ•˜๋ฉด ์„œ๋ฒ„๊ฐ€ ๊บผ์ ธ๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ง€๋˜๊ฒŒ ํ• ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ณต์ˆ˜ ์„œ๋ฒ„ ํ™˜๊ฒฝ(ํด๋Ÿฌ์Šคํ„ฐ๋ง)์—์„œ๋„  ์„ธ์…˜์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

node-redis-session


Connect-redis ์„ค์น˜ & ์‚ฌ์šฉ๋ฒ•

connect-redis๋Š” ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ์„ธ์…˜ ๊ณต์œ ๋ฅผ ์œ„ํ•ด ๋ ˆ๋””์Šค์™€ ์ต์Šคํ”„๋ ˆ์Šค๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ํŒจํ‚ค์ง€์ด๋‹ค.

๊ธฐ์กด์—๋Š” ๋กœ๊ทธ์ธํ•  ๋•Œ express-session์˜ ์„ธ์…˜ ์•„์ด๋””์™€ ์‹ค์ œ ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋œ๋‹ค.

์‹ฑ๊ธ€ ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ• ๋• ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ pm2๋ฅผ ํ†ตํ•ด ๋…ธ๋“œ ํด๋Ÿฌ์Šคํ„ฐ ์„œ๋ฒ„ ์ธํ”„๋ผ๋ฅผ ๊ตฌ์ถ•ํ–ˆ์„๋•Œ ์„ธ์…˜ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด๋‹ค.

๋”ฐ๋ผ์„œ ์„ธ์…˜ ๋ถˆ์ผ์น˜๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ด ์„ธ์…˜ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์•„๋‹Œ redis db์— ์ €์žฅํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž.

 

connect-redis

Redis session store for Connect. Latest version: 6.1.3, last published: 3 months ago. Start using connect-redis in your project by running `npm i connect-redis`. There are 867 other projects in the npm registry using connect-redis.

www.npmjs.com

> npm i connect-redis
const express = require('express');
const cookieParser = require('cookie-parser');
const dotenv = require('dotenv');
const session = require('express-session');
const redis = require('redis');
const RedisStore = require('connect-redis')(session); // express-session ๊ฐ์ฒด๋ฅผ ๋„ฃ๋Š”๋‹ค.

dotenv.config(); // envํ™˜๊ฒฝ๋ณ€์ˆ˜ ํŒŒ์ผ ๊ฐ€์ ธ์˜ค๊ธฐ
const app = express();

//* Redis ์—ฐ๊ฒฐ
// redis[s]://[[username][:password]@][host][:port][/db-number]
const redisClient = redis.createClient({
   url: `redis://${process.env.REDIS_USERNAME}:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}/0`,
   legacyMode: true, // ๋ฐ˜๋“œ์‹œ ์„ค์ • !! ์„ค์ • ์•ˆํ•˜๋ฉด connect-redis ๋™์ž‘ ์•ˆํ•จ
});
redisClient.on('connect', () => {
   console.info('Redis connected!');
});
redisClient.on('error', (err) => {
   console.error('Redis Client Error', err);
});
redisClient.connect().then(); // redis v4 ์—ฐ๊ฒฐ (๋น„๋™๊ธฐ)
const redisCli = redisClient.v4; // ๊ธฐ๋ณธ redisClient ๊ฐ์ฒด๋Š” ์ฝœ๋ฐฑ๊ธฐ๋ฐ˜์ธ๋ฐ v4๋ฒ„์ ผ์€ ํ”„๋กœ๋ฏธ์Šค ๊ธฐ๋ฐ˜์ด๋ผ ์‚ฌ์šฉ

//* ์„ธ์…˜ ์ฟ ํ‚ค ๋ฏธ๋“ค์›จ์–ด 
app.use(cookieParser(process.env.COOKIE_SECRET)); 
const sessionOption = {
   resave: false, 
   saveUninitialized: true, 
   secret: process.env.COOKIE_SECRET,
   cookie: {
      httpOnly: true,
      secure: false,
   },
   store: new RedisStore({ client: redisClient, prefix: 'session:' }), // ์„ธ์…˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ์ปฌ ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์•„๋‹Œ redis db์— ์ €์žฅํ•˜๋„๋ก ๋“ฑ๋ก
};
app.use(session(sessionOption));
createClient({  legacyMode: true }) ๋กœ ๋ ˆ๊ฑฐ์‹œ ๋ชจ๋“œ๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด connect-redis ๋ชจ๋“ˆ์ด ๋™์ž‘ ์•ˆํ•˜๋‹ˆ ์ฃผ์˜

sessionOption.store๋Š” ์„ธ์…˜์„ ์–ด๋””์— ์ €์žฅํ• ์ง€ ๊ณ ๋ฅด๋Š” ์˜ต์…˜ ์ •๋ณด๋กœ, ๊ธฐ๋ณธ๊ฐ’์€ ์„œ๋ฒ„์˜ ๋ฉ”๋ชจ๋ฆฌ ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅํ•˜๋Š”๋ฐ, ์ด๊ฒƒ์„ RedisStore๋กœ ๋ฐ”๊พธ๊ฒŒ ๋˜๋ฉด ์„ธ์…˜ ๋ฐ์ดํ„ฐ๋ฅผ Redis์— ์ €์žฅํ•˜๊ฒŒ ๋œ๋‹ค.

์ฆ‰, ์žฌ๊ธฐ๋™์„ ํ•˜์—ฌ๋„ ์„ธ์…˜์ด ๋‚ ๋ผ๊ฐ€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค. 

 

์ด์ œ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ด๋ณด๊ณ  ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์˜ Application ํƒญ์— cookies ํƒญ์„ ํ™•์ธํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด connect.sid ๋กœ ์„ธ์…˜ ์ฟ ํ‚ค๊ฐ€ ์„œ๋ช…๋˜์–ด ์ €์žฅ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

node-redis-session

 

๊ทธ๋ฆฌ๊ณ  redis db๋ฅผ ํ™•์ธํ•ด ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด session:์„ธ์…˜ID ํ˜•ํƒœ๋กœ ํ‚ค๊ฐ€ ์ €์žฅ ๋จ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐ’์„ ์กฐํšŒํ•ด ๋ณด๋ฉด ์„ธ์…˜ ์ฟ ํ‚ค ๊ฐ์ฒด๊ฐ€ ๋ฌธ์ž์—ด ํ˜•ํƒœ๋กœ ๋“ค์–ด์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

node-redis-session
์‚ฌ์ง„์—์„œ  session: ์ด ๋‘๊ฐœ์ธ ์ด์œ ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์ดํŠธ๋ฅผ ์ ‘์†ํ–ˆ๊ธฐ์— ์‚ฌ์šฉ์ž ์„ธ์…˜ ๋‘๊ฐœ๊ฐ€ redis์— ์ €์žฅ๋œ ๊ฒƒ์ด๋‹ค.

Node API ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ• ๋•Œ ์‚ฌ๋žŒ๋“ค์ด ๋งŽ์ด ๋“ค ์“ฐ๋Š” api ์‚ฌ์šฉ๋Ÿ‰์„ ์ œํ•œํ•˜๋Š” ํŒจํ‚ค์ง€ 'express-rate-limit' ์—ญ์‹œ ์‚ฌ์šฉ๋Ÿ‰์„ ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ์— ๊ธฐ๋กํ•˜๋ฏ€๋กœ ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ์„œ๋ฒ„๋กœ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ ํ•˜๋ฉด ์‚ฌ์šฉ๋Ÿ‰ ๊ธฐ๋ก์ด ์ดˆ๊ธฐํ™”๋˜๊ฒŒ ๋œ๋‹ค.
๋”ฐ๋ผ์„œ ์ด๊ฒƒ๋„ redis์— ๊ธฐ๋กํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
express-rate-limit ํŒจํ‚ค์ง€์™€ rate-limit-redis ํŒจํ‚ค์ง€๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.