사랑하애오
article thumbnail

회사에서 서버를 배포하려고 하니 로컬환경에서는 문제 없이 잘 작동되는 exress-session에서 자꾸 warning을 뱉어내서

알아보니 production 환경에서는 제대로 작동하지 않는다는 것이었다. 그래서 결국 redis를 쓰기로 다짐했다.

Node 프로젝트에서 pm2로 다중 클러스터 인프라를 구축했다면 세션 불일치 문제가 생기게 마련이다.

만일 서버가 종료되어 메모리가 날라가면 접속자들의 로그인이나 혹은 기타 세션들이 모두 날라가버리게 된다.

따라서 이를 방지하기 위해 세션 아이디와 실제 사용자 정보를 외부 데이터베이스에 저장하는 편이다.

이때 개발자들이 많이 사용하는 것이 Redis db이다.

다른 데이터베이스를 사용해도 되지만, 세션은 빠릿빠릿하게 응답을 해야되기 떄문에 메모리 기반의 데이터베이스인 레디스를 사용한다.

지금부터 Node 프로젝트에서 Redis와 연결하고 사용하는 법을 알아보자

 

Redis DB Setting

 

Redis Cloud

MySQL을 사용하기 위해서 워크벤치를 설치했던 것 처럼, 레디스를 사용하려면 레디스 데이터베이스를 설치해야 한다.

직접 서버 컴퓨터에 직접 설치할 수도 있지만, 레디스를 호스팅 해주는 서비스를 쓰는 것이 협업할때 편리하다.

레디스를 사용하려면 클라우드 대표격인 aws 에서 제공하는 elastic cache 를 사용해도 되지만,

과금이 되므로 대신 무료로 사용할 수 있는 Redislabs를 사용해보자.

Redislabs는 1개의 레디스 DB를 30MB만큼 무료로 제공해준다.

기가, 테라 시대에서 30 메가바이트는 너무 보일수도 있지만 ram영역에서의 30mb는 결코 작은 수치가 아니다.

 

Redislabs 등록하기

