JUINTINATION

Express.js의 morgan과 cookie-parser, express-session 본문

StudyNote

Express.js의 morgan과 cookie-parser, express-session

DEOKJAE KWON 2024. 1. 21. 18:12
반응형

morgan

morgan은 Logger API로 request와 response를 깔끔하게 포매팅해주어 호출된 router가 어떤 상태이고 어떤 결과 값인지 콘솔에 로그를 찍는 역할을 한다. 이렇게 찍힌 로그를 콘솔로만 확인해도 되지만 json 형태로 dump 파일에 기록해주는 winston이라는 모듈도 있다.

const logger = require('morgan') 과 같은 형태로 morgan 모듈을 불러와서 const app = express() 일 때 app.use(logger('옵션')); 과 같은 형태로 사용한다.

morgan에 들어가는 매개변수인 각 옵션마다 보여주는 정보가 다르며 여러 옵션을 넣어줄 수도 있다. 주로 개발 시에는 dev 옵션을 사용하는데 요청 메서드, url, 상태, 응답시간 등을 보여주고 배포시에는 combined 옵션을 사용하여 사용자의 주소, 브라우저 등 더 세부적인 정보를 로깅할 수 있다.

나는 개발 중이라는 가정하에 dev 옵션을 사용하여 다음과 같이 사용했다. 또한 시간 단축을 위해 지난번에 작성한 Express.js + Controller & Service & Repository 글에서 사용한 코드를 수정했다.

app.js

const express = require('express');
const http = require('http');

// morgan 모듈 불러오기
const logger = require('morgan');

const app = express();
app.set('port', process.env.PORT || 3000);

app.use(express.json());

// 'dev' 옵션 사용
app.use(logger('dev'));

const userRouter = require('./routes/user-router');
app.use('/user', userRouter);

app.use(express.static(__dirname + '/public'));
app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

http.createServer(app).listen(app.get('port'), () => {
  console.log('Express server listening on port ' + app.get('port'));
});

코드를 실행한 후에 localhost:3000 에 접속하면 다음과 같이 로그가 남는 것을 확인할 수 있다.

cookie-parser, express-session

우리가 클라이언트에게 요청을 받을 때 사용자의 정보에 관해서는 IP 주소와 브라우저의 정보 정도만 알 수 있다. 그 정보만을 가지고 사용자를 식별하기는 힘들기 때문에 로그인을 구현하는 것이고 그 로그인을 구현할 때 쿠키세션이 사용된다.

https://www.wisecleaner.com/images/think-tank/292/cookie.png

클라이언트가 요청을 보낼 때마다 key-value 쌍으로 이뤄진 쿠키를 보내고 서버는 클라이언트가 보낸 쿠키를 읽어 사용자가 누군지 식별하게 되는 것이다. 결국 쿠키는 출입을 위한 출입증이라고 생각해도 될 것 같다.

처음에 딱 한 번만 서버에서 Set-cookies에 값을 넣어주면 브라우저가 key-value 쌍으로 이루어진 쿠키가 헤더에 저장되며 이후에는 브라우저에서 자동으로 쿠키를 요청할 때마다 서버에게 보낸다.

위 그림을 비롯한 설명은 WiseCleaner의 What are Cookie & Session에 자세히 나와있다.

 

What are Cookie & Session

 

www.wisecleaner.com

클라이언트가 쿠키와 함께 요청을 보내면 req.headers.cookie를 통해 쿠키 값에 접근을 할 수 있게 되는데 이 때 req.headers.cookie에 저장된 값인 문자열을 객체로 파싱(데이터를 원하는 형태로 가공하는 과정)하기 위해 조금 복잡한 함수를 만들어야 하는데 cookie-parser를 사용하면 따로 파싱하는 함수를 만들 필요 없이 res.cookies.쿠키명 을 통해 쿠키 값에 접근할 수 있게 된다.

쿠키만 사용하게 되면 개발자도구 [Application] 탭의 Cookies에서 쿠키 값을 확인할 수도 있고 값을 바꿀 수도 있다. 따라서 실제 정보는 서버에만 저장해두고 브라우저에는 암호화된 키 값만 보내고 그 키 값으로 실제 값에 접근할 수 있도록 하는 것을 세션이라고 한다.

세션도 마찬가지로 cookies.session 값을 이용해서 세션을 다뤄야 하지만 express-session 모듈을 사용하여 req.session을 통해 접근할 수 있게 된다. 테스트를 위해 $ npm install express-session 커맨드를 실행해서 해당 모듈을 설치하자. morgan과 cookie-parser 모듈은 이미 설치되어 있었는데 아마 $ express morgan-test 커맨드를 통해 같이 설치된 듯 하다. 설치되어 있지 않다면 설치하자.

