본문으로 바로가기

node에서 redis를 활용하여 캐싱을 구현해보도록 하겠습니다.

 

왜 캐싱이 필요한가?

save network call
avoid recomputation
reduce DB load

여래저래 좋다. 문제는 여러 서버를 사용시에 sync 문제가 발생할 수 있다는 것.

global caching server를 통해 해결할 수 있지만 이것도 나름의 문제가 있다.

 

 

설치 및 실습 시작

www.npmjs.com/package/redis

npm i redis

 

우선 caching 기능 없는 코드는 다음과 같다.

github api에 요청하여 유저의 public repos의 갯수를 가져오는 간단한 코드이다.

 

const express = require("express");
const axios = require("axios");

const PORT = 3001;

const app = express();

app.get("/repos/:username", async (req, res, next) => {
  try {
    console.log("fetching data...");
    const { username } = req.params;
    const {
      data: { public_repos },
    } = await axios(`https://api.github.com/users/${username}`);

    res.send(`<h2>${username} has ${public_repos} repos</h2>`);
  } catch (error) {
    console.log(error.message);
    next();
  }
});

app.listen(PORT, () => {
  console.log(`http://localhost:${PORT}`);
});

 

관리자 도구의 Network 탭을 보아하니 654ms가 걸리는 것을 확인할 수 있다. (비공개 탭에서, extension없이 진행했을 경우). 만약 실제 이용하는 브라우저에서 extension 로드까지 기다리는 걸로 측정해봈더니 2.17 초 정도 걸렸다.

 

 

* 주의) axios라서 느린 것이 아니다. 속도 측정 결과 axios는 468ms 정도 (0.4초) 걸리며 fetch와 비교해도 별반 차이가 없는 것을 console.time을 통해 확인해보았다."아, caching을 안하면 느리구나" 정도로만 이해하는 것이 좋다.

 

 

여기에 Redis를 추가해주고 caching을 구현해보도록하자. 

 

핵심은 간단하다.

1. 결과물을 redis에 저장해준다. caching invalidation을 위한 ttl 설정을 잊지 말자

2. 다음 요청 시에 미들웨어에서 redis에 저장된 값이 있다면 해당 내용을 사용하고 바로 return해버려서 실제 함수까지 도달하지 않게끔하면 된다.

 

const express = require("express");
const redis = require("redis");
const axios = require("axios");
const fetch = require("node-fetch");

const PORT = 3001;
const REDIS_PORT = 6379; // default가 있지만 explicit하게 명시

const app = express();
const client = redis.createClient({ port: REDIS_PORT }); // redis client

// event는 https://www.npmjs.com/package/redis#api 참고
client.on("connect", function (error) {
  console.error("redis is ready!");
});
client.on("error", function (error) {
  console.error(error.message);
});

// cache middlware
function cache(req, res, next) {
  const { username } = req.params;

  client.get(username, (err, data) => {
    if (err) throw err;

    if (data) {
      return res.send(`<h2>${username} has ${data} repos</h2>`);
    } else {
      next();
    }
  });
}

app.get("/repos/:username", cache, async (req, res, next) => {
  try {
    console.log("fetching data...");
    const { username } = req.params;

    const {
      data: { public_repos },
    } = await axios(`https://api.github.com/users/${username}`);

    // set data to redis
    client.setex(username, 3600, public_repos); // set인데 ttl 설정

    res.send(`<h2>${username} has ${public_repos} repos</h2>`);
  } catch (error) {
    console.log(error.message);
    next();
  }
});

app.listen(PORT, () => {
  console.log(`http://localhost:${PORT}`);
});

 

 

 

DB 요청 시의 캐슁도 이와 비슷하게 진행하면 된다.

backend에서 요청을 날리면 중간에 redis에서 해당 값이 없는지 체킹하고, 있으면 해당 값을 사용하여 캐슁하고, 없으면 실제로 정보를 받아오면 된다.

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체