우선 Redislabs 웹 사이트(http://redislabs.com/Visit Website)에 접속하고 회원가입을 진행해 준다.

 redis.com
Build and run faster apps with the world’s leading real-time data platform.

 

가입이 완료됬으면 다음과 같이 클라우드 업체를 선택할 수 있다.

클라우드 서비스를 쓰지 않더라도 문제없으니 적당히 체크해주고 시작하자.

 

그러면 다음과 같이 공짜로 레디스 데이터베이스가 하나 생성됨을 확인 할 수 있다.

이제 우리가 카카오나 구글 로그인을 구현하기위해 사이트에서 호스트와 패스워드 값을 얻어온 것 처럼, 레디스도 레디스 호스트에 접속하기 위해서 해당 정보가 필요하다.

필요한 정보는 레디스 host port, password 3가지 이다.

Tip

Data Persistence 옵션 있지만 None으로 고정되어 있고 선택 불가능 한것 같다.
다른 옵션은 AOF 1초마다 fsync, 1시간마다 스냅샷, 6시간마다 스냅샷, 12시간마다 스냅샷으로 지정 되어 있다.

 

Redis Cloud 접속하기

이제 redis 클라우드에 접속해서 redis를 사용해보자.

redis 클라우드에 접속하기 위해선 redis cli 툴이 따로 필요하다.

윈도우 redis-cli

npm install -g redis-cli # redis cli 설치

or

yarn add global redis-cli

and

rdcli -h <endpoint> -p <port> <password> # redis cloud 접속

 

리눅스 redis-cli

$ sudo apt-get install redis-tools # redis cli 설치

$ redis-cli -v # 설치 버전 확인

$ redis-cli -h <endpoint> -p <port> -a <password> # redis clouds 접속

로컬 redis-cli와 마찬가지로 저기서 ping을 날리면 PONG을 받을 수 있다.

 

redislabs 등록을 맞췄으면, 서버에서 위의 redislabs 사이트의 레디스 db에 접속하기 위해서는,

필요한 정보가 있는데 레디스 host와 port, username, password 4가지를 알아야 한다. (링크 참고)

계정을 등록하고 얻은 redis db의 host와 password 정보들을 .env 파일에 환경변수로 저장해준다.

 

Redis Events 종류

connct 클라이언트가 서버에 대한 연결이 될 경우
ready 클라이언트가 서버에 대한 연결을 성공적으로 시작할 경우
end 클라이언트가 .quit() 또는 .disconnect()를 통해 서버 연결을 끊었을 경우
error 서버에 연결할 수 없거나 예기치 못하게 연결이 끊어지는 등 네트워크 오류가 발생한 경우
reconnecting 클라이언트가 서버에 다시 연결을 시도하고 있을 겨우
redisClient.on('connect', () => {
  console.info('Redis connected!');
});

redisClient.on('error', (err) => {
  console.error('Redis Client Error', err);
});

 

Node(Express) Sesstion Store

Redis 를 유용하게 활용할 수 있는 경우 또 하나는 session 이다.

일반적으로 express-session에서의 세션은 휘발성 인메모리 데이터베이스에 잠깐 저장되고 서버가 종료되면 사라지게 된다.

이때 redis db서버를 사용하여 세션관리를 하면 서버가 꺼져도 데이터가 유지되게 할수 있으며, 복수 서버 환경(클러스터링)에서도  세션을 공유할 수 있게 된다.


Connect-redis 설치 & 사용법

connect-redis는 멀티 프로세스 간 세션 공유를 위해 레디스와 익스프레스를 연결해주는 패키지이다.

기존에는 로그인할 때 express-session의 세션 아이디와 실제 사용자 정보가 서버 메모리에 저장된다.

싱글 서버를 사용할땐 문제가 없지만 pm2를 통해 노드 클러스터 서버 인프라를 구축했을때 세션 불일치 문제가 생긴다.

따라서 세션 불일치를 해결하기 위해 이 세션 데이터를 서버 메모리가 아닌 redis db에 저장하도록 코드를 구현해보자.

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));

Tip

createClient({  legacyMode: true }) 로 레거시 모드를 설정하지 않으면 connect-redis 모듈이 동작 안하니 주의

sessionOption.store는 세션을 어디에 저장할지 고르는 옵션 정보로, 기본값은 서버의 메모리 스토리지에 저장하는데, 이것을 RedisStore로 바꾸게 되면 세션 데이터를 Redis에 저장하게 된다.

즉, 재기동을 하여도 세션이 날라가는 문제가 발생하지 않도록 처리해줄 수 있다. 

 

이제 서버를 실행해보고 개발자 도구의 Application 탭에 cookies 탭을 확인하면 다음과 같이 connect.sid 로 세션 쿠키가 서명되어 저장되어 있는 것을 볼 수 있다.

 

그리고 redis db를 확인해 보면 다음과 같이 session:세션ID 형태로 키가 저장 됨을 확인 할 수 있다.

값을 조회해 보면 세션 쿠키 객체가 문자열 형태로 들어있는 것을 확인 할 수 있다.

사진에서&nbsp; session: 이 두개인 이유는 서로 다른 사용자가 사이트를 접속했기에 사용자 세션 두개가 redis에 저장된 것이다.

Tip

Node API 서버를 구축할때 사람들이 많이 들 쓰는 api 사용량을 제한하는 패키지 'express-rate-limit' 역시 사용량을 서버 메모리에 기록하므로 서버를 재시작하거나 다른 서버로 로드밸런싱 하면 사용량 기록이 초기화되게 된다.
따라서 이것도 redis에 기록하는 것이 좋다.
express-rate-limit 패키지와 rate-limit-redis 패키지를 같이 사용하면 된다.

 

profile

사랑하애오

@사랑하애

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!