app.js

const express = require('express');
const http = require('http');
const path = require('path');

const logger = require('morgan');
const cookieParser = require('cookie-parser');
const session = require('express-session');

const app = express();
app.set('port', process.env.PORT || 3000);

app.use(express.json());
app.use(logger('dev'));

app.use(cookieParser('secret@1234')); // 암호화된 쿠키를 사용하기 위한 임의의 문자 전송
app.use(session({
  secret: 'secret@1234',              // 암호화
  resave: false,                      // 새로운 요청 시 세션에 변동 사항이 없어도 다시 저장할지 설정
  saveUninitialized: true,            // 세션에 저장할 내용이 없어도 저장할지 설정
  cookie: {
    // 세션 쿠키 옵션 설정(httpOnly, expires, domain, path, secure, sameSite)
    httpOnly:true,                    // 로그인 구현 시 필수 적용, 자바스크립트로 접근할 수 없게 하는 기능
  },
  // name: 'connect.sid'              // 세션 쿠키의 Name 지정, default가 connect.sid임
}));

// indexRouter 설정
app.get('/', (req, res) => {
  let output = `<h2>로그인하지 않은 사용자님</h2><p>로그인 해주세요.</p>`
  if (req.session.name) {
    output = `<h2>로그인한 사용자님</h2><p>${req.session.name}님 안녕하세요.</p>`
  }
  res.send(output);
});

const userRouter = require('./routes/users');
app.use('/user', userRouter);

// 실제 구현 시 app.post
app.get('/login', (req, res) => {
  console.log(req.session);
  req.session.name = 'test';
  res.end('Login OK');
});

app.get('/logout', (req, res) => {
  res.clearCookie('connect.sid'); // 세션 쿠키 삭제
  res.end('Logout Ok');
});

app.set('views', path.join(__dirname, 'views'));
// app.set('view engine', 'jade');

app.use(express.static(__dirname + '/public'));

http.createServer(app).listen(app.get('port'), () => {
  console.log('Express server listening on port ' + app.get('port'));
});

처음에 쿠키 관련해서 연구할 때 뭐가 문제인지 확인하는 과정에서 파일 구조 등을 많이 바꿨다가 최대한 원래 모습으로 돌려서 최종 테스트를 진행했다.. 기존의 코드 중 routes/user-router.js는 users.js로 파일명을 바꿨으며 원래는 app.get('/', ...) 대신에 app.get('/', indexRouter);를 사용하여 routes/index.js에 설정했었는데 최종 테스트 및 공부를 위해 위와 같이 바꿨다.

지피티가 Express.js는 잘 모르는 것 같다.

전체 코드는 Github에 morgan-test로 올릴 예정이다.

아무튼 $ node app 커맨드 실행을 통해 프로그램을 실행한 뒤에 localhost:3000 에 접속하게 되면 다음과 같이 뜬다.

여기서 localhost:3000/login 에 접속하면 Login ok라고 뜨는데 이후에 localhost:3000 다시 접속하게 되면 다음과 같이 뜬다.

브라우저를 닫을 때까지 req.session.name의 값은 유효하다. 실제로 로그인 기능을 구현할 때는 app.get이 아닌 app.post를 통해 데이터를 전송해야 할 것이다.

이후에 localhost:3000/logout 에 접속한 후에 localhost:3000 에 다시 접속하게 되면 다시 로그인하지 않은 사용자님이라는 처음에 봤던 화면이 나오게 된다.

위의 localhost:3000 사진을 다 본 뒤에 코드를 천천히 살펴보면 해석은 쉽다. 하지만 cookieParser와 session 설정 관련해서 조금 짚고 넘어가려고 한다.

app.js 코드 해석

안전하게 쿠키를 전송하기 위해 쿠키를 서명해야 하기 때문에 secret 값이 필요하다. 'secret@1234' 쿠키를 암호화하기 위한 키, 즉 secret 값으로 클라이언트에게 전송되는 쿠키를 암호화할 사용되며 cookie-parser의 인자 값과 쿠키 세션의 값을 동일하게 설정해줘야 한다. 이외의 내용은 주석에 달아두었으니 참고하면 된다.


결론

morgan 모듈을 통해 로그를 남기도록 해봤으며, cookie-parser, express-session 모듈을 통해 쿠키 및 세션을 통한 로그인 테스트를 진행해봤다. 처음에 쿠키 관련 실험을 할 때 제대로 이해가 되지 않아서였는지 많이 헤맸는데 아마 지금 뭔가 촉박하다고 느끼는 것 같다. 조금 쉬었다가 JWT Token와 Passport.js를 공부해보고 실제 로그인 기능을 구현해보도록 하겠다.

728x90
Comments